extends CharacterBody2D
# Additional import
var DamageLabelScene = load("res://Scenes/DamageLabel.tscn")
var CorpseScene = preload("res://Scenes/Corpse.tscn")
# Punching Variables
enum State {IDLE, MOVING, PUNCHING}
var state = State.IDLE
var punch_key = ""
var punch_damage = randi_range(7, 12)
var stamina_cost = 5
var bodies_in_hitbox = []
var hitbox_collision_shape
var punch_cooldown = 1.0
var punch_timer = 0.0
var walk_speed = 55
var run_speed = 80
var current_speed = walk_speed
var follow_distance = 300
var minimum_distance = 26 # Adjust this value as needed
var player_follow = false
var player = null
var current_dir = "none"
var last_dir = "down"
var animated_sprite
var linear_velocity = Vector2.ZERO
var running = false
# Attributes
var maxHealth = 100
var currentHealth
var minHealth = 0
var maxStamina = 100
var currentStamina
var minStamina = 0
var hitbox
func _ready():
add_to_group("npc")
animated_sprite = get_node("AnimatedSprite2D")
$healthbar.max_value = maxHealth
currentHealth = maxHealth
$healthbar.value = currentHealth
$staminabar.max_value = maxStamina
currentStamina = maxStamina
$staminabar.value = currentStamina
set_motion_mode(MOTION_MODE_FLOATING)
hitbox = get_node("npc_hitbox")
hitbox_collision_shape = hitbox.get_node("CollisionShape2D")
$PunchTimer.connect("timeout", self._on_punch_timer_timeout)
func _physics_process(delta):
update_running_state()
if state != State.PUNCHING:
move_and_slide()
if player_follow:
follow_player(delta)
velocity = linear_velocity
move_and_slide()
else:
velocity = Vector2.ZERO
update_animation()
punch_timer += delta
if player in bodies_in_hitbox and punch_timer >= punch_cooldown:
check_punch_condition()
punch_timer = 0
func check_punch_condition():
if state != State.PUNCHING and player in bodies_in_hitbox and punch_timer >= punch_cooldown:
perform_punch()
punch_timer = 0
func follow_player(delta):
var distance = global_position.distance_to(player.global_position)
var target_velocity = Vector2.ZERO
if distance <= follow_distance:
target_velocity = (player.global_position - global_position).normalized() * current_speed
if distance < minimum_distance:
target_velocity = Vector2.ZERO
linear_velocity = linear_velocity.lerp(target_velocity, 8 * delta)
func update_running_state():
if currentStamina > 20:
running = true
current_speed = run_speed
else:
running = false
current_speed = walk_speed
@warning_ignore("unused_parameter")
func take_damage(damage, attacker_position):
if currentStamina > 0:
currentStamina -= damage
update_stamina_bar()
else:
currentHealth -= damage
update_health_bar()
var damage_label = DamageLabelScene.instantiate()
damage_label.text = str(damage)
damage_label.modulate = Color.WHITE
var x_offset = randf_range(-10, 10) # Adjust the range as needed
damage_label.position = Vector2(x_offset, -50)
add_child(damage_label)
var tween = get_tree().create_tween()
tween.tween_property(damage_label, "position:y", damage_label.position.y - 100, 1)
tween.tween_property(damage_label, "modulate:a", 0, 1)
if currentHealth <= 0:
die()
func die():
animated_sprite.play("dead")
set_physics_process(false)
await get_tree().create_timer(15).timeout
queue_free()
$CollisionShape2D.disabled = false
func create_corpse():
var corpse = CorpseScene.instance()
corpse.global_position = get_node("npc").global_position
corpse.currentHealth = 0
corpse.take_damage = null
corpse.sprite_resource = animated_sprite.frames
get_parent().add_child(corpse)
await get_tree().create_timer(15).timeout
func update_health_bar():
$healthbar.value = currentHealth
func update_stamina_bar():
$staminabar.value = currentStamina
func update_animation():
if state == State.PUNCHING:
return
var anim_name = ""
@warning_ignore("shadowed_variable")
var state = "walk"
if running:
state = "run"
if velocity.length() < 10: # Add this threshold
anim_name = "idle_" + last_dir
else:
if abs(velocity.x) > abs(velocity.y): # Change this line
if velocity.x > 0:
last_dir = "right"
else:
last_dir = "left"
else:
if velocity.y > 0:
last_dir = "down"
else:
last_dir = "up"
anim_name = state + "_" + last_dir
if animated_sprite.animation != anim_name:
animated_sprite.play(anim_name)
func perform_punch():
print("perform_punch called")
if currentStamina > 0:
punch_damage = randi_range(7, 12)
play_punch_anim()
await get_tree().create_timer(1.0).timeout # Add a delay to wait for the punch animation to finish
for body in bodies_in_hitbox:
if body.has_method("take_damage"):
body.take_damage(punch_damage, global_position) # Pass the player's global_position
currentStamina -= stamina_cost
state = State.IDLE
func play_punch_anim():
print("play_punch_anim called")
if randi() % 2 == 0:
punch_key = "rpunch"
else:
punch_key = "lpunch"
animated_sprite.play(punch_key + "_" + last_dir)
func _process(_delta):
if state == State.PUNCHING and not animated_sprite.is_playing():
_on_punch_anim_finished()
func _on_punch_anim_finished():
if state == State.PUNCHING:
state = State.IDLE
animated_sprite.disconnect("animation_finished", self, "_on_punch_anim_finished")
for body in bodies_in_hitbox:
if body.has_method("take_damage"):
body.take_damage(punch_damage, global_position) # Pass the player's global_position
func _on_detectionarea_body_entered(body):
if body.is_in_group("player"):
player = body
player_follow = true
set_physics_process(true)
print("Following player")
func _on_detectionarea_body_exited(body):
if body.is_in_group("player"):
player = null
player_follow = false
print("Not following player")
func enemy():
pass
func _on_npc_hitbox_body_entered(body):
if body.is_in_group("player"):
bodies_in_hitbox.append(body)
print("npc hitbox")
if not $PunchTimer.is_stopped():
$PunchTimer.start()
func _on_npc_hitbox_body_exited(body):
if body.is_in_group("player"):
bodies_in_hitbox.erase(body)
print("npc out hitbox")
$PunchTimer.stop()
func _on_punch_timer_timeout():
perform_punch()
func _on_animated_sprite_2d_animation_finished():
if state == State.PUNCHING:
state = State.IDLE