diff --git a/Globals/Helper.gd b/Globals/Helper.gd index bdacbe7..73d684d 100644 --- a/Globals/Helper.gd +++ b/Globals/Helper.gd @@ -73,13 +73,14 @@ static func showConfirmPopup( text : String, nodeToAppend : Node, confirmCallback : Callable, - cancelCallback : Callable = func(): null + cancelCallback : Callable = func(): pass ) -> void: var confirmPopup : ConfirmPopup = CONFIRM_POPUP.instantiate() nodeToAppend.add_child(confirmPopup) + nodeToAppend.get_tree().paused = true confirmPopup.label.text = text - confirmPopup.confirmed.connect(confirmCallback) - confirmPopup.canceled.connect(cancelCallback) + confirmPopup.confirmed.connect(func(): confirmCallback.call(); nodeToAppend.get_tree().paused = false) + confirmPopup.canceled.connect(func(): cancelCallback.call(); nodeToAppend.get_tree().paused = false) enum POSITION { TOP, CENTER, DOWN } static func getHitBoxLocation(body : CollisionObject3D, position : POSITION) -> float: diff --git a/Projectiles/Projectile.gd b/Projectiles/Projectile.gd index 35f3c0b..6776d15 100644 --- a/Projectiles/Projectile.gd +++ b/Projectiles/Projectile.gd @@ -2,6 +2,9 @@ extends CharacterBody3D class_name Projectile +enum State { INIT, TRAVEL, EFFECT, DESPAWN } + + enum Mode { FOLLOW, ## Follow target LOCATION, ## Go to target location @@ -9,9 +12,15 @@ enum Mode { HITSCAN, ## Spawn on target location } -enum Duration { + +enum Effect { ONE_HIT, ## Make damage on hit - DAMAGE_OVER_TIME, ## Make damage every tick for specified duration[br]work with [member maxTargets] for number of tick + ## Make damage every tick for specified duration[br] + ## work with [member dotTicks] for number of tick + DAMAGE_OVER_TIME, + ## Make damage over time on hitted target for the specified duration[br] + ## work with [member dotTicks] for number of tick + POISON, } enum Type { ## Types of projectiles @@ -23,14 +32,17 @@ enum Type { ## Types of projectiles @export var mode : Mode = Mode.FOLLOW -@export var duration : Duration = Duration.ONE_HIT +@export var effect : Effect = Effect.ONE_HIT @export var type : Type = Type.BASIC @export var speed : int -## Usefull when [enum Type] is not [constant BASIC][br] -## [code]-1[/code] for no maximum -## Used as time when [enum Duration] is [constant HITSCAN] +## Usefull when [enum Type] is not [constant BASIC][br][code]-1[/code] for no maximum @export var maxTargets : int = 1 +## The amount of ticks +## Used as time when [enum Effect] is [constant DAMAGE_OVER_TIME] or [constant POISON] +@export var dotTicks : int +@export var tickInterval : float ## +var state : State = State.INIT var amount : float var target : PhysicsBody3D var vectorTarget : Vector3 @@ -41,25 +53,30 @@ var affectedTarget : Array[Node3D] func _ready() -> void: $HitBox.body_entered.connect(collidingBodies.append) - #$HitBox.body_entered.connect(test) $HitBox.body_exited.connect(collidingBodies.erase) $EffectArea.body_entered.connect(bodiesInRange.append) $EffectArea.body_exited.connect(bodiesInRange.erase) -func test(body): - print(body) - -var firstTick : bool = true func _physics_process(_delta: float) -> void: - if firstTick: - firstTick = false + if state == State.INIT: + state = State.TRAVEL return - resolveContact() + match state: + State.TRAVEL when isOnTarget(): + state = State.EFFECT + applyEffects() + return + State.EFFECT: return + State.DESPAWN: + queue_free.call_deferred() + return if shouldQueueFree(): - queue_free.call_deferred() + state = State.DESPAWN + + if state != State.TRAVEL: return if mode == Mode.FOLLOW: @@ -72,39 +89,63 @@ func _physics_process(_delta: float) -> void: func shouldQueueFree() -> bool: - if maxTargets == 0 || !is_instance_valid(target) && mode == Mode.FOLLOW: - return true - elif mode != Mode.FOLLOW: - return isOnTarget() + if !is_instance_valid(target): + return mode == Mode.FOLLOW || maxTargets == 0 elif target is Tower: return not target.visible - return false + return maxTargets == 0 func isOnTarget() -> bool: match mode: - Mode.LOCATION, Mode.SPAWN_ON_TARGET: return vectorTarget.distance_squared_to(global_position) < .4 + Mode.LOCATION: return vectorTarget.distance_squared_to(global_position) < .4 Mode.FOLLOW: return collidingBodies.has(target) - Mode.HITSCAN: return true + Mode.HITSCAN, Mode.SPAWN_ON_TARGET: return true _: return false -func resolveContact() -> void: - if collidingBodies.is_empty() || not isOnTarget(): - return +func applyEffects() -> void: + if not collidingBodies.is_empty() && isOnTarget(): + resolveContacts() + if effect == Effect.DAMAGE_OVER_TIME: + dotTicks -= 1 + state = State.DESPAWN + if dotTicks > 0: + state = State.EFFECT + await get_tree().create_timer(tickInterval).timeout + applyEffects.call_deferred() + + +func resolveContacts() -> void: match type: Type.AOE: + if maxTargets > 0: # No need to sort if we want to hit all targets + collidingBodies.sort_custom(sortTargets) collidingBodies.map(resolveEffect) _ when collidingBodies.has(target): resolveEffect(target) if type == Type.BOUNCING: - target = null if bodiesInRange.is_empty() else bodiesInRange[0] + target = chooseNextTarget() + state = State.TRAVEL + return + + +func sortTargets(body1: Node3D, body2: Node3D) -> bool: + return vectorTarget.distance_to(body1.global_position) < vectorTarget.distance_to(body2.global_position) + + +func chooseNextTarget() -> Node3D: + var bodies = bodiesInRange.filter(func(body): return not affectedTarget.has(body)) + if bodies.is_empty(): + return null + bodies.sort_custom(sortTargets) + return bodies[0] func resolveEffect(body : Node3D) -> void: - if affectedTarget.has(body) || body is GameTile || maxTargets == 0: + if body is GameTile || maxTargets == 0: return if type == Type.DISABLING && body.has_method("disable"): @@ -112,12 +153,21 @@ func resolveEffect(body : Node3D) -> void: elif body.has_method("take_damage"): body.take_damage(amount) + if effect == Effect.POISON: + dotTicks -= 1 + while dotTicks > 0 && is_instance_valid(body): + dotTicks -= 1 + await get_tree().create_timer(tickInterval).timeout + body.take_damage(amount) + affectedTarget.append(body) maxTargets -= 1 + state = State.DESPAWN func shoot(_target: Node3D, globalPos: Vector3) -> void: target = _target + vectorTarget = globalPos var transform3D : Transform3D = Transform3D() transform3D.origin = globalPos @@ -127,12 +177,11 @@ func shoot(_target: Node3D, globalPos: Vector3) -> void: transform3D = transform3D.looking_at(targetPosition) match mode: Mode.SPAWN_ON_TARGET: + vectorTarget = targetPosition transform3D.origin = targetPosition transform3D = transform3D.looking_at(globalPos) - vectorTarget = targetPosition Mode.LOCATION: vectorTarget = targetPosition velocity = transform3D.origin.direction_to(vectorTarget) * speed - print(transform3D) EventBus.projectile_shooted.emit(self, transform3D) diff --git a/Projectiles/Scenes/projectile-Evan.tscn b/Projectiles/Scenes/projectile-Evan.tscn new file mode 100644 index 0000000..d9d659a --- /dev/null +++ b/Projectiles/Scenes/projectile-Evan.tscn @@ -0,0 +1,41 @@ +[gd_scene load_steps=7 format=3 uid="uid://dp8tg6cpu3ftd"] + +[ext_resource type="PackedScene" uid="uid://oykrff3g74eo" path="res://Projectiles/projectile.tscn" id="1_yw5ty"] + +[sub_resource type="SphereShape3D" id="SphereShape3D_xshf4"] + +[sub_resource type="Gradient" id="Gradient_yw5ty"] +offsets = PackedFloat32Array(0, 0.5, 1) +colors = PackedColorArray(1, 0.007843138, 0, 1, 1, 0.54901963, 0, 1, 1, 0, 0, 1) +metadata/_snap_enabled = true + +[sub_resource type="GradientTexture1D" id="GradientTexture1D_xshf4"] +gradient = SubResource("Gradient_yw5ty") + +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_yw5ty"] +transparency = 4 +albedo_color = Color(1, 1, 1, 0.43137255) +albedo_texture = SubResource("GradientTexture1D_xshf4") + +[sub_resource type="SphereMesh" id="SphereMesh_q317a"] +material = SubResource("StandardMaterial3D_yw5ty") + +[node name="Projectile" instance=ExtResource("1_yw5ty")] +mode = 2 +effect = 1 +type = 1 +maxTargets = -1 +dotTicks = 6 +tickInterval = 0.5 + +[node name="Sprite3D" parent="." index="1"] +visible = false + +[node name="HitBox" parent="." index="2"] +collision_mask = 2 + +[node name="ProjectileSize" parent="HitBox" index="0"] +shape = SubResource("SphereShape3D_xshf4") + +[node name="MeshInstance3D" type="MeshInstance3D" parent="." index="4"] +mesh = SubResource("SphereMesh_q317a") diff --git a/Projectiles/Scenes/projectile-Victoria.tscn b/Projectiles/Scenes/projectile-Victoria.tscn index 46791c6..ae5e8bc 100644 --- a/Projectiles/Scenes/projectile-Victoria.tscn +++ b/Projectiles/Scenes/projectile-Victoria.tscn @@ -2,16 +2,18 @@ [ext_resource type="PackedScene" uid="uid://oykrff3g74eo" path="res://Projectiles/projectile.tscn" id="1_suva6"] -[sub_resource type="SphereShape3D" id="SphereShape3D_k24mn"] -radius = 1.5 +[sub_resource type="SphereShape3D" id="SphereShape3D_878kj"] [node name="Projectile" instance=ExtResource("1_suva6")] type = 2 speed = 5 maxTargets = 3 -[node name="DamageArea#BoucingRange" type="CollisionShape3D" parent="." index="0"] -shape = SubResource("SphereShape3D_k24mn") - -[node name="HitBox" parent="." index="3"] +[node name="HitBox" parent="." index="2"] collision_mask = 2 + +[node name="EffectArea" parent="." index="3"] +collision_mask = 2 + +[node name="CollisionShape3D" type="CollisionShape3D" parent="EffectArea" index="0"] +shape = SubResource("SphereShape3D_878kj") diff --git a/Towers/Scenes/evan.tscn b/Towers/Scenes/evan.tscn index 23c8b88..b15309e 100644 --- a/Towers/Scenes/evan.tscn +++ b/Towers/Scenes/evan.tscn @@ -1,7 +1,8 @@ -[gd_scene load_steps=5 format=3 uid="uid://c4ta0aynybpis"] +[gd_scene load_steps=6 format=3 uid="uid://c4ta0aynybpis"] [ext_resource type="PackedScene" uid="uid://trg7ag3dqr2l" path="res://Towers/tower.tscn" id="1_yctfx"] [ext_resource type="Texture2D" uid="uid://dwwgho6f8f4kj" path="res://Assets/Icones/penguin.svg" id="2_5uh04"] +[ext_resource type="PackedScene" uid="uid://dp8tg6cpu3ftd" path="res://Projectiles/Scenes/projectile-Evan.tscn" id="3_5uh04"] [sub_resource type="SphereShape3D" id="SphereShape3D_y05yr"] radius = 4.0 @@ -14,7 +15,14 @@ tower_name = "Evan" type = 5 icone = ExtResource("2_5uh04") bio = "" +price = 300 +damage = 1 +projectileScene = ExtResource("3_5uh04") towerRange = SubResource("SphereShape3D_y05yr") +action_cooldown = 3.0 +max_energy = 30.0 +energy_regen = 5.0 +energy_cost = 10.0 [node name="EnergyBar3D" parent="." index="6"] texture = SubResource("ViewportTexture_fegyx")