Godot Version
4.3
Question
I’m currently developing a card game in Godot where each card has unique rules for how it can be played. Specifically, some cards can only be placed on empty tiles, some must be placed on empty spaces adjacent to existing tiles, and others require the player to select multiple targets or positions sequentially before the card effect is resolved. For example, one card may require selecting an entity and then selecting an adjacent space to push that entity.
Current Implementation
I’ve created a CardConstraintResource class that extends Resource to define how each card can be played:
class_name CardConstraintResource extends Resource
enum constraint_type {ENTITY, EMPTY_TILE, SPACE_ADJACENT_TO_TILE}
@export var constraint_sequence: Array[constraint_type]
Each card references a CardConstraintResource that dictates the valid play sequence. For example, a card that plants something can only be played on EMPTY_TILE
, so its constraint_sequence would reflect this.
I’ve also started implementing a CardPlayer
script to manage the process of playing a card, but I’m facing difficulties in completing the logic where the game should wait for player input and proceed to the next step in the constraint_sequence after each selection.
CardPlayer.gd
extends Node2D
func collect_play_data(card: Card):
var card_resource: CardResource = card.card_resource
var constraint_sequence = card.card_resource.constraint_resource.constraint_sequence
if constraint_sequence == null:
return
var valid_moves = get_valid_positions(constraint_sequence[0])
highlight_available_moves(card, 0)
var selected_move = await wait_for_move_selection(valid_moves)
func play(card: Card):
pass
func highlight_available_moves(card: Card, index: int):
var positions = get_valid_positions(card.card_resource.constraint_resource.constraint_sequence[index])
for position in positions:
var valid_move_square = ColorRect.new()
valid_move_square.color = Color.GREEN
valid_move_square.color.a = 0.5
valid_move_square.position = TileManager.tilemap.map_to_local(position)
valid_move_square.size = Vector2(16, 16)
valid_move_square.z_index = 100
valid_move_square.position += Vector2(-8, -8)
add_child(valid_move_square)
func get_valid_positions(constraint_type: CardConstraintResource.constraint_type):
var valid_positions: Array[Vector2i] = []
match constraint_type:
CardConstraintResource.constraint_type.ENTITY:
for entity in get_tree().get_nodes_in_group("Entity"):
if entity is Entity:
valid_positions.append(TileManager.tilemap.local_to_map(entity.position))
else:
print_debug("Trying to get entity position on the node that is not of type Entity!")
CardConstraintResource.constraint_type.EMPTY_TILE:
var tile_positions = TileManager.tilemap.get_used_cells()
for tile_position in tile_positions:
var tile: Tile = TileManager.get_tile_at_position(tile_position)
if tile.entity_on_top == null:
valid_positions.append(tile_position)
CardConstraintResource.constraint_type.SPACE_ADJACENT_TO_TILE:
var tile_positions = TileManager.tilemap.get_used_cells()
var offset_vectors = [Vector2i.UP, Vector2i.DOWN, Vector2i.LEFT, Vector2i.RIGHT]
for tile_position in tile_positions:
for offset_vector in offset_vectors:
if TileManager.get_tile_at_position(tile_position + offset_vector) == null:
valid_positions.append(tile_position + offset_vector)
return valid_positions
func wait_for_move_selection(valid_moves: Array[Vector2i]):
pass
The Problem
The part I’m struggling with is how to handle waiting for player input to select a valid move and then continue to the next step in the constraint_sequence
. For example, if a card requires the player to:
- Select an entity (constraint
ENTITY
) - Choose a direction adjacent to that entity (constraint
DIRECTION
)
(I didn’t implement DIRECTION
constraint yet, since this constraint is dependent on what the player selected previously, and I can’t get any ideas on how to implement this)
Then the flow should be:
- Highlight available moves based on the first constraint.
- Wait for the player to select one.
- Highlight the moves for the next constraint.
- Wait for the player to select again.
- Resolve the card’s action.
What I Need Help With
I’m not sure how to structure the logic to handle this sequence of player selections effectively. Should I be looking at implementing a state machine to manage these interactions? Or is there a different pattern or method in Godot that would make managing this sequential player input easier?
Any advice, examples, or resources would be greatly appreciated!