248 lines
4.7 KiB
C#
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;
|
|
}
|
|
}
|