➕ADD: Initial Files
This commit is contained in:
1 parent
a6f3f95099
commit
9da1042577
271 files changed
+42251
-15
No files matched your search
@@ -0,0 +1,518 @@
|
||||
extends CharacterBody2D
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# MOVIMIENTO
|
||||
# ============================================================================
|
||||
|
||||
@export_category("Movement")
|
||||
|
||||
@export var speed: float = 300.0
|
||||
@export var ground_acceleration: float = 2200.0
|
||||
@export var ground_friction: float = 2600.0
|
||||
@export var air_acceleration: float = 1000.0
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# SALTO
|
||||
# ============================================================================
|
||||
|
||||
@export_category("Jump")
|
||||
|
||||
@export var jump_velocity: float = -600.0
|
||||
|
||||
# Permite saltar ligeramente después de abandonar el suelo.
|
||||
@export var coyote_time: float = 0.12
|
||||
|
||||
# Permite pulsar salto ligeramente antes de tocar el suelo.
|
||||
@export var jump_buffer_time: float = 0.12
|
||||
|
||||
# Al soltar el botón durante la subida reducimos la velocidad vertical.
|
||||
# Cuanto más pequeño, más brusco será el salto corto.
|
||||
@export_range(0.1, 1.0) var jump_cut_multiplier: float = 0.45
|
||||
|
||||
# La gravedad normal durante la subida.
|
||||
@export var gravity_multiplier: float = 1.0
|
||||
|
||||
# La caída es más rápida que la subida.
|
||||
@export var fall_gravity_multiplier: float = 1.7
|
||||
|
||||
@export var max_fall_speed: float = 1200.0
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# PUÑETAZO
|
||||
# ============================================================================
|
||||
|
||||
@export_category("Punch")
|
||||
|
||||
@export var punch_damage: int = 1
|
||||
@export var punch_duration: float = 0.13
|
||||
@export var punch_cooldown: float = 0.28
|
||||
|
||||
@export var punch_hitbox_size := Vector2(55.0, 38.0)
|
||||
@export var punch_offset := Vector2(38.0, 0.0)
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# GROUND POUND
|
||||
# ============================================================================
|
||||
|
||||
@export_category("Ground Pound")
|
||||
|
||||
@export var ground_pound_damage: int = 3
|
||||
|
||||
# Velocidad mínima que adquiere al comenzar el golpe hacia abajo.
|
||||
@export var ground_pound_start_speed: float = 450.0
|
||||
|
||||
# Gravedad adicional mientras cae haciendo ground pound.
|
||||
@export var ground_pound_gravity_multiplier: float = 4.5
|
||||
|
||||
# Control horizontal mientras cae.
|
||||
@export_range(0.0, 1.0) var ground_pound_horizontal_control: float = 0.25
|
||||
|
||||
@export var ground_pound_radius: float = 85.0
|
||||
@export var ground_pound_offset := Vector2(0.0, 20.0)
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# DEBUG
|
||||
# ============================================================================
|
||||
|
||||
@export_category("Debug")
|
||||
|
||||
@export var show_attack_hitboxes: bool = true
|
||||
@export var debug_impact_duration: float = 0.18
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# NODOS
|
||||
# ============================================================================
|
||||
|
||||
@onready var animated_sprite: AnimatedSprite2D = $AnimatedSprite2D
|
||||
|
||||
@onready var punch_hitbox: Area2D = $PunchHitbox
|
||||
@onready var punch_collision: CollisionShape2D = $PunchHitbox/CollisionShape2D
|
||||
|
||||
@onready var ground_pound_hitbox: Area2D = $GroundPoundHitbox
|
||||
@onready var ground_pound_collision: CollisionShape2D = \
|
||||
$GroundPoundHitbox/CollisionShape2D
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# ESTADO INTERNO
|
||||
# ============================================================================
|
||||
|
||||
var facing_direction: float = 1.0
|
||||
|
||||
var coyote_timer: float = 0.0
|
||||
var jump_buffer_timer: float = 0.0
|
||||
|
||||
var punch_timer: float = 0.0
|
||||
var attack_cooldown_timer: float = 0.0
|
||||
|
||||
var ground_pound_active: bool = false
|
||||
var ground_pound_debug_timer: float = 0.0
|
||||
|
||||
# Evita que un enemigo reciba daño 60 veces durante el mismo puñetazo.
|
||||
var punch_hit_targets: Dictionary = {}
|
||||
|
||||
|
||||
func _ready() -> void:
|
||||
_setup_hitboxes()
|
||||
|
||||
|
||||
func _physics_process(delta: float) -> void:
|
||||
var was_on_floor := is_on_floor()
|
||||
|
||||
_update_timers(delta)
|
||||
_update_jump_windows(delta)
|
||||
|
||||
_handle_attack_input()
|
||||
_handle_jump()
|
||||
_apply_gravity(delta)
|
||||
_handle_horizontal_movement(delta)
|
||||
|
||||
_update_punch_damage()
|
||||
_update_animation()
|
||||
|
||||
move_and_slide()
|
||||
|
||||
# El impacto debe comprobarse DESPUÉS de move_and_slide(),
|
||||
# porque es entonces cuando sabemos si hemos aterrizado.
|
||||
if ground_pound_active and not was_on_floor and is_on_floor():
|
||||
_ground_pound_impact()
|
||||
|
||||
queue_redraw()
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# MOVIMIENTO
|
||||
# ============================================================================
|
||||
|
||||
func _handle_horizontal_movement(delta: float) -> void:
|
||||
var direction := Input.get_axis("move_left", "move_right")
|
||||
|
||||
if direction != 0.0:
|
||||
facing_direction = sign(direction)
|
||||
_update_facing()
|
||||
|
||||
if ground_pound_active:
|
||||
var target_speed := (
|
||||
direction
|
||||
* speed
|
||||
* ground_pound_horizontal_control
|
||||
)
|
||||
|
||||
velocity.x = move_toward(
|
||||
velocity.x,
|
||||
target_speed,
|
||||
air_acceleration * delta
|
||||
)
|
||||
|
||||
return
|
||||
|
||||
if is_on_floor():
|
||||
if direction != 0.0:
|
||||
velocity.x = move_toward(
|
||||
velocity.x,
|
||||
direction * speed,
|
||||
ground_acceleration * delta
|
||||
)
|
||||
else:
|
||||
velocity.x = move_toward(
|
||||
velocity.x,
|
||||
0.0,
|
||||
ground_friction * delta
|
||||
)
|
||||
else:
|
||||
velocity.x = move_toward(
|
||||
velocity.x,
|
||||
direction * speed,
|
||||
air_acceleration * delta
|
||||
)
|
||||
|
||||
|
||||
func _update_facing() -> void:
|
||||
animated_sprite.flip_h = facing_direction < 0.0
|
||||
|
||||
punch_hitbox.position = Vector2(
|
||||
abs(punch_offset.x) * facing_direction,
|
||||
punch_offset.y
|
||||
)
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# GRAVEDAD
|
||||
# ============================================================================
|
||||
|
||||
func _apply_gravity(delta: float) -> void:
|
||||
if is_on_floor():
|
||||
return
|
||||
|
||||
var gravity := get_gravity()
|
||||
|
||||
if ground_pound_active:
|
||||
velocity += (
|
||||
gravity
|
||||
* ground_pound_gravity_multiplier
|
||||
* delta
|
||||
)
|
||||
|
||||
elif velocity.y > 0.0:
|
||||
# Más gravedad al caer.
|
||||
velocity += gravity * fall_gravity_multiplier * delta
|
||||
|
||||
else:
|
||||
# Gravedad normal durante la subida.
|
||||
velocity += gravity * gravity_multiplier * delta
|
||||
|
||||
velocity.y = min(velocity.y, max_fall_speed)
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# SALTO
|
||||
# ============================================================================
|
||||
|
||||
func _update_jump_windows(delta: float) -> void:
|
||||
if is_on_floor():
|
||||
coyote_timer = coyote_time
|
||||
else:
|
||||
coyote_timer = max(coyote_timer - delta, 0.0)
|
||||
|
||||
if Input.is_action_just_pressed("jump"):
|
||||
jump_buffer_timer = jump_buffer_time
|
||||
else:
|
||||
jump_buffer_timer = max(
|
||||
jump_buffer_timer - delta,
|
||||
0.0
|
||||
)
|
||||
|
||||
|
||||
func _handle_jump() -> void:
|
||||
if (
|
||||
jump_buffer_timer > 0.0
|
||||
and coyote_timer > 0.0
|
||||
and not ground_pound_active
|
||||
):
|
||||
velocity.y = jump_velocity
|
||||
|
||||
jump_buffer_timer = 0.0
|
||||
coyote_timer = 0.0
|
||||
|
||||
# Salto variable.
|
||||
#
|
||||
# Mantienes pulsado:
|
||||
# salto alto
|
||||
#
|
||||
# Sueltas pronto:
|
||||
# salto corto
|
||||
if (
|
||||
Input.is_action_just_released("jump")
|
||||
and velocity.y < 0.0
|
||||
):
|
||||
velocity.y *= jump_cut_multiplier
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# ATAQUE
|
||||
# ============================================================================
|
||||
|
||||
func _handle_attack_input() -> void:
|
||||
if not Input.is_action_just_pressed("attack"):
|
||||
return
|
||||
|
||||
if attack_cooldown_timer > 0.0:
|
||||
return
|
||||
|
||||
if is_on_floor():
|
||||
_start_punch()
|
||||
else:
|
||||
_start_ground_pound()
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# PUÑETAZO
|
||||
# ============================================================================
|
||||
|
||||
func _start_punch() -> void:
|
||||
punch_timer = punch_duration
|
||||
attack_cooldown_timer = punch_cooldown
|
||||
|
||||
punch_hit_targets.clear()
|
||||
|
||||
|
||||
func _update_punch_damage() -> void:
|
||||
if punch_timer <= 0.0:
|
||||
return
|
||||
|
||||
for body in punch_hitbox.get_overlapping_bodies():
|
||||
_damage_target(
|
||||
body,
|
||||
punch_damage,
|
||||
punch_hit_targets
|
||||
)
|
||||
|
||||
for area in punch_hitbox.get_overlapping_areas():
|
||||
_damage_target(
|
||||
area,
|
||||
punch_damage,
|
||||
punch_hit_targets
|
||||
)
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# GROUND POUND
|
||||
# ============================================================================
|
||||
|
||||
func _start_ground_pound() -> void:
|
||||
if ground_pound_active:
|
||||
return
|
||||
|
||||
ground_pound_active = true
|
||||
attack_cooldown_timer = punch_cooldown
|
||||
|
||||
# Si todavía estamos subiendo, cortamos inmediatamente la subida.
|
||||
velocity.y = max(
|
||||
velocity.y,
|
||||
ground_pound_start_speed
|
||||
)
|
||||
|
||||
|
||||
func _ground_pound_impact() -> void:
|
||||
ground_pound_active = false
|
||||
ground_pound_debug_timer = debug_impact_duration
|
||||
|
||||
var hit_targets: Dictionary = {}
|
||||
|
||||
for body in ground_pound_hitbox.get_overlapping_bodies():
|
||||
_damage_target(
|
||||
body,
|
||||
ground_pound_damage,
|
||||
hit_targets
|
||||
)
|
||||
|
||||
for area in ground_pound_hitbox.get_overlapping_areas():
|
||||
_damage_target(
|
||||
area,
|
||||
ground_pound_damage,
|
||||
hit_targets
|
||||
)
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# DAÑO
|
||||
# ============================================================================
|
||||
|
||||
func _damage_target(
|
||||
collider: Node,
|
||||
damage: int,
|
||||
hit_targets: Dictionary
|
||||
) -> void:
|
||||
if collider == self:
|
||||
return
|
||||
|
||||
var target := collider
|
||||
|
||||
# Esto permite que el enemigo tenga una Hurtbox Area2D.
|
||||
if not target.has_method("take_damage"):
|
||||
var parent := target.get_parent()
|
||||
|
||||
if parent != null and parent.has_method("take_damage"):
|
||||
target = parent
|
||||
|
||||
if not target.has_method("take_damage"):
|
||||
return
|
||||
|
||||
var target_id := target.get_instance_id()
|
||||
|
||||
if hit_targets.has(target_id):
|
||||
return
|
||||
|
||||
hit_targets[target_id] = true
|
||||
|
||||
target.take_damage(
|
||||
damage,
|
||||
global_position
|
||||
)
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# TIMERS
|
||||
# ============================================================================
|
||||
|
||||
func _update_timers(delta: float) -> void:
|
||||
punch_timer = max(
|
||||
punch_timer - delta,
|
||||
0.0
|
||||
)
|
||||
|
||||
attack_cooldown_timer = max(
|
||||
attack_cooldown_timer - delta,
|
||||
0.0
|
||||
)
|
||||
|
||||
ground_pound_debug_timer = max(
|
||||
ground_pound_debug_timer - delta,
|
||||
0.0
|
||||
)
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# ANIMACIONES
|
||||
# ============================================================================
|
||||
|
||||
func _update_animation() -> void:
|
||||
if ground_pound_active:
|
||||
animated_sprite.play("walk")
|
||||
|
||||
elif not is_on_floor():
|
||||
animated_sprite.play("walk")
|
||||
|
||||
elif abs(velocity.x) > 10.0:
|
||||
animated_sprite.play("walk")
|
||||
|
||||
else:
|
||||
animated_sprite.play("idle")
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# CONFIGURACIÓN DE HITBOX
|
||||
# ============================================================================
|
||||
|
||||
func _setup_hitboxes() -> void:
|
||||
# Puñetazo
|
||||
var punch_shape := RectangleShape2D.new()
|
||||
punch_shape.size = punch_hitbox_size
|
||||
|
||||
punch_collision.shape = punch_shape
|
||||
|
||||
punch_hitbox.collision_layer = 0
|
||||
punch_hitbox.collision_mask = 2
|
||||
punch_hitbox.monitoring = true
|
||||
|
||||
# Ground pound
|
||||
var pound_shape := CircleShape2D.new()
|
||||
pound_shape.radius = ground_pound_radius
|
||||
|
||||
ground_pound_collision.shape = pound_shape
|
||||
|
||||
ground_pound_hitbox.position = ground_pound_offset
|
||||
|
||||
ground_pound_hitbox.collision_layer = 0
|
||||
ground_pound_hitbox.collision_mask = 2
|
||||
ground_pound_hitbox.monitoring = true
|
||||
|
||||
_update_facing()
|
||||
|
||||
|
||||
# ============================================================================
|
||||
# DEBUG HITBOXES
|
||||
# ============================================================================
|
||||
|
||||
func _draw() -> void:
|
||||
if not show_attack_hitboxes:
|
||||
return
|
||||
|
||||
# Puñetazo
|
||||
if punch_timer > 0.0:
|
||||
var center := punch_hitbox.position
|
||||
|
||||
var rect := Rect2(
|
||||
center - punch_hitbox_size / 2.0,
|
||||
punch_hitbox_size
|
||||
)
|
||||
|
||||
draw_rect(
|
||||
rect,
|
||||
Color(1.0, 0.1, 0.1, 0.25),
|
||||
true
|
||||
)
|
||||
|
||||
draw_rect(
|
||||
rect,
|
||||
Color(1.0, 0.1, 0.1, 0.9),
|
||||
false,
|
||||
2.0
|
||||
)
|
||||
|
||||
# Ground pound:
|
||||
# solamente mostramos el área cuando realmente produce daño.
|
||||
if ground_pound_debug_timer > 0.0:
|
||||
var center := ground_pound_hitbox.position
|
||||
|
||||
draw_circle(
|
||||
center,
|
||||
ground_pound_radius,
|
||||
Color(1.0, 0.5, 0.0, 0.20)
|
||||
)
|
||||
|
||||
draw_arc(
|
||||
center,
|
||||
ground_pound_radius,
|
||||
0.0,
|
||||
TAU,
|
||||
48,
|
||||
Color(1.0, 0.5, 0.0, 0.9),
|
||||
3.0
|
||||
)
|
||||
Reference in new issue
Block a user