317 lines
6.9 KiB
C#
317 lines
6.9 KiB
C#
using System.Collections;
|
|
using System.Collections.Generic;
|
|
using System.ComponentModel;
|
|
using System.Linq;
|
|
using Godot;
|
|
using Godot.Bridge;
|
|
using Godot.Collections;
|
|
using Godot.NativeInterop;
|
|
|
|
[ScriptPath("res://Scripts/Entities/Enemy.cs")]
|
|
public partial class Enemy : Entity
|
|
{
|
|
public enum MovementType
|
|
{
|
|
None,
|
|
Wander,
|
|
LoopAround,
|
|
BackAndForth,
|
|
Circle
|
|
}
|
|
|
|
public enum Respawn
|
|
{
|
|
No,
|
|
NoMoney,
|
|
HalfMoney
|
|
}
|
|
|
|
public enum State
|
|
{
|
|
Unaware,
|
|
Noticed,
|
|
Chase
|
|
}
|
|
|
|
[Export(PropertyHint.None, "")]
|
|
private MovementType movementType = MovementType.Wander;
|
|
|
|
[Export(PropertyHint.None, "")]
|
|
private Vector2 circleMulti = Vector2.One;
|
|
|
|
[Export(PropertyHint.None, "")]
|
|
private float radius = 120f;
|
|
|
|
[Export(PropertyHint.None, "")]
|
|
private float moveTime = 100f;
|
|
|
|
[Export(PropertyHint.None, "")]
|
|
private Array<IDs> additionalEnemies;
|
|
|
|
[Export(PropertyHint.None, "")]
|
|
private StringName battleMes = "Test";
|
|
|
|
[Export(PropertyHint.None, "")]
|
|
public SaveFile.Flags flag;
|
|
|
|
[Export(PropertyHint.None, "")]
|
|
public SaveFile.Flags spawn = SaveFile.Flags.None;
|
|
|
|
[Export(PropertyHint.None, "")]
|
|
public SaveFile.Flags despawn = SaveFile.Flags.None;
|
|
|
|
[Export(PropertyHint.None, "")]
|
|
private Node2D[] points;
|
|
|
|
[Export(PropertyHint.None, "")]
|
|
private int current;
|
|
|
|
[Export(PropertyHint.None, "")]
|
|
public Respawn respawnType;
|
|
|
|
[Export(PropertyHint.None, "")]
|
|
private bool smooth = true;
|
|
|
|
[Export(PropertyHint.None, "")]
|
|
private bool immuneToWater;
|
|
|
|
private static readonly AudioStream noticedSound = GD.Load<AudioStream>("res://Audio/Sounds/snd_b.wav");
|
|
|
|
private Vector2 last;
|
|
|
|
private Vector2 next;
|
|
|
|
private State state;
|
|
|
|
private float cooldown = 180f;
|
|
|
|
private float noticeCD;
|
|
|
|
private float circleState;
|
|
|
|
private bool back;
|
|
|
|
public static readonly StringName waterTag = "WaterBullet";
|
|
|
|
public override void _EnterTree()
|
|
{
|
|
base._EnterTree();
|
|
if ((SaveFile.current.flags.Contains(flag) && respawnType == Respawn.No) || !SaveFile.CanDo(new Array<SaveFile.Flags> { spawn }, new Array<SaveFile.Flags> { despawn }))
|
|
{
|
|
QueueFree();
|
|
return;
|
|
}
|
|
if (movementType == MovementType.LoopAround || movementType == MovementType.BackAndForth)
|
|
{
|
|
cooldown = moveTime;
|
|
}
|
|
trigger.BodyEntered += OnTouch;
|
|
trigger.AreaEntered += AreaEnter;
|
|
trigger.CollisionMask = 129u;
|
|
Node2D[] array = points;
|
|
if (array != null && array.Length != 0)
|
|
{
|
|
last = points[current].GlobalPosition;
|
|
int num = (int)Main.Repeat(current + 1, points.Length);
|
|
next = points[num].GlobalPosition;
|
|
current = num;
|
|
}
|
|
}
|
|
|
|
public override void _Process(double delta)
|
|
{
|
|
if (TextSystem.instance.Visible)
|
|
{
|
|
return;
|
|
}
|
|
base._Process(delta);
|
|
if (noticeCD > 0f)
|
|
{
|
|
noticeCD -= Main.deltaTime;
|
|
}
|
|
switch (state)
|
|
{
|
|
case State.Unaware:
|
|
if (noticeCD > 0f)
|
|
{
|
|
break;
|
|
}
|
|
if (radius > 0f && base.GlobalPosition.DistanceTo(Player.instance.GlobalPosition) < radius)
|
|
{
|
|
cooldown = 30f;
|
|
StopMoving(Animations.Idle);
|
|
Emoticon(Emoticons.Exclaim);
|
|
Audio.PlaySound(noticedSound);
|
|
state = State.Noticed;
|
|
break;
|
|
}
|
|
switch (movementType)
|
|
{
|
|
case MovementType.Circle:
|
|
{
|
|
circleState = Main.Repeat(circleState + Main.deltaTime, moveTime);
|
|
float s = Mathf.DegToRad(Mathf.Lerp(0f, 360f, circleState / moveTime));
|
|
Vector2 vector = startPos + new Vector2(Mathf.Cos(s) * circleMulti.X, Mathf.Sin(s) * circleMulti.Y);
|
|
LookAt(vector);
|
|
base.GlobalPosition = base.GlobalPosition.Lerp(vector, Main.deltaTime * 0.065f);
|
|
break;
|
|
}
|
|
case MovementType.None:
|
|
StopMoving();
|
|
break;
|
|
default:
|
|
if (moving == null)
|
|
{
|
|
if (cooldown >= 0f)
|
|
{
|
|
currentAnim = Animations.Idle;
|
|
cooldown -= Main.deltaTime;
|
|
}
|
|
else
|
|
{
|
|
DoAutoMove(startPos + new Vector2(Main.RandomRange(0f - radius, radius), Main.RandomRange(0f - radius, radius)) / 3f, 0.5f);
|
|
cooldown = Main.RandomRange(120, 250);
|
|
}
|
|
}
|
|
break;
|
|
case MovementType.LoopAround:
|
|
case MovementType.BackAndForth:
|
|
base.Velocity = Vector2.Zero;
|
|
if (cooldown >= 0f)
|
|
{
|
|
currentAnim = Animations.Walk;
|
|
float weight = 1f - cooldown / moveTime;
|
|
if (smooth)
|
|
{
|
|
weight = Mathf.SmoothStep(0f, 1f, weight);
|
|
}
|
|
if (radius < 10f)
|
|
{
|
|
base.GlobalPosition = last.Lerp(next, weight);
|
|
}
|
|
else
|
|
{
|
|
base.GlobalPosition = base.GlobalPosition.Lerp(last.Lerp(next, weight), Main.deltaTime * 0.1f);
|
|
}
|
|
cooldown -= Main.deltaTime;
|
|
LookAt(next);
|
|
break;
|
|
}
|
|
currentAnim = Animations.Idle;
|
|
last = base.GlobalPosition;
|
|
if (back)
|
|
{
|
|
current--;
|
|
if (current <= 0)
|
|
{
|
|
back = false;
|
|
current = 0;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
current++;
|
|
if (current == points.Length)
|
|
{
|
|
if (movementType == MovementType.BackAndForth)
|
|
{
|
|
back = true;
|
|
current -= 2;
|
|
if (current < 0)
|
|
{
|
|
current = 0;
|
|
back = false;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
current = 0;
|
|
}
|
|
}
|
|
}
|
|
next = points[current].GlobalPosition;
|
|
cooldown = moveTime;
|
|
break;
|
|
}
|
|
break;
|
|
case State.Noticed:
|
|
if (cooldown > 0f)
|
|
{
|
|
LookAt(Player.instance.GlobalPosition);
|
|
cooldown -= Main.deltaTime;
|
|
}
|
|
else
|
|
{
|
|
state = State.Chase;
|
|
}
|
|
break;
|
|
case State.Chase:
|
|
if (base.GlobalPosition.DistanceTo(Player.instance.GlobalPosition) < radius && base.GlobalPosition.DistanceTo(startPos) < radius)
|
|
{
|
|
currentAnim = Animations.Walk;
|
|
base.Velocity = base.GlobalPosition.DirectionTo(Player.instance.GlobalPosition) * baseSpeed;
|
|
LookAt(Player.instance.GlobalPosition);
|
|
}
|
|
else
|
|
{
|
|
cooldown = 30f;
|
|
noticeCD = 30f;
|
|
state = State.Unaware;
|
|
StopMoving(Animations.Idle);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
public override void _PhysicsProcess(double delta)
|
|
{
|
|
if (!TextSystem.instance.Visible)
|
|
{
|
|
base._PhysicsProcess(delta);
|
|
}
|
|
}
|
|
|
|
private void OnTouch(Node other)
|
|
{
|
|
if (other is Player && Room.current.lifeTime >= 60f && base.ProcessMode == ProcessModeEnum.Inherit)
|
|
{
|
|
additionalEnemies.Insert(0, id);
|
|
Coroutine.Start(BattleDR.StartBattle(this, useOWBG: false, additionalEnemies.ToArray(), new List<Entity> { this }, battleMes));
|
|
}
|
|
}
|
|
|
|
private void AreaEnter(Node other)
|
|
{
|
|
if (other.HasMeta(waterTag))
|
|
{
|
|
if (!immuneToWater)
|
|
{
|
|
Coroutine.Start(HitByWater(((Node2D)other).GlobalPosition.X < base.GlobalPosition.X), this);
|
|
anim.ProcessMode = ProcessModeEnum.Always;
|
|
anim.Play("Hurt");
|
|
Audio.PlaySound("snd_bomb.wav");
|
|
SetDeferred("process_mode", 4);
|
|
immuneToWater = true;
|
|
}
|
|
Main.Particle("WaterBurst", other.GetParent<Node2D>().GlobalPosition, 1, null, localSpace: false);
|
|
other.GetParent().QueueFree();
|
|
}
|
|
}
|
|
|
|
private IEnumerator HitByWater(bool fromLeft)
|
|
{
|
|
Vector2 dir = new Vector2(fromLeft ? 1 : (-1), -1f) * 2f;
|
|
for (float a = 0f; a < 300f; a += Main.deltaTime)
|
|
{
|
|
if (!GodotObject.IsInstanceValid(this))
|
|
{
|
|
yield break;
|
|
}
|
|
base.GlobalPosition += dir * Main.deltaTime;
|
|
yield return null;
|
|
}
|
|
QueueFree();
|
|
}
|
|
|
|
}
|