Super-Mario-Bros-Remastered.../Scripts/Classes/Entities/PipeArea.gd
SkyanUltra a681fed56d
Fix sideways pipe hitbox collision (#730)
* Fixed sideways pipe hitbox collision

This fixes a bug where players would clip the top edge of a sideways pipe's hitbox and get counted as being on the floor despite being above the pipe, thus allowing them to get warped into the pipe from there.

* Fix to prevent entering pipes from too low

this implements a simple distance check from the player to the hitbox to ensure the player can only ever enter the pipe when they're close enough in proximity to the pipe, preventing them from entering from a tile too low.

* co mnment

:earnest:
2025-11-22 16:32:59 +00:00

124 lines
3.2 KiB
GDScript

@tool
@icon("res://Assets/Sprites/Editor/Pipe.svg")
class_name PipeArea
extends Node2D
signal pipe_entered
signal pipe_exited
@onready var arrow_joint: Node2D = $ArrowJoint
@onready var arrow: Sprite2D = $ArrowJoint/Arrow
@onready var hitbox: Area2D = $Hitbox
@export_enum("Down", "Up", "Left", "Right") var enter_direction := 0:
set(value):
enter_direction = value
update_visuals()
@export_range(0, 99) var pipe_id := 0:
set(value):
pipe_id = value
update_visuals()
@export_enum("0", "1", "2", "3", "4") var target_sub_level := 0
@export_file("*.tscn") var target_level := ""
@export var exit_only := false:
set(value):
exit_only = value
update_visuals()
var can_enter := true
static var exiting_pipe_id := -1
func _ready() -> void:
update_visuals()
if Engine.is_editor_hint() == false and Global.current_game_mode != Global.GameMode.LEVEL_EDITOR:
run_pipe_check()
func run_pipe_check() -> void:
if exiting_pipe_id == pipe_id and exit_only:
exit_pipe()
func _physics_process(_delta: float) -> void:
# SkyanUltra: Adjusted logic to offset pipe hitbox rather than stretching it,
# as it allowed characters to clip the edge of the pipe and get counted as
# grounded, getting warped into pipes from above.
if enter_direction >= 2:
$Hitbox.position.y = 14
else:
$Hitbox.position.y = 0
if Engine.is_editor_hint() == false:
in_game()
update_visuals()
func update_visuals() -> void:
if Engine.is_editor_hint() or (Global.current_game_mode == Global.GameMode.LEVEL_EDITOR and LevelEditor.playing_level == false):
show()
$ArrowJoint.show()
$ArrowJoint.rotation = get_vector(enter_direction).angle() - deg_to_rad(90)
$ArrowJoint/Arrow.flip_v = exit_only
var id := pipe_id
$Node2D/CenterContainer/Label.text = str(id)
else:
hide()
func exit_pipe() -> void:
can_enter = false
pipe_exited.emit()
for i in get_tree().get_nodes_in_group("Players"):
if i.is_node_ready() == false:
await i.ready
i.go_to_exit_pipe(self)
for i in get_tree().get_nodes_in_group("Players"):
await get_tree().create_timer(0.5, false).timeout
await i.exit_pipe(self)
exiting_pipe_id = -1
can_enter = true
func get_vector(direction := 0) -> Vector2:
match direction:
0:
return Vector2.DOWN
1:
return Vector2.UP
2:
return Vector2.LEFT
3:
return Vector2.RIGHT
_:
return Vector2.ZERO
func get_input_direction(direction := 0) -> String:
match direction:
0:
return "move_down"
1:
return "move_up"
2:
return "move_left"
3:
return "move_right"
_:
return ""
func in_game() -> void:
if exit_only:
return
for i in hitbox.get_overlapping_areas():
if i.owner is Player and can_enter:
run_player_check(i.owner)
func run_player_check(player: Player) -> void:
# guzlad: Added support for characters with a hitbox height below 1.0 to enter pipes underwater
# SkyanUltra: Added distance check to prevent entering pipes from too low.
var distance = player.global_position.distance_to(hitbox.global_position)
if distance <= 6 and Global.player_action_pressed(get_input_direction(enter_direction), player.player_id) and (player.is_actually_on_floor() or enter_direction == 1):
can_enter = false
pipe_entered.emit()
DiscoLevel.can_meter_tick = false
Level.in_vine_level = false
player.enter_pipe(self)