2025-08-26 13:03:22 +02:00
|
|
|
extends Node3D
|
2025-08-31 23:07:55 +02:00
|
|
|
class_name TowerManager
|
2025-08-26 13:03:22 +02:00
|
|
|
|
2025-08-29 20:07:36 +02:00
|
|
|
|
2025-08-26 13:03:22 +02:00
|
|
|
@onready var cam : Camera3D = $"../Camera3D"
|
2025-08-28 18:59:03 +02:00
|
|
|
@onready var selection_icon : Sprite3D = $Sprite3DSelection
|
2025-08-26 13:03:22 +02:00
|
|
|
|
2025-09-03 20:52:14 +02:00
|
|
|
|
2025-09-04 02:54:37 +02:00
|
|
|
var usedLocations : Dictionary
|
|
|
|
|
var selectedTile : Vector3
|
|
|
|
|
var selected_tower : Tower
|
2025-09-05 04:07:17 +02:00
|
|
|
var buildedTower : int = 0
|
2025-09-07 11:52:50 +02:00
|
|
|
var is_on_gui : bool = false
|
2025-09-03 20:52:14 +02:00
|
|
|
|
2025-08-26 13:03:22 +02:00
|
|
|
|
|
|
|
|
func _ready() -> void:
|
2025-09-06 17:32:46 +02:00
|
|
|
EventBus.tower_selected.connect(onTowerSelect)
|
2025-08-28 20:35:45 +02:00
|
|
|
#$AnimationPlayer.play("arrow_bobbing")
|
2025-09-07 11:52:50 +02:00
|
|
|
EventBus.mouse_entered_gui.connect(onMouseEnteredGui)
|
|
|
|
|
EventBus.mouse_exited_gui.connect(onMouseExitedGui)
|
2025-09-09 23:54:59 +02:00
|
|
|
Game.allowed_tower_has_change.connect(connectTowerSignals)
|
2025-08-26 13:03:22 +02:00
|
|
|
|
2025-09-04 02:54:37 +02:00
|
|
|
|
2025-09-03 03:44:44 +02:00
|
|
|
func _process(_delta: float) -> void:
|
2025-09-04 02:54:37 +02:00
|
|
|
var collider : CollisionObject3D = handle_player_controls()
|
|
|
|
|
|
|
|
|
|
var tower : Tower
|
|
|
|
|
if collider is GameTile:
|
|
|
|
|
tower = usedLocations.get(collider.global_position.round())
|
2025-09-13 16:35:20 +02:00
|
|
|
selection_icon.frame = 5
|
|
|
|
|
selection_icon.axis = Vector3.Axis.AXIS_Y
|
|
|
|
|
selection_icon.pixel_size = .03
|
|
|
|
|
else:
|
|
|
|
|
selection_icon.frame = 68
|
|
|
|
|
selection_icon.pixel_size = .02 if collider is Tower else .01
|
|
|
|
|
selection_icon.axis = Vector3.Axis.AXIS_Z
|
|
|
|
|
if collider is Tower:
|
|
|
|
|
tower = collider
|
2025-09-04 02:54:37 +02:00
|
|
|
|
2025-08-26 13:03:22 +02:00
|
|
|
if Input.is_action_just_pressed("build"):
|
2025-09-13 16:35:20 +02:00
|
|
|
if tower == selected_tower && selected_tower:
|
2025-08-26 13:03:22 +02:00
|
|
|
return
|
|
|
|
|
|
2025-09-13 16:35:20 +02:00
|
|
|
if isTileAndFree(collider):
|
2025-09-04 02:54:37 +02:00
|
|
|
placeTower()
|
2025-09-06 17:32:46 +02:00
|
|
|
elif tower:
|
|
|
|
|
selectTower(tower.type)
|
2025-09-13 16:35:20 +02:00
|
|
|
elif collider is TheCube:
|
|
|
|
|
selected_tower = null
|
|
|
|
|
EventBus.cube_selected.emit()
|
2025-08-26 13:03:22 +02:00
|
|
|
|
|
|
|
|
if Input.is_action_just_pressed("rest"):
|
2025-09-04 02:54:37 +02:00
|
|
|
if tower:
|
|
|
|
|
moveTower(tower, Vector3.INF)
|
|
|
|
|
else:
|
|
|
|
|
EventBus.tower_selected.emit(Tower.TYPE.NONE)
|
2025-08-27 14:37:26 +02:00
|
|
|
|
2025-09-09 23:54:59 +02:00
|
|
|
if Input.is_action_just_pressed("test"):
|
|
|
|
|
if tower:
|
2025-09-13 16:35:20 +02:00
|
|
|
tower.disable(2)
|
2025-09-09 23:54:59 +02:00
|
|
|
|
2025-08-26 13:03:22 +02:00
|
|
|
|
2025-09-06 17:32:46 +02:00
|
|
|
func _input(event: InputEvent) -> void:
|
|
|
|
|
if event is InputEventKey:
|
|
|
|
|
handleTowerShortCuts(event)
|
|
|
|
|
|
|
|
|
|
|
2025-09-13 16:35:20 +02:00
|
|
|
func handle_player_controls() -> CollisionObject3D:
|
2025-09-07 11:52:50 +02:00
|
|
|
#If the player has the mouse on the GUI, player can't place tower
|
|
|
|
|
if is_on_gui:
|
|
|
|
|
return
|
2025-09-07 19:48:58 +02:00
|
|
|
|
2025-08-26 13:03:22 +02:00
|
|
|
var space_state : PhysicsDirectSpaceState3D = get_world_3d().direct_space_state
|
|
|
|
|
var mouse_pos : Vector2 = get_viewport().get_mouse_position()
|
|
|
|
|
|
|
|
|
|
var origin : Vector3 = cam.project_ray_origin(mouse_pos)
|
|
|
|
|
var end : Vector3 = origin + cam.project_ray_normal(mouse_pos) * 100
|
|
|
|
|
var ray : PhysicsRayQueryParameters3D = PhysicsRayQueryParameters3D.create(origin, end)
|
|
|
|
|
ray.collide_with_bodies = true
|
|
|
|
|
|
|
|
|
|
var ray_result : Dictionary = space_state.intersect_ray(ray)
|
|
|
|
|
if ray_result.is_empty():
|
|
|
|
|
visible = false
|
2025-09-04 02:54:37 +02:00
|
|
|
return null
|
2025-08-26 13:03:22 +02:00
|
|
|
|
2025-09-13 16:35:20 +02:00
|
|
|
var collider : CollisionObject3D = ray_result.get("collider")
|
2025-08-26 13:03:22 +02:00
|
|
|
visible = true
|
2025-08-30 20:08:12 +02:00
|
|
|
selection_icon.visible = true
|
2025-09-13 16:35:20 +02:00
|
|
|
global_position = collider.global_position
|
2025-09-14 01:31:18 +02:00
|
|
|
global_position.y += Helper.getHitBoxLocation(collider, Helper.POSITION.TOP) + .01
|
2025-08-26 13:03:22 +02:00
|
|
|
|
2025-09-09 23:54:59 +02:00
|
|
|
if selected_tower && selected_tower.state == Tower.STATE.BLUEPRINT:
|
2025-09-04 02:54:37 +02:00
|
|
|
selected_tower.sprite.modulate = "ff4545c8" # If the tower can't be placed he is red
|
|
|
|
|
selection_icon.visible = false # If we are placing a tower, hide the selector model
|
2025-09-13 16:35:20 +02:00
|
|
|
if isTileAndFree(collider):
|
2025-09-04 02:54:37 +02:00
|
|
|
selected_tower.sprite.modulate = "61ff45c8" # If the tower can be placed he is green
|
2025-08-27 13:27:32 +02:00
|
|
|
|
2025-09-04 02:54:37 +02:00
|
|
|
return collider
|
2025-08-27 13:27:32 +02:00
|
|
|
|
2025-08-26 13:03:22 +02:00
|
|
|
|
2025-09-04 02:54:37 +02:00
|
|
|
func placeTower() -> void:
|
|
|
|
|
if not selected_tower:
|
2025-08-27 13:27:32 +02:00
|
|
|
return
|
2025-08-27 14:37:26 +02:00
|
|
|
|
2025-09-09 23:54:59 +02:00
|
|
|
if selected_tower.state == Tower.STATE.BLUEPRINT:
|
2025-09-04 02:54:37 +02:00
|
|
|
if not selected_tower.build():
|
|
|
|
|
return
|
2025-09-05 04:07:17 +02:00
|
|
|
buildedTower += 1
|
|
|
|
|
EventBus.tower_count_changed.emit(buildedTower)
|
2025-09-04 02:54:37 +02:00
|
|
|
remove_child(selected_tower)
|
|
|
|
|
$"../Towers".add_child(selected_tower)
|
|
|
|
|
|
|
|
|
|
moveTower(selected_tower, global_position)
|
|
|
|
|
|
2025-08-26 13:03:22 +02:00
|
|
|
|
2025-09-13 16:35:20 +02:00
|
|
|
func isTileAndFree(collider: CollisionObject3D) -> bool:
|
|
|
|
|
return collider is GameTile && collider.type == GameTile.TYPE.TOWER \
|
|
|
|
|
&& not usedLocations.has(collider.global_position.round())
|
|
|
|
|
|
2025-09-04 02:54:37 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
## Set [param toPosition] with [Vector3.INF] to make the tower rest
|
|
|
|
|
func moveTower(tower: Tower, toPosition: Vector3) -> void:
|
|
|
|
|
usedLocations.erase(tower.global_position.round())
|
|
|
|
|
|
|
|
|
|
if toPosition == Vector3.INF:
|
2025-09-09 23:54:59 +02:00
|
|
|
tower.changeState(Tower.STATE.REST)
|
2025-09-04 02:54:37 +02:00
|
|
|
else:
|
2025-09-04 17:59:23 +02:00
|
|
|
usedLocations.set(toPosition.round(), tower)
|
2025-09-04 02:54:37 +02:00
|
|
|
tower.global_position = toPosition
|
2025-09-09 23:54:59 +02:00
|
|
|
tower.changeState(Tower.STATE.ACTION)
|
2025-08-26 13:03:22 +02:00
|
|
|
|
2025-09-05 04:07:17 +02:00
|
|
|
var inAction : int = usedLocations.size()
|
|
|
|
|
EventBus.team_in_action_changed.emit(inAction)
|
|
|
|
|
EventBus.team_in_rest_changed.emit(buildedTower - inAction)
|
2025-08-26 13:03:22 +02:00
|
|
|
|
|
|
|
|
|
2025-09-06 17:32:46 +02:00
|
|
|
func onTowerSelect(towerType: Tower.TYPE):
|
2025-09-03 20:52:14 +02:00
|
|
|
# Hide current not builded tower
|
2025-09-09 23:54:59 +02:00
|
|
|
if selected_tower && selected_tower.state == Tower.STATE.BLUEPRINT:
|
2025-08-29 12:11:51 +02:00
|
|
|
selected_tower.visible = false
|
2025-08-26 13:03:22 +02:00
|
|
|
|
2025-09-04 17:59:23 +02:00
|
|
|
if selected_tower && selected_tower.type == towerType || towerType == Tower.TYPE.NONE:
|
2025-09-09 23:54:59 +02:00
|
|
|
if selected_tower && selected_tower.state == Tower.STATE.BLUEPRINT:
|
2025-09-06 22:08:29 +02:00
|
|
|
remove_child(selected_tower)
|
2025-08-29 12:11:51 +02:00
|
|
|
selected_tower = null
|
2025-09-06 22:08:29 +02:00
|
|
|
else:
|
2025-09-04 17:59:23 +02:00
|
|
|
selected_tower = Game.towers.get(towerType)
|
2025-09-09 23:54:59 +02:00
|
|
|
if selected_tower.state == Tower.STATE.BLUEPRINT:
|
2025-09-04 17:59:23 +02:00
|
|
|
selected_tower.visible = true
|
2025-09-06 22:08:29 +02:00
|
|
|
add_child(selected_tower)
|
2025-09-06 17:32:46 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
func selectTower(towerType: Tower.TYPE, force : bool = false) -> void:
|
2025-09-09 23:54:59 +02:00
|
|
|
if towerType && (force || not selected_tower || selected_tower.state):
|
2025-09-06 17:32:46 +02:00
|
|
|
EventBus.tower_selected.emit(towerType)
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func handleTowerShortCuts(event: InputEventKey) -> void:
|
|
|
|
|
if event.pressed:
|
|
|
|
|
return
|
|
|
|
|
|
|
|
|
|
var towerType : Tower.TYPE = Tower.TYPE.NONE
|
|
|
|
|
match event.physical_keycode:
|
|
|
|
|
KEY_Q: towerType = Tower.TYPE.PIERRE
|
|
|
|
|
KEY_W: towerType = Tower.TYPE.ALINE
|
|
|
|
|
KEY_E: towerType = Tower.TYPE.MAXENCE
|
|
|
|
|
KEY_R: towerType = Tower.TYPE.VICTORIA
|
|
|
|
|
KEY_A: towerType = Tower.TYPE.EVAN
|
|
|
|
|
KEY_S: towerType = Tower.TYPE.ALEX
|
|
|
|
|
KEY_D: towerType = Tower.TYPE.GERALDINE
|
|
|
|
|
#KEY_F: towerType = Tower.TYPE
|
|
|
|
|
|
2025-09-06 22:08:29 +02:00
|
|
|
if Game.towers.has(towerType):
|
2025-09-06 17:32:46 +02:00
|
|
|
selectTower(towerType, true)
|
2025-09-07 11:52:50 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
func onMouseEnteredGui() -> void:
|
|
|
|
|
is_on_gui = true
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func onMouseExitedGui() -> void:
|
|
|
|
|
is_on_gui = false
|
2025-09-09 23:54:59 +02:00
|
|
|
|
|
|
|
|
|
|
|
|
|
func connectTowerSignals() -> void:
|
|
|
|
|
for tower : Tower in Game.towers.values():
|
|
|
|
|
tower.state_changed.connect(onTowerStateChange.bind(tower))
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
func onTowerStateChange(tower : Tower) -> void:
|
|
|
|
|
if tower.state == Tower.STATE.DISABLED && tower == selected_tower:
|
|
|
|
|
EventBus.tower_selected.emit(Tower.TYPE.NONE)
|
|
|
|
|
moveTower(tower, Vector3.INF)
|