2025-05-13 19:22:01 +08:00

472 lines
9.8 KiB
C#

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using Godot;
using Godot.Bridge;
using Godot.NativeInterop;
[ScriptPath("res://Scripts/Entities/Player.cs")]
public partial class Player : Entity
{
[Signal]
public delegate void WasHurtEventHandler();
[Export(PropertyHint.None, "")]
public Sprite2D DWOverlay;
[Export(PropertyHint.None, "")]
public Sprite2D soul;
[Export(PropertyHint.None, "")]
public Area2D soulHitbox;
[Export(PropertyHint.None, "")]
private AnimationPlayer overlayAnim;
private ShapeCast2D interact;
public static Player instance;
[Export(PropertyHint.None, "")]
public bool canInput;
public bool run;
public bool waitForMove;
public bool lockMenu;
public float inputCD;
public float dangerZoneCD;
public float iFrames;
public float stepCD;
public DangerZone inDZone;
public Entity controlling;
public Entity splitEntity;
public List<Vector2> pos = new List<Vector2>();
public List<Follower> followers = new List<Follower>();
public Node2D bullet;
public Node2D onPlatform;
private static readonly Vector2I interactOffset = new Vector2I(0, -4);
public Slide slide;
private const float interactPos = 8f;
public const int maxPos = 100;
public Vector2 lastDelta = Vector2.Down;
private bool loaded;
public override void _EnterTree()
{
if (!loaded)
{
base._EnterTree();
base.ProcessPriority = -1;
instance = this;
DWOverlay = GetNode<Sprite2D>("DWCover");
soul = DWOverlay.GetNode<Sprite2D>("Soul");
soulHitbox = soul.GetNode<Area2D>("Hitbox");
overlayAnim = DWOverlay.GetNode<AnimationPlayer>("Anim");
for (int i = 0; i < 100; i++)
{
pos.Add(base.GlobalPosition);
}
if (!GodotObject.IsInstanceValid(interact))
{
ShapeCast2D obj = new ShapeCast2D
{
Shape = new RectangleShape2D
{
Size = new Vector2(16f, 20f)
},
Enabled = false,
TargetPosition = Vector2.Zero,
CollideWithAreas = true,
CollideWithBodies = true
};
ShapeCast2D node = obj;
interact = obj;
AddChild(node, forceReadableName: false, InternalMode.Disabled);
}
CameraController.instance.follow = this;
controlling = this;
Party.SpawnFollowers();
if (SaveFile.current.flags.Contains(SaveFile.Flags.IsInDW))
{
Party.AnimMod(AnimMods.DW);
}
soulHitbox.AreaEntered += Damage;
soulHitbox.CollisionLayer = 8u;
soulHitbox.CollisionMask = 8u;
Room.current?.entities.Add(this);
loaded = true;
}
}
public void FollowerSetUp()
{
ResetFollowStep();
for (int i = 0; i < followers.Count; i++)
{
followers[i].GlobalPosition = base.GlobalPosition + Vector2.Up * 0.01f;
if (SaveFile.current.flags.Contains(SaveFile.Flags.IsInDW))
{
followers[i].animMod = AnimMods.DW;
followers[i].UpdateAnim(force: true);
}
}
}
public void PartyStop()
{
Party.StopMoving();
}
public void StopInput()
{
canInput = false;
}
public override void _Process(double delta)
{
base._Process(delta);
if (inputCD > 0f)
{
inputCD -= Main.deltaTime;
}
if (iFrames > 0f)
{
iFrames -= Main.deltaTime;
}
if (stepCD > 0f && base.Velocity.LengthSquared() > 0f)
{
stepCD -= Main.deltaTime;
}
if (dangerZoneCD > 0f)
{
UpdateOverlay();
dangerZoneCD -= Main.deltaTime;
DWOverlay.Modulate = DWOverlay.Modulate.Lerp(Main.colorWhite, Main.deltaTime * 0.2f);
sprite.SelfModulate = sprite.SelfModulate.Lerp(Main.colorClear, Main.deltaTime * 0.2f);
}
else
{
DWOverlay.Modulate = DWOverlay.Modulate.Lerp(Main.colorClear, Main.deltaTime * 0.2f);
sprite.SelfModulate = sprite.SelfModulate.Lerp(Main.colorWhite, Main.deltaTime * 0.2f);
}
if (Main.inEvent == null)
{
if (canInput)
{
DoInput();
}
else
{
waitForMove = true;
}
}
}
public void UpdateOverlay()
{
DWOverlay.FlipH = sprite.FlipH;
if (overlayAnim.HasAnimation(anim.CurrentAnimation))
{
overlayAnim.Play(anim.CurrentAnimation);
}
}
public void DelayedColCheck()
{
Party.CheckColliders();
}
public static void AssumeControl(Entity other, bool playParticle = true, bool updateCamera = true)
{
ReleaseControl();
instance.controlling = other;
other.CollisionMask = 1u;
other.CollisionLayer = 1u;
other.StopMoving();
if (updateCamera)
{
CameraController.instance.follow = other;
}
CameraController.instance.offset = CameraController.defaultOffset;
Party.StopMoving();
if (playParticle)
{
Party.PlaySplitParticle(other);
}
UpdateLastDir();
}
public static void UpdateSplitEntity()
{
if (instance.controlling == instance)
{
instance.splitEntity = null;
instance.waitForMove = true;
}
else
{
instance.splitEntity = instance.controlling;
}
}
public static void ReleaseControl()
{
if (instance.controlling != null)
{
instance.controlling.StopMoving();
if (instance.controlling is Follower)
{
instance.controlling.CollisionLayer = 524288u;
instance.controlling.CollisionMask = 524288u;
}
else if (instance.controlling == instance)
{
instance.CollisionLayer = 1u;
instance.CollisionMask = 1u;
}
instance.controlling = null;
}
}
public override void _PhysicsProcess(double delta)
{
MoveAndSlide();
}
public override void LateUpdate()
{
base.LateUpdate();
if (Main.inEvent == null)
{
base.GlobalPosition = base.GlobalPosition.Round();
}
if (interact.Enabled)
{
interact.Enabled = false;
}
}
private void DoInput()
{
Vector2 vector = Main.GetDirection();
if (vector != Vector2.Zero)
{
vector = vector.Normalized() * baseSpeed;
if (Input.IsActionPressed(Main.keys[5]))
{
run = !Settings.file.run;
}
else
{
run = Settings.file.run;
}
if (run)
{
vector *= 2f;
}
lastDelta = vector.Normalized();
AddFollowerStep();
controlling.LookAt(controlling.GlobalPosition + lastDelta);
controlling.currentAnim = Animations.Walk;
if (Room.current.stepSound != null && stepCD <= 0f && slide == null)
{
Audio.PlaySound(Room.current.stepSound, 1.5f);
stepCD = (run ? 20 : 30);
}
waitForMove = false;
}
else
{
run = false;
controlling.currentAnim = Animations.Idle;
}
if (slide != null)
{
controlling.Velocity = new Vector2(vector.X, baseSpeed * 2f);
controlling.currentAnim = Animations.Slide;
}
else
{
controlling.Velocity = vector;
}
if (GodotObject.IsInstanceValid(bullet))
{
return;
}
if (Input.IsActionJustPressed(Main.keys[4]) && inputCD <= 0f && dangerZoneCD <= 0f && onPlatform == null)
{
interact.GlobalPosition = controlling.GlobalPosition + lastDelta * 8f + interactOffset;
interact.Enabled = true;
interact.ForceShapecastUpdate();
if (interact.GetCollisionCount() > 0)
{
for (int i = 0; i < interact.GetCollisionCount(); i++)
{
if (interact.GetCollider(i) is Interacteable interacteable)
{
canInput = false;
interacteable.Interact();
return;
}
if (((Node)interact.GetCollider(i)).GetParent() is Interacteable interacteable2)
{
canInput = false;
interacteable2.Interact();
return;
}
}
}
if (Room.current.waterBullet > 0 && controlling == this)
{
Main.inEvent = Coroutine.Start(CommonEvents.ShootWater());
}
}
else
{
if (!Input.IsActionJustPressed(Main.keys[6]) || !(dangerZoneCD <= 0f) || !(inputCD <= 0f) || slide != null || lockMenu || GodotObject.IsInstanceValid(bullet) || onPlatform != null)
{
return;
}
if (splitEntity == null)
{
if (animMod == AnimMods.DW || SaveFile.current.flags.Contains(SaveFile.Flags.IsInDW))
{
if (!DWMenu.instance.closing)
{
DWMenu.instance.Open();
}
}
else
{
LWMenu.instance.Open();
}
}
else
{
inputCD = 30f;
if (controlling == splitEntity)
{
AssumeControl(instance);
}
else
{
AssumeControl(splitEntity);
}
}
}
}
public void AddFollowerStep(bool update = false)
{
Vector2 globalPosition = base.GlobalPosition;
List<Vector2> list = pos;
if (list[list.Count - 1] != globalPosition)
{
pos.Add(globalPosition);
if (pos.Count >= 100)
{
pos.RemoveAt(0);
}
}
if (update)
{
for (int i = 0; i < followers.Count; i++)
{
followers[i].DoMovement(force: true);
}
}
}
public IEnumerator ResetInput(bool value = true, int waitFrames = 2)
{
for (int i = 0; i < waitFrames; i++)
{
yield return null;
}
canInput = value;
}
public void ChangeInput(bool value = true)
{
canInput = value;
}
public void ResetFollowStep()
{
Vector2 globalPosition = base.GlobalPosition;
for (int i = 0; i < pos.Count; i++)
{
pos[i] = globalPosition;
}
}
public void EndInteraction()
{
inputCD = 20f;
canInput = true;
}
public static void UpdateLastDir()
{
instance.lastDelta = Entity.directions.First((KeyValuePair<Vector2, Direction> x) => x.Value == instance.controlling.direction).Key;
}
private void Damage(Node other)
{
if (iFrames <= 0f && Room.current.lifeTime >= 60f)
{
Audio.PlaySound("snd_hurt1.wav");
CameraController.instance.shakeIntensity = 3f;
CameraController.instance.shakeTime = 5f;
Party.ChangeHP(-10);
if (Party.AlivePartyAmount() == 0)
{
Main.inEvent = Coroutine.Start(GameOver.DoGameOver());
Room.current.CallDeferred("DisableProcess");
return;
}
Party.ReviveAll(fullHP: false);
Party.UpdateHUD();
BattleDR.ShowFloatingText("10", base.GlobalPosition).ProcessMode = ProcessModeEnum.Always;
EmitSignal(SignalName.WasHurt);
iFrames = 30f;
DWMenu.instance.showHP = 180f;
}
}
public static void SetActive(bool state, bool alsoFollowers = true)
{
if (alsoFollowers)
{
for (int i = 0; i < instance.followers.Count; i++)
{
Main.SetActive(instance.followers[i], state);
}
}
Main.SetActive(instance, state);
}
}