extends CharacterBody2D class_name Plant signal shoot(ufo: UFO) @export var y_offset: float = 0.0 @export var height: float = 100.0 @export_range(1.0, 30.0) var grow_time: float = 1.0 var growing: bool = true var grow_value: float = 0.0 @export_range(0.0, 1.0) var base_scale: float = 0.5 @onready var target_scale: float = get_scale().x @export_range(0.0, 1000.0) var max_hp = 100.0 @onready var hp: float = max_hp var planted_position: Vector2 = Vector2(0, 0) var dead: bool = false var attackers: Array[UFO] = [] var target_attacker: UFO = null # Used when the plant is dying @export var cost: int = 10 @export var bullet_color: Color = Color(1, 1, 1, 1) @export var bullet_base_speed_u: int = 2000 @export var bullet_base_speed_l: int = 1000 @export var constant_sound: bool = false @onready var body = $Body enum TargetMode { NEAREST, HIGHEST, LOWEST } var ufo_search_timer: Timer = Timer.new() var attack_timer: Timer = Timer.new() @onready var ufos: Node2D = get_tree().get_root().find_child("UFOs", true, false) var victim: UFO = null @export var target_mode: TargetMode = TargetMode.NEAREST @export var attack_rate: float = 0.1 @export var attack_range: float = 300.0 @export var attack_damage: float = 10.0 func _ready(): move_local_y(y_offset) planted_position = get_position() ufo_search_timer.set_wait_time(0.1) ufo_search_timer.set_autostart(false) ufo_search_timer.set_one_shot(true) ufo_search_timer.connect("timeout", _on_attack) attack_timer.set_wait_time(attack_rate) attack_timer.set_autostart(false) attack_timer.set_one_shot(true) attack_timer.connect("timeout", _on_attack) add_child(ufo_search_timer) add_child(attack_timer) attack_timer.start() # Workaround crash run out of packets if constant_sound: $Coin.set_volume_db(-100) $Coin.play() func get_class(): return "Plant" func _on_attack(): find_ufo() if victim == null or victim.dead: ufo_search_timer.start() else: emit_signal("shoot", victim) attack_timer.start() if !constant_sound: $Coin.play() func find_ufo(): victim = null var all_ufos = ufos.get_children() var plant_pos = get_global_position() if all_ufos.size() > 0: var best_ufo = all_ufos[0] var best_value = 0.0 if target_mode == TargetMode.NEAREST: best_value = 10000000.0 for ufo in ufos.get_children(): if ufo.dead: continue var tested_value: float if target_mode == TargetMode.NEAREST: tested_value = ufo.get_global_position().distance_to(plant_pos) if tested_value <= attack_range: if tested_value < best_value: best_value = tested_value best_ufo = ufo victim = best_ufo else: tested_value = ufo.hp if tested_value > best_value: best_value = tested_value best_ufo = ufo victim = best_ufo func _process(delta): if growing: grow_value += delta / grow_time if grow_value > 1.0: grow_value = 1.0 growing = false set_scale(Vector2(target_scale, target_scale)) else: var new_scale = target_scale * (base_scale + grow_value * (1.0 - base_scale)) set_scale(Vector2(new_scale, new_scale)) elif dead: if target_attacker == null or target_attacker.dead: select_attacker() if target_attacker != null: velocity = target_attacker.get_global_position() - get_global_position() move_and_slide() var scale = get_scale().x scale *= (1.0 - delta) set_scale(Vector2(scale, scale)) set_z_index(-30) rotate(delta * PI) if scale < 0.1: get_parent().remove_child(self) queue_free() else: if target_attacker == null or target_attacker.dead: alter_hp(delta * max_hp / 10.0) if constant_sound: $Coin.set_volume_db(-100) if victim != null and not victim.dead and constant_sound: $Coin.set_volume_db(-12) func select_attacker(): for attacker in attackers: #if !attacker.dead: target_attacker = attacker func alter_hp(delta: float): hp += delta hp = min(hp, max_hp) set_position(Vector2(planted_position.x, planted_position.y - height * (1.0 - hp / max_hp))) func deal_damage(damage: float, attacker: UFO): if dead: return alter_hp(-damage) if attacker != null: attackers.push_back(attacker) if hp < 0: dead = true func is_ready(): return !growing