472 lines
9.8 KiB
C#
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);
|
|
}
|
|
|
|
}
|