diff --git a/Zennysoft.Game.Abstractions/Entity/IBehavior.cs b/Zennysoft.Game.Abstractions/Entity/IBehavior.cs new file mode 100644 index 00000000..77c446ff --- /dev/null +++ b/Zennysoft.Game.Abstractions/Entity/IBehavior.cs @@ -0,0 +1,5 @@ +namespace Zennysoft.Game.Abstractions.Entity; + +public interface IBehavior +{ +} diff --git a/Zennysoft.Game.Abstractions/Entity/IEntity.cs b/Zennysoft.Game.Abstractions/Entity/IEntity.cs new file mode 100644 index 00000000..9f6c4a96 --- /dev/null +++ b/Zennysoft.Game.Abstractions/Entity/IEntity.cs @@ -0,0 +1,11 @@ +namespace Zennysoft.Game.Abstractions.Entity +{ + public interface IAction + { + public Task PerformAction(); + } + public interface IAction + { + public Task PerformAction(T arg); + } +} diff --git a/Zennysoft.Game.Abstractions/Entity/IHealthComponent.cs b/Zennysoft.Game.Abstractions/Entity/IHealthComponent.cs new file mode 100644 index 00000000..c64c587f --- /dev/null +++ b/Zennysoft.Game.Abstractions/Entity/IHealthComponent.cs @@ -0,0 +1,8 @@ +namespace Zennysoft.Game.Abstractions; + +public interface IHealthComponent +{ + int MaximumHP { get; } + + int CurrentHP { get; set; } +} diff --git a/Zennysoft.Game.Abstractions/NPCs/ICanPatrol.cs b/Zennysoft.Game.Abstractions/NPCs/ICanPatrol.cs deleted file mode 100644 index e54ef7db..00000000 --- a/Zennysoft.Game.Abstractions/NPCs/ICanPatrol.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Zennysoft.Game.Abstractions; - -public interface ICanPatrol -{ - public void Patrol(); -} diff --git a/Zennysoft.Game.Ma.Implementation/Calculators/IDamageCalculator.cs b/Zennysoft.Game.Ma.Implementation/Calculators/IDamageCalculator.cs index 1184d7df..0e4d5105 100644 --- a/Zennysoft.Game.Ma.Implementation/Calculators/IDamageCalculator.cs +++ b/Zennysoft.Game.Ma.Implementation/Calculators/IDamageCalculator.cs @@ -3,50 +3,33 @@ using Zennysoft.Ma.Adapter.Entity; namespace Zennysoft.Ma.Adapter { - public class DamageCalculator : IDamageCalculator + public static class DamageCalculator { - public double CalculateDamage(double damage, - ElementType elementType, - double defense, - ElementalResistanceSet elementalResistanceSet, - bool isCriticalHit = false, - bool ignoreDefense = false, - bool ignoreElementalResistance = false) + public static int CalculateDamage(Damage damage, double defense, ElementalResistanceSet elementalResistanceSet) { - var calculatedDamage = damage; - if (!ignoreElementalResistance) - calculatedDamage = CalculateElementalResistance(calculatedDamage, elementType, elementalResistanceSet); - if (!ignoreDefense) + var calculatedDamage = damage.BaseDamage; + if (!damage.IgnoreDefense) calculatedDamage = CalculateDefenseResistance(calculatedDamage, defense); - if (isCriticalHit) + if (!damage.IgnoreElementalResistance) + calculatedDamage = CalculateElementalResistance(calculatedDamage, elementalResistanceSet.ElementalResistance[damage.ElementType]); + if (damage.IsCriticalHit) calculatedDamage *= 2; - return calculatedDamage; + return Mathf.Max(1, calculatedDamage); } - private static double CalculateDefenseResistance(double incomingDamage, double defense) + private static int CalculateDefenseResistance(int incomingDamage, double defense) { - return Mathf.Max(incomingDamage - defense, 0.0); + var result = incomingDamage - (int)(incomingDamage * (defense / 100)); + return result; } - private static double CalculateElementalResistance( - double incomingDamage, - ElementType incomingElementType, - ElementalResistanceSet elementalResistanceSet) + private static int CalculateElementalResistance( + int incomingDamage, + double elementalResistance) { - var resistance = elementalResistanceSet.ElementalResistance[incomingElementType]; - return Mathf.Max(incomingDamage - (incomingDamage * resistance), 0.0); + var result = incomingDamage - (int)(incomingDamage * (elementalResistance / 100)); + return result; } } - - public interface IDamageCalculator - { - public double CalculateDamage(double damage, - ElementType elementType, - double defense, - ElementalResistanceSet elementalResistanceSet, - bool isCriticalHit = false, - bool ignoreDefense = false, - bool ignoreElementalResistance = false); - } } diff --git a/Zennysoft.Game.Ma.Implementation/Entity/Behaviors/IBehavior.cs.uid b/Zennysoft.Game.Ma.Implementation/Entity/Behaviors/IBehavior.cs.uid new file mode 100644 index 00000000..9d8d931b --- /dev/null +++ b/Zennysoft.Game.Ma.Implementation/Entity/Behaviors/IBehavior.cs.uid @@ -0,0 +1 @@ +uid://ci7va4hsq6hyt diff --git a/Zennysoft.Game.Ma.Implementation/Entity/Behaviors/PatrolBehavior.cs.uid b/Zennysoft.Game.Ma.Implementation/Entity/Behaviors/PatrolBehavior.cs.uid new file mode 100644 index 00000000..81a2a56f --- /dev/null +++ b/Zennysoft.Game.Ma.Implementation/Entity/Behaviors/PatrolBehavior.cs.uid @@ -0,0 +1 @@ +uid://87d8kluait8y diff --git a/Zennysoft.Game.Ma.Implementation/Entity/Behaviors/PatrolBehavior.tscn b/Zennysoft.Game.Ma.Implementation/Entity/Behaviors/PatrolBehavior.tscn new file mode 100644 index 00000000..c0b5cd9c --- /dev/null +++ b/Zennysoft.Game.Ma.Implementation/Entity/Behaviors/PatrolBehavior.tscn @@ -0,0 +1,9 @@ +[gd_scene load_steps=2 format=3 uid="uid://c7e5g8l6wuph"] + +[ext_resource type="Script" uid="uid://87d8kluait8y" path="res://src/enemy/behaviors/PatrolBehavior.cs" id="1_lobva"] + +[node name="NavigationAgent" type="NavigationAgent3D"] +avoidance_enabled = true +debug_enabled = true +script = ExtResource("1_lobva") +_patrolSpeed = 100.0 diff --git a/Zennysoft.Game.Ma.Implementation/Entity/Damage.cs b/Zennysoft.Game.Ma.Implementation/Entity/Damage.cs new file mode 100644 index 00000000..5bde6f4c --- /dev/null +++ b/Zennysoft.Game.Ma.Implementation/Entity/Damage.cs @@ -0,0 +1,3 @@ +namespace Zennysoft.Ma.Adapter; + +public record Damage(int BaseDamage, ElementType ElementType, bool IsCriticalHit, bool IgnoreDefense, bool IgnoreElementalResistance); diff --git a/Zennysoft.Game.Ma.Implementation/Entity/IEnemy.cs b/Zennysoft.Game.Ma.Implementation/Entity/IEnemy.cs new file mode 100644 index 00000000..8b508df2 --- /dev/null +++ b/Zennysoft.Game.Ma.Implementation/Entity/IEnemy.cs @@ -0,0 +1,29 @@ +using Godot; +using System.Collections.Immutable; +using Zennysoft.Game.Ma; + +namespace Zennysoft.Ma.Adapter.Entity +{ + public interface IEnemy + { + public void Activate(); + + public void Idle(); + + public void Die(); + + public void PerformAction(); + + public void ReturnToDefaultState(); + + public void TakeDamage(int damage); + + public void SetTarget(Vector3 targetPosition); + + public void SetEnemyPosition(Vector3 position); + + public void LookAtTarget(Vector3 target); + + public IDungeonRoom GetCurrentRoom(ImmutableList dungeonRooms); + } +} \ No newline at end of file diff --git a/Zennysoft.Game.Ma.Implementation/Entity/IKnockbackable.cs b/Zennysoft.Game.Ma.Implementation/Entity/IKnockbackable.cs new file mode 100644 index 00000000..11c546fb --- /dev/null +++ b/Zennysoft.Game.Ma.Implementation/Entity/IKnockbackable.cs @@ -0,0 +1,9 @@ +using Godot; + +namespace Zennysoft.Ma.Adapter.Entity +{ + public interface IKnockbackable + { + void Knockback(float impulse, Vector3 direction); + } +} diff --git a/Zennysoft.Game.Ma.Implementation/Item/InventoryItem.cs b/Zennysoft.Game.Ma.Implementation/Item/InventoryItem.cs index 2de45b5f..b9440a65 100644 --- a/Zennysoft.Game.Ma.Implementation/Item/InventoryItem.cs +++ b/Zennysoft.Game.Ma.Implementation/Item/InventoryItem.cs @@ -16,7 +16,7 @@ public abstract partial class InventoryItem : Node3D [Save("inventory_item_spawn_rate")] public abstract float SpawnRate { get; } [Save("inventory_item_throw_damage")] - public abstract double ThrowDamage { get; } + public abstract int ThrowDamage { get; } [Save("inventory_item_throw_speed")] public abstract float ThrowSpeed { get; } [Save("inventory_item_tag")] diff --git a/Zennysoft.Game.Ma/src/map/dungeon/code/IDungeonFloor.cs b/Zennysoft.Game.Ma.Implementation/Map/IDungeonFloor.cs similarity index 100% rename from Zennysoft.Game.Ma/src/map/dungeon/code/IDungeonFloor.cs rename to Zennysoft.Game.Ma.Implementation/Map/IDungeonFloor.cs diff --git a/Zennysoft.Game.Ma/src/map/dungeon/code/IDungeonRoom.cs b/Zennysoft.Game.Ma.Implementation/Map/IDungeonRoom.cs similarity index 88% rename from Zennysoft.Game.Ma/src/map/dungeon/code/IDungeonRoom.cs rename to Zennysoft.Game.Ma.Implementation/Map/IDungeonRoom.cs index d9b13736..3f49d226 100644 --- a/Zennysoft.Game.Ma/src/map/dungeon/code/IDungeonRoom.cs +++ b/Zennysoft.Game.Ma.Implementation/Map/IDungeonRoom.cs @@ -1,6 +1,6 @@ using Chickensoft.GodotNodeInterfaces; -using Godot; using System.Collections.Immutable; +using Zennysoft.Ma.Adapter.Entity; namespace Zennysoft.Game.Ma; public interface IDungeonRoom : INode3D diff --git a/Zennysoft.Game.Ma.Implementation/Module/Module.cs b/Zennysoft.Game.Ma.Implementation/Module/Module.cs index 0eb25c9c..f3bbb5c5 100644 --- a/Zennysoft.Game.Ma.Implementation/Module/Module.cs +++ b/Zennysoft.Game.Ma.Implementation/Module/Module.cs @@ -1,7 +1,9 @@ using SimpleInjector; using System.IO.Abstractions; using Zennysoft.Game.Abstractions; +using Zennysoft.Game.Abstractions.Entity; using Zennysoft.Game.Implementation; +using Zennysoft.Ma.Adapter.Entity; namespace Zennysoft.Ma.Adapter; diff --git a/Zennysoft.Game.Ma.Implementation/Player/IPlayer.cs b/Zennysoft.Game.Ma.Implementation/Player/IPlayer.cs index 62b732cb..e93adc19 100644 --- a/Zennysoft.Game.Ma.Implementation/Player/IPlayer.cs +++ b/Zennysoft.Game.Ma.Implementation/Player/IPlayer.cs @@ -14,7 +14,7 @@ public interface IPlayer : IKillable public void Attack(); - public void TakeDamage(double damage, ElementType elementType = ElementType.None, bool isCriticalHit = false); + public void TakeDamage(Damage damage); public void Knockback(float impulse); diff --git a/Zennysoft.Game.Ma.Implementation/Zennysoft.Ma.Adapter.csproj b/Zennysoft.Game.Ma.Implementation/Zennysoft.Ma.Adapter.csproj index cca7f912..8aa6b266 100644 --- a/Zennysoft.Game.Ma.Implementation/Zennysoft.Ma.Adapter.csproj +++ b/Zennysoft.Game.Ma.Implementation/Zennysoft.Ma.Adapter.csproj @@ -8,11 +8,8 @@ - - - @@ -31,4 +28,8 @@ + + + + diff --git a/Zennysoft.Game.Ma/project.godot b/Zennysoft.Game.Ma/project.godot index 442e1ff3..446cf58a 100644 --- a/Zennysoft.Game.Ma/project.godot +++ b/Zennysoft.Game.Ma/project.godot @@ -34,6 +34,8 @@ runtime/advanced/uses_dotnet=true window/size/viewport_width=1920 window/size/viewport_height=1080 +window/stretch/mode="viewport" +window/stretch/aspect="expand" [dotnet] diff --git a/Zennysoft.Game.Ma/src/enemy/BossTypeA.cs b/Zennysoft.Game.Ma/src/enemy/BossTypeA.cs index c4a643a1..74ea4d51 100644 --- a/Zennysoft.Game.Ma/src/enemy/BossTypeA.cs +++ b/Zennysoft.Game.Ma/src/enemy/BossTypeA.cs @@ -5,19 +5,16 @@ using System.Collections.Generic; using System; using Zennysoft.Ma.Adapter; using System.Linq; -using Chickensoft.Collections; +using Zennysoft.Ma.Adapter.Entity; +using System.Collections.Immutable; namespace Zennysoft.Game.Ma; [Meta(typeof(IAutoNode))] -public partial class BossTypeA : CharacterBody3D, IEnemy, IHasPrimaryAttack, IHasSecondaryAttack, ICanActivate, IProvide +public partial class BossTypeA : CharacterBody3D { public override void _Notification(int what) => this.Notify(what); - protected IEnemyLogic _enemyLogic { get; set; } = default!; - - IEnemyLogic IProvide.Value() => _enemyLogic; - public EnemyLogic.IBinding EnemyBinding { get; set; } = default!; @@ -43,46 +40,12 @@ public partial class BossTypeA : CharacterBody3D, IEnemy, IHasPrimaryAttack, IHa [Node] protected Timer _attackTimer { get; set; } = default!; [Node] private CollisionShape3D _collisionShape { get; set; } = default!; + public float ThinkTime { get; } private Vector3 _target; private float _movementSpeed = 2.0f; - public AutoProp CurrentHP { get; set; } - - private DamageCalculator _damageCalculator; - - public void Setup() - { - _enemyLogic = new EnemyLogic(); - _enemyLogic.Set(_enemyStatResource); - _enemyLogic.Set(this as IEnemy); - _enemyLogic.Set(_player); - _damageCalculator = new DamageCalculator(); - SetPhysicsProcess(true); - } - - public void OnResolved() - { - EnemyBinding = _enemyLogic.Bind(); - - EnemyBinding - .Handle((in EnemyLogic.Output.TakeAction _) => - { - TakeAction(); - }) - .Handle((in EnemyLogic.Output.Defeated output) => - { - }); - - this.Provide(); - - _enemyLogic.Start(); - - CurrentHP = new AutoProp(_enemyStatResource.MaximumHP); - CurrentHP.Sync += OnHPChanged; - } - private void OnHPChanged(double newHP) { if (newHP <= 0) @@ -103,8 +66,6 @@ public partial class BossTypeA : CharacterBody3D, IEnemy, IHasPrimaryAttack, IHa { SetProcess(false); _movementSpeed = 0; - CurrentHP.OnNext(0); - _enemyLogic.Input(new EnemyLogic.Input.EnemyDefeated()); _collisionShape.SetDeferred("disabled", true); _enemyModelView.PlayDeathAnimation(); var tweener = CreateTween(); @@ -122,13 +83,8 @@ public partial class BossTypeA : CharacterBody3D, IEnemy, IHasPrimaryAttack, IHa public void OnPhysicsProcess(double delta) { - _enemyLogic.Input(new EnemyLogic.Input.PhysicsTick(delta)); - var direction = GlobalPosition.DirectionTo(_target).Normalized(); - if (_enemyLogic.Value is not EnemyLogic.State.Activated) - return; - var rotationAngle = GetRotationAngle(); if (GlobalBasis.Z.AngleTo(_player.CurrentBasis.Z) > Mathf.DegToRad(60)) { @@ -136,20 +92,9 @@ public partial class BossTypeA : CharacterBody3D, IEnemy, IHasPrimaryAttack, IHa _enemyModelView.PlayIdleAnimation(); return; } - - if (_enemyLogic.Value is EnemyLogic.State.FollowPlayer && GlobalPosition.DistanceTo(_player.CurrentPosition) < 5f) - _enemyLogic.Input(new EnemyLogic.Input.StartAttacking()); - if (_enemyLogic.Value is EnemyLogic.State.Attacking && GlobalPosition.DistanceTo(_player.CurrentPosition) > 5f) - _enemyLogic.Input(new EnemyLogic.Input.Alerted()); - - if (_enemyLogic.Value is EnemyLogic.State.FollowPlayer) - { - Velocity = direction * _movementSpeed; - MoveAndSlide(); - } } - public void TakeAction() + public void PerformAction() { var rng = new RandomNumberGenerator(); var options = new List() { PrimaryAttack, SecondaryAttack }; @@ -177,7 +122,7 @@ public partial class BossTypeA : CharacterBody3D, IEnemy, IHasPrimaryAttack, IHa _attackTimer.Timeout -= OnAttackTimeout; } - public void SetTarget(Vector3 target) => _target = target; + public void SetMovementTarget(Vector3 target) => _target = target; private void Hitbox_AreaEntered(Area3D area) { @@ -185,13 +130,12 @@ public partial class BossTypeA : CharacterBody3D, IEnemy, IHasPrimaryAttack, IHa if (target is IPlayer player) { var damage = _enemyStatResource.CurrentAttack * PrimaryAttackElementalDamageBonus; - player.TakeDamage(damage, PrimaryAttackElementalType, BattleExtensions.IsCriticalHit(_enemyStatResource.Luck)); + player.TakeDamage(new Damage((int)damage, PrimaryAttackElementalType, BattleExtensions.IsCriticalHit(_enemyStatResource.Luck), false, false)); } } public void StartFight() { - _enemyLogic.Input(new EnemyLogic.Input.Alerted()); } public void Activate() @@ -202,15 +146,8 @@ public partial class BossTypeA : CharacterBody3D, IEnemy, IHasPrimaryAttack, IHa private void OnAttackTimeout() { - if (GlobalPosition.DistanceTo(_player.CurrentPosition) > 5f) - { - _enemyLogic.Input(new EnemyLogic.Input.Alerted()); - return; - } - var rng = new RandomNumberGenerator(); rng.Randomize(); - _enemyLogic.Input(new EnemyLogic.Input.AttackTimer()); _attackTimer.Stop(); _attackTimer.WaitTime = rng.RandfRange(2f, 5.0f); _attackTimer.Start(); @@ -234,15 +171,5 @@ public partial class BossTypeA : CharacterBody3D, IEnemy, IHasPrimaryAttack, IHa public override void _ExitTree() { - CurrentHP.OnCompleted(); } - - public void Move(Vector3 velocity) => throw new NotImplementedException(); - public void TakeDamage(double damage, ElementType elementType = ElementType.None, bool isCriticalHit = false, bool ignoreDefense = false, bool ignoreElementalResistance = false) => throw new NotImplementedException(); - public void Knockback(float impulse, Vector3 direction) => throw new NotImplementedException(); - public void SetCurrentHP(int newHP) => throw new NotImplementedException(); - public int GetMaximumHP() => throw new NotImplementedException(); - public void SetEnemyGlobalPosition(Vector3 target) => throw new NotImplementedException(); - public Vector3 GetEnemyGlobalPosition() => throw new NotImplementedException(); - public IDungeonRoom GetCurrentRoom() => throw new NotImplementedException(); } diff --git a/Zennysoft.Game.Ma/src/enemy/Enemy.cs b/Zennysoft.Game.Ma/src/enemy/Enemy.cs index c8db700d..46c64af8 100644 --- a/Zennysoft.Game.Ma/src/enemy/Enemy.cs +++ b/Zennysoft.Game.Ma/src/enemy/Enemy.cs @@ -1,14 +1,15 @@ using Chickensoft.AutoInject; -using Chickensoft.Collections; using Chickensoft.Introspection; using Godot; +using System.Collections.Immutable; using System.Linq; using Zennysoft.Ma.Adapter; +using Zennysoft.Ma.Adapter.Entity; namespace Zennysoft.Game.Ma; [Meta(typeof(IAutoNode))] -public partial class Enemy : CharacterBody3D, IEnemy, IProvide +public abstract partial class Enemy : CharacterBody3D, IEnemy, IProvide { #region Registration public override void _Notification(int what) => this.Notify(what); @@ -21,50 +22,30 @@ public partial class Enemy : CharacterBody3D, IEnemy, IProvide #endregion #region Dependencies - - [Dependency] IGame Game => this.DependOn(); - [Dependency] protected IPlayer _player => this.DependOn(() => GetParent().GetChildren().OfType().Single()); #endregion - #region Exports - [Export] protected EnemyStatResource _enemyStatResource { get; set; } = default!; - - [Export] - protected float _movementSpeed = 0.5f; - #endregion - #region Node Dependencies - [Node] private CollisionShape3D _collisionShape { get; set; } = default!; - [Node] private Area3D LineOfSight { get; set; } = default!; - - [Node] protected Timer _attackTimer { get; set; } = default!; - [Node] private RayCast3D Raycast { get; set; } = default!; - [Node] public IEnemyModelView EnemyModelView { get; set; } = default!; + [Node] private HealthComponent _healthComponent { get; set; } = default!; #endregion - public AutoProp CurrentHP { get; set; } + public virtual IEnemyModelView EnemyModelView { get; set; } = default!; - public string EnemyName; - - private float _knockbackStrength = 0.0f; - - private Vector3 _knockbackDirection = Vector3.Zero; - - private DamageCalculator _damageCalculator; + public Vector3 TargetPosition { get; private set; } #region Godot methods public void Setup() { _enemyLogic = new EnemyLogic(); - _enemyLogic.Set(_enemyStatResource); _enemyLogic.Set(this as IEnemy); _enemyLogic.Set(_player); - _damageCalculator = new DamageCalculator(); SetPhysicsProcess(true); + _healthComponent.HealthReachedZero += Die; + _healthComponent.HealthLowered += TakeHit; + EnemyModelView.HitPlayer += EnemyModelView_HitPlayer; } public void OnResolved() @@ -72,143 +53,105 @@ public partial class Enemy : CharacterBody3D, IEnemy, IProvide EnemyBinding = _enemyLogic.Bind(); EnemyBinding - .Handle((in EnemyLogic.Output.TakeAction _) => + .Handle((in EnemyLogic.Output.Activate _) => { - TakeAction(); + Activate(); }) - .Handle((in EnemyLogic.Output.Defeated output) => + .Handle((in EnemyLogic.Output.Idle _) => { + Idle(); + }) + .Handle((in EnemyLogic.Output.Move _) => + { + Move(); + }) + .Handle((in EnemyLogic.Output.ReturnToDefaultState _) => + { + ReturnToDefaultState(); }); this.Provide(); _enemyLogic.Start(); - - CurrentHP = new AutoProp(_enemyStatResource.MaximumHP); - CurrentHP.Sync += OnHPChanged; LineOfSight.BodyEntered += LineOfSight_BodyEntered; } - - public override void _Process(double delta) - { - if (CurrentHP.Value <= 0) - return; - - var lookDir = GlobalPosition + Velocity; - if (!lookDir.IsEqualApprox(GlobalPosition)) - LookAt(lookDir, Vector3.Up, true); - - EnemyModelView.SetCurrentDirection(GlobalBasis, -_player.CurrentBasis.Z); - } #endregion - public virtual void TakeAction() - { - EnemyModelView.PlayPrimaryAttackAnimation(); - } - - public virtual void SetTarget(Vector3 target) + public virtual void Activate() { } - public virtual void Move(Vector3 velocity) - { - _knockbackStrength *= 0.9f; - Velocity = velocity * _movementSpeed + (_knockbackDirection * _knockbackStrength); - MoveAndSlide(); - } - - public virtual void TakeDamage(double damage, ElementType elementType, bool isCriticalHit = false, bool ignoreDefense = false, bool ignoreElementalResistance = false) - { - if (CurrentHP.Value > 0) - { - _damageCalculator.CalculateDamage(damage, - elementType, - _player.Stats.CurrentDefense.Value + _player.Stats.BonusDefense.Value, - _enemyStatResource.ElementalResistance, - isCriticalHit, - ignoreDefense, - ignoreElementalResistance); - GD.Print($"Enemy Hit for {damage} damage."); - CurrentHP.OnNext(CurrentHP.Value - damage); - GD.Print("Current HP: " + CurrentHP.Value); - - if (CurrentHP.Value <= 0) - return; - - EnemyModelView.PlayHitAnimation(); - _enemyLogic.Input(new EnemyLogic.Input.Alerted()); - - if (this is ICanActivate activatable) - activatable.Activate(); - - if (((Weapon)_player.EquippedWeapon.Value).WeaponTag == WeaponTag.SelfDamage) - _player.Stats.SetCurrentHP(_player.Stats.CurrentHP.Value - 5); - } - } - - public void Knockback(float impulse, Vector3 direction) - { - _knockbackDirection = direction; - _knockbackStrength = 0.3f; - } - - public virtual void Die() - { - SetProcess(false); - _movementSpeed = 0; - CurrentHP.OnNext(0); - _enemyLogic.Input(new EnemyLogic.Input.EnemyDefeated()); - _collisionShape.SetDeferred("disabled", true); - EnemyModelView.PlayDeathAnimation(); - var tweener = CreateTween(); - tweener.TweenInterval(1.0f); - tweener.TweenCallback(Callable.From(QueueFree)); - Game.EnemyDefeated(GlobalPosition, _enemyStatResource); - } - - public void SetCurrentHP(int targetHP) - { - CurrentHP.OnNext(targetHP); - } - - public int GetMaximumHP() - { - return _enemyStatResource.MaximumHP; - } - - public virtual void StartAttackTimer() - { - _attackTimer.Timeout += OnAttackTimeout; - } - - public virtual void StopAttackTimer() - { - _attackTimer.Timeout -= OnAttackTimeout; - } - - public void Idle() + public virtual void Idle() { EnemyModelView.PlayIdleAnimation(); } - public void Move() + public virtual void Move() { EnemyModelView.PlayWalkAnimation(); } - public Vector3 GetEnemyGlobalPosition() => GlobalPosition; - - public void SetEnemyGlobalPosition(Vector3 target) + public virtual void PerformAction() { - GlobalPosition = new Vector3(target.X, -0.5f, target.Z); + EnemyModelView.PlayPrimaryAttackAnimation(); } - public IDungeonRoom GetCurrentRoom() + public virtual void ReturnToDefaultState() { - var currentRooms = Game.CurrentFloor.Rooms; - foreach (var room in currentRooms) + + } + + public void SetTarget(Vector3 targetPosition) => TargetPosition = targetPosition; + + public virtual void SetEnemyPosition(Vector3 newPosition) + { + GlobalPosition = newPosition; + + if (this is IHavePatrolBehavior patrolEnemy) + patrolEnemy.PatrolBehavior.HomePosition = GlobalPosition; + + _enemyLogic.Input(new EnemyLogic.Input.Reset()); + } + + public void LookAtTarget(Vector3 targetPosition) + { + var lookDirection = GlobalPosition - targetPosition; + if (lookDirection != GlobalPosition) + LookAt(new Vector3(lookDirection.X, GlobalPosition.Y, lookDirection.Z), Vector3.Up); + } + + public virtual void TakeDamage(int damage) + { + GD.Print($"Enemy Hit for {damage} damage."); + _healthComponent.CurrentHP.OnNext(_healthComponent.CurrentHP.Value - damage); + } + + private void EnemyModelView_HitPlayer() + { + _player.TakeDamage(new Damage(30, ElementType.None, false, false, false)); + } + + public virtual void TakeHit() + { + _enemyLogic.Input(new EnemyLogic.Input.Alert()); + EnemyModelView.PlayHitAnimation(); + } + + public virtual void Die() + { + SetPhysicsProcess(false); + _healthComponent.CurrentHP.OnCompleted(); + _enemyLogic.Input(new EnemyLogic.Input.Defeated()); + EnemyModelView.PlayDeathAnimation(); + var tweener = CreateTween(); + tweener.TweenInterval(1.0f); + tweener.TweenCallback(Callable.From(QueueFree)); + } + + public IDungeonRoom GetCurrentRoom(ImmutableList roomList) + { + foreach (var room in roomList) { var enemiesInCurrentRoom = room.EnemiesInRoom; if (enemiesInCurrentRoom.Contains(this)) @@ -217,22 +160,6 @@ public partial class Enemy : CharacterBody3D, IEnemy, IProvide return null; } - private void OnAttackTimeout() - { - if (GlobalPosition.DistanceTo(_player.CurrentPosition) > 5f) - { - _enemyLogic.Input(new EnemyLogic.Input.Alerted()); - return; - } - - var rng = new RandomNumberGenerator(); - rng.Randomize(); - _enemyLogic.Input(new EnemyLogic.Input.AttackTimer()); - _attackTimer.Stop(); - _attackTimer.WaitTime = rng.RandfRange(2f, 5.0f); - _attackTimer.Start(); - } - private void LineOfSight_BodyEntered(Node3D body) { var overlappingBodies = LineOfSight.GetOverlappingBodies(); @@ -245,25 +172,11 @@ public partial class Enemy : CharacterBody3D, IEnemy, IProvide { var collider = Raycast.GetCollider(); if (collider is IPlayer) - { - Raycast.DebugShapeCustomColor = Color.FromString("Purple", Colors.Purple); - _enemyLogic.Input(new EnemyLogic.Input.Alerted()); - } + _enemyLogic.Input(new EnemyLogic.Input.Follow()); } } } - private void OnHPChanged(double newHP) - { - if (newHP <= 0) - Die(); - } - - private double CalculateDefenseResistance(double incomingDamage) - { - return Mathf.Max(incomingDamage - _enemyStatResource.CurrentDefense, 0.0); - } - public void OnExitTree() { _enemyLogic.Stop(); diff --git a/Zennysoft.Game.Ma/src/enemy/Enemy2D.cs b/Zennysoft.Game.Ma/src/enemy/Enemy2D.cs new file mode 100644 index 00000000..0a2a4263 --- /dev/null +++ b/Zennysoft.Game.Ma/src/enemy/Enemy2D.cs @@ -0,0 +1,48 @@ +using Chickensoft.AutoInject; +using Chickensoft.Introspection; +using Godot; +using Zennysoft.Ma.Adapter; + +namespace Zennysoft.Game.Ma; + +[Meta(typeof(IAutoNode))] +public abstract partial class Enemy2D : Enemy +{ + public override void _Notification(int what) => this.Notify(what); + + public override IEnemyModelView EnemyModelView => _enemyModelView; + + [Node] private EnemyModelView2D _enemyModelView { get; set; } = default!; + + public override void _PhysicsProcess(double delta) + { + _enemyModelView.SetCurrentDirection(GlobalBasis, -_player.CurrentBasis.Z); + } + + protected void PlayerDetector_BodyEntered(Node3D node) + { + if (node is IPlayer) + _enemyLogic.Input(new EnemyLogic.Input.ReachedPlayer()); + } + + protected void PlayerDetector_BodyExited(Node3D node) + { + if (node is IPlayer) + _enemyLogic.Input(new EnemyLogic.Input.Follow()); + } + + protected void OnVelocityComputed(Vector3 safeVelocity) + { + Velocity = safeVelocity; + LookAtTarget(safeVelocity); + if (Velocity > Vector3.Zero) + _enemyLogic.Input(new EnemyLogic.Input.Move()); + else + _enemyLogic.Input(new EnemyLogic.Input.Idle()); + MoveAndSlide(); + } + + protected void EngagePlayerBehavior_TakeAction() => EnemyModelView.PlayPrimaryAttackAnimation(); + + protected void EngagePlayerBehavior_AcquireTarget() => LookAt(new Vector3(_player.CurrentPosition.X, GlobalPosition.Y, _player.CurrentPosition.Z), Vector3.Up, true); +} diff --git a/Zennysoft.Game.Ma/src/enemy/Enemy2D.cs.uid b/Zennysoft.Game.Ma/src/enemy/Enemy2D.cs.uid new file mode 100644 index 00000000..60ff3605 --- /dev/null +++ b/Zennysoft.Game.Ma/src/enemy/Enemy2D.cs.uid @@ -0,0 +1 @@ +uid://d17jxdbqesmvg diff --git a/Zennysoft.Game.Ma/src/enemy/Enemy3D.cs b/Zennysoft.Game.Ma/src/enemy/Enemy3D.cs new file mode 100644 index 00000000..6230139a --- /dev/null +++ b/Zennysoft.Game.Ma/src/enemy/Enemy3D.cs @@ -0,0 +1,14 @@ +using Chickensoft.AutoInject; +using Chickensoft.Introspection; + +namespace Zennysoft.Game.Ma; + +[Meta(typeof(IAutoNode))] +public partial class Enemy3D : Enemy +{ + public override void _Notification(int what) => this.Notify(what); + + public override IEnemyModelView EnemyModelView => _enemyModelView; + + [Node] private EnemyModelView3D _enemyModelView { get; set; } = default!; +} diff --git a/Zennysoft.Game.Ma/src/enemy/Enemy3D.cs.uid b/Zennysoft.Game.Ma/src/enemy/Enemy3D.cs.uid new file mode 100644 index 00000000..e36effda --- /dev/null +++ b/Zennysoft.Game.Ma/src/enemy/Enemy3D.cs.uid @@ -0,0 +1 @@ +uid://bdq3pbyg8pifw diff --git a/Zennysoft.Game.Ma/src/enemy/EnemyModelView.cs b/Zennysoft.Game.Ma/src/enemy/EnemyModelView.cs new file mode 100644 index 00000000..0cc2473b --- /dev/null +++ b/Zennysoft.Game.Ma/src/enemy/EnemyModelView.cs @@ -0,0 +1,24 @@ +using Chickensoft.AutoInject; +using Chickensoft.Introspection; +using Godot; + +namespace Zennysoft.Game.Ma; + +[Meta(typeof(IAutoNode))] +public abstract partial class EnemyModelView : Node3D, IEnemyModelView +{ + public override void _Notification(int what) => this.Notify(what); + + public virtual void PlayActivateAnimation() => throw new System.NotImplementedException(); + public virtual void PlayDeathAnimation() => throw new System.NotImplementedException(); + public virtual void PlayHitAnimation() => throw new System.NotImplementedException(); + public virtual void PlayIdleAnimation() => throw new System.NotImplementedException(); + public virtual void PlayPrimaryAttackAnimation() => throw new System.NotImplementedException(); + public virtual void PlayPrimarySkillAnimation() => throw new System.NotImplementedException(); + public virtual void PlaySecondaryAttackAnimation() => throw new System.NotImplementedException(); + public virtual void PlayWalkAnimation() => throw new System.NotImplementedException(); + + [Signal] + public delegate void HitPlayerEventHandler(); + +} diff --git a/Zennysoft.Game.Ma/src/enemy/EnemyModelView.cs.uid b/Zennysoft.Game.Ma/src/enemy/EnemyModelView.cs.uid new file mode 100644 index 00000000..ec1cea97 --- /dev/null +++ b/Zennysoft.Game.Ma/src/enemy/EnemyModelView.cs.uid @@ -0,0 +1 @@ +uid://6ihdadepmsx2 diff --git a/Zennysoft.Game.Ma/src/enemy/EnemyModelView2D.cs b/Zennysoft.Game.Ma/src/enemy/EnemyModelView2D.cs index 2a20d4ac..3355296d 100644 --- a/Zennysoft.Game.Ma/src/enemy/EnemyModelView2D.cs +++ b/Zennysoft.Game.Ma/src/enemy/EnemyModelView2D.cs @@ -5,7 +5,7 @@ using System.Linq; namespace Zennysoft.Game.Ma; [Meta(typeof(IAutoNode))] -public partial class EnemyModelView2D : Node3D, IEnemyModelView +public partial class EnemyModelView2D : EnemyModelView, IEnemyModelView { private readonly string _idleName = "Idle"; private readonly string _walkingName = "Walking"; @@ -19,7 +19,7 @@ public partial class EnemyModelView2D : Node3D, IEnemyModelView [Node] public AnimatedSprite2D AnimatedSprite { get; set; } = default!; - [Node] public IHitbox Hitbox { get; set; } = default!; + [Node] public Area3D Hitbox { get; set; } = default!; [Node] public AnimationPlayer AnimationPlayer { get; set; } = default!; @@ -37,9 +37,12 @@ public partial class EnemyModelView2D : Node3D, IEnemyModelView public void OnReady() { - _stateMachine = (AnimationNodeStateMachinePlayback)AnimationTree.Get("parameters/playback"); + _stateMachine = (AnimationNodeStateMachinePlayback)AnimationTree.Get("parameters/playback"); + Hitbox.BodyEntered += Hitbox_BodyEntered; } + private void Hitbox_BodyEntered(Node3D body) => EmitSignal(SignalName.HitPlayer); + public void SetCurrentDirection(Basis enemyBasis, Vector3 cameraDirection) => _enemyDirection = GetEnemyDirection(enemyBasis, cameraDirection, _upperThreshold, _lowerThreshold); public void PlayPrimaryAttackAnimation() => _stateMachine.Travel(_primaryAttackName); @@ -52,115 +55,116 @@ public partial class EnemyModelView2D : Node3D, IEnemyModelView public void PlayWalkAnimation() => _stateMachine.Travel(_walkingName); - public void PlayActivateAnimation() => _stateMachine.Travel(_activateName); + public void PlayActivateAnimation() + { + } public void PlayHitAnimation() { - LoadShader("res://src/vfx/shaders/DamageHit.gdshader"); - var tweener = GetTree().CreateTween(); - tweener.TweenMethod(Callable.From((float x) => SetShaderValue(x)), 0.0f, 1.0f, 1.0f); + LoadShader("res://src/vfx/shaders/DamageHit.gdshader"); + var tweener = GetTree().CreateTween(); + tweener.TweenMethod(Callable.From((float x) => SetShaderValue(x)), 0.0f, 1.0f, 1.0f); } public void PlayDeathAnimation() { - LoadShader("res://src/vfx/shaders/PixelMelt.gdshader"); - var tweener = GetTree().CreateTween(); - tweener.TweenMethod(Callable.From((float x) => SetShaderValue(x)), 0.0f, 1.0f, 0.8f); - tweener.TweenCallback(Callable.From(QueueFree)); + LoadShader("res://src/vfx/shaders/PixelMelt.gdshader"); + var tweener = GetTree().CreateTween(); + tweener.TweenMethod(Callable.From((float x) => SetShaderValue(x)), 0.0f, 1.0f, 0.8f); } private EnemyDirection GetEnemyDirection( - Basis enemyBasis, - Vector3 cameraDirection, - float rotateUpperThreshold, - float rotateLowerThreshold) + Basis enemyBasis, + Vector3 cameraDirection, + float rotateUpperThreshold, + float rotateLowerThreshold) { - var enemyForwardDirection = enemyBasis.Z; - var enemyLeftDirection = enemyBasis.X; + var enemyForwardDirection = enemyBasis.Z; + var enemyLeftDirection = enemyBasis.X; - var leftDotProduct = enemyLeftDirection.Dot(cameraDirection); - var forwardDotProduct = enemyForwardDirection.Dot(cameraDirection); + var leftDotProduct = enemyLeftDirection.Dot(cameraDirection); + var forwardDotProduct = enemyForwardDirection.Dot(cameraDirection); - // Check if forward facing. If the dot product is -1, the enemy is facing the camera. - if (forwardDotProduct < _lowerThreshold) - { - SetForward(); - return EnemyDirection.Forward; - } + // Check if forward facing. If the dot product is -1, the enemy is facing the camera. + if (forwardDotProduct < _lowerThreshold) + { + SetForward(); + return EnemyDirection.Forward; + } - // Check if backward facing. If the dot product is 1, the enemy is facing the same direction as the camera. - else if (forwardDotProduct > rotateUpperThreshold) - { - SetBack(); - return EnemyDirection.Backward; - } - else - { - // If the dot product of the perpendicular direction is positive (up to 1), the enemy is facing to the left (since it's mirrored). - if (leftDotProduct < _lowerThreshold) - { - SetRight(); - return EnemyDirection.Left; - } + // Check if backward facing. If the dot product is 1, the enemy is facing the same direction as the camera. + else if (forwardDotProduct > rotateUpperThreshold) + { + SetBack(); + return EnemyDirection.Backward; + } + else + { + // If the dot product of the perpendicular direction is positive (up to 1), the enemy is facing to the left (since it's mirrored). + if (leftDotProduct < _lowerThreshold) + { + SetRight(); + return EnemyDirection.Left; + } - // Check if side facing. If the dot product is close to zero in the positive or negative direction, its close to the threshold for turning. - if (leftDotProduct > rotateUpperThreshold) - { - SetLeft(); - return EnemyDirection.Right; - } - } + // Check if side facing. If the dot product is close to zero in the positive or negative direction, its close to the threshold for turning. + if (leftDotProduct > rotateUpperThreshold) + { + SetLeft(); + return EnemyDirection.Right; + } + } - return _enemyDirection; + return _enemyDirection; } private void LoadShader(string shaderPath) { - var shader = GD.Load(shaderPath); - var sprites = FindChildren("*", "AnimatedSprite2D", true).Cast(); - foreach (var sprite in sprites) - { - sprite.Material = new ShaderMaterial(); - var shaderMaterial = (ShaderMaterial)sprite.Material; - shaderMaterial.Shader = shader; - } + var shader = GD.Load(shaderPath); + var sprites = FindChildren("*", "AnimatedSprite2D", true).Cast(); + foreach (var sprite in sprites) + { + sprite.Material = new ShaderMaterial(); + var shaderMaterial = (ShaderMaterial)sprite.Material; + shaderMaterial.Shader = shader; + } } private void SetShaderValue(float shaderValue) { - var sprites = FindChildren("*", "AnimatedSprite2D", true).Cast(); - foreach (var sprite in sprites) - { - var shaderMaterial = (ShaderMaterial)sprite.Material; - shaderMaterial.SetShaderParameter("progress", shaderValue); - } + var sprites = FindChildren("*", "AnimatedSprite2D", true).Cast(); + foreach (var sprite in sprites) + { + var shaderMaterial = (ShaderMaterial)sprite.Material; + shaderMaterial.SetShaderParameter("progress", shaderValue); + } } private void SetForward() { - _enemyDirection = EnemyDirection.Forward; + _enemyDirection = EnemyDirection.Forward; } private void SetLeft() { - _enemyDirection = EnemyDirection.Left; + _enemyDirection = EnemyDirection.Left; } private void SetRight() { - _enemyDirection = EnemyDirection.Right; + _enemyDirection = EnemyDirection.Right; } private void SetBack() { - _enemyDirection = EnemyDirection.Backward; + _enemyDirection = EnemyDirection.Backward; } private enum EnemyDirection { - Left, - Right, - Forward, - Backward + Left, + Right, + Forward, + Backward } } diff --git a/Zennysoft.Game.Ma/src/enemy/EnemyModelView3D.cs b/Zennysoft.Game.Ma/src/enemy/EnemyModelView3D.cs index 95fdd6e5..fdbda403 100644 --- a/Zennysoft.Game.Ma/src/enemy/EnemyModelView3D.cs +++ b/Zennysoft.Game.Ma/src/enemy/EnemyModelView3D.cs @@ -5,7 +5,7 @@ using Godot; namespace Zennysoft.Game.Ma; [Meta(typeof(IAutoNode))] -public partial class EnemyModelView3D : Node3D, IEnemyModelView +public partial class EnemyModelView3D : EnemyModelView, IEnemyModelView { private const string PRIMARY_ATTACK = "primary_attack"; private const string SECONDARY_ATTACK = "secondary_attack"; @@ -28,76 +28,76 @@ public partial class EnemyModelView3D : Node3D, IEnemyModelView public void PlayPrimaryAttackAnimation() { - AnimationTree.Get(PARAMETERS_PLAYBACK).As().Travel(PRIMARY_ATTACK); + AnimationTree.Get(PARAMETERS_PLAYBACK).As().Travel(PRIMARY_ATTACK); } public void PlaySecondaryAttackAnimation() { - AnimationTree.Get(PARAMETERS_PLAYBACK).As().Travel(SECONDARY_ATTACK); + AnimationTree.Get(PARAMETERS_PLAYBACK).As().Travel(SECONDARY_ATTACK); } public void PlayPrimarySkillAnimation() { - AnimationTree.Get(PARAMETERS_PLAYBACK).As().Travel(PRIMARY_SKILL); + AnimationTree.Get(PARAMETERS_PLAYBACK).As().Travel(PRIMARY_SKILL); } public void PlayHitAnimation() { - var tweener = CreateTween(); - ChangeMaterial(); - tweener.TweenMethod(Callable.From((float x) => SetTransparency(x)), 0.0f, 0.5f, 0.3f); - tweener.TweenCallback(Callable.From(ClearDamageEffect)); + var tweener = CreateTween(); + ChangeMaterial(); + tweener.TweenMethod(Callable.From((float x) => SetTransparency(x)), 0.0f, 0.5f, 0.3f); + tweener.TweenCallback(Callable.From(ClearDamageEffect)); } public void PlayDeathAnimation() { - LoadShader("res://src/enemy/EnemyDie.tres"); - var tweener = CreateTween(); - tweener.TweenMethod(Callable.From((float x) => SetTransparency(x)), 0.0f, 1.0f, 0.8f); - tweener.TweenCallback(Callable.From(QueueFree)); + LoadShader("res://src/enemy/EnemyDie.tres"); + var tweener = CreateTween(); + tweener.TweenMethod(Callable.From((float x) => SetTransparency(x)), 0.0f, 1.0f, 0.8f); + tweener.TweenCallback(Callable.From(QueueFree)); } public void PlayWalkAnimation() { - AnimationTree.Get(PARAMETERS_PLAYBACK).As().Travel(WALK); + AnimationTree.Get(PARAMETERS_PLAYBACK).As().Travel(WALK); } public void PlayActivateAnimation() => AnimationTree.Get(PARAMETERS_PLAYBACK).As().Travel("Activate"); public void PlayIdleAnimation() { - AnimationTree.Get(PARAMETERS_PLAYBACK).As().Travel(IDLE); + AnimationTree.Get(PARAMETERS_PLAYBACK).As().Travel(IDLE); } private void ChangeMaterial() { - var material = new StandardMaterial3D - { - AlbedoColor = Color.FromHsv(0, 1, 1, 1) - }; - MeshInstance.MaterialOverride = (Material)material.Duplicate(); + var material = new StandardMaterial3D + { + AlbedoColor = Color.FromHsv(0, 1, 1, 1) + }; + MeshInstance.MaterialOverride = (Material)material.Duplicate(); } private void LoadShader(string shaderPath) { - var shader = GD.Load(shaderPath); - MeshInstance.MaterialOverride = shader; + var shader = GD.Load(shaderPath); + MeshInstance.MaterialOverride = shader; } private void ClearDamageEffect() { - MeshInstance.MaterialOverride = null; - MeshInstance.Transparency = 0; + MeshInstance.MaterialOverride = null; + MeshInstance.Transparency = 0; } private void SetTransparency(float transparencyAmount) { - MeshInstance.Transparency = transparencyAmount; + MeshInstance.Transparency = transparencyAmount; } public override void _ExitTree() { - AnimationTree.Get(PARAMETERS_PLAYBACK).As().Stop(); + AnimationTree.Get(PARAMETERS_PLAYBACK).As().Stop(); } public void SetCurrentDirection(Basis enemyBasis, Vector3 cameraDirection) diff --git a/Zennysoft.Game.Ma/src/enemy/EnemyStatResource.cs b/Zennysoft.Game.Ma/src/enemy/EnemyStatResource.cs index 6183ead2..bbbb154f 100644 --- a/Zennysoft.Game.Ma/src/enemy/EnemyStatResource.cs +++ b/Zennysoft.Game.Ma/src/enemy/EnemyStatResource.cs @@ -6,12 +6,6 @@ namespace Zennysoft.Game.Ma; [GlobalClass] public partial class EnemyStatResource : Resource { - [Export] - public double CurrentHP { get; set; } - - [Export] - public int MaximumHP { get; set; } - [Export] public int CurrentAttack { get; set; } diff --git a/Zennysoft.Game.Ma/src/enemy/FollowsPlayerEnemyBase.cs b/Zennysoft.Game.Ma/src/enemy/FollowsPlayerEnemyBase.cs deleted file mode 100644 index 41f33e9a..00000000 --- a/Zennysoft.Game.Ma/src/enemy/FollowsPlayerEnemyBase.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Godot; - -namespace Zennysoft.Game.Ma; - -public abstract partial class FollowsPlayerEnemy : Enemy -{ - [Export] - public double StopMovingAndAttackDistance = 2.5f; - - [Export] - public double StopFollowingPlayerDistance = 30f; - - [Export] - public double StopAttackingAndMoveDistance = 3f; - - public virtual void FollowPlayerAndAttack(IEnemyLogic enemyLogic, Vector3 currentPosition, Vector3 targetPosition) - { - if (enemyLogic.Value is EnemyLogic.State.FollowPlayer && currentPosition.DistanceTo(targetPosition) < StopMovingAndAttackDistance) - enemyLogic.Input(new EnemyLogic.Input.StartAttacking()); - else if (enemyLogic.Value is EnemyLogic.State.FollowPlayer && currentPosition.DistanceTo(targetPosition) > StopFollowingPlayerDistance) - enemyLogic.Input(new EnemyLogic.Input.LostPlayer()); - else if (enemyLogic.Value is EnemyLogic.State.Attacking && currentPosition.DistanceTo(targetPosition) > StopAttackingAndMoveDistance) - enemyLogic.Input(new EnemyLogic.Input.Alerted()); - } -} diff --git a/Zennysoft.Game.Ma/src/enemy/ICanActivate.cs b/Zennysoft.Game.Ma/src/enemy/ICanActivate.cs deleted file mode 100644 index f922e86e..00000000 --- a/Zennysoft.Game.Ma/src/enemy/ICanActivate.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace Zennysoft.Game.Ma; - -public interface ICanActivate -{ - public void Activate(); -} diff --git a/Zennysoft.Game.Ma/src/enemy/IEnemy.cs b/Zennysoft.Game.Ma/src/enemy/IEnemy.cs deleted file mode 100644 index 1fd05dc5..00000000 --- a/Zennysoft.Game.Ma/src/enemy/IEnemy.cs +++ /dev/null @@ -1,39 +0,0 @@ -using Chickensoft.Collections; -using Godot; -using Zennysoft.Game.Abstractions; -using Zennysoft.Ma.Adapter; - -namespace Zennysoft.Game.Ma; - -public interface IEnemy : IKillable -{ - public void TakeAction(); - - public void Move(Vector3 velocity); - - public void TakeDamage(double damage, ElementType elementType = ElementType.None, bool isCriticalHit = false, bool ignoreDefense = false, bool ignoreElementalResistance = false); - - public void Knockback(float impulse, Vector3 direction); - - public AutoProp CurrentHP { get; } - - public void SetCurrentHP(int newHP); - - public int GetMaximumHP(); - - public void StartAttackTimer(); - - public void StopAttackTimer(); - - public abstract void SetTarget(Vector3 target); - - public void SetEnemyGlobalPosition(Vector3 target); - - public Vector3 GetEnemyGlobalPosition(); - - public IDungeonRoom GetCurrentRoom(); - - public void Idle(); - - public void Move(); -} diff --git a/Zennysoft.Game.Ma/src/enemy/IEnemyModelView.cs b/Zennysoft.Game.Ma/src/enemy/IEnemyModelView.cs index dc5bb8ca..c9bbfb7f 100644 --- a/Zennysoft.Game.Ma/src/enemy/IEnemyModelView.cs +++ b/Zennysoft.Game.Ma/src/enemy/IEnemyModelView.cs @@ -1,12 +1,9 @@ using Chickensoft.GodotNodeInterfaces; -using Godot; namespace Zennysoft.Game.Ma; public interface IEnemyModelView : INode3D { - void SetCurrentDirection(Basis enemyBasis, Vector3 cameraDirection); - public void PlayIdleAnimation(); public void PlayWalkAnimation(); @@ -22,4 +19,6 @@ public interface IEnemyModelView : INode3D public void PlayHitAnimation(); public void PlayDeathAnimation(); + + event EnemyModelView.HitPlayerEventHandler HitPlayer; } diff --git a/Zennysoft.Game.Ma/src/enemy/IKnockbackable.cs b/Zennysoft.Game.Ma/src/enemy/IKnockbackable.cs deleted file mode 100644 index 4814a138..00000000 --- a/Zennysoft.Game.Ma/src/enemy/IKnockbackable.cs +++ /dev/null @@ -1,8 +0,0 @@ -using Godot; - -namespace Zennysoft.Game.Ma; - -public interface IKnockbackable -{ - public void Knockback(float impulse, Vector3 direction); -} diff --git a/Zennysoft.Game.Ma/src/enemy/INavigationAgentClient.cs b/Zennysoft.Game.Ma/src/enemy/INavigationAgentClient.cs deleted file mode 100644 index 68b59ff5..00000000 --- a/Zennysoft.Game.Ma/src/enemy/INavigationAgentClient.cs +++ /dev/null @@ -1,10 +0,0 @@ -using Godot; - -namespace Zennysoft.Game.Ma; - -public interface INavigationAgentClient -{ - public Vector3 Velocity { get; } - - public void SetTarget(Vector3 target); -} diff --git a/Zennysoft.Game.Ma/src/enemy/NavigationAgentClient.cs b/Zennysoft.Game.Ma/src/enemy/NavigationAgentClient.cs deleted file mode 100644 index 7d93c8b6..00000000 --- a/Zennysoft.Game.Ma/src/enemy/NavigationAgentClient.cs +++ /dev/null @@ -1,57 +0,0 @@ -using Chickensoft.AutoInject; -using Chickensoft.Introspection; -using Godot; - -namespace Zennysoft.Game.Ma; - -[Meta(typeof(IAutoNode))] -public partial class NavigationAgentClient : Node3D, INavigationAgentClient -{ - public override void _Notification(int what) => this.Notify(what); - - [Node] private NavigationAgent3D _navAgent { get; set; } = default!; - - [Node] private Timer _patrolTimer { get; set; } = default!; - - public Vector3 Velocity { get; private set; } = Vector3.Zero; - - private double _speedMultiplier = 2f; - - public void Setup() - { - _navAgent.VelocityComputed += OnVelocityComputed; - _navAgent.WaypointReached += _navAgent_WaypointReached; - } - - public override void _PhysicsProcess(double delta) - { - if (_navAgent.IsNavigationFinished()) - return; - - var nextPathPosition = _navAgent.GetNextPathPosition(); - var newVelocity = GlobalPosition.DirectionTo(nextPathPosition) * (float)_speedMultiplier; - - if (_navAgent.AvoidanceEnabled) - _navAgent.Velocity = newVelocity; - else - OnVelocityComputed(newVelocity); - } - - public void SetTarget(Vector3 target) - { - _navAgent.TargetPosition = target; - } - - private void OnVelocityComputed(Vector3 safeVelocity) - { - Velocity = safeVelocity; - } - - private async void _navAgent_WaypointReached(Godot.Collections.Dictionary details) - { - _speedMultiplier = 0; - await ToSignal(GetTree().CreateTimer(2f), "timeout"); - _speedMultiplier = 2f; - } - -} diff --git a/Zennysoft.Game.Ma/src/enemy/NavigationAgentClient.tscn b/Zennysoft.Game.Ma/src/enemy/NavigationAgentClient.tscn deleted file mode 100644 index 9e7c668d..00000000 --- a/Zennysoft.Game.Ma/src/enemy/NavigationAgentClient.tscn +++ /dev/null @@ -1,12 +0,0 @@ -[gd_scene load_steps=2 format=3 uid="uid://pbnsngx5jvrh"] - -[ext_resource type="Script" uid="uid://rcnriaxfld2h" path="res://src/enemy/NavigationAgentClient.cs" id="1_qwonp"] - -[node name="NavigationAgentClient" type="Node3D"] -script = ExtResource("1_qwonp") - -[node name="NavAgent" type="NavigationAgent3D" parent="."] -unique_name_in_owner = true -avoidance_enabled = true -radius = 1.5 -debug_enabled = true diff --git a/Zennysoft.Game.Ma/src/enemy/PatrolComponent.cs.uid b/Zennysoft.Game.Ma/src/enemy/PatrolComponent.cs.uid new file mode 100644 index 00000000..2ce020e0 --- /dev/null +++ b/Zennysoft.Game.Ma/src/enemy/PatrolComponent.cs.uid @@ -0,0 +1 @@ +uid://vjb6sjktj6m0 diff --git a/Zennysoft.Game.Ma/src/enemy/PatrolComponent.tscn b/Zennysoft.Game.Ma/src/enemy/PatrolComponent.tscn new file mode 100644 index 00000000..8e218682 --- /dev/null +++ b/Zennysoft.Game.Ma/src/enemy/PatrolComponent.tscn @@ -0,0 +1,13 @@ +[gd_scene load_steps=2 format=3 uid="uid://p74f12fh5v0i"] + +[ext_resource type="Script" uid="uid://vjb6sjktj6m0" path="res://src/enemy/PatrolComponent.cs" id="1_dhoym"] + +[node name="PatrolComponent" type="Node3D"] +script = ExtResource("1_dhoym") + +[node name="Navigation" type="Node3D" parent="."] + +[node name="NavigationAgent" type="NavigationAgent3D" parent="Navigation"] +unique_name_in_owner = true +avoidance_enabled = true +debug_enabled = true diff --git a/Zennysoft.Game.Ma/src/enemy/PatrolTypeEnemy.cs.uid b/Zennysoft.Game.Ma/src/enemy/PatrolTypeEnemy.cs.uid new file mode 100644 index 00000000..dd67c29b --- /dev/null +++ b/Zennysoft.Game.Ma/src/enemy/PatrolTypeEnemy.cs.uid @@ -0,0 +1 @@ +uid://dssu6tgi8dapq diff --git a/Zennysoft.Game.Ma/src/enemy/animation_state_machines/WalkingStateMachine.tres b/Zennysoft.Game.Ma/src/enemy/animation_state_machines/WalkingStateMachine.tres index b4b7cb52..c152b7ad 100644 --- a/Zennysoft.Game.Ma/src/enemy/animation_state_machines/WalkingStateMachine.tres +++ b/Zennysoft.Game.Ma/src/enemy/animation_state_machines/WalkingStateMachine.tres @@ -1,4 +1,4 @@ -[gd_resource type="AnimationNodeStateMachine" load_steps=18 format=3 uid="uid://cy2ngl55c0rws"] +[gd_resource type="AnimationNodeStateMachine" load_steps=21 format=3 uid="uid://cy2ngl55c0rws"] [sub_resource type="AnimationNodeAnimation" id="AnimationNodeAnimation_ivy74"] animation = &"idle_back_walk" @@ -14,6 +14,7 @@ animation = &"idle_right_walk" [sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_ivy74"] advance_mode = 2 +advance_expression = "_enemyDirection == 2" [sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_x7uye"] switch_mode = 1 @@ -75,8 +76,21 @@ switch_mode = 1 advance_mode = 2 advance_expression = "_enemyDirection == 1" +[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_v537b"] +advance_mode = 2 +advance_expression = "_enemyDirection == 0" + +[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_11ejb"] +advance_mode = 2 +advance_expression = "_enemyDirection == 1" + +[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_ear7g"] +advance_mode = 2 +advance_expression = "_enemyDirection == 3" + [resource] states/End/position = Vector2(910, 100) +states/Start/position = Vector2(102, 111) states/back/node = SubResource("AnimationNodeAnimation_ivy74") states/back/position = Vector2(542, 276) states/forward/node = SubResource("AnimationNodeAnimation_x7uye") @@ -85,4 +99,5 @@ states/left/node = SubResource("AnimationNodeAnimation_djeua") states/left/position = Vector2(378, 179) states/right/node = SubResource("AnimationNodeAnimation_8wbs7") states/right/position = Vector2(701, 179) -transitions = ["Start", "forward", SubResource("AnimationNodeStateMachineTransition_ivy74"), "forward", "left", SubResource("AnimationNodeStateMachineTransition_x7uye"), "left", "forward", SubResource("AnimationNodeStateMachineTransition_djeua"), "forward", "back", SubResource("AnimationNodeStateMachineTransition_8wbs7"), "back", "forward", SubResource("AnimationNodeStateMachineTransition_mnr4r"), "left", "right", SubResource("AnimationNodeStateMachineTransition_l2wq1"), "right", "left", SubResource("AnimationNodeStateMachineTransition_jwlar"), "back", "left", SubResource("AnimationNodeStateMachineTransition_fdoul"), "left", "back", SubResource("AnimationNodeStateMachineTransition_kpotx"), "forward", "right", SubResource("AnimationNodeStateMachineTransition_lfuuf"), "right", "forward", SubResource("AnimationNodeStateMachineTransition_dfvqa"), "right", "back", SubResource("AnimationNodeStateMachineTransition_dnvt3"), "back", "right", SubResource("AnimationNodeStateMachineTransition_m7aft")] +transitions = ["Start", "forward", SubResource("AnimationNodeStateMachineTransition_ivy74"), "forward", "left", SubResource("AnimationNodeStateMachineTransition_x7uye"), "left", "forward", SubResource("AnimationNodeStateMachineTransition_djeua"), "forward", "back", SubResource("AnimationNodeStateMachineTransition_8wbs7"), "back", "forward", SubResource("AnimationNodeStateMachineTransition_mnr4r"), "left", "right", SubResource("AnimationNodeStateMachineTransition_l2wq1"), "right", "left", SubResource("AnimationNodeStateMachineTransition_jwlar"), "back", "left", SubResource("AnimationNodeStateMachineTransition_fdoul"), "left", "back", SubResource("AnimationNodeStateMachineTransition_kpotx"), "forward", "right", SubResource("AnimationNodeStateMachineTransition_lfuuf"), "right", "forward", SubResource("AnimationNodeStateMachineTransition_dfvqa"), "right", "back", SubResource("AnimationNodeStateMachineTransition_dnvt3"), "back", "right", SubResource("AnimationNodeStateMachineTransition_m7aft"), "Start", "left", SubResource("AnimationNodeStateMachineTransition_v537b"), "Start", "right", SubResource("AnimationNodeStateMachineTransition_11ejb"), "Start", "back", SubResource("AnimationNodeStateMachineTransition_ear7g")] +graph_offset = Vector2(0, 26.7654) diff --git a/Zennysoft.Game.Ma/src/enemy/behaviors/EngagePlayerBehavior.cs b/Zennysoft.Game.Ma/src/enemy/behaviors/EngagePlayerBehavior.cs new file mode 100644 index 00000000..4c79fc32 --- /dev/null +++ b/Zennysoft.Game.Ma/src/enemy/behaviors/EngagePlayerBehavior.cs @@ -0,0 +1,63 @@ +using Chickensoft.AutoInject; +using Chickensoft.Introspection; +using Godot; + +namespace Zennysoft.Game.Ma; + +[Meta(typeof(IAutoNode))] +public partial class EngagePlayerBehavior : Node, IEngagePlayerBehavior +{ + public override void _Notification(int what) => this.Notify(what); + + [Export] public float _minimumAttackTime { get; set; } = 2f; + [Export] public float _maximumAttackTime { get; set; } = 5f; + [Export] public float _acquireTargetTime { get; set; } = 1f; + + private Timer _actionTimer { get; set; } = default!; + private Timer _acquireTargetTimer { get; set; } = default!; + + [Signal] public delegate void TakeActionEventHandler(); + [Signal] public delegate void AcquireTargetEventHandler(); + + public void OnReady() + { + _actionTimer = new Timer(); + _acquireTargetTimer = new Timer() { WaitTime = _acquireTargetTime }; + _actionTimer.WaitTime = RandomizeTimer(_minimumAttackTime, _maximumAttackTime); + _actionTimer.Timeout += OnAttackTimeout; + _acquireTargetTimer.Timeout += OnAcquireTargetTimeout; + AddChild(_actionTimer); + AddChild(_acquireTargetTimer); + } + + public void Engage() + { + _actionTimer.Start(); + _acquireTargetTimer.Start(); + } + + public void Disengage() + { + _actionTimer.Stop(); + _acquireTargetTimer.Stop(); + } + + private void OnAttackTimeout() + { + _actionTimer.WaitTime = RandomizeTimer(_minimumAttackTime, _maximumAttackTime); + _actionTimer.Start(); + EmitSignal(SignalName.TakeAction); + } + + private void OnAcquireTargetTimeout() + { + EmitSignal(SignalName.AcquireTarget); + } + + private float RandomizeTimer(float min, float max) + { + var rng = new RandomNumberGenerator(); + rng.Randomize(); + return rng.RandfRange(min, max); + } +} diff --git a/Zennysoft.Game.Ma/src/enemy/behaviors/EngagePlayerBehavior.cs.uid b/Zennysoft.Game.Ma/src/enemy/behaviors/EngagePlayerBehavior.cs.uid new file mode 100644 index 00000000..f9447e7f --- /dev/null +++ b/Zennysoft.Game.Ma/src/enemy/behaviors/EngagePlayerBehavior.cs.uid @@ -0,0 +1 @@ +uid://bbe5nt3kpvk0f diff --git a/Zennysoft.Game.Ma/src/enemy/behaviors/EngagePlayerBehavior.tscn b/Zennysoft.Game.Ma/src/enemy/behaviors/EngagePlayerBehavior.tscn new file mode 100644 index 00000000..4a8a0c03 --- /dev/null +++ b/Zennysoft.Game.Ma/src/enemy/behaviors/EngagePlayerBehavior.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://8bcme8ao4axa"] + +[ext_resource type="Script" uid="uid://bbe5nt3kpvk0f" path="res://src/enemy/behaviors/EngagePlayerBehavior.cs" id="1_7r6b3"] + +[node name="EngagePlayerBehavior" type="Node"] +script = ExtResource("1_7r6b3") diff --git a/Zennysoft.Game.Ma/src/enemy/behaviors/FollowBehavior.cs b/Zennysoft.Game.Ma/src/enemy/behaviors/FollowBehavior.cs new file mode 100644 index 00000000..c4f822e3 --- /dev/null +++ b/Zennysoft.Game.Ma/src/enemy/behaviors/FollowBehavior.cs @@ -0,0 +1,57 @@ +using Chickensoft.AutoInject; +using Chickensoft.Introspection; +using Godot; +using System.Linq; +using Zennysoft.Game.Abstractions.Entity; +using Zennysoft.Ma.Adapter; + +namespace Zennysoft.Game.Ma; + +[Meta(typeof(IAutoNode))] +public partial class FollowBehavior : Node3D, IBehavior +{ + public override void _Notification(int what) => this.Notify(what); + + [Export] private double _followSpeed { get; set; } = 100f; + + [Export] private double _thinkTime { get; set; } = 2f; + + [Signal] public delegate void OnVelocityComputedEventHandler(Vector3 safeVelocity); + + [Dependency] private IPlayer _player => this.DependOn(() => GetParent().GetChildren().OfType().Single()); + + private NavigationAgent3D _navigationAgent; + private Timer _thinkTimer; + + public void Init(NavigationAgent3D navigationAgent) + { + _navigationAgent = navigationAgent; + _thinkTimer = new Timer() { WaitTime = _thinkTime }; + _thinkTimer.Timeout += OnTimeout; + AddChild(_thinkTimer); + SetPhysicsProcess(false); + } + + private void OnTimeout() => _navigationAgent.TargetPosition = _player.CurrentPosition; + + public void StartFollow(NavigationAgent3D navigationAgent) + { + _navigationAgent.TargetPosition = _player.CurrentPosition; + _thinkTimer.Start(); + SetPhysicsProcess(true); + } + + public void StopFollow() + { + SetPhysicsProcess(false); + _thinkTimer.Stop(); + } + + public void OnPhysicsProcess(double delta) + { + var nextVelocity = _navigationAgent.GetNextPathPosition(); + var parent = GetParent() as Node3D; + var velocity = parent.GlobalPosition.DirectionTo(nextVelocity) * (float)_followSpeed * (float)delta; + EmitSignal(SignalName.OnVelocityComputed, velocity); + } +} diff --git a/Zennysoft.Game.Ma/src/enemy/behaviors/FollowBehavior.cs.uid b/Zennysoft.Game.Ma/src/enemy/behaviors/FollowBehavior.cs.uid new file mode 100644 index 00000000..29222e66 --- /dev/null +++ b/Zennysoft.Game.Ma/src/enemy/behaviors/FollowBehavior.cs.uid @@ -0,0 +1 @@ +uid://chfhmralfmwva diff --git a/Zennysoft.Game.Ma/src/enemy/behaviors/FollowBehavior.tscn b/Zennysoft.Game.Ma/src/enemy/behaviors/FollowBehavior.tscn new file mode 100644 index 00000000..0f56a267 --- /dev/null +++ b/Zennysoft.Game.Ma/src/enemy/behaviors/FollowBehavior.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://mqj4jju3870v"] + +[ext_resource type="Script" uid="uid://chfhmralfmwva" path="res://src/enemy/behaviors/FollowBehavior.cs" id="1_3rkk2"] + +[node name="FollowBehavior" type="Node3D"] +script = ExtResource("1_3rkk2") diff --git a/Zennysoft.Game.Ma/src/enemy/behaviors/IEngagePlayerBehavior.cs b/Zennysoft.Game.Ma/src/enemy/behaviors/IEngagePlayerBehavior.cs new file mode 100644 index 00000000..4aaf03c5 --- /dev/null +++ b/Zennysoft.Game.Ma/src/enemy/behaviors/IEngagePlayerBehavior.cs @@ -0,0 +1,8 @@ +namespace Zennysoft.Game.Ma; + +public interface IEngagePlayerBehavior +{ + public void Engage(); + + public void Disengage(); +} diff --git a/Zennysoft.Game.Ma/src/enemy/behaviors/IEngagePlayerBehavior.cs.uid b/Zennysoft.Game.Ma/src/enemy/behaviors/IEngagePlayerBehavior.cs.uid new file mode 100644 index 00000000..c7a290b2 --- /dev/null +++ b/Zennysoft.Game.Ma/src/enemy/behaviors/IEngagePlayerBehavior.cs.uid @@ -0,0 +1 @@ +uid://dnl6yl3c7f2xs diff --git a/Zennysoft.Game.Ma/src/enemy/behaviors/IHaveEngagePlayerBehavior.cs b/Zennysoft.Game.Ma/src/enemy/behaviors/IHaveEngagePlayerBehavior.cs new file mode 100644 index 00000000..89c9c38b --- /dev/null +++ b/Zennysoft.Game.Ma/src/enemy/behaviors/IHaveEngagePlayerBehavior.cs @@ -0,0 +1,6 @@ +namespace Zennysoft.Game.Ma; + +public interface IHaveEngagePlayerBehavior +{ + public EngagePlayerBehavior EngagePlayerBehavior { get; } +} diff --git a/Zennysoft.Game.Ma/src/enemy/behaviors/IHaveEngagePlayerBehavior.cs.uid b/Zennysoft.Game.Ma/src/enemy/behaviors/IHaveEngagePlayerBehavior.cs.uid new file mode 100644 index 00000000..af605238 --- /dev/null +++ b/Zennysoft.Game.Ma/src/enemy/behaviors/IHaveEngagePlayerBehavior.cs.uid @@ -0,0 +1 @@ +uid://wiktkwsvcf38 diff --git a/Zennysoft.Game.Ma/src/enemy/behaviors/IHaveFollowBehavior.cs b/Zennysoft.Game.Ma/src/enemy/behaviors/IHaveFollowBehavior.cs new file mode 100644 index 00000000..73ee12b0 --- /dev/null +++ b/Zennysoft.Game.Ma/src/enemy/behaviors/IHaveFollowBehavior.cs @@ -0,0 +1,10 @@ +using Godot; + +namespace Zennysoft.Game.Ma; + +public interface IHaveFollowBehavior +{ + public FollowBehavior FollowBehavior { get; } + + public NavigationAgent3D NavigationAgent { get; } +} diff --git a/Zennysoft.Game.Ma/src/enemy/behaviors/IHaveFollowBehavior.cs.uid b/Zennysoft.Game.Ma/src/enemy/behaviors/IHaveFollowBehavior.cs.uid new file mode 100644 index 00000000..2243db75 --- /dev/null +++ b/Zennysoft.Game.Ma/src/enemy/behaviors/IHaveFollowBehavior.cs.uid @@ -0,0 +1 @@ +uid://j73uc3svvwj diff --git a/Zennysoft.Game.Ma/src/enemy/behaviors/IHavePatrolBehavior.cs b/Zennysoft.Game.Ma/src/enemy/behaviors/IHavePatrolBehavior.cs new file mode 100644 index 00000000..530e631f --- /dev/null +++ b/Zennysoft.Game.Ma/src/enemy/behaviors/IHavePatrolBehavior.cs @@ -0,0 +1,6 @@ +namespace Zennysoft.Game.Ma; + +public interface IHavePatrolBehavior +{ + public PatrolBehavior PatrolBehavior { get; } +} diff --git a/Zennysoft.Game.Ma/src/enemy/behaviors/IHavePatrolBehavior.cs.uid b/Zennysoft.Game.Ma/src/enemy/behaviors/IHavePatrolBehavior.cs.uid new file mode 100644 index 00000000..98e9ab79 --- /dev/null +++ b/Zennysoft.Game.Ma/src/enemy/behaviors/IHavePatrolBehavior.cs.uid @@ -0,0 +1 @@ +uid://dxepau20ove64 diff --git a/Zennysoft.Game.Ma/src/enemy/behaviors/PatrolBehavior.cs b/Zennysoft.Game.Ma/src/enemy/behaviors/PatrolBehavior.cs new file mode 100644 index 00000000..912bd6c9 --- /dev/null +++ b/Zennysoft.Game.Ma/src/enemy/behaviors/PatrolBehavior.cs @@ -0,0 +1,87 @@ +using Chickensoft.AutoInject; +using Chickensoft.Introspection; +using Godot; +using Godot.Collections; +using Zennysoft.Game.Abstractions.Entity; + +namespace Zennysoft.Game.Ma; + +[Meta(typeof(IAutoNode))] +public partial class PatrolBehavior : Node3D, IBehavior +{ + public override void _Notification(int what) => this.Notify(what); + + [Export] private double _patrolSpeed { get; set; } = 100f; + + [Export] private double _thinkTime { get; set; } = 0.8f; + + [Export] private float _patrolRange { get; set; } = 5f; + + [Export] private float _patrolTime { get; set; } = 10f; + + private Timer _patrolTimer { get; set; } = default!; + private NavigationAgent3D _navigationAgent; + + private int _recursiveCounter = 0; + + public Vector3 HomePosition { get; set; } + + [Signal] public delegate void OnVelocityComputedEventHandler(Vector3 safeVelocity); + + public void OnReady() + { + _patrolTimer = new Timer() { WaitTime = _patrolTime }; + _patrolTimer.Timeout += PatrolTimer_Timeout; + AddChild(_patrolTimer); + SetPhysicsProcess(false); + } + + public void Init(NavigationAgent3D navigationAgent) + { + _navigationAgent = navigationAgent; + _navigationAgent.WaypointReached += NavigationAgent_WaypointReached; + _navigationAgent.TargetPosition = HomePosition; + } + + private async void NavigationAgent_WaypointReached(Dictionary details) => await ToSignal(GetTree().CreateTimer(_thinkTime), "timeout"); + + public void StartPatrol() + { + SetPhysicsProcess(true); + _patrolTimer?.Start(); + } + + public void StopPatrol() + { + SetPhysicsProcess(false); + _patrolTimer?.Stop(); + } + + public void OnPhysicsProcess(double delta) + { + var nextVelocity = _navigationAgent.GetNextPathPosition(); + var parent = GetParent() as Node3D; + var velocity = parent.GlobalPosition.DirectionTo(nextVelocity) * (float)_patrolSpeed * (float)delta; + EmitSignal(SignalName.OnVelocityComputed, velocity); + } + + public void SetPatrolTarget() + { + var rng = new RandomNumberGenerator(); + rng.Randomize(); + var randomPointX = rng.RandfRange(-_patrolRange, _patrolRange); + var randomPointZ = rng.RandfRange(-_patrolRange, _patrolRange); + _navigationAgent.TargetPosition = HomePosition + new Vector3(randomPointX, 0, randomPointZ); + if (!_navigationAgent.IsTargetReachable()) + { + _recursiveCounter++; + if (_recursiveCounter <= 100) + SetPatrolTarget(); + else + _navigationAgent.TargetPosition = HomePosition; + } + _recursiveCounter = 0; + } + + private void PatrolTimer_Timeout() => SetPatrolTarget(); +} diff --git a/Zennysoft.Game.Ma/src/enemy/behaviors/PatrolBehavior.cs.uid b/Zennysoft.Game.Ma/src/enemy/behaviors/PatrolBehavior.cs.uid new file mode 100644 index 00000000..6b17b83e --- /dev/null +++ b/Zennysoft.Game.Ma/src/enemy/behaviors/PatrolBehavior.cs.uid @@ -0,0 +1 @@ +uid://hpb1f5r17k5y diff --git a/Zennysoft.Game.Ma/src/enemy/behaviors/PatrolBehavior.tscn b/Zennysoft.Game.Ma/src/enemy/behaviors/PatrolBehavior.tscn new file mode 100644 index 00000000..db2492bc --- /dev/null +++ b/Zennysoft.Game.Ma/src/enemy/behaviors/PatrolBehavior.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://2nkvacxsd46b"] + +[ext_resource type="Script" uid="uid://hpb1f5r17k5y" path="res://src/enemy/behaviors/PatrolBehavior.cs" id="1_lobva"] + +[node name="NavigationAgent" type="Node3D"] +script = ExtResource("1_lobva") diff --git a/Zennysoft.Game.Ma/src/enemy/enemy_types/01. sproingy/IHavePatrolBehavior.cs.uid b/Zennysoft.Game.Ma/src/enemy/enemy_types/01. sproingy/IHavePatrolBehavior.cs.uid new file mode 100644 index 00000000..96d8b696 --- /dev/null +++ b/Zennysoft.Game.Ma/src/enemy/enemy_types/01. sproingy/IHavePatrolBehavior.cs.uid @@ -0,0 +1 @@ +uid://cjw2a3wwyc4p0 diff --git a/Zennysoft.Game.Ma/src/enemy/enemy_types/01. sproingy/Sproingy.cs b/Zennysoft.Game.Ma/src/enemy/enemy_types/01. sproingy/Sproingy.cs index 3d0a0021..3e6a61f8 100644 --- a/Zennysoft.Game.Ma/src/enemy/enemy_types/01. sproingy/Sproingy.cs +++ b/Zennysoft.Game.Ma/src/enemy/enemy_types/01. sproingy/Sproingy.cs @@ -1,70 +1,34 @@ -using Chickensoft.AutoInject; +using Chickensoft.AutoInject; using Chickensoft.Introspection; using Godot; -using Zennysoft.Game.Abstractions; using Zennysoft.Ma.Adapter; namespace Zennysoft.Game.Ma; [Meta(typeof(IAutoNode))] -public partial class Sproingy : FollowsPlayerEnemy, IHasPrimaryAttack, ICanPatrol +public partial class Sproingy : Enemy2D, IHavePatrolBehavior, IHaveEngagePlayerBehavior, IHaveFollowBehavior { public override void _Notification(int what) => this.Notify(what); - [Export] - public ElementType PrimaryAttackElementalType { get; set; } = ElementType.None; - [Export] - public double PrimaryAttackElementalDamageBonus { get; set; } = 1.0; + [Node] public NavigationAgent3D NavigationAgent { get; set; } + [Node] public PatrolBehavior PatrolBehavior { get; set; } = default!; + [Node] public FollowBehavior FollowBehavior { get; set; } = default!; + [Node] public EngagePlayerBehavior EngagePlayerBehavior { get; set; } = default!; - [Node] private INavigationAgentClient _navigationAgentClient { get; set; } = default!; + [Node] public Area3D PlayerDetector { get; set; } = default!; public void OnReady() { + FollowBehavior.Init(NavigationAgent); + PatrolBehavior.Init(NavigationAgent); + PatrolBehavior.HomePosition = GlobalPosition; + PatrolBehavior.OnVelocityComputed += OnVelocityComputed; + FollowBehavior.OnVelocityComputed += OnVelocityComputed; + EngagePlayerBehavior.TakeAction += EngagePlayerBehavior_TakeAction; + EngagePlayerBehavior.AcquireTarget += EngagePlayerBehavior_AcquireTarget; + _enemyLogic.Input(new EnemyLogic.Input.Patrol()); + PlayerDetector.BodyEntered += PlayerDetector_BodyEntered; + PlayerDetector.BodyExited += PlayerDetector_BodyExited; SetPhysicsProcess(true); - ((EnemyModelView2D)EnemyModelView).Hitbox.AreaEntered += Hitbox_AreaEntered; } - - public void OnPhysicsProcess(double delta) - { - _enemyLogic.Input(new EnemyLogic.Input.PhysicsTick(delta)); - FollowPlayerAndAttack(_enemyLogic, GlobalPosition, _player.CurrentPosition); - Velocity = _navigationAgentClient.Velocity; - MoveAndSlide(); - } - - public override void TakeAction() - { - PrimaryAttack(); - } - - public override void Die() - { - base.Die(); - } - - public void PrimaryAttack() - { - EnemyModelView.PlayPrimaryAttackAnimation(); - } - - public override void SetTarget(Vector3 target) => _navigationAgentClient.SetTarget(target); - - public void Patrol() - { - var rng = new RandomNumberGenerator(); - rng.Randomize(); - var randomizedSpot = new Vector3(rng.RandfRange(-5.0f, 5.0f), 0, rng.RandfRange(-5.0f, 5.0f)); - _enemyLogic.Input(new EnemyLogic.Input.PatrolToRandomSpot(GlobalPosition + randomizedSpot)); - _enemyLogic.Input(new EnemyLogic.Input.StartPatrol()); - } - - private void Hitbox_AreaEntered(Area3D area) - { - var target = area.GetOwner(); - if (target is IPlayer player) - { - var damage = _enemyStatResource.CurrentAttack * PrimaryAttackElementalDamageBonus; - player.TakeDamage(damage, PrimaryAttackElementalType, BattleExtensions.IsCriticalHit(_enemyStatResource.Luck)); - } - } -} +} \ No newline at end of file diff --git a/Zennysoft.Game.Ma/src/enemy/enemy_types/01. sproingy/Sproingy.cs.uid b/Zennysoft.Game.Ma/src/enemy/enemy_types/01. sproingy/Sproingy.cs.uid index dd67c29b..d198a414 100644 --- a/Zennysoft.Game.Ma/src/enemy/enemy_types/01. sproingy/Sproingy.cs.uid +++ b/Zennysoft.Game.Ma/src/enemy/enemy_types/01. sproingy/Sproingy.cs.uid @@ -1 +1 @@ -uid://dssu6tgi8dapq +uid://cq6b4ma3sy1en diff --git a/Zennysoft.Game.Ma/src/enemy/enemy_types/01. sproingy/Sproingy.tscn b/Zennysoft.Game.Ma/src/enemy/enemy_types/01. sproingy/Sproingy.tscn index 3524f03a..53e14542 100644 --- a/Zennysoft.Game.Ma/src/enemy/enemy_types/01. sproingy/Sproingy.tscn +++ b/Zennysoft.Game.Ma/src/enemy/enemy_types/01. sproingy/Sproingy.tscn @@ -1,27 +1,11 @@ -[gd_scene load_steps=9 format=3 uid="uid://bs56ccgosmu47"] +[gd_scene load_steps=13 format=3 uid="uid://bs56ccgosmu47"] -[ext_resource type="Script" uid="uid://dssu6tgi8dapq" path="res://src/enemy/enemy_types/01. sproingy/Sproingy.cs" id="1_xsluo"] -[ext_resource type="Script" uid="uid://dnkmr0eq1sij0" path="res://src/enemy/EnemyStatResource.cs" id="2_p4gkk"] -[ext_resource type="PackedScene" uid="uid://pbnsngx5jvrh" path="res://src/enemy/NavigationAgentClient.tscn" id="3_ut5m2"] +[ext_resource type="Script" uid="uid://cq6b4ma3sy1en" path="res://src/enemy/enemy_types/01. sproingy/Sproingy.cs" id="1_xsluo"] +[ext_resource type="PackedScene" uid="uid://2nkvacxsd46b" path="res://src/enemy/behaviors/PatrolBehavior.tscn" id="4_drfkj"] [ext_resource type="PackedScene" uid="uid://bimjnsu52y3xi" path="res://src/enemy/enemy_types/01. sproingy/SproingyModelView.tscn" id="4_o3b7p"] - -[sub_resource type="Resource" id="Resource_drfkj"] -script = ExtResource("2_p4gkk") -CurrentHP = 25.0 -MaximumHP = 25 -CurrentAttack = 10 -CurrentDefense = 5 -MaxAttack = 10 -MaxDefense = 5 -ExpFromDefeat = 8 -Luck = 0.05 -_telluricResistance = 0.0 -_aeolicResistance = 0.0 -_hydricResistance = 0.0 -_igneousResistance = 0.0 -_ferrumResistance = 0.0 -DropsSoulGemChance = 0.75 -metadata/_custom_type_script = "uid://dnkmr0eq1sij0" +[ext_resource type="PackedScene" uid="uid://bxymnqkoi78oa" path="res://src/system/stats/HealthComponent.tscn" id="4_p4gkk"] +[ext_resource type="PackedScene" uid="uid://8bcme8ao4axa" path="res://src/enemy/behaviors/EngagePlayerBehavior.tscn" id="5_drfkj"] +[ext_resource type="PackedScene" uid="uid://mqj4jju3870v" path="res://src/enemy/behaviors/FollowBehavior.tscn" id="6_moun4"] [sub_resource type="CapsuleShape3D" id="CapsuleShape3D_cwfph"] radius = 0.106078 @@ -34,26 +18,29 @@ radius = 0.365183 height = 5.0 radius = 1.0 +[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_p4gkk"] +transparency = 1 +albedo_color = Color(0.988235, 0, 0.054902, 0.152941) + +[sub_resource type="CylinderMesh" id="CylinderMesh_drfkj"] +material = SubResource("StandardMaterial3D_p4gkk") +top_radius = 0.0 + +[sub_resource type="CylinderShape3D" id="CylinderShape3D_drfkj"] + [node name="Sproingy" type="CharacterBody3D"] process_mode = 1 -collision_layer = 10 -collision_mask = 3 +collision_layer = 11 axis_lock_linear_y = true axis_lock_angular_x = true axis_lock_angular_z = true script = ExtResource("1_xsluo") -_enemyStatResource = SubResource("Resource_drfkj") [node name="CollisionShape" type="CollisionShape3D" parent="."] unique_name_in_owner = true transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0, 0) shape = SubResource("CapsuleShape3D_cwfph") -[node name="Navigation" type="Node3D" parent="."] - -[node name="NavigationAgentClient" parent="Navigation" instance=ExtResource("3_ut5m2")] -unique_name_in_owner = true - [node name="Collision" type="Node3D" parent="."] [node name="Collision" type="Area3D" parent="Collision"] @@ -73,24 +60,47 @@ collision_mask = 2 transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0, -2) shape = SubResource("CylinderShape3D_jbgmx") +[node name="DebugCone" type="MeshInstance3D" parent="Collision/LineOfSight"] +transform = Transform3D(1, 0, 0, 0, -4.37114e-08, -1, 0, 1, -4.37114e-08, 1.27175e-07, 0, -1.45472) +visible = false +mesh = SubResource("CylinderMesh_drfkj") + [node name="Raycast" type="RayCast3D" parent="Collision"] unique_name_in_owner = true target_position = Vector3(0, 0, -5) collision_mask = 3 +[node name="PlayerDetector" type="Area3D" parent="Collision"] +unique_name_in_owner = true +collision_layer = 0 +collision_mask = 34 + +[node name="CollisionShape3D" type="CollisionShape3D" parent="Collision/PlayerDetector"] +shape = SubResource("CylinderShape3D_drfkj") + [node name="Visual" type="Node3D" parent="."] [node name="EnemyModelView" parent="Visual" instance=ExtResource("4_o3b7p")] unique_name_in_owner = true -[node name="Timers" type="Node" parent="."] +[node name="Components" type="Node3D" parent="."] -[node name="PatrolTimer" type="Timer" parent="Timers"] +[node name="PatrolBehavior" parent="Components" instance=ExtResource("4_drfkj")] unique_name_in_owner = true -wait_time = 10.0 -autostart = true -[node name="AttackTimer" type="Timer" parent="Timers"] +[node name="FollowBehavior" parent="Components" instance=ExtResource("6_moun4")] unique_name_in_owner = true -wait_time = 0.8 -autostart = true +_followSpeed = 150.0 + +[node name="EngagePlayerBehavior" parent="Components" instance=ExtResource("5_drfkj")] +unique_name_in_owner = true +_acquireTargetTime = 2.0 + +[node name="NavigationAgent" type="NavigationAgent3D" parent="Components"] +unique_name_in_owner = true +avoidance_enabled = true +radius = 1.0 + +[node name="HealthComponent" parent="Components" instance=ExtResource("4_p4gkk")] +unique_name_in_owner = true +InitialMaximumHP = 30 diff --git a/Zennysoft.Game.Ma/src/enemy/enemy_types/01. sproingy/SproingyModelView.tscn b/Zennysoft.Game.Ma/src/enemy/enemy_types/01. sproingy/SproingyModelView.tscn index bb53385c..89d1f93c 100644 --- a/Zennysoft.Game.Ma/src/enemy/enemy_types/01. sproingy/SproingyModelView.tscn +++ b/Zennysoft.Game.Ma/src/enemy/enemy_types/01. sproingy/SproingyModelView.tscn @@ -58,7 +58,6 @@ [ext_resource type="Texture2D" uid="uid://b3gndmrlrvexy" path="res://src/enemy/enemy_types/01. sproingy/animations/IDLE_WALK_SIDE/Layer 13.png" id="53_nr2vc"] [ext_resource type="Texture2D" uid="uid://b1cmx8l4ia3fv" path="res://src/enemy/enemy_types/01. sproingy/animations/IDLE_WALK_SIDE/Layer 14.png" id="54_jdvn0"] [ext_resource type="Texture2D" uid="uid://c7t4626rox02s" path="res://src/enemy/enemy_types/01. sproingy/animations/IDLE_WALK_SIDE/Layer 15.png" id="55_2eqor"] -[ext_resource type="Script" uid="uid://6edayafleq8y" path="res://src/hitbox/Hitbox.cs" id="57_lae8t"] [ext_resource type="AnimationNodeStateMachine" uid="uid://cbq8xog50cjjy" path="res://src/enemy/animation_state_machines/PrimaryAttackStateMachine.tres" id="60_x7uye"] [ext_resource type="AnimationNodeStateMachine" uid="uid://cy2ngl55c0rws" path="res://src/enemy/animation_state_machines/WalkingStateMachine.tres" id="61_djeua"] [ext_resource type="AnimationNodeStateMachine" uid="uid://co7lshemjrro8" path="res://src/enemy/animation_state_machines/IdleStateMachine.tres" id="62_8wbs7"] @@ -805,6 +804,8 @@ switch_mode = 1 [sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_fdoul"] +[sub_resource type="AnimationNodeStateMachineTransition" id="AnimationNodeStateMachineTransition_kpotx"] + [sub_resource type="AnimationNodeStateMachine" id="AnimationNodeStateMachine_l2wq1"] states/Idle/node = ExtResource("61_djeua") states/Idle/position = Vector2(453, 100) @@ -812,8 +813,8 @@ states/Idle/position = Vector2(453, 100) "states/Primary Attack/position" = Vector2(453, 237) states/Start/position = Vector2(201, 100) states/Walking/node = ExtResource("62_8wbs7") -states/Walking/position = Vector2(653, 100) -transitions = ["Start", "Idle", SubResource("AnimationNodeStateMachineTransition_djeua"), "Idle", "Primary Attack", SubResource("AnimationNodeStateMachineTransition_8wbs7"), "Primary Attack", "Idle", SubResource("AnimationNodeStateMachineTransition_mnr4r"), "Idle", "Walking", SubResource("AnimationNodeStateMachineTransition_l2wq1"), "Walking", "Idle", SubResource("AnimationNodeStateMachineTransition_jwlar"), "Walking", "Primary Attack", SubResource("AnimationNodeStateMachineTransition_fdoul")] +states/Walking/position = Vector2(703, 100) +transitions = ["Start", "Idle", SubResource("AnimationNodeStateMachineTransition_djeua"), "Idle", "Primary Attack", SubResource("AnimationNodeStateMachineTransition_8wbs7"), "Primary Attack", "Idle", SubResource("AnimationNodeStateMachineTransition_mnr4r"), "Idle", "Walking", SubResource("AnimationNodeStateMachineTransition_l2wq1"), "Walking", "Idle", SubResource("AnimationNodeStateMachineTransition_jwlar"), "Walking", "Primary Attack", SubResource("AnimationNodeStateMachineTransition_fdoul"), "Primary Attack", "Walking", SubResource("AnimationNodeStateMachineTransition_kpotx")] [node name="EnemyModelView" type="Node3D"] script = ExtResource("1_oh25a") @@ -854,7 +855,6 @@ unique_name_in_owner = true transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, -0.152949, 0, 0) collision_layer = 64 collision_mask = 64 -script = ExtResource("57_lae8t") [node name="CollisionShape3D" type="CollisionShape3D" parent="Hitbox"] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.189337, 0.217529, -1.45579) diff --git a/Zennysoft.Game.Ma/src/enemy/enemy_types/02. michael/Michael.cs b/Zennysoft.Game.Ma/src/enemy/enemy_types/02. michael/Michael.cs index 66aabc88..45e50d73 100644 --- a/Zennysoft.Game.Ma/src/enemy/enemy_types/02. michael/Michael.cs +++ b/Zennysoft.Game.Ma/src/enemy/enemy_types/02. michael/Michael.cs @@ -1,69 +1,33 @@ using Chickensoft.AutoInject; using Chickensoft.Introspection; using Godot; -using Zennysoft.Game.Abstractions; -using Zennysoft.Ma.Adapter; namespace Zennysoft.Game.Ma; [Meta(typeof(IAutoNode))] -public partial class Michael : FollowsPlayerEnemy, IHasPrimaryAttack, ICanPatrol +public partial class Michael : Enemy2D, IHavePatrolBehavior, IHaveEngagePlayerBehavior, IHaveFollowBehavior { - [Export] - public ElementType PrimaryAttackElementalType { get; set; } = ElementType.None; - [Export] - public double PrimaryAttackElementalDamageBonus { get; set; } = 1.0; + public override void _Notification(int what) => this.Notify(what); - [Node] private INavigationAgentClient _navigationAgentClient { get; set; } = default!; + [Node] public NavigationAgent3D NavigationAgent { get; set; } + [Node] public PatrolBehavior PatrolBehavior { get; set; } = default!; + [Node] public FollowBehavior FollowBehavior { get; set; } = default!; + [Node] public EngagePlayerBehavior EngagePlayerBehavior { get; set; } = default!; + + [Node] public Area3D PlayerDetector { get; set; } = default!; public void OnReady() { + FollowBehavior.Init(NavigationAgent); + PatrolBehavior.Init(NavigationAgent); + PatrolBehavior.HomePosition = GlobalPosition; + PatrolBehavior.OnVelocityComputed += OnVelocityComputed; + FollowBehavior.OnVelocityComputed += OnVelocityComputed; + EngagePlayerBehavior.TakeAction += EngagePlayerBehavior_TakeAction; + EngagePlayerBehavior.AcquireTarget += EngagePlayerBehavior_AcquireTarget; + _enemyLogic.Input(new EnemyLogic.Input.Patrol()); + PlayerDetector.BodyEntered += PlayerDetector_BodyEntered; + PlayerDetector.BodyExited += PlayerDetector_BodyExited; SetPhysicsProcess(true); - ((EnemyModelView2D)EnemyModelView).Hitbox.AreaEntered += Hitbox_AreaEntered; - } - - public void OnPhysicsProcess(double delta) - { - _enemyLogic.Input(new EnemyLogic.Input.PhysicsTick(delta)); - FollowPlayerAndAttack(_enemyLogic, GlobalPosition, _player.CurrentPosition); - Velocity = _navigationAgentClient.Velocity; - base._PhysicsProcess(delta); - } - - public override void Die() - { - //_navigationAgentClient.Stop(); - base.Die(); - } - - public override void TakeAction() - { - PrimaryAttack(); - } - - public void PrimaryAttack() - { - EnemyModelView.PlayPrimaryAttackAnimation(); - } - - public override void SetTarget(Vector3 target) => _navigationAgentClient.SetTarget(target); - - public void Patrol() - { - var rng = new RandomNumberGenerator(); - rng.Randomize(); - var randomizedSpot = new Vector3(rng.RandfRange(-5.0f, 5.0f), 0, rng.RandfRange(-5.0f, 5.0f)); - _enemyLogic.Input(new EnemyLogic.Input.PatrolToRandomSpot(GlobalPosition + randomizedSpot)); - _enemyLogic.Input(new EnemyLogic.Input.StartPatrol()); - } - - private void Hitbox_AreaEntered(Area3D area) - { - var target = area.GetOwner(); - if (target is IPlayer player) - { - var damage = _enemyStatResource.CurrentAttack * PrimaryAttackElementalDamageBonus; - player.TakeDamage(damage, PrimaryAttackElementalType, BattleExtensions.IsCriticalHit(_enemyStatResource.Luck)); - } } } \ No newline at end of file diff --git a/Zennysoft.Game.Ma/src/enemy/enemy_types/02. michael/Michael.tscn b/Zennysoft.Game.Ma/src/enemy/enemy_types/02. michael/Michael.tscn index 83a90e93..6a6759ae 100644 --- a/Zennysoft.Game.Ma/src/enemy/enemy_types/02. michael/Michael.tscn +++ b/Zennysoft.Game.Ma/src/enemy/enemy_types/02. michael/Michael.tscn @@ -1,27 +1,11 @@ -[gd_scene load_steps=9 format=3 uid="uid://b0gwivt7cw7nd"] +[gd_scene load_steps=11 format=3 uid="uid://b0gwivt7cw7nd"] -[ext_resource type="Script" uid="uid://c4pdledq3bll3" path="res://src/enemy/enemy_types/02. michael/Michael.cs" id="1_wfjxm"] -[ext_resource type="Script" uid="uid://dnkmr0eq1sij0" path="res://src/enemy/EnemyStatResource.cs" id="2_wrps7"] +[ext_resource type="Script" uid="uid://c4pdledq3bll3" path="res://src/enemy/enemy_types/02. michael/Michael.cs" id="1_lb5oy"] [ext_resource type="PackedScene" uid="uid://bjg8wyvp8q6oc" path="res://src/enemy/enemy_types/02. michael/MichaelModelView.tscn" id="3_wrps7"] -[ext_resource type="PackedScene" uid="uid://pbnsngx5jvrh" path="res://src/enemy/NavigationAgentClient.tscn" id="4_w7hys"] - -[sub_resource type="Resource" id="Resource_xhsah"] -script = ExtResource("2_wrps7") -CurrentHP = 30.0 -MaximumHP = 30 -CurrentAttack = 15 -CurrentDefense = 0 -MaxAttack = 15 -MaxDefense = 0 -ExpFromDefeat = 5 -Luck = 0.05 -_telluricResistance = 0.0 -_aeolicResistance = 0.0 -_hydricResistance = 0.0 -_igneousResistance = 0.0 -_ferrumResistance = 0.0 -DropsSoulGemChance = 0.75 -metadata/_custom_type_script = ExtResource("2_wrps7") +[ext_resource type="PackedScene" uid="uid://bxymnqkoi78oa" path="res://src/system/stats/HealthComponent.tscn" id="4_bngst"] +[ext_resource type="PackedScene" uid="uid://2nkvacxsd46b" path="res://src/enemy/behaviors/PatrolBehavior.tscn" id="5_fkx5j"] +[ext_resource type="PackedScene" uid="uid://mqj4jju3870v" path="res://src/enemy/behaviors/FollowBehavior.tscn" id="6_bun8r"] +[ext_resource type="PackedScene" uid="uid://8bcme8ao4axa" path="res://src/enemy/behaviors/EngagePlayerBehavior.tscn" id="7_x8mrp"] [sub_resource type="CylinderShape3D" id="CylinderShape3D_jbgmx"] height = 5.0 @@ -30,6 +14,8 @@ radius = 1.0 [sub_resource type="CapsuleShape3D" id="CapsuleShape3D_0h5s2"] height = 2.66932 +[sub_resource type="CylinderShape3D" id="CylinderShape3D_eek1b"] + [sub_resource type="SphereShape3D" id="SphereShape3D_wrps7"] radius = 1.0 @@ -41,8 +27,7 @@ axis_lock_linear_y = true axis_lock_angular_x = true axis_lock_angular_z = true motion_mode = 1 -script = ExtResource("1_wfjxm") -_enemyStatResource = SubResource("Resource_xhsah") +script = ExtResource("1_lb5oy") [node name="LineOfSight" type="Area3D" parent="."] unique_name_in_owner = true @@ -54,16 +39,6 @@ collision_mask = 2 transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0, -2) shape = SubResource("CylinderShape3D_jbgmx") -[node name="PatrolTimer" type="Timer" parent="."] -unique_name_in_owner = true -wait_time = 10.0 -autostart = true - -[node name="AttackTimer" type="Timer" parent="."] -unique_name_in_owner = true -wait_time = 1.8 -autostart = true - [node name="Raycast" type="RayCast3D" parent="."] unique_name_in_owner = true transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0, 0) @@ -78,6 +53,14 @@ shape = SubResource("CapsuleShape3D_0h5s2") [node name="EnemyModelView" parent="." instance=ExtResource("3_wrps7")] unique_name_in_owner = true +[node name="PlayerDetector" type="Area3D" parent="."] +unique_name_in_owner = true +collision_layer = 0 +collision_mask = 34 + +[node name="CollisionShape3D" type="CollisionShape3D" parent="PlayerDetector"] +shape = SubResource("CylinderShape3D_eek1b") + [node name="Collision" type="Area3D" parent="."] collision_layer = 2048 collision_mask = 0 @@ -86,5 +69,22 @@ collision_mask = 0 transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -0.120117, 0.221246, 0) shape = SubResource("SphereShape3D_wrps7") -[node name="NavigationAgentClient" parent="." instance=ExtResource("4_w7hys")] +[node name="Components" type="Node3D" parent="."] + +[node name="HealthComponent" parent="Components" instance=ExtResource("4_bngst")] + +[node name="PatrolBehavior" parent="Components" instance=ExtResource("5_fkx5j")] unique_name_in_owner = true + +[node name="FollowBehavior" parent="Components" instance=ExtResource("6_bun8r")] +unique_name_in_owner = true +_followSpeed = 150.0 + +[node name="EngagePlayerBehavior" parent="Components" instance=ExtResource("7_x8mrp")] +unique_name_in_owner = true +_acquireTargetTime = 2.0 + +[node name="NavigationAgent" type="NavigationAgent3D" parent="Components"] +unique_name_in_owner = true +avoidance_enabled = true +radius = 1.0 diff --git a/Zennysoft.Game.Ma/src/enemy/enemy_types/03. filth_eater/FilthEater.cs b/Zennysoft.Game.Ma/src/enemy/enemy_types/03. filth_eater/FilthEater.cs index 6b7e9973..f02bfb56 100644 --- a/Zennysoft.Game.Ma/src/enemy/enemy_types/03. filth_eater/FilthEater.cs +++ b/Zennysoft.Game.Ma/src/enemy/enemy_types/03. filth_eater/FilthEater.cs @@ -1,80 +1,33 @@ using Chickensoft.AutoInject; using Chickensoft.Introspection; using Godot; -using System; -using System.Collections.Generic; -using Zennysoft.Game.Abstractions; -using Zennysoft.Ma.Adapter; namespace Zennysoft.Game.Ma; [Meta(typeof(IAutoNode))] -public partial class FilthEater : FollowsPlayerEnemy, IHasPrimaryAttack, IHasSecondaryAttack, IHasRangedAttack, ICanPatrol +public partial class FilthEater : Enemy2D, IHavePatrolBehavior, IHaveEngagePlayerBehavior, IHaveFollowBehavior { - [Export] - public ElementType PrimaryAttackElementalType { get; set; } = ElementType.None; - [Export] - public double PrimaryAttackElementalDamageBonus { get; set; } = 1.0; + public override void _Notification(int what) => this.Notify(what); - [Export] - public ElementType SecondaryAttackElementalType { get; set; } = ElementType.None; - [Export] - public double SecondaryAttackElementalDamageBonus { get; set; } = 1.0; + [Node] public NavigationAgent3D NavigationAgent { get; set; } + [Node] public PatrolBehavior PatrolBehavior { get; set; } = default!; + [Node] public FollowBehavior FollowBehavior { get; set; } = default!; + [Node] public EngagePlayerBehavior EngagePlayerBehavior { get; set; } = default!; - [Node] private INavigationAgentClient _navigationAgentClient { get; set; } = default!; + [Node] public Area3D PlayerDetector { get; set; } = default!; public void OnReady() { + FollowBehavior.Init(NavigationAgent); + PatrolBehavior.Init(NavigationAgent); + PatrolBehavior.HomePosition = GlobalPosition; + PatrolBehavior.OnVelocityComputed += OnVelocityComputed; + FollowBehavior.OnVelocityComputed += OnVelocityComputed; + EngagePlayerBehavior.TakeAction += EngagePlayerBehavior_TakeAction; + EngagePlayerBehavior.AcquireTarget += EngagePlayerBehavior_AcquireTarget; + _enemyLogic.Input(new EnemyLogic.Input.Patrol()); + PlayerDetector.BodyEntered += PlayerDetector_BodyEntered; + PlayerDetector.BodyExited += PlayerDetector_BodyExited; SetPhysicsProcess(true); - ((EnemyModelView2D)EnemyModelView).Hitbox.AreaEntered += Hitbox_AreaEntered; - } - - public void OnPhysicsProcess(double delta) - { - _enemyLogic.Input(new EnemyLogic.Input.PhysicsTick(delta)); - FollowPlayerAndAttack(_enemyLogic, GlobalPosition, _player.CurrentPosition); - Velocity = _navigationAgentClient.Velocity; - base._PhysicsProcess(delta); - } - - public override void TakeAction() - { - var rng = new RandomNumberGenerator(); - var options = new List() { PrimaryAttack, SecondaryAttack }; - var selection = rng.RandWeighted([0.875f, 0.125f]); - options[(int)selection].Invoke(); - } - public void PrimaryAttack() - { - EnemyModelView.PlayPrimaryAttackAnimation(); - } - - public void SecondaryAttack() - { - EnemyModelView.PlaySecondaryAttackAnimation(); - } - - public void RangedAttack() - { - EnemyModelView.PlaySecondaryAttackAnimation(); - } - - public void Patrol() - { - var rng = new RandomNumberGenerator(); - rng.Randomize(); - var randomizedSpot = new Vector3(rng.RandfRange(-5.0f, 5.0f), 0, rng.RandfRange(-5.0f, 5.0f)); - _enemyLogic.Input(new EnemyLogic.Input.PatrolToRandomSpot(GlobalPosition + randomizedSpot)); - _enemyLogic.Input(new EnemyLogic.Input.StartPatrol()); - } - - private void Hitbox_AreaEntered(Area3D area) - { - var target = area.GetOwner(); - if (target is IPlayer player) - { - var damage = _enemyStatResource.CurrentAttack * PrimaryAttackElementalDamageBonus; - player.TakeDamage(damage, PrimaryAttackElementalType, BattleExtensions.IsCriticalHit(_enemyStatResource.Luck)); - } } } \ No newline at end of file diff --git a/Zennysoft.Game.Ma/src/enemy/enemy_types/03. filth_eater/FilthEater.tscn b/Zennysoft.Game.Ma/src/enemy/enemy_types/03. filth_eater/FilthEater.tscn index 49943fd7..425471ec 100644 --- a/Zennysoft.Game.Ma/src/enemy/enemy_types/03. filth_eater/FilthEater.tscn +++ b/Zennysoft.Game.Ma/src/enemy/enemy_types/03. filth_eater/FilthEater.tscn @@ -1,10 +1,12 @@ -[gd_scene load_steps=10 format=3 uid="uid://cvk007twac22c"] +[gd_scene load_steps=13 format=3 uid="uid://cvk007twac22c"] -[ext_resource type="Script" uid="uid://cohal8w5ceneg" path="res://src/enemy/enemy_types/03. filth_eater/FilthEater.cs" id="1_fyc13"] -[ext_resource type="Resource" uid="uid://coyidduu5uhsp" path="res://src/enemy/enemy_types/03. filth_eater/FilthEaterStats.tres" id="2_p438s"] +[ext_resource type="Script" uid="uid://cohal8w5ceneg" path="res://src/enemy/enemy_types/03. filth_eater/FilthEater.cs" id="1_p438s"] [ext_resource type="PackedScene" uid="uid://bup8c4x1na3aw" path="res://src/enemy/enemy_types/03. filth_eater/FilthEaterModelView.tscn" id="3_rrwed"] [ext_resource type="Script" uid="uid://dlsgyx4i1jmp3" path="res://src/enemy/EnemyLoreInfo.cs" id="4_5eid5"] -[ext_resource type="PackedScene" uid="uid://pbnsngx5jvrh" path="res://src/enemy/NavigationAgentClient.tscn" id="5_veila"] +[ext_resource type="PackedScene" uid="uid://bxymnqkoi78oa" path="res://src/system/stats/HealthComponent.tscn" id="4_hub7i"] +[ext_resource type="PackedScene" uid="uid://2nkvacxsd46b" path="res://src/enemy/behaviors/PatrolBehavior.tscn" id="5_pvjvo"] +[ext_resource type="PackedScene" uid="uid://mqj4jju3870v" path="res://src/enemy/behaviors/FollowBehavior.tscn" id="6_fccr3"] +[ext_resource type="PackedScene" uid="uid://8bcme8ao4axa" path="res://src/enemy/behaviors/EngagePlayerBehavior.tscn" id="7_8l567"] [sub_resource type="CylinderShape3D" id="CylinderShape3D_jbgmx"] height = 5.0 @@ -22,16 +24,15 @@ Name = "Filth Eater" Description = "This guy grosses me out." metadata/_custom_type_script = ExtResource("4_5eid5") +[sub_resource type="CylinderShape3D" id="CylinderShape3D_qbmfg"] + [node name="FilthEater" type="CharacterBody3D"] process_mode = 1 collision_layer = 10 collision_mask = 11 axis_lock_linear_y = true axis_lock_angular_x = true -script = ExtResource("1_fyc13") -SecondaryAttackElementalType = 2 -SecondaryAttackElementalDamageBonus = 1.15 -_enemyStatResource = ExtResource("2_p438s") +script = ExtResource("1_p438s") [node name="LineOfSight" type="Area3D" parent="."] unique_name_in_owner = true @@ -48,16 +49,6 @@ unique_name_in_owner = true transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0, 0) shape = SubResource("CapsuleShape3D_cwfph") -[node name="PatrolTimer" type="Timer" parent="."] -unique_name_in_owner = true -wait_time = 10.0 -autostart = true - -[node name="AttackTimer" type="Timer" parent="."] -unique_name_in_owner = true -wait_time = 0.8 -autostart = true - [node name="Raycast" type="RayCast3D" parent="."] unique_name_in_owner = true target_position = Vector3(0, 0, -5) @@ -75,5 +66,30 @@ shape = SubResource("SphereShape3D_0y048") unique_name_in_owner = true EnemyLoreInfo = SubResource("Resource_fv5vf") -[node name="NavigationAgentClient" parent="." instance=ExtResource("5_veila")] +[node name="PlayerDetector" type="Area3D" parent="."] unique_name_in_owner = true +collision_layer = 0 +collision_mask = 34 + +[node name="CollisionShape3D" type="CollisionShape3D" parent="PlayerDetector"] +shape = SubResource("CylinderShape3D_qbmfg") + +[node name="Components" type="Node3D" parent="."] + +[node name="HealthComponent" parent="Components" instance=ExtResource("4_hub7i")] + +[node name="PatrolBehavior" parent="Components" instance=ExtResource("5_pvjvo")] +unique_name_in_owner = true + +[node name="FollowBehavior" parent="Components" instance=ExtResource("6_fccr3")] +unique_name_in_owner = true +_followSpeed = 150.0 + +[node name="EngagePlayerBehavior" parent="Components" instance=ExtResource("7_8l567")] +unique_name_in_owner = true +_acquireTargetTime = 2.0 + +[node name="NavigationAgent" type="NavigationAgent3D" parent="Components"] +unique_name_in_owner = true +avoidance_enabled = true +radius = 1.0 diff --git a/Zennysoft.Game.Ma/src/enemy/enemy_types/04. sara/Sara.cs b/Zennysoft.Game.Ma/src/enemy/enemy_types/04. sara/Sara.cs index 584d228c..9f8f2f0a 100644 --- a/Zennysoft.Game.Ma/src/enemy/enemy_types/04. sara/Sara.cs +++ b/Zennysoft.Game.Ma/src/enemy/enemy_types/04. sara/Sara.cs @@ -1,84 +1,32 @@ using Chickensoft.AutoInject; using Chickensoft.Introspection; using Godot; -using System.Collections.Generic; -using System; -using Zennysoft.Game.Abstractions; -using Zennysoft.Ma.Adapter; - -namespace Zennysoft.Game.Ma; +using Zennysoft.Game.Ma; [Meta(typeof(IAutoNode))] -public partial class Sara : FollowsPlayerEnemy, IHasPrimaryAttack, IHasSecondaryAttack, ICanPatrol +public partial class Sara : Enemy2D, IHavePatrolBehavior, IHaveEngagePlayerBehavior, IHaveFollowBehavior { public override void _Notification(int what) => this.Notify(what); - [Export] - public ElementType PrimaryAttackElementalType { get; set; } = ElementType.None; - [Export] - public double PrimaryAttackElementalDamageBonus { get; set; } = 1.0; + [Node] public NavigationAgent3D NavigationAgent { get; set; } + [Node] public PatrolBehavior PatrolBehavior { get; set; } = default!; + [Node] public FollowBehavior FollowBehavior { get; set; } = default!; + [Node] public EngagePlayerBehavior EngagePlayerBehavior { get; set; } = default!; - [Node] private INavigationAgentClient _navigationAgentClient { get; set; } = default!; - - public ElementType SecondaryAttackElementalType { get; set; } = ElementType.None; - - public double SecondaryAttackElementalDamageBonus { get; set; } = 1.0; + [Node] public Area3D PlayerDetector { get; set; } = default!; public void OnReady() { + FollowBehavior.Init(NavigationAgent); + PatrolBehavior.Init(NavigationAgent); + PatrolBehavior.HomePosition = GlobalPosition; + PatrolBehavior.OnVelocityComputed += OnVelocityComputed; + FollowBehavior.OnVelocityComputed += OnVelocityComputed; + EngagePlayerBehavior.TakeAction += EngagePlayerBehavior_TakeAction; + EngagePlayerBehavior.AcquireTarget += EngagePlayerBehavior_AcquireTarget; + _enemyLogic.Input(new EnemyLogic.Input.Patrol()); + PlayerDetector.BodyEntered += PlayerDetector_BodyEntered; + PlayerDetector.BodyExited += PlayerDetector_BodyExited; SetPhysicsProcess(true); - ((EnemyModelView2D)EnemyModelView).Hitbox.AreaEntered += Hitbox_AreaEntered; - } - - public void OnPhysicsProcess(double delta) - { - _enemyLogic.Input(new EnemyLogic.Input.PhysicsTick(delta)); - FollowPlayerAndAttack(_enemyLogic, GlobalPosition, _player.CurrentPosition); - Velocity = _navigationAgentClient.Velocity; - base._PhysicsProcess(delta); - } - - public override void TakeAction() - { - var rng = new RandomNumberGenerator(); - var options = new List() { PrimaryAttack, SecondaryAttack }; - var selection = rng.RandWeighted([0.875f, 0.125f]); - options[(int)selection].Invoke(); - } - - public override void Die() - { - base.Die(); - } - - public void PrimaryAttack() - { - EnemyModelView.PlayPrimaryAttackAnimation(); - } - - public void SecondaryAttack() - { - EnemyModelView.PlaySecondaryAttackAnimation(); - } - - public override void SetTarget(Vector3 target) => _navigationAgentClient.SetTarget(target); - - public void Patrol() - { - var rng = new RandomNumberGenerator(); - rng.Randomize(); - var randomizedSpot = new Vector3(rng.RandfRange(-5.0f, 5.0f), 0, rng.RandfRange(-5.0f, 5.0f)); - _enemyLogic.Input(new EnemyLogic.Input.PatrolToRandomSpot(GlobalPosition + randomizedSpot)); - _enemyLogic.Input(new EnemyLogic.Input.StartPatrol()); - } - - private void Hitbox_AreaEntered(Area3D area) - { - var target = area.GetOwner(); - if (target is IPlayer player) - { - var damage = _enemyStatResource.CurrentAttack * PrimaryAttackElementalDamageBonus; - player.TakeDamage(damage, PrimaryAttackElementalType, BattleExtensions.IsCriticalHit(_enemyStatResource.Luck)); - } } } diff --git a/Zennysoft.Game.Ma/src/enemy/enemy_types/04. sara/Sara.tscn b/Zennysoft.Game.Ma/src/enemy/enemy_types/04. sara/Sara.tscn index bf85e610..9240d9bf 100644 --- a/Zennysoft.Game.Ma/src/enemy/enemy_types/04. sara/Sara.tscn +++ b/Zennysoft.Game.Ma/src/enemy/enemy_types/04. sara/Sara.tscn @@ -1,9 +1,11 @@ -[gd_scene load_steps=8 format=3 uid="uid://bksq62muhk3h5"] +[gd_scene load_steps=11 format=3 uid="uid://bksq62muhk3h5"] -[ext_resource type="Script" uid="uid://jjulhqd5g3bd" path="res://src/enemy/enemy_types/04. sara/Sara.cs" id="1_1grvw"] -[ext_resource type="Resource" uid="uid://b0e6yx54hhbw7" path="res://src/enemy/enemy_types/04. sara/SaraStats.tres" id="2_1grvw"] -[ext_resource type="PackedScene" uid="uid://pbnsngx5jvrh" path="res://src/enemy/NavigationAgentClient.tscn" id="3_c4qcm"] +[ext_resource type="Script" uid="uid://jjulhqd5g3bd" path="res://src/enemy/enemy_types/04. sara/Sara.cs" id="1_3ejdn"] +[ext_resource type="PackedScene" uid="uid://bxymnqkoi78oa" path="res://src/system/stats/HealthComponent.tscn" id="3_c4qcm"] +[ext_resource type="PackedScene" uid="uid://2nkvacxsd46b" path="res://src/enemy/behaviors/PatrolBehavior.tscn" id="4_8ymq6"] [ext_resource type="PackedScene" uid="uid://bli0t0d6ommvi" path="res://src/enemy/enemy_types/04. sara/SaraModelView.tscn" id="4_82s0m"] +[ext_resource type="PackedScene" uid="uid://mqj4jju3870v" path="res://src/enemy/behaviors/FollowBehavior.tscn" id="5_lxgpb"] +[ext_resource type="PackedScene" uid="uid://8bcme8ao4axa" path="res://src/enemy/behaviors/EngagePlayerBehavior.tscn" id="6_ddchx"] [sub_resource type="CapsuleShape3D" id="CapsuleShape3D_cwfph"] radius = 0.106078 @@ -16,6 +18,8 @@ radius = 0.57308 height = 5.0 radius = 1.0 +[sub_resource type="CylinderShape3D" id="CylinderShape3D_746fv"] + [node name="Sara" type="CharacterBody3D"] process_mode = 1 collision_layer = 10 @@ -23,19 +27,13 @@ collision_mask = 3 axis_lock_linear_y = true axis_lock_angular_x = true axis_lock_angular_z = true -script = ExtResource("1_1grvw") -_enemyStatResource = ExtResource("2_1grvw") +script = ExtResource("1_3ejdn") [node name="CollisionShape" type="CollisionShape3D" parent="."] unique_name_in_owner = true transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0, 0) shape = SubResource("CapsuleShape3D_cwfph") -[node name="Navigation" type="Node3D" parent="."] - -[node name="NavigationAgentClient" parent="Navigation" instance=ExtResource("3_c4qcm")] -unique_name_in_owner = true - [node name="Collision" type="Node3D" parent="."] [node name="Collision" type="Area3D" parent="Collision"] @@ -45,18 +43,19 @@ collision_mask = 0 [node name="CollisionShape3D" type="CollisionShape3D" parent="Collision/Collision"] shape = SubResource("SphereShape3D_8vcnq") -[node name="LineOfSight" type="Area3D" parent="Collision"] +[node name="LineOfSight" type="Area3D" parent="."] unique_name_in_owner = true transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0, 0) collision_layer = 2 collision_mask = 2 -[node name="CollisionShape3D" type="CollisionShape3D" parent="Collision/LineOfSight"] +[node name="CollisionShape3D" type="CollisionShape3D" parent="LineOfSight"] transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0, -2) shape = SubResource("CylinderShape3D_jbgmx") -[node name="Raycast" type="RayCast3D" parent="Collision"] +[node name="Raycast" type="RayCast3D" parent="LineOfSight"] unique_name_in_owner = true +transform = Transform3D(-1, 0, 8.74228e-08, 0, 1, 0, -8.74228e-08, 0, -1, 0, 0, 0) target_position = Vector3(0, 0, -5) collision_mask = 3 @@ -65,14 +64,30 @@ collision_mask = 3 [node name="EnemyModelView" parent="Visual" instance=ExtResource("4_82s0m")] unique_name_in_owner = true -[node name="Timers" type="Node" parent="."] - -[node name="PatrolTimer" type="Timer" parent="Timers"] +[node name="PlayerDetector" type="Area3D" parent="."] unique_name_in_owner = true -wait_time = 10.0 -autostart = true +collision_layer = 0 +collision_mask = 34 -[node name="AttackTimer" type="Timer" parent="Timers"] +[node name="CollisionShape3D" type="CollisionShape3D" parent="PlayerDetector"] +shape = SubResource("CylinderShape3D_746fv") + +[node name="Components" type="Node3D" parent="."] + +[node name="HealthComponent" parent="Components" instance=ExtResource("3_c4qcm")] + +[node name="PatrolBehavior" parent="Components" instance=ExtResource("4_8ymq6")] unique_name_in_owner = true -wait_time = 0.8 -autostart = true + +[node name="FollowBehavior" parent="Components" instance=ExtResource("5_lxgpb")] +unique_name_in_owner = true +_followSpeed = 150.0 + +[node name="EngagePlayerBehavior" parent="Components" instance=ExtResource("6_ddchx")] +unique_name_in_owner = true +_acquireTargetTime = 2.0 + +[node name="NavigationAgent" type="NavigationAgent3D" parent="Components"] +unique_name_in_owner = true +avoidance_enabled = true +radius = 1.0 diff --git a/Zennysoft.Game.Ma/src/enemy/enemy_types/05. ballos/Ballos.cs b/Zennysoft.Game.Ma/src/enemy/enemy_types/05. ballos/Ballos.cs index e25cfe0f..d42fff06 100644 --- a/Zennysoft.Game.Ma/src/enemy/enemy_types/05. ballos/Ballos.cs +++ b/Zennysoft.Game.Ma/src/enemy/enemy_types/05. ballos/Ballos.cs @@ -1,84 +1,32 @@ using Chickensoft.AutoInject; using Chickensoft.Introspection; using Godot; -using System.Collections.Generic; -using System; -using Zennysoft.Ma.Adapter; -using Zennysoft.Game.Abstractions; - -namespace Zennysoft.Game.Ma; +using Zennysoft.Game.Ma; [Meta(typeof(IAutoNode))] -public partial class Ballos : FollowsPlayerEnemy, IHasPrimaryAttack, IHasSecondaryAttack, ICanPatrol +public partial class Ballos : Enemy2D, IHavePatrolBehavior, IHaveEngagePlayerBehavior, IHaveFollowBehavior { public override void _Notification(int what) => this.Notify(what); - [Export] - public ElementType PrimaryAttackElementalType { get; set; } = ElementType.None; - [Export] - public double PrimaryAttackElementalDamageBonus { get; set; } = 1.0; - [Export] - public ElementType SecondaryAttackElementalType { get; set; } = ElementType.None; - [Export] - public double SecondaryAttackElementalDamageBonus { get; set; } = 1.0; + [Node] public NavigationAgent3D NavigationAgent { get; set; } + [Node] public PatrolBehavior PatrolBehavior { get; set; } = default!; + [Node] public FollowBehavior FollowBehavior { get; set; } = default!; + [Node] public EngagePlayerBehavior EngagePlayerBehavior { get; set; } = default!; - [Node] private INavigationAgentClient _navigationAgentClient { get; set; } = default!; + [Node] public Area3D PlayerDetector { get; set; } = default!; public void OnReady() { + FollowBehavior.Init(NavigationAgent); + PatrolBehavior.Init(NavigationAgent); + PatrolBehavior.HomePosition = GlobalPosition; + PatrolBehavior.OnVelocityComputed += OnVelocityComputed; + FollowBehavior.OnVelocityComputed += OnVelocityComputed; + EngagePlayerBehavior.TakeAction += EngagePlayerBehavior_TakeAction; + EngagePlayerBehavior.AcquireTarget += EngagePlayerBehavior_AcquireTarget; + _enemyLogic.Input(new EnemyLogic.Input.Patrol()); + PlayerDetector.BodyEntered += PlayerDetector_BodyEntered; + PlayerDetector.BodyExited += PlayerDetector_BodyExited; SetPhysicsProcess(true); - ((EnemyModelView2D)EnemyModelView).Hitbox.AreaEntered += Hitbox_AreaEntered; } - - public void OnPhysicsProcess(double delta) - { - _enemyLogic.Input(new EnemyLogic.Input.PhysicsTick(delta)); - FollowPlayerAndAttack(_enemyLogic, GlobalPosition, _player.CurrentPosition); - Velocity = _navigationAgentClient.Velocity; - base._PhysicsProcess(delta); - } - - public override void TakeAction() - { - var rng = new RandomNumberGenerator(); - var options = new List() { PrimaryAttack, SecondaryAttack }; - var selection = rng.RandWeighted([0.875f, 0.125f]); - options[(int)selection].Invoke(); - } - - public override void Die() - { - base.Die(); - } - - public void PrimaryAttack() - { - EnemyModelView.PlayPrimaryAttackAnimation(); - } - - public void SecondaryAttack() - { - EnemyModelView.PlaySecondaryAttackAnimation(); - } - - public override void SetTarget(Vector3 target) => _navigationAgentClient.SetTarget(target); - - public void Patrol() - { - var rng = new RandomNumberGenerator(); - rng.Randomize(); - var randomizedSpot = new Vector3(rng.RandfRange(-5.0f, 5.0f), 0, rng.RandfRange(-5.0f, 5.0f)); - _enemyLogic.Input(new EnemyLogic.Input.PatrolToRandomSpot(GlobalPosition + randomizedSpot)); - _enemyLogic.Input(new EnemyLogic.Input.StartPatrol()); - } - - private void Hitbox_AreaEntered(Area3D area) - { - var target = area.GetOwner(); - if (target is IPlayer player) - { - var damage = _enemyStatResource.CurrentAttack * PrimaryAttackElementalDamageBonus; - player.TakeDamage(damage, PrimaryAttackElementalType, BattleExtensions.IsCriticalHit(_enemyStatResource.Luck)); - } - } -} +} \ No newline at end of file diff --git a/Zennysoft.Game.Ma/src/enemy/enemy_types/05. ballos/Ballos.tscn b/Zennysoft.Game.Ma/src/enemy/enemy_types/05. ballos/Ballos.tscn index d56e4fd7..e3426e87 100644 --- a/Zennysoft.Game.Ma/src/enemy/enemy_types/05. ballos/Ballos.tscn +++ b/Zennysoft.Game.Ma/src/enemy/enemy_types/05. ballos/Ballos.tscn @@ -1,27 +1,11 @@ -[gd_scene load_steps=9 format=3 uid="uid://feegakykn3fv"] +[gd_scene load_steps=11 format=3 uid="uid://feegakykn3fv"] -[ext_resource type="Script" uid="uid://dwfxs5yrf7i3v" path="res://src/enemy/enemy_types/05. ballos/Ballos.cs" id="1_v2urn"] -[ext_resource type="Script" uid="uid://dnkmr0eq1sij0" path="res://src/enemy/EnemyStatResource.cs" id="2_iy2fp"] +[ext_resource type="Script" uid="uid://dwfxs5yrf7i3v" path="res://src/enemy/enemy_types/05. ballos/Ballos.cs" id="1_iy2fp"] [ext_resource type="PackedScene" uid="uid://c5xijwxkg4pf6" path="res://src/enemy/enemy_types/05. ballos/BallosModelView.tscn" id="2_v2urn"] -[ext_resource type="PackedScene" uid="uid://pbnsngx5jvrh" path="res://src/enemy/NavigationAgentClient.tscn" id="4_ko6aj"] - -[sub_resource type="Resource" id="Resource_ko6aj"] -script = ExtResource("2_iy2fp") -CurrentHP = 80.0 -MaximumHP = 80 -CurrentAttack = 20 -CurrentDefense = 1 -MaxAttack = 20 -MaxDefense = 1 -ExpFromDefeat = 50 -Luck = 0.05 -_telluricResistance = 0.0 -_aeolicResistance = 0.0 -_hydricResistance = 0.0 -_igneousResistance = 0.0 -_ferrumResistance = 0.0 -DropsSoulGemChance = 0.75 -metadata/_custom_type_script = "uid://dnkmr0eq1sij0" +[ext_resource type="PackedScene" uid="uid://bxymnqkoi78oa" path="res://src/system/stats/HealthComponent.tscn" id="3_jhnwb"] +[ext_resource type="PackedScene" uid="uid://2nkvacxsd46b" path="res://src/enemy/behaviors/PatrolBehavior.tscn" id="4_bjnvx"] +[ext_resource type="PackedScene" uid="uid://mqj4jju3870v" path="res://src/enemy/behaviors/FollowBehavior.tscn" id="5_55sdf"] +[ext_resource type="PackedScene" uid="uid://8bcme8ao4axa" path="res://src/enemy/behaviors/EngagePlayerBehavior.tscn" id="6_2xj0s"] [sub_resource type="CapsuleShape3D" id="CapsuleShape3D_cwfph"] radius = 0.717471 @@ -34,14 +18,15 @@ radius = 1.0 [sub_resource type="SphereShape3D" id="SphereShape3D_8vcnq"] radius = 1.20703 +[sub_resource type="CylinderShape3D" id="CylinderShape3D_jhnwb"] + [node name="Ballos" type="CharacterBody3D"] process_mode = 1 collision_layer = 10 collision_mask = 11 axis_lock_linear_y = true axis_lock_angular_x = true -script = ExtResource("1_v2urn") -_enemyStatResource = SubResource("Resource_ko6aj") +script = ExtResource("1_iy2fp") [node name="CollisionShape" type="CollisionShape3D" parent="."] unique_name_in_owner = true @@ -58,18 +43,9 @@ collision_mask = 2 transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0, -2) shape = SubResource("CylinderShape3D_jbgmx") -[node name="PatrolTimer" type="Timer" parent="."] -unique_name_in_owner = true -wait_time = 10.0 -autostart = true - -[node name="AttackTimer" type="Timer" parent="."] -unique_name_in_owner = true -wait_time = 0.8 -autostart = true - -[node name="Raycast" type="RayCast3D" parent="."] +[node name="Raycast" type="RayCast3D" parent="LineOfSight"] unique_name_in_owner = true +transform = Transform3D(-1, 0, 8.74228e-08, 0, 1, 0, -8.74228e-08, 0, -1, 0, 0, 0) target_position = Vector3(0, 0, -5) collision_mask = 3 @@ -83,7 +59,30 @@ shape = SubResource("SphereShape3D_8vcnq") [node name="EnemyModelView" parent="." instance=ExtResource("2_v2urn")] unique_name_in_owner = true -[node name="Navigation" type="Node3D" parent="."] - -[node name="NavigationAgentClient" parent="Navigation" instance=ExtResource("4_ko6aj")] +[node name="PlayerDetector" type="Area3D" parent="."] unique_name_in_owner = true +collision_layer = 0 +collision_mask = 34 + +[node name="CollisionShape3D" type="CollisionShape3D" parent="PlayerDetector"] +shape = SubResource("CylinderShape3D_jhnwb") + +[node name="Components" type="Node3D" parent="."] + +[node name="HealthComponent" parent="Components" instance=ExtResource("3_jhnwb")] + +[node name="PatrolBehavior" parent="Components" instance=ExtResource("4_bjnvx")] +unique_name_in_owner = true + +[node name="FollowBehavior" parent="Components" instance=ExtResource("5_55sdf")] +unique_name_in_owner = true +_followSpeed = 150.0 + +[node name="EngagePlayerBehavior" parent="Components" instance=ExtResource("6_2xj0s")] +unique_name_in_owner = true +_acquireTargetTime = 2.0 + +[node name="NavigationAgent" type="NavigationAgent3D" parent="Components"] +unique_name_in_owner = true +avoidance_enabled = true +radius = 1.0 diff --git a/Zennysoft.Game.Ma/src/enemy/enemy_types/06. chariot/Chariot.cs b/Zennysoft.Game.Ma/src/enemy/enemy_types/06. chariot/Chariot.cs index 147e2f50..56ba728a 100644 --- a/Zennysoft.Game.Ma/src/enemy/enemy_types/06. chariot/Chariot.cs +++ b/Zennysoft.Game.Ma/src/enemy/enemy_types/06. chariot/Chariot.cs @@ -1,86 +1,37 @@ using Chickensoft.AutoInject; using Chickensoft.Introspection; using Godot; -using Zennysoft.Game.Abstractions; -using Zennysoft.Ma.Adapter; - -namespace Zennysoft.Game.Ma; +using Zennysoft.Game.Ma; [Meta(typeof(IAutoNode))] -public partial class Chariot : FollowsPlayerEnemy, IHasPrimaryAttack, ICanActivate, ICanPatrol +public partial class Chariot : Enemy2D, IHavePatrolBehavior, IHaveEngagePlayerBehavior, IHaveFollowBehavior { - [Export] - public ElementType PrimaryAttackElementalType { get; set; } = ElementType.None; - [Export] - public double PrimaryAttackElementalDamageBonus { get; set; } = 1.0; + public override void _Notification(int what) => this.Notify(what); - [Node] private INavigationAgentClient _navigationAgentClient { get; set; } = default!; + [Node] public NavigationAgent3D NavigationAgent { get; set; } + [Node] public PatrolBehavior PatrolBehavior { get; set; } = default!; + [Node] public FollowBehavior FollowBehavior { get; set; } = default!; + [Node] public EngagePlayerBehavior EngagePlayerBehavior { get; set; } = default!; - private bool _activated = false; + [Node] public Area3D PlayerDetector { get; set; } = default!; - public void OnPhysicsProcess(double delta) + public void OnReady() { - _enemyLogic.Input(new EnemyLogic.Input.PhysicsTick(delta)); - FollowPlayerAndAttack(_enemyLogic, GlobalPosition, _player.CurrentPosition); - - if (_activated) - _movementSpeed = 0; - - Velocity = _navigationAgentClient.Velocity; - base._PhysicsProcess(delta); + FollowBehavior.Init(NavigationAgent); + PatrolBehavior.Init(NavigationAgent); + PatrolBehavior.HomePosition = GlobalPosition; + PatrolBehavior.OnVelocityComputed += OnVelocityComputed; + FollowBehavior.OnVelocityComputed += OnVelocityComputed; + EngagePlayerBehavior.TakeAction += EngagePlayerBehavior_TakeAction; + EngagePlayerBehavior.AcquireTarget += EngagePlayerBehavior_AcquireTarget; + _enemyLogic.Input(new EnemyLogic.Input.Patrol()); + PlayerDetector.BodyEntered += PlayerDetector_BodyEntered; + PlayerDetector.BodyExited += PlayerDetector_BodyExited; + SetPhysicsProcess(true); } - public override void _Process(double delta) + public override void Activate() { - if (CurrentHP.Value <= 0) - return; - - var lookDir = GlobalPosition + Velocity; - if (!lookDir.IsEqualApprox(GlobalPosition)) - LookAt(lookDir, Vector3.Up, true); - - base.EnemyModelView.SetCurrentDirection(GlobalBasis, -_player.CurrentBasis.Z); - - if (base.EnemyModelView is EnemyModelView2D && !_activated) - { - if (Velocity > Vector3.Zero) - base.EnemyModelView.PlayWalkAnimation(); - else - base.EnemyModelView.PlayIdleAnimation(); - } - } - - public override void Die() - { - base.Die(); - } - - public override void TakeAction() - { - PrimaryAttack(); - } - public void PrimaryAttack() - { - base.EnemyModelView.PlayPrimaryAttackAnimation(); - } - - public void Activate() - { - if (_activated) - return; - EnemyModelView.PlayActivateAnimation(); - _activated = true; - } - - public override void SetTarget(Vector3 target) => _navigationAgentClient.SetTarget(target); - - public void Patrol() - { - var rng = new RandomNumberGenerator(); - rng.Randomize(); - var randomizedSpot = new Vector3(rng.RandfRange(-5.0f, 5.0f), 0, rng.RandfRange(-5.0f, 5.0f)); - _enemyLogic.Input(new EnemyLogic.Input.PatrolToRandomSpot(GlobalPosition + randomizedSpot)); - _enemyLogic.Input(new EnemyLogic.Input.StartPatrol()); } } \ No newline at end of file diff --git a/Zennysoft.Game.Ma/src/enemy/enemy_types/06. chariot/Chariot.tscn b/Zennysoft.Game.Ma/src/enemy/enemy_types/06. chariot/Chariot.tscn index f3ded344..4b8d647c 100644 --- a/Zennysoft.Game.Ma/src/enemy/enemy_types/06. chariot/Chariot.tscn +++ b/Zennysoft.Game.Ma/src/enemy/enemy_types/06. chariot/Chariot.tscn @@ -1,27 +1,11 @@ -[gd_scene load_steps=9 format=3 uid="uid://dlw5cvutvypxn"] +[gd_scene load_steps=11 format=3 uid="uid://dlw5cvutvypxn"] -[ext_resource type="Script" uid="uid://djx5x5bhkku85" path="res://src/enemy/enemy_types/06. chariot/Chariot.cs" id="1_hqeyd"] -[ext_resource type="Script" uid="uid://dnkmr0eq1sij0" path="res://src/enemy/EnemyStatResource.cs" id="2_77bk6"] +[ext_resource type="Script" uid="uid://djx5x5bhkku85" path="res://src/enemy/enemy_types/06. chariot/Chariot.cs" id="1_q1q0f"] +[ext_resource type="PackedScene" uid="uid://bxymnqkoi78oa" path="res://src/system/stats/HealthComponent.tscn" id="3_ialne"] [ext_resource type="PackedScene" uid="uid://dcm53j3rncxdm" path="res://src/enemy/enemy_types/06. chariot/ChariotModelView.tscn" id="3_q1q0f"] -[ext_resource type="PackedScene" uid="uid://pbnsngx5jvrh" path="res://src/enemy/NavigationAgentClient.tscn" id="4_mdd1w"] - -[sub_resource type="Resource" id="Resource_dvne1"] -script = ExtResource("2_77bk6") -CurrentHP = 90.0 -MaximumHP = 90 -CurrentAttack = 5 -CurrentDefense = 5 -MaxAttack = 5 -MaxDefense = 5 -ExpFromDefeat = 15 -Luck = 0.05 -_telluricResistance = 0.0 -_aeolicResistance = 0.0 -_hydricResistance = 0.0 -_igneousResistance = 0.0 -_ferrumResistance = 0.0 -DropsSoulGemChance = 0.75 -metadata/_custom_type_script = ExtResource("2_77bk6") +[ext_resource type="PackedScene" uid="uid://2nkvacxsd46b" path="res://src/enemy/behaviors/PatrolBehavior.tscn" id="4_ee8v4"] +[ext_resource type="PackedScene" uid="uid://mqj4jju3870v" path="res://src/enemy/behaviors/FollowBehavior.tscn" id="5_uv8in"] +[ext_resource type="PackedScene" uid="uid://8bcme8ao4axa" path="res://src/enemy/behaviors/EngagePlayerBehavior.tscn" id="6_cfqmf"] [sub_resource type="CapsuleShape3D" id="CapsuleShape3D_cwfph"] radius = 1.0 @@ -33,25 +17,21 @@ radius = 1.0 [sub_resource type="SphereShape3D" id="SphereShape3D_lqifn"] radius = 1.20703 +[sub_resource type="CylinderShape3D" id="CylinderShape3D_582pa"] + [node name="Chariot" type="CharacterBody3D"] process_mode = 1 collision_layer = 10 collision_mask = 3 axis_lock_linear_y = true axis_lock_angular_x = true -script = ExtResource("1_hqeyd") -_enemyStatResource = SubResource("Resource_dvne1") +script = ExtResource("1_q1q0f") [node name="CollisionShape" type="CollisionShape3D" parent="."] unique_name_in_owner = true transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0, 0) shape = SubResource("CapsuleShape3D_cwfph") -[node name="Navigation" type="Node3D" parent="."] - -[node name="NavigationAgentClient" parent="Navigation" instance=ExtResource("4_mdd1w")] -unique_name_in_owner = true - [node name="LineOfSight" type="Area3D" parent="."] unique_name_in_owner = true transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0, 0) @@ -62,6 +42,10 @@ collision_mask = 2 transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0, -2) shape = SubResource("CylinderShape3D_jbgmx") +[node name="Raycast" type="RayCast3D" parent="LineOfSight"] +unique_name_in_owner = true +transform = Transform3D(-1, 0, 8.74228e-08, 0, 1, 0, -8.74228e-08, 0, -1, 0, 0, 0) + [node name="Collision" type="Area3D" parent="."] collision_layer = 2048 collision_mask = 0 @@ -73,17 +57,30 @@ shape = SubResource("SphereShape3D_lqifn") unique_name_in_owner = true transform = Transform3D(0.999848, 0, 0.0174524, 0, 1, 0, -0.0174524, 0, 0.999848, 0, 0, 0) -[node name="Raycast" type="RayCast3D" parent="."] +[node name="PlayerDetector" type="Area3D" parent="."] +unique_name_in_owner = true +collision_layer = 0 +collision_mask = 34 + +[node name="CollisionShape3D" type="CollisionShape3D" parent="PlayerDetector"] +shape = SubResource("CylinderShape3D_582pa") + +[node name="Components" type="Node3D" parent="."] + +[node name="HealthComponent" parent="Components" instance=ExtResource("3_ialne")] + +[node name="PatrolBehavior" parent="Components" instance=ExtResource("4_ee8v4")] unique_name_in_owner = true -[node name="Timers" type="Node" parent="."] - -[node name="PatrolTimer" type="Timer" parent="Timers"] +[node name="FollowBehavior" parent="Components" instance=ExtResource("5_uv8in")] unique_name_in_owner = true -wait_time = 10.0 -autostart = true +_followSpeed = 150.0 -[node name="AttackTimer" type="Timer" parent="Timers"] +[node name="EngagePlayerBehavior" parent="Components" instance=ExtResource("6_cfqmf")] unique_name_in_owner = true -wait_time = 0.8 -autostart = true +_acquireTargetTime = 2.0 + +[node name="NavigationAgent" type="NavigationAgent3D" parent="Components"] +unique_name_in_owner = true +avoidance_enabled = true +radius = 1.0 diff --git a/Zennysoft.Game.Ma/src/enemy/enemy_types/07. chinthe/Chinthe.cs b/Zennysoft.Game.Ma/src/enemy/enemy_types/07. chinthe/Chinthe.cs index f76e29d0..f7396768 100644 --- a/Zennysoft.Game.Ma/src/enemy/enemy_types/07. chinthe/Chinthe.cs +++ b/Zennysoft.Game.Ma/src/enemy/enemy_types/07. chinthe/Chinthe.cs @@ -1,83 +1,32 @@ using Chickensoft.AutoInject; using Chickensoft.Introspection; using Godot; -using Zennysoft.Game.Abstractions; -using Zennysoft.Ma.Adapter; - -namespace Zennysoft.Game.Ma; +using Zennysoft.Game.Ma; [Meta(typeof(IAutoNode))] -public partial class Chinthe : FollowsPlayerEnemy, IHasPrimaryAttack, ICanPatrol, ICanActivate +public partial class Chinthe : Enemy2D, IHaveEngagePlayerBehavior, IHaveFollowBehavior { public override void _Notification(int what) => this.Notify(what); - [Export] - public ElementType PrimaryAttackElementalType { get; set; } = ElementType.None; + [Node] public NavigationAgent3D NavigationAgent { get; set; } + [Node] public FollowBehavior FollowBehavior { get; set; } = default!; + [Node] public EngagePlayerBehavior EngagePlayerBehavior { get; set; } = default!; - [Export] - public double PrimaryAttackElementalDamageBonus { get; set; } = 1.0; - - [Node] private INavigationAgentClient _navigationAgentClient { get; set; } = default!; + [Node] public Area3D PlayerDetector { get; set; } = default!; public void OnReady() { + FollowBehavior.Init(NavigationAgent); + FollowBehavior.OnVelocityComputed += OnVelocityComputed; + EngagePlayerBehavior.TakeAction += EngagePlayerBehavior_TakeAction; + EngagePlayerBehavior.AcquireTarget += EngagePlayerBehavior_AcquireTarget; + PlayerDetector.BodyEntered += PlayerDetector_BodyEntered; + PlayerDetector.BodyExited += PlayerDetector_BodyExited; SetPhysicsProcess(true); } - public void OnPhysicsProcess(double delta) - { - if (_enemyLogic.Value is not EnemyLogic.State.Activated) - return; - - _enemyLogic.Input(new EnemyLogic.Input.PhysicsTick(delta)); - FollowPlayerAndAttack(_enemyLogic, GlobalPosition, _player.CurrentPosition); - Velocity = _navigationAgentClient.Velocity; - base._PhysicsProcess(delta); - } - - public override void Die() - { - base.Die(); - } - - public override void TakeAction() - { - PrimaryAttack(); - } - - public void PrimaryAttack() - { - EnemyModelView.PlayPrimaryAttackAnimation(); - } - - public override void SetTarget(Vector3 target) => _navigationAgentClient.SetTarget(target); - - public void Patrol() - { - var rng = new RandomNumberGenerator(); - rng.Randomize(); - var randomizedSpot = new Vector3(rng.RandfRange(-5.0f, 5.0f), 0, rng.RandfRange(-5.0f, 5.0f)); - _enemyLogic.Input(new EnemyLogic.Input.PatrolToRandomSpot(GlobalPosition + randomizedSpot)); - _enemyLogic.Input(new EnemyLogic.Input.StartPatrol()); - } - - public void Activate() + public override void Activate() { EnemyModelView.PlayActivateAnimation(); } - - public void Teleport() - { - //EnemyModelView.PlayTeleportAnimation(); - } - - private void Hitbox_AreaEntered(Area3D area) - { - var target = area.GetOwner(); - if (target is IPlayer player) - { - var damage = _enemyStatResource.CurrentAttack * PrimaryAttackElementalDamageBonus; - player.TakeDamage(damage, PrimaryAttackElementalType, BattleExtensions.IsCriticalHit(_enemyStatResource.Luck)); - } - } -} +} \ No newline at end of file diff --git a/Zennysoft.Game.Ma/src/enemy/enemy_types/07. chinthe/Chinthe.tscn b/Zennysoft.Game.Ma/src/enemy/enemy_types/07. chinthe/Chinthe.tscn index 198f6a39..861be9c3 100644 --- a/Zennysoft.Game.Ma/src/enemy/enemy_types/07. chinthe/Chinthe.tscn +++ b/Zennysoft.Game.Ma/src/enemy/enemy_types/07. chinthe/Chinthe.tscn @@ -1,10 +1,11 @@ -[gd_scene load_steps=10 format=3 uid="uid://c6tqt27ql8s35"] +[gd_scene load_steps=12 format=3 uid="uid://c6tqt27ql8s35"] -[ext_resource type="Script" uid="uid://fwtjthix6awv" path="res://src/enemy/enemy_types/07. chinthe/Chinthe.cs" id="1_vw2ww"] -[ext_resource type="Resource" uid="uid://c854s8bdil3" path="res://src/enemy/enemy_types/07. chinthe/ChintheStats.tres" id="2_d665t"] +[ext_resource type="Script" uid="uid://fwtjthix6awv" path="res://src/enemy/enemy_types/07. chinthe/Chinthe.cs" id="1_120m2"] [ext_resource type="Script" uid="uid://dlsgyx4i1jmp3" path="res://src/enemy/EnemyLoreInfo.cs" id="3_567xa"] -[ext_resource type="PackedScene" uid="uid://pbnsngx5jvrh" path="res://src/enemy/NavigationAgentClient.tscn" id="3_d665t"] [ext_resource type="PackedScene" uid="uid://byd7cwxq1be6f" path="res://src/enemy/enemy_types/07. chinthe/ChinteModelView.tscn" id="3_ncr2e"] +[ext_resource type="PackedScene" uid="uid://bxymnqkoi78oa" path="res://src/system/stats/HealthComponent.tscn" id="4_w103a"] +[ext_resource type="PackedScene" uid="uid://mqj4jju3870v" path="res://src/enemy/behaviors/FollowBehavior.tscn" id="6_t7elt"] +[ext_resource type="PackedScene" uid="uid://8bcme8ao4axa" path="res://src/enemy/behaviors/EngagePlayerBehavior.tscn" id="7_24q6i"] [sub_resource type="CapsuleShape3D" id="CapsuleShape3D_cwfph"] radius = 0.226425 @@ -23,6 +24,8 @@ metadata/_custom_type_script = "uid://dlsgyx4i1jmp3" [sub_resource type="SphereShape3D" id="SphereShape3D_8vcnq"] radius = 1.20703 +[sub_resource type="CylinderShape3D" id="CylinderShape3D_q6h01"] + [node name="Chinthe" type="CharacterBody3D"] process_mode = 1 collision_layer = 10 @@ -31,12 +34,7 @@ axis_lock_linear_y = true axis_lock_angular_x = true axis_lock_angular_z = true motion_mode = 1 -script = ExtResource("1_vw2ww") -_enemyStatResource = ExtResource("2_d665t") -_movementSpeed = 2.0 - -[node name="NavigationAgentClient" parent="." instance=ExtResource("3_d665t")] -unique_name_in_owner = true +script = ExtResource("1_120m2") [node name="CollisionShape" type="CollisionShape3D" parent="."] unique_name_in_owner = true @@ -53,18 +51,9 @@ collision_mask = 2 transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0, -2) shape = SubResource("CylinderShape3D_jbgmx") -[node name="PatrolTimer" type="Timer" parent="."] -unique_name_in_owner = true -wait_time = 10.0 -autostart = true - -[node name="AttackTimer" type="Timer" parent="."] -unique_name_in_owner = true -wait_time = 0.8 -autostart = true - -[node name="Raycast" type="RayCast3D" parent="."] +[node name="Raycast" type="RayCast3D" parent="LineOfSight"] unique_name_in_owner = true +transform = Transform3D(-1, 0, 8.74228e-08, 0, 1, 0, -8.74228e-08, 0, -1, 0, 0, 0) target_position = Vector3(0, 0, -5) collision_mask = 3 debug_shape_custom_color = Color(0.60023, 7.84531e-06, 0.405364, 1) @@ -80,3 +69,28 @@ collision_mask = 0 [node name="CollisionShape3D" type="CollisionShape3D" parent="Collision"] shape = SubResource("SphereShape3D_8vcnq") + +[node name="PlayerDetector" type="Area3D" parent="."] +unique_name_in_owner = true +collision_layer = 0 +collision_mask = 34 + +[node name="CollisionShape3D" type="CollisionShape3D" parent="PlayerDetector"] +shape = SubResource("CylinderShape3D_q6h01") + +[node name="Components" type="Node3D" parent="."] + +[node name="HealthComponent" parent="Components" instance=ExtResource("4_w103a")] + +[node name="FollowBehavior" parent="Components" instance=ExtResource("6_t7elt")] +unique_name_in_owner = true +_followSpeed = 150.0 + +[node name="EngagePlayerBehavior" parent="Components" instance=ExtResource("7_24q6i")] +unique_name_in_owner = true +_acquireTargetTime = 2.0 + +[node name="NavigationAgent" type="NavigationAgent3D" parent="Components"] +unique_name_in_owner = true +avoidance_enabled = true +radius = 1.0 diff --git a/Zennysoft.Game.Ma/src/enemy/enemy_types/08a. Ambassador/Ambassador.cs b/Zennysoft.Game.Ma/src/enemy/enemy_types/08a. Ambassador/Ambassador.cs index 480198f2..349d3b75 100644 --- a/Zennysoft.Game.Ma/src/enemy/enemy_types/08a. Ambassador/Ambassador.cs +++ b/Zennysoft.Game.Ma/src/enemy/enemy_types/08a. Ambassador/Ambassador.cs @@ -1,84 +1,32 @@ using Chickensoft.AutoInject; using Chickensoft.Introspection; using Godot; -using System.Collections.Generic; -using System; -using Zennysoft.Game.Abstractions; -using Zennysoft.Ma.Adapter; - -namespace Zennysoft.Game.Ma; +using Zennysoft.Game.Ma; [Meta(typeof(IAutoNode))] -public partial class Ambassador : FollowsPlayerEnemy, IHasPrimaryAttack, IHasSecondaryAttack, ICanPatrol +public partial class Ambassador : Enemy2D, IHavePatrolBehavior, IHaveEngagePlayerBehavior, IHaveFollowBehavior { public override void _Notification(int what) => this.Notify(what); - [Export] - public ElementType PrimaryAttackElementalType { get; set; } = ElementType.None; - [Export] - public double PrimaryAttackElementalDamageBonus { get; set; } = 1.0; + [Node] public NavigationAgent3D NavigationAgent { get; set; } + [Node] public PatrolBehavior PatrolBehavior { get; set; } = default!; + [Node] public FollowBehavior FollowBehavior { get; set; } = default!; + [Node] public EngagePlayerBehavior EngagePlayerBehavior { get; set; } = default!; - [Node] private INavigationAgentClient _navigationAgentClient { get; set; } = default!; - - public ElementType SecondaryAttackElementalType { get; set; } = ElementType.None; - - public double SecondaryAttackElementalDamageBonus { get; set; } = 1.0; + [Node] public Area3D PlayerDetector { get; set; } = default!; public void OnReady() { + FollowBehavior.Init(NavigationAgent); + PatrolBehavior.Init(NavigationAgent); + PatrolBehavior.HomePosition = GlobalPosition; + PatrolBehavior.OnVelocityComputed += OnVelocityComputed; + FollowBehavior.OnVelocityComputed += OnVelocityComputed; + EngagePlayerBehavior.TakeAction += EngagePlayerBehavior_TakeAction; + EngagePlayerBehavior.AcquireTarget += EngagePlayerBehavior_AcquireTarget; + _enemyLogic.Input(new EnemyLogic.Input.Patrol()); + PlayerDetector.BodyEntered += PlayerDetector_BodyEntered; + PlayerDetector.BodyExited += PlayerDetector_BodyExited; SetPhysicsProcess(true); - ((EnemyModelView2D)EnemyModelView).Hitbox.AreaEntered += Hitbox_AreaEntered; } - - public void OnPhysicsProcess(double delta) - { - _enemyLogic.Input(new EnemyLogic.Input.PhysicsTick(delta)); - FollowPlayerAndAttack(_enemyLogic, GlobalPosition, _player.CurrentPosition); - Velocity = _navigationAgentClient.Velocity; - base._PhysicsProcess(delta); - } - - public override void TakeAction() - { - var rng = new RandomNumberGenerator(); - var options = new List() { PrimaryAttack, SecondaryAttack }; - var selection = rng.RandWeighted([0.875f, 0.125f]); - options[(int)selection].Invoke(); - } - - public override void Die() - { - base.Die(); - } - - public void PrimaryAttack() - { - EnemyModelView.PlayPrimaryAttackAnimation(); - } - - public void SecondaryAttack() - { - EnemyModelView.PlaySecondaryAttackAnimation(); - } - - public override void SetTarget(Vector3 target) => _navigationAgentClient.SetTarget(target); - - public void Patrol() - { - var rng = new RandomNumberGenerator(); - rng.Randomize(); - var randomizedSpot = new Vector3(rng.RandfRange(-5.0f, 5.0f), 0, rng.RandfRange(-5.0f, 5.0f)); - _enemyLogic.Input(new EnemyLogic.Input.PatrolToRandomSpot(GlobalPosition + randomizedSpot)); - _enemyLogic.Input(new EnemyLogic.Input.StartPatrol()); - } - - private void Hitbox_AreaEntered(Area3D area) - { - var target = area.GetOwner(); - if (target is IPlayer player) - { - var damage = _enemyStatResource.CurrentAttack * PrimaryAttackElementalDamageBonus; - player.TakeDamage(damage, PrimaryAttackElementalType, BattleExtensions.IsCriticalHit(_enemyStatResource.Luck)); - } - } -} +} \ No newline at end of file diff --git a/Zennysoft.Game.Ma/src/enemy/enemy_types/08a. Ambassador/Ambassador.tscn b/Zennysoft.Game.Ma/src/enemy/enemy_types/08a. Ambassador/Ambassador.tscn index 5bcb4279..13db250b 100644 --- a/Zennysoft.Game.Ma/src/enemy/enemy_types/08a. Ambassador/Ambassador.tscn +++ b/Zennysoft.Game.Ma/src/enemy/enemy_types/08a. Ambassador/Ambassador.tscn @@ -1,9 +1,11 @@ -[gd_scene load_steps=8 format=3 uid="uid://fosk3kt7vp8d"] +[gd_scene load_steps=11 format=3 uid="uid://fosk3kt7vp8d"] [ext_resource type="Script" uid="uid://dauir5q616wyq" path="res://src/enemy/enemy_types/08a. Ambassador/Ambassador.cs" id="1_m2guv"] -[ext_resource type="Resource" uid="uid://bcpygpm2q5bpn" path="res://src/enemy/enemy_types/08a. Ambassador/AmbassadorStats.tres" id="2_pjmem"] -[ext_resource type="PackedScene" uid="uid://pbnsngx5jvrh" path="res://src/enemy/NavigationAgentClient.tscn" id="3_pj8fe"] +[ext_resource type="PackedScene" uid="uid://bxymnqkoi78oa" path="res://src/system/stats/HealthComponent.tscn" id="3_enaes"] +[ext_resource type="PackedScene" uid="uid://2nkvacxsd46b" path="res://src/enemy/behaviors/PatrolBehavior.tscn" id="4_hqy0f"] [ext_resource type="PackedScene" uid="uid://c2i8ylr3y0bri" path="res://src/enemy/enemy_types/08a. Ambassador/AmbassadorModelView.tscn" id="4_pjmem"] +[ext_resource type="PackedScene" uid="uid://mqj4jju3870v" path="res://src/enemy/behaviors/FollowBehavior.tscn" id="5_gy5yi"] +[ext_resource type="PackedScene" uid="uid://8bcme8ao4axa" path="res://src/enemy/behaviors/EngagePlayerBehavior.tscn" id="6_7f1qq"] [sub_resource type="CapsuleShape3D" id="CapsuleShape3D_cwfph"] radius = 0.106078 @@ -16,6 +18,8 @@ radius = 0.57308 height = 5.0 radius = 1.0 +[sub_resource type="CylinderShape3D" id="CylinderShape3D_sjoyv"] + [node name="Ambassador" type="CharacterBody3D"] process_mode = 1 collision_layer = 10 @@ -24,18 +28,12 @@ axis_lock_linear_y = true axis_lock_angular_x = true axis_lock_angular_z = true script = ExtResource("1_m2guv") -_enemyStatResource = ExtResource("2_pjmem") [node name="CollisionShape" type="CollisionShape3D" parent="."] unique_name_in_owner = true transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0, 0) shape = SubResource("CapsuleShape3D_cwfph") -[node name="Navigation" type="Node3D" parent="."] - -[node name="NavigationAgentClient" parent="Navigation" instance=ExtResource("3_pj8fe")] -unique_name_in_owner = true - [node name="Collision" type="Node3D" parent="."] [node name="Collision" type="Area3D" parent="Collision"] @@ -55,8 +53,9 @@ collision_mask = 2 transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0, -2) shape = SubResource("CylinderShape3D_jbgmx") -[node name="Raycast" type="RayCast3D" parent="Collision"] +[node name="Raycast" type="RayCast3D" parent="Collision/LineOfSight"] unique_name_in_owner = true +transform = Transform3D(-1, 0, 8.74228e-08, 0, 1, 0, -8.74228e-08, 0, -1, 0, 0, 0) target_position = Vector3(0, 0, -5) collision_mask = 3 @@ -65,14 +64,30 @@ collision_mask = 3 [node name="EnemyModelView" parent="Visual" instance=ExtResource("4_pjmem")] unique_name_in_owner = true -[node name="Timers" type="Node" parent="."] - -[node name="PatrolTimer" type="Timer" parent="Timers"] +[node name="PlayerDetector" type="Area3D" parent="."] unique_name_in_owner = true -wait_time = 10.0 -autostart = true +collision_layer = 0 +collision_mask = 34 -[node name="AttackTimer" type="Timer" parent="Timers"] +[node name="CollisionShape3D" type="CollisionShape3D" parent="PlayerDetector"] +shape = SubResource("CylinderShape3D_sjoyv") + +[node name="Components" type="Node3D" parent="."] + +[node name="HealthComponent" parent="Components" instance=ExtResource("3_enaes")] + +[node name="PatrolBehavior" parent="Components" instance=ExtResource("4_hqy0f")] unique_name_in_owner = true -wait_time = 0.8 -autostart = true + +[node name="FollowBehavior" parent="Components" instance=ExtResource("5_gy5yi")] +unique_name_in_owner = true +_followSpeed = 150.0 + +[node name="EngagePlayerBehavior" parent="Components" instance=ExtResource("6_7f1qq")] +unique_name_in_owner = true +_acquireTargetTime = 2.0 + +[node name="NavigationAgent" type="NavigationAgent3D" parent="Components"] +unique_name_in_owner = true +avoidance_enabled = true +radius = 1.0 diff --git a/Zennysoft.Game.Ma/src/enemy/enemy_types/08b. Ambassador (red)/AmbassadorRed.tscn b/Zennysoft.Game.Ma/src/enemy/enemy_types/08b. Ambassador (red)/AmbassadorRed.tscn index 0edb3fce..f2117501 100644 --- a/Zennysoft.Game.Ma/src/enemy/enemy_types/08b. Ambassador (red)/AmbassadorRed.tscn +++ b/Zennysoft.Game.Ma/src/enemy/enemy_types/08b. Ambassador (red)/AmbassadorRed.tscn @@ -1,21 +1,26 @@ -[gd_scene load_steps=8 format=3 uid="uid://c5gbaybqm4cuk"] +[gd_scene load_steps=12 format=3 uid="uid://c5gbaybqm4cuk"] [ext_resource type="Script" uid="uid://dauir5q616wyq" path="res://src/enemy/enemy_types/08a. Ambassador/Ambassador.cs" id="1_4nav4"] [ext_resource type="Resource" uid="uid://doycpt2aqxnx" path="res://src/enemy/enemy_types/08b. Ambassador (red)/AmbassadorStats.tres" id="2_hqkeq"] -[ext_resource type="PackedScene" uid="uid://pbnsngx5jvrh" path="res://src/enemy/NavigationAgentClient.tscn" id="3_31xt7"] +[ext_resource type="PackedScene" uid="uid://bxymnqkoi78oa" path="res://src/system/stats/HealthComponent.tscn" id="4_apdt1"] [ext_resource type="PackedScene" uid="uid://72lbcmp4bcx4" path="res://src/enemy/enemy_types/08b. Ambassador (red)/AmbassadorSmallModelView.tscn" id="4_hqkeq"] +[ext_resource type="PackedScene" uid="uid://2nkvacxsd46b" path="res://src/enemy/behaviors/PatrolBehavior.tscn" id="5_65xvc"] +[ext_resource type="PackedScene" uid="uid://mqj4jju3870v" path="res://src/enemy/behaviors/FollowBehavior.tscn" id="6_v4xmn"] +[ext_resource type="PackedScene" uid="uid://8bcme8ao4axa" path="res://src/enemy/behaviors/EngagePlayerBehavior.tscn" id="7_a21yr"] [sub_resource type="CapsuleShape3D" id="CapsuleShape3D_cwfph"] radius = 0.106078 height = 1.23076 -[sub_resource type="SphereShape3D" id="SphereShape3D_8vcnq"] -radius = 0.57308 - [sub_resource type="CylinderShape3D" id="CylinderShape3D_jbgmx"] height = 5.0 radius = 1.0 +[sub_resource type="SphereShape3D" id="SphereShape3D_8vcnq"] +radius = 0.57308 + +[sub_resource type="CylinderShape3D" id="CylinderShape3D_o0cbq"] + [node name="AmbassadorRed" type="CharacterBody3D"] process_mode = 1 collision_layer = 10 @@ -31,10 +36,21 @@ unique_name_in_owner = true transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0, 0) shape = SubResource("CapsuleShape3D_cwfph") -[node name="Navigation" type="Node3D" parent="."] - -[node name="NavigationAgentClient" parent="Navigation" instance=ExtResource("3_31xt7")] +[node name="LineOfSight" type="Area3D" parent="."] unique_name_in_owner = true +transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0, 0) +collision_layer = 2 +collision_mask = 2 + +[node name="CollisionShape3D" type="CollisionShape3D" parent="LineOfSight"] +transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0, -2) +shape = SubResource("CylinderShape3D_jbgmx") + +[node name="Raycast" type="RayCast3D" parent="LineOfSight"] +unique_name_in_owner = true +transform = Transform3D(-1, 0, 8.74228e-08, 0, 1, 0, -8.74228e-08, 0, -1, 0, 0, 0) +target_position = Vector3(0, 0, -5) +collision_mask = 3 [node name="Collision" type="Node3D" parent="."] @@ -45,34 +61,35 @@ collision_mask = 0 [node name="CollisionShape3D" type="CollisionShape3D" parent="Collision/Collision"] shape = SubResource("SphereShape3D_8vcnq") -[node name="LineOfSight" type="Area3D" parent="Collision"] -unique_name_in_owner = true -transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0, 0) -collision_layer = 2 -collision_mask = 2 - -[node name="CollisionShape3D" type="CollisionShape3D" parent="Collision/LineOfSight"] -transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0, -2) -shape = SubResource("CylinderShape3D_jbgmx") - -[node name="Raycast" type="RayCast3D" parent="Collision"] -unique_name_in_owner = true -target_position = Vector3(0, 0, -5) -collision_mask = 3 - [node name="Visual" type="Node3D" parent="."] [node name="EnemyModelView" parent="Visual" instance=ExtResource("4_hqkeq")] unique_name_in_owner = true -[node name="Timers" type="Node" parent="."] - -[node name="PatrolTimer" type="Timer" parent="Timers"] +[node name="PlayerDetector" type="Area3D" parent="."] unique_name_in_owner = true -wait_time = 10.0 -autostart = true +collision_layer = 0 +collision_mask = 34 -[node name="AttackTimer" type="Timer" parent="Timers"] +[node name="CollisionShape3D" type="CollisionShape3D" parent="PlayerDetector"] +shape = SubResource("CylinderShape3D_o0cbq") + +[node name="Components" type="Node3D" parent="."] + +[node name="HealthComponent" parent="Components" instance=ExtResource("4_apdt1")] + +[node name="PatrolBehavior" parent="Components" instance=ExtResource("5_65xvc")] unique_name_in_owner = true -wait_time = 0.8 -autostart = true + +[node name="FollowBehavior" parent="Components" instance=ExtResource("6_v4xmn")] +unique_name_in_owner = true +_followSpeed = 150.0 + +[node name="EngagePlayerBehavior" parent="Components" instance=ExtResource("7_a21yr")] +unique_name_in_owner = true +_acquireTargetTime = 2.0 + +[node name="NavigationAgent" type="NavigationAgent3D" parent="Components"] +unique_name_in_owner = true +avoidance_enabled = true +radius = 1.0 diff --git a/Zennysoft.Game.Ma/src/enemy/enemy_types/08c. Ambassador (steel)/AmbassadorSteel.tscn b/Zennysoft.Game.Ma/src/enemy/enemy_types/08c. Ambassador (steel)/AmbassadorSteel.tscn index 8d8bc0ba..75afe94e 100644 --- a/Zennysoft.Game.Ma/src/enemy/enemy_types/08c. Ambassador (steel)/AmbassadorSteel.tscn +++ b/Zennysoft.Game.Ma/src/enemy/enemy_types/08c. Ambassador (steel)/AmbassadorSteel.tscn @@ -1,21 +1,26 @@ -[gd_scene load_steps=8 format=3 uid="uid://b4oliop60eghn"] +[gd_scene load_steps=12 format=3 uid="uid://b4oliop60eghn"] [ext_resource type="Script" uid="uid://dauir5q616wyq" path="res://src/enemy/enemy_types/08a. Ambassador/Ambassador.cs" id="1_ln0kc"] [ext_resource type="Resource" uid="uid://dudtbfjfekkh1" path="res://src/enemy/enemy_types/08c. Ambassador (steel)/AmbassadorSteelStats.tres" id="2_kdt1g"] -[ext_resource type="PackedScene" uid="uid://pbnsngx5jvrh" path="res://src/enemy/NavigationAgentClient.tscn" id="3_aubqt"] +[ext_resource type="PackedScene" uid="uid://bxymnqkoi78oa" path="res://src/system/stats/HealthComponent.tscn" id="4_bo12t"] [ext_resource type="PackedScene" uid="uid://lc5koiqn1sca" path="res://src/enemy/enemy_types/08c. Ambassador (steel)/AmbassadorSteelModelView.tscn" id="4_kdt1g"] +[ext_resource type="PackedScene" uid="uid://2nkvacxsd46b" path="res://src/enemy/behaviors/PatrolBehavior.tscn" id="5_fmnae"] +[ext_resource type="PackedScene" uid="uid://mqj4jju3870v" path="res://src/enemy/behaviors/FollowBehavior.tscn" id="6_g5uri"] +[ext_resource type="PackedScene" uid="uid://8bcme8ao4axa" path="res://src/enemy/behaviors/EngagePlayerBehavior.tscn" id="7_5r3ee"] [sub_resource type="CapsuleShape3D" id="CapsuleShape3D_cwfph"] radius = 0.106078 height = 1.23076 -[sub_resource type="SphereShape3D" id="SphereShape3D_8vcnq"] -radius = 0.57308 - [sub_resource type="CylinderShape3D" id="CylinderShape3D_jbgmx"] height = 5.0 radius = 1.0 +[sub_resource type="SphereShape3D" id="SphereShape3D_8vcnq"] +radius = 0.57308 + +[sub_resource type="CylinderShape3D" id="CylinderShape3D_6o7lk"] + [node name="AmbassadorSteel" type="CharacterBody3D"] process_mode = 1 collision_layer = 10 @@ -31,10 +36,15 @@ unique_name_in_owner = true transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0, 0) shape = SubResource("CapsuleShape3D_cwfph") -[node name="Navigation" type="Node3D" parent="."] - -[node name="NavigationAgentClient" parent="Navigation" instance=ExtResource("3_aubqt")] +[node name="LineOfSight" type="Area3D" parent="."] unique_name_in_owner = true +transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0, 0) +collision_layer = 2 +collision_mask = 2 + +[node name="CollisionShape3D" type="CollisionShape3D" parent="LineOfSight"] +transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0, -2) +shape = SubResource("CylinderShape3D_jbgmx") [node name="Collision" type="Node3D" parent="."] @@ -45,16 +55,6 @@ collision_mask = 0 [node name="CollisionShape3D" type="CollisionShape3D" parent="Collision/Collision"] shape = SubResource("SphereShape3D_8vcnq") -[node name="LineOfSight" type="Area3D" parent="Collision"] -unique_name_in_owner = true -transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0, 0) -collision_layer = 2 -collision_mask = 2 - -[node name="CollisionShape3D" type="CollisionShape3D" parent="Collision/LineOfSight"] -transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0, -2) -shape = SubResource("CylinderShape3D_jbgmx") - [node name="Raycast" type="RayCast3D" parent="Collision"] unique_name_in_owner = true target_position = Vector3(0, 0, -5) @@ -65,14 +65,30 @@ collision_mask = 3 [node name="EnemyModelView" parent="Visual" instance=ExtResource("4_kdt1g")] unique_name_in_owner = true -[node name="Timers" type="Node" parent="."] - -[node name="PatrolTimer" type="Timer" parent="Timers"] +[node name="PlayerDetector" type="Area3D" parent="."] unique_name_in_owner = true -wait_time = 10.0 -autostart = true +collision_layer = 0 +collision_mask = 34 -[node name="AttackTimer" type="Timer" parent="Timers"] +[node name="CollisionShape3D" type="CollisionShape3D" parent="PlayerDetector"] +shape = SubResource("CylinderShape3D_6o7lk") + +[node name="Components" type="Node3D" parent="."] + +[node name="HealthComponent" parent="Components" instance=ExtResource("4_bo12t")] + +[node name="PatrolBehavior" parent="Components" instance=ExtResource("5_fmnae")] unique_name_in_owner = true -wait_time = 0.8 -autostart = true + +[node name="FollowBehavior" parent="Components" instance=ExtResource("6_g5uri")] +unique_name_in_owner = true +_followSpeed = 150.0 + +[node name="EngagePlayerBehavior" parent="Components" instance=ExtResource("7_5r3ee")] +unique_name_in_owner = true +_acquireTargetTime = 2.0 + +[node name="NavigationAgent" type="NavigationAgent3D" parent="Components"] +unique_name_in_owner = true +avoidance_enabled = true +radius = 1.0 diff --git a/Zennysoft.Game.Ma/src/enemy/enemy_types/09. Agni/AgniDemon.cs b/Zennysoft.Game.Ma/src/enemy/enemy_types/09. Agni/AgniDemon.cs index 1d88dac2..53aa0fe7 100644 --- a/Zennysoft.Game.Ma/src/enemy/enemy_types/09. Agni/AgniDemon.cs +++ b/Zennysoft.Game.Ma/src/enemy/enemy_types/09. Agni/AgniDemon.cs @@ -1,84 +1,32 @@ using Chickensoft.AutoInject; using Chickensoft.Introspection; using Godot; -using System.Collections.Generic; -using System; -using Zennysoft.Ma.Adapter; -using Zennysoft.Game.Abstractions; - -namespace Zennysoft.Game.Ma; +using Zennysoft.Game.Ma; [Meta(typeof(IAutoNode))] -public partial class AgniDemon : FollowsPlayerEnemy, IHasPrimaryAttack, IHasSecondaryAttack, ICanPatrol +public partial class AgniDemon : Enemy2D, IHavePatrolBehavior, IHaveEngagePlayerBehavior, IHaveFollowBehavior { public override void _Notification(int what) => this.Notify(what); - [Export] - public ElementType PrimaryAttackElementalType { get; set; } = ElementType.None; - [Export] - public double PrimaryAttackElementalDamageBonus { get; set; } = 1.0; - [Export] - public ElementType SecondaryAttackElementalType { get; set; } = ElementType.None; - [Export] - public double SecondaryAttackElementalDamageBonus { get; set; } = 1.0; + [Node] public NavigationAgent3D NavigationAgent { get; set; } + [Node] public PatrolBehavior PatrolBehavior { get; set; } = default!; + [Node] public FollowBehavior FollowBehavior { get; set; } = default!; + [Node] public EngagePlayerBehavior EngagePlayerBehavior { get; set; } = default!; - [Node] private INavigationAgentClient _navigationAgentClient { get; set; } = default!; + [Node] public Area3D PlayerDetector { get; set; } = default!; public void OnReady() { + FollowBehavior.Init(NavigationAgent); + PatrolBehavior.Init(NavigationAgent); + PatrolBehavior.HomePosition = GlobalPosition; + PatrolBehavior.OnVelocityComputed += OnVelocityComputed; + FollowBehavior.OnVelocityComputed += OnVelocityComputed; + EngagePlayerBehavior.TakeAction += EngagePlayerBehavior_TakeAction; + EngagePlayerBehavior.AcquireTarget += EngagePlayerBehavior_AcquireTarget; + _enemyLogic.Input(new EnemyLogic.Input.Patrol()); + PlayerDetector.BodyEntered += PlayerDetector_BodyEntered; + PlayerDetector.BodyExited += PlayerDetector_BodyExited; SetPhysicsProcess(true); - ((EnemyModelView2D)EnemyModelView).Hitbox.AreaEntered += Hitbox_AreaEntered; } - - public void OnPhysicsProcess(double delta) - { - _enemyLogic.Input(new EnemyLogic.Input.PhysicsTick(delta)); - FollowPlayerAndAttack(_enemyLogic, GlobalPosition, _player.CurrentPosition); - Velocity = _navigationAgentClient.Velocity; - base._PhysicsProcess(delta); - } - - public override void TakeAction() - { - var rng = new RandomNumberGenerator(); - var options = new List() { PrimaryAttack, SecondaryAttack }; - var selection = rng.RandWeighted([0.875f, 0.125f]); - options[(int)selection].Invoke(); - } - - public override void Die() - { - base.Die(); - } - - public void PrimaryAttack() - { - EnemyModelView.PlayPrimaryAttackAnimation(); - } - - public void SecondaryAttack() - { - EnemyModelView.PlaySecondaryAttackAnimation(); - } - - public override void SetTarget(Vector3 target) => _navigationAgentClient.SetTarget(target); - - public void Patrol() - { - var rng = new RandomNumberGenerator(); - rng.Randomize(); - var randomizedSpot = new Vector3(rng.RandfRange(-5.0f, 5.0f), 0, rng.RandfRange(-5.0f, 5.0f)); - _enemyLogic.Input(new EnemyLogic.Input.PatrolToRandomSpot(GlobalPosition + randomizedSpot)); - _enemyLogic.Input(new EnemyLogic.Input.StartPatrol()); - } - - private void Hitbox_AreaEntered(Area3D area) - { - var target = area.GetOwner(); - if (target is IPlayer player) - { - var damage = _enemyStatResource.CurrentAttack * PrimaryAttackElementalDamageBonus; - player.TakeDamage(damage, PrimaryAttackElementalType, BattleExtensions.IsCriticalHit(_enemyStatResource.Luck)); - } - } -} +} \ No newline at end of file diff --git a/Zennysoft.Game.Ma/src/enemy/enemy_types/09. Agni/AgniDemon.tscn b/Zennysoft.Game.Ma/src/enemy/enemy_types/09. Agni/AgniDemon.tscn index 68f160fa..4cdb3180 100644 --- a/Zennysoft.Game.Ma/src/enemy/enemy_types/09. Agni/AgniDemon.tscn +++ b/Zennysoft.Game.Ma/src/enemy/enemy_types/09. Agni/AgniDemon.tscn @@ -1,9 +1,11 @@ -[gd_scene load_steps=8 format=3 uid="uid://b8ewfgcjv60es"] +[gd_scene load_steps=11 format=3 uid="uid://b8ewfgcjv60es"] -[ext_resource type="Script" uid="uid://h6duv685n6eh" path="res://src/enemy/enemy_types/09. Agni/AgniDemon.cs" id="1_okj1v"] -[ext_resource type="Resource" uid="uid://2lflwab43lb0" path="res://src/enemy/enemy_types/09. Agni/AgniDemonStats.tres" id="2_eschf"] +[ext_resource type="Script" uid="uid://h6duv685n6eh" path="res://src/enemy/enemy_types/09. Agni/AgniDemon.cs" id="1_e2477"] [ext_resource type="PackedScene" uid="uid://bls3mcsyld4vy" path="res://src/enemy/enemy_types/09. Agni/AgniDemonModelView.tscn" id="3_tbkej"] -[ext_resource type="PackedScene" uid="uid://pbnsngx5jvrh" path="res://src/enemy/NavigationAgentClient.tscn" id="4_xj6b3"] +[ext_resource type="PackedScene" uid="uid://bxymnqkoi78oa" path="res://src/system/stats/HealthComponent.tscn" id="3_xj6b3"] +[ext_resource type="PackedScene" uid="uid://2nkvacxsd46b" path="res://src/enemy/behaviors/PatrolBehavior.tscn" id="4_dxxe5"] +[ext_resource type="PackedScene" uid="uid://mqj4jju3870v" path="res://src/enemy/behaviors/FollowBehavior.tscn" id="5_j6ob5"] +[ext_resource type="PackedScene" uid="uid://8bcme8ao4axa" path="res://src/enemy/behaviors/EngagePlayerBehavior.tscn" id="6_58r4a"] [sub_resource type="CapsuleShape3D" id="CapsuleShape3D_cwfph"] radius = 0.226425 @@ -16,16 +18,15 @@ radius = 1.0 [sub_resource type="SphereShape3D" id="SphereShape3D_8vcnq"] radius = 1.20703 +[sub_resource type="CylinderShape3D" id="CylinderShape3D_tbkej"] + [node name="Agni" type="CharacterBody3D"] process_mode = 1 collision_layer = 10 collision_mask = 11 axis_lock_linear_y = true axis_lock_angular_x = true -script = ExtResource("1_okj1v") -PrimaryAttackElementalType = 4 -SecondaryAttackElementalType = 4 -_enemyStatResource = ExtResource("2_eschf") +script = ExtResource("1_e2477") [node name="CollisionShape" type="CollisionShape3D" parent="."] unique_name_in_owner = true @@ -42,18 +43,9 @@ collision_mask = 2 transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0, -2) shape = SubResource("CylinderShape3D_jbgmx") -[node name="PatrolTimer" type="Timer" parent="."] -unique_name_in_owner = true -wait_time = 10.0 -autostart = true - -[node name="AttackTimer" type="Timer" parent="."] -unique_name_in_owner = true -wait_time = 0.8 -autostart = true - -[node name="Raycast" type="RayCast3D" parent="."] +[node name="Raycast" type="RayCast3D" parent="LineOfSight"] unique_name_in_owner = true +transform = Transform3D(-1, 0, 8.74228e-08, 0, 1, 0, -8.74228e-08, 0, -1, 0, 0, 0) target_position = Vector3(0, 0, -5) collision_mask = 3 @@ -67,5 +59,30 @@ shape = SubResource("SphereShape3D_8vcnq") [node name="EnemyModelView" parent="." instance=ExtResource("3_tbkej")] unique_name_in_owner = true -[node name="NavigationAgentClient" parent="." instance=ExtResource("4_xj6b3")] +[node name="PlayerDetector" type="Area3D" parent="."] unique_name_in_owner = true +collision_layer = 0 +collision_mask = 34 + +[node name="CollisionShape3D" type="CollisionShape3D" parent="PlayerDetector"] +shape = SubResource("CylinderShape3D_tbkej") + +[node name="Components" type="Node3D" parent="."] + +[node name="HealthComponent" parent="Components" instance=ExtResource("3_xj6b3")] + +[node name="PatrolBehavior" parent="Components" instance=ExtResource("4_dxxe5")] +unique_name_in_owner = true + +[node name="FollowBehavior" parent="Components" instance=ExtResource("5_j6ob5")] +unique_name_in_owner = true +_followSpeed = 150.0 + +[node name="EngagePlayerBehavior" parent="Components" instance=ExtResource("6_58r4a")] +unique_name_in_owner = true +_acquireTargetTime = 2.0 + +[node name="NavigationAgent" type="NavigationAgent3D" parent="Components"] +unique_name_in_owner = true +avoidance_enabled = true +radius = 1.0 diff --git a/Zennysoft.Game.Ma/src/enemy/enemy_types/10. Eden Pillar/EdenPillar.cs b/Zennysoft.Game.Ma/src/enemy/enemy_types/10. Eden Pillar/EdenPillar.cs index 0acb4bc0..57388c02 100644 --- a/Zennysoft.Game.Ma/src/enemy/enemy_types/10. Eden Pillar/EdenPillar.cs +++ b/Zennysoft.Game.Ma/src/enemy/enemy_types/10. Eden Pillar/EdenPillar.cs @@ -8,7 +8,7 @@ using Zennysoft.Ma.Adapter; namespace Zennysoft.Game.Ma; [Meta(typeof(IAutoNode))] -public partial class EdenPillar : Enemy, IHasPrimaryAttack, IHasSecondaryAttack, IHasTertiaryAttack +public partial class EdenPillar : Enemy3D, IHasPrimaryAttack, IHasSecondaryAttack, IHasTertiaryAttack { public override void _Notification(int what) => this.Notify(what); @@ -32,13 +32,7 @@ public partial class EdenPillar : Enemy, IHasPrimaryAttack, IHasSecondaryAttack, private float _targetAngle; - public override void _PhysicsProcess(double delta) - { - _enemyLogic.Input(new EnemyLogic.Input.PhysicsTick(delta)); - _enemyLogic.Input(new EnemyLogic.Input.StartAttacking()); - } - - public override void TakeAction() + public override void PerformAction() { var rng = new RandomNumberGenerator(); var options = new List() { PrimaryAttack, SecondaryAttack, TertiaryAttack }; diff --git a/Zennysoft.Game.Ma/src/enemy/enemy_types/11. Palan/Palan.cs b/Zennysoft.Game.Ma/src/enemy/enemy_types/11. Palan/Palan.cs index 4c08a344..8e116aa4 100644 --- a/Zennysoft.Game.Ma/src/enemy/enemy_types/11. Palan/Palan.cs +++ b/Zennysoft.Game.Ma/src/enemy/enemy_types/11. Palan/Palan.cs @@ -1,84 +1,32 @@ using Chickensoft.AutoInject; using Chickensoft.Introspection; using Godot; -using System.Collections.Generic; -using System; -using Zennysoft.Ma.Adapter; -using Zennysoft.Game.Abstractions; - -namespace Zennysoft.Game.Ma; +using Zennysoft.Game.Ma; [Meta(typeof(IAutoNode))] -public partial class Palan : FollowsPlayerEnemy, IHasPrimaryAttack, IHasSecondaryAttack, ICanPatrol +public partial class Palan : Enemy2D, IHavePatrolBehavior, IHaveEngagePlayerBehavior, IHaveFollowBehavior { public override void _Notification(int what) => this.Notify(what); - [Export] - public ElementType PrimaryAttackElementalType { get; set; } = ElementType.None; - [Export] - public double PrimaryAttackElementalDamageBonus { get; set; } = 1.0; - [Export] - public ElementType SecondaryAttackElementalType { get; set; } = ElementType.None; - [Export] - public double SecondaryAttackElementalDamageBonus { get; set; } = 1.0; + [Node] public NavigationAgent3D NavigationAgent { get; set; } + [Node] public PatrolBehavior PatrolBehavior { get; set; } = default!; + [Node] public FollowBehavior FollowBehavior { get; set; } = default!; + [Node] public EngagePlayerBehavior EngagePlayerBehavior { get; set; } = default!; - [Node] private INavigationAgentClient _navigationAgentClient { get; set; } = default!; + [Node] public Area3D PlayerDetector { get; set; } = default!; public void OnReady() { + FollowBehavior.Init(NavigationAgent); + PatrolBehavior.Init(NavigationAgent); + PatrolBehavior.HomePosition = GlobalPosition; + PatrolBehavior.OnVelocityComputed += OnVelocityComputed; + FollowBehavior.OnVelocityComputed += OnVelocityComputed; + EngagePlayerBehavior.TakeAction += EngagePlayerBehavior_TakeAction; + EngagePlayerBehavior.AcquireTarget += EngagePlayerBehavior_AcquireTarget; + _enemyLogic.Input(new EnemyLogic.Input.Patrol()); + PlayerDetector.BodyEntered += PlayerDetector_BodyEntered; + PlayerDetector.BodyExited += PlayerDetector_BodyExited; SetPhysicsProcess(true); - ((EnemyModelView2D)EnemyModelView).Hitbox.AreaEntered += Hitbox_AreaEntered; } - - public void OnPhysicsProcess(double delta) - { - _enemyLogic.Input(new EnemyLogic.Input.PhysicsTick(delta)); - FollowPlayerAndAttack(_enemyLogic, GlobalPosition, _player.CurrentPosition); - Velocity = _navigationAgentClient.Velocity; - base._PhysicsProcess(delta); - } - - public override void TakeAction() - { - var rng = new RandomNumberGenerator(); - var options = new List() { PrimaryAttack, SecondaryAttack }; - var selection = rng.RandWeighted([0.875f, 0.125f]); - options[(int)selection].Invoke(); - } - - public override void Die() - { - base.Die(); - } - - public void PrimaryAttack() - { - EnemyModelView.PlayPrimaryAttackAnimation(); - } - - public void SecondaryAttack() - { - EnemyModelView.PlaySecondaryAttackAnimation(); - } - - public override void SetTarget(Vector3 target) => _navigationAgentClient.SetTarget(target); - - public void Patrol() - { - var rng = new RandomNumberGenerator(); - rng.Randomize(); - var randomizedSpot = new Vector3(rng.RandfRange(-5.0f, 5.0f), 0, rng.RandfRange(-5.0f, 5.0f)); - _enemyLogic.Input(new EnemyLogic.Input.PatrolToRandomSpot(GlobalPosition + randomizedSpot)); - _enemyLogic.Input(new EnemyLogic.Input.StartPatrol()); - } - - private void Hitbox_AreaEntered(Area3D area) - { - var target = area.GetOwner(); - if (target is IPlayer player) - { - var damage = _enemyStatResource.CurrentAttack * PrimaryAttackElementalDamageBonus; - player.TakeDamage(damage, PrimaryAttackElementalType, BattleExtensions.IsCriticalHit(_enemyStatResource.Luck)); - } - } -} +} \ No newline at end of file diff --git a/Zennysoft.Game.Ma/src/enemy/enemy_types/11. Palan/Palan.tscn b/Zennysoft.Game.Ma/src/enemy/enemy_types/11. Palan/Palan.tscn index 8614d120..9d44e66a 100644 --- a/Zennysoft.Game.Ma/src/enemy/enemy_types/11. Palan/Palan.tscn +++ b/Zennysoft.Game.Ma/src/enemy/enemy_types/11. Palan/Palan.tscn @@ -1,9 +1,11 @@ -[gd_scene load_steps=8 format=3 uid="uid://boqjebx7yuiqy"] +[gd_scene load_steps=11 format=3 uid="uid://boqjebx7yuiqy"] [ext_resource type="Script" uid="uid://cjd7k1scp1am8" path="res://src/enemy/enemy_types/11. Palan/Palan.cs" id="1_2upgt"] -[ext_resource type="Resource" uid="uid://dc1i06laolear" path="res://src/enemy/enemy_types/11. Palan/PalanStats.tres" id="2_u6tof"] -[ext_resource type="PackedScene" uid="uid://pbnsngx5jvrh" path="res://src/enemy/NavigationAgentClient.tscn" id="3_c82i6"] +[ext_resource type="PackedScene" uid="uid://bxymnqkoi78oa" path="res://src/system/stats/HealthComponent.tscn" id="3_c82i6"] [ext_resource type="PackedScene" uid="uid://dxwwfbt2mtmer" path="res://src/enemy/enemy_types/11. Palan/PalanModelView.tscn" id="4_3ahu6"] +[ext_resource type="PackedScene" uid="uid://2nkvacxsd46b" path="res://src/enemy/behaviors/PatrolBehavior.tscn" id="4_3ogbp"] +[ext_resource type="PackedScene" uid="uid://mqj4jju3870v" path="res://src/enemy/behaviors/FollowBehavior.tscn" id="5_6scof"] +[ext_resource type="PackedScene" uid="uid://8bcme8ao4axa" path="res://src/enemy/behaviors/EngagePlayerBehavior.tscn" id="6_oy46w"] [sub_resource type="CapsuleShape3D" id="CapsuleShape3D_cwfph"] radius = 0.717471 @@ -16,6 +18,8 @@ radius = 1.0 [sub_resource type="SphereShape3D" id="SphereShape3D_8vcnq"] radius = 1.20703 +[sub_resource type="CylinderShape3D" id="CylinderShape3D_c82i6"] + [node name="Palan" type="CharacterBody3D"] process_mode = 1 collision_layer = 10 @@ -23,7 +27,6 @@ collision_mask = 11 axis_lock_linear_y = true axis_lock_angular_x = true script = ExtResource("1_2upgt") -_enemyStatResource = ExtResource("2_u6tof") [node name="CollisionShape" type="CollisionShape3D" parent="."] unique_name_in_owner = true @@ -40,18 +43,9 @@ collision_mask = 2 transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0, -2) shape = SubResource("CylinderShape3D_jbgmx") -[node name="PatrolTimer" type="Timer" parent="."] -unique_name_in_owner = true -wait_time = 10.0 -autostart = true - -[node name="AttackTimer" type="Timer" parent="."] -unique_name_in_owner = true -wait_time = 0.8 -autostart = true - -[node name="Raycast" type="RayCast3D" parent="."] +[node name="Raycast" type="RayCast3D" parent="LineOfSight"] unique_name_in_owner = true +transform = Transform3D(-1, 0, 8.74228e-08, 0, 1, 0, -8.74228e-08, 0, -1, 0, 0, 0) target_position = Vector3(0, 0, -5) collision_mask = 3 @@ -62,10 +56,33 @@ collision_mask = 0 [node name="CollisionShape3D" type="CollisionShape3D" parent="Collision"] shape = SubResource("SphereShape3D_8vcnq") -[node name="Navigation" type="Node3D" parent="."] - -[node name="NavigationAgentClient" parent="Navigation" instance=ExtResource("3_c82i6")] -unique_name_in_owner = true - [node name="EnemyModelView" parent="." instance=ExtResource("4_3ahu6")] unique_name_in_owner = true + +[node name="PlayerDetector" type="Area3D" parent="."] +unique_name_in_owner = true +collision_layer = 0 +collision_mask = 34 + +[node name="CollisionShape3D" type="CollisionShape3D" parent="PlayerDetector"] +shape = SubResource("CylinderShape3D_c82i6") + +[node name="Components" type="Node3D" parent="."] + +[node name="HealthComponent" parent="Components" instance=ExtResource("3_c82i6")] + +[node name="PatrolBehavior" parent="Components" instance=ExtResource("4_3ogbp")] +unique_name_in_owner = true + +[node name="FollowBehavior" parent="Components" instance=ExtResource("5_6scof")] +unique_name_in_owner = true +_followSpeed = 150.0 + +[node name="EngagePlayerBehavior" parent="Components" instance=ExtResource("6_oy46w")] +unique_name_in_owner = true +_acquireTargetTime = 2.0 + +[node name="NavigationAgent" type="NavigationAgent3D" parent="Components"] +unique_name_in_owner = true +avoidance_enabled = true +radius = 1.0 diff --git a/Zennysoft.Game.Ma/src/enemy/enemy_types/12. Shield of Heaven/ShieldModelView.tscn b/Zennysoft.Game.Ma/src/enemy/enemy_types/12. Shield of Heaven/ShieldModelView.tscn index 72314212..f5a95269 100644 --- a/Zennysoft.Game.Ma/src/enemy/enemy_types/12. Shield of Heaven/ShieldModelView.tscn +++ b/Zennysoft.Game.Ma/src/enemy/enemy_types/12. Shield of Heaven/ShieldModelView.tscn @@ -131,7 +131,7 @@ [ext_resource type="Texture2D" uid="uid://cky7x5xvv78hs" path="res://src/enemy/enemy_types/12. Shield of Heaven/animations/New/Idle Front/0109.png" id="92_wep0u"] [ext_resource type="Texture2D" uid="uid://dc56yupiyrfuo" path="res://src/enemy/enemy_types/12. Shield of Heaven/animations/New/Walk Back/0052.png" id="93_1lakb"] [ext_resource type="Texture2D" uid="uid://lklkc5xnj5c4" path="res://src/enemy/enemy_types/12. Shield of Heaven/animations/New/Idle Front/0112.png" id="93_ahxi6"] -[ext_resource type="Script" uid="uid://6edayafleq8y" path="res://src/hitbox/Hitbox.cs" id="94_32ire"] +[ext_resource type="Script" path="res://src/hitbox/Hitbox.cs" id="94_32ire"] [ext_resource type="Texture2D" uid="uid://0k5j8eklctd0" path="res://src/enemy/enemy_types/12. Shield of Heaven/animations/New/Idle Front/0115.png" id="94_ixnb4"] [ext_resource type="Texture2D" uid="uid://c5g8sn7rh164n" path="res://src/enemy/enemy_types/12. Shield of Heaven/animations/New/Walk Back/0055.png" id="94_qslmt"] [ext_resource type="Texture2D" uid="uid://v5gibgt1uaq5" path="res://src/enemy/enemy_types/12. Shield of Heaven/animations/New/Walk Back/0058.png" id="95_3na3d"] diff --git a/Zennysoft.Game.Ma/src/enemy/enemy_types/12. Shield of Heaven/ShieldOfHeaven.cs b/Zennysoft.Game.Ma/src/enemy/enemy_types/12. Shield of Heaven/ShieldOfHeaven.cs index c5e9224c..f96bc268 100644 --- a/Zennysoft.Game.Ma/src/enemy/enemy_types/12. Shield of Heaven/ShieldOfHeaven.cs +++ b/Zennysoft.Game.Ma/src/enemy/enemy_types/12. Shield of Heaven/ShieldOfHeaven.cs @@ -1,84 +1,32 @@ using Chickensoft.AutoInject; using Chickensoft.Introspection; using Godot; -using System.Collections.Generic; -using System; -using Zennysoft.Ma.Adapter; -using Zennysoft.Game.Abstractions; - -namespace Zennysoft.Game.Ma; +using Zennysoft.Game.Ma; [Meta(typeof(IAutoNode))] -public partial class ShieldOfHeaven : FollowsPlayerEnemy, IHasPrimaryAttack, IHasSecondaryAttack, ICanPatrol +public partial class ShieldOfHeaven : Enemy2D, IHavePatrolBehavior, IHaveEngagePlayerBehavior, IHaveFollowBehavior { public override void _Notification(int what) => this.Notify(what); - [Export] - public ElementType PrimaryAttackElementalType { get; set; } = ElementType.None; - [Export] - public double PrimaryAttackElementalDamageBonus { get; set; } = 1.0; - [Export] - public ElementType SecondaryAttackElementalType { get; set; } = ElementType.None; - [Export] - public double SecondaryAttackElementalDamageBonus { get; set; } = 1.0; + [Node] public NavigationAgent3D NavigationAgent { get; set; } + [Node] public PatrolBehavior PatrolBehavior { get; set; } = default!; + [Node] public FollowBehavior FollowBehavior { get; set; } = default!; + [Node] public EngagePlayerBehavior EngagePlayerBehavior { get; set; } = default!; - [Node] private INavigationAgentClient _navigationAgentClient { get; set; } = default!; + [Node] public Area3D PlayerDetector { get; set; } = default!; public void OnReady() { - SetPhysicsProcess(true); - ((EnemyModelView2D)EnemyModelView).Hitbox.AreaEntered += Hitbox_AreaEntered; - } - - public void OnPhysicsProcess(double delta) - { - _enemyLogic.Input(new EnemyLogic.Input.PhysicsTick(delta)); - FollowPlayerAndAttack(_enemyLogic, GlobalPosition, _player.CurrentPosition); - Velocity = _navigationAgentClient.Velocity; - base._PhysicsProcess(delta); - } - - public override void TakeAction() - { - var rng = new RandomNumberGenerator(); - var options = new List() { PrimaryAttack, SecondaryAttack }; - var selection = rng.RandWeighted([0.875f, 0.125f]); - options[(int)selection].Invoke(); - } - - public override void Die() - { - base.Die(); - } - - public void PrimaryAttack() - { - EnemyModelView.PlayPrimaryAttackAnimation(); - } - - public void SecondaryAttack() - { - EnemyModelView.PlaySecondaryAttackAnimation(); - } - - public override void SetTarget(Vector3 target) => _navigationAgentClient.SetTarget(target); - - public void Patrol() - { - var rng = new RandomNumberGenerator(); - rng.Randomize(); - var randomizedSpot = new Vector3(rng.RandfRange(-5.0f, 5.0f), 0, rng.RandfRange(-5.0f, 5.0f)); - _enemyLogic.Input(new EnemyLogic.Input.PatrolToRandomSpot(GlobalPosition + randomizedSpot)); - _enemyLogic.Input(new EnemyLogic.Input.StartPatrol()); - } - - private void Hitbox_AreaEntered(Area3D area) - { - var target = area.GetOwner(); - if (target is IPlayer player) - { - var damage = _enemyStatResource.CurrentAttack * PrimaryAttackElementalDamageBonus; - player.TakeDamage(damage, PrimaryAttackElementalType, BattleExtensions.IsCriticalHit(_enemyStatResource.Luck)); - } + FollowBehavior.Init(NavigationAgent); + PatrolBehavior.Init(NavigationAgent); + PatrolBehavior.HomePosition = GlobalPosition; + PatrolBehavior.OnVelocityComputed += OnVelocityComputed; + FollowBehavior.OnVelocityComputed += OnVelocityComputed; + EngagePlayerBehavior.TakeAction += EngagePlayerBehavior_TakeAction; + EngagePlayerBehavior.AcquireTarget += EngagePlayerBehavior_AcquireTarget; + _enemyLogic.Input(new EnemyLogic.Input.Patrol()); + PlayerDetector.BodyEntered += PlayerDetector_BodyEntered; + PlayerDetector.BodyExited += PlayerDetector_BodyExited; + SetPhysicsProcess(true); } } diff --git a/Zennysoft.Game.Ma/src/enemy/enemy_types/12. Shield of Heaven/ShieldOfHeaven.tscn b/Zennysoft.Game.Ma/src/enemy/enemy_types/12. Shield of Heaven/ShieldOfHeaven.tscn index 47d34390..9554c987 100644 --- a/Zennysoft.Game.Ma/src/enemy/enemy_types/12. Shield of Heaven/ShieldOfHeaven.tscn +++ b/Zennysoft.Game.Ma/src/enemy/enemy_types/12. Shield of Heaven/ShieldOfHeaven.tscn @@ -1,9 +1,11 @@ -[gd_scene load_steps=8 format=3 uid="uid://5s7c4dsb1wwk"] +[gd_scene load_steps=10 format=3 uid="uid://5s7c4dsb1wwk"] -[ext_resource type="Script" uid="uid://cjdivu0v1kfhy" path="res://src/enemy/enemy_types/12. Shield of Heaven/ShieldOfHeaven.cs" id="1_ips1f"] -[ext_resource type="Resource" uid="uid://c5fgcsruq5gx6" path="res://src/enemy/enemy_types/12. Shield of Heaven/ShieldOfHeavenStats.tres" id="2_oxa5b"] -[ext_resource type="PackedScene" uid="uid://pbnsngx5jvrh" path="res://src/enemy/NavigationAgentClient.tscn" id="3_d5a6t"] +[ext_resource type="Script" uid="uid://cjdivu0v1kfhy" path="res://src/enemy/enemy_types/12. Shield of Heaven/ShieldOfHeaven.cs" id="1_oxa5b"] +[ext_resource type="PackedScene" uid="uid://bxymnqkoi78oa" path="res://src/system/stats/HealthComponent.tscn" id="3_7w5bn"] [ext_resource type="PackedScene" uid="uid://drkaq6grim1fb" path="res://src/enemy/enemy_types/12. Shield of Heaven/ShieldModelView.tscn" id="3_r2swr"] +[ext_resource type="PackedScene" uid="uid://2nkvacxsd46b" path="res://src/enemy/behaviors/PatrolBehavior.tscn" id="4_jvpqg"] +[ext_resource type="PackedScene" uid="uid://mqj4jju3870v" path="res://src/enemy/behaviors/FollowBehavior.tscn" id="5_s5x4o"] +[ext_resource type="PackedScene" uid="uid://8bcme8ao4axa" path="res://src/enemy/behaviors/EngagePlayerBehavior.tscn" id="6_cacc5"] [sub_resource type="CapsuleShape3D" id="CapsuleShape3D_cwfph"] radius = 0.226425 @@ -22,17 +24,13 @@ collision_layer = 10 collision_mask = 11 axis_lock_linear_y = true axis_lock_angular_x = true -script = ExtResource("1_ips1f") -_enemyStatResource = ExtResource("2_oxa5b") +script = ExtResource("1_oxa5b") [node name="CollisionShape" type="CollisionShape3D" parent="."] unique_name_in_owner = true transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0, 0) shape = SubResource("CapsuleShape3D_cwfph") -[node name="NavigationAgentClient" parent="." instance=ExtResource("3_d5a6t")] -unique_name_in_owner = true - [node name="LineOfSight" type="Area3D" parent="."] unique_name_in_owner = true transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0, 0) @@ -43,18 +41,9 @@ collision_mask = 2 transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0, -2) shape = SubResource("CylinderShape3D_jbgmx") -[node name="PatrolTimer" type="Timer" parent="."] -unique_name_in_owner = true -wait_time = 10.0 -autostart = true - -[node name="AttackTimer" type="Timer" parent="."] -unique_name_in_owner = true -wait_time = 0.8 -autostart = true - -[node name="Raycast" type="RayCast3D" parent="."] +[node name="Raycast" type="RayCast3D" parent="LineOfSight"] unique_name_in_owner = true +transform = Transform3D(-1, 0, 8.74228e-08, 0, 1, 0, -8.74228e-08, 0, -1, 0, 0, 0) target_position = Vector3(0, 0, -5) collision_mask = 3 @@ -67,3 +56,23 @@ shape = SubResource("SphereShape3D_8vcnq") [node name="EnemyModelView" parent="." instance=ExtResource("3_r2swr")] unique_name_in_owner = true + +[node name="Components" type="Node3D" parent="."] + +[node name="HealthComponent" parent="Components" instance=ExtResource("3_7w5bn")] + +[node name="PatrolBehavior" parent="Components" instance=ExtResource("4_jvpqg")] +unique_name_in_owner = true + +[node name="FollowBehavior" parent="Components" instance=ExtResource("5_s5x4o")] +unique_name_in_owner = true +_followSpeed = 150.0 + +[node name="EngagePlayerBehavior" parent="Components" instance=ExtResource("6_cacc5")] +unique_name_in_owner = true +_acquireTargetTime = 2.0 + +[node name="NavigationAgent" type="NavigationAgent3D" parent="Components"] +unique_name_in_owner = true +avoidance_enabled = true +radius = 1.0 diff --git a/Zennysoft.Game.Ma/src/enemy/enemy_types/13. gold sproingy/GoldSproingy.cs b/Zennysoft.Game.Ma/src/enemy/enemy_types/13. gold sproingy/GoldSproingy.cs index 750d94ad..a76a707c 100644 --- a/Zennysoft.Game.Ma/src/enemy/enemy_types/13. gold sproingy/GoldSproingy.cs +++ b/Zennysoft.Game.Ma/src/enemy/enemy_types/13. gold sproingy/GoldSproingy.cs @@ -1,25 +1,26 @@ using Chickensoft.AutoInject; using Chickensoft.Introspection; - -namespace Zennysoft.Game.Ma; +using Godot; +using Zennysoft.Game.Ma; [Meta(typeof(IAutoNode))] -public partial class GoldSproingy : Enemy +public partial class GoldSproingy : Enemy2D, IHavePatrolBehavior { public override void _Notification(int what) => this.Notify(what); + [Node] public NavigationAgent3D NavigationAgent { get; set; } + [Node] public PatrolBehavior PatrolBehavior { get; set; } = default!; + + [Node] public Area3D PlayerDetector { get; set; } = default!; + public void OnReady() { + PatrolBehavior.Init(NavigationAgent); + PatrolBehavior.HomePosition = GlobalPosition; + PatrolBehavior.OnVelocityComputed += OnVelocityComputed; + _enemyLogic.Input(new EnemyLogic.Input.Patrol()); + PlayerDetector.BodyEntered += PlayerDetector_BodyEntered; + PlayerDetector.BodyExited += PlayerDetector_BodyExited; SetPhysicsProcess(true); } - - public void OnPhysicsProcess(double delta) - { - _enemyLogic.Input(new EnemyLogic.Input.PhysicsTick(delta)); - } - - public override void TakeAction() - { - // Run away - } } \ No newline at end of file diff --git a/Zennysoft.Game.Ma/src/enemy/enemy_types/13. gold sproingy/GoldSproingy.tscn b/Zennysoft.Game.Ma/src/enemy/enemy_types/13. gold sproingy/GoldSproingy.tscn index e0e4a1e8..9cd15a71 100644 --- a/Zennysoft.Game.Ma/src/enemy/enemy_types/13. gold sproingy/GoldSproingy.tscn +++ b/Zennysoft.Game.Ma/src/enemy/enemy_types/13. gold sproingy/GoldSproingy.tscn @@ -1,21 +1,23 @@ -[gd_scene load_steps=8 format=3 uid="uid://b3giib0jp3uod"] +[gd_scene load_steps=9 format=3 uid="uid://b3giib0jp3uod"] [ext_resource type="Script" uid="uid://jjulhqd5g3be" path="res://src/enemy/enemy_types/13. gold sproingy/GoldSproingy.cs" id="1_o1o4d"] -[ext_resource type="Resource" uid="uid://m8wl23q4kr4t" path="res://src/enemy/enemy_types/13. gold sproingy/GoldSproingyStats.tres" id="2_58d4o"] -[ext_resource type="PackedScene" uid="uid://pbnsngx5jvrh" path="res://src/enemy/NavigationAgentClient.tscn" id="3_u82b8"] +[ext_resource type="PackedScene" uid="uid://bxymnqkoi78oa" path="res://src/system/stats/HealthComponent.tscn" id="3_58d4o"] [ext_resource type="PackedScene" uid="uid://c5asojy73n44d" path="res://src/enemy/enemy_types/13. gold sproingy/GoldSproingyModelView.tscn" id="4_58d4o"] +[ext_resource type="PackedScene" uid="uid://2nkvacxsd46b" path="res://src/enemy/behaviors/PatrolBehavior.tscn" id="4_ik3p4"] [sub_resource type="CapsuleShape3D" id="CapsuleShape3D_cwfph"] radius = 0.106078 height = 1.23076 -[sub_resource type="SphereShape3D" id="SphereShape3D_8vcnq"] -radius = 0.57308 - [sub_resource type="CylinderShape3D" id="CylinderShape3D_jbgmx"] height = 5.0 radius = 1.0 +[sub_resource type="SphereShape3D" id="SphereShape3D_8vcnq"] +radius = 0.57308 + +[sub_resource type="CylinderShape3D" id="CylinderShape3D_58d4o"] + [node name="GoldSproingy" type="CharacterBody3D"] process_mode = 1 collision_layer = 10 @@ -24,18 +26,27 @@ axis_lock_linear_y = true axis_lock_angular_x = true axis_lock_angular_z = true script = ExtResource("1_o1o4d") -_enemyStatResource = ExtResource("2_58d4o") -_movementSpeed = 4.0 [node name="CollisionShape" type="CollisionShape3D" parent="."] unique_name_in_owner = true transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0, 0) shape = SubResource("CapsuleShape3D_cwfph") -[node name="Navigation" type="Node3D" parent="."] - -[node name="NavigationAgentClient" parent="Navigation" instance=ExtResource("3_u82b8")] +[node name="LineOfSight" type="Area3D" parent="."] unique_name_in_owner = true +transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0, 0) +collision_layer = 2 +collision_mask = 2 + +[node name="CollisionShape3D" type="CollisionShape3D" parent="LineOfSight"] +transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0, -2) +shape = SubResource("CylinderShape3D_jbgmx") + +[node name="Raycast" type="RayCast3D" parent="LineOfSight"] +unique_name_in_owner = true +transform = Transform3D(-1, 0, 8.74228e-08, 0, 1, 0, -8.74228e-08, 0, -1, 0, 0, 0) +target_position = Vector3(0, 0, -5) +collision_mask = 3 [node name="Collision" type="Node3D" parent="."] @@ -46,34 +57,27 @@ collision_mask = 0 [node name="CollisionShape3D" type="CollisionShape3D" parent="Collision/Collision"] shape = SubResource("SphereShape3D_8vcnq") -[node name="LineOfSight" type="Area3D" parent="Collision"] -unique_name_in_owner = true -transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0, 0) -collision_layer = 2 -collision_mask = 2 - -[node name="CollisionShape3D" type="CollisionShape3D" parent="Collision/LineOfSight"] -transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0, -2) -shape = SubResource("CylinderShape3D_jbgmx") - -[node name="Raycast" type="RayCast3D" parent="Collision"] -unique_name_in_owner = true -target_position = Vector3(0, 0, -5) -collision_mask = 3 - [node name="Visual" type="Node3D" parent="."] [node name="EnemyModelView" parent="Visual" instance=ExtResource("4_58d4o")] unique_name_in_owner = true -[node name="Timers" type="Node" parent="."] - -[node name="PatrolTimer" type="Timer" parent="Timers"] +[node name="PlayerDetector" type="Area3D" parent="."] unique_name_in_owner = true -wait_time = 10.0 -autostart = true +collision_layer = 0 +collision_mask = 34 -[node name="AttackTimer" type="Timer" parent="Timers"] +[node name="CollisionShape3D" type="CollisionShape3D" parent="PlayerDetector"] +shape = SubResource("CylinderShape3D_58d4o") + +[node name="Components" type="Node3D" parent="."] + +[node name="HealthComponent" parent="Components" instance=ExtResource("3_58d4o")] + +[node name="PatrolBehavior" parent="Components" instance=ExtResource("4_ik3p4")] unique_name_in_owner = true -wait_time = 0.8 -autostart = true + +[node name="NavigationAgent" type="NavigationAgent3D" parent="Components"] +unique_name_in_owner = true +avoidance_enabled = true +radius = 1.0 diff --git a/Zennysoft.Game.Ma/src/enemy/enemy_types/14. horse_head/HorseFace.tscn b/Zennysoft.Game.Ma/src/enemy/enemy_types/14. horse_head/HorseFace.tscn index 59012c40..d53105df 100644 --- a/Zennysoft.Game.Ma/src/enemy/enemy_types/14. horse_head/HorseFace.tscn +++ b/Zennysoft.Game.Ma/src/enemy/enemy_types/14. horse_head/HorseFace.tscn @@ -6,8 +6,6 @@ [sub_resource type="Resource" id="Resource_jl3qa"] script = ExtResource("2_jl3qa") -CurrentHP = 100.0 -MaximumHP = 100 CurrentAttack = 0 CurrentDefense = 0 MaxAttack = 0 @@ -37,10 +35,7 @@ axis_lock_angular_x = true axis_lock_angular_y = true motion_mode = 1 script = ExtResource("1_x21p4") -PrimaryAttackElementalType = null -PrimaryAttackElementalDamageBonus = null _enemyStatResource = SubResource("Resource_jl3qa") -_movementSpeed = null [node name="CollisionShape" type="CollisionShape3D" parent="."] unique_name_in_owner = true diff --git a/Zennysoft.Game.Ma/src/enemy/enemy_types/14. horse_head/HorseFaceModelView.tscn b/Zennysoft.Game.Ma/src/enemy/enemy_types/14. horse_head/HorseFaceModelView.tscn index be5b73de..5f91f770 100644 --- a/Zennysoft.Game.Ma/src/enemy/enemy_types/14. horse_head/HorseFaceModelView.tscn +++ b/Zennysoft.Game.Ma/src/enemy/enemy_types/14. horse_head/HorseFaceModelView.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=39 format=4 uid="uid://bid6f48l0q58o"] +[gd_scene load_steps=38 format=4 uid="uid://bid6f48l0q58o"] [ext_resource type="Script" uid="uid://bvcfww5827g74" path="res://src/enemy/enemy_types/BossTypeAEnemyModelView.cs" id="1_q3bfl"] [ext_resource type="Texture2D" uid="uid://2e4cp477ex0t" path="res://src/enemy/enemy_types/14. horse_head/animation/Horse Head 1_Metal054C_1K-JPG_Color.jpg" id="1_vv6g0"] @@ -6,7 +6,6 @@ [ext_resource type="Texture2D" uid="uid://dd7ocxanos2o7" path="res://src/enemy/enemy_types/14. horse_head/animation/HORSE-FACE 1_Metal054C_1K-JPG_Displacement.jpg" id="3_b3lw2"] [ext_resource type="Animation" uid="uid://ccq41qrm1lduk" path="res://src/enemy/enemy_types/14. horse_head/animation/walking2.res" id="3_bkc4x"] [ext_resource type="Texture2D" uid="uid://d3nsmrs41cpxs" path="res://src/enemy/enemy_types/14. horse_head/animation/Metal054C_1K-JPG_Metalness.jpg" id="4_58wyj"] -[ext_resource type="Script" uid="uid://6edayafleq8y" path="res://src/hitbox/Hitbox.cs" id="4_bkc4x"] [ext_resource type="Texture2D" uid="uid://dhk7u4r608cby" path="res://src/enemy/enemy_types/14. horse_head/animation/Metal054C_1K-JPG_NormalGL.jpg" id="5_qhoxi"] [ext_resource type="Texture2D" uid="uid://qp3uycxaljcs" path="res://src/enemy/enemy_types/14. horse_head/animation/Metal054C_1K-JPG_Roughness.jpg" id="6_lj3cb"] @@ -965,7 +964,7 @@ bones/0/name = "spine1" bones/0/parent = -1 bones/0/rest = Transform3D(1.49012e-06, 0.00846654, -0.999964, 2.93367e-08, 0.999964, 0.00846654, 1, -4.23752e-08, 1.49012e-06, 0.000155807, -0.00105953, -2.01735) bones/0/enabled = true -bones/0/position = Vector3(0.0996387, -0.350701, -1.53144) +bones/0/position = Vector3(0.0996386, -0.430121, -1.53144) bones/0/rotation = Quaternion(0.0256267, -0.805691, 0.0118477, 0.591662) bones/0/scale = Vector3(1, 1, 1) bones/1/name = "spine0" @@ -994,7 +993,7 @@ bones/4/parent = 3 bones/4/rest = Transform3D(0.901905, -0.410135, 0.135488, 0.412416, 0.910915, 0.0120912, -0.128377, 0.0449723, 0.990705, 2.5332e-07, 0.990515, -7.07805e-08) bones/4/enabled = true bones/4/position = Vector3(2.5332e-07, 0.990515, -7.07805e-08) -bones/4/rotation = Quaternion(0.00133629, 0.0635411, 0.1977, 0.9782) +bones/4/rotation = Quaternion(-0.00595614, 0.0593022, 0.184097, 0.981099) bones/4/scale = Vector3(1, 1, 1) bones/5/name = "neck4" bones/5/parent = 4 @@ -1008,7 +1007,7 @@ bones/6/parent = 5 bones/6/rest = Transform3D(0.0598389, 0.98531, 0.15995, -0.975271, 0.0235553, 0.219755, 0.212759, -0.169144, 0.962353, 3.65078e-07, 1.40318, 0) bones/6/enabled = true bones/6/position = Vector3(3.65078e-07, 1.40318, 0) -bones/6/rotation = Quaternion(-0.335877, 0.0514318, -0.474453, 0.812056) +bones/6/rotation = Quaternion(-0.327629, 0.0506387, -0.451418, 0.828442) bones/6/scale = Vector3(1, 1, 1) bones/7/name = "Bone.007" bones/7/parent = 6 @@ -1043,7 +1042,7 @@ bones/11/parent = 1 bones/11/rest = Transform3D(0.981457, 0.0769315, -0.175568, 0.18837, -0.217537, 0.957703, 0.035485, -0.973015, -0.227995, -1.09896e-07, 3.84743, -2.10479e-07) bones/11/enabled = true bones/11/position = Vector3(-1.09896e-07, 3.84743, -2.10479e-07) -bones/11/rotation = Quaternion(-0.809968, -0.0869372, -0.0236559, 0.579512) +bones/11/rotation = Quaternion(-0.814088, -0.0947363, -0.0238342, 0.572467) bones/11/scale = Vector3(1, 1, 1) bones/12/name = "arm2_L" bones/12/parent = 11 @@ -1070,7 +1069,7 @@ bones/15/name = "arm1_R" bones/15/parent = 1 bones/15/rest = Transform3D(-0.98213, 0.0512573, -0.181089, -0.187541, -0.185921, 0.964501, 0.0157694, 0.981227, 0.192212, 0.00107862, 3.8461, -0.0821097) bones/15/enabled = true -bones/15/position = Vector3(-0.187045, 3.5313, 0.0981292) +bones/15/position = Vector3(-0.169678, 3.39774, 0.123382) bones/15/rotation = Quaternion(-0.502686, 0.531044, 0.680821, -0.0422068) bones/15/scale = Vector3(1, 1, 1) bones/16/name = "arm2_R" @@ -1085,7 +1084,7 @@ bones/17/parent = 16 bones/17/rest = Transform3D(0.998789, 0.0488077, -0.00615137, -0.0491113, 0.996528, -0.0672226, 0.00284903, 0.0674433, 0.997719, -5.21541e-08, 3.04263, -1.31503e-06) bones/17/enabled = true bones/17/position = Vector3(-5.21541e-08, 3.04263, -1.31503e-06) -bones/17/rotation = Quaternion(-0.0284391, 0.0967671, 0.271047, 0.957268) +bones/17/rotation = Quaternion(-0.00888877, 0.0960696, 0.277786, 0.955786) bones/17/scale = Vector3(1, 1, 1) bones/18/name = "hand_R" bones/18/parent = 17 @@ -1098,7 +1097,7 @@ bones/19/name = "hip_L" bones/19/parent = -1 bones/19/rest = Transform3D(0.138486, 0.897208, 0.419333, -0.129033, -0.403458, 0.905854, 0.981923, -0.179556, 0.059896, 0.000155807, -0.00105953, -2.01735) bones/19/enabled = true -bones/19/position = Vector3(0.147751, -0.323457, -1.49267) +bones/19/position = Vector3(0.147751, -0.369418, -1.49267) bones/19/rotation = Quaternion(0.427793, 0.34021, 0.687061, -0.478745) bones/19/scale = Vector3(1, 1, 1) bones/20/name = "leg1_L" @@ -1106,14 +1105,14 @@ bones/20/parent = 19 bones/20/rest = Transform3D(0.945603, 0.113405, 0.304916, -0.324072, 0.410457, 0.852351, -0.0284943, -0.9048, 0.424881, 2.08616e-07, 2.00996, -7.1153e-07) bones/20/enabled = true bones/20/position = Vector3(2.08616e-07, 2.00996, -7.1153e-07) -bones/20/rotation = Quaternion(-0.435714, -0.327862, -0.372566, 0.750902) +bones/20/rotation = Quaternion(-0.433817, -0.330204, -0.37545, 0.749537) bones/20/scale = Vector3(1, 1, 1) bones/21/name = "leg2_L" bones/21/parent = 20 bones/21/rest = Transform3D(0.990336, -0.138679, 0.00180777, 0.138628, 0.990193, 0.0173138, -0.00419111, -0.0168959, 0.999848, 5.96046e-08, 5.85994, -5.23403e-07) bones/21/enabled = true bones/21/position = Vector3(5.96046e-08, 5.85994, -5.23403e-07) -bones/21/rotation = Quaternion(-0.0484256, 0.00188367, 0.38736, 0.920654) +bones/21/rotation = Quaternion(-0.0494072, 0.00187672, 0.395211, 0.917259) bones/21/scale = Vector3(1, 1, 1) bones/22/name = "foot1_L" bones/22/parent = 21 @@ -1147,7 +1146,7 @@ bones/26/name = "hip_R" bones/26/parent = -1 bones/26/rest = Transform3D(0.138486, -0.897208, -0.419333, 0.129033, -0.403458, 0.905854, -0.981923, -0.179556, 0.059896, -0.000155807, -0.00105953, -2.01735) bones/26/enabled = true -bones/26/position = Vector3(0.0289172, -0.325595, -1.59603) +bones/26/position = Vector3(0.0289172, -0.356236, -1.59603) bones/26/rotation = Quaternion(0.695067, -0.09936, -0.377924, -0.603475) bones/26/scale = Vector3(1, 1, 1) bones/27/name = "leg1_R" @@ -1155,14 +1154,14 @@ bones/27/parent = 26 bones/27/rest = Transform3D(0.945603, -0.113405, -0.304916, 0.324072, 0.410457, 0.852351, 0.0284943, -0.9048, 0.424881, -9.54606e-09, 2.00996, -3.52971e-07) bones/27/enabled = true bones/27/position = Vector3(-9.54606e-09, 2.00996, -3.52971e-07) -bones/27/rotation = Quaternion(-0.314391, 0.176313, 0.184131, 0.914422) +bones/27/rotation = Quaternion(-0.312056, 0.177992, 0.184279, 0.914867) bones/27/scale = Vector3(1, 1, 1) bones/28/name = "leg2_R" bones/28/parent = 27 bones/28/rest = Transform3D(0.990336, 0.138679, -0.00180777, -0.138628, 0.990193, 0.0173138, 0.00419111, -0.0168959, 0.999848, 4.51691e-08, 5.85994, -3.72529e-09) bones/28/enabled = true bones/28/position = Vector3(4.51691e-08, 5.85994, -3.72529e-09) -bones/28/rotation = Quaternion(-0.273011, 0.0201792, -0.178136, 0.945159) +bones/28/rotation = Quaternion(-0.278157, 0.0201334, -0.181494, 0.943018) bones/28/scale = Vector3(1, 1, 1) bones/29/name = "foot1_R" bones/29/parent = 28 @@ -1199,7 +1198,7 @@ mesh = SubResource("ArrayMesh_6e63x") skin = SubResource("Skin_yvw71") [node name="BoneAttachment3D" type="BoneAttachment3D" parent="Armature/Skeleton3D"] -transform = Transform3D(-0.370164, -0.13327, -0.919357, -0.279584, -0.927789, 0.247063, -0.885896, 0.348492, 0.306174, -2.00357, 8.75613, 6.23014) +transform = Transform3D(-0.370165, -0.13327, -0.919357, -0.302859, -0.918272, 0.255054, -0.878211, 0.372847, 0.29955, -2.00357, 8.72215, 6.32682) bone_name = "TOP OF SKULL" bone_idx = 8 @@ -1208,7 +1207,6 @@ unique_name_in_owner = true transform = Transform3D(1, 0, 0, 0, -4.37114e-08, -1, 0, 1, -4.37114e-08, 0, 0, 0) collision_layer = 16 collision_mask = 16 -script = ExtResource("4_bkc4x") [node name="CollisionShape3D" type="CollisionShape3D" parent="Hitbox"] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 10.428, 0) diff --git a/Zennysoft.Game.Ma/src/enemy/enemy_types/15. ox_face/OxFace.tscn b/Zennysoft.Game.Ma/src/enemy/enemy_types/15. ox_face/OxFace.tscn index 5f034839..cd6cf946 100644 --- a/Zennysoft.Game.Ma/src/enemy/enemy_types/15. ox_face/OxFace.tscn +++ b/Zennysoft.Game.Ma/src/enemy/enemy_types/15. ox_face/OxFace.tscn @@ -6,8 +6,6 @@ [sub_resource type="Resource" id="Resource_j7u30"] script = ExtResource("2_j7u30") -CurrentHP = 100.0 -MaximumHP = 100 CurrentAttack = 0 CurrentDefense = 0 MaxAttack = 0 @@ -51,50 +49,49 @@ shape = SubResource("CapsuleShape3D_7uhtm") unique_name_in_owner = true [node name="Skeleton3D" parent="EnemyModelView/Armature" index="0"] -bones/0/position = Vector3(-0.259743, -0.993026, -1.9718) +bones/0/position = Vector3(-0.259338, -0.946665, -1.97492) bones/0/rotation = Quaternion(0.0915277, -0.692111, -0.0341586, 0.715149) bones/1/rotation = Quaternion(0.0828172, 0.0642671, -0.39627, 0.91213) bones/2/rotation = Quaternion(-0.137837, 0.137086, 0.403643, 0.894025) bones/3/rotation = Quaternion(-0.00338816, 0.00852271, 0.0152662, 0.999842) bones/4/rotation = Quaternion(0.037164, 0.133882, 0.101977, 0.985036) bones/5/rotation = Quaternion(-0.0397875, -0.0104688, 0.0235613, 0.998875) -bones/6/rotation = Quaternion(-0.0605251, -0.298986, -0.744535, 0.593812) +bones/6/rotation = Quaternion(-0.076622, -0.304817, -0.744794, 0.58864) bones/7/rotation = Quaternion(0.0788712, -0.0306685, -0.220772, 0.971647) bones/8/rotation = Quaternion(-0.127286, 0.0273856, -0.425308, 0.895635) bones/9/rotation = Quaternion(-0.0931654, 0.0493592, -0.752794, 0.649757) bones/10/rotation = Quaternion(0.0429966, 0.0102923, 0.363547, 0.930526) -bones/11/rotation = Quaternion(-0.783022, -0.0601473, 0.0752645, 0.614487) +bones/11/rotation = Quaternion(-0.785686, -0.0625038, 0.0698825, 0.61148) bones/12/rotation = Quaternion(-0.607818, -0.670503, -0.284916, 0.31592) bones/13/rotation = Quaternion(-0.255941, 0.586097, -0.127235, 0.758153) bones/14/rotation = Quaternion(-0.513517, -0.227335, -0.228787, 0.795157) -bones/15/rotation = Quaternion(-0.212267, 0.740375, 0.61896, -0.153871) +bones/15/rotation = Quaternion(-0.20973, 0.736399, 0.623199, -0.159227) bones/16/rotation = Quaternion(-0.486067, -0.16412, -0.362283, 0.778174) bones/17/rotation = Quaternion(-0.0553629, -0.0361614, 0.62832, 0.77514) bones/18/rotation = Quaternion(-0.119289, 0.0998131, -0.0173011, 0.987678) -bones/19/position = Vector3(-0.332546, -1.15404, -1.87769) -bones/19/rotation = Quaternion(0.617734, 0.305017, 0.56153, -0.458315) -bones/20/rotation = Quaternion(-0.31739, -0.434429, -0.283374, 0.793873) -bones/21/rotation = Quaternion(-0.0603083, 0.00129965, 0.488199, 0.870645) +bones/19/position = Vector3(-0.295208, -1.11872, -2.00073) +bones/19/rotation = Quaternion(0.60979, 0.314261, 0.573867, -0.447296) +bones/20/rotation = Quaternion(-0.309203, -0.443293, -0.269813, 0.796923) +bones/21/rotation = Quaternion(-0.0601099, 0.00130101, 0.486592, 0.871558) bones/22/rotation = Quaternion(0.156218, 0.0483037, -0.624744, 0.763516) bones/23/rotation = Quaternion(0.123936, -0.00678731, -0.347765, 0.92933) bones/24/rotation = Quaternion(0.427621, 0.561851, 0.530083, 0.469549) bones/25/position = Vector3(4.82744, -12.3397, 0.183847) bones/25/rotation = Quaternion(-0.400051, 0.463947, -0.598439, 0.516317) -bones/26/position = Vector3(-0.165895, -1.11395, -2.0182) +bones/26/position = Vector3(-0.275647, -1.11395, -2.01745) bones/26/rotation = Quaternion(0.608697, -0.3155, -0.575514, -0.445793) -bones/27/rotation = Quaternion(-0.205675, 0.422771, 0.140472, 0.871338) -bones/28/rotation = Quaternion(-0.063614, -0.00115913, -0.507889, 0.85907) +bones/27/rotation = Quaternion(-0.208716, 0.421093, 0.142569, 0.871087) +bones/28/rotation = Quaternion(-0.0642794, -0.00115481, -0.513201, 0.855857) bones/29/rotation = Quaternion(0.150998, -0.0515735, 0.668372, 0.726511) bones/31/position = Vector3(-7.29038, -6.72226, -0.133983) bones/31/rotation = Quaternion(-0.453784, 0.542292, 0.542291, -0.453784) bones/32/rotation = Quaternion(0.456756, 0.539878, -0.539587, -0.456893) [node name="BoneAttachment3D" parent="EnemyModelView/Armature/Skeleton3D" index="0"] -transform = Transform3D(-0.28122, -0.0593245, -0.957808, -0.331584, -0.930606, 0.154995, -0.900537, 0.361182, 0.242033, -1.67603, 8.26688, 4.95147) +transform = Transform3D(-0.29961, -0.088055, -0.949989, -0.328337, -0.925392, 0.189326, -0.895783, 0.368641, 0.248345, -1.65943, 8.32036, 4.94649) -[node name="OmniLight3D" parent="EnemyModelView/Armature/Skeleton3D/BoneAttachment3D" index="0"] -light_energy = 0.0 -light_indirect_energy = 0.0 +[node name="Hitbox" parent="EnemyModelView" index="3"] +script = null [node name="AttackTimer" type="Timer" parent="."] unique_name_in_owner = true diff --git a/Zennysoft.Game.Ma/src/enemy/enemy_types/15. ox_face/OxFaceModelView.tscn b/Zennysoft.Game.Ma/src/enemy/enemy_types/15. ox_face/OxFaceModelView.tscn index bc7269e7..635e3936 100644 --- a/Zennysoft.Game.Ma/src/enemy/enemy_types/15. ox_face/OxFaceModelView.tscn +++ b/Zennysoft.Game.Ma/src/enemy/enemy_types/15. ox_face/OxFaceModelView.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=31 format=4 uid="uid://dnomfbym36ivg"] +[gd_scene load_steps=30 format=4 uid="uid://dnomfbym36ivg"] [ext_resource type="Script" uid="uid://ckv5dmrw6pvn6" path="res://src/enemy/EnemyModelView3D.cs" id="1_6miqu"] [ext_resource type="Script" uid="uid://bvcfww5827g74" path="res://src/enemy/enemy_types/BossTypeAEnemyModelView.cs" id="1_f2iok"] @@ -6,7 +6,6 @@ [ext_resource type="AnimationLibrary" uid="uid://dn4501qsypsu" path="res://src/enemy/enemy_types/14. horse_head/animation/OxFaceAnimations.tres" id="3_pmgg3"] [ext_resource type="Texture2D" uid="uid://d3nsmrs41cpxs" path="res://src/enemy/enemy_types/14. horse_head/animation/Metal054C_1K-JPG_Metalness.jpg" id="4_q73y1"] [ext_resource type="Texture2D" uid="uid://dhk7u4r608cby" path="res://src/enemy/enemy_types/14. horse_head/animation/Metal054C_1K-JPG_NormalGL.jpg" id="5_desgq"] -[ext_resource type="Script" uid="uid://6edayafleq8y" path="res://src/hitbox/Hitbox.cs" id="5_f2iok"] [ext_resource type="Texture2D" uid="uid://qp3uycxaljcs" path="res://src/enemy/enemy_types/14. horse_head/animation/Metal054C_1K-JPG_Roughness.jpg" id="6_1ch7e"] [sub_resource type="SphereMesh" id="SphereMesh_v4mpe"] @@ -216,7 +215,7 @@ bones/0/name = "spine1" bones/0/parent = -1 bones/0/rest = Transform3D(1.49012e-06, 0.00846654, -0.999964, 2.93367e-08, 0.999964, 0.00846654, 1, -4.23752e-08, 1.49012e-06, 0.000155807, -0.00105953, -2.01735) bones/0/enabled = true -bones/0/position = Vector3(-0.259798, -0.999285, -1.97138) +bones/0/position = Vector3(-0.260226, -1.04815, -1.96808) bones/0/rotation = Quaternion(0.0915277, -0.692111, -0.0341586, 0.715149) bones/0/scale = Vector3(1, 1, 1) bones/1/name = "spine0" @@ -259,7 +258,7 @@ bones/6/parent = 5 bones/6/rest = Transform3D(0.0598389, 0.98531, 0.15995, -0.975271, 0.0235553, 0.219755, 0.212759, -0.169144, 0.962353, 3.65078e-07, 1.40318, 0) bones/6/enabled = true bones/6/position = Vector3(3.65078e-07, 1.40318, 0) -bones/6/rotation = Quaternion(-0.0661862, -0.301047, -0.744653, 0.592016) +bones/6/rotation = Quaternion(-0.0474983, -0.294201, -0.744151, 0.597854) bones/6/scale = Vector3(1, 1, 1) bones/7/name = "Bone.007" bones/7/parent = 6 @@ -294,7 +293,7 @@ bones/11/parent = 1 bones/11/rest = Transform3D(0.981457, 0.0769315, -0.175568, 0.18837, -0.217537, 0.957703, 0.035485, -0.973015, -0.227995, -1.09896e-07, 3.84743, -2.10479e-07) bones/11/enabled = true bones/11/position = Vector3(-1.09896e-07, 3.84743, -2.10479e-07) -bones/11/rotation = Quaternion(-0.782654, -0.0598241, 0.0760018, 0.614897) +bones/11/rotation = Quaternion(-0.779862, -0.0573929, 0.0815417, 0.617959) bones/11/scale = Vector3(1, 0.999999, 1) bones/12/name = "arm2_L" bones/12/parent = 11 @@ -322,7 +321,7 @@ bones/15/parent = 1 bones/15/rest = Transform3D(-0.98213, 0.0512573, -0.181089, -0.187541, -0.185921, 0.964501, 0.0157694, 0.981227, 0.192212, 0.00107862, 3.8461, -0.0821097) bones/15/enabled = true bones/15/position = Vector3(0.00107886, 3.8461, -0.0821095) -bones/15/rotation = Quaternion(-0.21261, 0.740907, 0.618384, -0.153147) +bones/15/rotation = Quaternion(-0.215266, 0.745035, 0.613865, -0.14749) bones/15/scale = Vector3(1, 1, 1) bones/16/name = "arm2_R" bones/16/parent = 15 @@ -349,22 +348,22 @@ bones/19/name = "hip_L" bones/19/parent = -1 bones/19/rest = Transform3D(0.138486, 0.897208, 0.419333, -0.129033, -0.403458, 0.905854, 0.981923, -0.179556, 0.059896, 0.000155807, -0.00105953, -2.01735) bones/19/enabled = true -bones/19/position = Vector3(-0.337589, -1.15882, -1.86109) -bones/19/rotation = Quaternion(0.618788, 0.303759, 0.559846, -0.459788) +bones/19/position = Vector3(-0.376941, -1.19604, -1.73142) +bones/19/rotation = Quaternion(0.626841, 0.29386, 0.546553, -0.471165) bones/19/scale = Vector3(1, 1, 1) bones/20/name = "leg1_L" bones/20/parent = 19 bones/20/rest = Transform3D(0.945603, 0.113405, 0.304916, -0.324072, 0.410457, 0.852351, -0.0284943, -0.9048, 0.424881, 2.08616e-07, 2.00996, -7.1153e-07) bones/20/enabled = true bones/20/position = Vector3(2.08616e-07, 2.00996, -7.1153e-07) -bones/20/rotation = Quaternion(-0.318485, -0.433221, -0.285183, 0.793446) +bones/20/rotation = Quaternion(-0.326944, -0.42372, -0.299222, 0.789959) bones/20/scale = Vector3(1, 0.999999, 1) bones/21/name = "leg2_L" bones/21/parent = 20 bones/21/rest = Transform3D(0.990336, -0.138679, 0.00180777, 0.138628, 0.990193, 0.0173138, -0.00419111, -0.0168959, 0.999848, 5.96046e-08, 5.85994, -5.23403e-07) bones/21/enabled = true bones/21/position = Vector3(5.96046e-08, 5.85994, -5.23403e-07) -bones/21/rotation = Quaternion(-0.0603367, 0.00129945, 0.488427, 0.870515) +bones/21/rotation = Quaternion(-0.0604797, 0.00129848, 0.489585, 0.869855) bones/21/scale = Vector3(1, 1, 1) bones/22/name = "foot1_L" bones/22/parent = 21 @@ -398,7 +397,7 @@ bones/26/name = "hip_R" bones/26/parent = -1 bones/26/rest = Transform3D(0.138486, -0.897208, -0.419333, 0.129033, -0.403458, 0.905854, -0.981923, -0.179556, 0.059896, -0.000155807, -0.00105953, -2.01735) bones/26/enabled = true -bones/26/position = Vector3(-0.151076, -1.11395, -2.0183) +bones/26/position = Vector3(-0.0354083, -1.11395, -2.01909) bones/26/rotation = Quaternion(0.608697, -0.3155, -0.575514, -0.445793) bones/26/scale = Vector3(1, 1, 1) bones/27/name = "leg1_R" @@ -406,14 +405,14 @@ bones/27/parent = 26 bones/27/rest = Transform3D(0.945603, -0.113405, -0.304916, 0.324072, 0.410457, 0.852351, 0.0284943, -0.9048, 0.424881, -9.54606e-09, 2.00996, -3.52971e-07) bones/27/enabled = true bones/27/position = Vector3(-9.54606e-09, 2.00996, -3.52971e-07) -bones/27/rotation = Quaternion(-0.205251, 0.423005, 0.14017, 0.871373) +bones/27/rotation = Quaternion(-0.202394, 0.424587, 0.138062, 0.871609) bones/27/scale = Vector3(1, 0.999999, 1) bones/28/name = "leg2_R" bones/28/parent = 27 bones/28/rest = Transform3D(0.990336, 0.138679, -0.00180777, -0.138628, 0.990193, 0.0173138, 0.00419111, -0.0168959, 0.999848, 4.51691e-08, 5.85994, -3.72529e-09) bones/28/enabled = true bones/28/position = Vector3(4.51691e-08, 5.85994, -3.72529e-09) -bones/28/rotation = Quaternion(-0.06352, -0.00115973, -0.507138, 0.85952) +bones/28/rotation = Quaternion(-0.0628067, -0.0011643, -0.501442, 0.862908) bones/28/scale = Vector3(1, 1, 1) bones/29/name = "foot1_R" bones/29/parent = 28 @@ -445,7 +444,7 @@ bones/32/rotation = Quaternion(0.456756, 0.539878, -0.539587, -0.456893) bones/32/scale = Vector3(1, 1, 1) [node name="BoneAttachment3D" type="BoneAttachment3D" parent="Armature/Skeleton3D"] -transform = Transform3D(-0.287701, -0.069451, -0.955199, -0.330519, -0.928891, 0.167089, -0.898879, 0.363783, 0.244287, -1.67042, 8.26319, 4.95126) +transform = Transform3D(-0.266252, -0.0359368, -0.963233, -0.333724, -0.934064, 0.127095, -0.904288, 0.355294, 0.236703, -1.68944, 8.20558, 4.95656) bone_name = "TOP OF SKULL" bone_idx = 8 @@ -484,7 +483,6 @@ unique_name_in_owner = true transform = Transform3D(1, 0, 0, 0, -4.37114e-08, -1, 0, 1, -4.37114e-08, 0, 0, 0) collision_layer = 16 collision_mask = 16 -script = ExtResource("5_f2iok") [node name="CollisionShape3D" type="CollisionShape3D" parent="Hitbox"] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.300452, 11.5388, 0.257962) diff --git a/Zennysoft.Game.Ma/src/enemy/enemy_types/16. demon wall/DemonWall.cs b/Zennysoft.Game.Ma/src/enemy/enemy_types/16. demon wall/DemonWall.cs index d386abeb..5ae7e867 100644 --- a/Zennysoft.Game.Ma/src/enemy/enemy_types/16. demon wall/DemonWall.cs +++ b/Zennysoft.Game.Ma/src/enemy/enemy_types/16. demon wall/DemonWall.cs @@ -1,15 +1,15 @@ using Chickensoft.AutoInject; -using Chickensoft.Collections; using Chickensoft.Introspection; using Godot; using System; +using System.Collections.Immutable; using System.Linq; using Zennysoft.Ma.Adapter; namespace Zennysoft.Game.Ma; [Meta(typeof(IAutoNode))] -public partial class DemonWall : CharacterBody3D, IEnemy +public partial class DemonWall : CharacterBody3D { public override void _Notification(int what) => this.Notify(what); @@ -19,88 +19,44 @@ public partial class DemonWall : CharacterBody3D, IEnemy [Node] public DemonWallModelView EnemyModelView { get; set; } = default!; - public AutoProp CurrentHP { get; private set; } + public float ThinkTime { get; } [Export] private double _maximumWallMoveAmount = 24; private Timer _attackTimer; - private IDamageCalculator _damageCalculator; - public void OnReady() { - CurrentHP = new AutoProp(_enemyStatResource.MaximumHP); _attackTimer = new Timer { WaitTime = 5f }; _attackTimer.Timeout += AttackTimer_Timeout; AddChild(_attackTimer); - _damageCalculator = new DamageCalculator(); - CurrentHP.Sync += CurrentHP_Sync; - } - - private void CurrentHP_Sync(double newHP) - { - if (newHP <= 0) - Die(); } public void Activate() { EnemyModelView.PlayActivateAnimation(); SetPhysicsProcess(true); - StartAttackTimer(); } private void AttackTimer_Timeout() { - TakeAction(); + PerformAction(); } - public void TakeAction() + public void PerformAction() { EnemyModelView.Attack(_maximumWallMoveAmount); } - public void Move(Vector3 velocity) => throw new System.NotImplementedException(); - - public void TakeDamage(double damage, ElementType elementType = ElementType.None, bool isCriticalHit = false, bool ignoreDefense = false, bool ignoreElementalResistance = false) + public void TakeDamage(int damage) { - var damageTaken = _damageCalculator.CalculateDamage( - damage, - elementType, - _enemyStatResource.CurrentDefense, - _enemyStatResource.ElementalResistance, - isCriticalHit, - ignoreDefense, - ignoreElementalResistance); - - CurrentHP.OnNext(CurrentHP.Value - damageTaken); - GD.Print($"Demon Wall HP Left: {CurrentHP.Value}"); EnemyModelView.PlayHitAnimation(); } - public void Idle() - { - throw new NotImplementedException(); - } - - public void Move() - { - throw new NotImplementedException(); - } - - public void Knockback(float impulse, Vector3 direction) => throw new NotImplementedException(); - public void SetCurrentHP(int newHP) => throw new NotImplementedException(); - public int GetMaximumHP() => _enemyStatResource.MaximumHP; - public void StartAttackTimer() => _attackTimer.Start(); - public void StopAttackTimer() => _attackTimer.Stop(); - public void SetTarget(Vector3 target) => throw new NotImplementedException(); - public void SetEnemyGlobalPosition(Vector3 target) => throw new NotImplementedException(); - public Vector3 GetEnemyGlobalPosition() => throw new NotImplementedException(); - public IDungeonRoom GetCurrentRoom() => throw new NotImplementedException(); + public IDungeonRoom GetCurrentRoom(ImmutableList roomList) => throw new NotImplementedException(); public async void Die() { - CurrentHP.OnCompleted(); EnemyModelView.PlayDeathAnimation(); await ToSignal(GetTree().CreateTimer(0.8f), "timeout"); CallDeferred(MethodName.QueueFree); diff --git a/Zennysoft.Game.Ma/src/enemy/enemy_types/16. demon wall/DemonWallArm.cs b/Zennysoft.Game.Ma/src/enemy/enemy_types/16. demon wall/DemonWallArm.cs index 269ad19c..9bb7d498 100644 --- a/Zennysoft.Game.Ma/src/enemy/enemy_types/16. demon wall/DemonWallArm.cs +++ b/Zennysoft.Game.Ma/src/enemy/enemy_types/16. demon wall/DemonWallArm.cs @@ -33,13 +33,13 @@ public partial class DemonWallArm : Node3D public void OnReady() { - Hitbox.AreaEntered += Hitbox_AreaEntered; + Hitbox.AreaEntered += Hitbox_AreaEntered; } private void Hitbox_AreaEntered(Area3D area) { - var target = area.GetOwner(); - if (target is IPlayer player) - player.TakeDamage(Damage * PrimaryAttackElementalDamageBonus, PrimaryAttackElementalType, false); + var target = area.GetOwner(); + if (target is IPlayer player) + player.TakeDamage(new Damage((int)(Damage * PrimaryAttackElementalDamageBonus), PrimaryAttackElementalType, false, false, false)); } } diff --git a/Zennysoft.Game.Ma/src/enemy/enemy_types/9b. Aqueos Demon/AqueosDemon.cs b/Zennysoft.Game.Ma/src/enemy/enemy_types/9b. Aqueos Demon/AqueosDemon.cs new file mode 100644 index 00000000..3c312226 --- /dev/null +++ b/Zennysoft.Game.Ma/src/enemy/enemy_types/9b. Aqueos Demon/AqueosDemon.cs @@ -0,0 +1,32 @@ +using Chickensoft.AutoInject; +using Chickensoft.Introspection; +using Godot; +using Zennysoft.Game.Ma; + +[Meta(typeof(IAutoNode))] +public partial class AqueousDemon : Enemy2D, IHavePatrolBehavior, IHaveEngagePlayerBehavior, IHaveFollowBehavior +{ + public override void _Notification(int what) => this.Notify(what); + + [Node] public NavigationAgent3D NavigationAgent { get; set; } + [Node] public PatrolBehavior PatrolBehavior { get; set; } = default!; + [Node] public FollowBehavior FollowBehavior { get; set; } = default!; + [Node] public EngagePlayerBehavior EngagePlayerBehavior { get; set; } = default!; + + [Node] public Area3D PlayerDetector { get; set; } = default!; + + public void OnReady() + { + FollowBehavior.Init(NavigationAgent); + PatrolBehavior.Init(NavigationAgent); + PatrolBehavior.HomePosition = GlobalPosition; + PatrolBehavior.OnVelocityComputed += OnVelocityComputed; + FollowBehavior.OnVelocityComputed += OnVelocityComputed; + EngagePlayerBehavior.TakeAction += EngagePlayerBehavior_TakeAction; + EngagePlayerBehavior.AcquireTarget += EngagePlayerBehavior_AcquireTarget; + _enemyLogic.Input(new EnemyLogic.Input.Patrol()); + PlayerDetector.BodyEntered += PlayerDetector_BodyEntered; + PlayerDetector.BodyExited += PlayerDetector_BodyExited; + SetPhysicsProcess(true); + } +} \ No newline at end of file diff --git a/Zennysoft.Game.Ma/src/enemy/enemy_types/9b. Aqueos Demon/AqueosDemon.cs.uid b/Zennysoft.Game.Ma/src/enemy/enemy_types/9b. Aqueos Demon/AqueosDemon.cs.uid new file mode 100644 index 00000000..f76ac3d7 --- /dev/null +++ b/Zennysoft.Game.Ma/src/enemy/enemy_types/9b. Aqueos Demon/AqueosDemon.cs.uid @@ -0,0 +1 @@ +uid://8f4alhh2ubvg diff --git a/Zennysoft.Game.Ma/src/enemy/enemy_types/9b. Aqueos Demon/AqueosDemon.tscn b/Zennysoft.Game.Ma/src/enemy/enemy_types/9b. Aqueos Demon/AqueosDemon.tscn index 75be7642..450a3cda 100644 --- a/Zennysoft.Game.Ma/src/enemy/enemy_types/9b. Aqueos Demon/AqueosDemon.tscn +++ b/Zennysoft.Game.Ma/src/enemy/enemy_types/9b. Aqueos Demon/AqueosDemon.tscn @@ -1,27 +1,11 @@ -[gd_scene load_steps=9 format=3 uid="uid://dpq17ej06uah1"] +[gd_scene load_steps=11 format=3 uid="uid://dpq17ej06uah1"] -[ext_resource type="Script" uid="uid://h6duv685n6eh" path="res://src/enemy/enemy_types/09. Agni/AgniDemon.cs" id="1_wbopj"] +[ext_resource type="Script" uid="uid://8f4alhh2ubvg" path="res://src/enemy/enemy_types/9b. Aqueos Demon/AqueosDemon.cs" id="1_wtipe"] [ext_resource type="PackedScene" uid="uid://cu7n814hhtjwm" path="res://src/enemy/enemy_types/9b. Aqueos Demon/AqueosModelView.tscn" id="2_0hbxv"] -[ext_resource type="Script" uid="uid://dnkmr0eq1sij0" path="res://src/enemy/EnemyStatResource.cs" id="2_wtipe"] -[ext_resource type="PackedScene" uid="uid://pbnsngx5jvrh" path="res://src/enemy/NavigationAgentClient.tscn" id="4_m7ocm"] - -[sub_resource type="Resource" id="Resource_m7ocm"] -script = ExtResource("2_wtipe") -CurrentHP = 60.0 -MaximumHP = 60 -CurrentAttack = 10 -CurrentDefense = 5 -MaxAttack = 10 -MaxDefense = 5 -ExpFromDefeat = null -Luck = 0.05 -_telluricResistance = null -_aeolicResistance = null -_hydricResistance = 0.5 -_igneousResistance = null -_ferrumResistance = null -DropsSoulGemChance = null -metadata/_custom_type_script = "uid://dnkmr0eq1sij0" +[ext_resource type="PackedScene" uid="uid://bxymnqkoi78oa" path="res://src/system/stats/HealthComponent.tscn" id="3_gxowl"] +[ext_resource type="PackedScene" uid="uid://2nkvacxsd46b" path="res://src/enemy/behaviors/PatrolBehavior.tscn" id="4_5pbfd"] +[ext_resource type="PackedScene" uid="uid://mqj4jju3870v" path="res://src/enemy/behaviors/FollowBehavior.tscn" id="5_ha827"] +[ext_resource type="PackedScene" uid="uid://8bcme8ao4axa" path="res://src/enemy/behaviors/EngagePlayerBehavior.tscn" id="6_7afhy"] [sub_resource type="CapsuleShape3D" id="CapsuleShape3D_cwfph"] radius = 0.226425 @@ -34,19 +18,15 @@ radius = 1.0 [sub_resource type="SphereShape3D" id="SphereShape3D_8vcnq"] radius = 1.20703 +[sub_resource type="CylinderShape3D" id="CylinderShape3D_gxowl"] + [node name="AqueousDemon" type="CharacterBody3D"] process_mode = 1 collision_layer = 10 collision_mask = 11 axis_lock_linear_y = true axis_lock_angular_x = true -script = ExtResource("1_wbopj") -PrimaryAttackElementalType = 3 -PrimaryAttackElementalDamageBonus = null -SecondaryAttackElementalType = 3 -SecondaryAttackElementalDamageBonus = null -_enemyStatResource = SubResource("Resource_m7ocm") -_movementSpeed = null +script = ExtResource("1_wtipe") [node name="CollisionShape" type="CollisionShape3D" parent="."] unique_name_in_owner = true @@ -63,18 +43,9 @@ collision_mask = 2 transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0, -2) shape = SubResource("CylinderShape3D_jbgmx") -[node name="PatrolTimer" type="Timer" parent="."] -unique_name_in_owner = true -wait_time = 10.0 -autostart = true - -[node name="AttackTimer" type="Timer" parent="."] -unique_name_in_owner = true -wait_time = 0.8 -autostart = true - -[node name="Raycast" type="RayCast3D" parent="."] +[node name="Raycast" type="RayCast3D" parent="LineOfSight"] unique_name_in_owner = true +transform = Transform3D(-1, 0, 8.74228e-08, 0, 1, 0, -8.74228e-08, 0, -1, 0, 0, 0) target_position = Vector3(0, 0, -5) collision_mask = 3 @@ -88,5 +59,30 @@ shape = SubResource("SphereShape3D_8vcnq") [node name="EnemyModelView" parent="." instance=ExtResource("2_0hbxv")] unique_name_in_owner = true -[node name="NavigationAgentClient" parent="." instance=ExtResource("4_m7ocm")] +[node name="PlayerDetector" type="Area3D" parent="."] unique_name_in_owner = true +collision_layer = 0 +collision_mask = 34 + +[node name="CollisionShape3D" type="CollisionShape3D" parent="PlayerDetector"] +shape = SubResource("CylinderShape3D_gxowl") + +[node name="Components" type="Node3D" parent="."] + +[node name="HealthComponent" parent="Components" instance=ExtResource("3_gxowl")] + +[node name="PatrolBehavior" parent="Components" instance=ExtResource("4_5pbfd")] +unique_name_in_owner = true + +[node name="FollowBehavior" parent="Components" instance=ExtResource("5_ha827")] +unique_name_in_owner = true +_followSpeed = 150.0 + +[node name="EngagePlayerBehavior" parent="Components" instance=ExtResource("6_7afhy")] +unique_name_in_owner = true +_acquireTargetTime = 2.0 + +[node name="NavigationAgent" type="NavigationAgent3D" parent="Components"] +unique_name_in_owner = true +avoidance_enabled = true +radius = 1.0 diff --git a/Zennysoft.Game.Ma/src/enemy/enemy_types/BossTypeAEnemyModelView.cs b/Zennysoft.Game.Ma/src/enemy/enemy_types/BossTypeAEnemyModelView.cs index a140fcde..c2862269 100644 --- a/Zennysoft.Game.Ma/src/enemy/enemy_types/BossTypeAEnemyModelView.cs +++ b/Zennysoft.Game.Ma/src/enemy/enemy_types/BossTypeAEnemyModelView.cs @@ -1,5 +1,6 @@ using Chickensoft.AutoInject; using Chickensoft.Introspection; +using Godot; namespace Zennysoft.Game.Ma; [Meta(typeof(IAutoNode))] @@ -7,5 +8,5 @@ public partial class BossTypeAEnemyModelView : EnemyModelView3D { public override void _Notification(int what) => this.Notify(what); - [Node] public IHitbox Hitbox { get; set; } = default!; + [Node] public Area3D Hitbox { get; set; } = default!; } diff --git a/Zennysoft.Game.Ma/src/enemy/state/EnemyLogic.Input.cs b/Zennysoft.Game.Ma/src/enemy/state/EnemyLogic.Input.cs index 6fe88235..6fbf81b8 100644 --- a/Zennysoft.Game.Ma/src/enemy/state/EnemyLogic.Input.cs +++ b/Zennysoft.Game.Ma/src/enemy/state/EnemyLogic.Input.cs @@ -7,22 +7,26 @@ public partial class EnemyLogic { public static class Input { - public readonly record struct Alerted; + public readonly record struct Reset; - public readonly record struct LostPlayer; + public readonly record struct Activate; - public readonly record struct PhysicsTick(double Delta); + public readonly record struct Alert; - public readonly record struct EnemyDefeated; + public readonly record struct Patrol; - public readonly record struct StartPatrol; + public readonly record struct Follow; - public readonly record struct StopMoving; + public readonly record struct Move; - public readonly record struct PatrolToRandomSpot(Vector3 PatrolTarget); + public readonly record struct Idle; - public readonly record struct AttackTimer; + public readonly record struct ReachedPlayer; - public readonly record struct StartAttacking; + public readonly record struct LoseTrackOfTarget; + + public readonly record struct Attack; + + public readonly record struct Defeated; } } diff --git a/Zennysoft.Game.Ma/src/enemy/state/EnemyLogic.Output.cs b/Zennysoft.Game.Ma/src/enemy/state/EnemyLogic.Output.cs index 264cb1af..6a06f214 100644 --- a/Zennysoft.Game.Ma/src/enemy/state/EnemyLogic.Output.cs +++ b/Zennysoft.Game.Ma/src/enemy/state/EnemyLogic.Output.cs @@ -6,12 +6,14 @@ public partial class EnemyLogic { public static class Output { - public readonly record struct MoveTowardsPlayer(Vector3 TargetPosition); + public readonly record struct Activate; - public readonly record struct MovementComputed(Vector3 LinearVelocity); + public readonly record struct Idle; - public readonly record struct TakeAction; + public readonly record struct Move; - public readonly record struct Defeated; + public readonly record struct EngagePlayer; + + public readonly record struct ReturnToDefaultState; } } diff --git a/Zennysoft.Game.Ma/src/enemy/state/states/EnemyLogic.State.Activated.cs b/Zennysoft.Game.Ma/src/enemy/state/states/EnemyLogic.State.Activated.cs index 0877cd64..813f9d9c 100644 --- a/Zennysoft.Game.Ma/src/enemy/state/states/EnemyLogic.State.Activated.cs +++ b/Zennysoft.Game.Ma/src/enemy/state/states/EnemyLogic.State.Activated.cs @@ -1,5 +1,4 @@ using Chickensoft.Introspection; -using Godot; namespace Zennysoft.Game.Ma; @@ -10,9 +9,7 @@ public partial class EnemyLogic [Meta, Id("enemy_logic_state_activated")] public partial record Activated : Alive { - public Activated() - { - } + public Activated() => OnAttach(() => Output(new Output.Activate())); } } } \ No newline at end of file diff --git a/Zennysoft.Game.Ma/src/enemy/state/states/EnemyLogic.State.Alive.cs b/Zennysoft.Game.Ma/src/enemy/state/states/EnemyLogic.State.Alive.cs index 1785641f..4d575972 100644 --- a/Zennysoft.Game.Ma/src/enemy/state/states/EnemyLogic.State.Alive.cs +++ b/Zennysoft.Game.Ma/src/enemy/state/states/EnemyLogic.State.Alive.cs @@ -1,4 +1,5 @@ using Chickensoft.Introspection; +using static Zennysoft.Game.Ma.EnemyLogic.Input; namespace Zennysoft.Game.Ma; @@ -7,38 +8,39 @@ public partial class EnemyLogic public partial record State { [Meta, Id("enemy_logic_state_alive")] - public abstract partial record Alive : State, IGet, IGet, IGet, IGet, IGet + public abstract partial record Alive : State, + IGet, + IGet, + IGet, + IGet, + IGet, + IGet, + IGet { - public Transition On(in Input.AttackTimer input) - { - Output(new Output.TakeAction()); - return To(); - } + public Transition On(in ReachedPlayer input) => To(); - public Transition On(in Input.EnemyDefeated input) - { - Output(new Output.Defeated()); - return To(); - } + public Transition On(in Patrol _) => To(); - public Transition On(in Input.StopMoving input) + public Transition On(in Follow _) => To(); + + public Transition On(in Reset input) { + Output(new Output.ReturnToDefaultState()); return To(); } - public Transition On(in Input.StartAttacking input) + public Transition On(in Input.Defeated input) => To(); + + public Transition On(in Input.Idle input) { - return To(); + Output(new Output.Idle()); + return ToSelf(); } - public Transition On(in Input.Alerted _) + public Transition On(in Move input) { - var enemy = Get(); - if (enemy is IHasRangedAttack rangedAttacker) - rangedAttacker.RangedAttack(); - if (enemy is ICanActivate canActivate) - canActivate.Activate(); - return To(); + Output(new Output.Move()); + return ToSelf(); } } } diff --git a/Zennysoft.Game.Ma/src/enemy/state/states/EnemyLogic.State.Attacking.cs b/Zennysoft.Game.Ma/src/enemy/state/states/EnemyLogic.State.Attacking.cs deleted file mode 100644 index a263f7a9..00000000 --- a/Zennysoft.Game.Ma/src/enemy/state/states/EnemyLogic.State.Attacking.cs +++ /dev/null @@ -1,25 +0,0 @@ -using Chickensoft.Introspection; - -namespace Zennysoft.Game.Ma; - -public partial class EnemyLogic -{ - public partial record State - { - [Meta, Id("enemy_logic_state_attacking")] - public partial record Attacking : Activated, IGet - { - public Attacking() - { - OnAttach(() => - { - Get().StartAttackTimer(); - }); - OnDetach(() => - { - Get().StopAttackTimer(); - }); - } - } - } -} \ No newline at end of file diff --git a/Zennysoft.Game.Ma/src/enemy/state/states/EnemyLogic.State.Defeated.cs b/Zennysoft.Game.Ma/src/enemy/state/states/EnemyLogic.State.Defeated.cs index aa9f994c..3ea89cab 100644 --- a/Zennysoft.Game.Ma/src/enemy/state/states/EnemyLogic.State.Defeated.cs +++ b/Zennysoft.Game.Ma/src/enemy/state/states/EnemyLogic.State.Defeated.cs @@ -1,5 +1,4 @@ using Chickensoft.Introspection; -using static Zennysoft.Game.Ma.EnemyLogic.Input; namespace Zennysoft.Game.Ma; @@ -12,4 +11,4 @@ public partial class EnemyLogic { } } -} \ No newline at end of file +} diff --git a/Zennysoft.Game.Ma/src/enemy/state/states/EnemyLogic.State.EngagePlayer.cs b/Zennysoft.Game.Ma/src/enemy/state/states/EnemyLogic.State.EngagePlayer.cs new file mode 100644 index 00000000..be669999 --- /dev/null +++ b/Zennysoft.Game.Ma/src/enemy/state/states/EnemyLogic.State.EngagePlayer.cs @@ -0,0 +1,30 @@ +using Chickensoft.Introspection; +using Zennysoft.Ma.Adapter.Entity; + +namespace Zennysoft.Game.Ma; + +public partial class EnemyLogic +{ + public partial record State + { + [Meta, Id("enemy_logic_state_engage_player")] + public partial record EngagePlayer : Alive + { + public EngagePlayer() + { + OnAttach(() => + { + var enemy = Get(); + if (enemy is IHaveEngagePlayerBehavior engagePlayerEnemy) + engagePlayerEnemy.EngagePlayerBehavior.Engage(); + }); + OnDetach(() => + { + var enemy = Get(); + if (enemy is IHaveEngagePlayerBehavior engagePlayerEnemy) + engagePlayerEnemy.EngagePlayerBehavior.Disengage(); + }); + } + } + } +} \ No newline at end of file diff --git a/Zennysoft.Game.Ma/src/enemy/state/states/EnemyLogic.State.EngagePlayer.cs.uid b/Zennysoft.Game.Ma/src/enemy/state/states/EnemyLogic.State.EngagePlayer.cs.uid new file mode 100644 index 00000000..38df7512 --- /dev/null +++ b/Zennysoft.Game.Ma/src/enemy/state/states/EnemyLogic.State.EngagePlayer.cs.uid @@ -0,0 +1 @@ +uid://7dhnq5qeu2ud diff --git a/Zennysoft.Game.Ma/src/enemy/state/states/EnemyLogic.State.FollowPlayer.cs b/Zennysoft.Game.Ma/src/enemy/state/states/EnemyLogic.State.FollowPlayer.cs index 1871f609..d982980a 100644 --- a/Zennysoft.Game.Ma/src/enemy/state/states/EnemyLogic.State.FollowPlayer.cs +++ b/Zennysoft.Game.Ma/src/enemy/state/states/EnemyLogic.State.FollowPlayer.cs @@ -1,5 +1,5 @@ using Chickensoft.Introspection; -using Zennysoft.Ma.Adapter; +using Zennysoft.Ma.Adapter.Entity; namespace Zennysoft.Game.Ma; @@ -8,24 +8,27 @@ public partial class EnemyLogic public partial record State { [Meta, Id("enemy_logic_state_followplayer")] - public partial record FollowPlayer : Activated, IGet, IGet + public partial record FollowPlayer : Alive, IGet { public FollowPlayer() { - OnAttach(() => Get().Move()); + OnAttach(() => + { + var enemy = Get(); + if (enemy is IHaveFollowBehavior followEnemy) + followEnemy.FollowBehavior.StartFollow(followEnemy.NavigationAgent); + }); + OnDetach(() => + { + var enemy = Get(); + if (enemy is IHaveFollowBehavior patrolEnemy) + patrolEnemy.FollowBehavior.StopFollow(); + }); } - public Transition On(in Input.PhysicsTick input) - { - var enemy = Get(); - var player = Get(); - var target = player.CurrentPosition; - enemy.SetTarget(target); - return ToSelf(); - } - - public Transition On(in Input.LostPlayer input) + public Transition On(in Input.LoseTrackOfTarget input) { + Output(new Output.ReturnToDefaultState()); return To(); } } diff --git a/Zennysoft.Game.Ma/src/enemy/state/states/EnemyLogic.State.Idle.cs b/Zennysoft.Game.Ma/src/enemy/state/states/EnemyLogic.State.Idle.cs index 85e47e2f..3d012b56 100644 --- a/Zennysoft.Game.Ma/src/enemy/state/states/EnemyLogic.State.Idle.cs +++ b/Zennysoft.Game.Ma/src/enemy/state/states/EnemyLogic.State.Idle.cs @@ -1,5 +1,4 @@ using Chickensoft.Introspection; -using Godot; namespace Zennysoft.Game.Ma; @@ -8,17 +7,9 @@ public partial class EnemyLogic public partial record State { [Meta, Id("enemy_logic_state_idle")] - public partial record Idle : Alive, IGet + public partial record Idle : Alive { - public Idle() - { - OnAttach(() => Get().Idle()); - } - - public Transition On(in Input.StartPatrol _) - { - return To(); - } + public Idle() => OnAttach(() => Output(new Output.Idle())); } } } \ No newline at end of file diff --git a/Zennysoft.Game.Ma/src/enemy/state/states/EnemyLogic.State.Patrolling.cs b/Zennysoft.Game.Ma/src/enemy/state/states/EnemyLogic.State.Patrolling.cs index 16f57ba9..6e81d0e3 100644 --- a/Zennysoft.Game.Ma/src/enemy/state/states/EnemyLogic.State.Patrolling.cs +++ b/Zennysoft.Game.Ma/src/enemy/state/states/EnemyLogic.State.Patrolling.cs @@ -1,6 +1,6 @@ using Chickensoft.Introspection; using Godot; -using Org.BouncyCastle.Asn1.X509; +using Zennysoft.Ma.Adapter.Entity; namespace Zennysoft.Game.Ma; @@ -9,27 +9,22 @@ public partial class EnemyLogic public partial record State { [Meta, Id("enemy_logic_state_patrolling")] - public partial record Patrolling : Activated, IGet, IGet, IGet + public partial record Patrolling : Alive { - private Vector3 _patrolTarget { get; set; } - public Patrolling() { - OnAttach(() => Get().Move()); - } - - public Transition On(in Input.PhysicsTick input) - { - var delta = input.Delta; - var enemy = Get(); - enemy.SetTarget(_patrolTarget); - return ToSelf(); - } - - public Transition On(in Input.PatrolToRandomSpot input) - { - _patrolTarget = input.PatrolTarget; - return ToSelf(); + OnAttach(() => + { + var enemy = Get(); + if (enemy is IHavePatrolBehavior patrolEnemy) + patrolEnemy.PatrolBehavior.StartPatrol(); + }); + OnDetach(() => + { + var enemy = Get(); + if (enemy is IHavePatrolBehavior patrolEnemy) + patrolEnemy.PatrolBehavior.StopPatrol(); + }); } } } diff --git a/Zennysoft.Game.Ma/src/game/Game.cs b/Zennysoft.Game.Ma/src/game/Game.cs index 29110749..2a9721dd 100644 --- a/Zennysoft.Game.Ma/src/game/Game.cs +++ b/Zennysoft.Game.Ma/src/game/Game.cs @@ -321,9 +321,9 @@ public partial class Game : Node3D, IGame public IDungeonFloor CurrentFloor => _map.CurrentFloor; - public void EnemyDefeated(Vector3 defeatedLocation, EnemyStatResource resource) + public void EnemyDefeated(Vector3 defeatedLocation) { - _player.GainExp(resource.ExpFromDefeat * GameRepo.ExpRate); + _player.GainExp(10 * GameRepo.ExpRate); DropRestorative(defeatedLocation); } diff --git a/Zennysoft.Game.Ma/src/game/IGame.cs b/Zennysoft.Game.Ma/src/game/IGame.cs index c713f94a..617c1e88 100644 --- a/Zennysoft.Game.Ma/src/game/IGame.cs +++ b/Zennysoft.Game.Ma/src/game/IGame.cs @@ -26,7 +26,7 @@ public interface IGame : IProvide, IProvide, IProvide public void FloorExitReached(); - public void EnemyDefeated(Vector3 defeatedLocation, EnemyStatResource enemyStatResource); + public void EnemyDefeated(Vector3 defeatedLocation); public InventoryItem RerollItem(InventoryItem itemToReroll, bool insertIntoInventory = true); diff --git a/Zennysoft.Game.Ma/src/hitbox/Hitbox.cs b/Zennysoft.Game.Ma/src/hitbox/Hitbox.cs deleted file mode 100644 index f2357b29..00000000 --- a/Zennysoft.Game.Ma/src/hitbox/Hitbox.cs +++ /dev/null @@ -1,11 +0,0 @@ -using Chickensoft.GodotNodeInterfaces; -using Godot; - -namespace Zennysoft.Game.Ma; -public interface IHitbox : IArea3D -{ -} - -public partial class Hitbox : Area3D, IHitbox -{ -} diff --git a/Zennysoft.Game.Ma/src/hitbox/Hitbox.cs.uid b/Zennysoft.Game.Ma/src/hitbox/Hitbox.cs.uid deleted file mode 100644 index 000347a2..00000000 --- a/Zennysoft.Game.Ma/src/hitbox/Hitbox.cs.uid +++ /dev/null @@ -1 +0,0 @@ -uid://6edayafleq8y diff --git a/Zennysoft.Game.Ma/src/items/EffectService.cs b/Zennysoft.Game.Ma/src/items/EffectService.cs index 9af5aab4..f845d9de 100644 --- a/Zennysoft.Game.Ma/src/items/EffectService.cs +++ b/Zennysoft.Game.Ma/src/items/EffectService.cs @@ -2,6 +2,7 @@ using System.Linq; using System; using Zennysoft.Ma.Adapter; +using Zennysoft.Ma.Adapter.Entity; namespace Zennysoft.Game.Ma; @@ -40,7 +41,7 @@ public class EffectService var spawnPoints = currentMonsterRoom.EnemySpawnPoints.GetChildren().OfType().ToList(); var spawnPointsGodotCollection = new Godot.Collections.Array(spawnPoints); var randomSpawnPoint = spawnPointsGodotCollection.PickRandom(); - enemy.SetEnemyGlobalPosition(randomSpawnPoint.GlobalPosition); + enemy.SetEnemyPosition(randomSpawnPoint.GlobalPosition); } } @@ -53,8 +54,8 @@ public class EffectService var currentMonsterRoom = (MonsterRoom)currentRoom; var enemyList = currentMonsterRoom.GetEnemiesInCurrentRoom().ToList(); var enemiesToKill = enemyList.Count / 2; - for (var i = 0; i < enemiesToKill; i++) - enemyList[i].Die(); + //for (var i = 0; i < enemiesToKill; i++) + // enemyList[i].Die(); } public void TurnAllEnemiesInRoomIntoHealingItem() @@ -67,8 +68,8 @@ public class EffectService var currentEnemies = currentRoom.EnemiesInRoom; foreach (var enemy in currentEnemies) { - enemy.Die(); - DropHealingItem(enemy.GetEnemyGlobalPosition()); + //enemy.Die(); + //DropHealingItem(enemy.GetEnemyGlobalPosition()); } } @@ -95,8 +96,8 @@ public class EffectService return; var currentEnemies = currentRoom.EnemiesInRoom; - foreach (var enemy in currentEnemies) - enemy.SetCurrentHP(enemy.GetMaximumHP()); + //foreach (var enemy in currentEnemies) + // enemy.SetCurrentHP(enemy.GetMaximumHP()); _player.Stats.SetCurrentHP(_player.Stats.MaximumHP.Value); } @@ -109,8 +110,8 @@ public class EffectService var currentEnemies = currentRoom.EnemiesInRoom; var hpToAbsorb = 0.0; - foreach (var enemy in currentEnemies) - hpToAbsorb += enemy.CurrentHP.Value * 0.05; + //foreach (var enemy in currentEnemies) + // hpToAbsorb += enemy.CurrentHP.Value * 0.05; _player.Stats.SetCurrentHP(_player.Stats.CurrentHP.Value + (int)hpToAbsorb); GD.Print("HP to absorb: " + hpToAbsorb); } @@ -124,7 +125,10 @@ public class EffectService var currentEnemies = currentRoom.EnemiesInRoom; foreach (var enemy in currentEnemies) - enemy.TakeDamage(20, elementType); + { + var damageDealt = DamageCalculator.CalculateDamage(new Damage(20, elementType, false, false, false), 10, new ElementalResistanceSet(0, 0, 0, 0, 0)); + enemy.TakeDamage(damageDealt); + } } public void SwapHPandVT() @@ -175,7 +179,7 @@ public class EffectService { var currentFloor = _game.CurrentFloor; var rooms = currentFloor.Rooms; - var currentRoom = enemy.GetCurrentRoom(); + var currentRoom = enemy.GetCurrentRoom(rooms); var validRooms = rooms.OfType().ToList(); if (currentRoom is MonsterRoom currentMonsterRoom) validRooms.Remove(currentMonsterRoom); @@ -189,7 +193,7 @@ public class EffectService var spawnPointsGodotCollection = new Godot.Collections.Array(spawnPoints); var randomSpawnPoint = spawnPointsGodotCollection.PickRandom(); - enemy.SetEnemyGlobalPosition(randomSpawnPoint.GlobalPosition); + enemy.SetEnemyPosition(randomSpawnPoint.GlobalPosition); } public void TeleportToRandomRoom(IPlayer player) @@ -232,10 +236,4 @@ public class EffectService if (exitRoom.PlayerDiscoveredRoom) player.TeleportPlayer(exitRoom.PlayerSpawn.Transform); } - - public void WarpToExit(IEnemy enemy) - { - var exitRoom = _game.CurrentFloor.Rooms.OfType().Single(); - enemy.SetEnemyGlobalPosition(exitRoom.PlayerSpawn.GlobalPosition); - } } diff --git a/Zennysoft.Game.Ma/src/items/accessory/Accessory.cs b/Zennysoft.Game.Ma/src/items/accessory/Accessory.cs index e739c2d5..348203d0 100644 --- a/Zennysoft.Game.Ma/src/items/accessory/Accessory.cs +++ b/Zennysoft.Game.Ma/src/items/accessory/Accessory.cs @@ -23,7 +23,7 @@ public partial class Accessory : EquipableItem public override float SpawnRate => Stats.SpawnRate; - public override double ThrowDamage => Stats.ThrowDamage; + public override int ThrowDamage => Stats.ThrowDamage; public override float ThrowSpeed => Stats.ThrowSpeed; diff --git a/Zennysoft.Game.Ma/src/items/armor/Armor.cs b/Zennysoft.Game.Ma/src/items/armor/Armor.cs index 0e197a54..1279f60e 100644 --- a/Zennysoft.Game.Ma/src/items/armor/Armor.cs +++ b/Zennysoft.Game.Ma/src/items/armor/Armor.cs @@ -24,7 +24,7 @@ public partial class Armor : EquipableItem public override float SpawnRate => Stats.SpawnRate; - public override double ThrowDamage => Stats.ThrowDamage; + public override int ThrowDamage => Stats.ThrowDamage; public override float ThrowSpeed => Stats.ThrowSpeed; diff --git a/Zennysoft.Game.Ma/src/items/consumable/ConsumableItem.cs b/Zennysoft.Game.Ma/src/items/consumable/ConsumableItem.cs index 677ed6cc..31ef597f 100644 --- a/Zennysoft.Game.Ma/src/items/consumable/ConsumableItem.cs +++ b/Zennysoft.Game.Ma/src/items/consumable/ConsumableItem.cs @@ -24,7 +24,7 @@ public partial class ConsumableItem : InventoryItem public override float SpawnRate => Stats.SpawnRate; - public override double ThrowDamage => Stats.ThrowDamage; + public override int ThrowDamage => Stats.ThrowDamage; public override float ThrowSpeed => Stats.ThrowSpeed; diff --git a/Zennysoft.Game.Ma/src/items/effect/EffectItem.cs b/Zennysoft.Game.Ma/src/items/effect/EffectItem.cs index c2724d58..22d2d163 100644 --- a/Zennysoft.Game.Ma/src/items/effect/EffectItem.cs +++ b/Zennysoft.Game.Ma/src/items/effect/EffectItem.cs @@ -24,7 +24,7 @@ public partial class EffectItem : InventoryItem public override float SpawnRate => Stats.SpawnRate; - public override double ThrowDamage => Stats.ThrowDamage; + public override int ThrowDamage => Stats.ThrowDamage; public override float ThrowSpeed => Stats.ThrowSpeed; diff --git a/Zennysoft.Game.Ma/src/items/throwable/ThrowableItem.cs b/Zennysoft.Game.Ma/src/items/throwable/ThrowableItem.cs index 36acecb4..98f06c38 100644 --- a/Zennysoft.Game.Ma/src/items/throwable/ThrowableItem.cs +++ b/Zennysoft.Game.Ma/src/items/throwable/ThrowableItem.cs @@ -25,7 +25,7 @@ public partial class ThrowableItem : InventoryItem, IStackable public override float SpawnRate => Stats.SpawnRate; - public override double ThrowDamage => Stats.ThrowDamage; + public override int ThrowDamage => Stats.ThrowDamage; public override float ThrowSpeed => Stats.ThrowSpeed; diff --git a/Zennysoft.Game.Ma/src/items/thrown/ThrownItem.cs b/Zennysoft.Game.Ma/src/items/thrown/ThrownItem.cs index f6a48368..f4c897e7 100644 --- a/Zennysoft.Game.Ma/src/items/thrown/ThrownItem.cs +++ b/Zennysoft.Game.Ma/src/items/thrown/ThrownItem.cs @@ -2,6 +2,7 @@ using Chickensoft.AutoInject; using Chickensoft.Introspection; using Godot; using Zennysoft.Ma.Adapter; +using Zennysoft.Ma.Adapter.Entity; namespace Zennysoft.Game.Ma; @@ -107,21 +108,22 @@ public partial class ThrownItem : RigidBody3D { switch (throwableItem.ThrowableItemTag) { - case ThrowableItemTag.LowerTargetTo1HP: - enemy.TakeDamage(enemy.CurrentHP.Value - 1, ignoreDefense: true, ignoreElementalResistance: true); - break; + //case ThrowableItemTag.LowerTargetTo1HP: + // enemy.TakeDamage(enemy.CurrentHP.Value - 1, ignoreDefense: true, ignoreElementalResistance: true); + // break; case ThrowableItemTag.TeleportToRandomLocation: _effectService.TeleportToRandomRoom(enemy); break; - case ThrowableItemTag.WarpToExitIfFound: - _effectService.WarpToExit(enemy); - break; default: - enemy.TakeDamage(throwableItem.ThrowDamage, throwableItem.ElementType); + var damageDealt = DamageCalculator.CalculateDamage(new Damage(throwableItem.ThrowDamage, throwableItem.ElementType, false, false, false), 10, new ElementalResistanceSet(0, 0, 0, 0, 0)); + enemy.TakeDamage(damageDealt); break; } } else - enemy.TakeDamage(ItemThatIsThrown.ThrowDamage); + { + var damageDealt = DamageCalculator.CalculateDamage(new Damage(ItemThatIsThrown.ThrowDamage, ElementType.None, false, false, false), 10, new ElementalResistanceSet(0, 0, 0, 0, 0)); + enemy.TakeDamage(damageDealt); + } } } \ No newline at end of file diff --git a/Zennysoft.Game.Ma/src/items/weapons/Weapon.cs b/Zennysoft.Game.Ma/src/items/weapons/Weapon.cs index ad56ddba..73f360ac 100644 --- a/Zennysoft.Game.Ma/src/items/weapons/Weapon.cs +++ b/Zennysoft.Game.Ma/src/items/weapons/Weapon.cs @@ -24,7 +24,7 @@ public partial class Weapon : EquipableItem public override float SpawnRate => Stats.SpawnRate; - public override double ThrowDamage => Stats.ThrowDamage; + public override int ThrowDamage => Stats.ThrowDamage; public override float ThrowSpeed => Stats.ThrowSpeed; diff --git a/Zennysoft.Game.Ma/src/map/Map.tscn b/Zennysoft.Game.Ma/src/map/Map.tscn index 5dbe4c52..f332379f 100644 --- a/Zennysoft.Game.Ma/src/map/Map.tscn +++ b/Zennysoft.Game.Ma/src/map/Map.tscn @@ -76,15 +76,6 @@ libraries = { [node name="MapOrder" type="Node" parent="."] unique_name_in_owner = true -[node name="Overworld (Add SpecialFloorLayout node for special floors and pick the FloorName from the list)" type="Node" parent="MapOrder"] -script = ExtResource("2_00xd7") -metadata/_custom_type_script = "uid://cabvj6s31iucg" - -[node name="Altar (Arrange order of nodes to change default load order)" type="Node" parent="MapOrder"] -script = ExtResource("2_00xd7") -FloorName = 1 -metadata/_custom_type_script = "uid://cabvj6s31iucg" - [node name="Floor01 (Press Populate Map Data Button to load floor from SetAFloors folder)" type="Node" parent="MapOrder"] script = ExtResource("3_v14r0") LayoutWithSpawnRate = Dictionary[String, float]({ @@ -95,6 +86,15 @@ EnemySpawnRates = { } metadata/_custom_type_script = "uid://ci7o3nn4mdo8o" +[node name="Overworld (Add SpecialFloorLayout node for special floors and pick the FloorName from the list)" type="Node" parent="MapOrder"] +script = ExtResource("2_00xd7") +metadata/_custom_type_script = "uid://cabvj6s31iucg" + +[node name="Altar (Arrange order of nodes to change default load order)" type="Node" parent="MapOrder"] +script = ExtResource("2_00xd7") +FloorName = 1 +metadata/_custom_type_script = "uid://cabvj6s31iucg" + [node name="Floor02 (Add DungeonFloorLayout node for regular dungeon floors)" type="Node" parent="MapOrder"] script = ExtResource("3_v14r0") LayoutWithSpawnRate = Dictionary[String, float]({}) diff --git a/Zennysoft.Game.Ma/src/map/dungeon/code/BossRoomA.cs b/Zennysoft.Game.Ma/src/map/dungeon/code/BossRoomA.cs index bc87b418..415deb1f 100644 --- a/Zennysoft.Game.Ma/src/map/dungeon/code/BossRoomA.cs +++ b/Zennysoft.Game.Ma/src/map/dungeon/code/BossRoomA.cs @@ -33,44 +33,36 @@ public partial class BossRoomA : Node3D, IBossRoom, IDungeonFloor public void Setup() { - ActivateTrap.BodyEntered += ActivateTrap_BodyEntered; - OxFace.CurrentHP.Sync += BossStatusUpdate; - HorseFace.CurrentHP.Sync += BossStatusUpdate; - _exit.AreaEntered += Exit_AreaEntered; + ActivateTrap.BodyEntered += ActivateTrap_BodyEntered; + _exit.AreaEntered += Exit_AreaEntered; } private void ActivateTrap_BodyEntered(Node3D body) { - ActivateTrap.BodyEntered -= ActivateTrap_BodyEntered; - StartBossFight(); + ActivateTrap.BodyEntered -= ActivateTrap_BodyEntered; + StartBossFight(); } public void StartBossFight() { - OxFaceStatue.Hide(); - HorseHeadStatue.Hide(); - OxFace.StartFight(); - HorseFace.StartFight(); + OxFaceStatue.Hide(); + HorseHeadStatue.Hide(); + OxFace.StartFight(); + HorseFace.StartFight(); } public void OnBossFightEnded() { - GateCollision.CallDeferred(MethodName.QueueFree); - } - - private void BossStatusUpdate(double hp) - { - if (OxFace.CurrentHP.Value <= 0 && HorseFace.CurrentHP.Value <= 0) - OnBossFightEnded(); + GateCollision.CallDeferred(MethodName.QueueFree); } public void ExitReached() - => Game.FloorExitReached(); + => Game.FloorExitReached(); private void Exit_AreaEntered(Area3D area) { - if (area.GetOwner() is IPlayer) - ExitReached(); + if (area.GetOwner() is IPlayer) + ExitReached(); } public void InitializeDungeon() diff --git a/Zennysoft.Game.Ma/src/map/dungeon/code/DungeonRoom.cs b/Zennysoft.Game.Ma/src/map/dungeon/code/DungeonRoom.cs index c0104975..2ccd0524 100644 --- a/Zennysoft.Game.Ma/src/map/dungeon/code/DungeonRoom.cs +++ b/Zennysoft.Game.Ma/src/map/dungeon/code/DungeonRoom.cs @@ -3,6 +3,7 @@ using Chickensoft.Introspection; using Godot; using System.Collections.Immutable; using Zennysoft.Ma.Adapter; +using Zennysoft.Ma.Adapter.Entity; namespace Zennysoft.Game.Ma; diff --git a/Zennysoft.Game.Ma/src/map/dungeon/code/MonsterRoom.cs b/Zennysoft.Game.Ma/src/map/dungeon/code/MonsterRoom.cs index fbfdc09f..41bb369c 100644 --- a/Zennysoft.Game.Ma/src/map/dungeon/code/MonsterRoom.cs +++ b/Zennysoft.Game.Ma/src/map/dungeon/code/MonsterRoom.cs @@ -21,51 +21,51 @@ public partial class MonsterRoom : DungeonRoom public override void _Ready() { - SpawnItems(); + SpawnItems(); } public void SpawnEnemies(Godot.Collections.Dictionary enemyInfo) { - var rng = new RandomNumberGenerator(); - rng.Randomize(); - var enemySpawnPoints = EnemySpawnPoints.GetChildren(); - var numberOfEnemiesToSpawn = rng.RandiRange(1, enemySpawnPoints.Count); + var rng = new RandomNumberGenerator(); + rng.Randomize(); + var enemySpawnPoints = EnemySpawnPoints.GetChildren(); + var numberOfEnemiesToSpawn = rng.RandiRange(1, enemySpawnPoints.Count); - foreach (var spawnPoint in enemySpawnPoints.Cast()) - { - if (numberOfEnemiesToSpawn <= 0) - break; - numberOfEnemiesToSpawn--; + foreach (var spawnPoint in enemySpawnPoints.Cast()) + { + if (numberOfEnemiesToSpawn <= 0) + break; + numberOfEnemiesToSpawn--; - var index = rng.RandWeighted([.. enemyInfo.Values]); - var selectedEnemy = enemyInfo.ElementAt((int)index); - var instantiatedEnemy = EnemyTypeToEnemyConverter.Convert(selectedEnemy.Key); - instantiatedEnemy.Position = new Vector3(spawnPoint.Position.X, 1.75f, spawnPoint.Position.Z); - AddChild(instantiatedEnemy); - } + var index = rng.RandWeighted([.. enemyInfo.Values]); + var selectedEnemy = enemyInfo.ElementAt((int)index); + var instantiatedEnemy = EnemyTypeToEnemyConverter.Convert(selectedEnemy.Key); + instantiatedEnemy.Position = new Vector3(spawnPoint.Position.X, 1.75f, spawnPoint.Position.Z); + AddChild(instantiatedEnemy); + } } private void SpawnItems() { - if (ItemSpawnPoints == null) - return; + if (ItemSpawnPoints == null) + return; - var itemSpawnPoints = ItemSpawnPoints.GetChildren(); - var rng = new RandomNumberGenerator(); - rng.Randomize(); - var numberOfItemsToSpawn = rng.RandiRange(1, itemSpawnPoints.Count); - itemSpawnPoints.Shuffle(); - var database = new ItemDatabase(); - foreach (var spawnPoint in itemSpawnPoints.Cast()) - { - if (numberOfItemsToSpawn <= 0) - break; - numberOfItemsToSpawn--; + var itemSpawnPoints = ItemSpawnPoints.GetChildren(); + var rng = new RandomNumberGenerator(); + rng.Randomize(); + var numberOfItemsToSpawn = rng.RandiRange(1, itemSpawnPoints.Count); + itemSpawnPoints.Shuffle(); + var database = new ItemDatabase(); + foreach (var spawnPoint in itemSpawnPoints.Cast()) + { + if (numberOfItemsToSpawn <= 0) + break; + numberOfItemsToSpawn--; - var selectedItem = database.PickItem(); - var duplicated = selectedItem.Duplicate((int)DuplicateFlags.UseInstantiation) as Node3D; - duplicated.Position = new Vector3(spawnPoint.Position.X, -1.5f, spawnPoint.Position.Z); - AddChild(duplicated); - } + var selectedItem = database.PickItem(); + var duplicated = selectedItem.Duplicate((int)DuplicateFlags.UseInstantiation) as Node3D; + duplicated.Position = new Vector3(spawnPoint.Position.X, -1.5f, spawnPoint.Position.Z); + AddChild(duplicated); + } } } diff --git a/Zennysoft.Game.Ma/src/map/dungeon/models/Area 2/Puer/A2-Puer.glb.import b/Zennysoft.Game.Ma/src/map/dungeon/models/Area 2/Puer/A2-Puer.glb.import index 721d1ffb..90833faa 100644 --- a/Zennysoft.Game.Ma/src/map/dungeon/models/Area 2/Puer/A2-Puer.glb.import +++ b/Zennysoft.Game.Ma/src/map/dungeon/models/Area 2/Puer/A2-Puer.glb.import @@ -4,12 +4,12 @@ importer="scene" importer_version=1 type="PackedScene" uid="uid://dh8ji8g36mmx5" -path="res://.godot/imported/a2-puer.glb-20bdcd533f2a6024a2fa1d9a726cae55.scn" +path="res://.godot/imported/A2-Puer.glb-3b11ccd8f9ba6f91fbb05537c52490b0.scn" [deps] -source_file="res://src/map/dungeon/models/Area 2/Puer/a2-puer.glb" -dest_files=["res://.godot/imported/a2-puer.glb-20bdcd533f2a6024a2fa1d9a726cae55.scn"] +source_file="res://src/map/dungeon/models/Area 2/Puer/A2-Puer.glb" +dest_files=["res://.godot/imported/A2-Puer.glb-3b11ccd8f9ba6f91fbb05537c52490b0.scn"] [params] diff --git a/Zennysoft.Game.Ma/src/player/Player.cs b/Zennysoft.Game.Ma/src/player/Player.cs index 02889a44..3b4bd605 100644 --- a/Zennysoft.Game.Ma/src/player/Player.cs +++ b/Zennysoft.Game.Ma/src/player/Player.cs @@ -7,6 +7,7 @@ using Godot; using SimpleInjector; using System; using Zennysoft.Ma.Adapter; +using Zennysoft.Ma.Adapter.Entity; namespace Zennysoft.Game.Ma; @@ -72,7 +73,7 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide(Lifestyle.Singleton); - //container.Verify(); + var container = new SimpleInjector.Container(); + container.Register(Lifestyle.Singleton); + //container.Verify(); - PlayerLogic = container.GetInstance(); - PlayerLogic.Set(this as IPlayer); - PlayerLogic.Set(Settings); - PlayerLogic.Set(Stats); - PlayerLogic.Set(_gameRepo); + PlayerLogic = container.GetInstance(); + PlayerLogic.Set(this as IPlayer); + PlayerLogic.Set(Settings); + PlayerLogic.Set(Stats); + PlayerLogic.Set(_gameRepo); - _damageCalculator = new DamageCalculator(); - - Hitbox.AreaEntered += Hitbox_AreaEntered; - CollisionDetector.AreaEntered += CollisionDetector_AreaEntered; - AnimationPlayer.AnimationFinished += OnAnimationFinished; + Hitbox.AreaEntered += Hitbox_AreaEntered; + CollisionDetector.AreaEntered += CollisionDetector_AreaEntered; + AnimationPlayer.AnimationFinished += OnAnimationFinished; } public void OnResolved() { - Settings = new PlayerLogic.Settings() { RotationSpeed = _playerStatResource.RotationSpeed, MoveSpeed = _playerStatResource.MoveSpeed, Acceleration = _playerStatResource.Acceleration }; + Settings = new PlayerLogic.Settings() { RotationSpeed = _playerStatResource.RotationSpeed, MoveSpeed = _playerStatResource.MoveSpeed, Acceleration = _playerStatResource.Acceleration }; - PlayerChunk = new SaveChunk( - onSave: (chunk) => new PlayerData() - { - PlayerStats = Stats, - Inventory = Inventory - }, - onLoad: (chunk, data) => - { - Stats = new PlayerStats( - data.PlayerStats.CurrentHP, - data.PlayerStats.MaximumHP, - data.PlayerStats.CurrentVT, - data.PlayerStats.MaximumVT, - data.PlayerStats.CurrentAttack, - data.PlayerStats.BonusAttack, - data.PlayerStats.MaxAttack, - data.PlayerStats.CurrentDefense, - data.PlayerStats.BonusDefense, - data.PlayerStats.MaxDefense, - data.PlayerStats.CurrentExp, - data.PlayerStats.CurrentLevel, - data.PlayerStats.ExpToNextLevel, - data.PlayerStats.Luck); - Inventory = data.Inventory; - } - ); + PlayerChunk = new SaveChunk( + onSave: (chunk) => new PlayerData() + { + PlayerStats = Stats, + Inventory = Inventory + }, + onLoad: (chunk, data) => + { + Stats = new PlayerStats( + data.PlayerStats.CurrentHP, + data.PlayerStats.MaximumHP, + data.PlayerStats.CurrentVT, + data.PlayerStats.MaximumVT, + data.PlayerStats.CurrentAttack, + data.PlayerStats.BonusAttack, + data.PlayerStats.MaxAttack, + data.PlayerStats.CurrentDefense, + data.PlayerStats.BonusDefense, + data.PlayerStats.MaxDefense, + data.PlayerStats.CurrentExp, + data.PlayerStats.CurrentLevel, + data.PlayerStats.ExpToNextLevel, + data.PlayerStats.Luck); + Inventory = data.Inventory; + } + ); - PlayerBinding = PlayerLogic.Bind(); + PlayerBinding = PlayerLogic.Bind(); - PlayerBinding - .Handle((in PlayerLogic.Output.Animations.Attack output) => - { - if (PlayerIsHittingGeometry()) - { - AnimationPlayer.Play("hit_wall"); - _gameRepo.OnPlayerAttackedWall(); - } - else - { - PlayAttackAnimation(); - } - }) - .Handle((in PlayerLogic.Output.ThrowItem output) => - { - }) - .Handle((in PlayerLogic.Output.Move output) => - { - Move(output.delta); - }); + PlayerBinding + .Handle((in PlayerLogic.Output.Animations.Attack output) => + { + if (PlayerIsHittingGeometry()) + { + AnimationPlayer.Play("hit_wall"); + _gameRepo.OnPlayerAttackedWall(); + } + else + { + PlayAttackAnimation(); + } + }) + .Handle((in PlayerLogic.Output.ThrowItem output) => + { + }) + .Handle((in PlayerLogic.Output.Move output) => + { + Move(output.delta); + }); - GameChunk.AddChunk(PlayerChunk); + GameChunk.AddChunk(PlayerChunk); - PlayerLogic.Start(); - this.Provide(); + PlayerLogic.Start(); + this.Provide(); - SetProcessInput(false); - SetPhysicsProcess(false); + SetProcessInput(false); + SetPhysicsProcess(false); } public void OnReady() { - SwordSlashAnimation.Position = GetViewport().GetVisibleRect().Size / 2; + SwordSlashAnimation.Position = GetViewport().GetVisibleRect().Size / 2; } #endregion public void Activate() { - SetProcessInput(true); - SetPhysicsProcess(true); - SetHealthTimerStatus(HealthTimerIsActive); + SetProcessInput(true); + SetPhysicsProcess(true); + SetHealthTimerStatus(HealthTimerIsActive); } public void Deactivate() { - SetProcessInput(false); - SetPhysicsProcess(false); - SetHealthTimerStatus(false); + SetProcessInput(false); + SetPhysicsProcess(false); + SetHealthTimerStatus(false); } public void Attack() { - PlayerLogic.Input(new PlayerLogic.Input.Attack()); + PlayerLogic.Input(new PlayerLogic.Input.Attack()); } public void RaiseHP(int amountToRaise) { - Stats.SetMaximumHP(Stats.MaximumHP.Value + amountToRaise); - Stats.SetCurrentHP(Stats.MaximumHP.Value); - _gameRepo.AnnounceMessageInInventory($"{amountToRaise}MAXHP Up."); + Stats.SetMaximumHP(Stats.MaximumHP.Value + amountToRaise); + Stats.SetCurrentHP(Stats.MaximumHP.Value); + _gameRepo.AnnounceMessageInInventory($"{amountToRaise}MAXHP Up."); } public void HealHP(int amountToRestore) { - Stats.SetCurrentHP(Stats.CurrentHP.Value + amountToRestore); - var raiseString = amountToRestore == 1000 ? "MAX" : $"{amountToRestore}"; - _gameRepo.AnnounceMessageInInventory($"{raiseString}HP Restored."); + Stats.SetCurrentHP(Stats.CurrentHP.Value + amountToRestore); + var raiseString = amountToRestore == 1000 ? "MAX" : $"{amountToRestore}"; + _gameRepo.AnnounceMessageInInventory($"{raiseString}HP Restored."); } public void RaiseVT(int amountToRaise) { - if (Stats.CurrentVT == Stats.MaximumVT) - { - Stats.SetMaximumVT(Stats.MaximumVT.Value + amountToRaise); - Stats.SetCurrentVT(Stats.MaximumVT.Value); - _gameRepo.AnnounceMessageInInventory($"{amountToRaise}MAXVT Up."); - } + if (Stats.CurrentVT == Stats.MaximumVT) + { + Stats.SetMaximumVT(Stats.MaximumVT.Value + amountToRaise); + Stats.SetCurrentVT(Stats.MaximumVT.Value); + _gameRepo.AnnounceMessageInInventory($"{amountToRaise}MAXVT Up."); + } } public void HealVT(int amountToRestore) { - Stats.SetCurrentVT(Stats.CurrentVT.Value + amountToRestore); - var raiseString = amountToRestore == 1000 ? "MAX" : $"{amountToRestore}"; - _gameRepo.AnnounceMessageInInventory($"{raiseString}VT Restored."); + Stats.SetCurrentVT(Stats.CurrentVT.Value + amountToRestore); + var raiseString = amountToRestore == 1000 ? "MAX" : $"{amountToRestore}"; + _gameRepo.AnnounceMessageInInventory($"{raiseString}VT Restored."); } public void ModifyBonusAttack(int amount) { - Stats.SetBonusAttack(Stats.BonusAttack.Value + amount); + Stats.SetBonusAttack(Stats.BonusAttack.Value + amount); } public void ModifyBonusDefense(int amount) { - Stats.SetBonusDefense(Stats.BonusDefense.Value + amount); + Stats.SetBonusDefense(Stats.BonusDefense.Value + amount); } public void ModifyMaximumHP(int amount) { - Stats.SetMaximumHP(Stats.MaximumHP.Value + amount); + Stats.SetMaximumHP(Stats.MaximumHP.Value + amount); } public void ModifyMaximumVT(int amount) { - Stats.SetMaximumVT(Stats.MaximumVT.Value + amount); + Stats.SetMaximumVT(Stats.MaximumVT.Value + amount); } public void ModifyBonusLuck(double amount) { - Stats.SetLuck(Stats.Luck.Value + amount); + Stats.SetLuck(Stats.Luck.Value + amount); } public void SetHealthTimerStatus(bool isActive) { - if (isActive) - HealthTimer.Start(); - else - HealthTimer.Stop(); + if (isActive) + HealthTimer.Start(); + else + HealthTimer.Stop(); } public void Move(float delta) { - var rawInput = GlobalInputVector; - var strafeLeftInput = LeftStrafeInputVector; - var strafeRightInput = RightStrafeInputVector; + var rawInput = GlobalInputVector; + var strafeLeftInput = LeftStrafeInputVector; + var strafeRightInput = RightStrafeInputVector; - var transform = Transform; - transform.Basis = new Basis(Vector3.Up, Settings.RotationSpeed * -rawInput.X * delta) * transform.Basis; - var moveDirection = new Vector3(strafeRightInput - strafeLeftInput, 0, rawInput.Z).Normalized(); - var velocity = Basis * moveDirection * Settings.MoveSpeed * Settings.Acceleration; - _knockbackStrength *= 0.9f; - Transform = Transform with { Basis = transform.Basis }; - Velocity = velocity + (_knockbackDirection * _knockbackStrength); - MoveAndSlide(); + var transform = Transform; + transform.Basis = new Basis(Vector3.Up, Settings.RotationSpeed * -rawInput.X * delta) * transform.Basis; + var moveDirection = new Vector3(strafeRightInput - strafeLeftInput, 0, rawInput.Z).Normalized(); + var velocity = Basis * moveDirection * Settings.MoveSpeed * Settings.Acceleration; + _knockbackStrength *= 0.9f; + Transform = Transform with { Basis = transform.Basis }; + Velocity = velocity + (_knockbackDirection * _knockbackStrength); + MoveAndSlide(); } public void TeleportPlayer(Transform3D newTransform) { - Transform = newTransform; + Transform = newTransform; } - public void TakeDamage(double damage, ElementType elementType, bool isCriticalHit = false) + public void TakeDamage(Damage damage) { - if (Stats.CurrentHP.Value > 0) - { - _damageCalculator.CalculateDamage(damage, elementType, Stats.CurrentDefense.Value + Stats.BonusDefense.Value, ((Armor)_equippedArmor.Value).Stats.ElementalResistanceSet); - Stats.SetCurrentHP(Stats.CurrentHP.Value - (int)damage); - } + if (Stats.CurrentHP.Value > 0) + { + var damageDone = DamageCalculator.CalculateDamage(damage, Stats.CurrentDefense.Value + Stats.BonusDefense.Value, ((Armor)_equippedArmor.Value).Stats.ElementalResistanceSet); + Stats.SetCurrentHP(Stats.CurrentHP.Value - damageDone); + } } public void Knockback(float impulse) { - _knockbackStrength = impulse; - _knockbackDirection = GlobalBasis.Z.Normalized(); + _knockbackStrength = impulse; + _knockbackDirection = GlobalBasis.Z.Normalized(); } public void GainExp(double expGained) { - Stats.SetCurrentExp(Stats.CurrentExp.Value + expGained); + Stats.SetCurrentExp(Stats.CurrentExp.Value + expGained); } public void LevelUp() { - var rng = new RandomNumberGenerator(); - rng.Randomize(); - var hpIncrease = rng.RandiRange(3, 6); - Stats.SetMaximumHP(Stats.MaximumHP.Value + hpIncrease); - var nextLevel = Stats.CurrentLevel.Value + 1; - var expToNextLevel = (int)(6.5 * nextLevel + 4.5 * Mathf.Pow(nextLevel, 2) + Mathf.Pow(nextLevel, 3)); - var newCurrentExp = Mathf.Max(Stats.CurrentExp.Value - Stats.ExpToNextLevel.Value, 0); - Stats.SetCurrentLevel(nextLevel); - Stats.SetExpToNextLevel(expToNextLevel); - Stats.SetCurrentExp(newCurrentExp); + var rng = new RandomNumberGenerator(); + rng.Randomize(); + var hpIncrease = rng.RandiRange(3, 6); + Stats.SetMaximumHP(Stats.MaximumHP.Value + hpIncrease); + var nextLevel = Stats.CurrentLevel.Value + 1; + var expToNextLevel = (int)(6.5 * nextLevel + 4.5 * Mathf.Pow(nextLevel, 2) + Mathf.Pow(nextLevel, 3)); + var newCurrentExp = Mathf.Max(Stats.CurrentExp.Value - Stats.ExpToNextLevel.Value, 0); + Stats.SetCurrentLevel(nextLevel); + Stats.SetExpToNextLevel(expToNextLevel); + Stats.SetCurrentExp(newCurrentExp); } public void Die() { - EquippedWeapon.Sync -= EquippedWeapon_Sync; - EquippedArmor.Sync -= EquippedArmor_Sync; - EquippedAccessory.Sync -= EquippedAccessory_Sync; - Stats.CurrentHP.Sync -= CurrentHP_Sync; - Stats.CurrentExp.Sync -= CurrentEXP_Sync; + EquippedWeapon.Sync -= EquippedWeapon_Sync; + EquippedArmor.Sync -= EquippedArmor_Sync; + EquippedAccessory.Sync -= EquippedAccessory_Sync; + Stats.CurrentHP.Sync -= CurrentHP_Sync; + Stats.CurrentExp.Sync -= CurrentEXP_Sync; - HealthTimer.WaitTime = _healthTimerWaitTime; - HealthTimer.Timeout -= OnHealthTimerTimeout; - SwordSlashAnimation.Stop(); - SetProcessInput(false); - SetPhysicsProcess(false); - //Hitbox.AreaEntered -= Hitbox_AreaEntered; - //CollisionDetector.AreaEntered -= CollisionDetector_AreaEntered; - //AnimationPlayer.AnimationFinished -= OnAnimationFinished; + HealthTimer.WaitTime = _healthTimerWaitTime; + HealthTimer.Timeout -= OnHealthTimerTimeout; + SwordSlashAnimation.Stop(); + SetProcessInput(false); + SetPhysicsProcess(false); + //Hitbox.AreaEntered -= Hitbox_AreaEntered; + //CollisionDetector.AreaEntered -= CollisionDetector_AreaEntered; + //AnimationPlayer.AnimationFinished -= OnAnimationFinished; - Game.GameOver(); + Game.GameOver(); } public override void _Input(InputEvent @event) { - if (@event.IsActionPressed(GameInputs.Attack)) - Attack(); - if (@event.IsActionPressed(GameInputs.Sprint)) - Settings.MoveSpeed *= 2; - if (@event.IsActionReleased(GameInputs.Sprint)) - Settings.MoveSpeed /= 2; + if (@event.IsActionPressed(GameInputs.Attack)) + Attack(); + if (@event.IsActionPressed(GameInputs.Sprint)) + Settings.MoveSpeed *= 2; + if (@event.IsActionReleased(GameInputs.Sprint)) + Settings.MoveSpeed /= 2; } public void OnPhysicsProcess(double delta) { - PlayerLogic.Input(new PlayerLogic.Input.PhysicsTick(delta)); - PlayerLogic.Input(new PlayerLogic.Input.Moved(GlobalPosition, GlobalTransform)); + PlayerLogic.Input(new PlayerLogic.Input.PhysicsTick(delta)); + PlayerLogic.Input(new PlayerLogic.Input.Moved(GlobalPosition, GlobalTransform)); } public void Equip(EquipableItem equipable) { - if (equipable.ItemTag == ItemTag.MysteryItem) - { - var rerolledItem = Game.RerollItem(equipable) as EquipableItem; - Equip(rerolledItem); - return; - } + if (equipable.ItemTag == ItemTag.MysteryItem) + { + var rerolledItem = Game.RerollItem(equipable) as EquipableItem; + Equip(rerolledItem); + return; + } - if (equipable is Weapon weapon) - { - Unequip(_equippedWeapon.Value); - weapon.IsEquipped = true; - _equippedWeapon.OnNext(weapon); - } - else if (equipable is Armor armor) - { - Unequip(_equippedArmor.Value); - armor.IsEquipped = true; - _equippedArmor.OnNext(armor); - } - else if (equipable is Accessory accessory) - { - Unequip(_equippedAccessory.Value); - accessory.IsEquipped = true; - _equippedAccessory.OnNext(accessory); - } - else - throw new NotImplementedException("Item type is not supported."); + if (equipable is Weapon weapon) + { + Unequip(_equippedWeapon.Value); + weapon.IsEquipped = true; + _equippedWeapon.OnNext(weapon); + } + else if (equipable is Armor armor) + { + Unequip(_equippedArmor.Value); + armor.IsEquipped = true; + _equippedArmor.OnNext(armor); + } + else if (equipable is Accessory accessory) + { + Unequip(_equippedAccessory.Value); + accessory.IsEquipped = true; + _equippedAccessory.OnNext(accessory); + } + else + throw new NotImplementedException("Item type is not supported."); } public void Unequip(EquipableItem equipable) { - if (equipable is Weapon weapon) - { - weapon.IsEquipped = false; - ModifyBonusAttack(-weapon.Damage); - _equippedWeapon.OnNext(new Weapon()); - } - else if (equipable is Armor armor) - { - armor.IsEquipped = false; - ModifyBonusDefense(-armor.Defense); - _equippedArmor.OnNext(new Armor()); - } - else if (equipable is Accessory accessory) - { - accessory.IsEquipped = false; - ModifyMaximumHP(-accessory.MaxHPUp); - ModifyMaximumVT(-accessory.MaxVTUp); - ModifyBonusAttack(-accessory.ATKUp); - ModifyBonusDefense(-accessory.DEFUp); - ModifyBonusLuck(-accessory.LuckUp); - _equippedAccessory.OnNext(new Accessory()); - } - else - throw new NotImplementedException("Item type is not supported."); + if (equipable is Weapon weapon) + { + weapon.IsEquipped = false; + ModifyBonusAttack(-weapon.Damage); + _equippedWeapon.OnNext(new Weapon()); + } + else if (equipable is Armor armor) + { + armor.IsEquipped = false; + ModifyBonusDefense(-armor.Defense); + _equippedArmor.OnNext(new Armor()); + } + else if (equipable is Accessory accessory) + { + accessory.IsEquipped = false; + ModifyMaximumHP(-accessory.MaxHPUp); + ModifyMaximumVT(-accessory.MaxVTUp); + ModifyBonusAttack(-accessory.ATKUp); + ModifyBonusDefense(-accessory.DEFUp); + ModifyBonusLuck(-accessory.LuckUp); + _equippedAccessory.OnNext(new Accessory()); + } + else + throw new NotImplementedException("Item type is not supported."); } private static Vector3 GlobalInputVector { - get - { - var rawInput = Input.GetVector(GameInputs.MoveLeft, GameInputs.MoveRight, GameInputs.MoveUp, GameInputs.MoveDown); - var input = new Vector3 - { - X = rawInput.X, - Z = rawInput.Y - }; - return input with { Y = 0f }; - } + get + { + var rawInput = Input.GetVector(GameInputs.MoveLeft, GameInputs.MoveRight, GameInputs.MoveUp, GameInputs.MoveDown); + var input = new Vector3 + { + X = rawInput.X, + Z = rawInput.Y + }; + return input with { Y = 0f }; + } } private static float LeftStrafeInputVector { - get - { - return Input.GetActionStrength(GameInputs.StrafeLeft); - } + get + { + return Input.GetActionStrength(GameInputs.StrafeLeft); + } } private static float RightStrafeInputVector { - get - { - return Input.GetActionStrength(GameInputs.StrafeRight); - } + get + { + return Input.GetActionStrength(GameInputs.StrafeRight); + } } private void ThrowItem() { - var itemScene = GD.Load("res://src/items/throwable/ThrowableItem.tscn"); - var throwItem = itemScene.Instantiate(); - GetTree().Root.AddChildEx(throwItem); - throwItem.GlobalPosition = CurrentPosition + new Vector3(0, 3.5f, 0); - throwItem.GlobalRotation = GlobalRotation; + var itemScene = GD.Load("res://src/items/throwable/ThrowableItem.tscn"); + var throwItem = itemScene.Instantiate(); + GetTree().Root.AddChildEx(throwItem); + throwItem.GlobalPosition = CurrentPosition + new Vector3(0, 3.5f, 0); + throwItem.GlobalRotation = GlobalRotation; } private void PlayAttackAnimation() { - var attackSpeed = ((Weapon)EquippedWeapon.Value).AttackSpeed; - AnimationPlayer.SetSpeedScale((float)attackSpeed); - if (EquippedWeapon.Value.ItemName == "Atonement") - AnimationPlayer.Play("atonement_attack"); - else - AnimationPlayer.Play("attack"); - _gameRepo.OnPlayerAttack(); + var attackSpeed = ((Weapon)EquippedWeapon.Value).AttackSpeed; + AnimationPlayer.SetSpeedScale((float)attackSpeed); + AnimationPlayer.Play("attack"); + _gameRepo.OnPlayerAttack(); } private void OnAnimationFinished(StringName animation) { - PlayerLogic.Input(new PlayerLogic.Input.AttackAnimationFinished()); + PlayerLogic.Input(new PlayerLogic.Input.AttackAnimationFinished()); } private void OnExitTree() { - PlayerLogic.Stop(); - PlayerBinding.Dispose(); - AnimationPlayer.AnimationFinished -= OnAnimationFinished; + PlayerLogic.Stop(); + PlayerBinding.Dispose(); + AnimationPlayer.AnimationFinished -= OnAnimationFinished; } private void OnPlayerPositionUpdated(Vector3 globalPosition) => GlobalPosition = globalPosition; private void OnHealthTimerTimeout() { - if (Stats.CurrentHP.Value <= 0) - return; + if (Stats.CurrentHP.Value <= 0) + return; - if (Stats.CurrentVT.Value > 0) - { - if (((Accessory)EquippedAccessory.Value).AccessoryTag == AccessoryTag.HalfVTConsumption) - { - reduceOnTick = !reduceOnTick; - } - Stats.SetCurrentHP(Stats.CurrentHP.Value + 1); - if (reduceOnTick) - Stats.SetCurrentVT(Stats.CurrentVT.Value - 1); - } - else - Stats.SetCurrentHP(Stats.CurrentHP.Value - 1); + if (Stats.CurrentVT.Value > 0) + { + if (((Accessory)EquippedAccessory.Value).AccessoryTag == AccessoryTag.HalfVTConsumption) + { + reduceOnTick = !reduceOnTick; + } + Stats.SetCurrentHP(Stats.CurrentHP.Value + 1); + if (reduceOnTick) + Stats.SetCurrentVT(Stats.CurrentVT.Value - 1); + } + else + Stats.SetCurrentHP(Stats.CurrentHP.Value - 1); } private void EquippedWeapon_Sync(EquipableItem obj) { - ModifyBonusAttack(((Weapon)obj).Damage); + ModifyBonusAttack(((Weapon)obj).Damage); } private void EquippedArmor_Sync(EquipableItem obj) { - ModifyBonusDefense(((Armor)obj).Defense); + ModifyBonusDefense(((Armor)obj).Defense); } private void EquippedAccessory_Sync(EquipableItem accessory) { - ModifyMaximumHP(((Accessory)accessory).MaxHPUp); - ModifyMaximumVT(((Accessory)accessory).MaxVTUp); - ModifyBonusAttack(((Accessory)accessory).ATKUp); - ModifyBonusDefense(((Accessory)accessory).DEFUp); - ModifyBonusLuck(((Accessory)accessory).LuckUp); + ModifyMaximumHP(((Accessory)accessory).MaxHPUp); + ModifyMaximumVT(((Accessory)accessory).MaxVTUp); + ModifyBonusAttack(((Accessory)accessory).ATKUp); + ModifyBonusDefense(((Accessory)accessory).DEFUp); + ModifyBonusLuck(((Accessory)accessory).LuckUp); } private void CurrentHP_Sync(int newHealth) { - if (newHealth <= 0) - Die(); + if (newHealth <= 0) + Die(); } private void CurrentEXP_Sync(double newExp) { - if (Stats.CurrentExp.Value >= Stats.ExpToNextLevel.Value) - LevelUp(); + if (Stats.CurrentExp.Value >= Stats.ExpToNextLevel.Value) + LevelUp(); } private void Hitbox_AreaEntered(Area3D area) { - var target = area.GetOwner(); - if (target is IEnemy enemy) - HitEnemy(enemy); + var target = area.GetOwner(); + if (target is IEnemy enemy) + HitEnemy(enemy); } private void HitEnemy(IEnemy enemy) { - var attackValue = Stats.CurrentAttack.Value + Stats.BonusAttack.Value; - var ignoreElementalResistance = ((Weapon)EquippedWeapon.Value).WeaponTag == WeaponTag.IgnoreAffinity; - var isCriticalHit = BattleExtensions.IsCriticalHit(Stats.Luck.Value); - var element = ((Weapon)EquippedWeapon.Value).WeaponElement; + var attackValue = Stats.CurrentAttack.Value + Stats.BonusAttack.Value; + var ignoreElementalResistance = ((Weapon)EquippedWeapon.Value).WeaponTag == WeaponTag.IgnoreAffinity; + var isCriticalHit = BattleExtensions.IsCriticalHit(Stats.Luck.Value); + var element = ((Weapon)EquippedWeapon.Value).WeaponElement; + var baseAttack = new Damage( + (int)(attackValue * ((Weapon)EquippedWeapon.Value).ElementalDamageBonus), + element, + isCriticalHit, + false, + ignoreElementalResistance); + var damageDealt = DamageCalculator.CalculateDamage(baseAttack, 10f, new ElementalResistanceSet(0, 0, 0, 0, 0)); + enemy.TakeDamage(damageDealt); - enemy.TakeDamage( - attackValue * ((Weapon)EquippedWeapon.Value).ElementalDamageBonus, - element, - isCriticalHit, - false, - ignoreElementalResistance); + if (((Weapon)EquippedWeapon.Value).WeaponTag == WeaponTag.Knockback && enemy is IKnockbackable knockbackable) + knockbackable.Knockback(0.3f, -CurrentBasis.Z.Normalized()); + if (((Weapon)EquippedWeapon.Value).WeaponTag == WeaponTag.SelfDamage) + TakeDamage(new Damage(5, ElementType.None, false, true, true)); - if (((Weapon)EquippedWeapon.Value).WeaponTag == WeaponTag.Knockback) - enemy.Knockback(0.3f, -CurrentBasis.Z.Normalized()); - - _gameRepo.OnPlayerAttackedEnemy(); + _gameRepo.OnPlayerAttackedEnemy(); } private void CollisionDetector_AreaEntered(Area3D area) { - if (area.GetParent() is InventoryItem inventoryItem) - { - var isAdded = Inventory.TryAdd(inventoryItem); - if (isAdded) - { - _gameRepo.AnnounceMessageOnMainScreen($"{inventoryItem.ItemName} picked up."); - inventoryItem.QueueFree(); - } - else - _gameRepo.AnnounceMessageOnMainScreen($"Could not pick up {inventoryItem.ItemName}."); - } - if (area.GetParent() is DroppedItem droppedItem) - { - var isAdded = Inventory.TryAdd(droppedItem.Item); - if (isAdded) - { - _gameRepo.AnnounceMessageOnMainScreen($"{droppedItem.Item.ItemName} picked up."); - droppedItem.QueueFree(); - } - else - _gameRepo.AnnounceMessageOnMainScreen($"Could not pick up {droppedItem.Item.ItemName}."); - } - if (area.GetParent() is ThrownItem thrownItem) - { - var isAdded = Inventory.TryAdd(thrownItem.ItemThatIsThrown); - if (isAdded) - { - _gameRepo.AnnounceMessageOnMainScreen($"{thrownItem.ItemThatIsThrown.ItemName} picked up."); - thrownItem.QueueFree(); - } - else - _gameRepo.AnnounceMessageOnMainScreen($"Could not pick up {thrownItem.ItemThatIsThrown.ItemName}."); - } - if (area.GetParent() is Restorative restorative) - { - _gameRepo.OnRestorativePickedUp(restorative); - restorative.QueueFree(); - } + if (area.GetParent() is InventoryItem inventoryItem) + { + var isAdded = Inventory.TryAdd(inventoryItem); + if (isAdded) + { + _gameRepo.AnnounceMessageOnMainScreen($"{inventoryItem.ItemName} picked up."); + inventoryItem.QueueFree(); + } + else + _gameRepo.AnnounceMessageOnMainScreen($"Could not pick up {inventoryItem.ItemName}."); + } + if (area.GetParent() is DroppedItem droppedItem) + { + var isAdded = Inventory.TryAdd(droppedItem.Item); + if (isAdded) + { + _gameRepo.AnnounceMessageOnMainScreen($"{droppedItem.Item.ItemName} picked up."); + droppedItem.QueueFree(); + } + else + _gameRepo.AnnounceMessageOnMainScreen($"Could not pick up {droppedItem.Item.ItemName}."); + } + if (area.GetParent() is ThrownItem thrownItem) + { + var isAdded = Inventory.TryAdd(thrownItem.ItemThatIsThrown); + if (isAdded) + { + _gameRepo.AnnounceMessageOnMainScreen($"{thrownItem.ItemThatIsThrown.ItemName} picked up."); + thrownItem.QueueFree(); + } + else + _gameRepo.AnnounceMessageOnMainScreen($"Could not pick up {thrownItem.ItemThatIsThrown.ItemName}."); + } + if (area.GetParent() is Restorative restorative) + { + _gameRepo.OnRestorativePickedUp(restorative); + restorative.QueueFree(); + } } private PlayerStats InitializePlayerStats() { - var playerStats = new PlayerStats( - currentHP: new AutoProp(_playerStatResource.CurrentHP), - maximumHP: new AutoProp(_playerStatResource.MaximumHP), - currentVT: new AutoProp(_playerStatResource.CurrentVT), - maximumVT: new AutoProp(_playerStatResource.MaximumVT), - currentAttack: new AutoProp(_playerStatResource.CurrentAttack), - currentDefense: new AutoProp(_playerStatResource.CurrentDefense), - maxAttack: new AutoProp(_playerStatResource.MaxAttack), - maxDefense: new AutoProp(_playerStatResource.MaxDefense), - bonusAttack: new AutoProp(_playerStatResource.BonusAttack), - bonusDefense: new AutoProp(_playerStatResource.BonusDefense), - currentExp: new AutoProp(_playerStatResource.CurrentExp), - expToNextLevel: new AutoProp(_playerStatResource.ExpToNextLevel), - currentLevel: new AutoProp(_playerStatResource.CurrentLevel), - luck: new AutoProp(_playerStatResource.Luck)); + var playerStats = new PlayerStats( + currentHP: new AutoProp(_playerStatResource.CurrentHP), + maximumHP: new AutoProp(_playerStatResource.MaximumHP), + currentVT: new AutoProp(_playerStatResource.CurrentVT), + maximumVT: new AutoProp(_playerStatResource.MaximumVT), + currentAttack: new AutoProp(_playerStatResource.CurrentAttack), + currentDefense: new AutoProp(_playerStatResource.CurrentDefense), + maxAttack: new AutoProp(_playerStatResource.MaxAttack), + maxDefense: new AutoProp(_playerStatResource.MaxDefense), + bonusAttack: new AutoProp(_playerStatResource.BonusAttack), + bonusDefense: new AutoProp(_playerStatResource.BonusDefense), + currentExp: new AutoProp(_playerStatResource.CurrentExp), + expToNextLevel: new AutoProp(_playerStatResource.ExpToNextLevel), + currentLevel: new AutoProp(_playerStatResource.CurrentLevel), + luck: new AutoProp(_playerStatResource.Luck)); - return playerStats; + return playerStats; } private bool PlayerIsHittingGeometry() { - var collisions = WallCheck.GetCollidingBodies(); - return collisions.Count > 0; + var collisions = WallCheck.GetCollidingBodies(); + return collisions.Count > 0; } private void WallCheck_BodyEntered(Node body) { - PlayerLogic.Input(new PlayerLogic.Input.AttackAnimationFinished()); - GD.Print("Hit wall"); - AnimationPlayer.Stop(); + PlayerLogic.Input(new PlayerLogic.Input.AttackAnimationFinished()); + GD.Print("Hit wall"); + AnimationPlayer.Stop(); } } diff --git a/Zennysoft.Game.Ma/src/player/Player.tscn b/Zennysoft.Game.Ma/src/player/Player.tscn index 8cb663c5..04b8e969 100644 --- a/Zennysoft.Game.Ma/src/player/Player.tscn +++ b/Zennysoft.Game.Ma/src/player/Player.tscn @@ -1,8 +1,7 @@ -[gd_scene load_steps=48 format=3 uid="uid://cfecvvav8kkp6"] +[gd_scene load_steps=47 format=3 uid="uid://cfecvvav8kkp6"] [ext_resource type="Script" uid="uid://yxmiqy7i0t7r" path="res://src/player/Player.cs" id="1_xcol5"] [ext_resource type="Script" uid="uid://s6ku2kyc4rbk" path="res://src/player/PlayerStatResource.cs" id="2_ebyyx"] -[ext_resource type="Script" uid="uid://6edayafleq8y" path="res://src/hitbox/Hitbox.cs" id="2_lb3qc"] [ext_resource type="Texture2D" uid="uid://c6r3dhnkuw22w" path="res://src/vfx/hit_effects/FIRE_STRIKE_1.0.png" id="5_wr6lo"] [ext_resource type="Texture2D" uid="uid://b5qjlbcesth53" path="res://src/vfx/Weapon Strikes/NON ELEMENTAL SLASH.png" id="6_p34sl"] [ext_resource type="Texture2D" uid="uid://mjobx7ph7hf1" path="res://src/vfx/playerdot.png" id="7_8hi2n"] @@ -355,6 +354,7 @@ animations = [{ }] [sub_resource type="BoxShape3D" id="BoxShape3D_wedu3"] +size = Vector3(1, 1, 1.80176) [sub_resource type="BoxShape3D" id="BoxShape3D_hs4wf"] size = Vector3(1.94531, 3.38623, 1.50671) @@ -437,10 +437,9 @@ unique_name_in_owner = true transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.12691, -1) collision_layer = 2048 collision_mask = 2048 -script = ExtResource("2_lb3qc") [node name="HitboxCollision" type="CollisionShape3D" parent="Collision/Hitbox"] -transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 0.989787) +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -0.177762) shape = SubResource("BoxShape3D_wedu3") disabled = true diff --git a/Zennysoft.Game.Ma/src/system/stats/AttackComponent.cs b/Zennysoft.Game.Ma/src/system/stats/AttackComponent.cs new file mode 100644 index 00000000..9ccff00c --- /dev/null +++ b/Zennysoft.Game.Ma/src/system/stats/AttackComponent.cs @@ -0,0 +1,25 @@ +using Chickensoft.AutoInject; +using Chickensoft.Collections; +using Chickensoft.Introspection; +using Godot; + +namespace Zennysoft.Game.Ma; + +[Meta(typeof(IAutoNode))] +public partial class AttackComponent : Node +{ + public override void _Notification(int what) => this.Notify(what); + + [Export] + public int InitialMaximumAttack { get; set; } + + public AutoProp MaximumAttack { get; private set; } + + public AutoProp CurrentAttack { get; private set; } + + public void OnReady() + { + MaximumAttack = new AutoProp(InitialMaximumAttack); + CurrentAttack = new AutoProp(InitialMaximumAttack); + } +} diff --git a/Zennysoft.Game.Ma/src/system/stats/AttackComponent.cs.uid b/Zennysoft.Game.Ma/src/system/stats/AttackComponent.cs.uid new file mode 100644 index 00000000..06bb66ce --- /dev/null +++ b/Zennysoft.Game.Ma/src/system/stats/AttackComponent.cs.uid @@ -0,0 +1 @@ +uid://dvt65kjs85ro diff --git a/Zennysoft.Game.Ma/src/system/stats/HealthComponent.cs b/Zennysoft.Game.Ma/src/system/stats/HealthComponent.cs new file mode 100644 index 00000000..b955b1cc --- /dev/null +++ b/Zennysoft.Game.Ma/src/system/stats/HealthComponent.cs @@ -0,0 +1,39 @@ +using Chickensoft.AutoInject; +using Chickensoft.Collections; +using Chickensoft.Introspection; +using Godot; + +namespace Zennysoft.Game.Ma; + +[Meta(typeof(IAutoNode))] +public partial class HealthComponent : Node +{ + public override void _Notification(int what) => this.Notify(what); + + [Export] + public int InitialMaximumHP { get; set; } + + public AutoProp MaximumHP { get; private set; } + + public AutoProp CurrentHP { get; private set; } + + [Signal] + public delegate void HealthReachedZeroEventHandler(); + [Signal] + public delegate void HealthLoweredEventHandler(); + + public void OnReady() + { + MaximumHP = new AutoProp(InitialMaximumHP); + CurrentHP = new AutoProp(InitialMaximumHP); + CurrentHP.Changed += CurrentHP_Changed; + } + + private void CurrentHP_Changed(int obj) + { + if (obj <= 0) + EmitSignal(SignalName.HealthReachedZero); + else + EmitSignal(SignalName.HealthLowered); + } +} diff --git a/Zennysoft.Game.Ma/src/system/stats/HealthComponent.cs.uid b/Zennysoft.Game.Ma/src/system/stats/HealthComponent.cs.uid new file mode 100644 index 00000000..4eb9428f --- /dev/null +++ b/Zennysoft.Game.Ma/src/system/stats/HealthComponent.cs.uid @@ -0,0 +1 @@ +uid://bne4tlmh1i5e8 diff --git a/Zennysoft.Game.Ma/src/system/stats/HealthComponent.tscn b/Zennysoft.Game.Ma/src/system/stats/HealthComponent.tscn new file mode 100644 index 00000000..b38f2861 --- /dev/null +++ b/Zennysoft.Game.Ma/src/system/stats/HealthComponent.tscn @@ -0,0 +1,6 @@ +[gd_scene load_steps=2 format=3 uid="uid://bxymnqkoi78oa"] + +[ext_resource type="Script" uid="uid://bne4tlmh1i5e8" path="res://src/system/stats/HealthComponent.cs" id="1_177fp"] + +[node name="HealthComponent" type="Node"] +script = ExtResource("1_177fp") diff --git a/Zennysoft.Game.Ma/src/system/stats/ICharacterStats.cs b/Zennysoft.Game.Ma/src/system/stats/ICharacterStats.cs index 482d9f79..ab5fdbd1 100644 --- a/Zennysoft.Game.Ma/src/system/stats/ICharacterStats.cs +++ b/Zennysoft.Game.Ma/src/system/stats/ICharacterStats.cs @@ -4,9 +4,7 @@ namespace Zennysoft.Game.Ma; public interface ICharacterStats { - public IAutoProp CurrentHP { get; } - - public IAutoProp MaximumHP { get; } + public IAutoProp HP { get; } public IAutoProp CurrentAttack { get; }