Add Watcher
This commit is contained in:
parent
6514ce3c54
commit
d1536529e4
9 changed files with 448 additions and 10 deletions
241
scripts/enemies/Watcher.cs
Normal file
241
scripts/enemies/Watcher.cs
Normal file
|
@ -0,0 +1,241 @@
|
|||
using Godot;
|
||||
using System;
|
||||
|
||||
public partial class Watcher : Node2D
|
||||
{
|
||||
public enum State
|
||||
{
|
||||
Waiting,
|
||||
Opening,
|
||||
Looking,
|
||||
PreparingToTeleport,
|
||||
Teleporting,
|
||||
LitUp
|
||||
}
|
||||
|
||||
[Export] public float EyeDotSpeed = 2f;
|
||||
[Export] public Vector2 EyeDotDistance = new(4f, 3f);
|
||||
[Export] public float EyeTeleportDistance = 48f;
|
||||
|
||||
public State CurrentState
|
||||
{
|
||||
get => _state;
|
||||
|
||||
private set
|
||||
{
|
||||
_state = value;
|
||||
_timeSinceState = 0;
|
||||
GD.Print($"watcher: new state is {_state}");
|
||||
|
||||
switch (_state)
|
||||
{
|
||||
case State.Waiting:
|
||||
HideEye();
|
||||
break;
|
||||
|
||||
case State.Opening:
|
||||
_dotSprite.Position = _dotSpriteInitialPos;
|
||||
ShowEye();
|
||||
break;
|
||||
|
||||
case State.Looking:
|
||||
break;
|
||||
|
||||
case State.PreparingToTeleport:
|
||||
_sprite.PlayBackwards("default");
|
||||
break;
|
||||
|
||||
case State.Teleporting:
|
||||
HideEye();
|
||||
break;
|
||||
|
||||
case State.LitUp:
|
||||
_isLitUp = false;
|
||||
|
||||
var newPosition = (_player.Position - Position).Normalized();
|
||||
if (newPosition.IsZeroApprox())
|
||||
newPosition = new Vector2(0, -1);
|
||||
Position += newPosition * Constants.HalfScreenSize.Length();
|
||||
|
||||
HideEye();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private Player _player;
|
||||
private AnimatedSprite2D _sprite;
|
||||
private Sprite2D _dotSprite;
|
||||
private Vector2 _dotSpriteInitialPos;
|
||||
private State _state;
|
||||
private float _timeSinceState;
|
||||
private bool _isPlayerInside;
|
||||
private bool _isLitUp;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
_sprite = (AnimatedSprite2D)FindChild("AnimatedSprite2D");
|
||||
|
||||
_dotSprite = (Sprite2D)FindChild("Sprite2D");
|
||||
_dotSpriteInitialPos = _dotSprite.Position;
|
||||
|
||||
CurrentState = State.Waiting;
|
||||
}
|
||||
|
||||
public override void _PhysicsProcess(double delta)
|
||||
{
|
||||
_timeSinceState += (float)delta;
|
||||
|
||||
switch (_state)
|
||||
{
|
||||
case State.Waiting:
|
||||
break;
|
||||
|
||||
case State.Opening:
|
||||
break;
|
||||
|
||||
case State.Looking:
|
||||
{
|
||||
var playerRelative = _player.Position - Position;
|
||||
var normal = playerRelative.Normalized();
|
||||
_dotSprite.Position = _dotSprite.Position.Lerp(normal * EyeDotDistance, EyeDotSpeed * (float)delta);
|
||||
|
||||
if (_timeSinceState > 5f) // 5 seconds to look at the player
|
||||
CurrentState = State.PreparingToTeleport;
|
||||
}
|
||||
break;
|
||||
|
||||
case State.PreparingToTeleport:
|
||||
break;
|
||||
|
||||
case State.Teleporting:
|
||||
if (_timeSinceState > 1f) // 1 second to teleport
|
||||
{
|
||||
var newPosition = _player.Position - Position;
|
||||
if (newPosition.Length() > EyeTeleportDistance)
|
||||
newPosition = newPosition.Normalized() * EyeTeleportDistance;
|
||||
|
||||
Position += newPosition;
|
||||
CurrentState = State.Opening;
|
||||
}
|
||||
|
||||
break;
|
||||
|
||||
case State.LitUp:
|
||||
if (_timeSinceState > 2f) // 2 seconds of cooldown
|
||||
{
|
||||
CurrentState = State.Opening;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
CheckIfKilledPlayer();
|
||||
CheckIfLitUp();
|
||||
}
|
||||
|
||||
private void AnimationEnded()
|
||||
{
|
||||
switch (CurrentState)
|
||||
{
|
||||
case State.Opening:
|
||||
CurrentState = State.Looking;
|
||||
break;
|
||||
|
||||
case State.PreparingToTeleport:
|
||||
CurrentState = State.Teleporting;
|
||||
break;
|
||||
|
||||
case State.LitUp:
|
||||
//QueueFree();
|
||||
GD.Print("TODO: watcher: teleport");
|
||||
break;
|
||||
|
||||
case State.Waiting:
|
||||
case State.Looking:
|
||||
case State.Teleporting:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void LightEntered(Area2D area)
|
||||
{
|
||||
if (CurrentState is State.Waiting or State.Opening or State.Teleporting or State.LitUp)
|
||||
return;
|
||||
|
||||
if (area.GetParentOrNull<GameCamera>() is null)
|
||||
return; // Not a flashlight
|
||||
|
||||
_isLitUp = true;
|
||||
GD.Print("watcher: light enter");
|
||||
}
|
||||
|
||||
private void PlayerEntered(Node2D body)
|
||||
{
|
||||
if (body is not Player player)
|
||||
return;
|
||||
|
||||
_isPlayerInside = true;
|
||||
}
|
||||
|
||||
private void PlayerLeft(Node2D body)
|
||||
{
|
||||
if (body is not Player player)
|
||||
return;
|
||||
|
||||
_isPlayerInside = false;
|
||||
}
|
||||
|
||||
private void PlayerActivated(Node2D body)
|
||||
{
|
||||
GD.Print("watcher: player activated1");
|
||||
if (CurrentState != State.Waiting || body is not Player player)
|
||||
return;
|
||||
|
||||
// TODO: turn off the player activation field for better performance
|
||||
|
||||
GD.Print("watcher: player activated");
|
||||
_player = player;
|
||||
CurrentState = State.Opening;
|
||||
}
|
||||
|
||||
void HideEye()
|
||||
{
|
||||
Modulate = new Color(1, 1, 1, 0);
|
||||
_sprite.Stop();
|
||||
}
|
||||
|
||||
void ShowEye()
|
||||
{
|
||||
Modulate = new Color(1, 1, 1, 1);
|
||||
_sprite.Play("default");
|
||||
}
|
||||
|
||||
void CheckIfKilledPlayer()
|
||||
{
|
||||
if (_player is null || !_isPlayerInside)
|
||||
return;
|
||||
|
||||
if (CurrentState is State.LitUp or State.PreparingToTeleport or State.Teleporting or State.Waiting)
|
||||
return;
|
||||
|
||||
_player.Kill(this);
|
||||
}
|
||||
|
||||
void CheckIfLitUp()
|
||||
{
|
||||
if (!_isLitUp)
|
||||
return;
|
||||
|
||||
if (CurrentState is State.Waiting or State.Opening or State.Teleporting or State.LitUp)
|
||||
return;
|
||||
|
||||
CurrentState = State.LitUp;
|
||||
}
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue