84 lines
2.6 KiB
GDScript
84 lines
2.6 KiB
GDScript
extends Node2D
|
|
|
|
# Sword Slash - Swings around player and deals damage
|
|
|
|
@export var damage: float = 20.0
|
|
@export var swing_speed: float = 720.0 # Degrees per second
|
|
@export var swing_radius: float = 40.0 # Distance from player center (closer swing)
|
|
@export var lifetime: float = 0.3 # How long the slash lasts
|
|
|
|
var swing_angle: float = 0.0 # Current angle
|
|
var swing_start_angle: float = 0.0 # Starting angle
|
|
var swing_arc: float = 180.0 # Total arc to swing (180 degrees)
|
|
var elapsed_time: float = 0.0
|
|
var player_owner: Node = null
|
|
var hit_targets = {} # Track what we've already hit (Dictionary for O(1) lookup)
|
|
|
|
@onready var sprite = $Sprite2D
|
|
@onready var hit_area = $Area2D
|
|
|
|
func _ready():
|
|
# Connect area signals
|
|
if hit_area:
|
|
hit_area.body_entered.connect(_on_body_entered)
|
|
|
|
# Set initial rotation
|
|
rotation = deg_to_rad(swing_start_angle)
|
|
|
|
func setup(start_angle: float, owner_player: Node, arc_direction: float = 1.0):
|
|
swing_start_angle = start_angle
|
|
swing_angle = start_angle
|
|
player_owner = owner_player
|
|
swing_arc = 180.0 * arc_direction # Positive or negative arc
|
|
rotation = deg_to_rad(swing_start_angle)
|
|
|
|
func _physics_process(delta):
|
|
elapsed_time += delta
|
|
|
|
# Check lifetime
|
|
if elapsed_time >= lifetime:
|
|
queue_free()
|
|
return
|
|
|
|
# Calculate swing progress (0 to 1)
|
|
var progress = elapsed_time / lifetime
|
|
|
|
# Swing the sword in an arc
|
|
swing_angle = swing_start_angle + (swing_arc * progress)
|
|
rotation = deg_to_rad(swing_angle)
|
|
|
|
# Position around player if we have one
|
|
if player_owner and is_instance_valid(player_owner):
|
|
var offset = Vector2(swing_radius, 0).rotated(deg_to_rad(swing_angle))
|
|
global_position = player_owner.global_position + offset
|
|
|
|
func _on_body_entered(body):
|
|
# Don't hit the owner
|
|
if body == player_owner:
|
|
return
|
|
|
|
# Don't hit the same target twice - use Dictionary for O(1) lookup to prevent race conditions
|
|
if body in hit_targets:
|
|
return
|
|
|
|
# Add to hit_targets IMMEDIATELY to prevent multiple hits (mark as hit before processing)
|
|
hit_targets[body] = true
|
|
|
|
# Deal damage to players
|
|
if body.is_in_group("player") and body.has_method("take_damage"):
|
|
body.take_damage(damage, global_position)
|
|
print("Sword hit player: ", body.name, " for ", damage, " damage!")
|
|
|
|
# Deal damage to boxes or other damageable objects
|
|
elif "health" in body:
|
|
# Boxes have health property
|
|
body.health -= damage
|
|
if body.health <= 0 and body.has_method("_break_into_pieces"):
|
|
body._break_into_pieces()
|
|
print("Sword hit object: ", body.name)
|
|
|
|
# Push the hit target away slightly
|
|
if body is CharacterBody2D:
|
|
var knockback_dir = (body.global_position - global_position).normalized()
|
|
body.velocity = knockback_dir * 200.0
|