2023-07-13 23:46:58 -07:00
|
|
|
using Godot;
|
2023-07-21 02:54:13 -07:00
|
|
|
using GodotUtilities;
|
2023-07-24 21:29:14 -07:00
|
|
|
using SupaLidlGame.Extensions;
|
2023-07-23 23:39:20 -07:00
|
|
|
using SupaLidlGame.State.Character;
|
2023-07-13 23:46:58 -07:00
|
|
|
|
|
|
|
namespace SupaLidlGame.Characters;
|
|
|
|
|
2023-07-22 20:23:48 -07:00
|
|
|
public partial class Doc : Boss
|
2023-07-13 23:46:58 -07:00
|
|
|
{
|
2023-07-22 20:23:48 -07:00
|
|
|
public AnimationPlayer TelegraphAnimation { get; set; }
|
2023-07-17 09:30:49 -07:00
|
|
|
|
2023-07-25 03:47:31 -07:00
|
|
|
public AnimationPlayer MiscAnimation { get; set; }
|
|
|
|
|
2023-07-23 23:39:20 -07:00
|
|
|
[Export]
|
|
|
|
public Items.Weapons.Sword Lance { get; set; }
|
|
|
|
|
|
|
|
protected bool _dashedAway = false;
|
2023-07-24 21:29:14 -07:00
|
|
|
protected CharacterDashState _dashState;
|
|
|
|
protected float _originalDashModifier;
|
2023-07-23 23:39:20 -07:00
|
|
|
|
2023-07-25 03:47:31 -07:00
|
|
|
[Export]
|
|
|
|
public override bool IsActive
|
|
|
|
{
|
|
|
|
get => base.IsActive;
|
|
|
|
set
|
|
|
|
{
|
|
|
|
base.IsActive = value;
|
|
|
|
var introState = BossStateMachine
|
|
|
|
.GetNode<State.NPC.Doc.DocIntroState>("Intro");
|
|
|
|
BossStateMachine.ChangeState(introState);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-22 20:23:48 -07:00
|
|
|
public override float Health
|
|
|
|
{
|
|
|
|
get => base.Health;
|
|
|
|
set
|
|
|
|
{
|
|
|
|
if (IsActive)
|
|
|
|
{
|
|
|
|
base.Health = value;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
// play opening animation
|
|
|
|
// then become active when it finishes
|
|
|
|
base.Health = value;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public override int Intensity
|
2023-07-18 00:57:28 -07:00
|
|
|
{
|
|
|
|
get
|
|
|
|
{
|
|
|
|
switch (Health)
|
|
|
|
{
|
2023-07-24 21:29:14 -07:00
|
|
|
case < 300:
|
2023-07-18 00:57:28 -07:00
|
|
|
return 3;
|
2023-07-24 21:29:14 -07:00
|
|
|
case < 600:
|
2023-07-18 00:57:28 -07:00
|
|
|
return 2;
|
|
|
|
default:
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-22 20:23:48 -07:00
|
|
|
public Doc()
|
|
|
|
{
|
|
|
|
ShouldMove = false;
|
|
|
|
}
|
|
|
|
|
2023-07-17 09:30:49 -07:00
|
|
|
public override void _Ready()
|
|
|
|
{
|
2023-07-22 20:23:48 -07:00
|
|
|
TelegraphAnimation = GetNode<AnimationPlayer>("Animations/Telegraph");
|
2023-07-25 03:47:31 -07:00
|
|
|
MiscAnimation = GetNode<AnimationPlayer>("Animations/Misc");
|
|
|
|
|
2023-07-17 09:30:49 -07:00
|
|
|
base._Ready();
|
2023-07-23 11:05:01 -07:00
|
|
|
|
2023-07-24 21:29:14 -07:00
|
|
|
_dashState = StateMachine.FindChildOfType<CharacterDashState>();
|
|
|
|
_originalDashModifier = _dashState.VelocityModifier;
|
|
|
|
|
2023-07-31 01:12:47 -07:00
|
|
|
var dialog = GD.Load<Resource>("res://Assets/Dialogue/doc.dialogue");
|
2023-07-25 03:47:31 -07:00
|
|
|
|
|
|
|
GetNode<BoundingBoxes.InteractionTrigger>("InteractionTrigger")
|
|
|
|
.Interaction += () =>
|
|
|
|
{
|
|
|
|
//DialogueManager.ShowExampleDialogueBalloon(dialog, "duel");
|
|
|
|
this.GetAncestor<Utils.World>().DialogueBalloon
|
|
|
|
.Start(dialog, "duel");
|
|
|
|
//.Call("start", dialog, "duel");
|
|
|
|
};
|
|
|
|
|
|
|
|
GetNode<State.Global.GlobalState>("/root/GlobalState")
|
|
|
|
.SummonBoss += (string name) =>
|
|
|
|
{
|
|
|
|
if (name == "Doc")
|
|
|
|
{
|
|
|
|
IsActive = true;
|
|
|
|
Inventory.SelectedItem = Lance;
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
2023-07-24 21:29:14 -07:00
|
|
|
|
2023-07-23 11:05:01 -07:00
|
|
|
// when we are hurt, start the boss fight
|
|
|
|
Hurt += (Events.HealthChangedArgs args) =>
|
|
|
|
{
|
|
|
|
if (!IsActive)
|
|
|
|
{
|
|
|
|
IsActive = true;
|
2023-07-23 23:39:20 -07:00
|
|
|
Inventory.SelectedItem = Lance;
|
2023-07-25 03:47:31 -07:00
|
|
|
//DialogueManager.ShowExampleDialogueBalloon();
|
2023-07-23 11:05:01 -07:00
|
|
|
}
|
|
|
|
};
|
2023-07-17 09:30:49 -07:00
|
|
|
}
|
2023-08-01 23:49:48 -07:00
|
|
|
|
2023-07-13 23:46:58 -07:00
|
|
|
public override void _Process(double delta)
|
|
|
|
{
|
2023-07-22 20:23:48 -07:00
|
|
|
if (IsActive)
|
|
|
|
{
|
|
|
|
BossStateMachine.Process(delta);
|
|
|
|
}
|
2023-07-13 23:46:58 -07:00
|
|
|
base._Process(delta);
|
|
|
|
}
|
2023-07-21 02:54:13 -07:00
|
|
|
|
2023-07-22 20:23:48 -07:00
|
|
|
protected override float ReceiveDamage(
|
|
|
|
float damage,
|
|
|
|
Character inflictor,
|
|
|
|
float knockback,
|
|
|
|
Vector2 knockbackDir = default)
|
|
|
|
{
|
|
|
|
if (IsActive)
|
|
|
|
{
|
|
|
|
return base.ReceiveDamage(
|
|
|
|
damage, inflictor, knockback, knockbackDir);
|
|
|
|
}
|
|
|
|
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
|
2023-07-31 01:12:47 -07:00
|
|
|
/*
|
2023-07-21 02:54:13 -07:00
|
|
|
public override void OnReceivedDamage(
|
|
|
|
float damage,
|
|
|
|
Character inflictor,
|
|
|
|
float knockback,
|
|
|
|
Vector2 knockbackDir = default)
|
|
|
|
{
|
|
|
|
GetNode<GpuParticles2D>("Effects/HurtParticles")
|
|
|
|
.SetDirection(knockbackDir);
|
|
|
|
|
|
|
|
base.OnReceivedDamage(damage, inflictor, knockback, knockbackDir);
|
|
|
|
}
|
2023-07-31 01:12:47 -07:00
|
|
|
*/
|
2023-07-23 23:39:20 -07:00
|
|
|
|
|
|
|
protected override void Think()
|
|
|
|
{
|
|
|
|
if (BossStateMachine.CurrentState is State.NPC.Doc.DocLanceState)
|
|
|
|
{
|
|
|
|
ThirdPhaseThink();
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
base.Think();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
protected void ThirdPhaseThink()
|
|
|
|
{
|
|
|
|
Character bestTarget = FindBestTarget();
|
|
|
|
if (bestTarget is not null)
|
|
|
|
{
|
|
|
|
Vector2 pos = bestTarget.GlobalPosition;
|
|
|
|
Target = pos - GlobalPosition;
|
|
|
|
Vector2 dir = GlobalPosition.DirectionTo(pos);
|
|
|
|
float dist = GlobalPosition.DistanceSquaredTo(pos);
|
|
|
|
UpdateWeights(pos);
|
|
|
|
|
|
|
|
if (CanAttack && StunTime <= 0)
|
|
|
|
{
|
|
|
|
bool isTargetStunned = bestTarget.StunTime > 0;
|
2023-07-24 21:29:14 -07:00
|
|
|
|
|
|
|
bool shouldDashAway = false;
|
|
|
|
bool shouldDashTowards = false;
|
|
|
|
|
2023-07-31 01:12:47 -07:00
|
|
|
var lanceState = Lance.StateMachine.CurrentState;
|
|
|
|
|
|
|
|
if (Inventory.SelectedItem != Lance)
|
|
|
|
{
|
|
|
|
Inventory.SelectedItem = Lance;
|
|
|
|
}
|
2023-07-24 21:29:14 -07:00
|
|
|
|
|
|
|
float dot = Direction.Normalized()
|
|
|
|
.Dot(bestTarget.Direction.Normalized());
|
|
|
|
|
|
|
|
// doc will still dash if you are farther than normal but
|
|
|
|
// moving towards him
|
|
|
|
float distThreshold = 2500 - (dot * 400);
|
|
|
|
|
|
|
|
// or just directly dash towards you if you are too far
|
|
|
|
float distTowardsThreshold = 22500;
|
|
|
|
|
|
|
|
// dash towards if lance in anticipate state
|
|
|
|
shouldDashTowards = (isTargetStunned || _dashedAway) &&
|
|
|
|
lanceState is State.Weapon.SwordAnticipateState ||
|
|
|
|
dist > distTowardsThreshold;
|
|
|
|
|
|
|
|
shouldDashAway = dist < distThreshold && !isTargetStunned;
|
|
|
|
|
|
|
|
//if (!isTargetStunned && dist < 2500 && !_dashedAway)
|
|
|
|
if (shouldDashAway && !shouldDashTowards)
|
2023-07-23 23:39:20 -07:00
|
|
|
{
|
2023-07-24 21:29:14 -07:00
|
|
|
// dash away if too close
|
|
|
|
_dashState.VelocityModifier = _originalDashModifier;
|
|
|
|
DashTo(-dir);
|
|
|
|
UseCurrentItem();
|
|
|
|
_dashedAway = true;
|
2023-07-23 23:39:20 -07:00
|
|
|
}
|
2023-07-24 21:29:14 -07:00
|
|
|
else if (shouldDashTowards && !shouldDashAway)
|
2023-07-23 23:39:20 -07:00
|
|
|
{
|
2023-07-24 21:29:14 -07:00
|
|
|
// dash to player's predicted position
|
|
|
|
_dashState.VelocityModifier = _originalDashModifier * 1.75f;
|
|
|
|
var dashSpeed = _dashState.VelocityModifier * Speed;
|
|
|
|
var newPos = Utils.Physics.PredictNewPosition(
|
|
|
|
GlobalPosition,
|
|
|
|
dashSpeed,
|
|
|
|
pos,
|
|
|
|
bestTarget.Velocity,
|
|
|
|
out float _);
|
|
|
|
DashTo(GlobalPosition.DirectionTo(newPos));
|
|
|
|
_dashedAway = false;
|
2023-07-23 23:39:20 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
private void DashTo(Vector2 direction)
|
|
|
|
{
|
|
|
|
StateMachine.ChangeState<CharacterDashState>(out var state);
|
|
|
|
state.DashDirection = direction;
|
|
|
|
}
|
2023-07-13 23:46:58 -07:00
|
|
|
}
|