mirror of
https://github.com/JHDev2006/Super-Mario-Bros.-Remastered-Public.git
synced 2025-10-22 15:38:14 +00:00

Adds a quick change to parallel SMM2. I noticed when trying to duck under a Rocky Wrench's wrench while small, I still got hit. So I added an extra hitbox for when you crouch and are small. It was actually incredibly hard to test if this was accurate in SMM2 since the enemy had Stormtrooper aim. But when it throws a wrench directly at your face and you duck, you can dodge it.
815 lines
26 KiB
GDScript
815 lines
26 KiB
GDScript
class_name Player
|
|
extends CharacterBody2D
|
|
|
|
var AIR_ACCEL := 3.0
|
|
var AIR_SKID := 1.5
|
|
var DECEL := 3.0
|
|
var FALL_GRAVITY := 25.0
|
|
var GROUND_RUN_ACCEL := 1.25
|
|
var GROUND_WALK_ACCEL := 4.0
|
|
var JUMP_GRAVITY := 11.0
|
|
var JUMP_HEIGHT := 300.0
|
|
var JUMP_INCR := 8.0
|
|
var SWIM_GRAVITY := 2.5
|
|
var SWIM_SPEED := 95.0
|
|
var MAX_FALL_SPEED := 280
|
|
var MAX_SWIM_FALL_SPEED := 200
|
|
var RUN_SKID := 8.0
|
|
var RUN_SPEED := 160
|
|
var WALK_SKID := 8.0
|
|
var WALK_SPEED := 96.0
|
|
var CEILING_BUMP_SPEED := 45.0
|
|
@onready var camera_center_joint: Node2D = $CameraCenterJoint
|
|
|
|
@onready var sprite: AnimatedSprite2D = %Sprite
|
|
@onready var camera: Camera2D = $Camera
|
|
@onready var score_note_spawner: ScoreNoteSpawner = $ScoreNoteSpawner
|
|
|
|
var has_jumped := false
|
|
|
|
var direction := 1
|
|
var input_direction := 0
|
|
|
|
var flight_meter := 0.0
|
|
|
|
var velocity_direction := 1
|
|
|
|
var total_keys := 0
|
|
|
|
@export var power_state: PowerUpState = null:
|
|
set(value):
|
|
power_state = value
|
|
set_power_state_frame()
|
|
var character := "Mario"
|
|
|
|
var crouching := false
|
|
var skidding := false
|
|
|
|
var can_bump_sfx := true
|
|
|
|
@export var player_id := 0
|
|
const ONE_UP_NOTE = preload("uid://dopxwjj37gu0l")
|
|
var gravity := FALL_GRAVITY
|
|
|
|
var attacking := false
|
|
var pipe_enter_direction := Vector2.ZERO#
|
|
var pipe_move_direction := 1
|
|
var stomp_combo := 0
|
|
|
|
var is_invincible := false
|
|
|
|
const COMBO_VALS := [100, 200, 400, 500, 800, 1000, 2000, 4000, 5000, 8000, null]
|
|
|
|
@export_enum("Small", "Big", "Fire") var starting_power_state := 0
|
|
@onready var state_machine: StateMachine = $States
|
|
@onready var normal_state: Node = $States/Normal
|
|
@export var auto_death_pit := true
|
|
|
|
var can_hurt := true
|
|
|
|
var in_water := false
|
|
|
|
var has_hammer := false
|
|
|
|
var spring_bouncing := false
|
|
|
|
var low_gravity := false
|
|
|
|
var gravity_vector := Vector2.DOWN
|
|
|
|
var jump_cancelled := false
|
|
|
|
var camera_pan_amount := 24
|
|
|
|
var animating_camera := false
|
|
|
|
var can_uncrouch := false
|
|
|
|
var can_air_turn := false
|
|
|
|
static var CHARACTERS := ["Mario", "Luigi", "Toad", "Toadette"]
|
|
const POWER_STATES := ["Small", "Big", "Fire"]
|
|
|
|
signal moved
|
|
signal dead
|
|
|
|
var is_dead := false
|
|
|
|
static var CHARACTER_NAMES := ["CHAR_MARIO", "CHAR_LUIGI", "CHAR_TOAD", "CHAR_TOADETTE"]
|
|
|
|
static var CHARACTER_COLOURS := [preload("res://Assets/Sprites/Players/Mario/CharacterColour.json"), preload("res://Assets/Sprites/Players/Luigi/CharacterColour.json"), preload("res://Assets/Sprites/Players/Toad/CharacterColour.json"), preload("res://Assets/Sprites/Players/Toadette/CharacterColour.json")]
|
|
|
|
var can_timer_warn := true
|
|
|
|
var colour_palette_texture: Texture = null
|
|
|
|
static var CHARACTER_PALETTES := [
|
|
preload("res://Assets/Sprites/Players/Mario/ColourPalette.json"),
|
|
preload("res://Assets/Sprites/Players/Luigi/ColourPalette.json"),
|
|
preload("res://Assets/Sprites/Players/Toad/ColourPalette.json"),
|
|
preload("res://Assets/Sprites/Players/Toadette/ColourPalette.json")
|
|
]
|
|
|
|
const ANIMATION_FALLBACKS := {
|
|
"JumpFall": "Jump",
|
|
"Fall": "Move",
|
|
"Pipe": "Idle",
|
|
"Walk": "Move",
|
|
"Run": "Move",
|
|
"PipeWalk": "Move",
|
|
"LookUp": "Idle",
|
|
"CrouchFall": "Crouch",
|
|
"CrouchAttack": "Attack",
|
|
"FlagSlide": "Climb",
|
|
"WaterMove": "Move",
|
|
"WaterIdle": "Idle",
|
|
"DieFreeze": "Die",
|
|
"StarJump": "Jump",
|
|
"StarFall": "JumpFall"
|
|
}
|
|
|
|
var palette_transform := true
|
|
var transforming := false
|
|
|
|
static var camera_right_limit := 999999
|
|
|
|
static var times_hit := 0
|
|
|
|
var can_run := true
|
|
|
|
var air_frames := 0
|
|
|
|
static var classic_physics := false
|
|
|
|
var swim_stroke := false
|
|
|
|
var simulated_velocity := Vector2.ZERO
|
|
|
|
func _ready() -> void:
|
|
if classic_physics:
|
|
apply_classic_physics()
|
|
get_viewport().size_changed.connect(recenter_camera)
|
|
show()
|
|
$Checkpoint/Label.text = str(player_id + 1)
|
|
$Checkpoint/Label.modulate = [Color("5050FF"), Color("F73910"), Color("1A912E"), Color("FFB762")][player_id]
|
|
$Checkpoint/Label.visible = Global.connected_players > 1
|
|
Global.can_pause = true
|
|
character = CHARACTERS[int(Global.player_characters[player_id])]
|
|
Global.can_time_tick = true
|
|
if [Global.GameMode.BOO_RACE, Global.GameMode.MARATHON, Global.GameMode.MARATHON_PRACTICE].has(Global.current_game_mode) == false:
|
|
apply_character_physics()
|
|
apply_character_sfx_map()
|
|
Global.level_theme_changed.connect(apply_character_sfx_map)
|
|
Global.level_theme_changed.connect(apply_character_physics)
|
|
Global.level_theme_changed.connect(set_power_state_frame)
|
|
if Global.current_level.first_load and Global.current_game_mode == Global.GameMode.MARATHON_PRACTICE:
|
|
Global.player_power_states[player_id] = "0"
|
|
power_state = $PowerStates.get_node(POWER_STATES[int(Global.player_power_states[player_id])])
|
|
if Global.current_game_mode == Global.GameMode.LEVEL_EDITOR:
|
|
camera.enabled = false
|
|
handle_power_up_states(0)
|
|
set_power_state_frame()
|
|
if Global.level_editor == null:
|
|
recenter_camera()
|
|
|
|
func apply_character_physics() -> void:
|
|
var path = "res://Assets/Sprites/Players/" + character + "/CharacterInfo.json"
|
|
if int(Global.player_characters[player_id]) > 3:
|
|
path = path.replace("res://Assets/Sprites/Players", "user://custom_characters")
|
|
path = ResourceSetter.get_pure_resource_path(path)
|
|
var json = JSON.parse_string(FileAccess.open(path, FileAccess.READ).get_as_text())
|
|
for i in json.physics:
|
|
set(i, json.physics[i])
|
|
|
|
for i in get_tree().get_nodes_in_group("SmallCollisions"):
|
|
var hitbox_scale = json.get("small_hitbox_scale", [1, 1])
|
|
i.scale = Vector2(hitbox_scale[0], hitbox_scale[1])
|
|
i.update()
|
|
for i in get_tree().get_nodes_in_group("BigCollisions"):
|
|
var hitbox_scale = json.get("big_hitbox_scale", [1, 1])
|
|
i.scale = Vector2(hitbox_scale[0], hitbox_scale[1])
|
|
i.update()
|
|
|
|
func apply_classic_physics() -> void:
|
|
var json = JSON.parse_string(FileAccess.open("res://Resources/ClassicPhysics.json", FileAccess.READ).get_as_text())
|
|
for i in json:
|
|
set(i, json[i])
|
|
|
|
func recenter_camera() -> void:
|
|
%CameraHandler.recenter_camera()
|
|
%CameraHandler.update_camera_barriers()
|
|
|
|
func reparent_camera() -> void:
|
|
return
|
|
|
|
func editor_level_start() -> void:
|
|
if PipeArea.exiting_pipe_id == -1:
|
|
power_state = get_node("PowerStates").get_child(starting_power_state)
|
|
handle_power_up_states(0)
|
|
set_power_state_frame()
|
|
camera_make_current()
|
|
recenter_camera()
|
|
state_machine.transition_to("Normal")
|
|
if camera_right_limit <= global_position.x:
|
|
camera_right_limit = 99999999
|
|
await get_tree().create_timer(0.1, false).timeout
|
|
if camera_right_limit <= global_position.x:
|
|
camera_right_limit = 99999999
|
|
|
|
|
|
func _physics_process(delta: float) -> void:
|
|
if Input.is_action_just_pressed("debug_reload"):
|
|
set_power_state_frame()
|
|
if Input.is_action_just_pressed("debug_noclip") and Global.debug_mode:
|
|
state_machine.transition_to("NoClip")
|
|
up_direction = -gravity_vector
|
|
handle_directions()
|
|
handle_block_collision_detection()
|
|
handle_wing_flight(delta)
|
|
air_frames = (air_frames + 1 if is_on_floor() == false else 0)
|
|
for i in get_tree().get_nodes_in_group("StepCollision"):
|
|
var on_wall := false
|
|
for x in [$StepWallChecks/LWall, $StepWallChecks/RWall]:
|
|
if x.is_colliding():
|
|
on_wall = true
|
|
var step_enabled = (not on_wall and air_frames < 4 and velocity.y >= 0)
|
|
i.set_deferred("disabled", not step_enabled)
|
|
if is_actually_on_ceiling() and can_bump_sfx:
|
|
bump_ceiling()
|
|
elif is_actually_on_floor() and not is_invincible:
|
|
stomp_combo = 0
|
|
elif velocity.y > 15:
|
|
can_bump_sfx = true
|
|
handle_water_detection()
|
|
if $SkidSFX.playing:
|
|
if (is_actually_on_floor() and skidding) == false:
|
|
$SkidSFX.stop()
|
|
elif is_actually_on_floor() and skidding and Settings.file.audio.skid_sfx == 1:
|
|
$SkidSFX.play()
|
|
|
|
const BUBBLE_PARTICLE = preload("uid://bwjae1h1airtr")
|
|
|
|
func handle_water_detection() -> void:
|
|
var old_water = in_water
|
|
if $Hitbox.monitoring:
|
|
in_water = $Hitbox.get_overlapping_areas().any(func(area: Area2D): return area is WaterArea) or $WaterDetect.get_overlapping_bodies().is_empty() == false
|
|
if old_water != in_water and in_water == false and flight_meter <= 0:
|
|
water_exited()
|
|
|
|
func summon_bubble() -> void:
|
|
var bubble = BUBBLE_PARTICLE.instantiate()
|
|
bubble.global_position = global_position + Vector2(0, -16 if power_state.hitbox_size == "Small" else -32)
|
|
add_sibling(bubble)
|
|
|
|
func _process(delta: float) -> void:
|
|
handle_power_up_states(delta)
|
|
handle_invincible_palette()
|
|
if is_invincible:
|
|
DiscoLevel.combo_meter = 100
|
|
%Hammer.visible = has_hammer
|
|
|
|
func apply_gravity(delta: float) -> void:
|
|
if in_water or flight_meter > 0:
|
|
gravity = SWIM_GRAVITY
|
|
else:
|
|
if gravity_vector.y > 0:
|
|
if velocity.y > 0:
|
|
gravity = FALL_GRAVITY
|
|
elif gravity_vector.y < 0:
|
|
if velocity.y < 0:
|
|
gravity = FALL_GRAVITY
|
|
velocity += (gravity_vector * ((gravity / (1.5 if low_gravity else 1.0)) / delta)) * delta
|
|
var target_fall: float = MAX_FALL_SPEED
|
|
if in_water:
|
|
target_fall = MAX_SWIM_FALL_SPEED
|
|
if gravity_vector.y > 0:
|
|
velocity.y = clamp(velocity.y, -INF, (target_fall / (1.2 if low_gravity else 1.0)))
|
|
else:
|
|
velocity.y = clamp(velocity.y, -(target_fall / (1.2 if low_gravity else 1.0)), INF)
|
|
|
|
func camera_make_current() -> void:
|
|
camera.enabled = true
|
|
camera.make_current()
|
|
|
|
func play_animation(animation_name := "") -> void:
|
|
if sprite.sprite_frames == null: return
|
|
animation_name = get_fallback_animation(animation_name)
|
|
if sprite.animation != animation_name:
|
|
sprite.play(animation_name)
|
|
|
|
func get_fallback_animation(animation_name := "") -> String:
|
|
if sprite.sprite_frames.has_animation(animation_name) == false and ANIMATION_FALLBACKS.has(animation_name):
|
|
return get_fallback_animation(ANIMATION_FALLBACKS.get(animation_name))
|
|
else:
|
|
return animation_name
|
|
|
|
func apply_character_sfx_map() -> void:
|
|
var path = "res://Assets/Sprites/Players/" + character + "/SFX.json"
|
|
var custom_character := false
|
|
if int(Global.player_characters[player_id]) > 3:
|
|
custom_character = true
|
|
path = path.replace("res://Assets/Sprites/Players", "user://custom_characters")
|
|
path = ResourceSetter.get_pure_resource_path(path)
|
|
var json = JSON.parse_string(FileAccess.open(path, FileAccess.READ).get_as_text())
|
|
|
|
for i in json:
|
|
var res_path = "res://Assets/Audio/SFX/" + json[i]
|
|
res_path = ResourceSetter.get_pure_resource_path(res_path)
|
|
if FileAccess.file_exists(res_path) == false or custom_character:
|
|
var directory = "res://Assets/Sprites/Players/" + character + "/" + json[i]
|
|
if int(Global.player_characters[player_id]) > 3:
|
|
directory = directory.replace("res://Assets/Sprites/Players", "user://custom_characters")
|
|
directory = ResourceSetter.get_pure_resource_path(directory)
|
|
if FileAccess.file_exists(directory):
|
|
json[i] = directory
|
|
else:
|
|
json[i] = res_path
|
|
else:
|
|
json[i] = res_path
|
|
|
|
AudioManager.load_sfx_map(json)
|
|
|
|
func refresh_hitbox() -> void:
|
|
$Hitbox.set_deferred("monitoring", false)
|
|
$Hitbox.set_deferred("monitorable", false)
|
|
await get_tree().physics_frame
|
|
$Hitbox.set_deferred("monitoring", true)
|
|
$Hitbox.set_deferred("monitorable", true)
|
|
|
|
func is_actually_on_floor() -> bool:
|
|
if is_on_floor():
|
|
return true
|
|
else:
|
|
for i in get_tree().get_nodes_in_group("CollisionRays"):
|
|
if i.is_on_floor():
|
|
return true
|
|
return false
|
|
|
|
func is_actually_on_wall() -> bool:
|
|
if is_on_wall():
|
|
return true
|
|
else:
|
|
for i in get_tree().get_nodes_in_group("CollisionRays"):
|
|
if i.is_on_wall():
|
|
return true
|
|
return false
|
|
|
|
func is_actually_on_ceiling() -> bool:
|
|
if is_on_ceiling():
|
|
return true
|
|
else:
|
|
for i in get_tree().get_nodes_in_group("CollisionRays"):
|
|
if i.is_on_ceiling():
|
|
return true
|
|
return false
|
|
|
|
func enemy_bounce_off(add_combo := true) -> void:
|
|
if add_combo:
|
|
add_stomp_combo()
|
|
jump_cancelled = not Global.player_action_pressed("jump", player_id)
|
|
await get_tree().physics_frame
|
|
if Global.player_action_pressed("jump", player_id):
|
|
velocity.y = -300
|
|
gravity = JUMP_GRAVITY
|
|
has_jumped = true
|
|
else:
|
|
velocity.y = -200
|
|
|
|
func add_stomp_combo() -> void:
|
|
if stomp_combo >= 10:
|
|
if Global.current_game_mode == Global.GameMode.CHALLENGE or Settings.file.difficulty.inf_lives:
|
|
Global.score += 10000
|
|
score_note_spawner.spawn_note(10000)
|
|
else:
|
|
Global.lives += 1
|
|
AudioManager.play_global_sfx("1_up")
|
|
score_note_spawner.spawn_one_up_note()
|
|
else:
|
|
Global.score += COMBO_VALS[stomp_combo]
|
|
score_note_spawner.spawn_note(COMBO_VALS[stomp_combo])
|
|
stomp_combo += 1
|
|
|
|
func bump_ceiling() -> void:
|
|
AudioManager.play_sfx("bump", global_position)
|
|
velocity.y = CEILING_BUMP_SPEED
|
|
can_bump_sfx = false
|
|
await get_tree().create_timer(0.1).timeout
|
|
AudioManager.kill_sfx("small_jump")
|
|
AudioManager.kill_sfx("big_jump")
|
|
|
|
func super_star() -> void:
|
|
DiscoLevel.combo_meter += 1
|
|
is_invincible = true
|
|
$StarTimer.start()
|
|
|
|
var colour_palette: Texture = null
|
|
|
|
func stop_all_timers() -> void:
|
|
flight_meter = -1
|
|
for i in [$StarTimer, $HammerTimer]:
|
|
i.stop()
|
|
|
|
func handle_invincible_palette() -> void:
|
|
sprite.material.set_shader_parameter("mode", !Settings.file.visuals.rainbow_style)
|
|
sprite.material.set_shader_parameter("player_palette", $PlayerPalette.texture)
|
|
sprite.material.set_shader_parameter("palette_size", colour_palette.get_width())
|
|
sprite.material.set_shader_parameter("invincible_palette", $InvinciblePalette.texture)
|
|
sprite.material.set_shader_parameter("palette_idx", POWER_STATES.find(power_state.state_name))
|
|
sprite.material.set_shader_parameter("enabled", (is_invincible or (palette_transform and transforming)))
|
|
|
|
func handle_block_collision_detection() -> void:
|
|
if ["Pipe"].has(state_machine.state.name): return
|
|
|
|
if velocity.y <= FALL_GRAVITY:
|
|
for i in $BlockCollision.get_overlapping_bodies():
|
|
if i is Block:
|
|
if is_on_ceiling():
|
|
i.player_block_hit.emit(self)
|
|
func handle_directions() -> void:
|
|
input_direction = 0
|
|
if Global.player_action_pressed("move_right", player_id):
|
|
input_direction = 1
|
|
elif Global.player_action_pressed("move_left", player_id):
|
|
input_direction = -1
|
|
velocity_direction = sign(velocity.x)
|
|
|
|
var use_big_collision := false
|
|
|
|
func handle_power_up_states(delta) -> void:
|
|
var small_crouch := power_state.hitbox_size == "Small" and crouching
|
|
var big_crouch := power_state.hitbox_size == "Big" and crouching
|
|
for i in get_tree().get_nodes_in_group("SmallCollisions"):
|
|
if i.owner == self:
|
|
if i.name.begins_with("Crouch"):
|
|
i.set_deferred("disabled", power_state.hitbox_size == "Small" and !crouching)
|
|
else:
|
|
i.set_deferred("disabled", small_crouch or power_state.hitbox_size == "Big" and !crouching)
|
|
for i in get_tree().get_nodes_in_group("BigCollisions"):
|
|
if i.owner == self:
|
|
i.set_deferred("disabled", power_state.hitbox_size == "Small" or big_crouch)
|
|
$Checkpoint.position.y = -20 if small_crouch else -24 if power_state.hitbox_size == "Small" or big_crouch else -40
|
|
power_state.update(delta)
|
|
|
|
func handle_wing_flight(delta: float) -> void:
|
|
flight_meter -= delta
|
|
if flight_meter <= 0 && %Wings.visible:
|
|
AudioManager.stop_music_override(AudioManager.MUSIC_OVERRIDES.WING)
|
|
gravity = FALL_GRAVITY
|
|
%Wings.visible = flight_meter >= 0
|
|
if flight_meter < 0:
|
|
return
|
|
%BigWing.visible = power_state.hitbox_size == "Big"
|
|
%SmallWing.visible = power_state.hitbox_size == "Small"
|
|
for i in [%SmallWing, %BigWing]:
|
|
if velocity.y < 0:
|
|
i.play("Flap")
|
|
else:
|
|
i.play("Idle")
|
|
if flight_meter <= 3:
|
|
%Wings.get_node("AnimationPlayer").play("Flash")
|
|
else:
|
|
%Wings.get_node("AnimationPlayer").play("RESET")
|
|
|
|
func damage() -> void:
|
|
if can_hurt == false or is_invincible:
|
|
return
|
|
times_hit += 1
|
|
var damage_state = power_state.damage_state
|
|
if damage_state != null:
|
|
if Settings.file.difficulty.damage_style == 0:
|
|
damage_state = get_node("PowerStates/Small")
|
|
DiscoLevel.combo_meter -= 50
|
|
AudioManager.play_sfx("damage", global_position)
|
|
await power_up_animation(damage_state.state_name)
|
|
power_state = get_node("PowerStates/" + damage_state.state_name)
|
|
Global.player_power_states[player_id] = str(power_state.get_index())
|
|
do_i_frames()
|
|
else:
|
|
die()
|
|
|
|
var cam_direction := 1
|
|
@onready var last_position := global_position
|
|
|
|
@onready var camera_position = camera.global_position
|
|
var camera_offset = Vector2.ZERO
|
|
|
|
func point_to_camera_limit(point := 0, point_dir := -1) -> float:
|
|
return point + ((get_viewport_rect().size.x / 2.0) * -point_dir)
|
|
|
|
func point_to_camera_limit_y(point := 0, point_dir := -1) -> float:
|
|
return point + ((get_viewport_rect().size.y / 2.0) * -point_dir)
|
|
|
|
func passed_checkpoint() -> void:
|
|
if Settings.file.difficulty.checkpoint_style == 0:
|
|
$Checkpoint/Animation.play("Show")
|
|
AudioManager.play_sfx("checkpoint", global_position)
|
|
|
|
func do_i_frames() -> void:
|
|
can_hurt = false
|
|
for i in 25:
|
|
sprite.hide()
|
|
if get_tree() == null:
|
|
return
|
|
await get_tree().create_timer(0.04, false).timeout
|
|
sprite.show()
|
|
if get_tree() == null:
|
|
return
|
|
await get_tree().create_timer(0.04, false).timeout
|
|
can_hurt = true
|
|
refresh_hitbox()
|
|
|
|
func die(pit := false) -> void:
|
|
if ["Dead", "Pipe", "LevelExit"].has(state_machine.state.name):
|
|
return
|
|
is_dead = true
|
|
visible = not pit
|
|
flight_meter = 0
|
|
dead.emit()
|
|
Global.p_switch_active = false
|
|
Global.p_switch_timer = 0
|
|
stop_all_timers()
|
|
state_machine.transition_to("Dead", {"Pit": pit})
|
|
process_mode = Node.PROCESS_MODE_ALWAYS
|
|
get_tree().paused = true
|
|
Level.can_set_time = true
|
|
Level.first_load = true
|
|
if Global.current_game_mode != Global.GameMode.BOO_RACE:
|
|
AudioManager.set_music_override(AudioManager.MUSIC_OVERRIDES.DEATH, 999, false)
|
|
await get_tree().create_timer(3).timeout
|
|
else:
|
|
AudioManager.set_music_override(AudioManager.MUSIC_OVERRIDES.RACE_LOSE, 999, false)
|
|
await get_tree().create_timer(5).timeout
|
|
|
|
death_load()
|
|
|
|
func death_load() -> void:
|
|
power_state = get_node("PowerStates/Small")
|
|
Global.player_power_states = "0000"
|
|
if Global.death_load:
|
|
return
|
|
Global.death_load = true
|
|
if Global.current_game_mode == Global.GameMode.CUSTOM_LEVEL:
|
|
LevelTransition.level_to_transition_to = "res://Scenes/Levels/LevelEditor.tscn"
|
|
Global.transition_to_scene("res://Scenes/Levels/LevelTransition.tscn")
|
|
return
|
|
if Global.current_game_mode == Global.GameMode.LEVEL_EDITOR:
|
|
owner.stop_testing()
|
|
return
|
|
if [Global.GameMode.CAMPAIGN, Global.GameMode.MARATHON].has(Global.current_game_mode):
|
|
if Settings.file.difficulty.inf_lives == 0:
|
|
Global.lives -= 1
|
|
Global.death_load = true
|
|
if Global.current_game_mode == Global.GameMode.CHALLENGE:
|
|
Global.transition_to_scene("res://Scenes/Levels/ChallengeMiss.tscn")
|
|
elif Global.time <= 0:
|
|
Global.transition_to_scene("res://Scenes/Levels/TimeUp.tscn")
|
|
elif Global.lives <= 0 and Settings.file.difficulty.inf_lives == 0:
|
|
Global.death_load = false
|
|
Global.transition_to_scene("res://Scenes/Levels/GameOver.tscn")
|
|
else:
|
|
LevelPersistance.reset_states()
|
|
if Global.current_game_mode == Global.GameMode.BOO_RACE:
|
|
Global.reset_values()
|
|
Global.clear_saved_values()
|
|
Level.start_level_path = Global.current_level.scene_file_path
|
|
Global.current_level.reload_level()
|
|
|
|
func time_up() -> void:
|
|
die()
|
|
|
|
func set_power_state_frame() -> void:
|
|
colour_palette = ResourceSetter.get_resource(preload("uid://b0quveyqh25dn"))
|
|
$PlayerPalette/ResourceSetterNew.resource_json = (CHARACTER_PALETTES[int(Global.player_characters[player_id])])
|
|
if power_state != null:
|
|
$ResourceSetterNew.resource_json = load(get_character_sprite_path())
|
|
$ResourceSetterNew.update_resource()
|
|
|
|
func get_power_up(power_name := "") -> void:
|
|
if is_dead:
|
|
return
|
|
Global.score += 1000
|
|
DiscoLevel.combo_amount += 1
|
|
score_note_spawner.spawn_note(1000)
|
|
AudioManager.play_sfx("power_up", global_position)
|
|
if Settings.file.difficulty.damage_style == 0 and power_state.state_name != power_name:
|
|
if power_name != "Big" and power_state.state_name != "Big":
|
|
power_name = "Big"
|
|
var new_power_state = get_node("PowerStates/" + power_name)
|
|
if new_power_state.power_tier >= power_state.power_tier and new_power_state != power_state:
|
|
can_hurt = false
|
|
await power_up_animation(power_name)
|
|
else:
|
|
return
|
|
if new_power_state.hitbox_size == "Big" and power_state.hitbox_size == "Small":
|
|
check_for_block()
|
|
power_state = new_power_state
|
|
Global.player_power_states[player_id] = str(power_state.get_index())
|
|
can_hurt = true
|
|
refresh_hitbox()
|
|
|
|
func check_for_block() -> void:
|
|
if test_move(global_transform, (Vector2.UP * gravity_vector) * 4):
|
|
crouching = true
|
|
|
|
func power_up_animation(new_power_state := "") -> void:
|
|
if normal_state.jump_buffer > 0:
|
|
normal_state.jump_buffer += 10
|
|
var old_frames = sprite.sprite_frames
|
|
var new_frames = $ResourceSetterNew.get_resource(load(get_character_sprite_path(new_power_state)))
|
|
sprite.process_mode = Node.PROCESS_MODE_ALWAYS
|
|
sprite.show()
|
|
get_tree().paused = true
|
|
if get_node("PowerStates/" + new_power_state).hitbox_size != power_state.hitbox_size:
|
|
if Settings.file.visuals.transform_style == 0:
|
|
sprite.speed_scale = 3
|
|
sprite.play("Grow")
|
|
await get_tree().create_timer(0.4, true).timeout
|
|
sprite.sprite_frames = new_frames
|
|
sprite.play("Grow")
|
|
await get_tree().create_timer(0.4, true).timeout
|
|
transforming = false
|
|
else:
|
|
sprite.speed_scale = 0
|
|
if new_power_state == "Small":
|
|
%GrowAnimation.play("Shrink")
|
|
else:
|
|
sprite.sprite_frames = new_frames
|
|
%GrowAnimation.play("Grow")
|
|
await get_tree().create_timer(0.8, true).timeout
|
|
sprite.sprite_frames = new_frames
|
|
transforming = false
|
|
else:
|
|
if Settings.file.visuals.transform_style == 1:
|
|
for i in 6:
|
|
sprite.sprite_frames = new_frames
|
|
await get_tree().create_timer(0.05).timeout
|
|
sprite.sprite_frames = old_frames
|
|
await get_tree().create_timer(0.05).timeout
|
|
else:
|
|
sprite.stop()
|
|
sprite.material.set_shader_parameter("enabled", true)
|
|
transforming = true
|
|
await get_tree().create_timer(0.6).timeout
|
|
transforming = false
|
|
get_tree().paused = false
|
|
sprite.process_mode = Node.PROCESS_MODE_PAUSABLE
|
|
if Global.player_action_just_pressed("jump", player_id):
|
|
jump()
|
|
return
|
|
|
|
const RESERVE_ITEM = preload("res://Scenes/Prefabs/Entities/Items/ReserveItem.tscn")
|
|
|
|
func dispense_stored_item() -> void:
|
|
add_sibling(RESERVE_ITEM.instantiate())
|
|
|
|
func get_character_sprite_path(power_stateto_use := power_state.state_name) -> String:
|
|
var path = "res://Assets/Sprites/Players/" + character + "/" + power_stateto_use + ".json"
|
|
if int(Global.player_characters[player_id]) > 3:
|
|
path = path.replace("res://Assets/Sprites/Players", "user://custom_characters")
|
|
return path
|
|
|
|
func enter_pipe(pipe: PipeArea, warp_to_level := true) -> void:
|
|
z_index = -10
|
|
Global.can_pause = false
|
|
Global.can_time_tick = false
|
|
pipe_enter_direction = pipe.get_vector(pipe.enter_direction)
|
|
if pipe_enter_direction.x != 0:
|
|
global_position.y = pipe.global_position.y + 14
|
|
AudioManager.play_sfx("pipe", global_position)
|
|
state_machine.transition_to("Pipe")
|
|
PipeArea.exiting_pipe_id = pipe.pipe_id
|
|
hide_pipe_animation()
|
|
if warp_to_level:
|
|
await get_tree().create_timer(1, false).timeout
|
|
if Global.current_game_mode == Global.GameMode.LEVEL_EDITOR or Global.current_game_mode == Global.GameMode.CUSTOM_LEVEL:
|
|
LevelEditor.play_pipe_transition = true
|
|
owner.transition_to_sublevel(pipe.target_sub_level)
|
|
else:
|
|
Global.transition_to_scene(pipe.target_level)
|
|
|
|
func hide_pipe_animation() -> void:
|
|
if pipe_enter_direction.x != 0:
|
|
await get_tree().create_timer(0.3, false).timeout
|
|
hide()
|
|
else:
|
|
await get_tree().create_timer(0.6, false).timeout
|
|
hide()
|
|
|
|
func go_to_exit_pipe(pipe: PipeArea) -> void:
|
|
Global.can_time_tick = false
|
|
pipe_enter_direction = Vector2.ZERO
|
|
state_machine.transition_to("Pipe")
|
|
global_position = pipe.global_position + (pipe.get_vector(pipe.enter_direction) * 32)
|
|
if pipe.enter_direction == 1:
|
|
global_position = pipe.global_position + Vector2(0, -8)
|
|
recenter_camera()
|
|
if pipe.get_vector(pipe.enter_direction).y == 0:
|
|
global_position.y += 16
|
|
global_position.x -= 8 * pipe.get_vector(pipe.enter_direction).x
|
|
reset_physics_interpolation()
|
|
hide()
|
|
|
|
func exit_pipe(pipe: PipeArea) -> void:
|
|
show()
|
|
pipe_enter_direction = -pipe.get_vector(pipe.enter_direction)
|
|
AudioManager.play_sfx("pipe", global_position)
|
|
state_machine.transition_to("Pipe")
|
|
await get_tree().create_timer(0.6, false).timeout
|
|
Global.can_pause = true
|
|
state_machine.transition_to("Normal")
|
|
Global.can_time_tick = true
|
|
|
|
func jump() -> void:
|
|
if spring_bouncing:
|
|
return
|
|
velocity.y = calculate_jump_height() * gravity_vector.y
|
|
gravity = JUMP_GRAVITY
|
|
AudioManager.play_sfx("small_jump" if power_state.hitbox_size == "Small" else "big_jump", global_position)
|
|
has_jumped = true
|
|
await get_tree().physics_frame
|
|
has_jumped = true
|
|
|
|
func calculate_jump_height() -> float: # Thanks wye love you xxx
|
|
return -(JUMP_HEIGHT + JUMP_INCR * int(abs(velocity.x) / 25))
|
|
|
|
const SMOKE_PARTICLE = preload("res://Scenes/Prefabs/Particles/SmokeParticle.tscn")
|
|
|
|
func teleport_player(new_position := Vector2.ZERO) -> void:
|
|
hide()
|
|
do_smoke_effect()
|
|
var old_state = state_machine.state.name
|
|
state_machine.transition_to("Freeze")
|
|
await get_tree().create_timer(0.5, false).timeout
|
|
global_position = new_position
|
|
recenter_camera()
|
|
await get_tree().create_timer(0.5, false).timeout
|
|
state_machine.transition_to(old_state)
|
|
show()
|
|
velocity.y = 0
|
|
do_smoke_effect()
|
|
|
|
func do_smoke_effect() -> void:
|
|
for i in 2:
|
|
var node = SMOKE_PARTICLE.instantiate()
|
|
node.global_position = global_position - Vector2(0, 16 * i)
|
|
add_sibling(node)
|
|
if power_state.hitbox_size == "Small":
|
|
break
|
|
AudioManager.play_sfx("magic", global_position)
|
|
|
|
func on_timeout() -> void:
|
|
AudioManager.stop_music_override(AudioManager.MUSIC_OVERRIDES.STAR)
|
|
await get_tree().create_timer(1, false).timeout
|
|
is_invincible = false
|
|
|
|
|
|
func on_area_entered(area: Area2D) -> void:
|
|
if area.owner is Player and area.owner != self:
|
|
if area.owner.velocity.y > 0 and area.owner.is_actually_on_floor() == false:
|
|
area.owner.enemy_bounce_off(false)
|
|
velocity.y = 50
|
|
AudioManager.play_sfx("bump", global_position)
|
|
|
|
func hammer_get() -> void:
|
|
has_hammer = true
|
|
$HammerTimer.start()
|
|
AudioManager.set_music_override(AudioManager.MUSIC_OVERRIDES.HAMMER, 0, false)
|
|
|
|
func on_hammer_area_entered(area: Area2D) -> void:
|
|
pass
|
|
|
|
func wing_get() -> void:
|
|
AudioManager.set_music_override(AudioManager.MUSIC_OVERRIDES.WING, 0, false, false)
|
|
flight_meter = 10
|
|
|
|
func on_hammer_timeout() -> void:
|
|
has_hammer = false
|
|
AudioManager.stop_music_override(AudioManager.MUSIC_OVERRIDES.HAMMER)
|
|
|
|
func water_exited() -> void:
|
|
await get_tree().physics_frame
|
|
if in_water: return
|
|
normal_state.swim_up_meter = 0
|
|
if velocity.y < 0:
|
|
velocity.y = -250.0 if velocity.y < -50.0 or Global.player_action_pressed("move_up", player_id) else velocity.y
|
|
has_jumped = true
|
|
if Global.player_action_pressed("move_up", player_id):
|
|
gravity = JUMP_GRAVITY
|
|
else:
|
|
gravity = FALL_GRAVITY
|
|
|
|
func reset_camera_to_center() -> void:
|
|
animating_camera = true
|
|
var old_position = camera.position
|
|
camera.global_position = get_viewport().get_camera_2d().get_screen_center_position()
|
|
camera.reset_physics_interpolation()
|
|
var tween = create_tween()
|
|
tween.tween_property(camera, "position", old_position, 0.5)
|
|
await tween.finished
|
|
camera.position = old_position
|
|
animating_camera = false
|
|
|
|
func on_area_exited(area: Area2D) -> void:
|
|
if area is WaterArea:
|
|
water_exited()
|