175 lines
6.5 KiB
GDScript
175 lines
6.5 KiB
GDScript
extends SceneTree
|
|
|
|
const BattleStateScript := preload("res://scripts/core/battle_state.gd")
|
|
const BattleSceneScript := preload("res://scripts/scenes/battle_scene.gd")
|
|
|
|
|
|
func _init() -> void:
|
|
var failures: Array[String] = []
|
|
_check_directional_damage_bonuses(failures)
|
|
_check_attack_updates_facing(failures)
|
|
_check_move_cancel_restores_facing(failures)
|
|
_check_scene_pixel_facing(failures)
|
|
|
|
if failures.is_empty():
|
|
print("directional combat smoke ok")
|
|
quit(0)
|
|
return
|
|
|
|
for failure in failures:
|
|
push_error(failure)
|
|
quit(1)
|
|
|
|
|
|
func _check_directional_damage_bonuses(failures: Array[String]) -> void:
|
|
var state = _loaded_state(failures, "directional damage")
|
|
if state == null:
|
|
return
|
|
var attacker: Dictionary = state.get_unit("cao_cao")
|
|
var target: Dictionary = state.get_unit("yellow_turban_1")
|
|
if attacker.is_empty() or target.is_empty():
|
|
failures.append("missing units for directional damage")
|
|
return
|
|
|
|
_configure_duel_units(attacker, target)
|
|
target["pos"] = Vector2i(5, 9)
|
|
target["facing_x"] = 1
|
|
|
|
attacker["pos"] = Vector2i(6, 9)
|
|
var front_damage: int = state.calculate_damage(attacker, target)
|
|
var front_info: Dictionary = state.get_directional_attack_info(attacker, target)
|
|
attacker["pos"] = Vector2i(5, 8)
|
|
var side_damage: int = state.calculate_damage(attacker, target)
|
|
var side_info: Dictionary = state.get_directional_attack_info(attacker, target)
|
|
attacker["pos"] = Vector2i(4, 9)
|
|
var back_damage: int = state.calculate_damage(attacker, target)
|
|
var back_info: Dictionary = state.get_directional_attack_info(attacker, target)
|
|
|
|
if str(front_info.get("kind", "")) != BattleStateScript.DIRECTIONAL_FRONT_ATTACK:
|
|
failures.append("front attack should be classified as front: %s" % str(front_info))
|
|
if str(side_info.get("kind", "")) != BattleStateScript.DIRECTIONAL_SIDE_ATTACK:
|
|
failures.append("same-file attack should be classified as side: %s" % str(side_info))
|
|
if str(back_info.get("kind", "")) != BattleStateScript.DIRECTIONAL_BACK_ATTACK:
|
|
failures.append("rear attack should be classified as back: %s" % str(back_info))
|
|
if side_damage != front_damage + BattleStateScript.DIRECTIONAL_SIDE_DAMAGE_BONUS:
|
|
failures.append("side damage should add side bonus: front %d side %d" % [front_damage, side_damage])
|
|
if back_damage != front_damage + BattleStateScript.DIRECTIONAL_BACK_DAMAGE_BONUS:
|
|
failures.append("back damage should add back bonus: front %d back %d" % [front_damage, back_damage])
|
|
|
|
var preview: Dictionary = state.get_damage_preview("cao_cao", "yellow_turban_1")
|
|
if str(preview.get("directional_attack", "")) != BattleStateScript.DIRECTIONAL_BACK_ATTACK:
|
|
failures.append("damage preview should expose rear attack: %s" % str(preview))
|
|
if int(preview.get("directional_bonus", 0)) != BattleStateScript.DIRECTIONAL_BACK_DAMAGE_BONUS:
|
|
failures.append("damage preview should expose rear bonus: %s" % str(preview))
|
|
|
|
|
|
func _check_attack_updates_facing(failures: Array[String]) -> void:
|
|
var state = _loaded_state(failures, "attack facing")
|
|
if state == null:
|
|
return
|
|
var attacker: Dictionary = state.get_unit("cao_cao")
|
|
var target: Dictionary = state.get_unit("yellow_turban_1")
|
|
if attacker.is_empty() or target.is_empty():
|
|
failures.append("missing units for attack facing")
|
|
return
|
|
|
|
_configure_duel_units(attacker, target)
|
|
attacker["pos"] = Vector2i(4, 9)
|
|
attacker["facing_x"] = -1
|
|
target["pos"] = Vector2i(5, 9)
|
|
target["facing_x"] = 1
|
|
target["controllable"] = false
|
|
var expected_damage: int = state.calculate_damage(attacker, target)
|
|
var start_hp: int = int(target.get("hp", 0))
|
|
|
|
if not state.select_unit("cao_cao"):
|
|
failures.append("could not select Cao Cao for attack facing")
|
|
return
|
|
if not state.try_attack_selected("yellow_turban_1"):
|
|
failures.append("rear attack should execute")
|
|
return
|
|
|
|
if int(target.get("hp", 0)) != start_hp - expected_damage:
|
|
failures.append("rear attack damage mismatch: expected %d got %d" % [expected_damage, start_hp - int(target.get("hp", 0))])
|
|
if state.unit_facing_x(attacker) != 1:
|
|
failures.append("attacker should face target after attack")
|
|
|
|
|
|
func _check_move_cancel_restores_facing(failures: Array[String]) -> void:
|
|
var state = _loaded_state(failures, "move cancel facing")
|
|
if state == null:
|
|
return
|
|
var unit: Dictionary = state.get_unit("cao_cao")
|
|
if unit.is_empty():
|
|
failures.append("missing Cao Cao for move cancel facing")
|
|
return
|
|
|
|
unit["facing_x"] = -1
|
|
if not state.select_unit("cao_cao"):
|
|
failures.append("could not select Cao Cao for move cancel facing")
|
|
return
|
|
if not state.try_move_selected(Vector2i(2, 3), true):
|
|
failures.append("pending move should succeed for facing cancel")
|
|
return
|
|
if state.unit_facing_x(unit) != 1:
|
|
failures.append("pending move to the east should face right")
|
|
if not state.cancel_pending_move("cao_cao", Vector2i(1, 3), Vector2i(2, 3)):
|
|
failures.append("pending move cancel should succeed for facing restore")
|
|
return
|
|
if state.unit_facing_x(unit) != -1:
|
|
failures.append("canceling a pending move should restore previous facing")
|
|
|
|
|
|
func _check_scene_pixel_facing(failures: Array[String]) -> void:
|
|
var scene = BattleSceneScript.new()
|
|
if not scene.state.load_battle("res://data/scenarios/001_yellow_turbans.json"):
|
|
failures.append("could not load opening battle for pixel facing")
|
|
scene.free()
|
|
return
|
|
|
|
var player: Dictionary = scene.state.get_unit("cao_cao")
|
|
var enemy: Dictionary = scene.state.get_unit("yellow_turban_1")
|
|
player["facing_x"] = -1
|
|
enemy["facing_x"] = 1
|
|
if scene._unit_pixel_facing(player) != -1:
|
|
failures.append("pixel facing should use player facing_x")
|
|
if scene._unit_pixel_facing(enemy) != 1:
|
|
failures.append("pixel facing should use enemy facing_x")
|
|
|
|
scene.unit_action_motion_by_unit["cao_cao"] = {
|
|
"from": Vector2i(4, 3),
|
|
"to": Vector2i(3, 3)
|
|
}
|
|
if scene._unit_pixel_facing(player) != -1:
|
|
failures.append("pixel facing should prefer active action motion")
|
|
scene.free()
|
|
|
|
|
|
func _configure_duel_units(attacker: Dictionary, target: Dictionary) -> void:
|
|
attacker["atk"] = 30
|
|
attacker["agi"] = 50
|
|
attacker["hp"] = 80
|
|
attacker["max_hp"] = 80
|
|
attacker["range"] = 1
|
|
attacker["min_range"] = 1
|
|
attacker["acted"] = false
|
|
attacker["moved"] = false
|
|
target["def"] = 10
|
|
target["atk"] = 1
|
|
target["agi"] = 0
|
|
target["hp"] = 80
|
|
target["max_hp"] = 80
|
|
target["range"] = 1
|
|
target["min_range"] = 1
|
|
target["alive"] = true
|
|
target["acted"] = false
|
|
target["moved"] = false
|
|
|
|
|
|
func _loaded_state(failures: Array[String], label: String):
|
|
var state = BattleStateScript.new()
|
|
if not state.load_battle("res://data/scenarios/001_yellow_turbans.json"):
|
|
failures.append("%s could not load opening battle" % label)
|
|
return null
|
|
return state
|