Files
gobbo/gobbos_delivery/scripts/Player.cs
2026-04-10 17:53:31 -05:00

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;
}
}