TowerDefense/Towers/Tower.gd

182 lines
4.4 KiB
GDScript

@tool
extends StaticBody3D
class_name Tower
enum STATE { BLUEPRINT, REST, ACTION, EXHAUSTED, DISABLED }
signal state_changed
signal energy_changed
signal changed
# DANGER "NONE" Should always be first
enum TYPE { NONE, PIERRE, ALINE, MAXENCE, VICTORIA, EVAN, ALEX, GERALDINE }
var projectileScene : PackedScene = preload("res://Towers/Projectiles/projectile.tscn")
@export var tower_name : String = "None"
@export var type : TYPE
@export_group("Base data")
@export var icone : Texture2D
@export var bio : String
@export var price : int
@export_group("Attack")
@export var projectileRessource : ProjectileResource
@export var towerRange : Shape3D
@export var action_cooldown : float = 0.3 :
set(value):
action_cooldown = clamp(value, 0.3, 999)
@export_group("Energy")
@export var max_energy : float :
set(value):
var diff : float = value - max_energy
max_energy = value
if not Engine.is_editor_hint() && is_node_ready():
energy += diff
energyBar.max_value = max_energy
@export var energy_regen : float
@export var energy_cost : float
@export_group("Button")
@export var buttonTooltip : String
@onready var energyBar : ProgressBar = $EnergyBar3D/SubViewport/EnergyBar2D
@onready var sprite : Sprite3D = $Sprite3D
@onready var energyRecoveryCooldown : Timer = $EnergyRecoveryCooldown
var state : STATE = STATE.BLUEPRINT :
set(value):
state = value
state_changed.emit()
var energy : float :
set(value):
energy = clampf(value, 0, max_energy)
energyBar.value = energy
energy_changed.emit()
if not energy:
state = STATE.EXHAUSTED
var availableTargets : Array[Enemy]
var selectable : bool :
get():
return state != STATE.DISABLED && (state != STATE.BLUEPRINT || Game.money >= price)
@export_category("Upgrades")
@export var upgrades : Array[TowerUpgrade] :
set(value):
upgrades = EnhancedResource.arrayValueChanged(value, TowerUpgrade.new)
func _ready() -> void:
# WARNING : Prevent .tscn file to be modified by the load of the scene in editor
if not Engine.is_editor_hint():
energy = max_energy
energyRecoveryCooldown.timeout.connect(func(): energy += energy_regen + Game.energy_boost)
collision_layer = 0
collision_mask = 0
$PriceTag.text = str(price) + ""
$Range/Range.shape = towerRange
func _process(_delta: float) -> void:
if state == STATE.ACTION && $AttackCooldown.is_stopped():
shoot()
func changeState(newState : STATE) -> void:
if [STATE.BLUEPRINT, STATE.DISABLED].has(state):
return
match newState:
STATE.ACTION when state == STATE.REST: in_action()
STATE.ACTION when not energy: newState = STATE.EXHAUSTED
STATE.ACTION: pass
STATE.REST: resting()
_: return # NOTE Prevent changing of state
state = newState
func shoot() -> void:
var target : Enemy = choose_target()
if not target:
return
energy -= energy_cost
var projectile : Projectile = projectileScene.instantiate()
projectile.loadProjectile(projectileRessource, target)
EventBus.projectile_shooted.emit(projectile, $Aim.global_position)
$AttackCooldown.start(action_cooldown)
func resting() -> void:
visible = false
collision_layer = 0
collision_mask = 0
toggleConnection(false)
energyRecoveryCooldown.start()
func in_action() -> void:
visible = true
collision_layer = 0b100
collision_mask = 0b100
toggleConnection(true)
energyRecoveryCooldown.stop()
func choose_target() -> Enemy:
var target : Enemy = null
for enemy in availableTargets:
if not target || enemy.path.progress > target.path.progress:
target = enemy
return target
func build() -> bool:
if state != STATE.BLUEPRINT || not Game.spendMoney(price):
return false
sprite.modulate = "ffffffff"
$EnergyBar3D.visible = true
$PriceTag.visible = false
state = STATE.ACTION
in_action()
changed.emit()
return true
func onBodyEntered(body: Node3D) -> void:
if body is Enemy:
availableTargets.append(body)
func onBodyExited(body: Node3D) -> void:
if body is Enemy:
availableTargets.erase(body)
func toggleConnection(activate : bool) -> void:
if activate && not $Range.body_entered.is_connected(onBodyEntered):
$Range.body_entered.connect(onBodyEntered)
$Range.body_exited.connect(onBodyExited)
else:
if $Range.body_entered.is_connected(onBodyEntered):
$Range.body_entered.disconnect(onBodyEntered)
$Range.body_exited.disconnect(onBodyExited)
availableTargets.clear()
func disable(duration : float) -> void:
state = STATE.DISABLED
resting()
await get_tree().create_timer(duration).timeout
state = STATE.REST