Add poison damage over time tactics
This commit is contained in:
@@ -843,7 +843,10 @@ func _skill_has_support_effects(skill: Dictionary) -> bool:
|
||||
for effect in skill.get("effects", []):
|
||||
if typeof(effect) != TYPE_DICTIONARY:
|
||||
continue
|
||||
if str(effect.get("type", "")) == "stat_bonus" and _is_supported_status_stat(str(effect.get("stat", ""))) and int(effect.get("amount", 0)) != 0:
|
||||
var effect_type := str(effect.get("type", ""))
|
||||
if effect_type == "stat_bonus" and _is_supported_status_stat(str(effect.get("stat", ""))) and int(effect.get("amount", 0)) != 0:
|
||||
return true
|
||||
if effect_type == "damage_over_time" and int(effect.get("amount", 0)) > 0:
|
||||
return true
|
||||
return false
|
||||
|
||||
@@ -855,19 +858,33 @@ func _resolve_skill_support(caster: Dictionary, target: Dictionary, skill_id: St
|
||||
for effect in skill.get("effects", []):
|
||||
if typeof(effect) != TYPE_DICTIONARY:
|
||||
continue
|
||||
if str(effect.get("type", "")) != "stat_bonus":
|
||||
continue
|
||||
var stat := str(effect.get("stat", ""))
|
||||
var amount := int(effect.get("amount", 0))
|
||||
if not _is_supported_status_stat(stat) or amount == 0:
|
||||
continue
|
||||
status_effects.append({
|
||||
"source_skill": skill_id,
|
||||
"name": str(skill.get("name", skill_id)),
|
||||
"stat": stat,
|
||||
"amount": amount,
|
||||
"remaining_phases": max(1, int(effect.get("duration_turns", 1)))
|
||||
})
|
||||
var effect_type := str(effect.get("type", ""))
|
||||
if effect_type == "stat_bonus":
|
||||
var stat := str(effect.get("stat", ""))
|
||||
var amount := int(effect.get("amount", 0))
|
||||
if not _is_supported_status_stat(stat) or amount == 0:
|
||||
continue
|
||||
status_effects.append({
|
||||
"source_skill": skill_id,
|
||||
"name": str(skill.get("name", skill_id)),
|
||||
"type": effect_type,
|
||||
"stat": stat,
|
||||
"amount": amount,
|
||||
"remaining_phases": max(1, int(effect.get("duration_turns", 1)))
|
||||
})
|
||||
elif effect_type == "damage_over_time":
|
||||
var damage: int = max(0, int(effect.get("amount", 0)))
|
||||
if damage <= 0:
|
||||
continue
|
||||
var status := str(effect.get("status", "poison"))
|
||||
status_effects.append({
|
||||
"source_skill": skill_id,
|
||||
"name": str(skill.get("name", skill_id)),
|
||||
"type": effect_type,
|
||||
"status": status,
|
||||
"amount": damage,
|
||||
"remaining_phases": max(1, int(effect.get("duration_turns", 1)))
|
||||
})
|
||||
target["status_effects"] = status_effects
|
||||
_emit_log("%s casts %s on %s. %s" % [
|
||||
caster["name"],
|
||||
@@ -900,16 +917,22 @@ func _format_support_effects(skill: Dictionary) -> String:
|
||||
for effect in skill.get("effects", []):
|
||||
if typeof(effect) != TYPE_DICTIONARY:
|
||||
continue
|
||||
if str(effect.get("type", "")) != "stat_bonus":
|
||||
continue
|
||||
var stat := str(effect.get("stat", ""))
|
||||
var amount := int(effect.get("amount", 0))
|
||||
if not _is_supported_status_stat(stat) or amount == 0:
|
||||
continue
|
||||
var sign := "+" if amount > 0 else ""
|
||||
var effect_type := str(effect.get("type", ""))
|
||||
var duration: int = maxi(1, int(effect.get("duration_turns", 1)))
|
||||
var duration_text := "next phase" if duration == 1 else "%d phases" % duration
|
||||
parts.append("%s %s%d until %s" % [stat.to_upper(), sign, amount, duration_text])
|
||||
if effect_type == "stat_bonus":
|
||||
var stat := str(effect.get("stat", ""))
|
||||
var amount := int(effect.get("amount", 0))
|
||||
if not _is_supported_status_stat(stat) or amount == 0:
|
||||
continue
|
||||
var sign := "+" if amount > 0 else ""
|
||||
parts.append("%s %s%d until %s" % [stat.to_upper(), sign, amount, duration_text])
|
||||
elif effect_type == "damage_over_time":
|
||||
var damage := int(effect.get("amount", 0))
|
||||
if damage <= 0:
|
||||
continue
|
||||
var status_name := _status_display_name(str(effect.get("status", "poison")))
|
||||
parts.append("%s -%d HP for %s" % [status_name, damage, duration_text])
|
||||
if parts.is_empty():
|
||||
return "No support effect."
|
||||
return _join_strings(parts, ", ")
|
||||
@@ -920,20 +943,29 @@ func _format_support_popup(skill: Dictionary) -> String:
|
||||
for effect in skill.get("effects", []):
|
||||
if typeof(effect) != TYPE_DICTIONARY:
|
||||
continue
|
||||
if str(effect.get("type", "")) != "stat_bonus":
|
||||
continue
|
||||
var stat := str(effect.get("stat", ""))
|
||||
var amount := int(effect.get("amount", 0))
|
||||
if not _is_supported_status_stat(stat) or amount == 0:
|
||||
continue
|
||||
var sign := "+" if amount > 0 else ""
|
||||
parts.append("%s %s%d" % [stat.to_upper(), sign, amount])
|
||||
var effect_type := str(effect.get("type", ""))
|
||||
if effect_type == "stat_bonus":
|
||||
var stat := str(effect.get("stat", ""))
|
||||
var amount := int(effect.get("amount", 0))
|
||||
if not _is_supported_status_stat(stat) or amount == 0:
|
||||
continue
|
||||
var sign := "+" if amount > 0 else ""
|
||||
parts.append("%s %s%d" % [stat.to_upper(), sign, amount])
|
||||
elif effect_type == "damage_over_time":
|
||||
var damage := int(effect.get("amount", 0))
|
||||
if damage > 0:
|
||||
parts.append(_status_display_name(str(effect.get("status", "poison"))).to_upper())
|
||||
if parts.is_empty():
|
||||
return "Support"
|
||||
return _join_strings(parts, ", ")
|
||||
|
||||
|
||||
func _support_feedback_kind(skill: Dictionary) -> String:
|
||||
for effect in skill.get("effects", []):
|
||||
if typeof(effect) != TYPE_DICTIONARY:
|
||||
continue
|
||||
if str(effect.get("type", "")) == "damage_over_time" and int(effect.get("amount", 0)) > 0:
|
||||
return str(effect.get("status", "poison"))
|
||||
for effect in skill.get("effects", []):
|
||||
if typeof(effect) != TYPE_DICTIONARY:
|
||||
continue
|
||||
@@ -945,8 +977,13 @@ func _support_feedback_kind(skill: Dictionary) -> String:
|
||||
func _format_active_status_effects(unit: Dictionary) -> String:
|
||||
var parts := []
|
||||
for effect in _active_status_effects(unit):
|
||||
var stat := str(effect.get("stat", ""))
|
||||
var effect_type := str(effect.get("type", "stat_bonus"))
|
||||
var amount := int(effect.get("amount", 0))
|
||||
if effect_type == "damage_over_time":
|
||||
if amount > 0:
|
||||
parts.append("%s -%d HP" % [_status_display_name(str(effect.get("status", "poison"))), amount])
|
||||
continue
|
||||
var stat := str(effect.get("stat", ""))
|
||||
if not _is_supported_status_stat(stat) or amount == 0:
|
||||
continue
|
||||
var sign := "+" if amount > 0 else ""
|
||||
@@ -954,6 +991,13 @@ func _format_active_status_effects(unit: Dictionary) -> String:
|
||||
return _join_strings(parts, ", ")
|
||||
|
||||
|
||||
func _status_display_name(status: String) -> String:
|
||||
var normalized := status.strip_edges()
|
||||
if normalized.is_empty():
|
||||
return "Status"
|
||||
return normalized.replace("_", " ").capitalize()
|
||||
|
||||
|
||||
func _is_supported_status_stat(stat: String) -> bool:
|
||||
return stat == "atk" or stat == "def" or stat == "int" or stat == "agi"
|
||||
|
||||
@@ -979,12 +1023,15 @@ func end_player_turn() -> void:
|
||||
|
||||
selected_unit_id = ""
|
||||
current_team = TEAM_ENEMY
|
||||
_reset_team_actions(TEAM_ENEMY)
|
||||
_emit_log("Enemy phase begins.")
|
||||
_run_events("turn_start", current_team, turn_number)
|
||||
_reset_team_actions(TEAM_ENEMY)
|
||||
if battle_status == STATUS_ACTIVE:
|
||||
_run_events("turn_start", current_team, turn_number)
|
||||
_check_battle_status()
|
||||
if battle_status == STATUS_ACTIVE:
|
||||
_run_enemy_turn()
|
||||
else:
|
||||
_notify_changed()
|
||||
|
||||
|
||||
func get_unit(unit_id: String) -> Dictionary:
|
||||
@@ -1853,9 +1900,10 @@ func _run_enemy_turn() -> void:
|
||||
if battle_status == STATUS_ACTIVE:
|
||||
turn_number += 1
|
||||
current_team = TEAM_PLAYER
|
||||
_reset_team_actions(TEAM_PLAYER)
|
||||
_emit_log("Player phase begins.")
|
||||
_run_events("turn_start", current_team, turn_number)
|
||||
_reset_team_actions(TEAM_PLAYER)
|
||||
if battle_status == STATUS_ACTIVE:
|
||||
_run_events("turn_start", current_team, turn_number)
|
||||
_check_battle_status()
|
||||
|
||||
_notify_changed()
|
||||
@@ -2012,18 +2060,25 @@ func _support_ai_score(caster: Dictionary, target: Dictionary, skill: Dictionary
|
||||
for effect in skill.get("effects", []):
|
||||
if typeof(effect) != TYPE_DICTIONARY:
|
||||
continue
|
||||
if str(effect.get("type", "")) != "stat_bonus":
|
||||
continue
|
||||
var stat := str(effect.get("stat", ""))
|
||||
var amount := int(effect.get("amount", 0))
|
||||
if not _is_supported_status_stat(stat) or amount == 0:
|
||||
continue
|
||||
if amount > 0 and target_is_ally:
|
||||
score += amount * _support_ai_stat_weight(stat)
|
||||
if target.get("id", "") == caster.get("id", ""):
|
||||
score += 3
|
||||
elif amount < 0 and not target_is_ally:
|
||||
score += absi(amount) * _support_ai_stat_weight(stat)
|
||||
var effect_type := str(effect.get("type", ""))
|
||||
if effect_type == "stat_bonus":
|
||||
var stat := str(effect.get("stat", ""))
|
||||
var amount := int(effect.get("amount", 0))
|
||||
if not _is_supported_status_stat(stat) or amount == 0:
|
||||
continue
|
||||
if amount > 0 and target_is_ally:
|
||||
score += amount * _support_ai_stat_weight(stat)
|
||||
if target.get("id", "") == caster.get("id", ""):
|
||||
score += 3
|
||||
elif amount < 0 and not target_is_ally:
|
||||
score += absi(amount) * _support_ai_stat_weight(stat)
|
||||
score += _ai_target_priority_score(target)
|
||||
elif effect_type == "damage_over_time" and not target_is_ally:
|
||||
var damage := int(effect.get("amount", 0))
|
||||
if damage <= 0:
|
||||
continue
|
||||
var duration: int = max(1, int(effect.get("duration_turns", 1)))
|
||||
score += damage * duration * 5
|
||||
score += _ai_target_priority_score(target)
|
||||
return score
|
||||
|
||||
@@ -2681,6 +2736,10 @@ func _spawn_event_deployment(deployment) -> bool:
|
||||
|
||||
|
||||
func _reset_team_actions(team: String) -> void:
|
||||
_apply_phase_status_effects_for_team(team)
|
||||
_check_battle_status()
|
||||
if battle_status != STATUS_ACTIVE:
|
||||
return
|
||||
_expire_status_effects_for_team(team)
|
||||
for unit in units:
|
||||
if unit.get("team", "") == team and unit.get("alive", false):
|
||||
@@ -2688,6 +2747,27 @@ func _reset_team_actions(team: String) -> void:
|
||||
unit["moved"] = false
|
||||
|
||||
|
||||
func _apply_phase_status_effects_for_team(team: String) -> void:
|
||||
for unit in units:
|
||||
if unit.get("team", "") != team or not unit.get("alive", false):
|
||||
continue
|
||||
for effect in _active_status_effects(unit):
|
||||
if str(effect.get("type", "")) != "damage_over_time":
|
||||
continue
|
||||
var damage: int = max(0, int(effect.get("amount", 0)))
|
||||
if damage <= 0:
|
||||
continue
|
||||
unit["hp"] = max(0, int(unit.get("hp", 0)) - damage)
|
||||
var status := str(effect.get("status", "poison"))
|
||||
_emit_log("%s suffers %d %s damage." % [unit["name"], damage, status.replace("_", " ")])
|
||||
_emit_combat_feedback(unit, "-%d" % damage, status)
|
||||
if int(unit.get("hp", 0)) <= 0:
|
||||
unit["alive"] = false
|
||||
_emit_log("%s is defeated by %s." % [unit["name"], status.replace("_", " ")])
|
||||
_emit_combat_feedback(unit, "DOWN", "defeat")
|
||||
break
|
||||
|
||||
|
||||
func _expire_status_effects_for_team(team: String) -> void:
|
||||
for unit in units:
|
||||
if unit.get("team", "") != team:
|
||||
|
||||
Reference in New Issue
Block a user