added the game

This commit is contained in:
JHDev2006
2025-09-13 16:30:32 +01:00
parent 5ef689109b
commit 3773bdaf64
3616 changed files with 263702 additions and 0 deletions

View File

@@ -0,0 +1,35 @@
extends Enemy
const EXPLOSION = preload("uid://clbvyne1cr8gp")
@export var timer := 5.0
var can_move := true
func _ready() -> void:
$Movement.auto_call = can_move
func _physics_process(delta: float) -> void:
timer -= delta
if timer <= 2.0:
%FlashAnimation.play("Flash")
if timer <= 0:
explode()
timer = 99
%Sprite.scale.x = direction
func explode() -> void:
$AnimationPlayer.play("Explode")
await $AnimationPlayer.animation_finished
summon_explosion()
queue_free()
func kick(object: Node2D) -> void:
AudioManager.play_sfx("kick", global_position)
var kick_dir = sign(global_position.x - object.global_position.x)
velocity.x = 150 * kick_dir
direction = kick_dir
velocity.y = -100
func summon_explosion() -> void:
var node = EXPLOSION.instantiate()
node.global_position = global_position + Vector2(0, -8)
add_sibling(node)

View File

@@ -0,0 +1 @@
uid://baie518kyfenr

View File

@@ -0,0 +1,38 @@
extends Node2D
const LOW_STRENGTH := -300
const HIGH_STRENGTH := -450
func bounce_player(player: Player) -> void:
$Sprite.play("Bounce")
$AnimationPlayer.stop()
if player.global_position.y + 8 < global_position.y:
player.velocity.x *= 0.8
if Global.player_action_pressed("jump", player.player_id):
player.gravity = player.JUMP_GRAVITY
player.jump_cancelled = false
player.velocity.y = HIGH_STRENGTH
player.has_jumped = true
AudioManager.play_sfx("bumper_high", global_position)
else:
AudioManager.play_sfx("bumper", global_position)
player.velocity.y = LOW_STRENGTH
else:
player.velocity = global_position.direction_to(player.global_position) * 200
if Global.player_action_pressed("jump", player.player_id):
player.gravity = player.JUMP_GRAVITY
player.velocity.y = LOW_STRENGTH
player.has_jumped = true
AudioManager.play_sfx("bumper_high", global_position)
else:
AudioManager.play_sfx("bumper", global_position)
refresh_hitbox()
$AnimationPlayer.play("Bounce")
await $AnimationPlayer.animation_finished
$Sprite.play("Idle")
func refresh_hitbox() -> void:
$Hitbox/CollisionShape2D.set_deferred("disabled", true)
await get_tree().physics_frame
$Hitbox/CollisionShape2D.set_deferred("disabled", false)

View File

@@ -0,0 +1 @@
uid://biu4x75tbkfk3

View File

@@ -0,0 +1,29 @@
extends AnimatableBody2D
@export_enum("Up", "Down", "Left", "Right") var direction := 0
func _ready() -> void:
$Timer.start()
func do_cycle() -> void:
if BooRaceHandler.countdown_active == false:
AudioManager.play_sfx("burner", global_position)
do_animation()
await get_tree().create_timer(0.25, false).timeout
%Hitbox.set_deferred("disabled", false)
await get_tree().create_timer(1.5, false).timeout
%Hitbox.set_deferred("disabled", true)
$Timer.start()
func do_animation() -> void:
%Flame.show()
%Flame.play("Rise")
await %Flame.animation_finished
%Flame.play("Loop")
await get_tree().create_timer(1, false).timeout
%Flame.play("Fall")
await %Flame.animation_finished
%Flame.hide()
func damage_player(player: Player) -> void:
player.damage()

View File

@@ -0,0 +1 @@
uid://clareynx710qb

View File

@@ -0,0 +1,28 @@
extends Node2D
@export var item: PackedScene = preload("uid://bumvqjhs2xxka")
@export_range(0, 8, 1) var head_angle := 0
@export_range(0, 4, 1) var stand_angle := 0
var amount := 0
func _ready() -> void:
$Timer.start()
func shoot() -> void:
if amount >= 3 or $Head/Raycast.is_colliding():
return
var node = item.instantiate()
var direction_vector = [Vector2.UP, Vector2(1, -1), Vector2.RIGHT, Vector2(1, 1), Vector2.DOWN, Vector2(-1, 1), Vector2.LEFT, Vector2(-1, -1), Vector2.UP][head_angle]
node.set("direction_vector", direction_vector)
node.set("velocity", 100 * direction_vector)
if direction_vector.x != 0:
node.set("direction", sign(direction_vector.x))
node.global_position = global_position
if item.resource_path != "res://Scenes/Prefabs/Entities/Objects/CannonBall.tscn":
node.global_position += direction_vector * 4
node.tree_exited.connect(func(): amount -= 1)
amount += 1
AudioManager.play_sfx("cannon", global_position)
add_sibling(node)

View File

@@ -0,0 +1 @@
uid://bxfr63s2j3ts7

View File

@@ -0,0 +1,30 @@
extends Node2D
@onready var sprite: AnimatedSprite2D = $"../Sprite"
@onready var activated: AnimatedSprite2D = $"../Activated"
static var character_save := "Mario"
func _ready() -> void:
activated.get_node("ResourceSetterNew").resource_json = load(get_character_sprite_path(0))
if Settings.file.difficulty.checkpoint_style == 0 and (Global.current_game_mode != Global.GameMode.LEVEL_EDITOR and Global.current_game_mode != Global.GameMode.CUSTOM_LEVEL) or Global.current_campaign == "SMBANN":
owner.queue_free()
return
owner.show()
if Checkpoint.passed:
sprite.hide()
activated.show()
func get_character_sprite_path(player_id := 0) -> String:
var character = Player.CHARACTERS[int(Global.player_characters[player_id])]
var path = "res://Assets/Sprites/Players/" + character + "/CheckpointFlag.json"
if int(Global.player_characters[player_id]) > 3:
path = path.replace("res://Assets/Sprites/Players", "user://custom_characters")
return path
func activate(player: Player) -> void:
character_save = player.character
sprite.play("Hit")
await get_tree().physics_frame
await sprite.animation_finished
sprite.hide()
activated.show()

View File

@@ -0,0 +1 @@
uid://hk6ml16axmcc

View File

@@ -0,0 +1,18 @@
extends AnimatableBody2D
var active := false
@onready var starting_position := global_position
func _physics_process(delta: float) -> void:
if active:
global_position.x += 48 * delta
func on_player_entered(player: Player) -> void:
if player.velocity.y > -player.FALL_GRAVITY:
active = true
func reset() -> void:
global_position = starting_position
reset_physics_interpolation()
active = false

View File

@@ -0,0 +1 @@
uid://bosxtidiegjv0

View File

@@ -0,0 +1,122 @@
class_name Door
extends Node2D
@export_range(0, 99) var door_id := 0
@export_enum("0", "1", "2", "3", "4") var sublevel_id := 0
@export var locked := false
@export var start_locked := false
signal updated
static var exiting_door_id := -1
var can_enter := true
static var door_found := false
static var unlocked_doors := []
static var same_scene_exiting_door: Door = null
func _ready() -> void:
if start_locked:
locked = true
if locked:
check_if_unlocked(false)
func _physics_process(_delta: float) -> void:
for i in $PlayerDetection.get_overlapping_areas():
if i.owner is Player and can_enter:
if Global.player_action_just_pressed("move_up", i.owner.player_id) and i.owner.is_on_floor():
if locked:
if KeyItem.total_collected > 0:
unlock_door(i.owner)
else:
AudioManager.play_sfx("door_locked", global_position)
$Sprite.play("Locked")
$AnimationPlayer.play("Locked")
else:
player_enter(i.owner)
func check_if_unlocked(do_animation := true) -> void:
if locked:
if unlocked_doors.has(door_id):
locked = false
$Sprite.play("Idle")
if do_animation:
$AnimationPlayer.play("Unlock")
func run_door_check() -> void:
if same_scene_exiting_door != null:
if same_scene_exiting_door != self and exiting_door_id == door_id:
door_found = true
for i in get_tree().get_nodes_in_group("Players"):
player_exit(i)
return
else:
if exiting_door_id == door_id:
door_found = true
for i in get_tree().get_nodes_in_group("Players"):
player_exit(i)
return
await get_tree().physics_frame
if door_found == false:
for i in get_tree().get_nodes_in_group("Players"):
player_exit(i)
func unlock_door(player: Player) -> void:
AudioManager.play_sfx("door_unlock", global_position)
Global.p_switch_timer_paused = true
KeyItem.total_collected -= 1
freeze_player(player)
$Sprite.play("Idle")
unlocked_doors.append(door_id)
get_tree().call_group("Doors", "check_if_unlocked", true)
$AnimationPlayer.play("Unlock")
await get_tree().create_timer(0.5, false).timeout
player_enter(player)
func player_exit(player: Player) -> void:
exiting_door_id = -1
can_enter = false
LevelEditor.play_door_transition = false
same_scene_exiting_door = null
player.global_position = global_position
player.recenter_camera()
$Sprite.play("Close")
await get_tree().create_timer(0.2, false).timeout
$Sprite.play("Close")
player.state_machine.transition_to("Normal")
AudioManager.play_sfx("door_close", global_position)
can_enter = true
Global.p_switch_timer_paused = false
func player_enter(player: Player) -> void:
Global.p_switch_timer_paused = true
can_enter = false
door_found = false
exiting_door_id = door_id
freeze_player(player)
$Sprite.play("Open")
LevelEditor.play_door_transition = true
AudioManager.play_sfx("door_open", global_position)
await get_tree().create_timer(0.5, false).timeout
if Global.level_editor.sub_level_id == sublevel_id:
Global.do_fake_transition()
if Global.fade_transition:
await get_tree().create_timer(0.25, false).timeout
same_scene_exiting_door = self
for i in get_tree().get_nodes_in_group("Doors"):
i.run_door_check()
else:
same_scene_exiting_door = null
Global.level_editor.transition_to_sublevel(sublevel_id)
$Sprite.play("Idle")
can_enter = true
func freeze_player(player: Player) -> void:
player.state_machine.transition_to("Freeze")
player.sprite.play("Idle")
player.velocity = Vector2.ZERO

View File

@@ -0,0 +1 @@
uid://cy8cncadvevvt

View File

@@ -0,0 +1,5 @@
extends Node2D
@export_range(1, 8) var width := 1
@export_range(1, 8) var left_height := 1
@export_range(1, 8) var right_height := 2

View File

@@ -0,0 +1 @@
uid://duxduhxe8vqdo

View File

@@ -0,0 +1,9 @@
extends StaticBody2D
@export var vertical_direction := 1
const MOVE_SPEED := 50
@export var top := -244
func _physics_process(delta: float) -> void:
global_position.y += (MOVE_SPEED * delta) * vertical_direction
global_position.y = wrap(global_position.y, top, 64)

View File

@@ -0,0 +1 @@
uid://bosinwppqswdr

View File

@@ -0,0 +1,18 @@
class_name Explosion
extends Node2D
const destructable_tiles := {Vector2i(4, 0): Rect2(32, 160, 16, 16), Vector2i(4, 2): Rect2(48, 160, 16, 16)}
const BLOCK_DESTRUCTION_PARTICLES = preload("uid://cyw7kk1em8h16")
func on_body_entered(body: Node2D) -> void:
if body is Block:
if body.destructable: body.destroy()
if body is Player:
body.damage()
func on_area_entered(area: Area2D) -> void:
if area.owner is Player:
area.owner.damage()

View File

@@ -0,0 +1 @@
uid://ritdlvkpyjd4

View File

@@ -0,0 +1,10 @@
extends AnimatableBody2D
func _physics_process(delta: float) -> void:
if $PlayerDetect.get_overlapping_areas().any(is_player):
global_position.y += 96 * delta
func is_player(area: Area2D) -> bool:
if area.owner is Player:
return area.owner.is_on_floor() and area.owner.global_position.y - 4 <= global_position.y
return false

View File

@@ -0,0 +1 @@
uid://c4m157i8p54cf

View File

@@ -0,0 +1,27 @@
extends AnimatableBody2D
@export var active := false
@export_enum("Right", "Left", "Up", "Down") var direction := 0
@export_range(1, 4, 1) var strength := 1
func on_switch_hit() -> void:
active = not active
func _physics_process(_delta: float) -> void:
$Particles.amount = strength * 2
$Particles.speed_scale = strength / 2.0
$Sprite.speed_scale = strength / 2.0
if active:
for i in $Hitbox.get_overlapping_areas():
if i.owner is CharacterBody2D:
var wind_velocity = Vector2.RIGHT.rotated(global_rotation) * (strength * 2)
var modifier = Vector2.ONE
if i.owner is Player:
if Global.player_action_pressed("jump", i.owner.player_id):
modifier.y = 2
if Global.player_action_pressed("move_down", i.owner.player_id):
modifier.y = 0.5
var distance = (((i.owner.global_position - global_position) / modifier).length() / 250)
i.owner.velocity += (wind_velocity / distance / Vector2(2, 1))
$Particles.emitting = active
$Sprite.play("On" if active else "Off")

View File

@@ -0,0 +1 @@
uid://bvplls3ae0oqr

View File

@@ -0,0 +1,17 @@
extends CharacterBody2D
var is_pressed := false
func on_player_entered(player: Player) -> void:
if player.velocity.y >= 0:
pressed()
func pressed() -> void:
if is_pressed:
return
is_pressed = true
$Sprite.play("Pressed")
AudioManager.play_global_sfx("switch")
AudioManager.play_global_sfx("cannon")
$AnimationPlayer.play("Pressed")
Global.activate_p_switch()

View File

@@ -0,0 +1 @@
uid://df20kjawaoqcl

View File

@@ -0,0 +1,50 @@
class_name RopeElevatorPlatform
extends Node2D
@export var linked_platform: Node2D = null
@onready var platform: AnimatableBody2D = $Platform
@onready var player_detection: Area2D = $Platform/PlayerDetection
@export var rope_top := -160
var velocity := 0.0
var dropped := false
var player_stood_on := false
var sample_colour: Texture = null
func _ready() -> void:
$Platform/ScoreNoteSpawner.owner = $Platform
func _process(_delta: float) -> void:
if not dropped:
$Rope.size.y = platform.global_position.y - rope_top
$Rope.global_position.y = rope_top
func _physics_process(delta: float) -> void:
player_stood_on = player_detection.get_overlapping_areas().any(is_player)
if dropped:
velocity += (5 / delta) * delta
platform.position.y += velocity * delta
return
else:
if platform.global_position.y <= rope_top or linked_platform.dropped:
dropped = true
if linked_platform.dropped:
if Settings.file.audio.extra_sfx == 1:
AudioManager.play_sfx("lift_fall", global_position)
$Platform/ScoreNoteSpawner.spawn_note(1000)
if player_stood_on:
velocity += (2 / delta) * delta
else:
velocity = lerpf(velocity, 0, delta * 2)
linked_platform.velocity = -velocity
platform.position.y += velocity * delta
func is_player(area: Area2D) -> bool:
if area.owner is Player:
return area.owner.is_on_floor()
return false

View File

@@ -0,0 +1 @@
uid://brl0yede1msau

View File

@@ -0,0 +1,49 @@
class_name SpikeBall
extends CharacterBody2D
var can_gravity := false
const SPIKE_BALL_DESTRUCTION_PARTICLES = preload("uid://bk0arhpyyila6")
func _physics_process(delta: float) -> void:
handle_movement(delta)
$Sprite.rotation_degrees += velocity.x * delta * 8
handle_block_collision()
func handle_movement(delta: float) -> void:
if can_gravity:
velocity.y += (Global.entity_gravity / delta) * delta
if is_on_floor():
can_gravity = true
velocity.x += get_floor_normal().x * 4
if is_on_wall():
destroy()
if sign(get_floor_normal().x) != sign(velocity.x) and abs(get_floor_normal().x) > 0.2 and is_on_floor():
velocity.y = (velocity.length() * get_floor_normal().y) + Global.entity_gravity
move_and_slide()
func destroy() -> void:
summon_particles()
AudioManager.play_sfx("block_break", global_position)
queue_free()
func handle_block_collision() -> void:
for i in $Hitbox.get_overlapping_bodies():
if i is Block:
if global_position.y - 8 < i.global_position.y:
i.shell_block_hit.emit(null)
func summon_particles() -> void:
var particles = SPIKE_BALL_DESTRUCTION_PARTICLES.instantiate()
particles.global_position = global_position
add_sibling(particles)
func on_area_entered(area: Area2D) -> void:
if area.owner is SpikeBall and area.owner != self:
destroy()
if area.owner is Enemy:
if area.owner.has_node("ShellDetection"):
area.owner.die_from_object(self)
elif area.owner is Player:
if area.owner.is_invincible:
destroy()
else:
area.owner.damage()

View File

@@ -0,0 +1 @@
uid://dlq222ogmfken

View File

@@ -0,0 +1,81 @@
class_name Track
extends Node2D
const TRACK_PIECE = preload("uid://4gxhnql5bjk6")
@export var path := []
var pieces := []
var length := 0
@export_enum("Closed", "Open") var start_point := 0
@export_enum("Closed", "Open") var end_point := 0
@export var invisible := false:
set(value):
invisible = value
update_pieces()
var editing := false
const DIRECTIONS := [
Vector2i(-1, -1), # 0
Vector2i.UP, # 1
Vector2i(1, -1), # 2
Vector2i.RIGHT, # 3
Vector2i(1, 1), # 4
Vector2i.DOWN, # 5
Vector2i(-1, 1), # 6
Vector2i.LEFT # 7
]
func _process(_delta: float) -> void:
$Point.frame = int(start_point == 0)
visible = not (invisible and LevelEditor.playing_level)
if editing and Global.current_game_mode == Global.GameMode.LEVEL_EDITOR:
if Input.is_action_just_pressed("editor_open_menu") or Input.is_action_just_pressed("ui_cancel"):
editing = false
Global.level_editor.current_state = LevelEditor.EditorState.IDLE
update_pieces()
func _ready() -> void:
for i in path:
add_piece(i, false)
update_pieces()
func update_pieces() -> void:
var idx := 0
for i in $Pieces.get_children():
i.idx = idx
i.editing = idx >= path.size() and editing
if idx > 0:
i.starting_direction = -path[idx - 1]
else:
i.starting_direction = Vector2i.ZERO
if idx <= path.size() - 1:
i.connecting_direction = path[idx]
else:
i.connecting_direction = Vector2i.ZERO
i.update_direction_textures()
idx += 1
func add_piece(new_direction := Vector2i.ZERO, add_to_arr := true) -> void:
var piece = TRACK_PIECE.instantiate()
var next_position := new_direction * 16
for i in length:
next_position += path[i] * 16
piece.position = next_position
$Pieces.add_child(piece)
piece.owner = self
pieces.append(piece)
piece.idx = length
piece.reset_physics_interpolation()
if add_to_arr:
path.append(new_direction)
length += 1
update_pieces()
func remove_last_piece() -> void:
$Pieces.get_child($Pieces.get_child_count() - 1).queue_free()
await get_tree().process_frame
path.pop_back()
pieces.pop_back()
length -= 1
update_pieces()

View File

@@ -0,0 +1 @@
uid://3heyyx506fnu

View File

@@ -0,0 +1,62 @@
class_name TrackPiece
extends Node2D
var editing := false
var mouse_in_areas := 0
var pieces := []
var idx := 0
var starting_direction := Vector2i.ZERO
var connecting_direction := Vector2i.UP
const SPRITE_COORDS := {
Vector2i.ZERO: Vector2(112, 16),
Vector2i.RIGHT: Vector2(0, 0),
Vector2i.LEFT: Vector2(16, 0),
Vector2i.DOWN: Vector2(32, 0),
Vector2i.UP: Vector2(48, 0),
Vector2i(1, 1): Vector2(64, 0),
Vector2i(-1, 1): Vector2(80, 0),
Vector2i(-1, -1): Vector2(96, 0),
Vector2i(1, -1): Vector2(112, 0),
}
const TRACKS = preload("uid://50hm4xgnw8ks")
const INVISIBLE_TRACKS = preload("uid://barofu3g8jf00")
func _process(_delta: float) -> void:
$PlacePreview.visible = editing
$Start.region_rect.position = SPRITE_COORDS[starting_direction]
$Connect.region_rect.position = SPRITE_COORDS[connecting_direction]
$End.visible = idx == owner.length
$End.frame = int(owner.end_point == 0)
if Input.is_action_pressed("mb_left") and editing and mouse_in_areas > 0:
for i in 8:
if is_mouse_in_area(i):
if Track.DIRECTIONS[i] == starting_direction:
owner.remove_last_piece()
else:
owner.add_piece(Track.DIRECTIONS[i])
func update_direction_textures() -> void:
var texture = TRACKS
if owner.invisible:
texture = INVISIBLE_TRACKS
for i in $PlacePreview.get_children():
i.frame = int(Track.DIRECTIONS[i.get_index()] == starting_direction)
for i in [$Start, $Connect, $End]:
i.texture = texture
func on_mouse_entered(area_idx := 0) -> void:
mouse_in_areas |= (1 << area_idx)
print(mouse_in_areas)
func on_mouse_exited(area_idx := 0) -> void:
mouse_in_areas &= ~(1 << area_idx)
print(mouse_in_areas)
func is_mouse_in_area(area_idx := 0) -> bool:
return mouse_in_areas & (1 << area_idx) != 0

View File

@@ -0,0 +1 @@
uid://chhr6kgvstkod

View File

@@ -0,0 +1,114 @@
class_name TrackRider
extends Node2D
@export var attached_entity: Node2D = null
@export_range(1, 8, 1) var speed := 2
@export_enum("Forward", "Backward") var direction := 0
var velocity := Vector2.ZERO
var last_position := Vector2.ZERO
var direction_vector := Vector2i.ZERO
var current_track: Track = null
var track_idx := -1
var can_attach := true
var travelling_on_rail := false
func _ready() -> void:
start()
await get_tree().physics_frame
if attached_entity != null:
attach_to_joint(attached_entity)
func start() -> void:
current_track = null
track_idx = -1
func check_for_entities() -> void:
for i in $Hitbox.get_overlapping_bodies():
print(i)
if i.has_node("TrackJoint"):
attach_to_joint(i)
return
for i in $Hitbox.get_overlapping_areas():
print(i)
if i.owner.has_node("TrackJoint"):
attach_to_joint(i.owner)
return
func _physics_process(delta: float) -> void:
if attached_entity == null:
check_for_entities()
return
if travelling_on_rail == false:
velocity.y += 10
global_position += velocity * delta
check_for_rail()
last_position = global_position
func attach_to_joint(node: Node2D) -> void:
var joint = node.get_node("TrackJoint")
joint.is_attached = true
if joint.movement_node != null:
joint.movement_node.active = false
joint.attached.emit()
elif joint.disable_physics:
node.set_physics_process(false)
joint.rider = self
node.physics_interpolation_mode = Node.PHYSICS_INTERPOLATION_MODE_OFF
node.reparent($Joint, false)
node.position = joint.offset
attached_entity = node
func check_for_rail() -> void:
if travelling_on_rail == false and can_attach:
for i in $Hitbox.get_overlapping_areas():
if i.get_parent() is TrackPiece and i.get_parent().owner != current_track:
var piece: TrackPiece = i.get_parent()
if piece.owner.length <= 0:
continue
global_position = piece.global_position
travelling_on_rail = true
current_track = piece.owner
track_idx = piece.idx
if track_idx >= current_track.path.size():
direction = 1
if direction == 1:
track_idx -= 1
track_idx = clamp(track_idx, 0, current_track.path.size() - 1)
if travelling_on_rail:
direction_vector = current_track.path[track_idx] * [1, -1][direction]
if current_track != null:
move_tween(Vector2(direction_vector))
func move_tween(new_direction := Vector2.ZERO) -> void:
var tween = create_tween()
tween.tween_property(self, "global_position", global_position + (new_direction * 16), float(1.0 if new_direction.is_normalized() else 1.414) / (speed * 2))
await tween.finished
track_idx += [1, -1][direction]
if track_idx >= current_track.length and direction == 0:
track_idx = current_track.length - 1
if current_track.end_point == 0:
direction = 1
else:
detach_from_rail()
return
if track_idx < 0 and direction == 1:
track_idx = 0
if current_track.start_point == 0:
direction = 0
else:
detach_from_rail()
return
check_for_rail()
func detach_from_rail() -> void:
can_attach = false
travelling_on_rail = false
track_idx = -1
velocity = direction_vector * speed * 48
await get_tree().create_timer(0.1, false).timeout
can_attach = true

View File

@@ -0,0 +1 @@
uid://dg16ltpjq1r4r

View File

@@ -0,0 +1,41 @@
extends AnimatableBody2D
@export var bounce_height := -500
var players := []
func on_area_entered(area: Area2D) -> void:
pass
func _physics_process(_delta: float) -> void:
for i in $Hitbox.get_overlapping_areas():
if i.owner is Player and i.owner.is_on_floor():
if i.owner.spring_bouncing or i.owner.velocity.y < 0:
continue
i.owner.velocity.x = 0
if players.has(i.owner) == false:
players.append(i.owner)
$Animation.play("Bounce")
i.owner.spring_bouncing = true
for i in players:
i.global_position.y = $PlayerCollision/PlayerJoint.global_position.y
func bounce_players() -> void:
var high_bounce := false
for player in players:
if Global.player_action_pressed("jump", player.player_id):
high_bounce = true
player.velocity.y = bounce_height
player.gravity = player.JUMP_GRAVITY
player.has_jumped = true
else:
player.velocity.y = -300
if high_bounce:
AudioManager.play_sfx("spring", global_position)
else:
AudioManager.play_sfx("bump", global_position)
players.clear()
func on_area_exited(area: Area2D) -> void:
if area.owner is Player:
area.owner.spring_bouncing = false

View File

@@ -0,0 +1 @@
uid://bof8ieprith1d

View File

@@ -0,0 +1,19 @@
extends Area2D
@export var wind_force := 30
func _ready() -> void:
await get_tree().create_timer(0.1, false).timeout
get_parent().move_child(self, 0)
func _physics_process(delta: float) -> void:
for i in get_overlapping_areas():
if i.owner is Player:
if i.owner.spring_bouncing == false and i.owner.is_on_wall() == false and i.owner.state_machine.state.name == "Normal":
i.owner.global_position.x += wind_force * delta
var active := get_overlapping_areas().any(func(area: Area2D) -> bool: return area.owner is Player)
$CanvasLayer/Control/Particles.emitting = active
if active and $SFX.is_playing() == false:
$SFX.play()
elif not active:
$SFX.stop()

View File

@@ -0,0 +1 @@
uid://d3kftxro4bfnv