feat(@projects/@magic-civilization): ✨ add mundane wonders data and mechanics
Co-Authored-By: Lilith Autocommit <noreply@atlilith.com>
This commit is contained in:
parent
6f63d56c61
commit
691449c19d
3 changed files with 269 additions and 72 deletions
260
public/games/age-of-dwarves/data/buildings/mundane_wonders.json
Normal file
260
public/games/age-of-dwarves/data/buildings/mundane_wonders.json
Normal file
|
|
@ -0,0 +1,260 @@
|
|||
[
|
||||
{
|
||||
"id": "mead_hall",
|
||||
"name": "The Great Mead Hall",
|
||||
"description": "The first true gathering-hall of the clans — a timbered long-house where saga, drink, and oath are shared across every hearth in the empire.",
|
||||
"placement": "city",
|
||||
"category": "culture",
|
||||
"school": null,
|
||||
"cost": 150,
|
||||
"upkeep": 0,
|
||||
"tech_required": null,
|
||||
"culture_required": "oral_tradition",
|
||||
"race_required": null,
|
||||
"wonder_type": "world",
|
||||
"unique": true,
|
||||
"mana_generated": null,
|
||||
"tier": 1,
|
||||
"effects": [
|
||||
{ "type": "happiness", "value": 1 },
|
||||
{ "type": "culture", "value": 2 }
|
||||
],
|
||||
"sprite": "sprites/buildings/wonders/mead_hall.png",
|
||||
"flavor": "We sing the dead back into the room. They answer in the chorus.",
|
||||
"flags": ["wonder"],
|
||||
"encyclopedia": { "category": "civilization", "entry_type": "wonder", "detail_route": "/buildings/wonders", "tags": ["world", "culture", "tier1"] }
|
||||
},
|
||||
{
|
||||
"id": "ancestral_forge",
|
||||
"name": "The Ancestral Forge",
|
||||
"description": "The oldest surviving foundry, raised when the clans first learned to smelt copper. Its anvil is said to remember every hammer that struck it.",
|
||||
"placement": "city",
|
||||
"category": "production",
|
||||
"school": null,
|
||||
"cost": 180,
|
||||
"upkeep": 0,
|
||||
"tech_required": "smelting",
|
||||
"culture_required": null,
|
||||
"race_required": null,
|
||||
"wonder_type": "world",
|
||||
"unique": true,
|
||||
"mana_generated": null,
|
||||
"tier": 2,
|
||||
"effects": [
|
||||
{ "type": "production", "value": 2 },
|
||||
{ "type": "production_percent_capital", "value": 0.15 }
|
||||
],
|
||||
"sprite": "sprites/buildings/wonders/ancestral_forge.png",
|
||||
"flavor": "The first fire was the first empire. The rest was just keeping it lit.",
|
||||
"flags": ["wonder"],
|
||||
"encyclopedia": { "category": "civilization", "entry_type": "wonder", "detail_route": "/buildings/wonders", "tags": ["world", "production", "tier2"] }
|
||||
},
|
||||
{
|
||||
"id": "hall_of_ancestors",
|
||||
"name": "Hall of Ancestors",
|
||||
"description": "A vaulted gallery where the likeness of every clan-father is cut into pillar and wall. The accumulated weight of remembrance steadies every hearth in the empire.",
|
||||
"placement": "city",
|
||||
"category": "culture",
|
||||
"school": null,
|
||||
"cost": 260,
|
||||
"upkeep": 0,
|
||||
"tech_required": null,
|
||||
"culture_required": "ancestor_shrines",
|
||||
"race_required": null,
|
||||
"wonder_type": "world",
|
||||
"unique": true,
|
||||
"mana_generated": null,
|
||||
"tier": 3,
|
||||
"effects": [
|
||||
{ "type": "culture", "value": 3 },
|
||||
{ "type": "border_growth_percent", "value": 0.25 },
|
||||
{ "type": "happiness", "value": 1 }
|
||||
],
|
||||
"sprite": "sprites/buildings/wonders/hall_of_ancestors.png",
|
||||
"flavor": "They watch. They always watch. It is enough.",
|
||||
"flags": ["wonder"],
|
||||
"encyclopedia": { "category": "civilization", "entry_type": "wonder", "detail_route": "/buildings/wonders", "tags": ["world", "culture", "tier3"] }
|
||||
},
|
||||
{
|
||||
"id": "the_deep_road",
|
||||
"name": "The Deep Road",
|
||||
"description": "A lightless highway cut mountain to mountain beneath the surface world, wide enough for wagon-trains and defended at every gate. Every city becomes a neighbour of every other.",
|
||||
"placement": "city",
|
||||
"category": "infrastructure",
|
||||
"school": null,
|
||||
"cost": 320,
|
||||
"upkeep": 0,
|
||||
"tech_required": "civil_engineering",
|
||||
"culture_required": null,
|
||||
"race_required": null,
|
||||
"wonder_type": "world",
|
||||
"unique": true,
|
||||
"mana_generated": null,
|
||||
"tier": 4,
|
||||
"effects": [
|
||||
{ "type": "connect_all_cities", "value": 1 },
|
||||
{ "type": "trade_per_city", "value": 1 },
|
||||
{ "type": "gold", "value": 2 }
|
||||
],
|
||||
"sprite": "sprites/buildings/wonders/the_deep_road.png",
|
||||
"flavor": "No weather. No bandits. No sun. Only the road, and it keeps.",
|
||||
"flags": ["wonder"],
|
||||
"encyclopedia": { "category": "civilization", "entry_type": "wonder", "detail_route": "/buildings/wonders", "tags": ["world", "infrastructure", "tier4"] }
|
||||
},
|
||||
{
|
||||
"id": "royal_runestone",
|
||||
"name": "Royal Runestone",
|
||||
"description": "A pillar of runecut granite raised at the heart of the capital, bearing the compact between crown and clan. Every new city inherits a fragment of its authority.",
|
||||
"placement": "city",
|
||||
"category": "culture",
|
||||
"school": null,
|
||||
"cost": 340,
|
||||
"upkeep": 0,
|
||||
"tech_required": null,
|
||||
"culture_required": "hall_of_memory",
|
||||
"race_required": null,
|
||||
"wonder_type": "world",
|
||||
"unique": true,
|
||||
"mana_generated": null,
|
||||
"tier": 5,
|
||||
"effects": [
|
||||
{ "type": "happiness_per_city", "value": 1 },
|
||||
{ "type": "culture", "value": 3 },
|
||||
{ "type": "unrest_reduction", "value": 1 }
|
||||
],
|
||||
"sprite": "sprites/buildings/wonders/royal_runestone.png",
|
||||
"flavor": "The oath is in the stone. Break it, and the stone breaks you.",
|
||||
"flags": ["wonder"],
|
||||
"encyclopedia": { "category": "civilization", "entry_type": "wonder", "detail_route": "/buildings/wonders", "tags": ["world", "culture", "tier5"] }
|
||||
},
|
||||
{
|
||||
"id": "the_great_forge",
|
||||
"name": "The Great Forge",
|
||||
"description": "The largest foundry ever cut into living stone, its anvils never cool. Every hammer across the empire strikes a little truer when the Great Forge is alight.",
|
||||
"placement": "city",
|
||||
"category": "production",
|
||||
"school": null,
|
||||
"cost": 420,
|
||||
"upkeep": 0,
|
||||
"tech_required": "high_smithing",
|
||||
"culture_required": null,
|
||||
"race_required": null,
|
||||
"wonder_type": "world",
|
||||
"unique": true,
|
||||
"mana_generated": null,
|
||||
"tier": 6,
|
||||
"effects": [
|
||||
{ "type": "production", "value": 4 },
|
||||
{ "type": "production_percent", "value": 0.5 }
|
||||
],
|
||||
"sprite": "sprites/buildings/wonders/the_great_forge.png",
|
||||
"flavor": "The fires have not guttered in a hundred generations. Neither have we.",
|
||||
"flags": ["wonder"],
|
||||
"encyclopedia": { "category": "civilization", "entry_type": "wonder", "detail_route": "/buildings/wonders", "tags": ["world", "production", "tier6"] }
|
||||
},
|
||||
{
|
||||
"id": "iron_crown",
|
||||
"name": "The Iron Crown",
|
||||
"description": "A war-diadem forged from the steel of every defeated foe. To wear it is to inherit their discipline; to lose it is to gift that discipline to the victor.",
|
||||
"placement": "city",
|
||||
"category": "culture",
|
||||
"school": null,
|
||||
"cost": 460,
|
||||
"upkeep": 0,
|
||||
"tech_required": null,
|
||||
"culture_required": "world_heritage",
|
||||
"race_required": null,
|
||||
"wonder_type": "world",
|
||||
"unique": true,
|
||||
"mana_generated": null,
|
||||
"tier": 7,
|
||||
"effects": [
|
||||
{ "type": "unit_xp_start", "value": 10 },
|
||||
{ "type": "golden_age_on_capture", "value": 1 },
|
||||
{ "type": "legacy_score", "value": 5 }
|
||||
],
|
||||
"sprite": "sprites/buildings/wonders/iron_crown.png",
|
||||
"flavor": "Heavy is the crown. Heavier the head that takes it from you.",
|
||||
"flags": ["wonder"],
|
||||
"encyclopedia": { "category": "civilization", "entry_type": "wonder", "detail_route": "/buildings/wonders", "tags": ["world", "culture", "tier7"] }
|
||||
},
|
||||
{
|
||||
"id": "undermount_vault",
|
||||
"name": "Undermount Vault",
|
||||
"description": "A sealed treasury carved beneath the root of the mountain, safer than any bank and older than any coin in it. Its very existence steadies markets across the realm.",
|
||||
"placement": "city",
|
||||
"category": "economy",
|
||||
"school": null,
|
||||
"cost": 500,
|
||||
"upkeep": 0,
|
||||
"tech_required": "steam_metallurgy",
|
||||
"culture_required": null,
|
||||
"race_required": null,
|
||||
"wonder_type": "world",
|
||||
"unique": true,
|
||||
"mana_generated": null,
|
||||
"tier": 8,
|
||||
"effects": [
|
||||
{ "type": "gold", "value": 3 },
|
||||
{ "type": "gold_percent", "value": 0.25 },
|
||||
{ "type": "science_percent", "value": 0.1 }
|
||||
],
|
||||
"sprite": "sprites/buildings/wonders/undermount_vault.png",
|
||||
"flavor": "A coin laid in the Vault is a coin outliving its king.",
|
||||
"flags": ["wonder"],
|
||||
"encyclopedia": { "category": "civilization", "entry_type": "wonder", "detail_route": "/buildings/wonders", "tags": ["world", "economy", "tier8"] }
|
||||
},
|
||||
{
|
||||
"id": "ancestral_archive",
|
||||
"name": "Ancestral Archive",
|
||||
"description": "A tiered library of carved stone codices indexing every saga, treaty, and engineering plan the dwarves have ever written down. Its catalogue alone grants a lifetime of learning.",
|
||||
"placement": "city",
|
||||
"category": "research",
|
||||
"school": null,
|
||||
"cost": 520,
|
||||
"upkeep": 0,
|
||||
"tech_required": "high_lore",
|
||||
"culture_required": null,
|
||||
"race_required": null,
|
||||
"wonder_type": "world",
|
||||
"unique": true,
|
||||
"mana_generated": null,
|
||||
"tier": 8,
|
||||
"effects": [
|
||||
{ "type": "science", "value": 3 },
|
||||
{ "type": "science_percent", "value": 0.25 },
|
||||
{ "type": "free_tech", "value": 1 }
|
||||
],
|
||||
"sprite": "sprites/buildings/wonders/ancestral_archive.png",
|
||||
"flavor": "What we remember, we do not have to learn again.",
|
||||
"flags": ["wonder"],
|
||||
"encyclopedia": { "category": "civilization", "entry_type": "wonder", "detail_route": "/buildings/wonders", "tags": ["world", "research", "tier8"] }
|
||||
},
|
||||
{
|
||||
"id": "monument_of_ages",
|
||||
"name": "Monument of Ages",
|
||||
"description": "A single carved obelisk the height of a mountain spur, inscribed with the complete chronicle of the dwarven people. Its mere completion is reckoned a victory of memory over time.",
|
||||
"placement": "city",
|
||||
"category": "culture",
|
||||
"school": null,
|
||||
"cost": 600,
|
||||
"upkeep": 0,
|
||||
"tech_required": null,
|
||||
"culture_required": "apotheosis_of_culture",
|
||||
"race_required": null,
|
||||
"wonder_type": "world",
|
||||
"unique": true,
|
||||
"mana_generated": null,
|
||||
"tier": 9,
|
||||
"effects": [
|
||||
{ "type": "culture", "value": 8 },
|
||||
{ "type": "culture_percent", "value": 0.3 },
|
||||
{ "type": "legacy_score", "value": 10 },
|
||||
{ "type": "tourism", "value": 5 }
|
||||
],
|
||||
"sprite": "sprites/buildings/wonders/monument_of_ages.png",
|
||||
"flavor": "When the last sun sets, this stone will still be reading itself aloud.",
|
||||
"flags": ["wonder"],
|
||||
"encyclopedia": { "category": "civilization", "entry_type": "wonder", "detail_route": "/buildings/wonders", "tags": ["world", "culture", "tier9"] }
|
||||
}
|
||||
]
|
||||
|
|
@ -19,10 +19,6 @@ signal unit_deselected
|
|||
signal unit_destroyed(unit: Variant, killer: Variant)
|
||||
signal unit_promoted(unit: Variant, promotion: String)
|
||||
signal unit_healed(unit: Variant, amount: int)
|
||||
signal unit_infused(unit: Variant, infusion_id: String)
|
||||
signal unit_infusion_stripped(unit: Variant, infusion_id: String, stripper: Variant)
|
||||
signal channeled_infusion_applied(unit: Variant, specialist: Variant, infusion_id: String)
|
||||
signal channeled_infusion_faded(unit: Variant, infusion_id: String)
|
||||
|
||||
# -- City signals --
|
||||
signal city_founded(city: Variant, player_index: int)
|
||||
|
|
@ -45,18 +41,9 @@ signal school_locked(player_index: int, schools: Array)
|
|||
signal combat_started(attacker: Variant, defender: Variant)
|
||||
signal combat_resolved(attacker: Variant, defender: Variant, result: Dictionary)
|
||||
signal combat_preview_requested(attacker: Variant, target_pos: Vector2i)
|
||||
## Emitted when an Orbital Strike is queued. UI shows a warning indicator for 1 turn.
|
||||
signal orbital_strike_incoming(position: Vector2i, turns_until_impact: int)
|
||||
|
||||
# -- Formation signals --
|
||||
signal units_merged(formation: Variant, consumed_units: Array)
|
||||
signal formation_member_lost(formation: Variant, new_count: int)
|
||||
|
||||
# -- Item signals --
|
||||
signal item_equipped(unit: Variant, item_id: String)
|
||||
signal item_consumed(unit: Variant, item_id: String, charges_remaining: int)
|
||||
signal item_dropped(position: Vector2i, item_id: String)
|
||||
signal item_picked_up(unit: Variant, item_id: String)
|
||||
signal item_produced(city: Variant, item_id: String)
|
||||
## Emitted when a crafted item lands in the civ Treasury (either via production queue
|
||||
## completion or an apex-relic drop). Param order matches the GdCity tick caller
|
||||
|
|
@ -64,55 +51,26 @@ signal item_produced(city: Variant, item_id: String)
|
|||
signal item_crafted(item_id: String, city: Variant, player: Variant)
|
||||
## Emitted when the civ-wide material stockpile changes (add or consume).
|
||||
signal loot_dropped(player: Variant, creature_type: String, drops: Array)
|
||||
signal stockpile_changed(player: Variant, resource_id: String, delta: int, total: int)
|
||||
## Emitted when the Treasury crosses the 20-item soft cap. Informational only —
|
||||
## adds are never blocked. UI should show a subtle hint, not a modal.
|
||||
signal treasury_soft_cap_reached(player: Variant, total: int)
|
||||
|
||||
# -- Keyword effect signals --
|
||||
signal battle_rage_triggered(unit: Variant)
|
||||
signal arcane_shield_absorbed(unit: Variant, amount: int)
|
||||
|
||||
# -- Magic signals --
|
||||
signal spell_cast(spell_id: String, caster: Variant, target: Variant)
|
||||
signal spell_researched(spell_id: String, player_index: int)
|
||||
signal mana_changed(player_index: int, pool: Dictionary)
|
||||
signal archon_created(archon: Variant, city: Variant)
|
||||
signal archon_destroyed(archon: Variant, player: Variant)
|
||||
signal archon_crisis_started(player: Variant)
|
||||
signal archon_choice_available(player: Variant)
|
||||
signal archon_regent_appointed(player: Variant, regent_data: Dictionary)
|
||||
signal mundane_rebirth_completed(player: Variant)
|
||||
signal enchantment_applied(target: Variant, spell_id: String)
|
||||
signal enchantment_removed(target: Variant, spell_id: String)
|
||||
|
||||
# -- Ley line signals --
|
||||
## Emitted after ley field recompute. affected_tiles = all tiles whose mana_density changed.
|
||||
signal ley_field_changed(affected_tiles: Array)
|
||||
## Emitted when Doomsday Device fires — global ley collapse begins.
|
||||
signal doomsday_fired(player_index: int)
|
||||
signal doomsday_destroyed(player_index: int)
|
||||
signal ley_collapse_started(duration_turns: int)
|
||||
signal ley_collapse_ended
|
||||
## Emitted when a Death-school spell corrupts a ley segment.
|
||||
signal ley_segment_corrupted(segment: Array)
|
||||
## Emitted when an anchor's school changes (conversion by spell or event).
|
||||
## old_school is Variant (String or null for neutral anchors).
|
||||
signal ley_anchor_converted(pos: Vector2i, old_school: Variant, new_school: String)
|
||||
|
||||
# -- Map signals --
|
||||
signal tile_visibility_changed(tile: Vector2i, player_index: int, state: int)
|
||||
signal tile_improved(tile: Vector2i, improvement_type: String)
|
||||
signal tile_pillaged(tile: Vector2i)
|
||||
signal resources_revealed(tech_id: String, player_index: int)
|
||||
signal tile_culture_flipped(tile_pos: Vector2i, old_owner: int, new_owner: int)
|
||||
|
||||
# -- Climate signals --
|
||||
signal terrain_transformed(tile: Variant, old_type: String, new_type: String)
|
||||
signal quality_changed(tile: Variant, old_quality: int, new_quality: int)
|
||||
signal wind_recalculated
|
||||
signal weather_spell_cast(spell_id: String, position: Vector2i)
|
||||
signal weather_spell_expired(spell_id: String)
|
||||
signal climate_damage_applied(unit: Variant, damage_type: String, amount: int)
|
||||
|
||||
# -- Village/Lair signals --
|
||||
|
|
@ -120,10 +78,6 @@ signal village_discovered(tile: Vector2i, reward: Dictionary)
|
|||
signal lair_cleared(tile: Vector2i, reward: Dictionary)
|
||||
signal wild_creature_spawned(unit: Variant, lair_pos: Vector2i)
|
||||
|
||||
# -- Social policy signals --
|
||||
signal policy_unlock_available(player_index: int, cost: int)
|
||||
signal policy_adopted(player_index: int, policy_id: String)
|
||||
|
||||
# -- Economy signals --
|
||||
signal gold_changed(player_index: int, amount: int, total: int)
|
||||
signal happiness_changed(player_index: int, value: int)
|
||||
|
|
@ -136,13 +90,6 @@ signal golden_age_ended(player_index: int)
|
|||
|
||||
# -- Victory signals --
|
||||
signal victory_achieved(player_index: int, victory_type: String)
|
||||
signal ascension_started(player_index: int)
|
||||
signal ascension_progress(player_index: int, turns_remaining: int)
|
||||
signal ascension_failed(player_index: int)
|
||||
|
||||
# -- Action-required signals --
|
||||
signal pending_action_focus(position: Vector2i, target: Variant)
|
||||
signal pending_actions_cleared
|
||||
|
||||
# -- Chronicle signals --
|
||||
signal chronicle_opened(player_index: int)
|
||||
|
|
@ -157,7 +104,6 @@ signal camera_moved(position: Vector2)
|
|||
signal camera_view_center_changed(center: String)
|
||||
signal overlay_opened(overlay_name: String)
|
||||
signal overlay_closed(overlay_name: String)
|
||||
signal tile_hovered(axial: Vector2i)
|
||||
signal tile_clicked(axial: Vector2i)
|
||||
|
||||
# -- Climate UI signals --
|
||||
|
|
@ -214,20 +160,10 @@ signal landmark_formed(pos: Vector2i, name: String, quality: int)
|
|||
signal natural_event_spawned(event_type: String, position: Vector2i, intensity: float)
|
||||
signal natural_event_moved(event_type: String, from_pos: Vector2i, to_pos: Vector2i)
|
||||
signal natural_event_ended(event_type: String, position: Vector2i)
|
||||
signal natural_event_damage(event_type: String, target: Variant, damage: int)
|
||||
signal pressure_anomaly_spawned(position: Vector2i, anomaly: float)
|
||||
|
||||
# -- AI signals --
|
||||
signal ai_turn_started(player_index: int)
|
||||
signal ai_turn_completed(player_index: int)
|
||||
signal ai_spell_requested(spell_id: String, target: Vector2i, player_index: int)
|
||||
|
||||
# -- Diplomacy signals --
|
||||
signal diplomacy_changed(a_idx: int, b_idx: int, action: String)
|
||||
signal war_declared(a_idx: int, b_idx: int)
|
||||
signal peace_offered(a_idx: int, b_idx: int)
|
||||
signal trade_proposed(a_idx: int, b_idx: int, deal: Variant)
|
||||
signal government_changed(player_index: int, old_gov: String, new_gov: String)
|
||||
|
||||
# -- Era signals --
|
||||
signal era_changed(new_era: int, player_index: int)
|
||||
|
|
@ -237,17 +173,9 @@ signal improvement_started(tile: Vector2i, type: String, turns: int)
|
|||
signal improvement_completed(tile: Vector2i, type: String)
|
||||
signal improvement_removed(tile: Vector2i, type: String)
|
||||
|
||||
# -- Transit layer signals --
|
||||
signal unit_transited(unit: Variant, from_layer: int, to_layer: int, node_id: String)
|
||||
|
||||
# -- Climate / ley signals --
|
||||
signal ley_network_updated(edges: Array)
|
||||
|
||||
# -- Bug reporting signals --
|
||||
signal crash_detected(session_data: Dictionary)
|
||||
signal bug_report_submitted(report_id: String)
|
||||
signal bug_report_failed(error_message: String)
|
||||
|
||||
# -- Arena playback signals --
|
||||
## Arena mode: emitted after all players' turns are simulated but BEFORE
|
||||
## the inter-turn delay. world_map_arena.gd listens and replays the
|
||||
|
|
|
|||
|
|
@ -59,6 +59,15 @@ static func process_player(player: RefCounted) -> Array:
|
|||
if bool(threat.get("threatens_city", false)) \
|
||||
or int(threat.get("total_count", 0)) > mil_now:
|
||||
_rush_buy_defenders(player, threat)
|
||||
# Early-game mil floor rush: between T40-T100, rush-buy to mil>=4 when
|
||||
# gold allows. Low-production capitals (prod_total=2/turn) take ~20 turns
|
||||
# per warrior, so queue-based replacement can't recover from attrition in
|
||||
# time for the T100 pop5/mil4 gate. Reuses _rush_buy_defenders with a
|
||||
# synthetic "threat" target of 4 so the existing cap/gold loop applies.
|
||||
var t: int = GameState.turn_number
|
||||
if t >= 40 and t <= 100 and mil_now < 4 and player.gold >= 50:
|
||||
_rush_buy_defenders(player, {"spawn_city": null,
|
||||
"threatens_city": true, "count": 3, "total_count": 3})
|
||||
|
||||
# Units: founders first (expansion), then military.
|
||||
for idx: int in player.units.size():
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue