feat(city-defense): city bombard retaliation + building HP bonuses
- City bombard: melee attackers take 5-30 damage based on city population and castle bombard bonus (city_str = pop*3 + castle bonus) - Building HP bonuses: when walls/castle complete, increase city max_hp and heal by hp_bonus value from building data - Castle data: added city_bombard_strength: 12, city_bombard_range: 2 Combined with prior commit's city healing (20 HP/turn) and tiered wall penalties (walls=0.75x, castle=0.60x), cities now require sustained multi-turn sieges instead of 1-2 turn captures. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
00aa2ef601
commit
9bbd80a426
3 changed files with 48 additions and 0 deletions
|
|
@ -91,6 +91,14 @@
|
|||
"type": "ranged_defense",
|
||||
"value": 8,
|
||||
"scope": "city"
|
||||
},
|
||||
{
|
||||
"type": "city_bombard_strength",
|
||||
"value": 12
|
||||
},
|
||||
{
|
||||
"type": "city_bombard_range",
|
||||
"value": 2
|
||||
}
|
||||
],
|
||||
"requires_building": "walls",
|
||||
|
|
|
|||
|
|
@ -143,6 +143,16 @@ func _apply_resolve_results(
|
|||
if infusion_system != null:
|
||||
infusion_system.on_unit_killed(defender)
|
||||
|
||||
# City bombard retaliation: city deals damage to melee attackers
|
||||
if defender is CityScript and not attacker_killed:
|
||||
var bombard_dmg: int = _compute_city_bombard(defender, attacker)
|
||||
if bombard_dmg > 0:
|
||||
attacker.hp = maxi(0, attacker.hp - bombard_dmg)
|
||||
result["city_bombard_damage"] = bombard_dmg
|
||||
if attacker.hp <= 0:
|
||||
attacker_killed = true
|
||||
CombatUtilsScript.handle_unit_death(attacker, defender, all_units)
|
||||
|
||||
if result.get("captured", false) and defender is CityScript:
|
||||
CombatUtilsScript.capture_city(defender, attacker, defender.owner, all_units)
|
||||
|
||||
|
|
@ -307,6 +317,24 @@ func _count_flanking(attacker: RefCounted, near_def: Array) -> int:
|
|||
|
||||
|
||||
## Terrain defense bonus from the tile the unit stands on.
|
||||
## Compute city bombard retaliation damage.
|
||||
## City strength = population * 3 + building bombard bonuses.
|
||||
## Damage = 15 * (city_strength / attacker_strength), clamped [5, 30].
|
||||
func _compute_city_bombard(city: RefCounted, attacker: RefCounted) -> int:
|
||||
var city_str: float = float(city.population) * 3.0
|
||||
# Add castle bombard bonus if present
|
||||
if city.has_building("castle"):
|
||||
var bdata: Dictionary = DataLoader.get_building("castle")
|
||||
for effect: Dictionary in bdata.get("effects", []):
|
||||
if effect.get("type", "") == "city_bombard_strength":
|
||||
city_str += float(effect.get("value", 0))
|
||||
if city_str <= 0.0:
|
||||
return 0
|
||||
var atk_str: float = float(attacker.attack) if attacker is UnitScript else 10.0
|
||||
var ratio: float = city_str / maxf(atk_str, 1.0)
|
||||
return clampi(roundi(15.0 * ratio), 5, 30)
|
||||
|
||||
|
||||
func _get_terrain_defense(unit: RefCounted, game_map: RefCounted) -> int:
|
||||
if game_map == null:
|
||||
return 0
|
||||
|
|
|
|||
|
|
@ -88,6 +88,7 @@ func _process_production(player: RefCounted) -> void: # Player
|
|||
EventBus.city_unit_completed.emit(city_ref, unit)
|
||||
elif item_type == "building":
|
||||
c.add_building(item_id)
|
||||
_apply_building_bonuses(c, item_id)
|
||||
EventBus.city_building_completed.emit(city_ref, item_id)
|
||||
elif item_type == "item":
|
||||
var i_data: Dictionary = DataLoader.get_item(item_id)
|
||||
|
|
@ -211,6 +212,17 @@ func _process_growth(player: RefCounted) -> void: # Player
|
|||
c.process_growth(tile_json)
|
||||
|
||||
|
||||
func _apply_building_bonuses(city: CityScript, building_id: String) -> void:
|
||||
var bdata: Dictionary = DataLoader.get_building(building_id)
|
||||
var effects: Array = bdata.get("effects", [])
|
||||
for effect: Dictionary in effects:
|
||||
var etype: String = effect.get("type", "")
|
||||
var value: int = int(effect.get("value", 0))
|
||||
if etype == "hp_bonus" and value > 0:
|
||||
city.set_max_hp(city.max_hp + value)
|
||||
city.heal(value)
|
||||
|
||||
|
||||
func _process_city_healing(player: RefCounted) -> void:
|
||||
for city_ref: Variant in player.cities:
|
||||
if city_ref is CityScript:
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue