saumwacht-godot/scripts/combat/ability_component.gd
Martin Stickelbroeck c2b447610c Baseline: Saumwacht 0.15.0 (Phase 20A) vor dem Umbau zu Champions of Luneraria
Unveränderter Ausgangsstand inklusive Masterprompt und Konzeptbildern
unter assets/. .godot/ und docs/screenshots/ bleiben laut .gitignore
unversioniert; letztere sind unter ../backups gesichert.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-27 20:00:36 +02:00

121 lines
5 KiB
GDScript

extends RefCounted
## Shared content abilities: Glutpakt, ten neutral archetypes and five sends.
var ready_time: float = 6.0
var _opening_pose_pending: bool = false
func start(unit) -> void:
match str(unit.data.ability):
"g04": _opening_pose_pending = true
"w03", "w04": ready_time = 5.0
"w09", "w10": ready_time = 4.0
"s02": unit.grant_shield(unit.max_hp * 0.15)
"e08": unit.grant_shield(unit.max_hp * 0.20)
func step(unit, combatants: Array, _delta: float) -> void:
# Formation previews also call start(); defer the passive pose to combat.
if _opening_pose_pending and unit.combat_time > 0.0:
_opening_pose_pending = false
unit.play_ability()
if unit.combat_time + 0.000001 < ready_time:
return
var ability = str(unit.data.ability)
var success = false
match ability:
"w03", "w04":
if unit.hp < unit.max_hp:
unit.heal(100.0 if ability == "w04" else 35.0)
success = true
"w09", "w10":
var upgraded = ability == "w10"
var allies = nearby(unit, combatants, true, unit.position, 4.5 if upgraded else 4.0, 3 if upgraded else 1, null, true)
for ally in allies:
if ally.hp < ally.max_hp:
ally.heal(80.0 if upgraded else 65.0)
ally.ability_flash = 0.5
success = true
"g09", "g10":
var upgraded = ability == "g10"
var allies = nearby(unit, combatants, true, unit.position, 4.0 if upgraded else 3.0, 4 if upgraded else 3, null, true)
for ally in allies:
ally.apply_effect("hearth_warmth", "damage", 0.15 if upgraded else 0.10, 4.0)
if upgraded:
ally.heal(45.0)
success = not allies.is_empty()
"e06":
var allies = nearby(unit, combatants, true, unit.position, 4.0, 100, null, true)
for ally in allies:
if ally.hp < ally.max_hp:
# Derive the HP-wave factor exactly, not from rounded max HP.
ally.heal(30.0 * float(unit.data.get("hp_factor", 1.0)))
success = true
break
"s03", "e09":
var boss = ability == "e09"
var victims = nearby(unit, combatants, false, unit.position, 6.0 if boss else 3.0, 3 if boss else 4)
for victim in victims:
victim.apply_effect("dissonance", "attack_slow", 0.15, 3.0)
success = not victims.is_empty()
"s05":
var victims = nearby(unit, combatants, false, unit.position, 5.0, 1)
for victim in victims:
victim.apply_effect("rift_wound", "healing_reduction", 0.30, 4.0)
success = not victims.is_empty()
if success:
ready_time = unit.combat_time + (5.0 if ability in ["w03", "w04"] else (4.0 if ability in ["w09", "w10"] else 6.0))
unit.play_ability()
func on_hit(unit, primary, combatants: Array, hit_count: int) -> void:
var ability = str(unit.data.ability)
match ability:
"w01", "w02":
if hit_count % 3 == 0 and primary.alive:
primary.apply_effect("root_grasp", "movement_slow", 0.30 if ability == "w02" else 0.20, 3.0 if ability == "w02" else 2.0)
if ability == "w02":
primary.apply_effect("root_grasp_attack", "attack_slow", 0.10, 3.0)
"w05", "w06":
if hit_count % 4 == 0 and primary.alive:
primary.apply_dot("seed_burn", 10.0 if ability == "w06" else 3.0, 4.0, int(unit.data.attack_type))
"w07", "w08":
if hit_count % 3 == 0:
splash(unit, primary, combatants, 0.80 if ability == "w08" else 0.60, 2.5 if ability == "w08" else 2.0, 4 if ability == "w08" else 3)
"g01", "g02":
if hit_count % 3 == 0 and primary.alive:
primary.apply_dot("burn", 12.0 if ability == "g02" else 4.0, 3.0, int(unit.data.attack_type))
"g07": splash(unit, primary, combatants, 0.30, 1.5, 3)
"g08": splash(unit, primary, combatants, 0.40, 2.0, 4)
"e05":
if hit_count % 4 == 0: splash(unit, primary, combatants, 0.40, 1.5, 2)
"e07":
if hit_count % 4 == 0: splash(unit, primary, combatants, 0.60, 2.0, 3)
"e10":
if hit_count % 4 == 0: splash(unit, primary, combatants, 0.60, 2.5, 4)
"s04":
if hit_count % 4 == 0: splash(unit, primary, combatants, 0.50, 2.0, 3)
func splash(unit, primary, combatants: Array, factor: float, radius: float, maximum: int) -> void:
for victim in nearby(unit, combatants, false, primary.position, radius, maximum, primary):
victim.receive_damage(float(unit.data.damage) * factor, int(unit.data.attack_type))
func nearby(unit, combatants: Array, allied: bool, center: Vector3, radius: float, maximum: int, excluded = null, health_priority: bool = false) -> Array:
var result: Array = []
for other in combatants:
if not other.alive or other.team_id != unit.team_id or other == excluded:
continue
if (other.is_attacker == unit.is_attacker) != allied:
continue
if horizontal_distance(other.position, center) <= radius + 0.000001:
result.append(other)
result.sort_custom(func(a, b):
if health_priority and not is_equal_approx(a.hp / a.max_hp, b.hp / b.max_hp):
return a.hp / a.max_hp < b.hp / b.max_hp
var ad = horizontal_distance(a.position, center)
var bd = horizontal_distance(b.position, center)
return a.uid < b.uid if is_equal_approx(ad, bd) else ad < bd
)
if result.size() > maximum:
result.resize(maximum)
return result
func horizontal_distance(a: Vector3, b: Vector3) -> float:
return Vector2(a.x, a.z).distance_to(Vector2(b.x, b.z))