Nice idea, but it is not a race condition. I have a button that triggers the find_children
statement and I press it after I see the remote tree having the “Enemy” node.
extends Node3D
class_name Game
enum State {ENEMY_TURN, PLAYER_TURN, PLAYER_RUNNING, GAME_OVER, WIN}
@onready var path = $Path
@onready var player = path.find_child("Player")
@onready var boss = path.find_child("Mage")
var round = 0
var current_state
# Called when the node enters the scene tree for the first time.
func _ready():
$Camera3D.set_current(true)
self.player.new_turn()
#TODO: animation
self.current_state = State.PLAYER_TURN
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
pass
func _on_end_round_button_button_up() -> void:
current_state = State.ENEMY_TURN
self.enemy_turn()
self.player.new_turn()
current_state = State.PLAYER_TURN
round += 1
func is_player_turn():
return current_state == State.PLAYER_TURN
func enemy_turn():
for enemy in get_node("/root/Game/Path").find_children("Enemy"): #FIXME
path.move_enemy(enemy) #TODO: check if they don't block
if boss.is_summoning_round(self.round) && path.tiles[13].get_node("Enemy") == null && path.tiles[13].get_node("Player") == null:
path.spawn_enemy(preload("res://Scenes/skeleton.tscn"), 13)
extends Node3D
@onready var player = $Tile0/Player
var game
var player_selected = false
var tiles = {}
const NTILES = 32
var adjacency_matrix = [[1], [0,2,14], [1,3], [2,4], [3,5], [4,6], [5,7], [6,8], [7,9], [8,10], [9,11], [10,12], [11,13], [12, 30, 31], [1, 15], [14, 16], [15,17], [16,18], [17,19, 20], [18], [18,21], [20,22], [21,23], [22,24], [23,25], [24,26], [25,27], [26,28], [27,29], [28,30], [29, 13], [29]]
var path_matrix = []
var rng = RandomNumberGenerator.new()
# Called when the node enters the scene tree for the first time.
func _ready():
for i in self.NTILES:
self.tiles[i] = get_node("Tile" + str(i))
calculate_paths()
self.game = get_parent()
# Called every frame. 'delta' is the elapsed time since the previous frame.
func _process(delta):
pass
# extra_arg_0 is the tile number
func _on_area_3d_input_event(camera, event, position, normal, shape_idx, extra_arg_0):
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_LEFT and !event.pressed:
if self.player_selected and extra_arg_0 in reachable_tiles(self.player.current_tile(), self.player.remaining_movement()):
go_to_tile(extra_arg_0)
func reachable_tiles(position, steps):
var r_tiles = []
var current_positions = [position]
while steps >= 0:
var new_positions = []
for current_position in current_positions:
r_tiles.append(current_position)
if !Utils.any(self.tiles[current_position].get_children(), "obstructs"):
#if the square is not occupied, you can go to its neighbors
for tile in self.adjacency_matrix[current_position]:
if tile not in r_tiles:
new_positions.append(tile)
current_positions = new_positions
steps -= 1
return r_tiles
func unselect_player():
for i in self.tiles:
self.tiles[i].find_child("AnimationPlayer").play("unhighlight")
self.player_selected = false
func calculate_paths():
for i in self.NTILES:
var row = []
for j in self.NTILES:
row.append([])
self.path_matrix.append(row)
for tile in self.NTILES:
for neighbor in adjacency_matrix[tile]:
self.path_matrix[tile][neighbor].append(neighbor)
for k in self.NTILES:
for i in self.NTILES:
for j in self.NTILES:
if !self.path_matrix[i][j].is_empty():
var tile = self.path_matrix[i][j].back()
for neighbor in adjacency_matrix[tile]:
if neighbor != i:
if self.path_matrix[i][neighbor].size() > (self.path_matrix[i][j] + [neighbor]).size() or self.path_matrix[i][neighbor].is_empty():
self.path_matrix[i][neighbor] = self.path_matrix[i][j] + [neighbor]
func go_to_tile(tile):
game.current_state = game.State.PLAYER_RUNNING
unselect_player()
self.player.get_node("AnimationPlayer").play("Running_A")
var path = self.path_matrix[self.player.current_tile()][tile]
var tween = self.get_tree().create_tween()
var i = 0
for next_tile in path:
var target = self.tiles[next_tile].global_position + Vector3(0, 2, 0)
var target_vector = -self.player.global_position.direction_to(target)
GlobalAnimation.rotate_to_around_y(target_vector, self.player)
var enemies = self.tiles[next_tile].get_children().filter(func(node): return node is Enemy)
if enemies.size() > 0:
#fight
var prev_tile = self.tiles[path[i-1]]
if i == 0:
prev_tile = self.player.get_parent()
var halfway_target_vector = 0.5*(target - prev_tile.global_position)
self.player.get_node("AnimationPlayer").play("Running_A")
tween.tween_property(self.player, "global_position", prev_tile.global_position + halfway_target_vector, 0.3)
await tween.finished
self.player.fight(enemies[0])
var special_tiles = self.tiles[next_tile].get_children().filter(func(node): return node is GrassTile)
for special_tile in special_tiles:
special_tile.loot(self.player)
tween.tween_property(self.player, "global_position", target, 0.3)
i+=1
self.player.move(1)
await tween.finished #FIXME move after fight won
self.player.get_parent().remove_child(self.player)
self.tiles[tile].add_child(self.player)
self.player.set_global_position(self.tiles[tile].global_position + Vector3(0, 2, 0))
self.player.get_node("AnimationPlayer").play("Idle")
game.current_state = game.State.PLAYER_TURN
func move_enemy(enemy: Node3D):
#FIXME
var path = enemy.move_path()
var tween = self.get_tree().create_tween()
for next_tile in path:
var target = self.tiles[next_tile].global_position + Vector3(0, 2, 0)
var target_vector = -enemy.global_position.direction_to(target)
GlobalAnimation.rotate_to_around_y(target_vector, enemy)
var enemies = self.tiles[next_tile].get_children().filter(func(node): return node is Enemy)
if enemies == []:
var player = self.tiles[next_tile].get_node("Player")
if player:
#fight
var prev_tile = enemy.get_parent()
var halfway_target_vector = 0.5*(target - prev_tile.global_position)
enemy.get_node("AnimationPlayer").play("Running_A")
tween.tween_property(enemy, "global_position", prev_tile.global_position + halfway_target_vector, 0.3)
await tween.finished
self.player.fight(enemy)
#TODO: if trap, item, etc...
tween.tween_property(enemy, "global_position", target, 0.3)
await tween.finished
enemy.get_parent().remove_child(enemy)
self.tiles[next_tile].add_child(enemy)
enemy.get_node("AnimationPlayer").play("Running_A")
enemy.set_global_position(self.tiles[next_tile].global_position + Vector3(0, 2, 0))
enemy.get_node("AnimationPlayer").play("Idle")
func spawn_enemy(scene: PackedScene, tile: int):
var enemy_instance = scene.instantiate()
enemy_instance.name = "Enemy"
enemy_instance.scene = scene
# Add to tile node (e.g., tiles[2])
self.tiles[tile].add_child(enemy_instance)
enemy_instance.find_child("AnimationPlayer").play("Idle")
enemy_instance.global_transform.origin = self.tiles[tile].global_transform.origin + Vector3(0,2,0)
func _on_player_player_selected():
if game.is_player_turn():
self.player.get_node("AnimationPlayer").play("Jump_Full_Short")
self.player_selected = true
var reachable_tiles = reachable_tiles(self.player.current_tile(), self.player.remaining_movement())
for tile in reachable_tiles:
self.tiles[tile].find_child("AnimationPlayer").play("highlight") # animate to highlight
func _on_ui_gui_input(event):
if event is InputEventMouseButton and event.button_index == MOUSE_BUTTON_RIGHT and !event.pressed:
unselect_player()
extends Node3D
class_name Enemy
@export var max_health: int
@export var current_health: int
@export var armor: int
@export var attack: int
@export var speed: int
@export var xp_reward: int
@export var status_effects: Array
@export var is_obstacle: bool
@export var scene: PackedScene
var path = [13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]
var rng = RandomNumberGenerator.new()
#TODO
#@onready var health_bar = preload("res://Scenes/HealthBar.tscn").instantiate()
signal enemy_clicked(enemy)
func _ready() -> void:
$Area3D.input_event.connect(_on_area_3d_input_event)
self.enemy_clicked.connect(Callable(get_node("/root/Game/UI"), "_on_enemy_clicked"))
#add_child(health_bar)
self.get_node("AnimationPlayer").play("Idle")
func animate_damage(damage: int):
var damage_label = load("res://Scenes/damage_riser.tscn")
self.add_child(damage_label.instantiate())
self.get_node("Node3D/Label3D").set_text(str(damage))
self.get_node("Node3D").set_global_position(self.global_position + Vector3(0,8,0))
self.get_node("Node3D/AnimationPlayer").play("rise")
await self.get_node("Node3D/AnimationPlayer").animation_finished
self.get_node("Node3D").queue_free()
func current_tile():
return int(self.get_parent().name.substr(4))
func move_path():
var index = self.path.find(self.current_tile())
return path.slice(index + 1, index + 1 + self.speed)
func obstructs():
return self.is_obstacle
func _on_area_3d_input_event(camera: Node, event: InputEvent, event_position: Vector3, normal: Vector3, shape_idx: int) -> void:
if event is InputEventMouseButton and event.pressed:
emit_signal("enemy_clicked", self)