Hit shader

This commit is contained in:
2024-09-15 17:12:18 -07:00
parent 470b853916
commit 4a1fdd94f8
19 changed files with 528 additions and 257 deletions

View File

@@ -34,6 +34,8 @@ public partial class Enemy : CharacterBody3D, IEnemy, IProvide<IEnemyLogic>
[Dependency] IGameRepo GameRepo => this.DependOn<IGameRepo>();
[Dependency] IGameEventDepot GameEventDepot => this.DependOn<IGameEventDepot>();
[Export]
public EnemyStatResource EnemyStatResource { get; set; } = default!;
@@ -51,10 +53,12 @@ public partial class Enemy : CharacterBody3D, IEnemy, IProvide<IEnemyLogic>
[Node] public Timer AttackTimer { get; set; } = default!;
[Node] public AnimatedSprite3D AnimatedSprite { get; set; } = default!;
[Node] public AnimatedSprite2D AnimatedSprite { get; set; } = default!;
[Node] public RayCast3D Raycast { get; set; } = default!;
[Node] public AnimationPlayer AnimationPlayer { get; set; } = default!;
private const string IDLE_FORWARD = "idle_front_walk";
private const string IDLE_LEFT = "idle_left_walk";
private const string IDLE_BACK = "idle_back_walk";
@@ -67,17 +71,93 @@ public partial class Enemy : CharacterBody3D, IEnemy, IProvide<IEnemyLogic>
EnemyLogic.Set(EnemyStatResource);
EnemyLogic.Set(this as IEnemy);
EnemyLogic.Set(GameRepo);
AnimationPlayer.AnimationFinished += AnimationPlayer_AnimationFinished;
}
public void OnReady()
{
SetPhysicsProcess(true);
CollisionDetector = CollisionDetectorScene.Instantiate<Area3D>();
CollisionDetector.AreaEntered += OnPlayerHitboxEntered;
AddChild(CollisionDetector);
}
public void OnResolved()
{
EnemyBinding = EnemyLogic.Bind();
EnemyBinding
.Handle((in EnemyLogic.Output.MovementComputed output) =>
{
MoveAndSlide();
})
.Handle((in EnemyLogic.Output.HitByPlayer output) =>
{
if (CurrentHP.Value > 0)
AnimationPlayer.Play("hit");
// TODO: Make this an event to notify game that player hit someone
if (GameRepo.PlayerData.Inventory.EquippedWeapon.Value.WeaponStats.WeaponTags.Contains(WeaponTag.SelfDamage))
GameRepo.PlayerData.SetCurrentHP(GameRepo.PlayerData.CurrentHP.Value - 5);
})
.Handle((in EnemyLogic.Output.Defeated output) =>
{
AnimationPlayer.Play("defeated");
});
this.Provide();
EnemyLogic.Start();
CurrentHP = new AutoProp<double>(EnemyStatResource.MaximumHP);
CurrentHP.Sync += OnHPChanged;
LineOfSight.BodyEntered += LineOfSight_BodyEntered;
PatrolTimer.Timeout += OnPatrolTimeout;
AttackTimer.Timeout += OnAttackTimeout;
var rng = new RandomNumberGenerator();
rng.Randomize();
PatrolTimer.WaitTime = rng.RandfRange(7.0f, 15.0f);
}
public void OnExitTree()
{
EnemyLogic.Stop();
EnemyBinding.Dispose();
}
private void OnPatrolTimeout()
{
var rng = new RandomNumberGenerator();
rng.Randomize();
var randomizedSpot = new Vector3(rng.RandfRange(-3.0f, 3.0f), 0, rng.RandfRange(-3.0f, 3.0f));
var randomizedSpot = new Vector3(rng.RandfRange(-7.0f, 7.0f), 0, rng.RandfRange(-7.0f, 7.0f));
EnemyLogic.Input(new EnemyLogic.Input.PatrolToRandomSpot(GlobalPosition + randomizedSpot));
PatrolTimer.WaitTime = rng.RandfRange(7.0f, 15.0f);
}
public void OnPhysicsProcess(double delta)
{
EnemyLogic.Input(new EnemyLogic.Input.PhysicsTick(delta));
RotateEnemy(-GameRepo.PlayerGlobalTransform.Value.Basis.Z);
}
public void OnPlayerHitboxEntered(Area3D body)
{
if (body is IHitbox hitBox)
{
if (CurrentHP.Value > 0)
{
var isCriticalHit = false;
var rng = new RandomNumberGenerator();
rng.Randomize();
var roll = rng.Randf();
if (roll <= GameRepo.PlayerData.Inventory.EquippedWeapon.Value.WeaponStats.Luck)
isCriticalHit = true;
var damage = DamageCalculator.CalculatePlayerDamage(GameRepo.PlayerData.CurrentAttack.Value + GameRepo.PlayerData.BonusAttack, EnemyStatResource, GameRepo.PlayerData.Inventory.EquippedWeapon.Value.WeaponStats, isCriticalHit);
GD.Print($"Enemy Hit for {damage} damage.");
EnemyLogic.Input(new EnemyLogic.Input.HitByPlayer(damage));
}
}
}
private void OnAttackTimeout()
{
if (GlobalPosition.DistanceTo(GameRepo.PlayerGlobalPosition.Value) > 2.5f)
@@ -92,54 +172,20 @@ public partial class Enemy : CharacterBody3D, IEnemy, IProvide<IEnemyLogic>
private void LineOfSight_BodyEntered(Node3D body)
{
var overlappingBodies = LineOfSight.GetOverlappingBodies();
//foreach (var overlap in overlappingBodies)
//{
// Raycast.LookAt(GameRepo.PlayerGlobalPosition.Value, Vector3.Up);
// Raycast.ForceRaycastUpdate();
// if (Raycast.IsColliding())
// {
// var collider = Raycast.GetCollider();
// if (collider is IPlayer player)
// {
// Raycast.DebugShapeCustomColor = Color.FromString("Purple", Colors.Purple);
// EnemyLogic.Input(new EnemyLogic.Input.Alerted());
// }
// }
//}
}
public void OnResolved()
{
EnemyBinding = EnemyLogic.Bind();
EnemyBinding
.Handle((in EnemyLogic.Output.MovementComputed output) =>
foreach (var overlap in overlappingBodies)
{
Raycast.LookAt(GameRepo.PlayerGlobalPosition.Value, Vector3.Up);
Raycast.ForceRaycastUpdate();
if (Raycast.IsColliding())
{
MoveAndSlide();
})
.Handle((in EnemyLogic.Output.Die output) =>
{
QueueFree();
})
.Handle((in EnemyLogic.Output.HitByPlayer output) =>
{
// TODO: Make this an event to notify game that player hit someone
if (GameRepo.PlayerData.Inventory.EquippedWeapon.Value.WeaponStats.WeaponTags.Contains(WeaponTag.SelfDamage))
GameRepo.PlayerData.SetCurrentHP(GameRepo.PlayerData.CurrentHP.Value - 5);
});
this.Provide();
EnemyLogic.Start();
CurrentHP = new AutoProp<double>(EnemyStatResource.MaximumHP);
CurrentHP.Sync += OnHPChanged;
LineOfSight.BodyEntered += LineOfSight_BodyEntered;
PatrolTimer.Timeout += OnPatrolTimeout;
AttackTimer.Timeout += OnAttackTimeout;
var rng = new RandomNumberGenerator();
rng.Randomize();
PatrolTimer.WaitTime = rng.RandfRange(7.0f, 15.0f);
var collider = Raycast.GetCollider();
if (collider is IPlayer player)
{
Raycast.DebugShapeCustomColor = Color.FromString("Purple", Colors.Purple);
EnemyLogic.Input(new EnemyLogic.Input.Alerted());
}
}
}
}
private void RotateEnemy(Vector3 cameraDirection)
@@ -169,58 +215,18 @@ public partial class Enemy : CharacterBody3D, IEnemy, IProvide<IEnemyLogic>
}
}
public void OnPhysicsProcess(double delta)
{
EnemyLogic.Input(new EnemyLogic.Input.PhysicsTick(delta));
RotateEnemy(-GameRepo.PlayerGlobalTransform.Value.Basis.Z);
}
public void OnPlayerHitboxEntered(Area3D body)
{
if (body is IHitbox hitBox)
{
if (CurrentHP.Value > 0)
{
var isCriticalHit = false;
var rng = new RandomNumberGenerator();
rng.Randomize();
var roll = rng.Randf();
if (roll <= GameRepo.PlayerData.Inventory.EquippedWeapon.Value.WeaponStats.Luck)
isCriticalHit = true;
var damage = DamageCalculator.CalculatePlayerDamage(GameRepo.PlayerData.CurrentAttack.Value + GameRepo.PlayerData.BonusAttack, EnemyStatResource, GameRepo.PlayerData.Inventory.EquippedWeapon.Value.WeaponStats, isCriticalHit);
GD.Print($"Enemy Hit for {damage} damage.");
EnemyLogic.Input(new EnemyLogic.Input.HitByPlayer(damage));
}
}
}
private void OnHPChanged(double newHP)
{
if (newHP <= 0)
{
EnemyLogic.Input(new EnemyLogic.Input.Killed());
EnemyLogic.Input(new EnemyLogic.Input.EnemyDefeated());
GameEventDepot.OnEnemyDefeated(EnemyStatResource);
}
}
public void OnReady()
private void AnimationPlayer_AnimationFinished(StringName animName)
{
SetPhysicsProcess(true);
CollisionDetector = CollisionDetectorScene.Instantiate<Area3D>();
CollisionDetector.AreaEntered += OnPlayerHitboxEntered;
AddChild(CollisionDetector);
}
public void OnExitTree()
{
EnemyLogic.Stop();
EnemyBinding.Dispose();
if (animName == "defeated")
QueueFree();
}
}
public enum WeaponTag
{
SelfDamage,
IgnoreAffinity,
Knockback,
BreaksOnChange
}