From 7fb6989584f9d1b8ad861bfed94937e48c7b9491 Mon Sep 17 00:00:00 2001 From: Will Thompson Date: Tue, 18 Nov 2025 11:06:09 +0000 Subject: [PATCH 1/4] player: Add "glide" special ability If the player holds down the "jump" action, their character's downwards speed is capped to a constant. This allows the player-character to travel across wider gaps by jumping then gliding. The function call for this ability is in _physics_process(), after other "normal" adjustments to the velocity but before move_and_slide(). However, it is commented out. A learner must find this commented-out line and uncomment it to enable this ability. The sound effect is from https://freesound.org/people/tothrec2/sounds/596541/, licensed under CC0-1.0. --- ...__tothrec2__large-wings-flapping-foley.ogg | Bin 0 -> 31673 bytes ...ec2__large-wings-flapping-foley.ogg.import | 19 ++++++++++++++ components/player/player.tscn | 7 ++++- scripts/player.gd | 24 ++++++++++++++++++ 4 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 assets/sounds/596541__tothrec2__large-wings-flapping-foley.ogg create mode 100644 assets/sounds/596541__tothrec2__large-wings-flapping-foley.ogg.import diff --git a/assets/sounds/596541__tothrec2__large-wings-flapping-foley.ogg b/assets/sounds/596541__tothrec2__large-wings-flapping-foley.ogg new file mode 100644 index 0000000000000000000000000000000000000000..fda27bf68a2f48326d187f955a7b0f8b1c7d764d GIT binary patch literal 31673 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 UOG)q0L-0<930&ZHKx6>^AAlvD00000 literal 0 HcmV?d00001 diff --git a/assets/sounds/596541__tothrec2__large-wings-flapping-foley.ogg.import b/assets/sounds/596541__tothrec2__large-wings-flapping-foley.ogg.import new file mode 100644 index 0000000..bd16e8b --- /dev/null +++ b/assets/sounds/596541__tothrec2__large-wings-flapping-foley.ogg.import @@ -0,0 +1,19 @@ +[remap] + +importer="oggvorbisstr" +type="AudioStreamOggVorbis" +uid="uid://p0b3e0t7h8j3" +path="res://.godot/imported/596541__tothrec2__large-wings-flapping-foley.ogg-715998e9d4f908348de867f3ceb6fce5.oggvorbisstr" + +[deps] + +source_file="res://assets/sounds/596541__tothrec2__large-wings-flapping-foley.ogg" +dest_files=["res://.godot/imported/596541__tothrec2__large-wings-flapping-foley.ogg-715998e9d4f908348de867f3ceb6fce5.oggvorbisstr"] + +[params] + +loop=false +loop_offset=0 +bpm=0 +beat_count=0 +bar_beats=4 diff --git a/components/player/player.tscn b/components/player/player.tscn index 656c8eb..9c8be6d 100644 --- a/components/player/player.tscn +++ b/components/player/player.tscn @@ -1,8 +1,9 @@ -[gd_scene load_steps=8 format=3 uid="uid://8st4scqt06l8"] +[gd_scene load_steps=9 format=3 uid="uid://8st4scqt06l8"] [ext_resource type="Script" uid="uid://umb21eb2y1oj" path="res://scripts/player.gd" id="1_w3ms2"] [ext_resource type="SpriteFrames" uid="uid://bo581k1esb50n" path="res://components/player/spriteframes-red.tres" id="2_msaml"] [ext_resource type="AudioStream" uid="uid://bx1joarpc14j5" path="res://assets/sounds/538066__stevielematt__boing.ogg" id="3_wa6cj"] +[ext_resource type="AudioStream" uid="uid://p0b3e0t7h8j3" path="res://assets/sounds/596541__tothrec2__large-wings-flapping-foley.ogg" id="4_mpawu"] [sub_resource type="CapsuleShape2D" id="CapsuleShape2D_7x5a4"] radius = 31.0 @@ -51,3 +52,7 @@ one_way_collision = true [node name="JumpSFX" type="AudioStreamPlayer" parent="SoundEffects"] unique_name_in_owner = true stream = SubResource("AudioStreamRandomizer_mpawu") + +[node name="GlideSFX" type="AudioStreamPlayer" parent="SoundEffects"] +unique_name_in_owner = true +stream = ExtResource("4_mpawu") diff --git a/scripts/player.gd b/scripts/player.gd index 15e182d..4076788 100644 --- a/scripts/player.gd +++ b/scripts/player.gd @@ -3,6 +3,12 @@ class_name Player extends CharacterBody2D ## A player's character, which can walk, jump, and stomp on enemies. +## The player-character's maximum downwards speed while gliding. +## Making this number smaller allows the player to glide further. +## [br][br] +## Used by [method _glide]. +const GLIDE_TERMINAL_VELOCITY = 100 + ## Which player controls this character? @export var player: Global.Player = Global.Player.ONE @@ -57,6 +63,7 @@ var original_position: Vector2 @onready var _double_jump_particles: CPUParticles2D = %DoubleJumpParticles @onready var _jump_sfx: AudioStreamPlayer = %JumpSFX +@onready var _glide_sfx: AudioStreamPlayer = %GlideSFX func _set_sprite_frames(new_sprite_frames): @@ -106,6 +113,21 @@ func stomp(): _jump() +## If the player-character is in the air, and the "jump" action is held, clamp the downwards +## velocity to a constant. Must be called after applying gravity to the player-character. +func _glide() -> void: + if not is_on_floor() and Input.is_action_pressed(Actions.lookup(player, "jump")): + if velocity.y > GLIDE_TERMINAL_VELOCITY: + velocity.y = GLIDE_TERMINAL_VELOCITY + + # Only play the sound effect when the player-character is moving downwards, not while + # jumping upwards + if velocity.y > 0 and not _glide_sfx.playing: + _glide_sfx.play() + elif _glide_sfx.playing: + _glide_sfx.stop() + + func _physics_process(delta): # Don't move if there are no lives left. if Global.lives <= 0: @@ -142,6 +164,8 @@ func _physics_process(delta): else: velocity.x = move_toward(velocity.x, 0, acceleration * delta) + # _glide() + if velocity == Vector2.ZERO: _sprite.play("idle") else: From 42a20a33c6b52f30421ec14494aa0f1f18dce691 Mon Sep 17 00:00:00 2001 From: Will Thompson Date: Tue, 18 Nov 2025 11:06:09 +0000 Subject: [PATCH 2/4] player: Add teleport/blink action If the player enters this action while also holding left or right, the player-character teleports by a constant distance horizontally. On keyboard, this is bound to "Enter" for player 1 (who moves with the arrow keys), and "Shift" for player 2 (who moves with WASD). On gamepad, this is bound to Xbox button B. The function call for this ability is in the correct place in _physics_process() but is commented out. A learner must find this commented-out line and uncomment it to enable this ability. The sound effect is https://freesound.org/people/D.S.G./sounds/244654/ (CC0-1.0). --- assets/sounds/244654__dsg__pop-2.ogg | Bin 0 -> 4457 bytes assets/sounds/244654__dsg__pop-2.ogg.import | 19 +++++++++++++++++ components/player/player.tscn | 11 ++++++++++ project.godot | 12 +++++++++++ scripts/actions.gd | 1 + scripts/player.gd | 22 ++++++++++++++++++++ 6 files changed, 65 insertions(+) create mode 100644 assets/sounds/244654__dsg__pop-2.ogg create mode 100644 assets/sounds/244654__dsg__pop-2.ogg.import diff --git a/assets/sounds/244654__dsg__pop-2.ogg b/assets/sounds/244654__dsg__pop-2.ogg new file mode 100644 index 0000000000000000000000000000000000000000..ddc0080e78540b300eb2d4a296d81a46d829e846 GIT binary patch literal 4457 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 z void: _glide_sfx.stop() +## If the "teleport" action is pressed, and the player is moving the character horizontally, +## teleport the character in that horizontal direction. +func _teleport(input_direction: float) -> void: + if ( + Input.is_action_just_pressed(Actions.lookup(player, "teleport")) + and not is_zero_approx(input_direction) + ): + # TODO: Check if we are teleporting into a wall (in which case the player should lose a + # life) or an enemy (in which case maybe the enemy should be telefragged/defeated?) + global_position.x += TELEPORT_DISTANCE * input_direction + _teleport_sfx.play() + + func _physics_process(delta): # Don't move if there are no lives left. if Global.lives <= 0: @@ -180,6 +200,8 @@ func _physics_process(delta): move_and_slide() + # _teleport(direction) + coyote_timer -= delta jump_buffer_timer -= delta From e4a66afb3c05b520fb5a3d27a90f340a1c7213ea Mon Sep 17 00:00:00 2001 From: Will Thompson Date: Wed, 19 Nov 2025 16:09:08 +0000 Subject: [PATCH 3/4] player: Add "phase" ability This adds an action that, while held, temporarily prevents the player-character from colliding with enemies or coins. The new action is bound to: - Player 1: Backspace - Player 2: Tab - Gamepad: Xbox X / PlayStation Square As with the other new actions, the function call to implement this is commented out. --- project.godot | 12 ++++++++++++ scripts/actions.gd | 1 + scripts/player.gd | 25 +++++++++++++++++++++++++ 3 files changed, 38 insertions(+) diff --git a/project.godot b/project.godot index 7b47473..04afe16 100644 --- a/project.godot +++ b/project.godot @@ -65,6 +65,12 @@ player_2_teleport={ , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":1,"button_index":1,"pressure":0.0,"pressed":true,"script":null) ] } +player_2_phase={ +"deadzone": 0.2, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194306,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":1,"button_index":2,"pressure":0.0,"pressed":true,"script":null) +] +} player_1_jump={ "deadzone": 0.5, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194320,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) @@ -92,6 +98,12 @@ player_1_teleport={ , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":1,"pressure":0.0,"pressed":true,"script":null) ] } +player_1_phase={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194308,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":2,"pressure":0.0,"pressed":true,"script":null) +] +} [layer_names] diff --git a/scripts/actions.gd b/scripts/actions.gd index b4948a2..7a66d6b 100644 --- a/scripts/actions.gd +++ b/scripts/actions.gd @@ -13,6 +13,7 @@ const ACTIONS = [ &"left", &"right", &"teleport", + &"phase", ] # Dictionary[Global.Player, Dictionary[StringName, StringName]] diff --git a/scripts/player.gd b/scripts/player.gd index 0d4f83c..5d608ad 100644 --- a/scripts/player.gd +++ b/scripts/player.gd @@ -148,11 +148,36 @@ func _teleport(input_direction: float) -> void: _teleport_sfx.play() +## If the "phase" action is pressed, make the player-character invulnerable, but also unable to +## interact with coins. +func _phase() -> void: + # Check if the player is holding the "phase" action button. + if Input.is_action_just_pressed(Actions.lookup(player, "phase")): + # While phasing, disable collisions on the PLAYER physics layer. + set_collision_layer_value(Global.PhysicsLayers.PLAYER, false) + set_collision_mask_value(Global.PhysicsLayers.PLAYER, false) + + # Make the sprite semitransparent + _sprite.modulate.a = 0.5 + + # TODO: Is this ability too powerful? Should it have a timer/stamina so the player can only + # use it occasionally and for a short time? + elif Input.is_action_just_released(Actions.lookup(player, "phase")): + # Re-enable collisions on the PLAYER physics layer. + set_collision_layer_value(Global.PhysicsLayers.PLAYER, true) + set_collision_mask_value(Global.PhysicsLayers.PLAYER, true) + + # Make the sprite opaque again + _sprite.modulate.a = 1 + + func _physics_process(delta): # Don't move if there are no lives left. if Global.lives <= 0: return + # _phase() + # Handle jump if is_on_floor(): coyote_timer = (coyote_time + delta) From 4643c11bb87e609271a9cd09658c04b0678c32b0 Mon Sep 17 00:00:00 2001 From: Will Thompson Date: Thu, 20 Nov 2025 12:20:28 +0000 Subject: [PATCH 4/4] player: Add "shrink" special ability As with the other special abilities, the function call in _physics_process() that implements this ability is commented out. --- project.godot | 15 +++++++++++++++ scripts/actions.gd | 1 + scripts/player.gd | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 51 insertions(+) diff --git a/project.godot b/project.godot index 04afe16..2c0a272 100644 --- a/project.godot +++ b/project.godot @@ -71,6 +71,14 @@ player_2_phase={ , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":1,"button_index":2,"pressure":0.0,"pressed":true,"script":null) ] } +player_2_shrink={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":83,"key_label":0,"unicode":111,"location":0,"echo":false,"script":null) +, Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":83,"physical_keycode":0,"key_label":0,"unicode":115,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":1,"button_index":12,"pressure":0.0,"pressed":true,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":1,"button_index":7,"pressure":0.0,"pressed":true,"script":null) +] +} player_1_jump={ "deadzone": 0.5, "events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":4194320,"physical_keycode":0,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) @@ -104,6 +112,13 @@ player_1_phase={ , Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":2,"pressure":0.0,"pressed":true,"script":null) ] } +player_1_shrink={ +"deadzone": 0.5, +"events": [Object(InputEventKey,"resource_local_to_scene":false,"resource_name":"","device":-1,"window_id":0,"alt_pressed":false,"shift_pressed":false,"ctrl_pressed":false,"meta_pressed":false,"pressed":false,"keycode":0,"physical_keycode":4194322,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":12,"pressure":0.0,"pressed":true,"script":null) +, Object(InputEventJoypadButton,"resource_local_to_scene":false,"resource_name":"","device":0,"button_index":7,"pressure":0.0,"pressed":true,"script":null) +] +} [layer_names] diff --git a/scripts/actions.gd b/scripts/actions.gd index 7a66d6b..9b005ce 100644 --- a/scripts/actions.gd +++ b/scripts/actions.gd @@ -14,6 +14,7 @@ const ACTIONS = [ &"right", &"teleport", &"phase", + &"shrink", ] # Dictionary[Global.Player, Dictionary[StringName, StringName]] diff --git a/scripts/player.gd b/scripts/player.gd index 5d608ad..3fa0273 100644 --- a/scripts/player.gd +++ b/scripts/player.gd @@ -15,6 +15,13 @@ const GLIDE_TERMINAL_VELOCITY = 100 ## Used by [method _teleport]. const TELEPORT_DISTANCE = 512 +## How much to scale [member jump_velocity] when the player-character is shrunk. Setting this close +## to or below [code]0[/code] prevents jumping; setting this to [code]1[/code] or greater causes +## jumps while shrunk to be the same as normal size. +## [br][br] +## Used by [method _shrink]. +const JUMP_VELOCITY_SCALE_WHEN_SMALL = 0.85 + ## Which player controls this character? @export var player: Global.Player = Global.Player.ONE @@ -64,6 +71,9 @@ var double_jump_armed: bool = false var gravity = ProjectSettings.get_setting("physics/2d/default_gravity") var original_position: Vector2 +# Whether the player-character is currently shrunk. See _shrink(). +var _is_shrunk := false + @onready var _sprite: AnimatedSprite2D = %AnimatedSprite2D @onready var _initial_sprite_frames: SpriteFrames = %AnimatedSprite2D.sprite_frames @onready var _double_jump_particles: CPUParticles2D = %DoubleJumpParticles @@ -171,6 +181,29 @@ func _phase() -> void: _sprite.modulate.a = 1 +## When the "shrink" action is pressed, toggle the player between normal size and half-size. While +## shrunk, the player can pass through narrower passages, but cannot jump so high. +func _shrink() -> void: + if Input.is_action_just_pressed(Actions.lookup(player, "shrink")): + _is_shrunk = not _is_shrunk + + if _is_shrunk: + # Shrink the player-character's sprite and collision shape + scale = Vector2(0.5, 0.5) + else: + scale = Vector2(1, 1) + + if _is_shrunk: + # Reduce the jump height while shrunk. _jump() sets velocity.y to -jump_velocity, so + # clamping this to a smaller value cuts the initial upwards velocity, and hence the jump + # height. + if velocity.y < -jump_velocity * JUMP_VELOCITY_SCALE_WHEN_SMALL: + velocity.y = -jump_velocity * JUMP_VELOCITY_SCALE_WHEN_SMALL + + # TODO: should there be other consequences to being small? Could we make the player somehow more + # vulnerable to enemies? + + func _physics_process(delta): # Don't move if there are no lives left. if Global.lives <= 0: @@ -198,6 +231,8 @@ func _physics_process(delta): if coyote_timer <= 0: velocity.y += gravity * delta + # _shrink() + # Get the input direction and handle the movement/deceleration. var direction = Input.get_axis(Actions.lookup(player, "left"), Actions.lookup(player, "right")) if direction: