forked from Saplings-Projects/1M_sub
-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Issue-35: Death handling and combat end (Saplings-Projects#60)
* Handle player killing enemies * Refactor enemy attack loop - Splits enemy attack loop into two, where first actions are generated in the first loop and then executed in the second one - Introduced EnemyAction class to use as structure holding action details and necessary checks * Emit signal on combat end * Restart combat when battle ends - Added signal for end of battle into phase manager - Battler emits signal when it detects end of battle - Added scene controller to switch to new testingscene after combat - Changed EnemyAction target checking to account for the possibility of it being dead but not freed yet * Remove call of death handling from enemy actions - removed explicit call of handling enemy deaths as it is already being called from inside because of the signal from on_card_play * Scene mapping dictionary * Add death handling tests * Assert enemy deletions * Refactor phase changing in SceneController --------- Co-authored-by: Tomzkk <[email protected]>
- Loading branch information
Showing
9 changed files
with
281 additions
and
14 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
extends Node | ||
|
||
|
||
var current_scene: Node = null | ||
|
||
var SCENE_MAPPING: Dictionary = { | ||
Enums.CombatResult.VICTORY: "res://#Scenes/TestingScene.tscn", | ||
Enums.CombatResult.DEFEAT: "res://#Scenes/TestingScene.tscn", | ||
} | ||
|
||
func _ready(): | ||
var root: Window = get_tree().root | ||
current_scene = root.get_child(root.get_child_count() - 1) | ||
PhaseManager.on_combat_end.connect(_combat_end_change_scene) | ||
|
||
|
||
func goto_scene(path: String) -> void: | ||
# This function will usually be called from a signal callback, | ||
# or some other function in the current scene. | ||
# Deleting the current scene at this point is | ||
# a bad idea, because it may still be executing code. | ||
# This will result in a crash or unexpected behavior. | ||
|
||
# The solution is to defer the load to a later time, when | ||
# we can be sure that no code from the current scene is running: | ||
|
||
call_deferred("_deferred_goto_scene", path) | ||
|
||
|
||
func _deferred_goto_scene(path: String) -> void: | ||
# It is now safe to remove the current scene | ||
current_scene.free() | ||
|
||
# Load the new scene. | ||
var new_scene: Resource = ResourceLoader.load(path) | ||
|
||
# Instance the new scene. | ||
current_scene = new_scene.instantiate() | ||
|
||
# Add it to the active scene, as child of root. | ||
get_tree().root.add_child(current_scene) | ||
|
||
# Optionally, to make it compatible with the SceneTree.change_scene_to_file() API. | ||
get_tree().current_scene = current_scene | ||
|
||
|
||
func _combat_end_change_scene(combat_result: Enums.CombatResult) -> void: | ||
PhaseManager.call_deferred("set_phase", Enums.Phase.SCENE_END) | ||
if combat_result == Enums.CombatResult.DEFEAT: | ||
print('Defeat') | ||
goto_scene(SCENE_MAPPING[Enums.CombatResult.DEFEAT]) | ||
elif combat_result == Enums.CombatResult.VICTORY: | ||
print("Victory") | ||
goto_scene(SCENE_MAPPING[Enums.CombatResult.VICTORY]) | ||
PhaseManager.call_deferred("initialize_game") |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,29 @@ | ||
extends RefCounted | ||
|
||
class_name EnemyAction | ||
|
||
var caster: Entity | ||
var action: CardBase | ||
var target_list: Array[Entity] | ||
|
||
func _init(_caster: Entity, _action: CardBase, _target_list: Array[Entity]): | ||
self.caster = _caster | ||
self.action = _action | ||
self.target_list = _target_list | ||
|
||
# function to attack that plays card | ||
func execute() -> void: | ||
if not is_instance_valid(caster): | ||
print("Caster died, skipping action") | ||
return | ||
|
||
# Simplified for now, will need refactor once we have advanced enemy actions | ||
if not is_instance_valid(target_list[0]) or target_list[0].get_health_component().current_health == 0: | ||
print("Target died, skipping action") | ||
return | ||
|
||
var can_execute: bool = action.can_play_card(caster, target_list[0]) | ||
assert(can_execute == true, "Enemy failed to attack.") | ||
|
||
if can_execute: | ||
action.on_card_play(caster, target_list) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,137 @@ | ||
extends TestBase | ||
## Tests for things relating to death handling things | ||
|
||
|
||
# @Override | ||
func before_each(): | ||
super() | ||
# disconnecting signal as _combat_end_change_scene causes scene to be (re)loaded | ||
# which if called from test actually starts the game and the test doesnt end | ||
watch_signals(PhaseManager) | ||
if PhaseManager.is_connected("on_combat_end", SceneController._combat_end_change_scene): | ||
PhaseManager.disconnect("on_combat_end", SceneController._combat_end_change_scene) | ||
|
||
|
||
# No typing for argument as if it's already been freed it doesn't have one | ||
func _free_if_valid(node): | ||
if is_instance_valid(node): | ||
node.free() | ||
|
||
# @Override | ||
func after_each(): | ||
_free_if_valid(_player) | ||
_free_if_valid(_enemy) | ||
_free_if_valid(_enemy_2) | ||
_battler.free() | ||
assert_no_new_orphans("Orphans still exist, please free up test resources.") | ||
|
||
|
||
func test_player_death_during_enemy_turn(): | ||
_player.get_health_component()._set_health(1.0) | ||
_battler._on_enemy_start_turn() | ||
assert_eq(_player.get_health_component().current_health, 0.) | ||
assert_signal_emitted_with_parameters(PhaseManager, "on_combat_end", [Enums.CombatResult.DEFEAT]) | ||
|
||
|
||
func test_check_and_handle_battle_end_player_death(): | ||
_player_health_component._set_health(0.) | ||
|
||
_battler._check_and_handle_battle_end() | ||
|
||
assert_signal_emitted_with_parameters(PhaseManager, "on_combat_end", [Enums.CombatResult.DEFEAT]) | ||
|
||
|
||
func test_check_and_handle_battle_end_enemy_death(): | ||
_battler._enemy_list = [] | ||
|
||
_battler._check_and_handle_battle_end() | ||
|
||
assert_signal_emitted_with_parameters(PhaseManager, "on_combat_end", [Enums.CombatResult.VICTORY]) | ||
|
||
|
||
func test_handle_enemy_deaths_none(): | ||
assert_eq(_enemy_list.size(), 2) | ||
_battler._handle_enemy_deaths() | ||
assert_eq(_enemy_list.size(), 2) | ||
|
||
|
||
func test_handle_enemy_deaths_single(): | ||
_enemy_health_component._set_health(0.) | ||
|
||
assert_eq(_enemy_list.size(), 2) | ||
_battler._handle_enemy_deaths() | ||
assert_eq(_enemy_list.size(), 1) | ||
assert_true(_enemy.is_queued_for_deletion()) | ||
await get_tree().process_frame | ||
assert_false(is_instance_valid(_enemy)) | ||
|
||
|
||
func test_handle_enemy_deaths_all(): | ||
_enemy_health_component._set_health(0.) | ||
_enemy_2_health_component._set_health(0.) | ||
|
||
assert_eq(_enemy_list.size(), 2) | ||
_battler._handle_enemy_deaths() | ||
assert_eq(_enemy_list.size(), 0) | ||
assert_true(_enemy.is_queued_for_deletion()) | ||
assert_true(_enemy_2.is_queued_for_deletion()) | ||
await get_tree().process_frame | ||
assert_false(is_instance_valid(_enemy)) | ||
assert_false(is_instance_valid(_enemy_2)) | ||
|
||
|
||
func test_enemy_death_to_player_attack(): | ||
_enemy_health_component._set_health(1.0) | ||
var card_damage: CardBase = load("res://Cards/Resource/Card_Damage.tres") | ||
|
||
assert_eq(_enemy_list.size(), 2) | ||
card_damage.on_card_play(_player, [_enemy]) | ||
assert_eq(_enemy_list.size(), 1) | ||
assert_true(_enemy.is_queued_for_deletion()) | ||
await get_tree().process_frame | ||
assert_false(is_instance_valid(_enemy)) | ||
|
||
|
||
func test_all_enemy_death_to_player_attack_all(): | ||
_enemy_health_component._set_health(1.0) | ||
_enemy_2_health_component._set_health(1.0) | ||
var card_damage_all: CardBase = load("res://Cards/Resource/Card_DamageAll.tres") | ||
|
||
assert_eq(_enemy_list.size(), 2) | ||
card_damage_all.on_card_play(_player, []) | ||
assert_eq(_enemy_list.size(), 0) | ||
assert_true(_enemy.is_queued_for_deletion()) | ||
assert_true(_enemy_2.is_queued_for_deletion()) | ||
await get_tree().process_frame | ||
assert_false(is_instance_valid(_enemy)) | ||
assert_false(is_instance_valid(_enemy_2)) | ||
|
||
|
||
|
||
func test_enemy_death_to_poison(): | ||
_enemy_health_component._set_health(1.0) | ||
var card_poison: CardBase = load("res://Cards/Resource/Card_Poison.tres") | ||
card_poison.on_card_play(_player, [_enemy]) | ||
|
||
assert_eq(_enemy_list.size(), 2) | ||
_battler._on_enemy_start_turn() | ||
assert_eq(_enemy_list.size(), 1) | ||
assert_true(_enemy.is_queued_for_deletion()) | ||
await get_tree().process_frame | ||
assert_false(is_instance_valid(_enemy)) | ||
|
||
|
||
func test_enemy_death_to_expiring_poison(): | ||
_enemy_health_component._set_health(1.0) | ||
var card_poison: CardBase = load("res://Cards/Resource/Card_Poison.tres") | ||
card_poison.on_card_play(_player, [_enemy]) | ||
_enemy.get_status_component().current_status[0].status_turn_duration = 1 | ||
|
||
assert_eq(_enemy_list.size(), 2) | ||
assert_eq(_enemy.get_status_component().current_status.size(), 1) | ||
_battler._on_enemy_start_turn() | ||
assert_eq(_enemy.get_status_component().current_status.size(), 0) | ||
assert_eq(_enemy_list.size(), 1) | ||
assert_true(_enemy.is_queued_for_deletion()) | ||
await get_tree().process_frame | ||
assert_false(is_instance_valid(_enemy)) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters