1bit-game-jam/scripts/enemies/Watcher.cs

248 lines
4.7 KiB
C#

using Godot;
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;
((AudioStreamPlayer2D)FindChild("SighPlayer2D")).Play();
var newPosition = (_player.Position - Position).Normalized();
if (newPosition.IsZeroApprox())
newPosition = new Vector2(0, -1);
Position = _player.Position + newPosition * Constants.HalfScreenSize * 1.5f; // TODO: magic number
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 (area.GetParentOrNull<GameCamera>() is null)
return; // Not a flashlight
_isLitUp = true;
GD.Print("watcher: light enter");
}
private void LightExited(Area2D area)
{
if (area.GetParentOrNull<GameCamera>() is null)
return; // Not a flashlight
_isLitUp = false;
GD.Print("watcher: light exit");
}
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;
}
}