364 lines
11 KiB
GDScript
364 lines
11 KiB
GDScript
extends GutTest
|
|
|
|
## Tests for the Modal Choice System (CHOOSE_MODE effects)
|
|
## Tests parser output, ChoiceModal UI, and AbilitySystem integration
|
|
|
|
|
|
# =============================================================================
|
|
# PARSER OUTPUT TESTS
|
|
# =============================================================================
|
|
|
|
func test_modal_effect_has_correct_structure() -> void:
|
|
# Simulate a parsed CHOOSE_MODE effect
|
|
var effect = {
|
|
"type": "CHOOSE_MODE",
|
|
"select_count": 1,
|
|
"select_up_to": false,
|
|
"mode_count": 3,
|
|
"modes": [
|
|
{
|
|
"index": 0,
|
|
"description": "Choose 1 Forward. Deal it 7000 damage.",
|
|
"effects": [{"type": "DAMAGE", "amount": 7000}]
|
|
},
|
|
{
|
|
"index": 1,
|
|
"description": "Choose 1 Monster. Break it.",
|
|
"effects": [{"type": "BREAK"}]
|
|
},
|
|
{
|
|
"index": 2,
|
|
"description": "Deal 3000 damage to all Forwards.",
|
|
"effects": [{"type": "DAMAGE", "amount": 3000}]
|
|
}
|
|
]
|
|
}
|
|
|
|
assert_eq(effect.type, "CHOOSE_MODE", "Effect type should be CHOOSE_MODE")
|
|
assert_eq(effect.select_count, 1, "Should select 1 mode")
|
|
assert_eq(effect.mode_count, 3, "Should have 3 modes")
|
|
assert_eq(effect.modes.size(), 3, "Modes array should have 3 entries")
|
|
assert_false(effect.select_up_to, "Should not be 'up to' selection")
|
|
|
|
|
|
func test_modal_effect_with_enhanced_condition() -> void:
|
|
var effect = {
|
|
"type": "CHOOSE_MODE",
|
|
"select_count": 1,
|
|
"select_up_to": false,
|
|
"mode_count": 3,
|
|
"modes": [],
|
|
"enhanced_condition": {
|
|
"description": "if you have 5 or more ifrit in your break zone",
|
|
"select_count": 3,
|
|
"select_up_to": true
|
|
}
|
|
}
|
|
|
|
assert_true(effect.has("enhanced_condition"), "Should have enhanced condition")
|
|
assert_eq(effect.enhanced_condition.select_count, 3, "Enhanced should allow 3 selections")
|
|
assert_true(effect.enhanced_condition.select_up_to, "Enhanced should be 'up to'")
|
|
|
|
|
|
func test_modal_mode_has_description_and_effects() -> void:
|
|
var mode = {
|
|
"index": 0,
|
|
"description": "Draw 2 cards.",
|
|
"effects": [{"type": "DRAW", "amount": 2}]
|
|
}
|
|
|
|
assert_true(mode.has("description"), "Mode should have description")
|
|
assert_true(mode.has("effects"), "Mode should have effects array")
|
|
assert_eq(mode.effects.size(), 1, "Mode should have 1 effect")
|
|
assert_eq(mode.effects[0].type, "DRAW", "Effect should be DRAW")
|
|
|
|
|
|
# =============================================================================
|
|
# CHOICE MODAL TESTS
|
|
# =============================================================================
|
|
|
|
func test_choice_modal_instantiates() -> void:
|
|
var modal = ChoiceModal.new()
|
|
assert_not_null(modal, "ChoiceModal should instantiate")
|
|
modal.free()
|
|
|
|
|
|
func test_choice_modal_starts_invisible() -> void:
|
|
var modal = ChoiceModal.new()
|
|
add_child_autofree(modal)
|
|
|
|
# Need to wait for _ready to complete
|
|
await get_tree().process_frame
|
|
|
|
assert_false(modal.visible, "ChoiceModal should start invisible")
|
|
|
|
|
|
func test_choice_modal_has_correct_layer() -> void:
|
|
var modal = ChoiceModal.new()
|
|
add_child_autofree(modal)
|
|
|
|
await get_tree().process_frame
|
|
|
|
assert_eq(modal.layer, 200, "ChoiceModal should be at layer 200")
|
|
|
|
|
|
func test_choice_modal_signals_exist() -> void:
|
|
var modal = ChoiceModal.new()
|
|
|
|
assert_true(modal.has_signal("choice_made"), "Should have choice_made signal")
|
|
assert_true(modal.has_signal("choice_cancelled"), "Should have choice_cancelled signal")
|
|
|
|
modal.free()
|
|
|
|
|
|
# =============================================================================
|
|
# EFFECT RESOLVER TESTS
|
|
# =============================================================================
|
|
|
|
func test_effect_resolver_has_choice_required_signal() -> void:
|
|
var resolver = EffectResolver.new()
|
|
|
|
assert_true(resolver.has_signal("choice_required"), "Should have choice_required signal")
|
|
|
|
|
|
func test_effect_resolver_emits_choice_required_for_choose_mode() -> void:
|
|
var resolver = EffectResolver.new()
|
|
var signal_received = false
|
|
var received_effect = {}
|
|
var received_modes = []
|
|
|
|
resolver.choice_required.connect(func(effect, modes):
|
|
signal_received = true
|
|
received_effect = effect
|
|
received_modes = modes
|
|
)
|
|
|
|
var effect = {
|
|
"type": "CHOOSE_MODE",
|
|
"modes": [
|
|
{"index": 0, "description": "Option 1", "effects": []},
|
|
{"index": 1, "description": "Option 2", "effects": []}
|
|
]
|
|
}
|
|
|
|
# Resolve the effect
|
|
resolver.resolve(effect, null, [], null)
|
|
|
|
assert_true(signal_received, "Should emit choice_required signal")
|
|
assert_eq(received_modes.size(), 2, "Should pass modes to signal")
|
|
|
|
|
|
# =============================================================================
|
|
# FIELD EFFECT MANAGER TESTS
|
|
# =============================================================================
|
|
|
|
func test_field_effect_manager_block_immunity_empty() -> void:
|
|
var manager = FieldEffectManager.new()
|
|
|
|
# With no abilities registered, should return false
|
|
var result = manager.has_block_immunity(null, null, null)
|
|
assert_false(result, "Should return false with no abilities")
|
|
|
|
|
|
func test_field_effect_manager_attack_restriction_empty() -> void:
|
|
var manager = FieldEffectManager.new()
|
|
|
|
var result = manager.has_attack_restriction(null, null)
|
|
assert_false(result, "Should return false with no abilities")
|
|
|
|
|
|
func test_field_effect_manager_block_restriction_empty() -> void:
|
|
var manager = FieldEffectManager.new()
|
|
|
|
var result = manager.has_block_restriction(null, null)
|
|
assert_false(result, "Should return false with no abilities")
|
|
|
|
|
|
func test_field_effect_manager_taunt_targets_empty() -> void:
|
|
var manager = FieldEffectManager.new()
|
|
|
|
var result = manager.get_taunt_targets(0, null)
|
|
assert_eq(result.size(), 0, "Should return empty array with no abilities")
|
|
|
|
|
|
func test_field_effect_manager_cost_modifier_empty() -> void:
|
|
var manager = FieldEffectManager.new()
|
|
|
|
var result = manager.get_cost_modifier(null, 0, null)
|
|
assert_eq(result, 0, "Should return 0 with no abilities")
|
|
|
|
|
|
func test_field_effect_manager_max_attacks_default() -> void:
|
|
var manager = FieldEffectManager.new()
|
|
|
|
var result = manager.get_max_attacks(null, null)
|
|
assert_eq(result, 1, "Default max attacks should be 1")
|
|
|
|
|
|
# =============================================================================
|
|
# BLOCKER IMMUNITY CONDITION TESTS
|
|
# =============================================================================
|
|
|
|
func test_blocker_immunity_condition_empty() -> void:
|
|
var manager = FieldEffectManager.new()
|
|
|
|
# Empty condition means unconditional immunity
|
|
var result = manager._blocker_matches_immunity_condition(null, {}, null)
|
|
assert_true(result, "Empty condition should return true (unconditional)")
|
|
|
|
|
|
func test_blocker_immunity_condition_cost_gte() -> void:
|
|
var manager = FieldEffectManager.new()
|
|
|
|
# Create mock card data
|
|
var blocker = _create_mock_card(3, 5000) # cost 3, power 5000
|
|
|
|
var condition = {
|
|
"comparison": "GTE",
|
|
"attribute": "cost",
|
|
"value": 4
|
|
}
|
|
|
|
var result = manager._blocker_matches_immunity_condition(blocker, condition, null)
|
|
assert_false(result, "Cost 3 is not >= 4")
|
|
|
|
blocker.card_data.cost = 4
|
|
result = manager._blocker_matches_immunity_condition(blocker, condition, null)
|
|
assert_true(result, "Cost 4 is >= 4")
|
|
|
|
blocker.free()
|
|
|
|
|
|
func test_blocker_immunity_condition_power_lt_self() -> void:
|
|
var manager = FieldEffectManager.new()
|
|
|
|
var blocker = _create_mock_card(3, 5000)
|
|
var attacker = _create_mock_card(4, 8000)
|
|
|
|
var condition = {
|
|
"comparison": "LT",
|
|
"attribute": "power",
|
|
"compare_to": "SELF_POWER"
|
|
}
|
|
|
|
# Blocker power 5000 < attacker power 8000
|
|
var result = manager._blocker_matches_immunity_condition(blocker, condition, attacker)
|
|
assert_true(result, "5000 < 8000 should be true")
|
|
|
|
# Change blocker power to be higher
|
|
blocker.current_power = 9000
|
|
result = manager._blocker_matches_immunity_condition(blocker, condition, attacker)
|
|
assert_false(result, "9000 < 8000 should be false")
|
|
|
|
blocker.free()
|
|
attacker.free()
|
|
|
|
|
|
# =============================================================================
|
|
# ABILITY SYSTEM INTEGRATION TESTS
|
|
# =============================================================================
|
|
|
|
func test_ability_system_has_choice_modal_reference() -> void:
|
|
# Check that AbilitySystem has the choice_modal variable
|
|
var ability_system_script = load("res://scripts/game/abilities/AbilitySystem.gd")
|
|
assert_not_null(ability_system_script, "AbilitySystem script should exist")
|
|
|
|
|
|
func test_modes_selected_queues_effects() -> void:
|
|
# This tests the logic of _on_modes_selected indirectly
|
|
var effect = {
|
|
"type": "CHOOSE_MODE",
|
|
"modes": [
|
|
{"index": 0, "description": "Deal damage", "effects": [{"type": "DAMAGE", "amount": 5000}]},
|
|
{"index": 1, "description": "Draw cards", "effects": [{"type": "DRAW", "amount": 2}]}
|
|
]
|
|
}
|
|
|
|
var modes = effect.modes
|
|
var selected_indices = [1] # Player selected option 2 (draw)
|
|
|
|
# Verify mode selection logic
|
|
var queued_effects = []
|
|
for index in selected_indices:
|
|
if index >= 0 and index < modes.size():
|
|
var mode = modes[index]
|
|
for mode_effect in mode.get("effects", []):
|
|
queued_effects.append(mode_effect)
|
|
|
|
assert_eq(queued_effects.size(), 1, "Should queue 1 effect")
|
|
assert_eq(queued_effects[0].type, "DRAW", "Queued effect should be DRAW")
|
|
assert_eq(queued_effects[0].amount, 2, "Draw amount should be 2")
|
|
|
|
|
|
func test_multi_select_queues_multiple_effects() -> void:
|
|
var effect = {
|
|
"type": "CHOOSE_MODE",
|
|
"select_count": 2,
|
|
"modes": [
|
|
{"index": 0, "effects": [{"type": "DAMAGE", "amount": 5000}]},
|
|
{"index": 1, "effects": [{"type": "DRAW", "amount": 2}]},
|
|
{"index": 2, "effects": [{"type": "BREAK"}]}
|
|
]
|
|
}
|
|
|
|
var modes = effect.modes
|
|
var selected_indices = [0, 2] # Player selected damage and break
|
|
|
|
var queued_effects = []
|
|
for index in selected_indices:
|
|
if index >= 0 and index < modes.size():
|
|
var mode = modes[index]
|
|
for mode_effect in mode.get("effects", []):
|
|
queued_effects.append(mode_effect)
|
|
|
|
assert_eq(queued_effects.size(), 2, "Should queue 2 effects")
|
|
assert_eq(queued_effects[0].type, "DAMAGE", "First effect should be DAMAGE")
|
|
assert_eq(queued_effects[1].type, "BREAK", "Second effect should be BREAK")
|
|
|
|
|
|
# =============================================================================
|
|
# ENHANCED CONDITION TESTS
|
|
# =============================================================================
|
|
|
|
func test_enhanced_condition_parsing() -> void:
|
|
var condition = {
|
|
"description": "if you have a total of 5 or more card name ifrita and/or card name ifrit in your break zone",
|
|
"select_count": 3,
|
|
"select_up_to": true
|
|
}
|
|
|
|
# Test that description contains expected keywords
|
|
assert_true("break zone" in condition.description, "Should mention break zone")
|
|
assert_true("5 or more" in condition.description, "Should mention count requirement")
|
|
|
|
|
|
func test_regex_count_extraction() -> void:
|
|
var description = "if you have 5 or more ifrit in your break zone"
|
|
|
|
var regex = RegEx.new()
|
|
regex.compile(r"(\d+) or more")
|
|
var match_result = regex.search(description)
|
|
|
|
assert_not_null(match_result, "Should find count pattern")
|
|
assert_eq(match_result.get_string(1), "5", "Should extract '5'")
|
|
assert_eq(int(match_result.get_string(1)), 5, "Should convert to int 5")
|
|
|
|
|
|
# =============================================================================
|
|
# HELPER FUNCTIONS
|
|
# =============================================================================
|
|
|
|
func _create_mock_card(cost: int, power: int) -> CardInstance:
|
|
var card = CardInstance.new()
|
|
|
|
# Create minimal card data
|
|
var data = CardDatabase.CardData.new()
|
|
data.cost = cost
|
|
data.power = power
|
|
data.type = Enums.CardType.FORWARD
|
|
|
|
card.card_data = data
|
|
card.current_power = power
|
|
|
|
return card
|