diff --git a/assets/sounds/244654__dsg__pop-2.ogg b/assets/sounds/244654__dsg__pop-2.ogg new file mode 100644 index 0000000..ddc0080 Binary files /dev/null and b/assets/sounds/244654__dsg__pop-2.ogg differ diff --git a/assets/sounds/244654__dsg__pop-2.ogg.import b/assets/sounds/244654__dsg__pop-2.ogg.import new file mode 100644 index 0000000..b7ff4b4 --- /dev/null +++ b/assets/sounds/244654__dsg__pop-2.ogg.import @@ -0,0 +1,19 @@ +[remap] + +importer="oggvorbisstr" +type="AudioStreamOggVorbis" +uid="uid://dmx2rtf2vy1" +path="res://.godot/imported/244654__dsg__pop-2.ogg-27b8aa37b813c338a8b122e748e26d18.oggvorbisstr" + +[deps] + +source_file="res://assets/sounds/244654__dsg__pop-2.ogg" +dest_files=["res://.godot/imported/244654__dsg__pop-2.ogg-27b8aa37b813c338a8b122e748e26d18.oggvorbisstr"] + +[params] + +loop=false +loop_offset=0 +bpm=0 +beat_count=0 +bar_beats=4 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 0000000..fda27bf Binary files /dev/null and b/assets/sounds/596541__tothrec2__large-wings-flapping-foley.ogg differ 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..86cf7f3 100644 --- a/components/player/player.tscn +++ b/components/player/player.tscn @@ -1,8 +1,10 @@ -[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://dmx2rtf2vy1" path="res://assets/sounds/244654__dsg__pop-2.ogg" id="3_ph1f6"] [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 @@ -13,6 +15,11 @@ random_pitch = 1.1 streams_count = 1 stream_0/stream = ExtResource("3_wa6cj") +[sub_resource type="AudioStreamRandomizer" id="AudioStreamRandomizer_ph1f6"] +random_pitch = 1.2 +streams_count = 1 +stream_0/stream = ExtResource("3_ph1f6") + [node name="Player" type="CharacterBody2D" groups=["players"]] collision_layer = 3 collision_mask = 5 @@ -51,3 +58,12 @@ 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") + +[node name="TeleportSFX" type="AudioStreamPlayer" parent="SoundEffects"] +unique_name_in_owner = true +stream = SubResource("AudioStreamRandomizer_ph1f6") +volume_db = -10.0 diff --git a/project.godot b/project.godot index b99ab97..2c0a272 100644 --- a/project.godot +++ b/project.godot @@ -59,6 +59,26 @@ player_2_right={ , Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":1,"axis":0,"axis_value":1.0,"script":null) ] } +player_2_teleport={ +"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":4194325,"key_label":0,"unicode":0,"location":1,"echo":false,"script":null) +, 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_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) @@ -80,6 +100,25 @@ player_1_right={ , Object(InputEventJoypadMotion,"resource_local_to_scene":false,"resource_name":"","device":0,"axis":0,"axis_value":1.0,"script":null) ] } +player_1_teleport={ +"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":4194309,"key_label":0,"unicode":0,"location":0,"echo":false,"script":null) +, 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) +] +} +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 f6a557f..9b005ce 100644 --- a/scripts/actions.gd +++ b/scripts/actions.gd @@ -12,6 +12,9 @@ const ACTIONS = [ &"jump", &"left", &"right", + &"teleport", + &"phase", + &"shrink", ] # Dictionary[Global.Player, Dictionary[StringName, StringName]] diff --git a/scripts/player.gd b/scripts/player.gd index 15e182d..3fa0273 100644 --- a/scripts/player.gd +++ b/scripts/player.gd @@ -3,6 +3,25 @@ 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 + +## How many pixels the player-character should teleport horizontally when the +## teleport special ability is used. +## [br][br] +## 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 @@ -52,11 +71,16 @@ 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 @onready var _jump_sfx: AudioStreamPlayer = %JumpSFX +@onready var _glide_sfx: AudioStreamPlayer = %GlideSFX +@onready var _teleport_sfx: AudioStreamPlayer = %TeleportSFX func _set_sprite_frames(new_sprite_frames): @@ -106,11 +130,87 @@ 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() + + +## 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() + + +## 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 + + +## 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: return + # _phase() + # Handle jump if is_on_floor(): coyote_timer = (coyote_time + delta) @@ -131,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: @@ -142,6 +244,8 @@ func _physics_process(delta): else: velocity.x = move_toward(velocity.x, 0, acceleration * delta) + # _glide() + if velocity == Vector2.ZERO: _sprite.play("idle") else: @@ -156,6 +260,8 @@ func _physics_process(delta): move_and_slide() + # _teleport(direction) + coyote_timer -= delta jump_buffer_timer -= delta