feat: add projectile feature + refactor

This commit is contained in:
Varylios 2025-08-31 21:26:02 +02:00
parent 874a03a5fa
commit 15dc6acf68
19 changed files with 187 additions and 134 deletions

View file

@ -1,26 +0,0 @@
extends CharacterBody3D
class_name Bullet
var target : CharacterBody3D
@export var speed : int = 20
var bullet_damage : int = 1
var start_position
func _ready() -> void:
global_position = start_position
func _physics_process(delta: float) -> void:
if is_instance_valid(target):
velocity = global_position.direction_to(target.global_position) * speed
look_at(target.global_position)
move_and_slide()
else:
queue_free()
func _on_impact_body_entered(body: Node3D) -> void:
if body == target:
var enemy : Enemy = body
enemy.take_damage(bullet_damage)
queue_free()

View file

@ -1,34 +0,0 @@
[gd_scene load_steps=5 format=3 uid="uid://oykrff3g74eo"]
[ext_resource type="Script" uid="uid://b788twwo1o6l2" path="res://Bullets/bullet.gd" id="1_p5eas"]
[ext_resource type="Texture2D" uid="uid://dqyhhvxpwtpsy" path="res://Assets/Emotes/emote_star.png" id="2_46820"]
[sub_resource type="SphereShape3D" id="SphereShape3D_r5o86"]
radius = 0.1
[sub_resource type="SphereShape3D" id="SphereShape3D_dsts2"]
radius = 0.12
[node name="Arrow" type="CharacterBody3D"]
script = ExtResource("1_p5eas")
speed = 10
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.0715332, 0)
shape = SubResource("SphereShape3D_r5o86")
[node name="Sprite3D" type="Sprite3D" parent="."]
transform = Transform3D(2.5, 0, 0, 0, 2.5, 0, 0, 0, 2.5, 0, 0, 0)
billboard = 2
texture = ExtResource("2_46820")
[node name="Impact" type="Area3D" parent="."]
collision_layer = 4
collision_mask = 4
[node name="CollisionShape3D2" type="CollisionShape3D" parent="Impact"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.0714111, 0)
shape = SubResource("SphereShape3D_dsts2")
debug_color = Color(0.926858, 0.237749, 0.335021, 0.42)
[connection signal="body_entered" from="Impact" to="." method="_on_impact_body_entered"]

View file

@ -2,84 +2,84 @@
extends Node
#Emitter : TowerButton.gd
#Subscriber : World.gd
signal tower_selected(towerType : Tower.TYPES)
## [b]Emitter[/b] : [method TowerButton._ready][br]
## [b]Subscriber[/b] : [method PlayerManager._init]
signal tower_selected(towerType : Tower.TYPE)
#Emitter :
#Subscriber :
## [b]Emitter[/b] : [PlayerManager][br]
## [b]Subscriber[/b] hero_icon.gd:
signal tower_builded(tower : Tower)
#Emitter : UpgradeButton.gd
#Subscriber : tower.gd
## [b]Emitter[/b] : [UpgradeButton][br]
## [b]Subscriber[/b] : [Tower]
signal tower_upgraded(tower_to_upgrade : String, upgrade : TowerUpgrade)
#Emitter : cube.gd
#Subscriber : progress_bar_cube_integrity.gd
## [b]Emitter[/b] : [TheCube][br]
## [b]Subscriber[/b] : cube_integrity.gd
signal cube_integrity_changed(_value : int, _max_value : int)
#Emitter : cube.gd
#Subscriber : label_money.gd
## [b]Emitter[/b] : [TheCube][br]
## [b]Subscriber[/b] : label_money.gd
signal money_changed(_value : int)
#Emitter : cube.gd
#Subscriber : label_team.gd
## [b]Emitter[/b] : [PlayerManager][br]
## [b]Subscriber[/b] : label_team.gd
signal tower_count_changed(_value : int)
#Emitter : PlayerManager.gd
#Subscriber : label_team.gd, label_tower_on_terrain.gd
## [b]Emitter[/b] : [PlayerManager][br]
## [b]Subscriber[/b] : label_team.gd, label_tower_on_terrain.gd
signal team_in_action_changed(_value : int)
#Emitter : PlayerManager.gd
#Subscriber : label_tower_in_cube.gd
## [b]Emitter[/b] : [PlayerManager][br]
## [b]Subscriber[/b] : label_tower_in_cube[TheCube]
signal team_in_rest_changed(_value : int)
#Emitter : bullet.gd
#Subscriber : bullet_container.gd
signal bullet_shooted(_value : Bullet)
## [b]Emitter[/b] : [Projectile][br]
## [b]Subscriber[/b] : [code]null[/code]
signal projectile_shooted(_value : Projectile)
#Emitter : enemy.gd
#Subscriber : cube.gd
## [b]Emitter[/b] : [Enemy][br]
## [b]Subscriber[/b] : [TheCube]
signal money_received(_value : int)
#Emitter : world.gd
#Subscriber : cube.gd
## [b]Emitter[/b] : [PlayerManager][br]
## [b]Subscriber[/b] : [TheCube]
signal money_spent(_value : int)
#Emitter : spawner.gd
#Subscriber : cube.gd
## [b]Emitter[/b] : [WaveManager][br]
## [b]Subscriber[/b] : [TheCube]
signal player_has_won()
#Emitter : WaveManager.gd
#Subscriber : cube.gd
## [b]Emitter[/b] : [TheCube][br]
## [b]Subscriber[/b] : [WaveManager]
signal player_defeated()
#Emitter : PlayerManager.gd
#Subscriber : game_menu.gd
## [b]Emitter[/b] : [PlayerManager][br]
## [b]Subscriber[/b] : [code]null[/code]
signal open_shop()
#Emitter : PlayerManager.gd
#Subscriber : game_menu.gd
## [b]Emitter[/b] : [PlayerManager][br]
## [b]Subscriber[/b] : [code]null[/code]
signal close_shop()
#Emitter : tower.gd
#Subscriber : tower button.gd
## [b]Emitter[/b] : [Tower][br]
## [b]Subscriber[/b] : [TowerButton]
signal energy_has_changed(tower : Tower)
#Emitter : WaveManager.gd
#Subscriber : gui.gd
signal allowedTowerHasChange(allowedTowers : Array[Tower.TYPES])
## [b]Emitter[/b] : [WaveManager], [Level][br]
## [b]Subscriber[/b] : gui.gd
signal allowedTowerHasChange(allowedTowers : Array[Tower.TYPE])
#Emitter : WaveManager.gd
#Subscriber : gui.gd
## [b]Emitter[/b] : [WaveManager][br]
## [b]Subscriber[/b] : [code]null[/code]
signal waveHasChange(waveNumber : int)

74
Projectiles/Projectile.gd Normal file
View file

@ -0,0 +1,74 @@
extends CharacterBody3D
class_name Projectile
const ENEMY : int = 1
const ALLY : int = 2
const MINIMUN_AREA : float = .1
@export var speed : int = 20
@export_range(MINIMUN_AREA, 1000) var radius : float = MINIMUN_AREA
@export var isAOE := false
@export_flags("Enemies", "Alliés") var allowedTargets : int = ENEMY
@onready var projectileArea : CollisionShape3D = $Impact/ProjectileArea
@onready var projectileSize : CollisionShape3D = $Impact/ProjectileSize
var target : PhysicsBody3D
var damage : int = 1
var bodiesInRange : Array[Node3D]
var enemiesInRange : Array[Enemy]
var allyInRange : Array[Tower]
func _ready() -> void:
enemiesInRange.append(target)
func _physics_process(delta: float) -> void:
if is_instance_valid(target):
velocity = global_position.direction_to(target.global_position) * speed
look_at(target.global_position)
move_and_slide()
else:
queue_free()
func onBodyEnteredDamageArea(body: Node3D) -> void:
print(body.get_class())
if isAOE && targetable(body):
if body is Enemy:
enemiesInRange.append(body)
if body is Tower:
allyInRange.append(body)
func OnBodyExitedDamageArea(body: Node3D) -> void:
if body is Enemy:
enemiesInRange.erase(body)
if body is Tower:
allyInRange.erase(body)
func onBodyCollideWithProjectile(body : Node3D) -> void:
if body == target:
resolveDamages()
func targetable(body: Node3D) -> bool:
if body is Enemy:
return ENEMY & allowedTargets
if body is Tower:
return ALLY & allowedTargets
return false
func resolveDamages() -> void:
print(enemiesInRange)
for enemy in enemiesInRange:
enemy.take_damage(damage)
queue_free()

View file

@ -0,0 +1,42 @@
[gd_scene load_steps=5 format=3 uid="uid://oykrff3g74eo"]
[ext_resource type="Script" uid="uid://b788twwo1o6l2" path="res://Projectiles/Projectile.gd" id="1_ggq0q"]
[ext_resource type="Texture2D" uid="uid://dqyhhvxpwtpsy" path="res://Assets/Emotes/emote_star.png" id="2_08w86"]
[sub_resource type="SphereShape3D" id="SphereShape3D_r5o86"]
radius = 0.1
[sub_resource type="SphereShape3D" id="SphereShape3D_dsts2"]
radius = 1.5
[node name="Projectile" type="CharacterBody3D"]
script = ExtResource("1_ggq0q")
speed = 10
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
shape = SubResource("SphereShape3D_r5o86")
[node name="Sprite3D" type="Sprite3D" parent="."]
transform = Transform3D(2.5, 0, 0, 0, 2.5, 0, 0, 0, 2.5, 0, 0, 0)
billboard = 2
texture = ExtResource("2_08w86")
[node name="HitBox" type="Area3D" parent="."]
collision_layer = 4
collision_mask = 4
[node name="ProjectileSize" type="CollisionShape3D" parent="HitBox"]
shape = SubResource("SphereShape3D_dsts2")
debug_color = Color(0.926858, 0.237749, 0.335021, 0.42)
[node name="DamageArea" type="Area3D" parent="."]
collision_layer = 4
collision_mask = 4
[node name="ProjectileArea" type="CollisionShape3D" parent="DamageArea"]
shape = SubResource("SphereShape3D_dsts2")
debug_color = Color(0.926858, 0.237749, 0.335021, 0.42)
[connection signal="body_entered" from="HitBox" to="." method="onBodyCollideWithProjectile" flags=3]
[connection signal="body_entered" from="DamageArea" to="." method="onBodyEnteredDamageArea"]
[connection signal="body_exited" from="DamageArea" to="." method="OnBodyExitedDamageArea"]

View file

@ -4,7 +4,7 @@ class_name Level
@export var waves : Array[Wave]
@export var auto_start : bool = false
@export var allowedTowers : Array[Tower.TYPES] : set = allowedTowersHasChanged
@export var allowedTowers : Array[Tower.TYPE] : set = allowedTowersHasChanged
func allowedTowersHasChanged(value) -> void:

View file

@ -7,7 +7,7 @@ enum STATE { IDLE, PLACING }
var _state := STATE.IDLE
@onready var selected_tower : Tower = null
var selected_tower_type : Tower.TYPES = Tower.TYPES.NONE
var selected_tower_type : Tower.TYPE = Tower.TYPE.NONE
@onready var cam : Camera3D = $"../Camera3D"
@onready var the_cube : TheCube = %TheCube
@ -114,13 +114,13 @@ func build_tower() -> void:
emitTeamChanges()
func selectTower(towerType : Tower.TYPES):
func selectTower(towerType : Tower.TYPE):
if selected_tower && not selected_tower.builded:
selected_tower.visible = false
if towerType == selected_tower_type:
selected_tower = null
selected_tower_type = Tower.TYPES.NONE
selected_tower_type = Tower.TYPE.NONE
_state = STATE.IDLE
return

View file

@ -6,6 +6,7 @@
[node name="Pierre" instance=ExtResource("1_7f7qx")]
script = ExtResource("1_v16mf")
tower_name = "Pierre"
tower_type = 1
icone = ExtResource("3_odfqx")
bio = null

View file

@ -4,7 +4,7 @@ class_name TowerListResource
@export var towers : Array[TowerResource] : set = towersHasChanged
func getTowerSceneById(towerType : Tower.TYPES) -> PackedScene :
func getTowerSceneById(towerType : Tower.TYPE) -> PackedScene :
var towerIndex := towers.find_custom(func(towerResource): return towerResource.towerType == towerType)
return towers[towerIndex].towerScene

View file

@ -3,7 +3,7 @@ extends Resource
class_name TowerResource
@export var towerScene : PackedScene : set = towerSceneHasChanged
var towerType : Tower.TYPES
var towerType : Tower.TYPE
func towerSceneHasChanged(value) -> void :
towerScene = value

View file

@ -1,19 +1,20 @@
extends StaticBody3D
class_name Tower
# DANGER NONE Should always be first
enum TYPES { NONE, PIERRE, ALINE, MAXENCE, VICTORIA, EVAN, ALEX, GERALDINE }
var bullet : PackedScene = preload("res://Bullets/bullet.tscn")
@export var tower_name : String = "Pierre"
@export var tower_type : TYPES
# DANGER "NONE" Should always be first
enum TYPE { NONE, PIERRE, ALINE, MAXENCE, VICTORIA, EVAN, ALEX, GERALDINE }
var projectile : PackedScene = preload("res://Projectiles/projectile.tscn")
@export var tower_name : String = "None"
@export var tower_type : TYPE
@export var icone : Texture2D
@export var bio : String
@export var price : int = 100
@export_group("Attack")
@export var bullet_damage : int = 1
@export var projectile_damage : int = 1
@export var action_cooldown : float = 1.5:
set(value):
action_cooldown = clamp(value, 0.3, 999)
@ -97,19 +98,16 @@ func _process(delta: float) -> void:
shoot()
can_shoot = false
$Cooldown.start()
else:
for i in $BulletContainer.get_child_count():
$BulletContainer.get_child(i).queue_free()
func shoot() -> void:
energy -= energy_cost
var temp_bullet : Bullet = bullet.instantiate()
temp_bullet.target = current
temp_bullet.bullet_damage = bullet_damage
temp_bullet.start_position = $Aim.global_position
EventBus.bullet_shooted.emit(temp_bullet)
var projectile : Projectile = projectile.instantiate()
projectile.target = current
projectile.damage = projectile_damage
projectile.global_position = $Aim.global_position
EventBus.projectile_shooted.emit(projectile)
func resting() -> void:

View file

@ -20,7 +20,7 @@ collision_mask = 4
script = ExtResource("1_egfuc")
icone = ExtResource("2_mnaic")
bio = "Aime se promener dans l'herbe et manger des framboises. Sa petite bouille la rend trop mignonne."
bullet_damage = 5
projectile_damage = 5
tower_shop = Array[ExtResource("3_5dr1v")]([ExtResource("3_jv31o"), ExtResource("4_5dr1v")])
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
@ -39,8 +39,6 @@ shape = SubResource("SphereShape3D_pajr1")
[node name="Aim" type="Marker3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.572046, 0)
[node name="BulletContainer" type="Node" parent="."]
[node name="Cooldown" type="Timer" parent="."]
wait_time = 1.5

View file

@ -8,7 +8,7 @@ var towerListResource := preload("res://Towers/towers.tres")
@onready var buttonContainer = $HBoxContainer/ControlPanelBase/MarginContainer/GridContainer
var allowedTowers : Array[Tower.TYPES]
var allowedTowers : Array[Tower.TYPE]
func _ready() -> void:
@ -30,7 +30,7 @@ func _on_button_quit_game_pressed() -> void:
get_tree().quit()
func allowedTowerHasChange(allowedTowers : Array[Tower.TYPES]) -> void:
func allowedTowerHasChange(allowedTowers : Array[Tower.TYPE]) -> void:
self.allowedTowers = allowedTowers
if is_node_ready():
addTowerButtonNodes()

View file

@ -2,7 +2,7 @@
extends Button
class_name TowerButton
var towerType : Tower.TYPES
var towerType : Tower.TYPE
func _ready() -> void:
#font_outline_color = Color.YELLOW

View file

@ -29,9 +29,9 @@ func _ready() -> void:
resetApp()
towerSelector.clear()
towerSelector.max_columns = Tower.TYPES.size()
for towerType : String in Tower.TYPES:
if Tower.TYPES.NONE != Tower.TYPES.get(towerType):
towerSelector.max_columns = Tower.TYPE.size()
for towerType : String in Tower.TYPE:
if Tower.TYPE.NONE != Tower.TYPE.get(towerType):
towerSelector.add_item(" " + towerType + " ")

View file

@ -1,8 +1,8 @@
extends Node3D
func _ready() -> void:
EventBus.bullet_shooted.connect(_on_EventBus_bullet_shooted)
EventBus.projectile_shooted.connect(_on_EventBus_projectile_shooted)
func _on_EventBus_bullet_shooted(bullet : Bullet) -> void:
add_child(bullet)
func _on_EventBus_projectile_shooted(projectile : Projectile) -> void:
add_child(projectile)

View file

@ -34,8 +34,8 @@ enabled=PackedStringArray("res://addons/LevelEditor/plugin.cfg", "res://addons/s
folder_colors={
"res://Assets/": "green",
"res://Bullets/": "orange",
"res://Globals/": "blue",
"res://Projectiles/": "orange",
"res://Scripts/": "blue",
"res://Tiles/": "orange",
"res://Towers/": "orange",