Rework enemy behavior (still in progress but shouldn't crash)
This commit is contained in:
5
Zennysoft.Game.Abstractions/Entity/IBehavior.cs
Normal file
5
Zennysoft.Game.Abstractions/Entity/IBehavior.cs
Normal file
@@ -0,0 +1,5 @@
|
||||
namespace Zennysoft.Game.Abstractions.Entity;
|
||||
|
||||
public interface IBehavior
|
||||
{
|
||||
}
|
||||
11
Zennysoft.Game.Abstractions/Entity/IEntity.cs
Normal file
11
Zennysoft.Game.Abstractions/Entity/IEntity.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace Zennysoft.Game.Abstractions.Entity
|
||||
{
|
||||
public interface IAction
|
||||
{
|
||||
public Task PerformAction();
|
||||
}
|
||||
public interface IAction<T>
|
||||
{
|
||||
public Task PerformAction(T arg);
|
||||
}
|
||||
}
|
||||
8
Zennysoft.Game.Abstractions/Entity/IHealthComponent.cs
Normal file
8
Zennysoft.Game.Abstractions/Entity/IHealthComponent.cs
Normal file
@@ -0,0 +1,8 @@
|
||||
namespace Zennysoft.Game.Abstractions;
|
||||
|
||||
public interface IHealthComponent
|
||||
{
|
||||
int MaximumHP { get; }
|
||||
|
||||
int CurrentHP { get; set; }
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
namespace Zennysoft.Game.Abstractions;
|
||||
|
||||
public interface ICanPatrol
|
||||
{
|
||||
public void Patrol();
|
||||
}
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
uid://ci7va4hsq6hyt
|
||||
@@ -0,0 +1 @@
|
||||
uid://87d8kluait8y
|
||||
@@ -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
|
||||
3
Zennysoft.Game.Ma.Implementation/Entity/Damage.cs
Normal file
3
Zennysoft.Game.Ma.Implementation/Entity/Damage.cs
Normal file
@@ -0,0 +1,3 @@
|
||||
namespace Zennysoft.Ma.Adapter;
|
||||
|
||||
public record Damage(int BaseDamage, ElementType ElementType, bool IsCriticalHit, bool IgnoreDefense, bool IgnoreElementalResistance);
|
||||
29
Zennysoft.Game.Ma.Implementation/Entity/IEnemy.cs
Normal file
29
Zennysoft.Game.Ma.Implementation/Entity/IEnemy.cs
Normal file
@@ -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<IDungeonRoom> dungeonRooms);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
using Godot;
|
||||
|
||||
namespace Zennysoft.Ma.Adapter.Entity
|
||||
{
|
||||
public interface IKnockbackable
|
||||
{
|
||||
void Knockback(float impulse, Vector3 direction);
|
||||
}
|
||||
}
|
||||
@@ -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")]
|
||||
|
||||
@@ -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
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -8,11 +8,8 @@
|
||||
|
||||
<ItemGroup>
|
||||
<Compile Remove="Game\state\states\**" />
|
||||
<Compile Remove="Map\**" />
|
||||
<EmbeddedResource Remove="Game\state\states\**" />
|
||||
<EmbeddedResource Remove="Map\**" />
|
||||
<None Remove="Game\state\states\**" />
|
||||
<None Remove="Map\**" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
@@ -31,4 +28,8 @@
|
||||
<ProjectReference Include="..\Zennysoft.Game.Godot.Implementation\Zennysoft.Game.Implementation.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Actions\" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@@ -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]
|
||||
|
||||
|
||||
@@ -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<IEnemyLogic>
|
||||
public partial class BossTypeA : CharacterBody3D
|
||||
{
|
||||
public override void _Notification(int what) => this.Notify(what);
|
||||
|
||||
protected IEnemyLogic _enemyLogic { get; set; } = default!;
|
||||
|
||||
IEnemyLogic IProvide<IEnemyLogic>.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<double> 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<double>(_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<Action>() { 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();
|
||||
}
|
||||
|
||||
@@ -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<IEnemyLogic>
|
||||
public abstract partial class Enemy : CharacterBody3D, IEnemy, IProvide<IEnemyLogic>
|
||||
{
|
||||
#region Registration
|
||||
public override void _Notification(int what) => this.Notify(what);
|
||||
@@ -21,50 +22,30 @@ public partial class Enemy : CharacterBody3D, IEnemy, IProvide<IEnemyLogic>
|
||||
#endregion
|
||||
|
||||
#region Dependencies
|
||||
|
||||
[Dependency] IGame Game => this.DependOn<IGame>();
|
||||
|
||||
[Dependency] protected IPlayer _player => this.DependOn(() => GetParent().GetChildren().OfType<IPlayer>().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<double> 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<IEnemyLogic>
|
||||
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<double>(_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<IDungeonRoom> 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<IEnemyLogic>
|
||||
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<IEnemyLogic>
|
||||
{
|
||||
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();
|
||||
|
||||
48
Zennysoft.Game.Ma/src/enemy/Enemy2D.cs
Normal file
48
Zennysoft.Game.Ma/src/enemy/Enemy2D.cs
Normal file
@@ -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);
|
||||
}
|
||||
1
Zennysoft.Game.Ma/src/enemy/Enemy2D.cs.uid
Normal file
1
Zennysoft.Game.Ma/src/enemy/Enemy2D.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://d17jxdbqesmvg
|
||||
14
Zennysoft.Game.Ma/src/enemy/Enemy3D.cs
Normal file
14
Zennysoft.Game.Ma/src/enemy/Enemy3D.cs
Normal file
@@ -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!;
|
||||
}
|
||||
1
Zennysoft.Game.Ma/src/enemy/Enemy3D.cs.uid
Normal file
1
Zennysoft.Game.Ma/src/enemy/Enemy3D.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://bdq3pbyg8pifw
|
||||
24
Zennysoft.Game.Ma/src/enemy/EnemyModelView.cs
Normal file
24
Zennysoft.Game.Ma/src/enemy/EnemyModelView.cs
Normal file
@@ -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();
|
||||
|
||||
}
|
||||
1
Zennysoft.Game.Ma/src/enemy/EnemyModelView.cs.uid
Normal file
1
Zennysoft.Game.Ma/src/enemy/EnemyModelView.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://6ihdadepmsx2
|
||||
@@ -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<Shader>(shaderPath);
|
||||
var sprites = FindChildren("*", "AnimatedSprite2D", true).Cast<AnimatedSprite2D>();
|
||||
foreach (var sprite in sprites)
|
||||
{
|
||||
sprite.Material = new ShaderMaterial();
|
||||
var shaderMaterial = (ShaderMaterial)sprite.Material;
|
||||
shaderMaterial.Shader = shader;
|
||||
}
|
||||
var shader = GD.Load<Shader>(shaderPath);
|
||||
var sprites = FindChildren("*", "AnimatedSprite2D", true).Cast<AnimatedSprite2D>();
|
||||
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<AnimatedSprite2D>();
|
||||
foreach (var sprite in sprites)
|
||||
{
|
||||
var shaderMaterial = (ShaderMaterial)sprite.Material;
|
||||
shaderMaterial.SetShaderParameter("progress", shaderValue);
|
||||
}
|
||||
var sprites = FindChildren("*", "AnimatedSprite2D", true).Cast<AnimatedSprite2D>();
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<AnimationNodeStateMachinePlayback>().Travel(PRIMARY_ATTACK);
|
||||
AnimationTree.Get(PARAMETERS_PLAYBACK).As<AnimationNodeStateMachinePlayback>().Travel(PRIMARY_ATTACK);
|
||||
}
|
||||
|
||||
public void PlaySecondaryAttackAnimation()
|
||||
{
|
||||
AnimationTree.Get(PARAMETERS_PLAYBACK).As<AnimationNodeStateMachinePlayback>().Travel(SECONDARY_ATTACK);
|
||||
AnimationTree.Get(PARAMETERS_PLAYBACK).As<AnimationNodeStateMachinePlayback>().Travel(SECONDARY_ATTACK);
|
||||
}
|
||||
|
||||
public void PlayPrimarySkillAnimation()
|
||||
{
|
||||
AnimationTree.Get(PARAMETERS_PLAYBACK).As<AnimationNodeStateMachinePlayback>().Travel(PRIMARY_SKILL);
|
||||
AnimationTree.Get(PARAMETERS_PLAYBACK).As<AnimationNodeStateMachinePlayback>().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<AnimationNodeStateMachinePlayback>().Travel(WALK);
|
||||
AnimationTree.Get(PARAMETERS_PLAYBACK).As<AnimationNodeStateMachinePlayback>().Travel(WALK);
|
||||
}
|
||||
|
||||
public void PlayActivateAnimation() => AnimationTree.Get(PARAMETERS_PLAYBACK).As<AnimationNodeStateMachinePlayback>().Travel("Activate");
|
||||
|
||||
public void PlayIdleAnimation()
|
||||
{
|
||||
AnimationTree.Get(PARAMETERS_PLAYBACK).As<AnimationNodeStateMachinePlayback>().Travel(IDLE);
|
||||
AnimationTree.Get(PARAMETERS_PLAYBACK).As<AnimationNodeStateMachinePlayback>().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<ShaderMaterial>(shaderPath);
|
||||
MeshInstance.MaterialOverride = shader;
|
||||
var shader = GD.Load<ShaderMaterial>(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<AnimationNodeStateMachinePlayback>().Stop();
|
||||
AnimationTree.Get(PARAMETERS_PLAYBACK).As<AnimationNodeStateMachinePlayback>().Stop();
|
||||
}
|
||||
|
||||
public void SetCurrentDirection(Basis enemyBasis, Vector3 cameraDirection)
|
||||
|
||||
@@ -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; }
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -1,6 +0,0 @@
|
||||
namespace Zennysoft.Game.Ma;
|
||||
|
||||
public interface ICanActivate
|
||||
{
|
||||
public void Activate();
|
||||
}
|
||||
@@ -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<double> 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();
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -1,8 +0,0 @@
|
||||
using Godot;
|
||||
|
||||
namespace Zennysoft.Game.Ma;
|
||||
|
||||
public interface IKnockbackable
|
||||
{
|
||||
public void Knockback(float impulse, Vector3 direction);
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
using Godot;
|
||||
|
||||
namespace Zennysoft.Game.Ma;
|
||||
|
||||
public interface INavigationAgentClient
|
||||
{
|
||||
public Vector3 Velocity { get; }
|
||||
|
||||
public void SetTarget(Vector3 target);
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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
|
||||
1
Zennysoft.Game.Ma/src/enemy/PatrolComponent.cs.uid
Normal file
1
Zennysoft.Game.Ma/src/enemy/PatrolComponent.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://vjb6sjktj6m0
|
||||
13
Zennysoft.Game.Ma/src/enemy/PatrolComponent.tscn
Normal file
13
Zennysoft.Game.Ma/src/enemy/PatrolComponent.tscn
Normal file
@@ -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
|
||||
1
Zennysoft.Game.Ma/src/enemy/PatrolTypeEnemy.cs.uid
Normal file
1
Zennysoft.Game.Ma/src/enemy/PatrolTypeEnemy.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dssu6tgi8dapq
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://bbe5nt3kpvk0f
|
||||
@@ -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")
|
||||
57
Zennysoft.Game.Ma/src/enemy/behaviors/FollowBehavior.cs
Normal file
57
Zennysoft.Game.Ma/src/enemy/behaviors/FollowBehavior.cs
Normal file
@@ -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<IPlayer>().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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://chfhmralfmwva
|
||||
@@ -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")
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Zennysoft.Game.Ma;
|
||||
|
||||
public interface IEngagePlayerBehavior
|
||||
{
|
||||
public void Engage();
|
||||
|
||||
public void Disengage();
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://dnl6yl3c7f2xs
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Zennysoft.Game.Ma;
|
||||
|
||||
public interface IHaveEngagePlayerBehavior
|
||||
{
|
||||
public EngagePlayerBehavior EngagePlayerBehavior { get; }
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://wiktkwsvcf38
|
||||
10
Zennysoft.Game.Ma/src/enemy/behaviors/IHaveFollowBehavior.cs
Normal file
10
Zennysoft.Game.Ma/src/enemy/behaviors/IHaveFollowBehavior.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using Godot;
|
||||
|
||||
namespace Zennysoft.Game.Ma;
|
||||
|
||||
public interface IHaveFollowBehavior
|
||||
{
|
||||
public FollowBehavior FollowBehavior { get; }
|
||||
|
||||
public NavigationAgent3D NavigationAgent { get; }
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://j73uc3svvwj
|
||||
@@ -0,0 +1,6 @@
|
||||
namespace Zennysoft.Game.Ma;
|
||||
|
||||
public interface IHavePatrolBehavior
|
||||
{
|
||||
public PatrolBehavior PatrolBehavior { get; }
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://dxepau20ove64
|
||||
87
Zennysoft.Game.Ma/src/enemy/behaviors/PatrolBehavior.cs
Normal file
87
Zennysoft.Game.Ma/src/enemy/behaviors/PatrolBehavior.cs
Normal file
@@ -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();
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://hpb1f5r17k5y
|
||||
@@ -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")
|
||||
@@ -0,0 +1 @@
|
||||
uid://cjw2a3wwyc4p0
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +1 @@
|
||||
uid://dssu6tgi8dapq
|
||||
uid://cq6b4ma3sy1en
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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<Action>() { 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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<Action>() { 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<Action>() { 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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<Action>() { 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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<Action>() { 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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<Action>() { PrimaryAttack, SecondaryAttack, TertiaryAttack };
|
||||
|
||||
@@ -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<Action>() { 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));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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"]
|
||||
|
||||
@@ -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<Action>() { 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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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<double> 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<double>(_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<IDungeonRoom> roomList) => throw new NotImplementedException();
|
||||
|
||||
public async void Die()
|
||||
{
|
||||
CurrentHP.OnCompleted();
|
||||
EnemyModelView.PlayDeathAnimation();
|
||||
await ToSignal(GetTree().CreateTimer(0.8f), "timeout");
|
||||
CallDeferred(MethodName.QueueFree);
|
||||
|
||||
@@ -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));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
uid://8f4alhh2ubvg
|
||||
@@ -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
|
||||
|
||||
@@ -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!;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user