Wiki / Player Guide / Missions / Buhen

Buhen — Expansion to Nubia

Mission
13 of 38 — choice A of the Buhen / South Dahshur pair
Era
Old Kingdom · Dynasty 4 · Pharaoh Snofru
Classical name
Buhen (fortress at the second cataract of the Nile, Nubia)
Starting funds
Very Easy 20 000 Db Easy 13 300 Db Normal 10 000 Db (pak) Hard 6 700 Db Very Hard 5 300 Db
Debt interest
Very Easy 10% Easy 15% Normal 20% (pak) Hard 25% Very Hard 30%
Deities
Ptah · Seth (no Osiris or Ra at Buhen)
Trade
Men-nefer (sea, 800 Db) · Selima Oasis (land, 300 Db) · Abu (land, 600 Db) · Meidum (sea, 900 Db) · Enkomi (sea, 1580 Db) · Serabit Khadim (land, 1250 Db) — all start closed
Previous mission
Serabit Khadim or Meidum
Next mission
North Dahshur (15) — not yet scripted
Status
Choice A Implemented

Briefing

The mission opens with the history panel (message_history_buhen) and the briefing popup (message_mission_buhen):

Our new Pharaoh, Snofru, is determined that this, the fourth dynasty, be remembered as the greatest ever to rule Egypt. Pharaoh wishes that our borders be pushed even further south. He has therefore decreed that we invade Nubia, and establish a fortified city at Buhen, beside the second cataract of the Nile. There you must also erect a granite obelisk, to serve as a beacon that this area forever belongs to Egypt. There is no granite to be found so far south, however, so you will need to import it from Abu.

At Buhen you will encounter fierce and seasoned Nubian warriors. After rigorous training at a Military Academy, your fighting men may emerge as seasoned veterans themselves. Our military engineers have perfected the design of fortified Towers, Walls and Gatehouses. Transport ships may also be employed, to aid in moving your army around by water.

Pharaoh's burial place, a unique and spectacular monument, is currently under construction at Dahshur. From time to time Pharaoh may request that you contribute some limestone to aid in the completion of this project.

Buhen was a real Egyptian fortress at the second cataract of the Nile in Lower Nubia, one of a chain of mud-brick strongholds that guarded the southern frontier and the gold and trade routes. In the campaign it is the military branch of the Snofru fork: a walled garrison town that must survive repeated raids from the south (the briefing text calls the attackers "Nubian", but the scenario data wires them as the Kushite enemy pack) while feeding limestone to Pharaoh's pyramid at Dahshur — all under the patronage of Ptah and Seth alone, with no temples to Osiris or Ra this far south.

Objectives

All goals match mission1.pak scenario 13 except the monument rating, which is a temporary stand-in — see the deviation note below.

Temporary deviation from the original: the pak's raw monument goal is 6, but one finished small obelisk (weight 2) already produces rating 9 under the current additive formula in city/monuments.js (2.25 × 2 + 4.5 = 9). The goal is set to 9 to match what the building actually yields; it reverts to 6 once per-type weights are recalibrated against the original point table (pending weight recalibration).

Scripted events

Full empire redefine + event schedule verified against pak scenario 13 (2026-07-26 dump). Empire id 13. Scenario enemy is Kushite (ENEMY_6_KUSHITE) — not Nubian, despite the briefing text. Pharaoh's favour chains three waves if kingdom rating collapses: size 22 → 21 → 28. Timed raids and favour are proxied in JS until B2-migrate.

Year 1 · month 6 Pharaoh requests 8 limestone / 24 months for the Dahshur pyramid. Completion +6 kingdom rating; refusal −8; lateness −4 — the shared limestone leaves reused by every limestone request below.
Year 2+ · month 9 Recurring Pharaoh request for 9 beer / 24 months (sender is Pharaoh, not the city). Same +6 / −8 / −4 kingdom-rating leaves; gated so a fresh request never stacks on top of an unresolved one.
Limestone ladder — years 3, 4, 7, 9, 11 Further one-shot limestone requests keep the Dahshur pyramid supplied: 16 / 20mo (y3m2), 25 / 24mo (y4m10), 32 / 24mo (y7m2), 20 / 12mo (y9m11), and 11 / 18mo (y11m7) — all sharing the same +6 / −8 / −4 leaves.
Year 4 · month 7 The city itself asks for 60 troops / 12 months. Success chains a "city saved" message → kingdom rating +30 → a second levy of 400 troops that re-arms the same request as long as you keep succeeding. Refusal or lateness cascades instead: Dahshur's status drops → kingdom rating −35 → copper price +201% / demand −9, gems price +80% / demand −7, plus a further kingdom-rating hit if Enkomi's status falls too.
Year 5 · month 1 & Year 10 Granite demand rises by 6, then by a further 5 at year 10.
Year 6 · month 1 Pharaoh requests 2 000 oil / 6 months — an unusually large amount, taken straight from the pak data.
Kushite raids — years 6 to 20 Eight scripted invasions escalate through the mission: size 8 (y6m5), 16 (y10m6), 16 (y12m8), 24 (y13m3), 24 (y15m5), 32 (y17m1), 32 (y18m1), and 36 (y20m8) — attack targets rotate between best buildings, random, vaults, and food stores.
Year 7 · month 4 A festival request for 13 beer / 8 months pays out a granite gift chain on success — 12 granite followed by +9 kingdom rating — or a smaller 6-granite gift if delivered late.
Year 1 onward Clay pit floods recur every year from month 0, hitting up to eight working pits per year (amounts 8/8/9/5/8/6/9/5).
Kingdom rating collapse If kingdom rating bottoms out at zero, Pharaoh's favour sends a punitive army of 22, chaining to 21 and then 28 if each wave isn't cleared before the next one triggers.

Strategy

Buhen is a fortress economy under a demanding kingdom rating: hold the line against escalating Kushite raids, keep Pharaoh's Dahshur pyramid supplied with limestone, and answer the city's own call for troops — all under Ptah and Seth alone, with no Osiris or Ra to lean on.

Phase 1 — Settle behind walls

Pick a compact site and wall it early with mud walls, gatehouses, and towers. Hunting Lodges and floodplain grain feed the town; a Limestone Quarry is essential — it supplies your own needs plus the five limestone requests spread across years 1, 3, 4, 7, 9, and 11. All six trading partners start closed: Selima Oasis (300 Db) and Abu (600 Db) are the cheapest land routes; Men-nefer (800 Db, sea) and Meidum (900 Db, sea) come next; Serabit Khadim (1 250 Db, land) and Enkomi (1 580 Db, sea) are the priciest.

Phase 2 — A veteran army

Build a Recruiter, Weaponsmith, and a Military Academy, then raise infantry and archer companies and run them through the academy — trained veterans matter against the Kushite raids, which grow from size 8 in year 6 to size 36 by year 20. Keep a standing garrison at all times behind the mud fortifications; the raids never really stop, and a Transport Wharf helps shuttle troops along the Nile.

Phase 3 — The obelisk and the kingdom rating

There is no granite this far south — import it from Abu until you have 100 blocks in a staffed Storage Yard, then place the small obelisk for 9 monument points. The real challenge is kingdom rating 75: answer every limestone and beer request, keep the year-4 troop levy (and its recurring 400-troop follow-ups) fulfilled, and watch for the granite demand spikes at years 5 and 10.

Tips

Available buildings

All buildings are available from mission start:

CategoryBuildings
Housing & roadsVacant Lot · Clear Land · Road · Road Block · Low Bridge · Ferry
MunicipalFirehouse · Architect's Post · Police Station · Village Palace · Personal Mansion · Tax Collector · Courthouse
HealthWater Supply · Apothecary · Physician · Mortuary
Food & farmingGrain Farm · Figs Farm · Hunting Lodge · Water Lift · Irrigation Ditch · Bazaar · Granary · Storage Yard
Raw materialsLimestone Quarry · Stone Quarry · Clay Pit
IndustryBricks · Pottery · Brewery · Papyrus · Weaver · Jewels Workshops · Work Camp
MonumentsSmall Obelisk (100 granite in staffed SY to place)
GuildsStonemasons' · Carpenters' · Bricklayers'
MilitaryRecruiter · Weaponsmith · Military Academy · Infantry / Archers / Charioteers Fort · Mud Wall · Mud Gatehouse · Mud Tower · Warship Wharf · Transport Wharf · Shipwright
ReligionTemples & Shrines of Ptah and Seth only · Festival Square (no Osiris or Ra)
EducationScribal School · Library
EntertainmentBooth · Juggler School · Bandstand · Conservatory · Pavilion · Dance School
BeautificationSmall / Medium / Large Statue · Gardens · Plaza

Developer reference

Collapsed sections below show exactly where each part of this mission lives in the source tree. All paths are relative to the repository root.

Mission script — src/scripts/mission/m_013_buhen.js

The entire mission — configuration block, variables, and all event handlers — lives in one file, imported by src/scripts/missions.js. Key parts of the mission13 { … } block (verified vs mission1.pak scenario 13, 2026-07-26 dump):

mission13 { // Buhen — Expansion to Nubia
  player_rank : 5
  next_mission : 15          // converges on North Dahshur

  // pak Normal funds=10000 loan=5000 debt_interest=20 → int_dcy row (verified)
  initial_funds [20000, 13300, 10000, 6700, 5300]
  debt_interest [10, 15, 20, 25, 30]

  // Gods fixed to Ptah + Seth per pak (GOD_PTAH:1, GOD_SETH:2) — Osiris/Ra removed
  // pop 3000, culture 25, prosperity 25, monuments 9 (pak raw 6 → formula), kingdom 75, housing 10
  win_criteria {
    population    { enabled : true, goal : 3000 }
    culture       { enabled : true, goal : 25 }
    prosperity    { enabled : true, goal : 25 }
    monuments     { enabled : true, goal : 9 }
    kingdom       { enabled : true, goal : 75 }
    housing_level { enabled : true, goal : 10 }
  }
}

There is no choice[]: Buhen and South Dahshur are the two arms of a fork the player picks at the end of mission 11/12; both set next_mission : 15. Empire id is set to 13 via empire.set_id(13) in mission13_on_start (m_013_buhen.js:629) together with a full hide_pak_* cities/routes/ornaments/texts redefine (six openable trading partners — Men-nefer, Selima Oasis, Abu, Meidum, Enkomi, Serabit Khadim — plus seven display-only cities).

Event handlers — all functions in m_013_buhen.js

Handlers fire only while mission=mission13 is active. Raids use the JS API city.start_foreign_army_invasion with ENEMY_6_KUSHITE; native EVENT_TYPE_INVASION binds exist (B2), but this mission still uses JS poll until B2-migrate. Every pak event index that isn't skipped junk gets its own event_advance_month handler, gated by a once-flag or a per-year "last fired" guard for recurring roots:

  • event_mission_start line 620 Start message; empire.set_id(13); city.set_scenario_enemy_id(ENEMY_6_KUSHITE); advisors up to Diplomacy; wires every dormant chain leaf up front. mission13_on_start
  • Shared helper builders lines 537–603 Dormant ONLY_VIA_EVENT chain leaves reused by most requests: the three shared KR leaves (+6 / −8 / −4), the granite gift chain kicked off by the year-7 beer festival, and the troops chain kicked off by the year-4 troops request (message → KR+30 → 400-troop re-arm, or refuse/late → status/KR/price cascade). mission13_ensure_shared_kr_leaves / mission13_ensure_granite_gift_leaves / mission13_ensure_troops_chain_leaves
  • Once calendar roots lines 654–853 Limestone ladder y1m6 / y3m2 / y4m10 / y7m2 / y9m11 / y11m7; troops y4m7; granite demand y5m1 / y10m0; oil y6m1; beer festival y7m4 (granite gift chain). mission13_event_i<n>_*
  • Recurring calendar roots lines 668 / 943 Beer y2m9+, idle-gated so a fresh request never stacks on an unresolved one; clay pit floods every year from y1m0, hitting up to 8 working pits per year. mission13_event_i3_beer_recurring / mission13_clay_pit_flood_recurring
  • Kushite raids + Pharaoh favour lines 758–937 / 969 Eight one-shot ENEMY_6_KUSHITE invasions (size 8→36, years 6–20, attack targets vary); a monthly favour tick drops a size-22 Pharaoh army when kingdom rating bottoms out, chaining to size-21 then size-28 waves — the pak's three-wave by_favour chain, mirrored locally since the shared favour helper only supports a single chained wave. mission13_event_i<n>_kushite_invasion / mission13_pharaoh_favour_invasion

Dump helper: src/js/js_test_mission_pak_dump.cpp (__test_mission_pak_dump / GamestateIO::load_mission_pak_raw; ad-hoc tests/99_tmp_mission13_dump.js, deleted after verification).

Game messages — src/scripts/game_messages_en.js

Popup dialogs and the opening history panel are defined in src/scripts/game_messages_en.js. To edit text, find the entry by key and change the text: field inside its content { … } block.

KeyIDLineWhen shown
message_history_buhen2132637History briefing (subtitle "Expansion to Nubia")
message_mission_buhen4134898start_message — first popup on mission load

Audio: Voice/Mission/213_mission.mp3 and Voice/Mission/213_victory.mp3.

Known deviations from the original (tracked)
  • Monuments: pak raw goal is 6; TEMP goal is 9 — one finished small obelisk (weight 2) already produces rating 9 under the additive formula (2.25×2+4.5) in src/scripts/city/monuments.js. Reverts to 6 once per-type weights are recalibrated against the original point table (pending weight recalibration).
  • Invasion points omitted: pak inv_land_count / inv_sea_count are both 0 — the engine falls back to the map exit tile for every Kushite raid (scenario_start_invasion_impl).
  • Troops defeat chain: set_defeat_action_tag is wired; fulfilling defers to a linked distant battle whose egyptian strength comes from empire-service bataillons (B8 — not equal-abstract). No / weak army → lose → i=36→47; strong enough → ok chain. Refuse/late still skip the battle and fire i=34. Hardcoded kingdom swings from fight_distant_battle are suppressed when request-linked. Smoke: tests/179_troops_distant_battle.js.
  • Troops MESSAGE / CITY_STATUS subtypes: i=33 MESSAGE subtype 0 = city saved (already handled). i=34/36 CITY_STATUS subtype 0 = Egyptian city falls — engine now closes trade, marks the city foreign-occupied, and posts the falls message (was a silent no-op / AUD1).
  • Empire routes 8/9/10 skipped: the pak polylines exist but no map object references them — orphan data, the same pattern seen in earlier missions.
  • Oil×2000: the year-6 request amount is unusually large but matches the pak value exactly — not a transcription error.
  • Invasions and Pharaoh's favour are proxied in JS (city.start_foreign_army_invasion) rather than the native EVENT_TYPE_INVASION — same pattern as every mission before B2-migrate.
  • Next mission 15 (North Dahshur) is scripted (m_015_north_dahshur.js); both Buhen and South Dahshur set next_mission : 15.
Save file and map data

The map layout, terrain, and the kingdom road entry point come from the original Pharaoh campaign pack:

mission1.pak (scenario offset for id 13 — see GamestateIO::get_campaign_scenario_offset, src/io/gamestate/boilerplate.cpp:120)

The binary map is loaded by the engine before the JS script runs. The scripting system is loaded from src/scripts/missions.js, which imports mission/m_013_buhen.js by path. If you rename or move the file, update the import there.