Implement skeleton combat and memory piece progression
- Created Skeleton enemy scene with idle, walk, attack, hurt, and death animations - Added player detection and enemy pursuit AI - Implemented skeleton attack system and player damage - Added player punch attack for adult character - Implemented enemy damage and health system - Added skeleton death handling - Connected MemoryPiece spawning to defeated skeletons - Added configurable memory piece textures and wardrobe assignments - Implemented memory piece collection and return-to-graveyard workflow - Added interact action to Input Map - Continued development of Wardrobe 1 combat encounter - Fixed multiple collision, hitbox, and animation issues
This commit is contained in:
1 parent
89c109b26f
commit
0f7889adfb
21 files changed
+908
-29
No files matched your search
+33
-9
@@ -4,7 +4,7 @@ extends Area2D
|
||||
@export var memory_texture: Texture2D:
|
||||
set(value):
|
||||
memory_texture = value
|
||||
if $Sprite2D:
|
||||
if has_node("Sprite2D"):
|
||||
$Sprite2D.texture = memory_texture
|
||||
|
||||
@export var return_spawn: Marker2D
|
||||
@@ -12,23 +12,47 @@ extends Area2D
|
||||
|
||||
@onready var sprite: Sprite2D = $Sprite2D
|
||||
|
||||
var player_inside := false
|
||||
var player: Player = null
|
||||
|
||||
func _ready() -> void:
|
||||
if memory_texture:
|
||||
sprite.texture = memory_texture
|
||||
|
||||
if not Engine.is_editor_hint():
|
||||
body_entered.connect(_on_body_entered)
|
||||
body_exited.connect(_on_body_exited)
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if Engine.is_editor_hint():
|
||||
return
|
||||
|
||||
if player_inside and Input.is_action_just_pressed("interact"):
|
||||
collect_memory()
|
||||
|
||||
func _on_body_entered(body: Node) -> void:
|
||||
if body is Player:
|
||||
print("Memory piece collected")
|
||||
player_inside = true
|
||||
player = body
|
||||
print("Press ENTER to collect memory piece")
|
||||
|
||||
if wardrobe and wardrobe.has_method("complete_wardrobe"):
|
||||
wardrobe.complete_wardrobe()
|
||||
func _on_body_exited(body: Node) -> void:
|
||||
if body is Player:
|
||||
player_inside = false
|
||||
player = null
|
||||
|
||||
if return_spawn:
|
||||
body.global_position = return_spawn.global_position
|
||||
else:
|
||||
print("No return_spawn assigned to memory piece")
|
||||
func collect_memory() -> void:
|
||||
if player == null:
|
||||
return
|
||||
|
||||
queue_free()
|
||||
print("Memory piece collected")
|
||||
|
||||
if wardrobe and wardrobe.has_method("complete_wardrobe"):
|
||||
wardrobe.complete_wardrobe()
|
||||
|
||||
if return_spawn:
|
||||
player.global_position = return_spawn.global_position
|
||||
else:
|
||||
print("No return_spawn assigned to memory piece")
|
||||
|
||||
queue_free()
|
||||
+28
-6
@@ -11,6 +11,7 @@ const JUMP_VELOCITY = -400.0
|
||||
@onready var player_graphic: AnimatedSprite2D = $PlayerGraphic
|
||||
@onready var camera: Camera2D = $Camera2D
|
||||
@onready var child_graphic: AnimatedSprite2D = $ChildGraphic
|
||||
@onready var punch_hitbox: Area2D = $PunchHitbox
|
||||
var using_child_form := false
|
||||
|
||||
|
||||
@@ -107,7 +108,7 @@ func update_animation():
|
||||
State.DEATH:
|
||||
graphic.play("death")
|
||||
State.PUNCH:
|
||||
graphic.play("Punch")
|
||||
graphic.play("punch")
|
||||
|
||||
func handle_movement(_delta):
|
||||
if direction:
|
||||
@@ -120,18 +121,21 @@ func handle_movement(_delta):
|
||||
get_active_graphic().flip_h = false
|
||||
else:
|
||||
velocity.x = move_toward(velocity.x, 0, SPEED)
|
||||
|
||||
if current_state == State.PUNCH:
|
||||
velocity.x = 0
|
||||
return
|
||||
|
||||
func handle_input():
|
||||
if Input.is_action_just_pressed("ui_accept") and is_on_floor():
|
||||
if Input.is_action_just_pressed("jump") and is_on_floor():
|
||||
velocity.y = JUMP_VELOCITY
|
||||
current_state = State.JUMP
|
||||
upJump = true
|
||||
|
||||
# Child punch attack
|
||||
if Input.is_action_just_pressed("attack"):
|
||||
print("ATTACK PRESSED")
|
||||
print("using_child_form =", using_child_form)
|
||||
current_state = State.PUNCH
|
||||
_do_punch_damage()
|
||||
|
||||
if Input.is_action_just_pressed("shove") && pushEnabled:
|
||||
print("shove pressed")
|
||||
@@ -182,8 +186,8 @@ const STAIRS_LAYER := 3 # correct layer number for "stairs"
|
||||
|
||||
func _ready() -> void:
|
||||
_set_stair_collision(false)
|
||||
# Allow climbing up steeper slopes (default is ~45°)
|
||||
floor_max_angle = deg_to_rad(60) # you can increase or decrease as needed
|
||||
floor_max_angle = deg_to_rad(60)
|
||||
punch_hitbox.monitoring = false
|
||||
|
||||
func set_camera_limits(left: int, top: int, right: int, bottom: int) -> void:
|
||||
camera.limit_left = left
|
||||
@@ -242,6 +246,24 @@ func on_stairs_top_reached(stairs: Node) -> void:
|
||||
if stairs_node == stairs and is_on_stairs:
|
||||
_end_stairs_mode()
|
||||
|
||||
func _do_punch_damage() -> void:
|
||||
print("Punch damage started")
|
||||
|
||||
punch_hitbox.monitoring = true
|
||||
await get_tree().physics_frame
|
||||
|
||||
var bodies = punch_hitbox.get_overlapping_bodies()
|
||||
print("Punch overlaps: ", bodies.size())
|
||||
|
||||
for body in bodies:
|
||||
print("Punch hit: ", body.name)
|
||||
|
||||
if body.has_method("take_damage"):
|
||||
body.take_damage(1)
|
||||
|
||||
await get_tree().create_timer(0.1).timeout
|
||||
punch_hitbox.monitoring = false
|
||||
|
||||
func _on_animation_finished() -> void:
|
||||
match current_state:
|
||||
State.JUMP:
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
extends CharacterBody2D
|
||||
|
||||
@export var speed := 75.0
|
||||
@export var gravity := 980.0
|
||||
@export var max_health := 3
|
||||
@export var attack_range := 40.0
|
||||
@export var memory_piece_scene: PackedScene
|
||||
@export var memory_texture: Texture2D
|
||||
@export var memory_return_spawn: Marker2D
|
||||
@export var memory_wardrobe: Node2D
|
||||
|
||||
@onready var sprite: AnimatedSprite2D = $AnimatedSprite2D
|
||||
@onready var detection_area: Area2D = $Area2D
|
||||
@onready var attack_hitbox = $AttackHitbox
|
||||
|
||||
|
||||
var health := 3
|
||||
var player: Player = null
|
||||
var is_dead := false
|
||||
var is_hurt := false
|
||||
var is_attacking := false
|
||||
var attack_cooldown := false
|
||||
|
||||
func _ready() -> void:
|
||||
health = max_health
|
||||
attack_hitbox.monitoring = false
|
||||
sprite.play("idle")
|
||||
|
||||
detection_area.body_entered.connect(_on_detection_body_entered)
|
||||
detection_area.body_exited.connect(_on_detection_body_exited)
|
||||
|
||||
sprite.animation_finished.connect(_on_animation_finished)
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
if is_dead:
|
||||
return
|
||||
|
||||
if not is_on_floor():
|
||||
velocity.y += gravity * delta
|
||||
|
||||
if is_hurt or is_attacking:
|
||||
velocity.x = 0
|
||||
move_and_slide()
|
||||
return
|
||||
|
||||
if player:
|
||||
var direction := player.global_position.x - global_position.x
|
||||
|
||||
if abs(direction) > attack_range:
|
||||
velocity.x = sign(direction) * speed
|
||||
sprite.play("walk")
|
||||
|
||||
if velocity.x < 0:
|
||||
sprite.flip_h = true
|
||||
elif velocity.x > 0:
|
||||
sprite.flip_h = false
|
||||
else:
|
||||
velocity.x = 0
|
||||
attack()
|
||||
else:
|
||||
velocity.x = move_toward(velocity.x, 0, speed)
|
||||
sprite.play("idle")
|
||||
|
||||
move_and_slide()
|
||||
|
||||
func attack() -> void:
|
||||
if is_attacking:
|
||||
return
|
||||
if attack_cooldown:
|
||||
return
|
||||
is_attacking = true
|
||||
attack_cooldown = true
|
||||
velocity.x = 0
|
||||
sprite.play("attack")
|
||||
|
||||
await get_tree().create_timer(0.2).timeout
|
||||
|
||||
do_attack_damage()
|
||||
|
||||
func do_attack_damage() -> void:
|
||||
print("Checking attack hit")
|
||||
|
||||
attack_hitbox.monitoring = true
|
||||
await get_tree().physics_frame
|
||||
await get_tree().physics_frame
|
||||
|
||||
var bodies = attack_hitbox.get_overlapping_bodies()
|
||||
print("Bodies found: ", bodies.size())
|
||||
|
||||
for body in bodies:
|
||||
if body is Player:
|
||||
print("PLAYER HIT")
|
||||
body.playerTakesDamage(1)
|
||||
|
||||
attack_hitbox.monitoring = false
|
||||
|
||||
func take_damage(amount: int = 1) -> void:
|
||||
if is_dead:
|
||||
return
|
||||
|
||||
health -= amount
|
||||
print("Skeleton health:", health)
|
||||
|
||||
if health <= 0:
|
||||
die()
|
||||
else:
|
||||
is_hurt = true
|
||||
velocity.x = 0
|
||||
sprite.play("hurt")
|
||||
|
||||
func die() -> void:
|
||||
if is_dead:
|
||||
return
|
||||
|
||||
is_dead = true
|
||||
velocity.x = 0
|
||||
sprite.play("death")
|
||||
print("Skeleton died")
|
||||
|
||||
func _on_detection_body_entered(body: Node) -> void:
|
||||
if body is Player:
|
||||
player = body
|
||||
print("Skeleton detected player")
|
||||
|
||||
func _on_detection_body_exited(body: Node) -> void:
|
||||
if body == player:
|
||||
player = null
|
||||
print("Skeleton lost player")
|
||||
|
||||
func _on_animation_finished() -> void:
|
||||
if sprite.animation == "hurt":
|
||||
is_hurt = false
|
||||
|
||||
if sprite.animation == "attack":
|
||||
is_attacking = false
|
||||
await get_tree().create_timer(1.0).timeout
|
||||
attack_cooldown = false
|
||||
|
||||
if sprite.animation == "death":
|
||||
if memory_piece_scene:
|
||||
var piece = memory_piece_scene.instantiate()
|
||||
get_parent().add_child(piece)
|
||||
piece.global_position = global_position
|
||||
|
||||
piece.memory_texture = memory_texture
|
||||
piece.return_spawn = memory_return_spawn
|
||||
piece.wardrobe = memory_wardrobe
|
||||
|
||||
queue_free()
|
||||
@@ -0,0 +1 @@
|
||||
uid://f88hycnuf3m5
|
||||
+1
-1
@@ -19,7 +19,7 @@ func _ready() -> void:
|
||||
area.body_exited.connect(_on_body_exited)
|
||||
|
||||
func _process(_delta: float) -> void:
|
||||
if player_inside and not completed and Input.is_action_just_pressed("ui_up"):
|
||||
if player_inside and not completed and Input.is_action_just_pressed("interact"):
|
||||
enter_wardrobe()
|
||||
|
||||
func _on_body_entered(body: Node) -> void:
|
||||
|
||||
Reference in new issue
Block a user