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 pos = new List(); public List followers = new List(); 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("DWCover"); soul = DWOverlay.GetNode("Soul"); soulHitbox = soul.GetNode("Hitbox"); overlayAnim = DWOverlay.GetNode("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 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 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); } }