diff --git a/.github/workflows/export.yml b/.github/workflows/export.yml index 26bfeed..f08097e 100644 --- a/.github/workflows/export.yml +++ b/.github/workflows/export.yml @@ -32,10 +32,10 @@ jobs: godot_export_templates_download_url: https://downloads.tuxfamily.org/godotengine/${{env.GODOT_MONO_URL_VERSION}}/mono/Godot_v${{env.GODOT_MONO_FILE_VERSION}}_mono_export_templates.tpz relative_project_path: ./ archive_export_output: true - create_release: false + create_release: true base_version: 0.0.1 - name: upload executables uses: actions/upload-artifact@v2 with: name: executables - path: ~/.local/share/godot/dist/*.zip \ No newline at end of file + path: ~/.local/share/godot/dist/*.zip diff --git a/Cliffs.cs b/Cliffs.cs index 31fa8fd..d3bce61 100644 --- a/Cliffs.cs +++ b/Cliffs.cs @@ -12,16 +12,39 @@ public class Cliffs : Area2D private Vector2 _cliffsPosition; private CollisionShape2D _cliffsCollider; private bool _isPlayerIntersectingCliffs; - private bool _isPlayerFullyIntersectingCliffs; private AudioStreamPlayer _audio; + private AudioStreamPlayer _music; + private TileMap _iceTileMap; + private Vector2 _topLeft; + private Vector2 _bottomRight; + private Vector2 _topRight; + private Vector2 _bottomLeft; public override void _Ready() { _audio = GetNode ("../AudioStreamPlayer"); _audio.Stream = ResourceLoader.Load ("res://ambience_summer.wav"); + _music = GetNode ("../AudioStreamPlayer2"); + _music.Stream = ResourceLoader.Load ("res://music2_trimmed.wav"); LoopAudio (_audio.Stream); + LoopAudio (_music.Stream); _cliffsCollider = GetNode ("CollisionShape2D"); _cliffsPosition = _cliffsCollider.GlobalPosition; + _iceTileMap = GetNode ("Ice"); + + // TODO Remove. + for (var i = 1; i < 11; ++i) + { + if (Name != "Upper Cliffs") return; + GetNode ("Waterfall/waterfall " + i).Play(); + } + + // TODO Remove. + for (var i = 1; i < 4; ++i) + { + if (Name != "Upper Cliffs") return; + GetNode ("Waterfall/waterfall mist " + i).Play(); + } } // ReSharper disable once UnusedMember.Global @@ -40,13 +63,15 @@ public void _on_cliffs_area_exited (Area2D area) _playerArea = area; _isPlayerIntersectingCliffs = false; + GetNode ("../Player").IsInCliffs = false; } public override void _Process (float delta) { Update(); + Sounds(); - if (_playerArea == null) return; + if (!_isPlayerIntersectingCliffs) return; _playerExtents = GetExtents (_playerArea); _cliffsExtents = GetExtents (this); @@ -58,20 +83,25 @@ public override void _Process (float delta) _cliffsRect.Position = _cliffsPosition - _cliffsExtents; _cliffsRect.Size = _cliffsExtents * 2; - _isPlayerFullyIntersectingCliffs = _isPlayerIntersectingCliffs && _cliffsRect.Encloses (_playerRect); + GetNode ("../Player").IsInCliffs = _isPlayerIntersectingCliffs && _cliffsRect.Encloses (_playerRect); - var player = GetNode ("../Player"); - player.Set ("IsIntersectingCliffs", _isPlayerIntersectingCliffs); - player.Set ("IsFullyIntersectingCliffs", _isPlayerFullyIntersectingCliffs); + _topLeft = _playerArea.GlobalPosition - _playerExtents; + _bottomRight = _playerArea.GlobalPosition + _playerExtents; + _topRight.x = _playerArea.GlobalPosition.x + _playerExtents.x; + _topRight.y = _playerArea.GlobalPosition.y - _playerExtents.y; + _bottomLeft.x = _playerArea.GlobalPosition.x - _playerExtents.x; + _bottomLeft.y = _playerArea.GlobalPosition.y + _playerExtents.y; - Sounds(); + GetNode ("../Player").IsTouchingCliffIce = IsIntersectingAnyTile (_topLeft, _iceTileMap) || + IsIntersectingAnyTile (_bottomRight, _iceTileMap) || + IsIntersectingAnyTile (_topRight, _iceTileMap) || + IsIntersectingAnyTile (_bottomLeft, _iceTileMap); } private void Sounds() { - if (_audio.Playing) return; - - _audio.Play(); + if (!_audio.Playing) _audio.Play(); + if (!_music.Playing) _music.Play(); } private static Vector2 GetExtents (Area2D area) @@ -79,6 +109,7 @@ private static Vector2 GetExtents (Area2D area) var collisionShape = area.GetNode ("CollisionShape2D"); var collisionRect = collisionShape.Shape as RectangleShape2D; + // ReSharper disable once InvertIf if (collisionRect == null) { OnWrongCollisionShape (area, collisionShape.Shape); diff --git a/IStateMachine.cs b/IStateMachine.cs new file mode 100644 index 0000000..679f5d5 --- /dev/null +++ b/IStateMachine.cs @@ -0,0 +1,22 @@ +using System; + +public interface IStateMachine where T : Enum +{ + public delegate void TransitionAction(); + public delegate bool TransitionTrigger(); + public bool Is (T state); + public void OnTransitionTo (T to, TransitionAction action); + public void OnTransitionFrom (T from, TransitionAction action); + public void OnTransition (T from, T to, TransitionAction action); + public void AddTrigger (T from, T to, TransitionTrigger trigger); + public void Update(); + public void To (T to); + public void ToIf (T to, bool condition); + public void Push (T to); + public void PushIf (T to, bool condition); + public void Pop(); + public void PopIf (bool condition); + public void PopIf (T state); + public void PopIf (T state, bool condition); + public void Reset(); +} \ No newline at end of file diff --git a/Log.cs b/Log.cs new file mode 100644 index 0000000..012ca60 --- /dev/null +++ b/Log.cs @@ -0,0 +1,27 @@ +using System; +using Godot; + +public static class Log +{ + public static Level CurrentLevel { get; set; } = Level.Info; + + public enum Level + { + Off, + Warn, + Info, + Debug, + All + } + + public static bool All (string message) => CurrentLevel > Level.Debug && Print (message, Level.All); + public static bool Debug (string message) => CurrentLevel > Level.Info && Print (message, Level.Debug); + public static bool Info (string message) => CurrentLevel > Level.Warn && Print (message, Level.Info); + public static bool Warn (string message) => CurrentLevel > Level.Off && Print (message, Level.Warn); + + private static bool Print (string message, Level level) + { + GD.Print ($"{level}: {DateTime.Now:HH:mm:ss:ffff} ", message); + return true; + } +} \ No newline at end of file diff --git a/Player.cs b/Player.cs index 75c80c7..e00010a 100644 --- a/Player.cs +++ b/Player.cs @@ -1,97 +1,113 @@ +using System; using System.Collections.Generic; using Godot; using static Tools; - +using Input = Tools.Input; + +// TODO Bug: Running / preparing to climb with up arrow and single horizontal arrow alternates states in a loop. +// TODO Bug: Running / climbing horizontally against a wall alternates in a loop between idle and running. +// TODO Bug: Climbing to cliff gap above waterfall still thinks IsFullyIntersectingCliffs is true. +// TODO Stop polling and have event-based input state changes only. +// TODO Check if item being used for scraping is pickaxe. For now it's the default and only item. +// TODO Create _isPreparingToScrapeCliff, which is true when attempting scraping, but falling hasn't +// TODO gotten fast enough yet to activate it (i.e., _velocity.y < CliffScrapingActivationVelocity). +// TODO In this case show an animation of pulling out pickaxe and swinging it into the cliff, timing it +// TODO so that the pickaxe sinks into the cliff when CliffScrapingActivationVelocity is reached. +// TODO The cliff scraping sound effect will need to be adjusted as well to not begin until +// TODO _isPreparingToScrapeCliff is complete (may resolve itself automatically). +// TODO Climbing down? Or leave out? // Climbing terminology: -// Traversing: Climbing horizontally // Scraping: Using pickaxe to slow a cliff fall // Cliffhanging: Attached to a cliff above the ground, not moving -// TODO Use ray tracing to fix IsFullyIntersectingCliffs bug, where IsOnFloor is true and IsFullyIntersectingCliffs -// TODO should be true, but is false, even when standing fully in front of cliffs. +// Traversing: Climbing horizontally +// Free falling: Moving down, without scraping, can also be coming down from a jump. public class Player : KinematicBody2D { + // @formatter:off // Godot-configurable options [Export] public float HorizontalRunningSpeed = 50.0f; - [Export] public float HorizontalClimbingSpeed = 200.0f; + [Export] public float TraverseSpeed = 200.0f; [Export] public float VerticalClimbingSpeed = 200.0f; [Export] public float HorizontalRunJumpFriction = 0.9f; [Export] public float HorizontalRunJumpStoppingFriction = 0.6f; - [Export] public float HorizontalClimbFriction = 0.9f; + [Export] public float TraverseFriction = 0.9f; [Export] public float HorizontalClimbStoppingFriction = 0.6f; + [Export] public float HorizontalFreeFallingSpeed = 10.0f; [Export] public float CliffScrapingSpeed = 40.0f; [Export] public float CliffScrapingActivationVelocity = 800.0f; - [Export] public float VelocityEpsilon = 100.0f; + [Export] public float VelocityEpsilon = 1.0f; [Export] public float JumpPower = 800.0f; + // ReSharper disable once InconsistentNaming [Export] public float Gravity_ = 30.0f; [Export] public string IdleLeftAnimation = "player_idle_left"; - [Export] public string PreparingToClimbUpAnimation = "player_idle_back"; - [Export] public string FallingAnimation = "player_falling"; + [Export] public string ClimbingPrepAnimation = "player_idle_back"; + [Export] public string FreeFallingAnimation = "player_falling"; [Export] public string ClimbingUpAnimation = "player_climbing_up"; [Export] public string CliffHangingAnimation = "player_cliff_hanging"; - [Export] public string ClimbingHorizontallyAnimation = "player_climbing_horizontally"; + [Export] public string TraversingAnimation = "player_traversing"; [Export] public string ScrapingAnimation = "player_scraping"; [Export] public string RunAnimation = "player_running"; [Export] public string CliffScrapingSoundFile = "res://cliff_scrape.wav"; [Export] public bool CliffScrapingSoundLooping = true; + // ReSharper disable once RedundantDefaultMemberInitializer [Export] public float CliffScrapingSoundLoopBeginSeconds = 0.0f; [Export] public float CliffScrapingSoundLoopEndSeconds = 4.5f; [Export] public float CliffScrapingSoundVelocityPitchScaleModulation = 4.0f; [Export] public float CliffScrapingSoundMinPitchScale = 2.0f; + [Export] public State InitialState = State.Idle; + [Export] public Log.Level LogLevel = Log.Level.Debug; + // @formatter:on // Field must be publicly accessible from Cliffs.cs // ReSharper disable once MemberCanBePrivate.Global // ReSharper disable once UnassignedField.Global - public bool IsIntersectingCliffs; + public bool IsInCliffs; // Field must be publicly accessible from Cliffs.cs // ReSharper disable once MemberCanBePrivate.Global // ReSharper disable once UnassignedField.Global - public bool IsFullyIntersectingCliffs; + public bool IsTouchingCliffIce; + + public enum State + { + Idle, + Running, + Jumping, + ClimbingPrep, + ClimbingUp, + CliffHanging, + Traversing, + Scraping, + FreeFalling + } private Vector2 _velocity; - private RichTextLabel _label; - private AnimatedSprite _sprite; - private AudioStreamPlayer _audio; - private Timer _preparingToClimbUpTimer; + private RichTextLabel _label = null!; + private AnimatedSprite _sprite = null!; + private AudioStreamPlayer _audio = null!; + private Timer _climbingPrepTimer = null!; private bool _isFlippedHorizontally; - private readonly List _printLines = new List (); - - // TODO State machine - private bool _isRunning; - private bool _isJumping; - private bool _isPreparingToClimbUp; - private bool _isPreparedToClimbUp; - private bool _isClimbingUp; - private bool _isCliffHanging; - private bool _isClimbingHorizontally; - private bool _isClimbingLeft; - private bool _isClimbingRight; + private bool _wasFlippedHorizontally; + private readonly List _printLines = new(); + private IStateMachine _stateMachine = null!; private uint _fallingStartTimeMs; private uint _elapsedFallingTimeMs; private uint _lastTotalFallingTimeMs; private float _highestVerticalVelocity; - private bool _isScrapingCliff; + private readonly List _disabledFloors = new(); - private bool IsScrapingCliff + private static readonly Dictionary TransitionTable = new() { - get => _isScrapingCliff; - set - { - // TODO State Machine - if (!_isScrapingCliff && value && IsFalling() && !_audio.Playing) - { - _audio.Play(); - PrintLine ("Sound effects: Playing cliff scraping sound."); - } - else if (_isScrapingCliff && !value && _audio.Playing) - { - _audio.Stop(); - PrintLine ("Sound effects: Stopped cliff scraping sound."); - } - - _isScrapingCliff = value; - } - } + { State.Idle, new[] { State.Running, State.Jumping, State.ClimbingPrep, State.ClimbingUp, State.FreeFalling } }, + { State.Running, new[] { State.Idle, State.Jumping, State.FreeFalling, State.ClimbingPrep } }, + { State.Jumping, new[] { State.Idle, State.FreeFalling, State.Running } }, + { State.ClimbingPrep, new[] { State.ClimbingUp, State.Idle, State.Running, State.Jumping } }, + { State.ClimbingUp, new[] { State.FreeFalling } }, + { State.CliffHanging, new[] { State.ClimbingUp, State.Traversing, State.FreeFalling } }, + { State.Traversing, new[] { State.CliffHanging, State.FreeFalling } }, + { State.Scraping, new[] { State.CliffHanging, State.FreeFalling, State.Idle } }, + { State.FreeFalling, new[] { State.Scraping, State.Idle, State.Running, State.Jumping } } + }; public override void _Ready() { @@ -100,515 +116,222 @@ public override void _Ready() LoopAudio (_audio.Stream, CliffScrapingSoundLoopBeginSeconds, CliffScrapingSoundLoopEndSeconds); _label = GetNode ("DebuggingText"); _sprite = GetNode ("AnimatedSprite"); - _preparingToClimbUpTimer = GetNode ("ClimbingReadyTimer"); + _climbingPrepTimer = GetNode ("ClimbingReadyTimer"); _sprite.Animation = IdleLeftAnimation; + _sprite.Play(); + InitializeStateMachine(); } public override void _PhysicsProcess (float delta) { - Run(); - Jump(); - Climb(); - Friction(); - Gravity(); - PostGravity(); + if (ShouldDisableFloor()) DisableFloor(); // TODO Create a separate state machine for the dropdownable floors. + _stateMachine.Update(); + HorizontalVelocity(); + VerticalVelocity(); SoundEffects(); - + if (Mathf.Abs (_velocity.x) < VelocityEpsilon) _velocity.x = 0.0f; + if (Mathf.Abs (_velocity.y) < VelocityEpsilon) _velocity.y = 0.0f; _velocity = MoveAndSlide (_velocity, Vector2.Up); - - if (ShouldBecomeIdle()) BecomeIdle(); - Animations(); CalculateFallingStats(); - - // @formatter:off - PrintLine ("IsRightArrowPressed(): " + IsRightArrowPressed() + "\nIsLeftArrowPressed(): " + IsLeftArrowPressed()); - PrintLine ("IsInMotion(): " + IsInMotion()); - PrintLine ("IsFalling(): " + IsFalling()); - PrintLine ("_velocity: " + _velocity); - PrintLine ("Vertical velocity (mph): " + _velocity.y * 0.028334573333333); - PrintLine ("Highest vertical velocity: " + _highestVerticalVelocity); - PrintLine ("Highest vertical velocity (mph): " + _highestVerticalVelocity * 0.028334573333333); - PrintLine ("Falling time (sec): " + (_elapsedFallingTimeMs > 0 ? _elapsedFallingTimeMs / 1000.0f : _lastTotalFallingTimeMs / 1000.0f)); + PrintLine (DumpState()); Print(); - // @formatter:on - } - - // Godot Timer callback - // ReSharper disable once UnusedMember.Global - public void _OnClimbingReadyTimerTimeout() - { - if (!_isPreparingToClimbUp) return; - - _isPreparedToClimbUp = IsUpArrowPressed(); - _isPreparingToClimbUp = false; } + // Godot input callback public override void _UnhandledInput (InputEvent @event) { - if (!(@event is InputEventKey eventKey)) return; - - if (eventKey.IsActionReleased ("use_item")) IsScrapingCliff = false; - else if (eventKey.IsActionReleased ("show_text")) _label.Visible = !_label.Visible; - else if (eventKey.IsActionReleased ("respawn")) GlobalPosition = new Vector2 (952, -4032); + if (IsReleased (Input.Text, @event)) _label.Visible = !_label.Visible; + if (IsReleased (Input.Respawn, @event)) Respawn(); } - private void Run() + private void Respawn() { - PrintLine ("IsAnyHorizontalArrowPressed(): " + IsAnyHorizontalArrowPressed()); - PrintLine ("IsAnyVerticalArrowPressed(): " + IsAnyVerticalArrowPressed()); - PrintLine ("_isRunning: " + _isRunning); - PrintLine ("shouldRun(): " + ShouldRun()); - - if (!IsInMotionHorizontally() || !IsOnFloor()) _isRunning = false; - - if (!ShouldRun()) return; - if (ShouldRunRight()) RunRight(); - else if (ShouldRunLeft()) RunLeft(); - - _isRunning = true; - _isJumping = false; - _isPreparingToClimbUp = false; - _isPreparedToClimbUp = false; - _isClimbingUp = false; - _isClimbingHorizontally = false; - _isClimbingLeft = false; - _isClimbingRight = false; - IsScrapingCliff = false; - _isCliffHanging = false; + _stateMachine.Reset(); + GlobalPosition = new Vector2 (952, -4032); + EnableFloors(); } - private bool ShouldRun() + private void EnableFloors() { - return IsExclusivelyActiveUnless (InputType.Horizontal, _isRunning) && !ShouldClimbHorizontally(); + _disabledFloors.ForEach (x => x.Disabled = false); + _disabledFloors.Clear(); } - private static bool ShouldRunRight() - { - return IsRightArrowPressed() && !IsLeftArrowPressed(); - } + private bool ShouldDisableFloor() => IsOnFloor() && WasDownArrowPressedOnce(); + private bool IsMoving() => IsMovingHorizontally() || IsMovingVertically(); + private bool IsMovingVertically() => Mathf.Abs (_velocity.y) > VelocityEpsilon; + private bool IsMovingHorizontally() => Mathf.Abs (_velocity.x) > VelocityEpsilon; + private bool IsMovingUp() => _velocity.y + VelocityEpsilon < 0.0f; + private bool IsMovingDown() => _velocity.y - VelocityEpsilon > 0.0f; + private bool IsMovingLeft() => _velocity.x + VelocityEpsilon < 0.0f; + private bool IsMovingRight() => _velocity.x + VelocityEpsilon < 0.0f; - private void RunRight() - { - _velocity.x += HorizontalRunningSpeed; - FlipHorizontally (true); - } - - private static bool ShouldRunLeft() - { - return IsLeftArrowPressed() && !IsRightArrowPressed(); - } - - private void RunLeft() - { - _velocity.x -= HorizontalRunningSpeed; - FlipHorizontally (false); - } - - // Flips everything except RichTextLabel - private void FlipHorizontally (bool flip) + // Only for ground/floor that has climbable cliffs below it + private void DisableFloor() { - if (_isFlippedHorizontally == flip) return; + for (var i = 0; i < GetSlideCount(); ++i) + { + var collision = GetSlideCollision (i); + var collider = collision.Collider as StaticBody2D; - _isFlippedHorizontally = flip; + if (collider == null) continue; + if (!collider.IsInGroup ("Cliffs")) continue; + if (!collider.IsInGroup ("Ground")) continue; + if (!collider.IsInGroup ("Dropdownable")) continue; - // Flip entire parent node and all children. - var scale = Scale; - scale.x = -scale.x; - Scale = scale; + var colliderShape = collision.ColliderShape as CollisionShape2D; - // Bugfix: Undo inadvertent label flip (reverses text). - var labelRectScale = _label.RectScale; - labelRectScale.x = -labelRectScale.x; - _label.RectScale = labelRectScale; + if (colliderShape == null) continue; - // Bugfix: Move label over instead of reversing text. - var labelRectPosition = _label.RectPosition; - labelRectPosition.x += _isFlippedHorizontally ? 160 : -160; - _label.RectPosition = labelRectPosition; + colliderShape.Disabled = true; + _disabledFloors.Add (colliderShape); + } } - private void Jump() + private void HorizontalVelocity() { - if (IsOnFloor()) _isJumping = false; - - if (Input.IsActionJustPressed ("jump") && IsOnFloor()) + if (_stateMachine.Is (State.Running) && IsExclusivelyActiveUnless (Input.Left, _stateMachine.Is (State.Running))) { - _isJumping = true; - _isRunning = false; - _isPreparingToClimbUp = false; - _isPreparedToClimbUp = false; - _isClimbingUp = false; - _isClimbingHorizontally = false; - _isClimbingLeft = false; - _isClimbingRight = false; - IsScrapingCliff = false; - _isCliffHanging = false; - _velocity.y -= JumpPower; + _velocity.x -= HorizontalRunningSpeed; + FlipHorizontally (false); } - // Make jumps less high when releasing jump button early. - // (Holding down jump continuously allows gravity to take over.) - if (Input.IsActionJustReleased ("jump") && _isJumping && IsMovingUp()) _velocity.y = 0.0f; - - PrintLine ("_isJumping: " + _isJumping); - } - - // TODO Test adding && !_isJumping - // TODO Differentiate between jump-falling (landing) and other falling. - private bool IsFalling() - { - return _velocity.y - VelocityEpsilon > 0.0f; - } - - private bool IsMovingUp() - { - return _velocity.y + VelocityEpsilon < 0.0f; - } - - private void Climb() - { - if (ShouldPrepareToClimbUp()) PrepareToClimbUp(); - else if (ShouldScrapeCliff()) ScrapeCliff(); - else if (ShouldCliffHang()) CliffHang(); - else if (ShouldClimbHorizontally()) ClimbHorizontally(); - else if (_isClimbingHorizontally) CliffHang(); - else if (ShouldClimbUp()) ClimbUp(); - else if (_isClimbingUp) DropFromCliff(); - else if (ShouldClimbDownFromFloor()) ClimbDownFromFloor(); - else if (ShouldDropFromCliff()) DropFromCliff(); - - // @formatter:off - PrintLine ("_isPreparingToClimbUp: " + _isPreparingToClimbUp + - "\n_isPreparedToClimbUp: " + _isPreparedToClimbUp + - "\nShouldClimbUp(): " + ShouldClimbUp() + - "\n_isClimbingUp: " + _isClimbingUp + - "\nIsFalling(): " + IsFalling() + - "\nIsMovingUp(): " + IsMovingUp() + - "\n_isScrapingCliff: " + IsScrapingCliff + - "\nShouldScrapeCliff(): " + ShouldScrapeCliff() + - "\nIsOnFloor(): " + IsOnFloor() + - "\nIsIntersectingCliffs: " + IsIntersectingCliffs + - "\nIsFullyIntersectingCliffs: " + IsFullyIntersectingCliffs + - "\n_isClimbingHorizontally: " + _isClimbingHorizontally + - "\n_isCliffHanging: " + _isCliffHanging + - "\nShouldClimbHorizontally(): " + ShouldClimbHorizontally()); - // @formatter:on - } - - // TODO Test ignore input exclusion condition _isReadyToClimbUp - // TODO Use IsFullyIntersectingCliffs after using ray tracing to fix errors. - // Currently, the running state is allowed to transition instantly to the getting ready to climb state. - // Otherwise, !IsInMotion() would need to be a condition here. - private bool ShouldPrepareToClimbUp() - { - return IsIntersectingCliffs && !_isPreparingToClimbUp && !_isPreparedToClimbUp && !_isClimbingUp && IsOnFloor() && - !IsFalling() && WasUpArrowPressedOnce(); - } - - private void PrepareToClimbUp() - { - FlipHorizontally (false); - - _isPreparingToClimbUp = true; - _isPreparedToClimbUp = false; - IsScrapingCliff = false; - _isRunning = false; - _isJumping = false; - _isClimbingUp = false; - _isClimbingHorizontally = false; - _isClimbingLeft = false; - _isClimbingRight = false; - _isCliffHanging = false; - - _preparingToClimbUpTimer.Start(); - } - - // TODO Create _isPreparingToScrapeCliff, which is true when attempting scraping, but falling hasn't - // TODO gotten fast enough yet to activate it (i.e., ShouldScrapeCliff() would return true except - // TODO _velocity.y < CliffScrapingActivationVelocity) In this case show an animation of pulling out pickaxe and - // TODO swinging it into the cliff, timing it so that the pickaxe sinks into the cliff when - // TODO CliffScrapingActivationVelocity is reached. The cliff scraping sound effect will need to be adjusted - // TODO as well to not begin until _isPreparingToScrapeCliff is complete (may resolve itself automatically). - // TODO Test with !IsOnFloor() removed since IsFalling() makes it redundant. - // TODO Check if item being used is pickaxe. For now it's the default and only item. - private bool ShouldScrapeCliff() - { - return IsFullyIntersectingCliffs && !IsOnFloor() && IsFalling() && _velocity.y >= CliffScrapingActivationVelocity && - IsExclusivelyActiveUnless (InputType.Item, IsScrapingCliff); - } - - private void ScrapeCliff() - { - IsScrapingCliff = true; - _isRunning = false; - _isJumping = false; - _isPreparingToClimbUp = false; - _isPreparedToClimbUp = false; - _isClimbingUp = false; - _isClimbingHorizontally = false; - _isClimbingLeft = false; - _isClimbingRight = false; - _isCliffHanging = false; - } - - private bool ShouldCliffHang() - { - return IsScrapingCliff && !IsInMotion() && !IsOnFloor(); - } - - private void CliffHang() - { - _isCliffHanging = true; - _isRunning = false; - _isJumping = false; - _isPreparingToClimbUp = false; - _isPreparedToClimbUp = false; - _isClimbingUp = false; - _isClimbingHorizontally = false; - _isClimbingLeft = false; - _isClimbingRight = false; - IsScrapingCliff = false; - } - - private bool ShouldClimbHorizontally() - { - return (_isCliffHanging || _isClimbingHorizontally) && IsExclusivelyActiveUnless (InputType.Horizontal, _isClimbingHorizontally); - } - - private void ClimbHorizontally() - { - if (ShouldClimbLeft()) ClimbLeft(); - else if (ShouldClimbRight()) ClimbRight(); - - _isClimbingHorizontally = true; - _isRunning = false; - _isJumping = false; - _isPreparingToClimbUp = false; - _isPreparedToClimbUp = false; - _isClimbingUp = false; - IsScrapingCliff = false; - _isCliffHanging = false; - } - - private static bool ShouldClimbLeft() - { - return IsLeftArrowPressed() && !IsRightArrowPressed(); - } - - private void ClimbLeft() - { - _isClimbingLeft = true; - _isClimbingRight = false; - } - - private static bool ShouldClimbRight() - { - return IsRightArrowPressed() && !IsLeftArrowPressed(); - } - - private void ClimbRight() - { - _isClimbingRight = true; - _isClimbingLeft = false; - } - - private bool ShouldClimbUp() - { - return (_isPreparedToClimbUp || _isClimbingUp || _isCliffHanging) && IsIntersectingCliffs && - IsExclusivelyActiveUnless (InputType.Up, _isClimbingUp); - } - - private void ClimbUp() - { - FlipHorizontally (false); - - _isClimbingUp = true; - _isRunning = false; - _isJumping = false; - _isPreparingToClimbUp = false; - _isPreparedToClimbUp = false; - _isClimbingHorizontally = false; - _isClimbingLeft = false; - _isClimbingRight = false; - IsScrapingCliff = false; - _isCliffHanging = false; - } - - private bool ShouldDropFromCliff() - { - return _isCliffHanging && WasDownArrowPressedOnce(); - } - - private void DropFromCliff() - { - FlipHorizontally (false); - - _isCliffHanging = false; - _isRunning = false; - _isJumping = false; - _isPreparingToClimbUp = false; - _isPreparedToClimbUp = false; - _isClimbingUp = false; - _isClimbingHorizontally = false; - _isClimbingLeft = false; - _isClimbingRight = false; - IsScrapingCliff = false; - } - - private bool ShouldClimbDownFromFloor() - { - return WasDownArrowPressedOnce() && IsOnFloor(); - } + if (_stateMachine.Is (State.Running) && IsExclusivelyActiveUnless (Input.Right, _stateMachine.Is (State.Running))) + { + _velocity.x += HorizontalRunningSpeed; + FlipHorizontally (true); + } - // Only for ground/floor that has climbable cliffs below it - private void ClimbDownFromFloor() - { - var isClimbingDown = false; + if (_stateMachine.Is (State.Traversing)) + { + var left = IsLeftArrowPressed(); + var right = IsRightArrowPressed(); + var leftOnly = left && !right; + var rightOnly = right && !left; + _velocity.x += TraverseSpeed * (leftOnly ? -1 : rightOnly ? 1 : 0.0f); + _velocity.x = SafelyClamp (_velocity.x, -TraverseSpeed, TraverseSpeed); + } - for (var i = 0; i < GetSlideCount(); ++i) + if (_stateMachine.Is (State.Jumping) && IsExclusivelyActiveUnless (Input.Left, _stateMachine.Is (State.Jumping))) { - var collision = GetSlideCollision (i); - var collider = collision.Collider as StaticBody2D; - if (collider == null) continue; - if (!collider.IsInGroup ("Cliffs")) continue; - if (!collider.IsInGroup ("Ground")) continue; - if (!collider.IsInGroup ("Dropdownable")) continue; - var colliderShape = collision.ColliderShape as CollisionShape2D; - if (colliderShape == null) continue; - colliderShape.Disabled = true; - isClimbingDown = true; + _velocity.x -= HorizontalRunningSpeed; + FlipHorizontally (false); } - if (!isClimbingDown) return; + if (_stateMachine.Is (State.Jumping) && IsExclusivelyActiveUnless (Input.Right, _stateMachine.Is (State.Jumping))) + { + _velocity.x += HorizontalRunningSpeed; + FlipHorizontally (true); + } - _isRunning = false; - _isJumping = false; - _isPreparingToClimbUp = false; - _isPreparedToClimbUp = false; - _isClimbingUp = false; - _isClimbingHorizontally = false; - _isClimbingLeft = false; - _isClimbingRight = false; - IsScrapingCliff = false; - _isCliffHanging = false; + if (_stateMachine.Is (State.FreeFalling) && IsExclusivelyActiveUnless (Input.Left, _stateMachine.Is (State.FreeFalling))) + { + _velocity.x -= HorizontalRunningSpeed; + FlipHorizontally (false); + } - FlipHorizontally (false); - } + if (_stateMachine.Is (State.FreeFalling) && IsExclusivelyActiveUnless (Input.Right, _stateMachine.Is (State.FreeFalling))) + { + _velocity.x += HorizontalRunningSpeed; + FlipHorizontally (true); + } - private void Friction() - { + // TODO Check if running or jumping + // TODO Get rid of else if + // Friction + // @formatter:off if (!IsAnyHorizontalArrowPressed()) _velocity.x *= HorizontalRunJumpStoppingFriction; - else if (_isClimbingHorizontally) _velocity.x *= HorizontalClimbFriction; + else if (_stateMachine.Is (State.Traversing)) _velocity.x *= TraverseFriction; else _velocity.x *= HorizontalRunJumpFriction; + // @formatter:on } - private void Gravity() + private void VerticalVelocity() { _velocity.y += Gravity_; - } - private void PostGravity() - { - if (_isClimbingUp) + // TODO Remove else if's, order shouldn't matter. + // TODO Make relative by subtracting gravity from _velocity.y and round to 0 within a couple decimal places (or epsilon). + if (_stateMachine.Is (State.Jumping)) { - _velocity.y -= Gravity_ + VerticalClimbingSpeed * 0.01f; - _velocity.y = SafelyClampMin (_velocity.y, -VerticalClimbingSpeed); + // Makes jumps less high when releasing jump button early. + // (Holding down jump continuously allows gravity to take over.) + if (WasJumpKeyReleased() && IsMovingUp()) _velocity.y = 0.0f; // TODO Subtract gravity and round to 0. + } + else if (_stateMachine.Is (State.ClimbingUp)) + { + // TODO Testing + // if ((_sprite.Frame + 1) % 3 == 0) + if (_sprite.Frame == 1 || _sprite.Frame == 4) + { + _velocity.y = -VerticalClimbingSpeed; + //_velocity.y = SafelyClampMin (_velocity.y, -VerticalClimbingSpeed); + } + else + { + _velocity.y = 0.0f; + } } - else if (_isClimbingHorizontally) + else if (_stateMachine.Is (State.Traversing)) { - _velocity.x = _isClimbingLeft ? _velocity.x - HorizontalClimbingSpeed : _velocity.x + HorizontalClimbingSpeed; - _velocity.x = SafelyClamp (_velocity.x, -HorizontalClimbingSpeed, HorizontalClimbingSpeed); - _velocity.y = 0.0f; + _velocity.y = 0.0f; // TODO Subtract gravity and round to 0. } - else if (_isCliffHanging) + else if (_stateMachine.Is (State.CliffHanging)) { - _velocity.y = 0.0f; + _velocity.y = 0.0f; // TODO Subtract gravity and round to 0. } - else if (IsScrapingCliff) + else if (_stateMachine.Is (State.Scraping)) { _velocity.y -= CliffScrapingSpeed; _velocity.y = SafelyClampMin (_velocity.y, 0.0f); } } - private bool IsInMotion() + // Flips everything except RichTextLabel + private void FlipHorizontally (bool flip) { - return IsInMotionHorizontally() || IsInMotionVertically(); - } + if (_isFlippedHorizontally == flip) return; - private bool IsInMotionHorizontally() - { - return Mathf.Abs (_velocity.x) > VelocityEpsilon; - } + _isFlippedHorizontally = flip; - private bool IsInMotionVertically() - { - return Mathf.Abs (_velocity.y) > VelocityEpsilon; + // Flip entire parent node and all children. + var scale = Scale; + scale.x = -scale.x; + Scale = scale; + + // Bugfix: Undo inadvertent label flip (reverses text). + var labelRectScale = _label.RectScale; + labelRectScale.x = -labelRectScale.x; + _label.RectScale = labelRectScale; + + // Bugfix: Move label over instead of reversing text. + var labelRectPosition = _label.RectPosition; + labelRectPosition.x += _isFlippedHorizontally ? 160 : -160; + _label.RectPosition = labelRectPosition; } private void SoundEffects() { - if (IsScrapingCliff) + if (_stateMachine.Is (State.Scraping)) { _audio.PitchScale = CliffScrapingSoundMinPitchScale + - _velocity.y / CliffScrapingActivationVelocity / - CliffScrapingSoundVelocityPitchScaleModulation; + _velocity.y / CliffScrapingActivationVelocity / CliffScrapingSoundVelocityPitchScaleModulation; } } - private bool ShouldBecomeIdle() - { - return IsOnFloor() && !IsInMotion() && !_isPreparingToClimbUp && !_isPreparedToClimbUp && !_isClimbingUp; - } - - private void BecomeIdle() - { - _isRunning = false; - _isJumping = false; - _isPreparingToClimbUp = false; - _isPreparedToClimbUp = false; - _isClimbingUp = false; - _isClimbingHorizontally = false; - _isClimbingLeft = false; - _isClimbingRight = false; - IsScrapingCliff = false; - _isCliffHanging = false; - } - - private void Animations() - { - var isFalling = IsFalling(); - var isInMotion = IsInMotion(); - - // TODO Use a running animation - // TODO _isRunning should not be true when moving horizontally through air; these are 2 different states - // TODO Falling from a jump should be a different state than falling from a cliff. - // @formatter:off - if (_isRunning && !isFalling) _sprite.Animation = IdleLeftAnimation; - else if (_isPreparingToClimbUp || _isPreparedToClimbUp) _sprite.Animation = PreparingToClimbUpAnimation; - else if (IsScrapingCliff) _sprite.Animation = ScrapingAnimation; - else if (_isCliffHanging) _sprite.Animation = CliffHangingAnimation; - else if (_isClimbingHorizontally) _sprite.Animation = ClimbingHorizontallyAnimation; - else if (_isClimbingUp) _sprite.Animation = ClimbingUpAnimation; - else if (isFalling && !_isJumping && IsItemKeyPressed() && IsFullyIntersectingCliffs) _sprite.Animation = ScrapingAnimation; - else if (isFalling && !_isJumping) _sprite.Animation = FallingAnimation; - else if (!isInMotion) _sprite.Animation = IdleLeftAnimation; - // @formatter:on - } - private void CalculateFallingStats() { if (_velocity.y > _highestVerticalVelocity) _highestVerticalVelocity = _velocity.y; - if (IsFalling() && _fallingStartTimeMs == 0) + if (IsMovingDown() && _fallingStartTimeMs == 0) { _fallingStartTimeMs = OS.GetTicksMsec(); } - else if (IsFalling()) + else if (IsMovingDown()) { _elapsedFallingTimeMs = OS.GetTicksMsec() - _fallingStartTimeMs; } - else if (!IsFalling() && _elapsedFallingTimeMs > 0) + else if (!IsMovingDown() && _elapsedFallingTimeMs > 0) { _lastTotalFallingTimeMs = OS.GetTicksMsec() - _fallingStartTimeMs; _fallingStartTimeMs = 0; @@ -616,10 +339,7 @@ private void CalculateFallingStats() } } - private void PrintLine (string line) - { - _printLines.Add (line); - } + private void PrintLine (string line) => _printLines.Add (line); private void Print() { @@ -628,4 +348,117 @@ private void Print() foreach (var line in _printLines) _label.AddText (line + "\n"); _printLines.Clear(); } + + private float GetFallingTimeSeconds() => + _elapsedFallingTimeMs > 0 ? _elapsedFallingTimeMs / 1000.0f : _lastTotalFallingTimeMs / 1000.0f; + + // @formatter:off + private string DumpState() => + "\nIdle: " + _stateMachine.Is (State.Idle) + + "\nRunning: " + _stateMachine.Is (State.Running) + + "\nJumping: " + _stateMachine.Is (State.Jumping) + + "\nClimbing prep: " + _stateMachine.Is (State.ClimbingPrep) + + "\nClimbing prep timer: " + _climbingPrepTimer.TimeLeft + + "\nClimbing up: " + _stateMachine.Is (State.ClimbingUp) + + "\nTouching cliff ice: " + IsTouchingCliffIce + + "\nScraping cliff: " + _stateMachine.Is (State.Scraping) + + "\nCliff hanging: " + _stateMachine.Is (State.CliffHanging) + + "\nClimbing Horizontally: " + _stateMachine.Is (State.Traversing) + + "\nFree falling: " + _stateMachine.Is (State.FreeFalling) + + "\nIsOnFloor(): " + IsOnFloor() + + "\nIsFullyIntersectingCliffs: " + IsInCliffs + + "\nIsInMotion(): " + IsMoving() + + "\nIsMovingDown(): " + IsMovingDown() + + "\nIsMovingUp(): " + IsMovingUp() + + "\nIsInMotionHorizontally(): " + IsMovingHorizontally() + + "\nIsInMotionVertically(): " + IsMovingVertically() + + "\nIsRightArrowPressed(): " + IsRightArrowPressed() + + "\nIsLeftArrowPressed(): " + IsLeftArrowPressed() + + "\nIsUpArrowPressed(): " + IsUpArrowPressed() + + "\nIsDownArrowPressed(): " + IsDownArrowPressed() + + "\nIsAnyHorizontalArrowPressed(): " + IsAnyHorizontalArrowPressed() + + "\nIsAnyVerticalArrowPressed(): " + IsAnyVerticalArrowPressed() + + "\n_velocity: " + _velocity + + "\nVertical velocity (mph): " + _velocity.y * 0.028334573333333 + + "\nHighest vertical velocity: " + _highestVerticalVelocity + + "\nHighest vertical velocity (mph): " + _highestVerticalVelocity * 0.028334573333333 + + "\nFalling time (sec): " + GetFallingTimeSeconds(); + // @formatter:on + + private void InitializeStateMachine() + { + _stateMachine = new StateMachine (TransitionTable, InitialState); + _stateMachine.OnTransitionTo (State.Idle, () => _sprite.Animation = IdleLeftAnimation); + _stateMachine.OnTransitionTo (State.Running, () => _sprite.Animation = RunAnimation); + _stateMachine.OnTransitionTo (State.CliffHanging, () => _sprite.Animation = CliffHangingAnimation); + _stateMachine.OnTransitionTo (State.FreeFalling, () => _sprite.Animation = FreeFallingAnimation); + _stateMachine.OnTransitionFrom (State.ClimbingPrep, () => _climbingPrepTimer.Stop()); + _stateMachine.OnTransition (State.ClimbingPrep, State.Idle, () => FlipHorizontally (_wasFlippedHorizontally)); + _stateMachine.OnTransitionTo (State.Traversing, () => _sprite.Animation = TraversingAnimation); + + _stateMachine.OnTransitionTo (State.Jumping, () => + { + _sprite.Animation = IdleLeftAnimation; + _velocity.y -= JumpPower; + }); + + _stateMachine.OnTransitionTo (State.ClimbingUp, () => + { + _sprite.Animation = ClimbingUpAnimation; + FlipHorizontally (false); + }); + + _stateMachine.OnTransitionFrom (State.Scraping, () => + { + if (!_audio.Playing) return; + + _audio.Stop(); + PrintLine ("Sound effects: Stopped cliff scraping sound."); + }); + + _stateMachine.OnTransitionTo (State.Scraping, () => + { + _sprite.Animation = ScrapingAnimation; + + if (_audio.Playing) return; + + _audio.Play(); + PrintLine ("Sound effects: Playing cliff scraping sound."); + }); + + _stateMachine.OnTransitionTo (State.ClimbingPrep, () => + { + _sprite.Animation = ClimbingPrepAnimation; + _wasFlippedHorizontally = _isFlippedHorizontally; + FlipHorizontally (false); + _climbingPrepTimer.Start(); + }); + + // TODO Move conditions into state machine conditions, leaving only input for triggers. + // @formatter:off + _stateMachine.AddTrigger (State.Idle, State.Running, () => IsOneActiveOf (Input.Horizontal)); + _stateMachine.AddTrigger (State.Idle, State.Jumping, WasJumpKeyPressed); + _stateMachine.AddTrigger (State.Idle, State.ClimbingPrep, () => IsUpArrowPressed() && IsInCliffs); + _stateMachine.AddTrigger (State.Idle, State.FreeFalling, () => IsMovingDown() && !IsOnFloor()); + _stateMachine.AddTrigger (State.Running, State.Idle, () => !IsOneActiveOf (Input.Horizontal) && ! IsMovingHorizontally()); + _stateMachine.AddTrigger (State.Running, State.Jumping, WasJumpKeyPressed); + _stateMachine.AddTrigger (State.Running, State.FreeFalling, () => IsMovingDown() && !IsOnFloor()); + _stateMachine.AddTrigger (State.Running, State.ClimbingPrep, () => IsUpArrowPressed() && IsInCliffs); + _stateMachine.AddTrigger (State.Jumping, State.FreeFalling, () => IsMovingDown() && !IsOnFloor()); + _stateMachine.AddTrigger (State.ClimbingPrep, State.Idle, WasUpArrowReleased); + _stateMachine.AddTrigger (State.ClimbingPrep, State.ClimbingUp, () => IsUpArrowPressed() && _climbingPrepTimer.TimeLeft == 0); + _stateMachine.AddTrigger (State.ClimbingUp, State.FreeFalling, () => WasUpArrowReleased() || !IsInCliffs || IsTouchingCliffIce); + _stateMachine.AddTrigger (State.FreeFalling, State.Idle, () => !IsOneActiveOf (Input.Horizontal) && IsOnFloor() && !IsMovingHorizontally()); + _stateMachine.AddTrigger (State.FreeFalling, State.Running, () => IsOneActiveOf (Input.Horizontal) && IsOnFloor()); + _stateMachine.AddTrigger (State.FreeFalling, State.Scraping, () => IsItemKeyPressed() && IsInCliffs && _velocity.y >= CliffScrapingActivationVelocity); + _stateMachine.AddTrigger (State.CliffHanging, State.ClimbingUp, IsUpArrowPressed); + _stateMachine.AddTrigger (State.CliffHanging, State.FreeFalling, WasDownArrowPressedOnce); + _stateMachine.AddTrigger (State.CliffHanging, State.Traversing, () => IsOneActiveOf (Input.Horizontal)); + _stateMachine.AddTrigger (State.Traversing, State.FreeFalling, () => WasDownArrowPressedOnce() || !IsInCliffs || IsTouchingCliffIce); + _stateMachine.AddTrigger (State.Traversing, State.CliffHanging, () => !IsOneActiveOf (Input.Horizontal) && !IsMovingHorizontally()); + _stateMachine.AddTrigger (State.Scraping, State.FreeFalling, WasItemKeyReleased); + _stateMachine.AddTrigger (State.Scraping, State.CliffHanging, () => !IsOnFloor() && !IsMoving() && IsInCliffs); + _stateMachine.AddTrigger (State.Scraping, State.Idle, IsOnFloor); + // @formatter:on + } } \ No newline at end of file diff --git a/StateMachine.cs b/StateMachine.cs new file mode 100644 index 0000000..ef83d8c --- /dev/null +++ b/StateMachine.cs @@ -0,0 +1,256 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +// TODO Implement State Conditions for entering / exiting +// TODO Implement per-frame delegate actions that repeat while in a specific state. +// TODO Automatically transition states if exit condition is true and only one transition has true enter condition +// TODO if (MustTransitionFrom (State.CliffHanging) && +// TODO AllTransitionsInvalidExcept (State.FreeFalling)) // then transition to FreeFall +// 1. Child states can be pushed and popped from a parent state. +// 2. Child state can transition to new parent state. +// 3. Parent states cannot be pushed / popped. +// 3. Any child states are lost when changing to new parent state. +// 4. Initial state is a parent state. +public class StateMachine : IStateMachine where T : struct, Enum +{ + private T _currentState; + private T _parentState; + private readonly T _initialState; + private static readonly T AnyState = (T) (object) -1; + private readonly Dictionary > _transitionTable; + private readonly Stack _childStates = new(); + private readonly Dictionary .TransitionAction>> _actions = new(); + private readonly Dictionary .TransitionTrigger>> _triggers = new(); + + public StateMachine (Dictionary transitionTable, T initialState) : this ( + transitionTable.ToDictionary (kvp => kvp.Key, kvp => kvp.Value.ToHashSet()), initialState) + { + } + + private StateMachine (Dictionary > transitionTable, T initialState) + { + if (transitionTable.ContainsKey (AnyState) || transitionTable.Values.Any (x => x.Any (y => Equals (y, AnyState)))) + { + throw new ArgumentException ($"{nameof (transitionTable)} cannot contain wildcard {ToString (AnyState)}"); + } + + _initialState = initialState; + _currentState = initialState; + _parentState = initialState; + _transitionTable = transitionTable; + } + + public bool Is (T state) => Equals (_currentState, state); + public void OnTransitionTo (T to, IStateMachine .TransitionAction action) => OnTransition (AnyState, to, action); + public void OnTransitionFrom (T from, IStateMachine .TransitionAction action) => OnTransition (from, AnyState, action); + + public void OnTransition (T from, T to, IStateMachine .TransitionAction action) + { + if (!HasWildcards (from, to) && !CanTransition (from, to)) + { + Log.Warn ($"Ignoring invalid transition from {ToString (from)} to {ToString (to)}."); + + return; + } + + if (!_actions.ContainsKey (from)) _actions.Add (from, new Dictionary .TransitionAction>()); + + if (_actions[from].ContainsKey (to)) + { + throw new ArgumentException ($"Transition action from {ToString (from)} to {ToString (to)} already exists."); + } + + _actions[from].Add (to, action); + } + + public void AddTrigger (T from, T to, IStateMachine .TransitionTrigger trigger) + { + if (HasWildcards (to)) + { + throw new ArgumentException ( + $"Trigger from {ToString (from)} to {ToString (to)} cannot contain a destination wildcard {ToString (AnyState)}."); + } + + if (!HasWildcards (from, to) && !CanTransition (from, to)) + { + Log.Warn ($"Ignoring invalid trigger from {ToString (from)} to {ToString (to)}."); + + return; + } + + if (!_triggers.ContainsKey (from)) _triggers.Add (from, new Dictionary .TransitionTrigger>()); + + if (_triggers[from].ContainsKey (to)) + { + throw new ArgumentException ($"Trigger from {ToString (from)} to {ToString (to)} already exists."); + } + + _triggers[from].Add (to, trigger); + } + + public void Update() + { + if (!_triggers.ContainsKey (_currentState)) return; + + var triggeredStates = _triggers[_currentState].Keys.Where (to => _triggers[_currentState][to]()).ToList(); + + switch (triggeredStates.Count) + { + case 0: + break; + case 1: + var to = triggeredStates.Single(); + + if (CanPopTo (to)) + { + Pop(); + } + else if (IsReversible (to)) + { + Push (to); + } + else + { + To (to); + } + + break; + default: + Log.Warn ( + $"Ignoring multiple valid triggers from {ToString (_currentState)} to {Tools.ToString (triggeredStates, f: ToString)}."); + + break; + } + } + + public void To (T to) + { + if (!ShouldExecuteChangeState (to)) return; + + ExecuteChangeState (to); + _childStates.Clear(); + _parentState = to; + } + + public void ToIf (T to, bool condition) + { + if (condition) To (to); + } + + public void Push (T to) + { + if (!ShouldExecuteChangeState (to)) return; + + if (!IsReversible (to)) + { + Log.Warn ($"Cannot push {ToString (to)}: Would not be able to change back to {ToString (_currentState)} " + + $"from {ToString (to)}.\nUse {nameof (IStateMachine )}#{nameof (To)} for a one-way transition."); + + return; + } + + if (!IsCurrentChild (to)) _childStates.Push (to); + Log.Debug ($"Pushed: {ToString (to)}"); + Log.All (PrintStates()); + ExecuteChangeState (to); + } + + public void PushIf (T to, bool condition) + { + if (condition) Push (to); + } + + public void Pop() + { + if (!CanPop()) + { + Log.Warn ($"Cannot pop {ToString (_currentState)}: wasn't pushed.\nUse {nameof (IStateMachine )}#{nameof (To)} instead."); + + return; + } + + var to = DoublePeek(); + + if (!ShouldExecuteChangeState (to)) return; + + _childStates.Pop(); + Log.Debug ($"Popped: {ToString (_currentState)}"); + Log.Debug (PrintStates()); + ExecuteChangeState (to); + } + + private T DoublePeek() + { + if (_childStates.Count == 0) + { + throw new InvalidOperationException ($"Cannot double peek: current state {ToString (_currentState)} wasn't pushed."); + } + + return _childStates.Count > 1 ? _childStates.Skip (1).First() : _parentState; + } + + public void Reset() + { + _currentState = _initialState; + _parentState = _initialState; + _childStates.Clear(); + } + + public void PopIf (bool condition) + { + if (condition) Pop(); + } + + private bool IsReversible (T state) => CanTransition (state, _currentState); + public void PopIf (T state, bool condition) => PopIf (Is (state) && condition); + public void PopIf (T state) => PopIf (Is (state)); + private bool CanPopTo (T to) => CanPop() && Equals (DoublePeek(), to); + private bool CanPop() => IsCurrentChild (_currentState); + private bool IsCurrentChild (T state) => _childStates.Count > 0 && Equals (state, _childStates.Peek()); + private bool CanTransitionTo (T to) => CanTransition (_currentState, to); + private bool CanTransition (T from, T to) { return _transitionTable.TryGetValue (from, out var toStates) && toStates.Contains (to); } + private bool HasTransitionAction (T from, T to) { return _actions.TryGetValue (from, out var actions) && actions.ContainsKey (to); } + private static bool HasWildcards (params T[] states) => states.Any (state => Equals (state, AnyState)); + + private bool ShouldExecuteChangeState (T to) + { + if (Equals (to, _currentState)) + { + Log.All ($"Ignoring duplicate {ToString (to)} state transition."); + + return false; + } + + // ReSharper disable once InvertIf + if (!CanTransitionTo (to)) + { + Log.Warn ($"Ignoring invalid transition from {ToString (_currentState)} to {ToString (to)}."); + + return false; + } + + return true; + } + + private void ExecuteChangeState (T to) + { + ExecuteTransitionActionsTo (to); + Log.Info ($"State transition: {ToString (_currentState)} => {ToString (to)}"); + _currentState = to; + } + + private void ExecuteTransitionActionsTo (T to) + { + foreach (var fromState in new[] { _currentState, AnyState }) + { + foreach (var toState in new[] { to, AnyState }) + { + if (HasTransitionAction (fromState, toState)) _actions[fromState][toState](); + } + } + } + + private string PrintStates() => $"States:\nChildren:\n{Tools.ToString (_childStates, "\n")}\nParent:\n{_parentState}"; + private static string ToString (T t) => Equals (t, AnyState) ? nameof (AnyState) : t.ToString(); +} \ No newline at end of file diff --git a/TileSet.cs b/TileSet.cs index 76465c4..4753ce1 100644 --- a/TileSet.cs +++ b/TileSet.cs @@ -11,7 +11,7 @@ private enum Tile CliffGem = 1 } - private readonly Dictionary _binds = new Dictionary () + private readonly Dictionary _binds = new() { { Tile.Cliff, new[] { Tile.CliffGem } }, { Tile.CliffGem, new[] { Tile.Cliff } } diff --git a/Tools.cs b/Tools.cs index 9d476b6..7fde503 100644 --- a/Tools.cs +++ b/Tools.cs @@ -1,116 +1,72 @@ +using System; using System.Collections.Generic; using System.Linq; using Godot; +// ReSharper disable MemberCanBePrivate.Global public static class Tools { - public enum InputType + public enum Input { Horizontal, Vertical, Item, + Text, + Respawn, Up, Down, Left, - Right - } - - private static readonly Dictionary InputTypes = new Dictionary () - { - { InputType.Horizontal, new[] { "move_left", "move_right" } }, - { InputType.Vertical, new[] { "move_up", "move_down" } }, - { InputType.Item, new[] { "use_item" } }, - { InputType.Up, new[] { "move_up" } }, - { InputType.Down, new[] { "move_down" } }, - { InputType.Left, new[] { "move_left" } }, - { InputType.Right, new[] { "move_right" } } + Right, + Jump + } + + private static readonly Dictionary Inputs = new() + { + { Input.Horizontal, new[] { "move_left", "move_right" } }, + { Input.Vertical, new[] { "move_up", "move_down" } }, + { Input.Item, new[] { "use_item" } }, + { Input.Text, new[] { "show_text" } }, + { Input.Respawn, new[] { "respawn" } }, + { Input.Up, new[] { "move_up" } }, + { Input.Down, new[] { "move_down" } }, + { Input.Left, new[] { "move_left" } }, + { Input.Right, new[] { "move_right" } }, + { Input.Jump, new[] { "jump" } } }; - public static bool IsLeftArrowPressed() - { - return Input.IsActionPressed (InputTypes [InputType.Left] [0]); - } - - public static bool WasLeftArrowPressedOnce() - { - return Input.IsActionJustPressed (InputTypes [InputType.Left] [0]); - } - - public static bool IsRightArrowPressed() - { - return Input.IsActionPressed (InputTypes [InputType.Right] [0]); - } - - public static bool WasRightArrowPressedOnce() - { - return Input.IsActionJustPressed (InputTypes [InputType.Right] [0]); - } - - public static bool IsUpArrowPressed() - { - return Input.IsActionPressed (InputTypes [InputType.Up] [0]); - } - - public static bool WasUpArrowPressedOnce() - { - return Input.IsActionJustPressed (InputTypes [InputType.Up] [0]); - } - - public static bool IsDownArrowPressed() - { - return Input.IsActionPressed (InputTypes [InputType.Down] [0]); - } - - public static bool WasDownArrowPressedOnce() - { - return Input.IsActionJustPressed (InputTypes [InputType.Down] [0]); - } - - public static bool IsAnyHorizontalArrowPressed() - { - return IsLeftArrowPressed() || IsRightArrowPressed(); - } - - public static bool IsAnyVerticalArrowPressed() - { - return IsUpArrowPressed() || IsDownArrowPressed(); - } - - public static bool IsItemKeyPressed() - { - return Input.IsActionPressed (InputTypes [InputType.Item] [0]); - } - - public static float SafelyClampMin (float value, float min) - { - return IsSafelyLessThan (value, min) ? min : value; - } - - public static float SafelyClampMax (float value, float max) - { - return IsSafelyGreaterThan (value, max) ? max : value; - } - - public static float SafelyClamp (float value, float min, float max) - { - return SafelyClampMin (SafelyClampMax (value, max), min); - } - - public static bool IsSafelyLessThan (float f1, float f2) - { - return f1 < f2 && !Mathf.IsEqualApprox (f1, f2); - } - - public static bool IsSafelyGreaterThan (float f1, float f2) - { - return f1 > f2 && !Mathf.IsEqualApprox (f1, f2); - } + public static bool IsReleased (Input i, InputEvent e) => e is InputEventKey k && Inputs[i].Any (x => k.IsActionReleased (x)); + public static bool IsOneActiveOf (Input i) => Inputs[i].Where (Godot.Input.IsActionPressed).Take (2).Count() == 1; + public static bool IsAnyActiveOf (Input i) => Inputs[i].Any (Godot.Input.IsActionPressed); + public static bool IsLeftArrowPressed() => Godot.Input.IsActionPressed (Inputs[Input.Left][0]); + public static bool WasLeftArrowPressedOnce() => Godot.Input.IsActionJustPressed (Inputs[Input.Left][0]); + public static bool IsRightArrowPressed() => Godot.Input.IsActionPressed (Inputs[Input.Right][0]); + public static bool WasRightArrowPressedOnce() => Godot.Input.IsActionJustPressed (Inputs[Input.Right][0]); + public static bool IsUpArrowPressed() => Godot.Input.IsActionPressed (Inputs[Input.Up][0]); + public static bool WasUpArrowReleased() => Godot.Input.IsActionJustReleased (Inputs[Input.Up][0]); + public static bool WasUpArrowPressedOnce() => Godot.Input.IsActionJustPressed (Inputs[Input.Up][0]); + public static bool IsDownArrowPressed() => Godot.Input.IsActionPressed (Inputs[Input.Down][0]); + public static bool WasDownArrowPressedOnce() => Godot.Input.IsActionJustPressed (Inputs[Input.Down][0]); + public static bool IsAnyHorizontalArrowPressed() => IsLeftArrowPressed() || IsRightArrowPressed(); + public static bool IsEveryHorizontalArrowPressed() => IsLeftArrowPressed() && IsRightArrowPressed(); + public static bool IsAnyVerticalArrowPressed() => IsUpArrowPressed() || IsDownArrowPressed(); + public static bool IsEveryVerticalArrowPressed() => IsUpArrowPressed() && IsDownArrowPressed(); + public static bool IsAnyArrowKeyPressed() => IsAnyHorizontalArrowPressed() || IsAnyVerticalArrowPressed(); + public static bool IsItemKeyPressed() => Godot.Input.IsActionPressed (Inputs[Input.Item][0]); + public static bool WasItemKeyReleased() => Godot.Input.IsActionJustReleased (Inputs[Input.Item][0]); + public static bool WasJumpKeyPressed() => Godot.Input.IsActionJustPressed (Inputs[Input.Jump][0]); + public static bool WasJumpKeyReleased() => Godot.Input.IsActionJustReleased (Inputs[Input.Jump][0]); + public static float SafelyClampMin (float f, float min) => IsSafelyLessThan (f, min) ? min : f; + public static float SafelyClampMax (float f, float max) => IsSafelyGreaterThan (f, max) ? max : f; + public static float SafelyClamp (float f, float min, float max) => SafelyClampMin (SafelyClampMax (f, max), min); + public static bool IsSafelyLessThan (float f1, float f2) => f1 < f2 && !Mathf.IsEqualApprox (f1, f2); + public static bool IsSafelyGreaterThan (float f1, float f2) => f1 > f2 && !Mathf.IsEqualApprox (f1, f2); + public static void LoopAudio (AudioStream a) { LoopAudio (a, 0.0f, a.GetLength()); } /// /// Whether or not the specified input type is the only active input. /// - /// - /// if true, bypass exclusivity requirement for active input + /// + /// if true, bypass exclusivity requirement for active input ///
/// Useful when the desired action from the specified input is already being executed, /// since ignoring exclusivity prevents said action from being canceled when @@ -131,17 +87,18 @@ public static bool IsSafelyGreaterThan (float f1, float f2) /// would be disabled while running because the right arrow key is already being pressed. /// /// - public static bool IsExclusivelyActiveUnless (InputType inputType, bool ignoreExclusions) + public static bool IsExclusivelyActiveUnless (Input input, bool disableExclusivity) { var activeInclusions = new List (); var activeExclusions = new List (); - foreach (var dict in InputTypes) + foreach (var (key, values) in Inputs) { - foreach (var value in dict.Value) + foreach (var value in values) { - var isActionPressed = Input.IsActionPressed (value); - if (dict.Key == inputType && isActionPressed) activeInclusions.Add (value); + var isActionPressed = Godot.Input.IsActionPressed (value); + + if (key == input && isActionPressed) activeInclusions.Add (value); else if (isActionPressed) activeExclusions.Add (value); } } @@ -150,12 +107,7 @@ public static bool IsExclusivelyActiveUnless (InputType inputType, bool ignoreEx // E.g., InputType.Vertical & InputType.Up both contain "move_up", so if the specified input type is // Vertical and "move_up" is an active inclusion, then InputType.Up ["move_up"] will not count as an // active exclusion, since it isn't unique (even though it is active). - return activeInclusions.Any() && (ignoreExclusions || !activeExclusions.Except (activeInclusions).Any()); - } - - public static void LoopAudio (AudioStream stream) - { - LoopAudio (stream, 0.0f, stream.GetLength()); + return activeInclusions.Any() && (disableExclusivity || !activeExclusions.Except (activeInclusions).Any()); } public static void LoopAudio (AudioStream stream, float loopBeginSeconds, float loopEndSecondsWavOnly) @@ -166,6 +118,7 @@ public static void LoopAudio (AudioStream stream, float loopBeginSeconds, float { ogg.Loop = true; ogg.LoopOffset = loopBeginSeconds; + break; } case AudioStreamSample wav: @@ -173,8 +126,17 @@ public static void LoopAudio (AudioStream stream, float loopBeginSeconds, float wav.LoopMode = AudioStreamSample.LoopModeEnum.Forward; wav.LoopBegin = Mathf.RoundToInt (loopBeginSeconds * wav.MixRate); wav.LoopEnd = Mathf.RoundToInt (loopEndSecondsWavOnly * wav.MixRate); + break; } } } + + public static string ToString (IEnumerable e, string sep = ", ", Func f = null) => + e.Select (f ?? (s => s.ToString())).DefaultIfEmpty (string.Empty).Aggregate ((a, b) => a + sep + b); + + public static bool IsIntersectingAnyTile (Vector2 globalPosition, TileMap tileMap) + { + return tileMap.GetCellv (tileMap.WorldToMap (tileMap.ToLocal (globalPosition))) != -1; + } } \ No newline at end of file diff --git a/cliffs.png b/cliffs.png index 504d11f..d9f9f81 100644 Binary files a/cliffs.png and b/cliffs.png differ diff --git a/cliffs.tres b/cliffs.tres index d3664e1..b1937ad 100644 --- a/cliffs.tres +++ b/cliffs.tres @@ -68,3 +68,170 @@ 24/shape_one_way_margin = 0.0 24/shapes = [ ] 24/z_index = 1 +25/name = "cliffs.png 25" +25/texture = ExtResource( 1 ) +25/tex_offset = Vector2( 0, 0 ) +25/modulate = Color( 1, 1, 1, 1 ) +25/region = Rect2( 72, 25, 0, 0 ) +25/tile_mode = 1 +25/autotile/bitmask_mode = 0 +25/autotile/bitmask_flags = [ ] +25/autotile/icon_coordinate = Vector2( 0, 0 ) +25/autotile/tile_size = Vector2( 32, 32 ) +25/autotile/spacing = 0 +25/autotile/occluder_map = [ ] +25/autotile/navpoly_map = [ ] +25/autotile/priority_map = [ ] +25/autotile/z_index_map = [ ] +25/occluder_offset = Vector2( 0, 0 ) +25/navigation_offset = Vector2( 0, 0 ) +25/shape_offset = Vector2( 0, 0 ) +25/shape_transform = Transform2D( 1, 0, 0, 1, 0, 0 ) +25/shape_one_way = false +25/shape_one_way_margin = 0.0 +25/shapes = [ ] +25/z_index = 0 +26/name = "cliffs.png 26" +26/texture = ExtResource( 1 ) +26/tex_offset = Vector2( 0, 0 ) +26/modulate = Color( 1, 1, 1, 1 ) +26/region = Rect2( 73, 24, 0, 0 ) +26/tile_mode = 1 +26/autotile/bitmask_mode = 0 +26/autotile/bitmask_flags = [ ] +26/autotile/icon_coordinate = Vector2( 0, 0 ) +26/autotile/tile_size = Vector2( 32, 32 ) +26/autotile/spacing = 0 +26/autotile/occluder_map = [ ] +26/autotile/navpoly_map = [ ] +26/autotile/priority_map = [ ] +26/autotile/z_index_map = [ ] +26/occluder_offset = Vector2( 0, 0 ) +26/navigation_offset = Vector2( 0, 0 ) +26/shape_offset = Vector2( 0, 0 ) +26/shape_transform = Transform2D( 1, 0, 0, 1, 0, 0 ) +26/shape_one_way = false +26/shape_one_way_margin = 0.0 +26/shapes = [ ] +26/z_index = 0 +27/name = "cliff-snow-top-left" +27/texture = ExtResource( 1 ) +27/tex_offset = Vector2( 0, 0 ) +27/modulate = Color( 1, 1, 1, 1 ) +27/region = Rect2( 48, 0, 16, 16 ) +27/tile_mode = 0 +27/occluder_offset = Vector2( 0, 0 ) +27/navigation_offset = Vector2( 0, 0 ) +27/shape_offset = Vector2( 0, 0 ) +27/shape_transform = Transform2D( 1, 0, 0, 1, 0, 0 ) +27/shape_one_way = false +27/shape_one_way_margin = 0.0 +27/shapes = [ ] +27/z_index = 0 +28/name = "cliff-snow-top-middle" +28/texture = ExtResource( 1 ) +28/tex_offset = Vector2( 0, 0 ) +28/modulate = Color( 1, 1, 1, 1 ) +28/region = Rect2( 64, 0, 16, 16 ) +28/tile_mode = 0 +28/occluder_offset = Vector2( 0, 0 ) +28/navigation_offset = Vector2( 0, 0 ) +28/shape_offset = Vector2( 0, 0 ) +28/shape_transform = Transform2D( 1, 0, 0, 1, 0, 0 ) +28/shape_one_way = false +28/shape_one_way_margin = 0.0 +28/shapes = [ ] +28/z_index = 0 +29/name = "cliff-snow-top-right" +29/texture = ExtResource( 1 ) +29/tex_offset = Vector2( 0, 0 ) +29/modulate = Color( 1, 1, 1, 1 ) +29/region = Rect2( 80, 0, 16, 16 ) +29/tile_mode = 0 +29/occluder_offset = Vector2( 0, 0 ) +29/navigation_offset = Vector2( 0, 0 ) +29/shape_offset = Vector2( 0, 0 ) +29/shape_transform = Transform2D( 1, 0, 0, 1, 0, 0 ) +29/shape_one_way = false +29/shape_one_way_margin = 0.0 +29/shapes = [ ] +29/z_index = 0 +30/name = "cliff-grass-top-right" +30/texture = ExtResource( 1 ) +30/tex_offset = Vector2( 0, 0 ) +30/modulate = Color( 1, 1, 1, 1 ) +30/region = Rect2( 32, 0, 16, 16 ) +30/tile_mode = 0 +30/occluder_offset = Vector2( 0, 0 ) +30/navigation_offset = Vector2( 0, 0 ) +30/shape_offset = Vector2( 0, 0 ) +30/shape_transform = Transform2D( 1, 0, 0, 1, 0, 0 ) +30/shape_one_way = false +30/shape_one_way_margin = 0.0 +30/shapes = [ ] +30/z_index = 0 +31/name = "cliff-ice-auto-tile" +31/texture = ExtResource( 1 ) +31/tex_offset = Vector2( 0, 0 ) +31/modulate = Color( 1, 1, 1, 1 ) +31/region = Rect2( 48, 16, 48, 48 ) +31/tile_mode = 1 +31/autotile/bitmask_mode = 0 +31/autotile/bitmask_flags = [ Vector2( 0, 0 ), 256, Vector2( 0, 1 ), 260, Vector2( 0, 2 ), 4, Vector2( 1, 0 ), 320, Vector2( 1, 1 ), 325, Vector2( 1, 2 ), 5, Vector2( 2, 0 ), 64, Vector2( 2, 1 ), 65, Vector2( 2, 2 ), 1 ] +31/autotile/icon_coordinate = Vector2( 0, 0 ) +31/autotile/tile_size = Vector2( 16, 16 ) +31/autotile/spacing = 0 +31/autotile/occluder_map = [ ] +31/autotile/navpoly_map = [ ] +31/autotile/priority_map = [ ] +31/autotile/z_index_map = [ ] +31/occluder_offset = Vector2( 0, 0 ) +31/navigation_offset = Vector2( 0, 0 ) +31/shape_offset = Vector2( 0, 0 ) +31/shape_transform = Transform2D( 1, 0, 0, 1, 0, 0 ) +31/shape_one_way = false +31/shape_one_way_margin = 0.0 +31/shapes = [ ] +31/z_index = 0 +32/name = "cliff-ice-left" +32/texture = ExtResource( 1 ) +32/tex_offset = Vector2( 0, 0 ) +32/modulate = Color( 1, 1, 1, 1 ) +32/region = Rect2( 48, 16, 16, 16 ) +32/tile_mode = 0 +32/occluder_offset = Vector2( 0, 0 ) +32/navigation_offset = Vector2( 0, 0 ) +32/shape_offset = Vector2( 0, 0 ) +32/shape_transform = Transform2D( 1, 0, 0, 1, 0, 0 ) +32/shape_one_way = false +32/shape_one_way_margin = 0.0 +32/shapes = [ ] +32/z_index = 0 +33/name = "cliff-ice-middle" +33/texture = ExtResource( 1 ) +33/tex_offset = Vector2( 0, 0 ) +33/modulate = Color( 1, 1, 1, 1 ) +33/region = Rect2( 64, 16, 16, 16 ) +33/tile_mode = 0 +33/occluder_offset = Vector2( 0, 0 ) +33/navigation_offset = Vector2( 0, 0 ) +33/shape_offset = Vector2( 0, 0 ) +33/shape_transform = Transform2D( 1, 0, 0, 1, 0, 0 ) +33/shape_one_way = false +33/shape_one_way_margin = 0.0 +33/shapes = [ ] +33/z_index = 0 +34/name = "cliff-ice-right" +34/texture = ExtResource( 1 ) +34/tex_offset = Vector2( 0, 0 ) +34/modulate = Color( 1, 1, 1, 1 ) +34/region = Rect2( 80, 16, 16, 16 ) +34/tile_mode = 0 +34/occluder_offset = Vector2( 0, 0 ) +34/navigation_offset = Vector2( 0, 0 ) +34/shape_offset = Vector2( 0, 0 ) +34/shape_transform = Transform2D( 1, 0, 0, 1, 0, 0 ) +34/shape_one_way = false +34/shape_one_way_margin = 0.0 +34/shapes = [ ] +34/z_index = 0 diff --git a/coa.sln b/coa.sln index 15a3601..e63f857 100644 --- a/coa.sln +++ b/coa.sln @@ -1,12 +1,12 @@ -Microsoft Visual Studio Solution File, Format Version 12.00 +Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 2012 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "coa", "coa.csproj", "{4DE98C13-5BB6-40F7-8979-2AE66A4C0B30}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - ExportDebug|Any CPU = ExportDebug|Any CPU - ExportRelease|Any CPU = ExportRelease|Any CPU + Debug|Any CPU = Debug|Any CPU + ExportDebug|Any CPU = ExportDebug|Any CPU + ExportRelease|Any CPU = ExportRelease|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {4DE98C13-5BB6-40F7-8979-2AE66A4C0B30}.Debug|Any CPU.ActiveCfg = Debug|Any CPU diff --git a/klas.png b/klas.png index adecf95..f83f643 100644 Binary files a/klas.png and b/klas.png differ diff --git a/main.tscn b/main.tscn index 956f929..95f5afb 100644 --- a/main.tscn +++ b/main.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=64 format=2] +[gd_scene load_steps=68 format=2] [ext_resource path="res://Player.cs" type="Script" id=1] [ext_resource path="res://waterfall2.png" type="Texture" id=2] @@ -8,255 +8,268 @@ [ext_resource path="res://ambience_summer.wav" type="AudioStream" id=6] [ext_resource path="res://klas.png" type="Texture" id=7] [ext_resource path="res://waterfall.wav" type="AudioStream" id=8] +[ext_resource path="res://music2_trimmed.wav" type="AudioStream" id=9] [sub_resource type="AtlasTexture" id=1] atlas = ExtResource( 7 ) -region = Rect2( 0, 64, 16, 16 ) +region = Rect2( 0, 0, 16, 16 ) [sub_resource type="AtlasTexture" id=2] atlas = ExtResource( 7 ) -region = Rect2( 0, 48, 16, 16 ) +region = Rect2( 16, 0, 16, 16 ) [sub_resource type="AtlasTexture" id=3] atlas = ExtResource( 7 ) -region = Rect2( 16, 48, 16, 16 ) +region = Rect2( 32, 0, 16, 16 ) [sub_resource type="AtlasTexture" id=4] atlas = ExtResource( 7 ) -region = Rect2( 0, 0, 16, 16 ) +region = Rect2( 48, 0, 16, 16 ) [sub_resource type="AtlasTexture" id=5] atlas = ExtResource( 7 ) -region = Rect2( 16, 0, 16, 16 ) +region = Rect2( 64, 0, 16, 16 ) [sub_resource type="AtlasTexture" id=6] atlas = ExtResource( 7 ) -region = Rect2( 32, 0, 16, 16 ) +region = Rect2( 0, 48, 16, 16 ) [sub_resource type="AtlasTexture" id=7] atlas = ExtResource( 7 ) -region = Rect2( 48, 0, 16, 16 ) +region = Rect2( 16, 48, 16, 16 ) [sub_resource type="AtlasTexture" id=8] atlas = ExtResource( 7 ) -region = Rect2( 64, 0, 16, 16 ) +region = Rect2( 32, 48, 16, 16 ) [sub_resource type="AtlasTexture" id=9] atlas = ExtResource( 7 ) -region = Rect2( 0, 16, 16, 16 ) +region = Rect2( 48, 48, 16, 16 ) [sub_resource type="AtlasTexture" id=10] atlas = ExtResource( 7 ) -region = Rect2( 16, 16, 16, 16 ) +region = Rect2( 64, 48, 16, 16 ) [sub_resource type="AtlasTexture" id=11] atlas = ExtResource( 7 ) -region = Rect2( 32, 16, 16, 16 ) +region = Rect2( 0, 64, 16, 16 ) [sub_resource type="AtlasTexture" id=12] atlas = ExtResource( 7 ) -region = Rect2( 48, 16, 16, 16 ) +region = Rect2( 0, 16, 16, 16 ) [sub_resource type="AtlasTexture" id=13] atlas = ExtResource( 7 ) -region = Rect2( 64, 16, 16, 16 ) +region = Rect2( 16, 16, 16, 16 ) [sub_resource type="AtlasTexture" id=14] atlas = ExtResource( 7 ) -region = Rect2( 0, 0, 16, 16 ) +region = Rect2( 32, 16, 16, 16 ) [sub_resource type="AtlasTexture" id=15] atlas = ExtResource( 7 ) -region = Rect2( 16, 0, 16, 16 ) +region = Rect2( 48, 16, 16, 16 ) [sub_resource type="AtlasTexture" id=16] atlas = ExtResource( 7 ) -region = Rect2( 32, 0, 16, 16 ) +region = Rect2( 64, 16, 16, 16 ) [sub_resource type="AtlasTexture" id=17] atlas = ExtResource( 7 ) -region = Rect2( 48, 0, 16, 16 ) +region = Rect2( 0, 64, 16, 16 ) [sub_resource type="AtlasTexture" id=18] atlas = ExtResource( 7 ) -region = Rect2( 64, 0, 16, 16 ) +region = Rect2( 0, 64, 16, 16 ) [sub_resource type="AtlasTexture" id=19] atlas = ExtResource( 7 ) -region = Rect2( 0, 64, 16, 16 ) +region = Rect2( 0, 0, 16, 16 ) [sub_resource type="AtlasTexture" id=20] atlas = ExtResource( 7 ) -region = Rect2( 0, 0, 16, 16 ) +region = Rect2( 16, 0, 16, 16 ) [sub_resource type="AtlasTexture" id=21] atlas = ExtResource( 7 ) -region = Rect2( 16, 0, 16, 16 ) +region = Rect2( 32, 0, 16, 16 ) [sub_resource type="AtlasTexture" id=22] atlas = ExtResource( 7 ) -region = Rect2( 32, 0, 16, 16 ) +region = Rect2( 48, 0, 16, 16 ) [sub_resource type="AtlasTexture" id=23] atlas = ExtResource( 7 ) -region = Rect2( 48, 0, 16, 16 ) +region = Rect2( 64, 0, 16, 16 ) [sub_resource type="AtlasTexture" id=24] atlas = ExtResource( 7 ) -region = Rect2( 64, 0, 16, 16 ) +region = Rect2( 0, 0, 16, 16 ) [sub_resource type="AtlasTexture" id=25] atlas = ExtResource( 7 ) -region = Rect2( 0, 64, 16, 16 ) +region = Rect2( 16, 0, 16, 16 ) + +[sub_resource type="AtlasTexture" id=26] +atlas = ExtResource( 7 ) +region = Rect2( 32, 0, 16, 16 ) + +[sub_resource type="AtlasTexture" id=27] +atlas = ExtResource( 7 ) +region = Rect2( 48, 0, 16, 16 ) + +[sub_resource type="AtlasTexture" id=28] +atlas = ExtResource( 7 ) +region = Rect2( 64, 0, 16, 16 ) -[sub_resource type="SpriteFrames" id=26] +[sub_resource type="SpriteFrames" id=29] animations = [ { -"frames": [ SubResource( 1 ) ], +"frames": [ SubResource( 1 ), SubResource( 2 ), SubResource( 3 ), SubResource( 4 ), SubResource( 5 ) ], "loop": true, -"name": "player_climbing_horizontally", +"name": "player_falling", "speed": 5.0 }, { -"frames": [ SubResource( 2 ), SubResource( 3 ) ], +"frames": [ SubResource( 6 ), SubResource( 7 ), SubResource( 8 ), SubResource( 9 ), SubResource( 10 ), SubResource( 8 ) ], "loop": true, "name": "player_climbing_up", -"speed": 2.0 +"speed": 5.0 }, { -"frames": [ SubResource( 4 ), SubResource( 5 ), SubResource( 6 ), SubResource( 7 ), SubResource( 8 ) ], +"frames": [ SubResource( 11 ) ], "loop": true, -"name": "player_idle_left", +"name": "player_cliff_hanging", "speed": 5.0 }, { -"frames": [ SubResource( 9 ), SubResource( 10 ), SubResource( 11 ), SubResource( 12 ), SubResource( 13 ) ], +"frames": [ SubResource( 12 ), SubResource( 13 ), SubResource( 14 ), SubResource( 15 ), SubResource( 16 ) ], "loop": true, "name": "player_idle_back", "speed": 5.0 }, { -"frames": [ SubResource( 14 ), SubResource( 15 ), SubResource( 16 ), SubResource( 17 ), SubResource( 18 ) ], +"frames": [ SubResource( 17 ) ], "loop": true, -"name": "player_falling", +"name": "player_traversing", "speed": 5.0 }, { -"frames": [ SubResource( 19 ) ], +"frames": [ SubResource( 18 ) ], "loop": true, "name": "player_scraping", "speed": 5.0 }, { -"frames": [ SubResource( 20 ), SubResource( 21 ), SubResource( 22 ), SubResource( 23 ), SubResource( 24 ) ], +"frames": [ SubResource( 19 ), SubResource( 20 ), SubResource( 21 ), SubResource( 22 ), SubResource( 23 ) ], "loop": true, "name": "player_running", "speed": 4.0 }, { -"frames": [ SubResource( 25 ) ], +"frames": [ SubResource( 24 ), SubResource( 25 ), SubResource( 26 ), SubResource( 27 ), SubResource( 28 ) ], "loop": true, -"name": "player_cliff_hanging", +"name": "player_idle_left", "speed": 5.0 } ] -[sub_resource type="RectangleShape2D" id=27] +[sub_resource type="RectangleShape2D" id=30] extents = Vector2( 24, 56 ) -[sub_resource type="RectangleShape2D" id=28] +[sub_resource type="RectangleShape2D" id=31] extents = Vector2( 24, 56 ) -[sub_resource type="AtlasTexture" id=29] +[sub_resource type="AtlasTexture" id=32] atlas = ExtResource( 2 ) region = Rect2( 0, 48, 48, 48 ) -[sub_resource type="AtlasTexture" id=30] +[sub_resource type="AtlasTexture" id=33] atlas = ExtResource( 2 ) region = Rect2( 48, 48, 48, 48 ) -[sub_resource type="AtlasTexture" id=31] +[sub_resource type="AtlasTexture" id=34] atlas = ExtResource( 2 ) region = Rect2( 96, 48, 48, 48 ) -[sub_resource type="AtlasTexture" id=32] +[sub_resource type="AtlasTexture" id=35] atlas = ExtResource( 2 ) region = Rect2( 144, 48, 48, 48 ) -[sub_resource type="AtlasTexture" id=33] +[sub_resource type="AtlasTexture" id=36] atlas = ExtResource( 2 ) region = Rect2( 192, 48, 48, 48 ) -[sub_resource type="SpriteFrames" id=34] +[sub_resource type="SpriteFrames" id=37] animations = [ { -"frames": [ SubResource( 29 ), SubResource( 30 ), SubResource( 31 ), SubResource( 32 ), SubResource( 33 ) ], +"frames": [ SubResource( 32 ), SubResource( 33 ), SubResource( 34 ), SubResource( 35 ), SubResource( 36 ) ], "loop": true, "name": "default", "speed": 20.0 } ] -[sub_resource type="AtlasTexture" id=51] +[sub_resource type="AtlasTexture" id=38] atlas = ExtResource( 2 ) region = Rect2( 0, 0, 48, 48 ) -[sub_resource type="AtlasTexture" id=52] +[sub_resource type="AtlasTexture" id=39] atlas = ExtResource( 2 ) region = Rect2( 48, 0, 48, 48 ) -[sub_resource type="AtlasTexture" id=53] +[sub_resource type="AtlasTexture" id=40] atlas = ExtResource( 2 ) region = Rect2( 96, 0, 48, 48 ) -[sub_resource type="AtlasTexture" id=54] +[sub_resource type="AtlasTexture" id=41] atlas = ExtResource( 2 ) region = Rect2( 144, 0, 48, 48 ) -[sub_resource type="AtlasTexture" id=55] +[sub_resource type="AtlasTexture" id=42] atlas = ExtResource( 2 ) region = Rect2( 192, 0, 48, 48 ) -[sub_resource type="SpriteFrames" id=35] +[sub_resource type="SpriteFrames" id=43] animations = [ { -"frames": [ SubResource( 51 ), SubResource( 52 ), SubResource( 53 ), SubResource( 54 ), SubResource( 55 ) ], +"frames": [ SubResource( 38 ), SubResource( 39 ), SubResource( 40 ), SubResource( 41 ), SubResource( 42 ) ], "loop": true, "name": "default", "speed": 8.0 } ] -[sub_resource type="RectangleShape2D" id=36] +[sub_resource type="RectangleShape2D" id=44] extents = Vector2( 960, 1920 ) -[sub_resource type="RectangleShape2D" id=37] +[sub_resource type="RectangleShape2D" id=45] extents = Vector2( 664, 64 ) -[sub_resource type="RectangleShape2D" id=38] +[sub_resource type="RectangleShape2D" id=46] extents = Vector2( 88, 64.0054 ) -[sub_resource type="RectangleShape2D" id=39] +[sub_resource type="RectangleShape2D" id=47] extents = Vector2( 128, 64.0076 ) -[sub_resource type="RectangleShape2D" id=40] +[sub_resource type="RectangleShape2D" id=48] extents = Vector2( 960, 64 ) -[sub_resource type="RectangleShape2D" id=41] +[sub_resource type="RectangleShape2D" id=49] extents = Vector2( 960, 64 ) -[sub_resource type="RectangleShape2D" id=42] +[sub_resource type="RectangleShape2D" id=50] extents = Vector2( 960, 64 ) -[sub_resource type="RectangleShape2D" id=43] +[sub_resource type="RectangleShape2D" id=51] extents = Vector2( 960, 1920 ) -[sub_resource type="RectangleShape2D" id=44] +[sub_resource type="RectangleShape2D" id=52] extents = Vector2( 960, 64 ) -[sub_resource type="RectangleShape2D" id=45] +[sub_resource type="RectangleShape2D" id=53] extents = Vector2( 960, 64 ) -[sub_resource type="RectangleShape2D" id=46] +[sub_resource type="RectangleShape2D" id=54] extents = Vector2( 960, 64 ) -[sub_resource type="RectangleShape2D" id=47] +[sub_resource type="RectangleShape2D" id=55] extents = Vector2( 960, 64 ) -[sub_resource type="RectangleShape2D" id=48] +[sub_resource type="RectangleShape2D" id=56] extents = Vector2( 64, 4288 ) -[sub_resource type="RectangleShape2D" id=49] +[sub_resource type="RectangleShape2D" id=57] extents = Vector2( 64, 4288 ) -[sub_resource type="RectangleShape2D" id=50] +[sub_resource type="RectangleShape2D" id=58] extents = Vector2( 63.9966, 960 ) [node name="Main Scene" type="Node2D"] @@ -279,10 +292,8 @@ CliffScrapingSoundLoopEndSeconds = 5.55 "Player", ]] scale = Vector2( 8, 8 ) -frames = SubResource( 26 ) -animation = "player_idle_left" -frame = 2 -playing = true +frames = SubResource( 29 ) +animation = "player_falling" [node name="Camera2D" type="Camera2D" parent="Player" groups=[ "Player", @@ -307,25 +318,26 @@ drag_margin_bottom = 0.0 ]] visible = false position = Vector2( -8, 0 ) -shape = SubResource( 27 ) +shape = SubResource( 30 ) [node name="Area2D" type="Area2D" parent="Player" groups=[ "Player", ]] visible = false +collision_mask = 126 [node name="CollisionShape2D" type="CollisionShape2D" parent="Player/Area2D" groups=[ "Player", ]] position = Vector2( -8, 0 ) -shape = SubResource( 28 ) +shape = SubResource( 31 ) [node name="DebuggingText" type="RichTextLabel" parent="Player" groups=[ "Player", ]] visible = false margin_left = 72.0 -margin_top = -192.0 +margin_top = -320.0 margin_right = 456.0 margin_bottom = -64.0 bbcode_enabled = true @@ -352,7 +364,7 @@ collision_layer = 66 collision_mask = 13 script = ExtResource( 4 ) -[node name="Background TileMap" type="TileMap" parent="Upper Cliffs" groups=[ +[node name="Rock" type="TileMap" parent="Upper Cliffs" groups=[ "Cliffs", ]] scale = Vector2( 8, 8 ) @@ -363,7 +375,20 @@ collision_use_parent = true format = 1 tile_data = PoolIntArray( -1966080, 21, 0, -1966079, 21, 1, -1966078, 21, 1, -1966077, 21, 1, -1966076, 21, 1, -1966075, 21, 1, -1966074, 21, 1, -1966073, 21, 1, -1966072, 21, 1, -1966071, 21, 1, -1966070, 21, 2, -1966067, 21, 0, -1966066, 21, 2, -1900544, 21, 65536, -1900543, 21, 65537, -1900542, 21, 65537, -1900541, 21, 65537, -1900540, 21, 65537, -1900539, 21, 65537, -1900538, 21, 65537, -1900537, 21, 65537, -1900536, 21, 65537, -1900535, 21, 65537, -1900534, 21, 65537, -1900533, 21, 65537, -1900532, 21, 1, -1900531, 21, 1, -1900530, 21, 2, -1835008, 21, 65536, -1835007, 21, 65537, -1835006, 21, 65537, -1835005, 21, 65537, -1835004, 21, 65537, -1835003, 21, 65537, -1835002, 21, 65537, -1835001, 21, 65537, -1835000, 21, 65537, -1834999, 21, 65537, -1834998, 21, 65537, -1834997, 21, 65537, -1834996, 21, 65537, -1834995, 21, 65537, -1834994, 21, 65538, -1769472, 21, 65536, -1769471, 21, 65537, -1769470, 21, 65537, -1769469, 21, 65537, -1769468, 21, 65537, -1769467, 21, 65537, -1769466, 21, 65537, -1769465, 21, 65537, -1769464, 21, 65537, -1769463, 21, 65537, -1769462, 21, 65537, -1769461, 21, 65537, -1769460, 21, 65537, -1769459, 21, 65537, -1769458, 21, 65538, -1703936, 21, 65536, -1703935, 21, 65537, -1703934, 21, 65537, -1703933, 21, 65537, -1703932, 21, 65537, -1703931, 21, 65537, -1703930, 21, 65537, -1703929, 21, 65537, -1703928, 21, 65537, -1703927, 21, 65537, -1703926, 21, 65537, -1703925, 21, 65537, -1703924, 21, 65537, -1703923, 21, 65537, -1703922, 21, 65538, -1638400, 21, 65536, -1638399, 21, 65537, -1638398, 21, 65537, -1638397, 21, 65537, -1638396, 21, 65537, -1638395, 21, 65537, -1638394, 21, 65537, -1638393, 21, 65537, -1638392, 21, 65537, -1638391, 21, 65537, -1638390, 21, 65537, -1638389, 21, 65537, -1638388, 21, 65537, -1638387, 21, 65537, -1638386, 21, 65538, -1572864, 21, 65536, -1572863, 21, 65537, -1572862, 21, 65537, -1572861, 21, 65537, -1572860, 21, 65537, -1572859, 21, 65537, -1572858, 21, 65537, -1572857, 21, 65537, -1572856, 21, 65537, -1572855, 21, 65537, -1572854, 21, 65537, -1572853, 21, 65537, -1572852, 21, 65537, -1572851, 21, 65537, -1572850, 21, 65538, -1507328, 21, 65536, -1507327, 21, 65537, -1507326, 21, 65537, -1507325, 21, 65537, -1507324, 21, 65537, -1507323, 21, 65537, -1507322, 21, 65537, -1507321, 21, 65537, -1507320, 21, 65537, -1507319, 21, 65537, -1507318, 21, 65537, -1507317, 21, 65537, -1507316, 21, 65537, -1507315, 21, 65537, -1507314, 21, 65538, -1441792, 21, 65536, -1441791, 21, 65537, -1441790, 21, 65537, -1441789, 21, 65537, -1441788, 21, 65537, -1441787, 21, 65537, -1441786, 21, 65537, -1441785, 21, 65537, -1441784, 21, 65537, -1441783, 21, 65537, -1441782, 21, 65537, -1441781, 21, 65537, -1441780, 21, 65537, -1441779, 21, 65537, -1441778, 21, 65538, -1376256, 21, 65536, -1376255, 21, 65537, -1376254, 21, 65537, -1376253, 21, 65537, -1376252, 21, 65537, -1376251, 21, 65537, -1376250, 21, 65537, -1376249, 21, 65537, -1376248, 21, 65537, -1376247, 21, 65537, -1376246, 21, 65537, -1376245, 21, 65537, -1376244, 21, 65537, -1376243, 21, 65537, -1376242, 21, 65538, -1310720, 21, 65536, -1310719, 21, 65537, -1310718, 21, 65537, -1310717, 21, 65537, -1310716, 21, 65537, -1310715, 21, 65537, -1310714, 21, 65537, -1310713, 21, 65537, -1310712, 21, 65537, -1310711, 21, 65537, -1310710, 21, 65537, -1310709, 21, 65537, -1310708, 21, 65537, -1310707, 21, 65537, -1310706, 21, 65538, -1245184, 21, 65536, -1245183, 21, 65537, -1245182, 21, 65537, -1245181, 21, 65537, -1245180, 21, 65537, -1245179, 21, 65537, -1245178, 21, 65537, -1245177, 21, 65537, -1245176, 21, 65537, -1245175, 21, 65537, -1245174, 21, 65537, -1245173, 21, 65537, -1245172, 21, 65537, -1245171, 21, 65537, -1245170, 21, 65538, -1179648, 21, 65536, -1179647, 21, 65537, -1179646, 21, 65537, -1179645, 21, 65537, -1179644, 21, 65537, -1179643, 21, 65537, -1179642, 21, 65537, -1179641, 21, 65537, -1179640, 21, 65537, -1179639, 21, 65537, -1179638, 21, 65537, -1179637, 21, 65537, -1179636, 21, 65537, -1179635, 21, 65537, -1179634, 21, 65538, -1114112, 21, 65536, -1114111, 21, 65537, -1114110, 21, 65537, -1114109, 21, 65537, -1114108, 21, 65537, -1114107, 21, 65537, -1114106, 21, 65537, -1114105, 21, 65537, -1114104, 21, 65537, -1114103, 21, 65537, -1114102, 21, 65537, -1114101, 21, 65537, -1114100, 21, 65537, -1114099, 21, 65537, -1114098, 21, 65538, -1048576, 21, 65536, -1048575, 21, 65537, -1048574, 21, 65537, -1048573, 21, 65537, -1048572, 21, 65537, -1048571, 21, 65537, -1048570, 21, 65537, -1048569, 21, 65537, -1048568, 21, 65537, -1048567, 21, 65537, -1048566, 21, 65537, -1048565, 21, 65537, -1048564, 21, 65537, -1048563, 21, 65537, -1048562, 21, 65538, -983040, 21, 65536, -983039, 21, 65537, -983038, 21, 65537, -983037, 21, 65537, -983036, 21, 65537, -983035, 21, 65537, -983034, 21, 65537, -983033, 21, 65537, -983032, 21, 65537, -983031, 21, 65537, -983030, 21, 65537, -983029, 21, 65537, -983028, 21, 65537, -983027, 21, 65537, -983026, 21, 65538, -917504, 21, 65536, -917503, 21, 65537, -917502, 21, 65537, -917501, 21, 65537, -917500, 21, 65537, -917499, 21, 65537, -917498, 21, 65537, -917497, 21, 65537, -917496, 21, 65537, -917495, 21, 65537, -917494, 21, 65537, -917493, 21, 65537, -917492, 21, 65537, -917491, 21, 65537, -917490, 21, 65538, -851968, 21, 65536, -851967, 21, 65537, -851966, 21, 65537, -851965, 21, 65537, -851964, 21, 65537, -851963, 21, 65537, -851962, 21, 65537, -851961, 21, 65537, -851960, 21, 65537, -851959, 21, 65537, -851958, 21, 65537, -851957, 21, 65537, -851956, 21, 65537, -851955, 21, 65537, -851954, 21, 65538, -786432, 21, 65536, -786431, 21, 65537, -786430, 21, 65537, -786429, 21, 65537, -786428, 21, 65537, -786427, 21, 65537, -786426, 21, 65537, -786425, 21, 65537, -786424, 21, 65537, -786423, 21, 65537, -786422, 21, 65537, -786421, 21, 65537, -786420, 21, 65537, -786419, 21, 65537, -786418, 21, 65538, -720896, 21, 65536, -720895, 21, 65537, -720894, 21, 65537, -720893, 21, 65537, -720892, 21, 65537, -720891, 21, 65537, -720890, 21, 65537, -720889, 21, 65537, -720888, 21, 65537, -720887, 21, 65537, -720886, 21, 65537, -720885, 21, 65537, -720884, 21, 65537, -720883, 21, 65537, -720882, 21, 65538, -655360, 21, 65536, -655359, 21, 65537, -655358, 21, 65537, -655357, 21, 65537, -655356, 21, 65537, -655355, 21, 65537, -655354, 21, 65537, -655353, 21, 65537, -655352, 21, 65537, -655351, 21, 65537, -655350, 21, 65537, -655349, 21, 65537, -655348, 21, 65537, -655347, 21, 65537, -655346, 21, 65538, -589824, 21, 65536, -589823, 21, 65537, -589822, 21, 65537, -589821, 21, 65537, -589820, 21, 65537, -589819, 21, 65537, -589818, 21, 65537, -589817, 21, 65537, -589816, 21, 65537, -589815, 21, 65537, -589814, 21, 65537, -589813, 21, 65537, -589812, 21, 65537, -589811, 21, 65537, -589810, 21, 65538, -524288, 21, 65536, -524287, 21, 65537, -524286, 21, 65537, -524285, 21, 65537, -524284, 21, 65537, -524283, 21, 65537, -524282, 21, 65537, -524281, 21, 65537, -524280, 21, 65537, -524279, 21, 65537, -524278, 21, 65537, -524277, 21, 65537, -524276, 21, 65537, -524275, 21, 65537, -524274, 21, 65538, -458752, 21, 65536, -458751, 21, 65537, -458750, 21, 65537, -458749, 21, 65537, -458748, 21, 65537, -458747, 21, 65537, -458746, 21, 65537, -458745, 21, 65537, -458744, 21, 65537, -458743, 21, 65537, -458742, 21, 65537, -458741, 21, 65537, -458740, 21, 65537, -458739, 21, 65537, -458738, 21, 65538, -393216, 21, 65536, -393215, 21, 65537, -393214, 21, 65537, -393213, 21, 65537, -393212, 21, 65537, -393211, 21, 65537, -393210, 21, 65537, -393209, 21, 65537, -393208, 21, 65537, -393207, 21, 65537, -393206, 21, 65537, -393205, 21, 65537, -393204, 21, 65537, -393203, 21, 65537, -393202, 21, 65538, -327680, 21, 65536, -327679, 21, 65537, -327678, 21, 65537, -327677, 21, 65537, -327676, 21, 65537, -327675, 21, 65537, -327674, 21, 65537, -327673, 21, 65537, -327672, 21, 65537, -327671, 21, 65537, -327670, 21, 65537, -327669, 21, 65537, -327668, 21, 65537, -327667, 21, 65537, -327666, 21, 65538, -262144, 21, 65536, -262143, 21, 65537, -262142, 21, 65537, -262141, 21, 65537, -262140, 21, 65537, -262139, 21, 65537, -262138, 21, 65537, -262137, 21, 65537, -262136, 21, 65537, -262135, 21, 65537, -262134, 21, 65537, -262133, 21, 65537, -262132, 21, 65537, -262131, 21, 65537, -262130, 21, 65538, -196608, 21, 65536, -196607, 21, 65537, -196606, 21, 65537, -196605, 21, 65537, -196604, 21, 65537, -196603, 21, 65537, -196602, 21, 65537, -196601, 21, 65537, -196600, 21, 65537, -196599, 21, 65537, -196598, 21, 65537, -196597, 21, 65537, -196596, 21, 65537, -196595, 21, 65537, -196594, 21, 65538, -131072, 21, 65536, -131071, 21, 65537, -131070, 21, 65537, -131069, 21, 65537, -131068, 21, 65537, -131067, 21, 65537, -131066, 21, 65537, -131065, 21, 65537, -131064, 21, 65537, -131063, 21, 65537, -131062, 21, 65537, -131061, 21, 65537, -131060, 21, 65537, -131059, 21, 65537, -131058, 21, 65538, -65536, 21, 131072, -65535, 21, 131073, -65534, 21, 131073, -65533, 21, 131073, -65532, 21, 131073, -65531, 21, 131073, -65530, 21, 131073, -65529, 21, 131073, -65528, 21, 131073, -65527, 21, 131073, -65526, 21, 131073, -65525, 21, 131073, -65524, 21, 131073, -65523, 21, 131073, -65522, 21, 131074 ) -[node name="Foreground TileMap" type="TileMap" parent="Upper Cliffs" groups=[ +[node name="Ice" type="TileMap" parent="Upper Cliffs" groups=[ +"Cliffs", +"Ice", +]] +scale = Vector2( 8, 8 ) +tile_set = ExtResource( 3 ) +cell_size = Vector2( 16, 16 ) +cell_custom_transform = Transform2D( 0, 0, 0, 0, 0, 0 ) +collision_layer = 66 +collision_mask = 13 +format = 1 +tile_data = PoolIntArray( -1900541, 31, 0, -1900540, 31, 2, -1900538, 31, 0, -1900536, 31, 0, -1900535, 31, 0, -1900530, 34, 0, -1835006, 31, 0, -1835005, 31, 131072, -1835004, 31, 131074, -1835003, 31, 0, -1835002, 31, 0, -1835001, 31, 1, -1835000, 31, 2, -1769470, 31, 0, -1769466, 31, 131072, -1769465, 31, 131073, -1769464, 31, 131074, -1769463, 31, 0, -1769462, 31, 0, -1769458, 34, 0, -1703931, 31, 0, -1703930, 31, 0, -1703926, 31, 0, -1703925, 31, 0, -1638387, 31, 0, -1638386, 34, 0, -1572863, 31, 0, -1572862, 31, 2, -1572861, 31, 0, -1572852, 31, 0, -1572851, 31, 2, -1572850, 34, 0, -1507327, 31, 131072, -1507326, 31, 131074, -1507324, 31, 0, -1507323, 31, 1, -1507322, 31, 2, -1507318, 31, 0, -1507317, 31, 0, -1507316, 31, 131072, -1507315, 31, 131074, -1507314, 34, 0, -1441790, 31, 0, -1441789, 31, 0, -1441788, 31, 131072, -1441787, 31, 131073, -1441786, 31, 0, -1441785, 31, 2, -1441784, 31, 0, -1441783, 31, 0, -1441778, 34, 0, -1376256, 32, 0, -1376253, 31, 0, -1376250, 31, 131072, -1376249, 31, 131074, -1376242, 34, 0, -1310720, 32, 0, -1310712, 31, 0, -1310710, 31, 0, -1245184, 32, 0, -1245174, 31, 0, -1245173, 31, 2, -1245172, 31, 0, -1179638, 31, 131072, -1179637, 31, 131074, -1114109, 31, 0, -1114108, 31, 1, -1114107, 31, 2, -1114102, 31, 0, -1048573, 31, 65536, -1048572, 31, 65537, -1048571, 31, 65538, -1048570, 31, 0, -1048566, 31, 0, -983037, 31, 131072, -983036, 31, 0, -983035, 31, 65538, -917500, 31, 131072, -917499, 31, 131074, -851963, 31, 0, -851959, 31, 0, -851958, 31, 0, -720895, 33, 0, -720886, 33, 0, -655358, 31, 0, -655357, 31, 2, -589824, 31, 0, -589823, 31, 1, -589822, 31, 0, -589821, 31, 131074, -524288, 31, 131072, -524287, 31, 131073, -524286, 31, 131074, -524284, 33, 0, -458742, 33, 0, 262147, 33, 0, 327683, 33, 0, 393219, 33, 0, 458755, 33, 0, 458756, 33, 0, 589835, 33, 0, 655371, 33, 0, 655372, 33, 0, 720907, 33, 0, 720908, 33, 0, 1179650, 31, 0 ) + +[node name="Snow" type="TileMap" parent="Upper Cliffs" groups=[ "Cliffs", ]] scale = Vector2( 8, 8 ) @@ -372,7 +397,7 @@ cell_size = Vector2( 16, 16 ) cell_custom_transform = Transform2D( 0, 0, 0, 0, 0, 0 ) collision_use_parent = true format = 1 -tile_data = PoolIntArray( -1966080, 23, 0, -1966079, 22, 0, -1966078, 22, 0, -1966077, 22, 0, -1966076, 22, 0, -1966075, 22, 0, -1966074, 22, 0, -1966073, 22, 0, -1966072, 22, 0, -1966071, 22, 0, -1966070, 24, 0, -1966067, 23, 0, -1966066, 24, 0, 131072, 2, 0, 131073, 0, 0, 131074, 0, 0, 131075, 0, 0, 131076, 0, 0, 131077, 0, 0, 131078, 0, 0, 131079, 0, 0, 131080, 0, 0, 131081, 0, 0, 131082, 0, 0, 131083, 0, 0, 131084, 0, 0, 131085, 0, 0, 131086, 1, 0 ) +tile_data = PoolIntArray( -1966080, 27, 0, -1966079, 28, 0, -1966078, 28, 0, -1966077, 28, 0, -1966076, 28, 0, -1966075, 28, 0, -1966074, 28, 0, -1966073, 28, 0, -1966072, 28, 0, -1966071, 28, 0, -1966070, 29, 0, -1966067, 27, 0, -1966066, 29, 0, 131072, 2, 0, 131073, 0, 0, 131074, 0, 0, 131075, 0, 0, 131076, 0, 0, 131077, 0, 0, 131078, 0, 0, 131079, 0, 0, 131080, 0, 0, 131081, 0, 0, 131082, 0, 0, 131083, 0, 0, 131084, 0, 0, 131085, 0, 0, 131086, 1, 0 ) [node name="Waterfall" type="Area2D" parent="Upper Cliffs"] z_index = 3 @@ -380,9 +405,7 @@ z_index = 3 [node name="waterfall 1" type="AnimatedSprite" parent="Upper Cliffs/Waterfall"] position = Vector2( 1536, -64 ) scale = Vector2( 8, 8 ) -frames = SubResource( 34 ) -frame = 2 -playing = true +frames = SubResource( 37 ) [node name="AudioStreamPlayer2D" type="AudioStreamPlayer2D" parent="Upper Cliffs/Waterfall/waterfall 1"] scale = Vector2( 0.125, 0.125 ) @@ -396,9 +419,7 @@ attenuation = 8.28211 [node name="waterfall 2" type="AnimatedSprite" parent="Upper Cliffs/Waterfall"] position = Vector2( 1536, -448 ) scale = Vector2( 8, 8 ) -frames = SubResource( 34 ) -frame = 2 -playing = true +frames = SubResource( 37 ) [node name="AudioStreamPlayer2D" type="AudioStreamPlayer2D" parent="Upper Cliffs/Waterfall/waterfall 2"] scale = Vector2( 0.125, 0.125 ) @@ -412,9 +433,7 @@ attenuation = 8.28211 [node name="waterfall 3" type="AnimatedSprite" parent="Upper Cliffs/Waterfall"] position = Vector2( 1536, -832 ) scale = Vector2( 8, 8 ) -frames = SubResource( 34 ) -frame = 2 -playing = true +frames = SubResource( 37 ) [node name="AudioStreamPlayer2D" type="AudioStreamPlayer2D" parent="Upper Cliffs/Waterfall/waterfall 3"] scale = Vector2( 0.125, 0.125 ) @@ -428,9 +447,7 @@ attenuation = 8.28211 [node name="waterfall 4" type="AnimatedSprite" parent="Upper Cliffs/Waterfall"] position = Vector2( 1536, -1216 ) scale = Vector2( 8, 8 ) -frames = SubResource( 34 ) -frame = 2 -playing = true +frames = SubResource( 37 ) [node name="AudioStreamPlayer2D" type="AudioStreamPlayer2D" parent="Upper Cliffs/Waterfall/waterfall 4"] scale = Vector2( 0.125, 0.125 ) @@ -444,9 +461,7 @@ attenuation = 8.28211 [node name="waterfall 5" type="AnimatedSprite" parent="Upper Cliffs/Waterfall"] position = Vector2( 1536, -1600 ) scale = Vector2( 8, 8 ) -frames = SubResource( 34 ) -frame = 2 -playing = true +frames = SubResource( 37 ) [node name="AudioStreamPlayer2D" type="AudioStreamPlayer2D" parent="Upper Cliffs/Waterfall/waterfall 5"] scale = Vector2( 0.125, 0.125 ) @@ -460,9 +475,7 @@ attenuation = 8.28211 [node name="waterfall 6" type="AnimatedSprite" parent="Upper Cliffs/Waterfall"] position = Vector2( 1536, -1984 ) scale = Vector2( 8, 8 ) -frames = SubResource( 34 ) -frame = 2 -playing = true +frames = SubResource( 37 ) [node name="AudioStreamPlayer2D" type="AudioStreamPlayer2D" parent="Upper Cliffs/Waterfall/waterfall 6"] scale = Vector2( 0.125, 0.125 ) @@ -476,9 +489,7 @@ attenuation = 8.28211 [node name="waterfall 7" type="AnimatedSprite" parent="Upper Cliffs/Waterfall"] position = Vector2( 1536, -2368 ) scale = Vector2( 8, 8 ) -frames = SubResource( 34 ) -frame = 2 -playing = true +frames = SubResource( 37 ) [node name="AudioStreamPlayer2D" type="AudioStreamPlayer2D" parent="Upper Cliffs/Waterfall/waterfall 7"] scale = Vector2( 0.125, 0.125 ) @@ -492,9 +503,7 @@ attenuation = 8.28211 [node name="waterfall 8" type="AnimatedSprite" parent="Upper Cliffs/Waterfall"] position = Vector2( 1536, -2752 ) scale = Vector2( 8, 8 ) -frames = SubResource( 34 ) -frame = 2 -playing = true +frames = SubResource( 37 ) [node name="AudioStreamPlayer2D" type="AudioStreamPlayer2D" parent="Upper Cliffs/Waterfall/waterfall 8"] scale = Vector2( 0.125, 0.125 ) @@ -508,9 +517,7 @@ attenuation = 8.28211 [node name="waterfall 9" type="AnimatedSprite" parent="Upper Cliffs/Waterfall"] position = Vector2( 1536, -3136 ) scale = Vector2( 8, 8 ) -frames = SubResource( 34 ) -frame = 2 -playing = true +frames = SubResource( 37 ) [node name="AudioStreamPlayer2D" type="AudioStreamPlayer2D" parent="Upper Cliffs/Waterfall/waterfall 9"] scale = Vector2( 0.125, 0.125 ) @@ -523,9 +530,7 @@ attenuation = 8.28211 [node name="waterfall 10" type="AnimatedSprite" parent="Upper Cliffs/Waterfall"] position = Vector2( 1536, -3520 ) scale = Vector2( 8, 8 ) -frames = SubResource( 34 ) -frame = 2 -playing = true +frames = SubResource( 37 ) [node name="AudioStreamPlayer2D" type="AudioStreamPlayer2D" parent="Upper Cliffs/Waterfall/waterfall 10"] scale = Vector2( 0.125, 0.125 ) @@ -539,27 +544,24 @@ attenuation = 8.28211 [node name="waterfall mist 1" type="AnimatedSprite" parent="Upper Cliffs/Waterfall"] position = Vector2( 1328, -16 ) scale = Vector2( 16, 16 ) -frames = SubResource( 35 ) -playing = true +frames = SubResource( 43 ) [node name="waterfall mist 2" type="AnimatedSprite" parent="Upper Cliffs/Waterfall"] position = Vector2( 1720, -16 ) scale = Vector2( 16, 16 ) -frames = SubResource( 35 ) -playing = true +frames = SubResource( 43 ) [node name="waterfall mist 3" type="AnimatedSprite" parent="Upper Cliffs/Waterfall"] -position = Vector2( 1528, 24 ) +position = Vector2( 1536, 0 ) scale = Vector2( 16, 16 ) -frames = SubResource( 35 ) -playing = true +frames = SubResource( 43 ) [node name="CollisionShape2D" type="CollisionShape2D" parent="Upper Cliffs" groups=[ "Cliffs", ]] visible = false position = Vector2( 960, -1920 ) -shape = SubResource( 36 ) +shape = SubResource( 44 ) [node name="Upper Cliffs Top Ground Static Collider" type="StaticBody2D" parent="Upper Cliffs" groups=[ "Cliffs", @@ -577,7 +579,7 @@ collision_mask = 29 ]] position = Vector2( 704, -3776 ) rotation = 18.8496 -shape = SubResource( 37 ) +shape = SubResource( 45 ) one_way_collision = true [node name="Upper Cliffs Top Ground Static Collider2" type="StaticBody2D" parent="Upper Cliffs" groups=[ @@ -596,7 +598,7 @@ collision_mask = 29 ]] position = Vector2( 1792, -3776 ) rotation = 18.8496 -shape = SubResource( 38 ) +shape = SubResource( 46 ) one_way_collision = true [node name="Upper Cliffs Top Ground Static Collider3" type="StaticBody2D" parent="Upper Cliffs" groups=[ @@ -615,7 +617,7 @@ collision_mask = 29 ]] position = Vector2( 1536, -3648 ) rotation = 18.8496 -shape = SubResource( 39 ) +shape = SubResource( 47 ) one_way_collision = true [node name="Upper Cliffs Top Ground Area Collider" type="Area2D" parent="Upper Cliffs" groups=[ @@ -631,7 +633,7 @@ collision_mask = 29 "Ground", ]] position = Vector2( 960, -3776 ) -shape = SubResource( 40 ) +shape = SubResource( 48 ) [node name="Upper Cliffs Bottom Ground Static Collider" type="StaticBody2D" parent="Upper Cliffs" groups=[ "Cliffs", @@ -649,7 +651,7 @@ collision_mask = 29 ]] position = Vector2( 960, 64 ) rotation = 6.28319 -shape = SubResource( 41 ) +shape = SubResource( 49 ) one_way_collision = true [node name="Upper Cliffs Bottom Ground Area Collider" type="Area2D" parent="Upper Cliffs" groups=[ @@ -665,7 +667,7 @@ collision_mask = 29 "Ground", ]] position = Vector2( 960, 64 ) -shape = SubResource( 42 ) +shape = SubResource( 50 ) [node name="Lower Cliffs" type="Area2D" parent="." groups=[ "Cliffs", @@ -675,11 +677,10 @@ collision_layer = 66 collision_mask = 13 script = ExtResource( 4 ) __meta__ = { -"_edit_group_": true, -"_edit_lock_": true +"_edit_group_": true } -[node name="Background TileMap" type="TileMap" parent="Lower Cliffs" groups=[ +[node name="Rock" type="TileMap" parent="Lower Cliffs" groups=[ "Cliffs", ]] scale = Vector2( 8, 8 ) @@ -689,7 +690,20 @@ collision_use_parent = true format = 1 tile_data = PoolIntArray( 131072, 21, 0, 131073, 21, 1, 131074, 21, 1, 131075, 21, 1, 131076, 21, 1, 131077, 21, 1, 131078, 21, 1, 131079, 21, 1, 131080, 21, 1, 131081, 21, 1, 131082, 21, 1, 131083, 21, 1, 131084, 21, 1, 131085, 21, 1, 131086, 21, 2, 196608, 21, 65536, 196609, 21, 65537, 196610, 21, 65537, 196611, 21, 65537, 196612, 21, 65537, 196613, 21, 65537, 196614, 21, 65537, 196615, 21, 65537, 196616, 21, 65537, 196617, 21, 65537, 196618, 21, 65537, 196619, 21, 65537, 196620, 21, 65537, 196621, 21, 65537, 196622, 21, 65538, 262144, 21, 65536, 262145, 21, 65537, 262146, 21, 65537, 262147, 21, 65537, 262148, 21, 65537, 262149, 21, 65537, 262150, 21, 65537, 262151, 21, 65537, 262152, 21, 65537, 262153, 21, 65537, 262154, 21, 65537, 262155, 21, 65537, 262156, 21, 65537, 262157, 21, 65537, 262158, 21, 65538, 327680, 21, 65536, 327681, 21, 65537, 327682, 21, 65537, 327683, 21, 65537, 327684, 21, 65537, 327685, 21, 65537, 327686, 21, 65537, 327687, 21, 65537, 327688, 21, 65537, 327689, 21, 65537, 327690, 21, 65537, 327691, 21, 65537, 327692, 21, 65537, 327693, 21, 65537, 327694, 21, 65538, 393216, 21, 65536, 393217, 21, 65537, 393218, 21, 65537, 393219, 21, 65537, 393220, 21, 65537, 393221, 21, 65537, 393222, 21, 65537, 393223, 21, 65537, 393224, 21, 65537, 393225, 21, 65537, 393226, 21, 65537, 393227, 21, 65537, 393228, 21, 65537, 393229, 21, 65537, 393230, 21, 65538, 458752, 21, 65536, 458753, 21, 65537, 458754, 21, 65537, 458755, 21, 65537, 458756, 21, 65537, 458757, 21, 65537, 458758, 21, 65537, 458759, 21, 65537, 458760, 21, 65537, 458761, 21, 65537, 458762, 21, 65537, 458763, 21, 65537, 458764, 21, 65537, 458765, 21, 65537, 458766, 21, 65538, 524288, 21, 65536, 524289, 21, 65537, 524290, 21, 65537, 524291, 21, 65537, 524292, 21, 65537, 524293, 21, 65537, 524294, 21, 65537, 524295, 21, 65537, 524296, 21, 65537, 524297, 21, 65537, 524298, 21, 65537, 524299, 21, 65537, 524300, 21, 65537, 524301, 21, 65537, 524302, 21, 65538, 589824, 21, 65536, 589825, 21, 65537, 589826, 21, 65537, 589827, 21, 65537, 589828, 21, 65537, 589829, 21, 65537, 589830, 21, 65537, 589831, 21, 65537, 589832, 21, 65537, 589833, 21, 65537, 589834, 21, 65537, 589835, 21, 65537, 589836, 21, 65537, 589837, 21, 65537, 589838, 21, 65538, 655360, 21, 65536, 655361, 21, 65537, 655362, 21, 65537, 655363, 21, 65537, 655364, 21, 65537, 655365, 21, 65537, 655366, 21, 65537, 655367, 21, 65537, 655368, 21, 65537, 655369, 21, 65537, 655370, 21, 65537, 655371, 21, 65537, 655372, 21, 65537, 655373, 21, 65537, 655374, 21, 65538, 720896, 21, 65536, 720897, 21, 65537, 720898, 21, 65537, 720899, 21, 65537, 720900, 21, 65537, 720901, 21, 65537, 720902, 21, 65537, 720903, 21, 65537, 720904, 21, 65537, 720905, 21, 65537, 720906, 21, 65537, 720907, 21, 65537, 720908, 21, 65537, 720909, 21, 65537, 720910, 21, 65538, 786432, 21, 65536, 786433, 21, 65537, 786434, 21, 65537, 786435, 21, 65537, 786436, 21, 65537, 786437, 21, 65537, 786438, 21, 65537, 786439, 21, 65537, 786440, 21, 65537, 786441, 21, 65537, 786442, 21, 65537, 786443, 21, 65537, 786444, 21, 65537, 786445, 21, 65537, 786446, 21, 65538, 851968, 21, 65536, 851969, 21, 65537, 851970, 21, 65537, 851971, 21, 65537, 851972, 21, 65537, 851973, 21, 65537, 851974, 21, 65537, 851975, 21, 65537, 851976, 21, 65537, 851977, 21, 65537, 851978, 21, 65537, 851979, 21, 65537, 851980, 21, 65537, 851981, 21, 65537, 851982, 21, 65538, 917504, 21, 65536, 917505, 21, 65537, 917506, 21, 65537, 917507, 21, 65537, 917508, 21, 65537, 917509, 21, 65537, 917510, 21, 65537, 917511, 21, 65537, 917512, 21, 65537, 917513, 21, 65537, 917514, 21, 65537, 917515, 21, 65537, 917516, 21, 65537, 917517, 21, 65537, 917518, 21, 65538, 983040, 21, 65536, 983041, 21, 65537, 983042, 21, 65537, 983043, 21, 65537, 983044, 21, 65537, 983045, 21, 65537, 983046, 21, 65537, 983047, 21, 65537, 983048, 21, 65537, 983049, 21, 65537, 983050, 21, 65537, 983051, 21, 65537, 983052, 21, 65537, 983053, 21, 65537, 983054, 21, 65538, 1048576, 21, 65536, 1048577, 21, 65537, 1048578, 21, 65537, 1048579, 21, 65537, 1048580, 21, 65537, 1048581, 21, 65537, 1048582, 21, 65537, 1048583, 21, 65537, 1048584, 21, 65537, 1048585, 21, 65537, 1048586, 21, 65537, 1048587, 21, 65537, 1048588, 21, 65537, 1048589, 21, 65537, 1048590, 21, 65538, 1114112, 21, 65536, 1114113, 21, 65537, 1114114, 21, 65537, 1114115, 21, 65537, 1114116, 21, 65537, 1114117, 21, 65537, 1114118, 21, 65537, 1114119, 21, 65537, 1114120, 21, 65537, 1114121, 21, 65537, 1114122, 21, 65537, 1114123, 21, 65537, 1114124, 21, 65537, 1114125, 21, 65537, 1114126, 21, 65538, 1179648, 21, 65536, 1179649, 21, 65537, 1179650, 21, 65537, 1179651, 21, 65537, 1179652, 21, 65537, 1179653, 21, 65537, 1179654, 21, 65537, 1179655, 21, 65537, 1179656, 21, 65537, 1179657, 21, 65537, 1179658, 21, 65537, 1179659, 21, 65537, 1179660, 21, 65537, 1179661, 21, 65537, 1179662, 21, 65538, 1245184, 21, 65536, 1245185, 21, 65537, 1245186, 21, 65537, 1245187, 21, 65537, 1245188, 21, 65537, 1245189, 21, 65537, 1245190, 21, 65537, 1245191, 21, 65537, 1245192, 21, 65537, 1245193, 21, 65537, 1245194, 21, 65537, 1245195, 21, 65537, 1245196, 21, 65537, 1245197, 21, 65537, 1245198, 21, 65538, 1310720, 21, 65536, 1310721, 21, 65537, 1310722, 21, 65537, 1310723, 21, 65537, 1310724, 21, 65537, 1310725, 21, 65537, 1310726, 21, 65537, 1310727, 21, 65537, 1310728, 21, 65537, 1310729, 21, 65537, 1310730, 21, 65537, 1310731, 21, 65537, 1310732, 21, 65537, 1310733, 21, 65537, 1310734, 21, 65538, 1376256, 21, 65536, 1376257, 21, 65537, 1376258, 21, 65537, 1376259, 21, 65537, 1376260, 21, 65537, 1376261, 21, 65537, 1376262, 21, 65537, 1376263, 21, 65537, 1376264, 21, 65537, 1376265, 21, 65537, 1376266, 21, 65537, 1376267, 21, 65537, 1376268, 21, 65537, 1376269, 21, 65537, 1376270, 21, 65538, 1441792, 21, 65536, 1441793, 21, 65537, 1441794, 21, 65537, 1441795, 21, 65537, 1441796, 21, 65537, 1441797, 21, 65537, 1441798, 21, 65537, 1441799, 21, 65537, 1441800, 21, 65537, 1441801, 21, 65537, 1441802, 21, 65537, 1441803, 21, 65537, 1441804, 21, 65537, 1441805, 21, 65537, 1441806, 21, 65538, 1507328, 21, 65536, 1507329, 21, 65537, 1507330, 21, 65537, 1507331, 21, 65537, 1507332, 21, 65537, 1507333, 21, 65537, 1507334, 21, 65537, 1507335, 21, 65537, 1507336, 21, 65537, 1507337, 21, 65537, 1507338, 21, 65537, 1507339, 21, 65537, 1507340, 21, 65537, 1507341, 21, 65537, 1507342, 21, 65538, 1572864, 21, 65536, 1572865, 21, 65537, 1572866, 21, 65537, 1572867, 21, 65537, 1572868, 21, 65537, 1572869, 21, 65537, 1572870, 21, 65537, 1572871, 21, 65537, 1572872, 21, 65537, 1572873, 21, 65537, 1572874, 21, 65537, 1572875, 21, 65537, 1572876, 21, 65537, 1572877, 21, 65537, 1572878, 21, 65538, 1638400, 21, 65536, 1638401, 21, 65537, 1638402, 21, 65537, 1638403, 21, 65537, 1638404, 21, 65537, 1638405, 21, 65537, 1638406, 21, 65537, 1638407, 21, 65537, 1638408, 21, 65537, 1638409, 21, 65537, 1638410, 21, 65537, 1638411, 21, 65537, 1638412, 21, 65537, 1638413, 21, 65537, 1638414, 21, 65538, 1703936, 21, 65536, 1703937, 21, 65537, 1703938, 21, 65537, 1703939, 21, 65537, 1703940, 21, 65537, 1703941, 21, 65537, 1703942, 21, 65537, 1703943, 21, 65537, 1703944, 21, 65537, 1703945, 21, 65537, 1703946, 21, 65537, 1703947, 21, 65537, 1703948, 21, 65537, 1703949, 21, 65537, 1703950, 21, 65538, 1769472, 21, 65536, 1769473, 21, 65537, 1769474, 21, 65537, 1769475, 21, 65537, 1769476, 21, 65537, 1769477, 21, 65537, 1769478, 21, 65537, 1769479, 21, 65537, 1769480, 21, 65537, 1769481, 21, 65537, 1769482, 21, 65537, 1769483, 21, 65537, 1769484, 21, 65537, 1769485, 21, 65537, 1769486, 21, 65538, 1835008, 21, 65536, 1835009, 21, 65537, 1835010, 21, 65537, 1835011, 21, 65537, 1835012, 21, 65537, 1835013, 21, 65537, 1835014, 21, 65537, 1835015, 21, 65537, 1835016, 21, 65537, 1835017, 21, 65537, 1835018, 21, 65537, 1835019, 21, 65537, 1835020, 21, 65537, 1835021, 21, 65537, 1835022, 21, 65538, 1900544, 21, 65536, 1900545, 21, 65537, 1900546, 21, 65537, 1900547, 21, 65537, 1900548, 21, 65537, 1900549, 21, 65537, 1900550, 21, 65537, 1900551, 21, 65537, 1900552, 21, 65537, 1900553, 21, 65537, 1900554, 21, 65537, 1900555, 21, 65537, 1900556, 21, 65537, 1900557, 21, 65537, 1900558, 21, 65538, 1966080, 21, 65536, 1966081, 21, 65537, 1966082, 21, 65537, 1966083, 21, 65537, 1966084, 21, 65537, 1966085, 21, 65537, 1966086, 21, 65537, 1966087, 21, 65537, 1966088, 21, 65537, 1966089, 21, 65537, 1966090, 21, 65537, 1966091, 21, 65537, 1966092, 21, 65537, 1966093, 21, 65537, 1966094, 21, 65538, 2031616, 21, 131072, 2031617, 21, 131073, 2031618, 21, 131073, 2031619, 21, 131073, 2031620, 21, 131073, 2031621, 21, 131073, 2031622, 21, 131073, 2031623, 21, 131073, 2031624, 21, 131073, 2031625, 21, 131073, 2031626, 21, 131073, 2031627, 21, 131073, 2031628, 21, 131073, 2031629, 21, 131073, 2031630, 21, 131074 ) -[node name="Foreground TileMap" type="TileMap" parent="Lower Cliffs" groups=[ +[node name="Ice" type="TileMap" parent="Lower Cliffs" groups=[ +"Cliffs", +"Ice", +]] +scale = Vector2( 8, 8 ) +tile_set = ExtResource( 3 ) +cell_size = Vector2( 16, 16 ) +cell_custom_transform = Transform2D( 0, 0, 0, 0, 0, 0 ) +collision_layer = 67 +collision_mask = 13 +format = 1 +tile_data = PoolIntArray( 131074, 33, 0, 131076, 31, 0, 131077, 31, 1, 131078, 31, 1, 131079, 31, 1, 131080, 31, 2, 131083, 31, 0, 131085, 33, 0, 131086, 34, 0, 196608, 31, 0, 196611, 31, 0, 196612, 31, 131072, 196613, 31, 131073, 196614, 31, 131073, 196615, 31, 131073, 196616, 31, 131074, 262144, 31, 0, 262145, 31, 2, 262147, 31, 0, 262153, 31, 0, 262156, 31, 0, 327680, 31, 131072, 327681, 31, 131074, 327688, 31, 0, 327689, 31, 2, 327691, 31, 0, 327692, 31, 2, 393216, 31, 0, 393221, 33, 0, 393224, 31, 131072, 393225, 31, 0, 393226, 31, 1, 393227, 31, 0, 393228, 31, 131074, 458761, 31, 65536, 458762, 31, 0, 458763, 31, 131074, 524291, 31, 0, 524292, 31, 2, 524297, 31, 131072, 524298, 31, 131074, 589827, 31, 131072, 589828, 31, 131074, 589832, 31, 0, 589833, 31, 2, 589837, 31, 0, 589838, 31, 2, 655360, 31, 0, 655368, 31, 131072, 655369, 31, 131074, 655373, 31, 65536, 655374, 31, 65538, 720896, 31, 0, 720897, 31, 0, 720898, 31, 2, 720909, 31, 65536, 720910, 31, 65538, 786433, 31, 131072, 786434, 31, 131074, 786445, 31, 131072, 786446, 31, 131074, 851972, 31, 0, 851973, 31, 1, 851974, 31, 2, 851980, 31, 0, 851981, 31, 2, 917508, 31, 131072, 917509, 31, 0, 917510, 31, 0, 917511, 31, 2, 917512, 31, 0, 917513, 31, 0, 917516, 31, 131072, 917517, 31, 0, 917518, 31, 2, 983045, 31, 131072, 983046, 31, 131073, 983047, 31, 131074, 983049, 31, 0, 983050, 31, 2, 983053, 31, 131072, 983054, 31, 131074, 1048584, 31, 0, 1048585, 31, 0, 1048586, 31, 131074, 1048589, 31, 0, 1114120, 31, 65536, 1114121, 31, 65538, 1114125, 31, 0, 1179656, 31, 131072, 1179657, 31, 131074, 1441802, 33, 0, 1441806, 31, 0, 1507333, 33, 0, 1507341, 31, 0, 1507342, 31, 2, 1572877, 31, 131072, 1572878, 31, 131074, 1638402, 33, 0, 1703947, 33, 0 ) + +[node name="Snow" type="TileMap" parent="Lower Cliffs" groups=[ "Cliffs", ]] scale = Vector2( 8, 8 ) @@ -697,14 +711,14 @@ tile_set = ExtResource( 3 ) cell_size = Vector2( 16, 16 ) collision_use_parent = true format = 1 -tile_data = PoolIntArray( -3735549, 2, 0, 131072, 23, 0, 131073, 22, 0, 131074, 22, 0, 131075, 22, 0, 131076, 22, 0, 131077, 22, 0, 131078, 22, 0, 131079, 22, 0, 131080, 22, 0, 131081, 22, 0, 131082, 22, 0, 131083, 22, 0, 131084, 22, 0, 131085, 22, 0, 131086, 24, 0 ) +tile_data = PoolIntArray( -3735549, 2, 0, 131072, 27, 0, 131073, 28, 0, 131074, 28, 0, 131075, 28, 0, 131076, 28, 0, 131077, 28, 0, 131078, 28, 0, 131079, 28, 0, 131080, 28, 0, 131081, 28, 0, 131082, 28, 0, 131083, 28, 0, 131084, 28, 0, 131085, 28, 0, 131086, 29, 0 ) [node name="CollisionShape2D" type="CollisionShape2D" parent="Lower Cliffs" groups=[ "Cliffs", ]] visible = false position = Vector2( 960, 2176 ) -shape = SubResource( 43 ) +shape = SubResource( 51 ) [node name="Lower Cliffs Top Ground Static Collider" type="StaticBody2D" parent="Lower Cliffs" groups=[ "Cliffs", @@ -722,7 +736,7 @@ collision_mask = 29 "Ground", ]] position = Vector2( 0, -1856 ) -shape = SubResource( 44 ) +shape = SubResource( 52 ) one_way_collision = true [node name="Lower Cliffs Top Ground Area Collider" type="Area2D" parent="Lower Cliffs" groups=[ @@ -738,7 +752,7 @@ collision_mask = 29 "Ground", ]] position = Vector2( 960, 320 ) -shape = SubResource( 45 ) +shape = SubResource( 53 ) [node name="Lower Cliffs Bottom Ground Static Collider" type="StaticBody2D" parent="Lower Cliffs" groups=[ "Cliffs", @@ -754,7 +768,7 @@ collision_mask = 29 "Ground", ]] position = Vector2( 960, 4032 ) -shape = SubResource( 46 ) +shape = SubResource( 54 ) one_way_collision = true [node name="Lower Cliffs Bottom Ground Area Collider" type="Area2D" parent="Lower Cliffs" groups=[ @@ -770,7 +784,7 @@ collision_mask = 29 "Ground", ]] position = Vector2( 960, 4160 ) -shape = SubResource( 47 ) +shape = SubResource( 55 ) [node name="Scene Boundary - Left" type="StaticBody2D" parent="."] visible = false @@ -784,7 +798,7 @@ __meta__ = { [node name="CollisionShape2D" type="CollisionShape2D" parent="Scene Boundary - Left"] position = Vector2( 128, -192 ) -shape = SubResource( 48 ) +shape = SubResource( 56 ) [node name="Scene Boundary - Right" type="StaticBody2D" parent="."] visible = false @@ -797,7 +811,7 @@ __meta__ = { [node name="CollisionShape2D" type="CollisionShape2D" parent="Scene Boundary - Right"] position = Vector2( 1984, -64 ) -shape = SubResource( 49 ) +shape = SubResource( 57 ) [node name="Scene Boundary - Top" type="StaticBody2D" parent="."] visible = false @@ -811,12 +825,16 @@ __meta__ = { [node name="CollisionShape2D" type="CollisionShape2D" parent="Scene Boundary - Top"] position = Vector2( 960, -4288 ) rotation = 1.5708 -shape = SubResource( 50 ) +shape = SubResource( 58 ) [node name="AudioStreamPlayer" type="AudioStreamPlayer" parent="."] stream = ExtResource( 6 ) volume_db = -10.156 -[connection signal="timeout" from="Player/ClimbingReadyTimer" to="Player" method="_OnClimbingReadyTimerTimeout"] + +[node name="AudioStreamPlayer2" type="AudioStreamPlayer" parent="."] +stream = ExtResource( 9 ) +volume_db = -21.932 + [connection signal="area_entered" from="Upper Cliffs" to="Upper Cliffs" method="_on_cliffs_area_entered"] [connection signal="area_exited" from="Upper Cliffs" to="Upper Cliffs" method="_on_cliffs_area_exited"] [connection signal="area_entered" from="Lower Cliffs" to="Lower Cliffs" method="_on_cliffs_area_entered"] diff --git a/music2_trimmed.wav b/music2_trimmed.wav new file mode 100644 index 0000000..80365b1 Binary files /dev/null and b/music2_trimmed.wav differ diff --git a/music2_trimmed.wav.import b/music2_trimmed.wav.import new file mode 100644 index 0000000..cfa4e19 --- /dev/null +++ b/music2_trimmed.wav.import @@ -0,0 +1,21 @@ +[remap] + +importer="wav" +type="AudioStreamSample" +path="res://.import/music2_trimmed.wav-bc809647170e7c4fe1e30c45e5dcf349.sample" + +[deps] + +source_file="res://music2_trimmed.wav" +dest_files=[ "res://.import/music2_trimmed.wav-bc809647170e7c4fe1e30c45e5dcf349.sample" ] + +[params] + +force/8_bit=false +force/mono=false +force/max_rate=false +force/max_rate_hz=44100 +edit/trim=false +edit/normalize=false +edit/loop=false +compress/mode=0 diff --git a/project.godot b/project.godot index dd0f4e3..6c276a9 100644 --- a/project.godot +++ b/project.godot @@ -10,7 +10,6 @@ config_version=4 _global_script_classes=[ ] _global_script_class_icons={ - } [application] @@ -86,6 +85,7 @@ respawn={ quality/intended_usage/framebuffer_allocation=1 quality/intended_usage/framebuffer_allocation.mobile=1 -quality/2d/use_pixel_snap=true +2d/snapping/use_gpu_pixel_snap=true environment/default_environment="res://default_env.tres" quality/dynamic_fonts/use_oversampling=false +quality/2d/use_pixel_snap=true diff --git a/pyxel/cliffs3-tileset.pyxel b/pyxel/cliffs3-tileset.pyxel index 8236e7e..7531323 100644 Binary files a/pyxel/cliffs3-tileset.pyxel and b/pyxel/cliffs3-tileset.pyxel differ diff --git a/pyxel/klas3-animations.pyxel b/pyxel/klas3-animations.pyxel index b175124..851817b 100644 Binary files a/pyxel/klas3-animations.pyxel and b/pyxel/klas3-animations.pyxel differ