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
- Population3 000 citizens
- Culture rating25
- Prosperity rating25
- Monument rating9 — one small granite obelisk
- Kingdom rating75 — the toughest requirement here
- Housing level10
All goals match mission1.pak scenario 13 except the monument rating, which is a temporary stand-in — see the deviation note below.
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.
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
- Fulfilling the year-4 troops request starts a self-renewing 400-troop tribute loop — keep succeeding and it keeps paying kingdom rating; fail it and Dahshur's status, kingdom rating, and copper/gem prices crash together.
- Keep a limestone buffer at all times; the ladder recurs at years 1, 3, 4, 7, 9, and 11, each fulfilled request worth +6 kingdom rating toward the goal of 75.
- Temples of Seth (god of the desert and war) matter here — Osiris and Ra have no presence at Buhen at all, so don't budget for their festivals.
- The year-6 oil request (2 000!) and the granite demand spikes at years 5 and 10 can catch an unprepared Storage Yard off guard — keep spare capacity.
Available buildings
All buildings are available from mission start:
| Category | Buildings |
|---|---|
| Housing & roads | Vacant Lot · Clear Land · Road · Road Block · Low Bridge · Ferry |
| Municipal | Firehouse · Architect's Post · Police Station · Village Palace · Personal Mansion · Tax Collector · Courthouse |
| Health | Water Supply · Apothecary · Physician · Mortuary |
| Food & farming | Grain Farm · Figs Farm · Hunting Lodge · Water Lift · Irrigation Ditch · Bazaar · Granary · Storage Yard |
| Raw materials | Limestone Quarry · Stone Quarry · Clay Pit |
| Industry | Bricks · Pottery · Brewery · Papyrus · Weaver · Jewels Workshops · Work Camp |
| Monuments | Small Obelisk (100 granite in staffed SY to place) |
| Guilds | Stonemasons' · Carpenters' · Bricklayers' |
| Military | Recruiter · Weaponsmith · Military Academy · Infantry / Archers / Charioteers Fort · Mud Wall · Mud Gatehouse · Mud Tower · Warship Wharf · Transport Wharf · Shipwright |
| Religion | Temples & Shrines of Ptah and Seth only · Festival Square (no Osiris or Ra) |
| Education | Scribal School · Library |
| Entertainment | Booth · Juggler School · Bandstand · Conservatory · Pavilion · Dance School |
| Beautification | Small / 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_EVENTchain 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_KUSHITEinvasions (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-waveby_favourchain, 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.
| Key | ID | Line | When shown |
|---|---|---|---|
message_history_buhen | 213 | 2637 | History briefing (subtitle "Expansion to Nubia") |
message_mission_buhen | 413 | 4898 | start_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_countare 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_tagis 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 fromfight_distant_battleare 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 nativeEVENT_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 setnext_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.