add kick system

This commit is contained in:
2026-04-22 17:44:54 +03:00
parent 0a637a6160
commit 8a6ecf3f45
4 changed files with 80 additions and 13 deletions
+22 -4
View File
@@ -18,6 +18,7 @@ var chain_factor: float = 0.65
var stun_time: float = 0.5
var base_scale: float = 1.0
var wave_num: int = 1
var damage_modifier: float = 0.75
var state: State = State.CHASING
var fly_vel: Vector3 = Vector3.ZERO
@@ -140,14 +141,18 @@ func _fly(delta: float) -> void:
hit_wall = true
break
elif col3d.is_in_group("enemies") and col3d != self:
var other: Node = col3d
if speed_now >= 3.0 and other.get("enemy_level") == enemy_level and other.get("is_upgrading") == false and is_upgrading == false:
_start_merge(other)
else:
var merged := KickSystem.resolve(self, col3d, fly_vel)
if not merged and is_instance_valid(col3d):
var chain_dir := col3d.global_position - global_position
chain_dir.y = 0.0
if chain_dir.length() > 0.01:
col3d.call("receive_kick", chain_dir.normalized(), speed_now * chain_factor)
elif col3d.is_in_group("rocks"):
KickSystem.resolve(self, col3d, fly_vel)
var rock_dir := col3d.global_position - global_position
rock_dir.y = 0.0
if rock_dir.length() > 0.01:
col3d.call("receive_kick", rock_dir.normalized(), speed_now * 0.5)
elif col3d.is_in_group("player"):
col3d.call("take_damage", int(speed_now * 0.6))
@@ -166,6 +171,19 @@ func _stun_tick(delta: float) -> void:
if stun_timer <= 0.0:
_enter_chase()
func can_merge_with(other: Node3D, collision_speed: float) -> bool:
return (collision_speed >= 3.0
and other.get("enemy_type") == enemy_type
and other.get("enemy_level") == enemy_level
and not is_upgrading
and not other.get("is_upgrading"))
func do_merge_with(other: Node3D) -> void:
_start_merge(other)
func apply_collision_damage(dmg: float) -> void:
_take_hit(int(dmg))
func receive_kick(direction: Vector3, force: float) -> void:
if state == State.DEAD:
return
+41
View File
@@ -0,0 +1,41 @@
class_name KickSystem
# Deduplication: each (A,B) pair resolved once per physics frame
static var _frame: int = -1
static var _pairs: Dictionary = {}
# Called by a flying kickable when it detects another kickable.
# Returns true if a merge happened (both objects will despawn).
static func resolve(owner: Node3D, other: Node3D, owner_vel: Vector3) -> bool:
var f := Engine.get_physics_frames()
if f != _frame:
_frame = f
_pairs.clear()
var id_lo := mini(owner.get_instance_id(), other.get_instance_id())
var id_hi := maxi(owner.get_instance_id(), other.get_instance_id())
var key := str(id_lo) + "_" + str(id_hi)
if _pairs.has(key):
return false
_pairs[key] = true
var other_vel: Vector3 = other.get("fly_vel") if other.get("fly_vel") != null else Vector3.ZERO
var speed_a := Vector2(owner_vel.x, owner_vel.z).length()
var speed_b := Vector2(other_vel.x, other_vel.z).length()
var collision_speed := speed_a + speed_b
# ── 1. Merge ──────────────────────────────────────────────────────────────
if owner.has_method("can_merge_with") and owner.call("can_merge_with", other, collision_speed):
owner.call("do_merge_with", other)
return true
# ── 2. Damage ─────────────────────────────────────────────────────────────
var mod_a: float = owner.get("damage_modifier") if owner.get("damage_modifier") != null else 0.0
var mod_b: float = other.get("damage_modifier") if other.get("damage_modifier") != null else 0.0
if is_instance_valid(other) and other.has_method("apply_collision_damage") and mod_a > 0.0:
other.call("apply_collision_damage", collision_speed * mod_a)
if is_instance_valid(owner) and owner.has_method("apply_collision_damage") and mod_b > 0.0:
owner.call("apply_collision_damage", collision_speed * mod_b)
return false
+1
View File
@@ -0,0 +1 @@
uid://ddke1n1a07f8x
+16 -9
View File
@@ -4,15 +4,14 @@ enum State { IDLE, FLYING }
const AIR_FRICTION := 0.84
const MIN_SPEED := 0.5
const DAMAGE_MULT := 0.9
const WALL_BOUNCE := 0.5
const WALL_SELF_DMG := 0.6
const HIT_SELF_DMG := 0.4
var state: State = State.IDLE
var fly_vel: Vector3 = Vector3.ZERO
var health: float = 60.0
var dead: bool = false
var damage_modifier: float = 1.25
@onready var mesh_node: MeshInstance3D = $RockMesh
var rock_mat: StandardMaterial3D
@@ -25,6 +24,12 @@ func _ready() -> void:
rock_mat = mesh_node.material_override.duplicate() as StandardMaterial3D
mesh_node.material_override = rock_mat
func can_merge_with(_other: Node3D, _speed: float) -> bool:
return false
func apply_collision_damage(dmg: float) -> void:
_take_damage(dmg)
func receive_kick(direction: Vector3, force: float) -> void:
fly_vel = direction * force
fly_vel.y = 0.0
@@ -57,14 +62,16 @@ func _fly(delta: float) -> void:
_take_damage(speed_now * WALL_SELF_DMG)
handled = true
break
elif col3d.is_in_group("enemies"):
var to_enemy := col3d.global_position - global_position
to_enemy.y = 0.0
var dir := to_enemy.normalized() if to_enemy.length() > 0.01 else (fly_vel.normalized() if fly_vel.length() > 0.01 else Vector3.FORWARD)
col3d.call("_take_hit", int(speed_now * DAMAGE_MULT))
col3d.call("receive_kick", dir, speed_now * 0.65)
elif col3d.is_in_group("enemies") or col3d.is_in_group("rocks"):
if col3d == self:
continue
KickSystem.resolve(self, col3d, fly_vel)
if not dead and is_instance_valid(col3d):
var kick_dir := col3d.global_position - global_position
kick_dir.y = 0.0
if kick_dir.length() > 0.01:
col3d.call("receive_kick", kick_dir.normalized(), speed_now * 0.65)
fly_vel *= 0.45
_take_damage(speed_now * HIT_SELF_DMG)
handled = true
break