216 lines
8.4 KiB
C#
216 lines
8.4 KiB
C#
|
|
using Godot;
|
||
|
|
using gobbos_logic;
|
||
|
|
|
||
|
|
public partial class Player : CharacterBody2D, IPlayer
|
||
|
|
{
|
||
|
|
[Signal]
|
||
|
|
public delegate void BlinkTimerTimeoutEventHandler();
|
||
|
|
|
||
|
|
[Signal]
|
||
|
|
public delegate void WallCastDelayTimeoutEventHandler();
|
||
|
|
|
||
|
|
// Constants
|
||
|
|
const double COYOTE_JUMP_WINDOW = 0.15;
|
||
|
|
|
||
|
|
// private readonly Vector2 TILE_SIZE = new(8, 8);
|
||
|
|
|
||
|
|
[Export]
|
||
|
|
public AnimationPlayer Blinkplayer { get; set; }
|
||
|
|
|
||
|
|
private double timeSinceLeavingGround = 0.0;
|
||
|
|
|
||
|
|
public bool CanCoyoteJump => timeSinceLeavingGround < COYOTE_JUMP_WINDOW;
|
||
|
|
|
||
|
|
public void ResetCoyoteTimer()
|
||
|
|
{
|
||
|
|
timeSinceLeavingGround = 0.0;
|
||
|
|
}
|
||
|
|
|
||
|
|
public float Acceleration { get; set; } = 4.0f;
|
||
|
|
public float Friction { get; set; } = 4.0f;
|
||
|
|
public float AirFriction { get; set; } = 0.1f;
|
||
|
|
public float WallFriction { get; set; } = 1200.0f;
|
||
|
|
public int Speed { get; set; } = 40;
|
||
|
|
public int WallSpeed { get; set; } = 15;
|
||
|
|
public float SlowMod { get; set; } = 0.5f;
|
||
|
|
public int Gravity { get; set; } = 250;
|
||
|
|
public int Jump { get; set; } = -95;
|
||
|
|
public int WallJump { get; set; } = 55;
|
||
|
|
public int LedgeJump { get; set; } = -70;
|
||
|
|
public float FallMod { get; set; } = 1.5f;
|
||
|
|
public float ParachuteMod { get; set; } = 0.2f;
|
||
|
|
public bool OnLedge { get; set; } = false;
|
||
|
|
public bool InParachute { get; set; } = false;
|
||
|
|
public bool CanParachute { get; set; } = false;
|
||
|
|
|
||
|
|
public Vector2 AnimDir { get; set; } = Vector2.Zero;
|
||
|
|
public Vector2 LastDir { get; set; } = Vector2.Zero;
|
||
|
|
public Vector2 MoveDir { get; set; } = Vector2.Zero;
|
||
|
|
public Label StateLabel { get; set; }
|
||
|
|
public RayCast2D WallCastTop { get; set; }
|
||
|
|
public RayCast2D WallCastMid { get; set; }
|
||
|
|
public RayCast2D WallCastLow { get; set; }
|
||
|
|
public AnimationNodeStateMachinePlayback AnimStateMachine { get; set; }
|
||
|
|
public AnimationNodeStateMachinePlayback GroundedStateMachine { get; set; }
|
||
|
|
public AnimationNodeStateMachinePlayback AirborneStateMachine { get; set; }
|
||
|
|
public AnimationNodeStateMachinePlayback SprintStateMachine { get; set; }
|
||
|
|
public float LastVelocityY { get; set; } = 0.0f;
|
||
|
|
public float WallCastLength { get; set; } = 3.8f;
|
||
|
|
public Timer WallCastDelayTimer { get; set; }
|
||
|
|
|
||
|
|
private RayCast2D cameraOffsetCast;
|
||
|
|
private RayCast2D cameraDownCast;
|
||
|
|
private Node2D modelContainer;
|
||
|
|
private Timer blinkTimer;
|
||
|
|
public AnimationTree animTree;
|
||
|
|
private StateMachine stateMachine;
|
||
|
|
|
||
|
|
public override void _Ready()
|
||
|
|
{
|
||
|
|
modelContainer = GetNode<Node2D>("ModelContainer");
|
||
|
|
blinkTimer = GetNode<Timer>("BlinkTimer");
|
||
|
|
animTree = GetNode<AnimationTree>("AnimationTree");
|
||
|
|
cameraOffsetCast = GetNode<RayCast2D>("CameraOffsetCast");
|
||
|
|
cameraDownCast = GetNode<RayCast2D>("CameraOffsetCast/CameraDownCast");
|
||
|
|
stateMachine = GetNode<StateMachine>("StateMachine");
|
||
|
|
animTree.Set("parameters/Airborne/Standard/blend_position", 1.0f);
|
||
|
|
animTree.Set("parameters/Airborne/Parachute/blend_position", 1.0f);
|
||
|
|
|
||
|
|
WallCastTop = GetNode<RayCast2D>("WallCastTop");
|
||
|
|
WallCastMid = GetNode<RayCast2D>("WallCastMid");
|
||
|
|
WallCastLow = GetNode<RayCast2D>("WallCastLow");
|
||
|
|
WallCastDelayTimer = GetNode<Timer>("WallCastDelay");
|
||
|
|
WallCastDelayTimer.Timeout += OnWallCastDelayTimeout;
|
||
|
|
AnimStateMachine = (AnimationNodeStateMachinePlayback)animTree.Get("parameters/playback");
|
||
|
|
GroundedStateMachine = (AnimationNodeStateMachinePlayback)animTree.Get("parameters/Grounded/playback");
|
||
|
|
AirborneStateMachine = (AnimationNodeStateMachinePlayback)animTree.Get("parameters/Airborne/playback");
|
||
|
|
SprintStateMachine = (AnimationNodeStateMachinePlayback)animTree.Get("parameters/Grounded/Sprint/playback");
|
||
|
|
StateLabel = GetNode<Label>("StateLabel");
|
||
|
|
}
|
||
|
|
|
||
|
|
public override void _PhysicsProcess(double delta)
|
||
|
|
{
|
||
|
|
var floatDelta = (float)delta;
|
||
|
|
Vector2 inputDir = new(Input.GetAxis("move_left", "move_right"), Input.GetAxis("move_up", "move_down"));
|
||
|
|
|
||
|
|
if (inputDir.X != 0.0f)
|
||
|
|
{
|
||
|
|
MoveDir = new(Mathf.MoveToward(MoveDir.X, inputDir.X, 20 * floatDelta), MoveDir.Y);
|
||
|
|
LastDir = new(inputDir.X, LastDir.Y);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
MoveDir = new Vector2(
|
||
|
|
Mathf.MoveToward(MoveDir.X, 0.0f, 20 * floatDelta),
|
||
|
|
MoveDir.Y);
|
||
|
|
}
|
||
|
|
|
||
|
|
if (inputDir.Y != 0.0f)
|
||
|
|
{
|
||
|
|
MoveDir = new(MoveDir.X, Mathf.MoveToward(MoveDir.Y, inputDir.Y, 20 * floatDelta));
|
||
|
|
LastDir = new(LastDir.X, inputDir.Y);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
MoveDir = new(MoveDir.X, Mathf.MoveToward(MoveDir.Y, 0.0f, 20 * floatDelta));
|
||
|
|
}
|
||
|
|
|
||
|
|
AnimDir = new(Mathf.MoveToward(AnimDir.X, LastDir.X, 20 * floatDelta), AnimDir.Y);
|
||
|
|
|
||
|
|
// Set wall cast positions based on input direction
|
||
|
|
if (inputDir.X > 0)
|
||
|
|
{
|
||
|
|
WallCastTop.TargetPosition = new(WallCastLength, WallCastTop.TargetPosition.Y);
|
||
|
|
WallCastMid.TargetPosition = new(WallCastLength, WallCastMid.TargetPosition.Y);
|
||
|
|
WallCastLow.TargetPosition = new(WallCastLength, WallCastLow.TargetPosition.Y);
|
||
|
|
}
|
||
|
|
else if (inputDir.X < 0)
|
||
|
|
{
|
||
|
|
WallCastTop.TargetPosition = new(0.0f - WallCastLength, WallCastTop.TargetPosition.Y);
|
||
|
|
WallCastMid.TargetPosition = new(0.0f - WallCastLength, WallCastMid.TargetPosition.Y);
|
||
|
|
WallCastLow.TargetPosition = new(0.0f - WallCastLength, WallCastLow.TargetPosition.Y);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
WallCastTop.TargetPosition = new(0.0f, WallCastTop.TargetPosition.Y);
|
||
|
|
WallCastMid.TargetPosition = new(0.0f, WallCastMid.TargetPosition.Y);
|
||
|
|
WallCastLow.TargetPosition = new(0.0f, WallCastLow.TargetPosition.Y);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Update animation parameters
|
||
|
|
animTree.Set("parameters/Airborne/Standard/0/blend_position", Velocity.Y);
|
||
|
|
animTree.Set("parameters/Airborne/Standard/1/blend_position", Velocity.Y);
|
||
|
|
animTree.Set("parameters/Airborne/Parachute/0/blend_position", Velocity.Y);
|
||
|
|
animTree.Set("parameters/Airborne/Parachute/1/blend_position", Velocity.Y);
|
||
|
|
|
||
|
|
// Create tweens for animation blending
|
||
|
|
var idleBlend = GetTree().CreateTween();
|
||
|
|
var walkBlend = GetTree().CreateTween();
|
||
|
|
var airBlend = GetTree().CreateTween();
|
||
|
|
var parachuteBlend = GetTree().CreateTween();
|
||
|
|
|
||
|
|
idleBlend.TweenProperty(animTree, "parameters/Grounded/Idle/blend_position", AnimDir.X, 0.2f);
|
||
|
|
walkBlend.TweenProperty(animTree, "parameters/Grounded/Walk/blend_position", AnimDir.X, 0.2f);
|
||
|
|
airBlend.TweenProperty(animTree, "parameters/Airborne/Standard/blend_position", AnimDir.X, 0.2f);
|
||
|
|
parachuteBlend.TweenProperty(animTree, "parameters/Airborne/Parachute/blend_position", AnimDir.X, 0.5f);
|
||
|
|
|
||
|
|
TiltModel(floatDelta);
|
||
|
|
CameraOffset();
|
||
|
|
|
||
|
|
// Increment time since leaving ground if not on floor
|
||
|
|
if (!IsOnFloor())
|
||
|
|
{
|
||
|
|
timeSinceLeavingGround += delta;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Call StateMachine PhysicsUpdate to handle state-specific movement
|
||
|
|
stateMachine?.PhysicsUpdate(floatDelta);
|
||
|
|
|
||
|
|
MoveAndSlide();
|
||
|
|
}
|
||
|
|
|
||
|
|
private void CameraOffset()
|
||
|
|
{
|
||
|
|
var targetX = Mathf.Lerp(cameraOffsetCast.TargetPosition.X, AnimDir.X * 50.0f, 0.6f);
|
||
|
|
cameraOffsetCast.TargetPosition = new(targetX, cameraOffsetCast.TargetPosition.Y);
|
||
|
|
|
||
|
|
if (cameraOffsetCast.IsColliding())
|
||
|
|
{
|
||
|
|
cameraDownCast.Position = cameraOffsetCast.GetCollisionPoint();
|
||
|
|
cameraDownCast.Position = new(cameraDownCast.Position.X, cameraDownCast.Position.Y + 1);
|
||
|
|
}
|
||
|
|
else
|
||
|
|
{
|
||
|
|
cameraDownCast.Position = cameraOffsetCast.GlobalPosition + cameraOffsetCast.TargetPosition;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
private void TiltModel(float delta)
|
||
|
|
{
|
||
|
|
var floorNormal = GetFloorNormal();
|
||
|
|
var tilt = 0.0f;
|
||
|
|
|
||
|
|
if (floorNormal != new Vector2(0.0f, -1.0f))
|
||
|
|
{
|
||
|
|
tilt = Mathf.Atan2(floorNormal.X, floorNormal.Y);
|
||
|
|
tilt /= 10;
|
||
|
|
}
|
||
|
|
|
||
|
|
modelContainer.Rotation = Mathf.Lerp(modelContainer.Rotation, tilt, 5 * delta);
|
||
|
|
}
|
||
|
|
|
||
|
|
private void OnBlinkTimerTimeout()
|
||
|
|
{
|
||
|
|
Blinkplayer?.Play("blink");
|
||
|
|
var randomWaitTime = GD.RandRange(1.0f, 7.0f);
|
||
|
|
blinkTimer?.WaitTime = randomWaitTime;
|
||
|
|
}
|
||
|
|
|
||
|
|
private void OnWallCastDelayTimeout()
|
||
|
|
{
|
||
|
|
WallCastTop.Enabled = true;
|
||
|
|
WallCastMid.Enabled = true;
|
||
|
|
WallCastLow.Enabled = true;
|
||
|
|
}
|
||
|
|
}
|