@tool extends StaticBody3D class_name Tower # 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 = 100 @export_group("Attack") @export var projectileRessource : ProjectileResource @export var towerRange : Shape3D @export var action_cooldown : float = 1.5: set(value): action_cooldown = clamp(value, 0.3, 999) @export_group("Energy") @export var max_energy : float = 100.0 : set(value): max_energy = value towerChange.call_deferred() @export var energy_regen : float = 10.0 @export var energy_cost : float = 50.0 @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 energy : float = max_energy: set(value): energyBar.value = value energy = clampf(value, 0.0, max_energy) is_exhausted = energy < energy_cost EventBus.tower_changed.emit(self) var availableTargets : Array[Enemy] var target : Enemy var is_exhausted : bool = false var is_rest : bool : get(): return not energyRecoveryCooldown.is_stopped() var builded : bool = false @export_category("Upgrades") @export var upgrades : Array[TowerUpgrade] func _ready() -> void: EventBus.tower_upgraded.connect(apply_upgrade) energyRecoveryCooldown.timeout.connect(func(): energy += energy_regen) # WARNING : Prevent .tscn file to be modified by the load of the scene in editor if not Engine.is_editor_hint(): collision_layer = 0 collision_mask = 0 $PriceTag.text = str(price) + " €" $Range/Range.shape = towerRange func _process(_delta: float) -> void: if visible && $AttackCooldown.is_stopped() && builded && is_instance_valid(target): shoot() func get_available_upgrades() -> Array[TowerUpgrade]: return upgrades.filter(func(u: TowerUpgrade): u.isLevelMax) func apply_upgrade(towerType : Tower.TYPE, upgrade : TowerUpgrade): if towerType == type && upgrade.canUpgrade(): upgrade.upgrade(self) func shoot() -> void: 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 if builded: energyRecoveryCooldown.start() func in_action() -> void: visible = true if builded: collision_layer = 0b100 collision_mask = 0b100 energyRecoveryCooldown.stop() func choose_target() -> void: target = null for enemy in availableTargets: if not target || enemy.progress > target.progress: target = enemy func build() -> bool: if builded || not Game.spendMoney(price): return false sprite.modulate = "ffffffff" energyBar.value = energy energyBar.max_value = max_energy $EnergyBar3D.visible = true builded = true $PriceTag.visible = false EventBus.tower_changed.emit(self) return true func towerChange() -> void: energyBar.max_value = max_energy EventBus.tower_changed.emit(self) func _on_range_body_entered(body: Node3D) -> void: if body is Enemy: availableTargets.append(body) choose_target() func _on_range_body_exited(body: Node3D) -> void: if body is Enemy: availableTargets.erase(body) choose_target()