inital commit
This commit is contained in:
@@ -0,0 +1,202 @@
|
||||
extends CharacterBody3D
|
||||
|
||||
signal died(points: int)
|
||||
|
||||
enum State { CHASING, FLYING, STUNNED, DEAD }
|
||||
|
||||
var move_speed: float = 3.0
|
||||
var health: int = 30
|
||||
var damage_to_player: int = 8
|
||||
var score_value: int = 10
|
||||
var wall_damage_mult: float = 1.8
|
||||
var chain_factor: float = 0.65
|
||||
var stun_time: float = 0.5
|
||||
var base_scale: float = 1.0
|
||||
|
||||
var state: State = State.CHASING
|
||||
var fly_vel: Vector3 = Vector3.ZERO
|
||||
var stun_timer: float = 0.0
|
||||
var contact_timer: float = 0.0
|
||||
var target: Node3D = null
|
||||
|
||||
var mesh_node: MeshInstance3D
|
||||
var mat: StandardMaterial3D
|
||||
|
||||
var COLOR_CHASE = Color(1.0, 0.28, 0.18)
|
||||
var COLOR_FLY = Color(1.0, 0.85, 0.1)
|
||||
var COLOR_STUN = Color(0.55, 0.55, 0.65)
|
||||
const CONTACT_CD = 0.7
|
||||
const AIR_FRICTION = 0.86
|
||||
|
||||
func _ready() -> void:
|
||||
add_to_group("enemies")
|
||||
_build_mesh()
|
||||
_build_collider()
|
||||
|
||||
func setup(type: String, wave: int) -> void:
|
||||
match type:
|
||||
"slime":
|
||||
move_speed = 2.8 + wave * 0.12
|
||||
health = 28 + wave * 4
|
||||
score_value = 10
|
||||
damage_to_player = 8
|
||||
"bat":
|
||||
move_speed = 5.5 + wave * 0.15
|
||||
health = 14 + wave * 2
|
||||
score_value = 15
|
||||
damage_to_player = 6
|
||||
base_scale = 0.7
|
||||
mesh_node.scale = Vector3(0.7, 0.7, 0.7)
|
||||
COLOR_CHASE = Color(0.6, 0.2, 0.8)
|
||||
mat.albedo_color = COLOR_CHASE
|
||||
"ogre":
|
||||
move_speed = 1.8 + wave * 0.08
|
||||
health = 80 + wave * 12
|
||||
score_value = 25
|
||||
damage_to_player = 18
|
||||
base_scale = 1.5
|
||||
mesh_node.scale = Vector3(1.5, 1.5, 1.5)
|
||||
COLOR_CHASE = Color(0.3, 0.7, 0.3)
|
||||
mat.albedo_color = COLOR_CHASE
|
||||
|
||||
func _build_mesh() -> void:
|
||||
mesh_node = MeshInstance3D.new()
|
||||
var box := BoxMesh.new()
|
||||
box.size = Vector3(0.85, 0.85, 0.85)
|
||||
mesh_node.mesh = box
|
||||
mesh_node.position.y = 0.425
|
||||
mat = StandardMaterial3D.new()
|
||||
mat.albedo_color = COLOR_CHASE
|
||||
mat.roughness = 0.8
|
||||
mesh_node.material_override = mat
|
||||
add_child(mesh_node)
|
||||
|
||||
func _build_collider() -> void:
|
||||
var col := CollisionShape3D.new()
|
||||
var shape := BoxShape3D.new()
|
||||
shape.size = Vector3(0.85, 0.85, 0.85)
|
||||
col.shape = shape
|
||||
col.position.y = 0.425
|
||||
add_child(col)
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
match state:
|
||||
State.CHASING: _chase(delta)
|
||||
State.FLYING: _fly(delta)
|
||||
State.STUNNED: _stun_tick(delta)
|
||||
State.DEAD: pass
|
||||
|
||||
func _chase(delta: float) -> void:
|
||||
if not is_instance_valid(target):
|
||||
return
|
||||
contact_timer = max(0.0, contact_timer - delta)
|
||||
|
||||
var diff := target.global_position - global_position
|
||||
diff.y = 0.0
|
||||
var dist := diff.length()
|
||||
|
||||
if dist < 1.0 and contact_timer <= 0.0:
|
||||
contact_timer = CONTACT_CD
|
||||
if target.has_method("take_damage"):
|
||||
target.take_damage(damage_to_player)
|
||||
|
||||
if dist > 0.05:
|
||||
var dir := diff.normalized()
|
||||
velocity.x = dir.x * move_speed
|
||||
velocity.z = dir.z * move_speed
|
||||
rotation.y = lerp_angle(rotation.y, atan2(dir.x, dir.z), 8.0 * delta)
|
||||
velocity.y = 0.0
|
||||
move_and_slide()
|
||||
|
||||
func _fly(delta: float) -> void:
|
||||
var speed_now := Vector2(fly_vel.x, fly_vel.z).length()
|
||||
velocity = fly_vel
|
||||
velocity.y = 0.0
|
||||
move_and_slide()
|
||||
|
||||
var hit_wall := false
|
||||
for i in get_slide_collision_count():
|
||||
var col := get_slide_collision(i)
|
||||
var col3d := col.get_collider() as Node3D
|
||||
if col3d == null:
|
||||
continue
|
||||
|
||||
if col3d.has_meta("is_wall"):
|
||||
var dmg := int(speed_now * wall_damage_mult)
|
||||
_take_hit(dmg)
|
||||
_wall_impact_effect()
|
||||
fly_vel = Vector3.ZERO
|
||||
velocity = Vector3.ZERO
|
||||
_enter_stun()
|
||||
hit_wall = true
|
||||
break
|
||||
elif col3d.is_in_group("enemies") and col3d != self:
|
||||
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("player"):
|
||||
col3d.call("take_damage", int(speed_now * 0.6))
|
||||
|
||||
if not hit_wall:
|
||||
fly_vel = velocity
|
||||
fly_vel.y = 0.0
|
||||
fly_vel *= pow(AIR_FRICTION, delta * 60.0)
|
||||
if Vector2(fly_vel.x, fly_vel.z).length() < 0.4:
|
||||
_enter_chase()
|
||||
|
||||
# Spin while airborne
|
||||
mesh_node.rotation.y += delta * 10.0
|
||||
|
||||
func _stun_tick(delta: float) -> void:
|
||||
velocity = Vector3.ZERO
|
||||
stun_timer -= delta
|
||||
if stun_timer <= 0.0:
|
||||
_enter_chase()
|
||||
|
||||
func receive_kick(direction: Vector3, force: float) -> void:
|
||||
if state == State.DEAD:
|
||||
return
|
||||
fly_vel = direction * force
|
||||
fly_vel.y = 0.0
|
||||
state = State.FLYING
|
||||
mat.albedo_color = COLOR_FLY
|
||||
var tw := create_tween()
|
||||
tw.tween_property(mesh_node, "scale:y", base_scale * 0.35, 0.06)
|
||||
tw.tween_property(mesh_node, "scale:y", base_scale, 0.18)
|
||||
|
||||
func _enter_stun() -> void:
|
||||
state = State.STUNNED
|
||||
stun_timer = stun_time
|
||||
mat.albedo_color = COLOR_STUN
|
||||
var bs := base_scale
|
||||
var tw := create_tween()
|
||||
tw.tween_property(mesh_node, "scale", Vector3(bs * 1.6, bs * 0.25, bs * 1.6), 0.07)
|
||||
tw.tween_property(mesh_node, "scale", Vector3(bs, bs, bs), 0.22)
|
||||
|
||||
func _enter_chase() -> void:
|
||||
state = State.CHASING
|
||||
mat.albedo_color = COLOR_CHASE
|
||||
|
||||
func _take_hit(dmg: int) -> void:
|
||||
if state == State.DEAD:
|
||||
return
|
||||
health -= dmg
|
||||
if health <= 0:
|
||||
_die()
|
||||
|
||||
func _wall_impact_effect() -> void:
|
||||
var tw := create_tween()
|
||||
tw.tween_property(mat, "albedo_color", Color.WHITE, 0.04)
|
||||
tw.tween_property(mat, "albedo_color", COLOR_STUN, 0.12)
|
||||
|
||||
func _die() -> void:
|
||||
if state == State.DEAD:
|
||||
return
|
||||
state = State.DEAD
|
||||
set_physics_process(false)
|
||||
emit_signal("died", score_value)
|
||||
var tw := create_tween()
|
||||
tw.tween_property(self, "scale", Vector3(2.0, 0.05, 2.0), 0.18)
|
||||
tw.tween_property(self, "scale", Vector3(0.0, 0.0, 0.0), 0.1)
|
||||
tw.tween_callback(queue_free)
|
||||
Reference in New Issue
Block a user