diff --git a/PCs/Mechanic/ClassCards/BombLauncher/tower_bomb_launcher.gd b/PCs/Mechanic/ClassCards/BombLauncher/tower_bomb_launcher.gd index 685c048..7323d60 100644 --- a/PCs/Mechanic/ClassCards/BombLauncher/tower_bomb_launcher.gd +++ b/PCs/Mechanic/ClassCards/BombLauncher/tower_bomb_launcher.gd @@ -9,9 +9,9 @@ func _ready() -> void: func aim() -> void: super.aim() var pos: Vector2 = Vector2(global_position.x, global_position.z) - var t_pos: Vector2 = Vector2(targeted_enemy.global_position.x, targeted_enemy.global_position.z) + var t_pos: Vector2 = Vector2(target_finder.get_target().global_position.x, target_finder.get_target().global_position.z) var x: float = pos.distance_to(t_pos) - var y: float = targeted_enemy.global_position.y - yaw_model.global_position.y + var y: float = target_finder.get_target().global_position.y - yaw_model.global_position.y var v: float = force var g: float = ProjectSettings.get_setting("physics/3d/default_gravity") var v2: float = pow(v, 2) diff --git a/PCs/Mechanic/ClassCards/Gatling/tower_gatling.gd b/PCs/Mechanic/ClassCards/Gatling/tower_gatling.gd index c2436aa..95eb47b 100644 --- a/PCs/Mechanic/ClassCards/Gatling/tower_gatling.gd +++ b/PCs/Mechanic/ClassCards/Gatling/tower_gatling.gd @@ -20,18 +20,14 @@ func _process(delta: float) -> void: func _physics_process(delta: float) -> void: - if !targeted_enemy: - acquire_target() + if !target_finder.get_target(): + time_since_firing_started = 0.0 + current_time_between_shots = time_between_shots else: - if !targeted_enemy.alive or global_position.distance_to(targeted_enemy.global_position) > target_range: - targeted_enemy = null - time_since_firing_started = 0.0 - current_time_between_shots = time_between_shots - if targeted_enemy: - aim() - time_since_firing_started += delta - var progress: float = clamp(time_since_firing_started / time_to_reach_max_speed, 0, 1.0) - current_time_between_shots = lerpf(time_between_shots, final_time_between_shots, progress) - if time_since_firing >= current_time_between_shots: - time_since_firing -= current_time_between_shots - shoot() + aim() + time_since_firing_started += delta + var progress: float = clamp(time_since_firing_started / time_to_reach_max_speed, 0, 1.0) + current_time_between_shots = lerpf(time_between_shots, final_time_between_shots, progress) + if time_since_firing >= current_time_between_shots: + time_since_firing -= current_time_between_shots + shoot() diff --git a/PCs/Mechanic/ClassCards/RocketLauncher/tower_rocket_launcher.gd b/PCs/Mechanic/ClassCards/RocketLauncher/tower_rocket_launcher.gd index 3135c7c..f2390c6 100644 --- a/PCs/Mechanic/ClassCards/RocketLauncher/tower_rocket_launcher.gd +++ b/PCs/Mechanic/ClassCards/RocketLauncher/tower_rocket_launcher.gd @@ -1,63 +1,27 @@ class_name RocketLauncherTower extends ProjectileTower -var target_max: float = 3 -var targets: Array[EnemyController] = [] - func _ready() -> void: super._ready() - target_max = floori(stats.get_attribute("Target Limit")) + target_finder.max_targets = floori(stats.get_attribute("Target Limit")) func _physics_process(_delta: float) -> void: if !is_multiplayer_authority(): #only doing the graphical sort of stuff but not shoot logic - if targeted_enemy and is_instance_valid(targeted_enemy): - if !targeted_enemy.alive or global_position.distance_to(targeted_enemy.global_position) > target_range: - targeted_enemy = null - else: - aim() - return - if targets.size() < target_max: - acquire_target() - if targets.size() > 0: - var valid_targets: Array[EnemyController] = [] - for target: EnemyController in targets: - if is_instance_valid(target) and target.alive and global_position.distance_to(target.global_position) < target_range: - valid_targets.append(target) - targets = valid_targets - if targets.size() > 0: - targeted_enemy = targets[0] - networked_acquire_target.rpc(get_tree().root.get_path_to(targeted_enemy)) + if target_finder.get_multiple_targets().size() >= 1: aim() - if time_since_firing >= time_between_shots: - time_since_firing -= time_between_shots - shoot() - - -func acquire_target() -> void: - var possible_enemies: Array[EnemyController] = [] - for enemy: EnemyController in get_tree().get_nodes_in_group("Enemies"): - if !is_instance_valid(enemy): - continue - if global_position.distance_to(enemy.global_position) > target_range: - continue - if !(enemy.stats.target_type & stats.target_type): - continue - if targets.has(enemy): - continue - possible_enemies.append(enemy) - - for x: int in target_max - targets.size(): - if possible_enemies.size() == 0: - return - var chosen: EnemyController = possible_enemies.pick_random() - possible_enemies.erase(chosen) - targets.append(chosen) + return + if target_finder.get_multiple_targets().size() >= 1: + #networked_acquire_target.rpc(get_tree().root.get_path_to(targeted_enemy)) + aim() + if time_since_firing >= time_between_shots: + time_since_firing -= time_between_shots + shoot() func shoot() -> void: - for target: EnemyController in targets: + for target: EnemyController in target_finder.get_multiple_targets(): networked_spawn_rocket.rpc(get_tree().root.get_path_to(target), multiplayer.get_unique_id()) diff --git a/Scenes/Shredder/shredder.gd b/Scenes/Shredder/shredder.gd index a6b8ffc..7445407 100644 --- a/Scenes/Shredder/shredder.gd +++ b/Scenes/Shredder/shredder.gd @@ -4,3 +4,4 @@ class_name Shredder extends StaticBody3D func _on_interact_button_button_interacted(_value: int, callback: Hero) -> void: var card: Card = callback.inventory.remove_at(callback.inventory_selected_index) as Card callback.currency += 5 * (card.rarity + 1) + callback.decrement_selected() diff --git a/Scenes/Towers/tower.tscn b/Scenes/Towers/tower.tscn index 6d51055..0ee834a 100644 --- a/Scenes/Towers/tower.tscn +++ b/Scenes/Towers/tower.tscn @@ -1,6 +1,7 @@ -[gd_scene load_steps=6 format=3 uid="uid://bvqu1heobgboe"] +[gd_scene load_steps=7 format=3 uid="uid://bvqu1heobgboe"] [ext_resource type="Script" path="res://Scripts/Towers/tower.gd" id="1_u8bfo"] +[ext_resource type="Script" path="res://Scripts/target_finder.gd" id="2_txlxp"] [sub_resource type="Animation" id="Animation_vk4a8"] resource_name = "shoot" @@ -18,8 +19,9 @@ cull_mode = 1 shading_mode = 0 albedo_color = Color(1, 0, 0.415686, 0.223529) -[node name="Tower" type="Node3D" node_paths=PackedStringArray("animator", "pitch_model", "yaw_model", "range_indicator", "audio_player")] +[node name="Tower" type="Node3D" node_paths=PackedStringArray("target_finder", "animator", "pitch_model", "yaw_model", "range_indicator", "audio_player")] script = ExtResource("1_u8bfo") +target_finder = NodePath("TargetFinder") animator = NodePath("AnimationPlayer") pitch_model = NodePath("Pitch") yaw_model = NodePath("Yaw") @@ -51,3 +53,7 @@ radius = 10.0 radial_segments = 16 rings = 16 material = SubResource("StandardMaterial3D_1ucq4") + +[node name="TargetFinder" type="Node" parent="." node_paths=PackedStringArray("tower")] +script = ExtResource("2_txlxp") +tower = NodePath("..") diff --git a/Scripts/EnemyAI/beelining_controller.gd b/Scripts/EnemyAI/beelining_controller.gd index 4051fc1..7ac5411 100644 --- a/Scripts/EnemyAI/beelining_controller.gd +++ b/Scripts/EnemyAI/beelining_controller.gd @@ -5,7 +5,7 @@ var direction: Vector3 func _ready() -> void: - distance_remaining = character.global_position.distance_squared_to(goal.global_position) + distance_remaining = character.global_position.distance_to(goal.global_position) direction = character.global_position.direction_to(goal.global_position) diff --git a/Scripts/Towers/hitscan_tower.gd b/Scripts/Towers/hitscan_tower.gd index 529dad9..c4a9ad7 100644 --- a/Scripts/Towers/hitscan_tower.gd +++ b/Scripts/Towers/hitscan_tower.gd @@ -3,10 +3,9 @@ class_name HitscanTower extends Tower func shoot() -> void: super.shoot() - if targeted_enemy and is_instance_valid(targeted_enemy) and targeted_enemy.alive: - targeted_enemy.damage(damage) - if Data.preferences.display_tower_damage_indicators: - spawn_damage_indicator(targeted_enemy.sprite.global_position) + target_finder.get_target().damage(damage) + if Data.preferences.display_tower_damage_indicators: + spawn_damage_indicator(target_finder.get_target().sprite.global_position) @rpc("reliable") diff --git a/Scripts/Towers/shapecast_tower.gd b/Scripts/Towers/shapecast_tower.gd index 3ba9fb4..ad5536f 100644 --- a/Scripts/Towers/shapecast_tower.gd +++ b/Scripts/Towers/shapecast_tower.gd @@ -7,7 +7,7 @@ class_name ShapecastTower extends Tower func _process(delta: float) -> void: super._process(delta) - if targeted_enemy: + if target_finder.get_target(): particlesystem.emitting = true else: particlesystem.emitting = false @@ -20,8 +20,8 @@ func shoot() -> void: func aim() -> void: - yaw_model.look_at(targeted_enemy.global_position) - pitch_model.look_at(targeted_enemy.global_position) + yaw_model.look_at(target_finder.get_target().global_position) + pitch_model.look_at(target_finder.get_target().global_position) pitch_model.rotation.x = 0.0 diff --git a/Scripts/Towers/status_applying_tower.gd b/Scripts/Towers/status_applying_tower.gd index 37a9451..2de0e37 100644 --- a/Scripts/Towers/status_applying_tower.gd +++ b/Scripts/Towers/status_applying_tower.gd @@ -5,9 +5,9 @@ class_name StatusApplyingTower extends HitscanTower func shoot() -> void: super.shoot() - if targeted_enemy and is_instance_valid(targeted_enemy) and targeted_enemy.alive: - targeted_enemy.damage(damage) - targeted_enemy.status_manager.add_effect(build_status_object()) + if target_finder.get_target(): + target_finder.get_target().damage(damage) + target_finder.get_target().status_manager.add_effect(build_status_object()) func build_status_object() -> StatusEffect: diff --git a/Scripts/Towers/tower.gd b/Scripts/Towers/tower.gd index 705ec59..123e058 100644 --- a/Scripts/Towers/tower.gd +++ b/Scripts/Towers/tower.gd @@ -1,6 +1,7 @@ class_name Tower extends Node3D @export var stats: CardText +@export var target_finder: TargetFinder @export var animator: AnimationPlayer @export var pitch_model: MeshInstance3D @export var yaw_model: MeshInstance3D @@ -10,7 +11,7 @@ class_name Tower extends Node3D var owner_id: int var damage_particle_scene: PackedScene = preload("res://Scenes/damage_particle.tscn") var base_name: String -var targeted_enemy: EnemyController +#var targeted_enemy: EnemyController var time_since_firing: float = 0.0 var time_between_shots: float = 0.0 var damage: float = 0.0 @@ -38,46 +39,22 @@ func _process(delta: float) -> void: func _physics_process(_delta: float) -> void: if !is_multiplayer_authority(): #only doing the graphical sort of stuff but not shoot logic - if targeted_enemy: - if !is_instance_valid(targeted_enemy) or !targeted_enemy.alive or global_position.distance_to(targeted_enemy.global_position) > target_range: - targeted_enemy = null - else: - aim() - return - if !targeted_enemy: - acquire_target() - else: - if !is_instance_valid(targeted_enemy) or !targeted_enemy.alive or global_position.distance_to(targeted_enemy.global_position) > target_range: - targeted_enemy = null - if targeted_enemy: + if target_finder.get_target(): aim() - if time_since_firing >= time_between_shots: - time_since_firing -= time_between_shots - shoot() + return + if target_finder.get_target(): + aim() + if time_since_firing >= time_between_shots: + time_since_firing -= time_between_shots + shoot() func aim() -> void: - yaw_model.look_at(targeted_enemy.global_position) - pitch_model.look_at(targeted_enemy.global_position) + yaw_model.look_at(target_finder.get_target().global_position) + pitch_model.look_at(target_finder.get_target().global_position) pitch_model.rotation.x = 0.0 -func acquire_target() -> void: - var most_progressed_enemy: EnemyController = null - for enemy: EnemyController in get_tree().get_nodes_in_group("Enemies"): - if global_position.distance_to(enemy.global_position) > target_range: - continue - var em_1: EnemyMovement = enemy.movement_controller as EnemyMovement - var em_2: EnemyMovement - if most_progressed_enemy != null: - em_2 = most_progressed_enemy.movement_controller as EnemyMovement - if (most_progressed_enemy == null or em_1.distance_remaining < em_2.distance_remaining) and enemy.stats.target_type & stats.target_type: - most_progressed_enemy = enemy - if most_progressed_enemy != null: - targeted_enemy = most_progressed_enemy - networked_acquire_target.rpc(get_tree().root.get_path_to(most_progressed_enemy)) - - func shoot() -> void: animator.play("shoot") audio_player.play() @@ -98,6 +75,6 @@ func networked_shoot() -> void: shoot() -@rpc("reliable") -func networked_acquire_target(target_node_path: String) -> void: - targeted_enemy = get_tree().root.get_node(target_node_path) +#@rpc("reliable") +#func networked_acquire_target(target_node_path: String) -> void: + #targeted_enemy = get_tree().root.get_node(target_node_path) diff --git a/Scripts/target_finder.gd b/Scripts/target_finder.gd new file mode 100644 index 0000000..e74ff51 --- /dev/null +++ b/Scripts/target_finder.gd @@ -0,0 +1,62 @@ +class_name TargetFinder extends Node + +@export var tower: Tower +@export var max_targets: int = 1 + +var target_cache: EnemyController +var multiple_targets_cache: Array[EnemyController] + + +func get_multiple_targets() -> Array[EnemyController]: + var new_cache: Array[EnemyController] = [] + for enemy: EnemyController in multiple_targets_cache: + if is_instance_valid(enemy) and enemy.alive and enemy.global_position.distance_to(tower.global_position) <= tower.target_range: + new_cache.append(enemy) + if new_cache.size() < max_targets: + multiple_targets_cache = find_multiple_targets(new_cache) + return multiple_targets_cache + + +func get_target() -> EnemyController: + if !is_instance_valid(target_cache) or !target_cache.alive or tower.global_position.distance_to(target_cache.global_position) > tower.target_range: + target_cache = find_enemy() + return target_cache + + +func find_enemy() -> EnemyController: + var most_progressed_enemy: EnemyController = null + for enemy: EnemyController in get_tree().get_nodes_in_group("Enemies"): + if tower.global_position.distance_to(enemy.global_position) > tower.target_range: + continue + var em_1: EnemyMovement = enemy.movement_controller as EnemyMovement + var em_2: EnemyMovement + if most_progressed_enemy != null: + em_2 = most_progressed_enemy.movement_controller as EnemyMovement + if (most_progressed_enemy == null or em_1.distance_remaining < em_2.distance_remaining) and enemy.stats.target_type & tower.stats.target_type: + most_progressed_enemy = enemy + return most_progressed_enemy + #TODO: Figure out how to multiplayer-ize this + #networked_acquire_target.rpc(get_tree().root.get_path_to(most_progressed_enemy)) + + +func find_multiple_targets(existing_cache: Array[EnemyController]) -> Array[EnemyController]: + var possible_enemies: Array[EnemyController] = [] + for enemy: EnemyController in get_tree().get_nodes_in_group("Enemies"): + if !is_instance_valid(enemy): + continue + if tower.global_position.distance_to(enemy.global_position) > tower.target_range: + continue + if !(enemy.stats.target_type & tower.stats.target_type): + continue + if multiple_targets_cache.has(enemy): + continue + possible_enemies.append(enemy) + + for x: int in max_targets - existing_cache.size(): + if possible_enemies.size() == 0: + break + var chosen: EnemyController = possible_enemies.pick_random() + possible_enemies.erase(chosen) + existing_cache.append(chosen) + + return existing_cache