class_name TargetSelector extends RefCounted ## TargetSelector - Validates and provides target options for effects ## Get all valid targets for an effect's target specification func get_valid_targets( target_spec: Dictionary, source: CardInstance, game_state ) -> Array: if target_spec.is_empty(): return [] var candidates: Array = [] var zone = str(target_spec.get("zone", "FIELD")).to_upper() var owner = str(target_spec.get("owner", "ANY")).to_upper() var filter = target_spec.get("filter", {}) var target_type = str(target_spec.get("type", "CHOOSE")).to_upper() # Handle SELF and ALL targets specially if target_type == "SELF": return [source] elif target_type == "ALL": return _get_all_matching(owner, zone, filter, source, game_state) # Collect candidates from appropriate zones match zone: "FIELD": candidates = _get_field_cards(owner, source, game_state) "HAND": candidates = _get_hand_cards(owner, source, game_state) "BREAK_ZONE", "BREAK": candidates = _get_break_zone_cards(owner, source, game_state) "DECK": candidates = _get_deck_cards(owner, source, game_state) _: # Default to field candidates = _get_field_cards(owner, source, game_state) # Apply filters using CardFilter utility return CardFilter.get_matching(candidates, filter, source) ## Get all cards matching filter (for "ALL" target type) func _get_all_matching( owner: String, zone: String, filter: Dictionary, source: CardInstance, game_state ) -> Array: var candidates = _get_field_cards(owner, source, game_state) return CardFilter.get_matching(candidates, filter, source) ## Get cards from field func _get_field_cards( owner: String, source: CardInstance, game_state ) -> Array: var cards: Array = [] match owner: "CONTROLLER": var player = game_state.get_player(source.controller_index) if player: cards.append_array(_get_player_field_cards(player)) "OPPONENT": var opponent = game_state.get_player(1 - source.controller_index) if opponent: cards.append_array(_get_player_field_cards(opponent)) "ANY", _: for player in game_state.players: cards.append_array(_get_player_field_cards(player)) return cards ## Get all field cards for a player func _get_player_field_cards(player) -> Array: var cards: Array = [] cards.append_array(player.field_forwards.get_cards()) cards.append_array(player.field_backups.get_cards()) return cards ## Get cards from hand func _get_hand_cards( owner: String, source: CardInstance, game_state ) -> Array: var cards: Array = [] var player_index = source.controller_index if owner == "OPPONENT": player_index = 1 - player_index var player = game_state.get_player(player_index) if player: cards.append_array(player.hand.get_cards()) return cards ## Get cards from break zone func _get_break_zone_cards( owner: String, source: CardInstance, game_state ) -> Array: var cards: Array = [] var player_index = source.controller_index if owner == "OPPONENT": player_index = 1 - player_index var player = game_state.get_player(player_index) if player: cards.append_array(player.break_zone.get_cards()) return cards ## Get cards from deck func _get_deck_cards( owner: String, source: CardInstance, game_state ) -> Array: # Usually not directly targetable, used for search effects var cards: Array = [] var player_index = source.controller_index if owner == "OPPONENT": player_index = 1 - player_index var player = game_state.get_player(player_index) if player: cards.append_array(player.deck.get_cards()) return cards ## Validate that a set of targets meets the target specification requirements func validate_targets( targets: Array, target_spec: Dictionary, source: CardInstance, game_state ) -> bool: var target_type = str(target_spec.get("type", "CHOOSE")).to_upper() # Check count requirements if target_spec.has("count"): var required = int(target_spec.count) if targets.size() != required: return false elif target_spec.has("count_up_to"): var max_count = int(target_spec.count_up_to) if targets.size() > max_count: return false # Validate each target is valid var valid_targets = get_valid_targets(target_spec, source, game_state) for target in targets: if target not in valid_targets: return false return true