Revamp boss logic
@@ -30,6 +30,8 @@
|
||||
<PackageReference Include="Zeroconf" Version="3.7.16" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="src\enemy\enemy_types\14. horse_head\" />
|
||||
<Folder Include="src\enemy\enemy_types\15. ox_face\" />
|
||||
<Folder Include="src\ui\dialogue\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
|
||||
@@ -1,163 +0,0 @@
|
||||
using Chickensoft.AutoInject;
|
||||
using Chickensoft.Collections;
|
||||
using Chickensoft.GodotNodeInterfaces;
|
||||
using Chickensoft.Introspection;
|
||||
using Godot;
|
||||
using Zennysoft.Ma.Adapter;
|
||||
|
||||
namespace Zennysoft.Game.Ma;
|
||||
|
||||
public interface IBoss : ICharacterBody3D
|
||||
{
|
||||
public void Activate();
|
||||
|
||||
public void TakeDamage(double damage, ElementType elementType = ElementType.None, bool isCriticalHit = false, bool ignoreDefense = false, bool ignoreElementalResistance = false);
|
||||
|
||||
public void MoveToLocation(Vector3 target, float delta);
|
||||
|
||||
public void StartAttackTimer();
|
||||
|
||||
public void StopAttackTimer();
|
||||
|
||||
public double CurrentHP { get; }
|
||||
|
||||
public AutoProp<bool> IsDefeated { get; }
|
||||
}
|
||||
|
||||
[Meta(typeof(IAutoNode))]
|
||||
public partial class Boss : CharacterBody3D, IBoss, IProvide<IBossLogic>
|
||||
{
|
||||
public override void _Notification(int what) => this.Notify(what);
|
||||
|
||||
public double CurrentHP => _currentHP.Value;
|
||||
|
||||
public AutoProp<bool> IsDefeated { get; set; }
|
||||
|
||||
private AutoProp<double> _currentHP { get; set; }
|
||||
|
||||
#region Autoinject
|
||||
protected IBossLogic _bossLogic { get; set; } = default!;
|
||||
|
||||
IBossLogic IProvide<IBossLogic>.Value() => _bossLogic;
|
||||
|
||||
public BossLogic.IBinding BossBinding { get; set; } = default!;
|
||||
#endregion
|
||||
|
||||
#region Export
|
||||
[Export] private EnemyStatResource _bossStatResource { get; set; } = default!;
|
||||
#endregion
|
||||
|
||||
#region Dependencies
|
||||
[Dependency] private IPlayer _player => this.DependOn<IPlayer>();
|
||||
#endregion
|
||||
|
||||
#region Nodes
|
||||
[Node] private AnimationTree _animationTree { get; set; } = default!;
|
||||
|
||||
[Node] private Timer _attackTimer { get; set; } = default!;
|
||||
|
||||
[Node] private AnimationPlayer _hitAnimation { get; set; } = default!;
|
||||
|
||||
[Node] private Area3D _hitbox { get; set; } = default!;
|
||||
|
||||
[Node] private Area3D _attackBox { get; set; } = default!;
|
||||
|
||||
[Node] private Area3D _secondaryAttackBox { get; set; } = default!;
|
||||
#endregion
|
||||
|
||||
public void Setup()
|
||||
{
|
||||
_bossLogic = new BossLogic();
|
||||
_bossLogic.Set(this as IBoss);
|
||||
_bossLogic.Set(_player);
|
||||
|
||||
SetPhysicsProcess(false);
|
||||
Hide();
|
||||
}
|
||||
|
||||
public void OnResolved()
|
||||
{
|
||||
BossBinding = _bossLogic.Bind();
|
||||
BossBinding
|
||||
.Handle((in BossLogic.Output.Defeated output) =>
|
||||
{
|
||||
_hitAnimation.Play("Defeated");
|
||||
});
|
||||
this.Provide();
|
||||
_bossLogic.Start();
|
||||
_currentHP = new AutoProp<double>(_bossStatResource.MaximumHP);
|
||||
_currentHP.Sync += OnHPChanged;
|
||||
_attackTimer.Timeout += AttackTimer_Timeout;
|
||||
}
|
||||
|
||||
public void Activate()
|
||||
{
|
||||
_bossLogic.Input(new BossLogic.Input.Activate());
|
||||
}
|
||||
|
||||
public void TakeDamage(double damage, ElementType elementType, bool isCriticalHit = false, bool ignoreDefense = false, bool ignoreElementalResistance = false)
|
||||
{
|
||||
if (_currentHP.Value > 0)
|
||||
{
|
||||
if (!ignoreElementalResistance)
|
||||
damage = CalculateElementalResistance(damage, elementType);
|
||||
if (!ignoreDefense)
|
||||
damage = CalculateDefenseResistance(damage);
|
||||
if (isCriticalHit)
|
||||
damage *= 2;
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
public void MoveToLocation(Vector3 target, float delta) => throw new System.NotImplementedException();
|
||||
|
||||
public void StartAttackTimer() => _attackTimer.Start();
|
||||
|
||||
public void StopAttackTimer() => _attackTimer.Stop();
|
||||
|
||||
private void AttackTimer_Timeout() => _bossLogic.Input(new BossLogic.Input.AttackTimer());
|
||||
|
||||
public virtual void TakeAction()
|
||||
{
|
||||
}
|
||||
|
||||
public void OnPhysicsProcess(double delta)
|
||||
{
|
||||
_bossLogic.Input(new BossLogic.Input.PhysicsTick(delta));
|
||||
MoveAndSlide();
|
||||
}
|
||||
|
||||
private void OnHPChanged(double newHP)
|
||||
{
|
||||
if (newHP <= 0)
|
||||
_bossLogic.Input(new BossLogic.Input.BossDefeated());
|
||||
}
|
||||
|
||||
private double CalculateElementalResistance(double incomingDamage, ElementType incomingElementType)
|
||||
{
|
||||
if (incomingElementType == ElementType.Aeolic)
|
||||
return Mathf.Max(incomingDamage - (incomingDamage * _bossStatResource.AeolicResistance), 0.0);
|
||||
if (incomingElementType == ElementType.Hydric)
|
||||
return Mathf.Max(incomingDamage - (incomingDamage * _bossStatResource.HydricResistance), 0.0);
|
||||
if (incomingElementType == ElementType.Igneous)
|
||||
return Mathf.Max(incomingDamage - (incomingDamage * _bossStatResource.IgneousResistance), 0.0);
|
||||
if (incomingElementType == ElementType.Ferrum)
|
||||
return Mathf.Max(incomingDamage - (incomingDamage * _bossStatResource.FerrumResistance), 0.0);
|
||||
if (incomingElementType == ElementType.Telluric)
|
||||
return Mathf.Max(incomingDamage - (incomingDamage * _bossStatResource.TelluricResistance), 0.0);
|
||||
|
||||
return Mathf.Max(incomingDamage, 0.0);
|
||||
}
|
||||
|
||||
private double CalculateDefenseResistance(double incomingDamage)
|
||||
{
|
||||
return Mathf.Max(incomingDamage - _bossStatResource.CurrentDefense, 0.0);
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
uid://dwpswg0xufxa7
|
||||
@@ -1,19 +0,0 @@
|
||||
namespace Zennysoft.Game.Ma;
|
||||
|
||||
public partial class BossLogic
|
||||
{
|
||||
public static class Input
|
||||
{
|
||||
public readonly record struct Activate;
|
||||
|
||||
public readonly record struct PhysicsTick(double Delta);
|
||||
|
||||
public readonly record struct StopMoving;
|
||||
|
||||
public readonly record struct AttackTimer;
|
||||
|
||||
public readonly record struct StartAttacking;
|
||||
|
||||
public readonly record struct BossDefeated;
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
uid://cc1wadksmbq6h
|
||||
@@ -1,17 +0,0 @@
|
||||
using Godot;
|
||||
|
||||
namespace Zennysoft.Game.Ma;
|
||||
|
||||
public partial class BossLogic
|
||||
{
|
||||
public static class Output
|
||||
{
|
||||
public readonly record struct MoveTowardsPlayer(Vector3 TargetPosition);
|
||||
|
||||
public readonly record struct MovementComputed(Vector3 LinearVelocity);
|
||||
|
||||
public readonly record struct TakeAction;
|
||||
|
||||
public readonly record struct Defeated;
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
uid://c7r8l4w4nwd8x
|
||||
@@ -1,15 +0,0 @@
|
||||
using Chickensoft.Introspection;
|
||||
using Chickensoft.LogicBlocks;
|
||||
|
||||
namespace Zennysoft.Game.Ma;
|
||||
|
||||
public partial class BossLogic
|
||||
{
|
||||
[Meta]
|
||||
public abstract partial record State : StateLogic<State>
|
||||
{
|
||||
protected State()
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
uid://ds1hjuenunrht
|
||||
@@ -1,13 +0,0 @@
|
||||
using Chickensoft.Introspection;
|
||||
using Chickensoft.LogicBlocks;
|
||||
|
||||
namespace Zennysoft.Game.Ma;
|
||||
|
||||
public interface IBossLogic : ILogicBlock<BossLogic.State>;
|
||||
|
||||
[Meta, Id("boss_logic")]
|
||||
[LogicBlock(typeof(State), Diagram = true)]
|
||||
public partial class BossLogic : LogicBlock<BossLogic.State>, IBossLogic
|
||||
{
|
||||
public override Transition GetInitialState() => To<State.Unactivated>();
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
uid://dnpj001c3iabh
|
||||
@@ -1,29 +0,0 @@
|
||||
@startuml BossLogic
|
||||
state "BossLogic State" as GameJamDungeon_BossLogic_State {
|
||||
state "Defeated" as GameJamDungeon_BossLogic_State_Defeated
|
||||
state "Alive" as GameJamDungeon_BossLogic_State_Alive {
|
||||
state "Idle" as GameJamDungeon_BossLogic_State_Idle
|
||||
state "EngagePlayer" as GameJamDungeon_BossLogic_State_EngagePlayer
|
||||
state "ApproachPlayer" as GameJamDungeon_BossLogic_State_ApproachPlayer
|
||||
state "Activated" as GameJamDungeon_BossLogic_State_Activated {
|
||||
state "Attacking" as GameJamDungeon_BossLogic_State_Attacking
|
||||
state "FollowPlayer" as GameJamDungeon_BossLogic_State_FollowPlayer
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
GameJamDungeon_BossLogic_State_Alive --> GameJamDungeon_BossLogic_State_Defeated : BossDefeated
|
||||
GameJamDungeon_BossLogic_State_ApproachPlayer --> GameJamDungeon_BossLogic_State_ApproachPlayer : PhysicsTick
|
||||
GameJamDungeon_BossLogic_State_ApproachPlayer --> GameJamDungeon_BossLogic_State_EngagePlayer : PhysicsTick
|
||||
GameJamDungeon_BossLogic_State_EngagePlayer --> GameJamDungeon_BossLogic_State_ApproachPlayer : PhysicsTick
|
||||
GameJamDungeon_BossLogic_State_EngagePlayer --> GameJamDungeon_BossLogic_State_EngagePlayer : PhysicsTick
|
||||
GameJamDungeon_BossLogic_State_EngagePlayer --> GameJamDungeon_BossLogic_State_EngagePlayer : PrimaryAttack
|
||||
GameJamDungeon_BossLogic_State_EngagePlayer --> GameJamDungeon_BossLogic_State_EngagePlayer : SecondaryAttack
|
||||
GameJamDungeon_BossLogic_State_FollowPlayer --> GameJamDungeon_BossLogic_State_FollowPlayer : PhysicsTick
|
||||
GameJamDungeon_BossLogic_State_Idle --> GameJamDungeon_BossLogic_State_ApproachPlayer : Activate
|
||||
|
||||
GameJamDungeon_BossLogic_State : On() → Defeated
|
||||
GameJamDungeon_BossLogic_State_Alive : OnBossDefeated → Defeated
|
||||
|
||||
[*] --> GameJamDungeon_BossLogic_State_Idle
|
||||
@enduml
|
||||
@@ -1,14 +0,0 @@
|
||||
using Chickensoft.Introspection;
|
||||
|
||||
namespace Zennysoft.Game.Ma;
|
||||
|
||||
public partial class BossLogic
|
||||
{
|
||||
public partial record State
|
||||
{
|
||||
[Meta, Id("boss_logic_state_activated")]
|
||||
public partial record Activated : Alive
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
uid://dk60nmw42pm82
|
||||
@@ -1,37 +0,0 @@
|
||||
using Chickensoft.Introspection;
|
||||
using Godot;
|
||||
|
||||
namespace Zennysoft.Game.Ma;
|
||||
|
||||
public partial class BossLogic
|
||||
{
|
||||
public partial record State
|
||||
{
|
||||
[Meta, Id("boss_logic_state_alive")]
|
||||
public abstract partial record Alive : State, IGet<Input.BossDefeated>, IGet<Input.AttackTimer>, IGet<Input.StopMoving>, IGet<Input.StartAttacking>
|
||||
{
|
||||
}
|
||||
|
||||
public Transition On(in Input.AttackTimer input)
|
||||
{
|
||||
Output(new Output.TakeAction());
|
||||
return To<Attacking>();
|
||||
}
|
||||
|
||||
public Transition On(in Input.BossDefeated input)
|
||||
{
|
||||
Output(new Output.Defeated());
|
||||
return To<Defeated>();
|
||||
}
|
||||
|
||||
public Transition On(in Input.StopMoving input)
|
||||
{
|
||||
return To<Idle>();
|
||||
}
|
||||
|
||||
public Transition On(in Input.StartAttacking input)
|
||||
{
|
||||
return To<Attacking>();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
uid://bbwhocehdcsbt
|
||||
@@ -1,34 +0,0 @@
|
||||
using Chickensoft.Introspection;
|
||||
using Godot;
|
||||
using Zennysoft.Ma.Adapter;
|
||||
|
||||
namespace Zennysoft.Game.Ma;
|
||||
|
||||
public partial class BossLogic
|
||||
{
|
||||
public partial record State
|
||||
{
|
||||
[Meta, Id("boss_logic_state_approach_player")]
|
||||
public partial record ApproachPlayer : Alive, IGet<Input.PhysicsTick>
|
||||
{
|
||||
public Transition On(in Input.PhysicsTick input)
|
||||
{
|
||||
var boss = Get<IBoss>();
|
||||
var player = Get<IPlayer>();
|
||||
var delta = (float)input.Delta;
|
||||
|
||||
var playerPosition = new Vector3(player.CurrentPosition.X, boss.GlobalPosition.Y, player.CurrentPosition.Z);
|
||||
|
||||
if (boss.GlobalPosition.DistanceTo(player.CurrentPosition) <= 5.0f)
|
||||
return To<EngagePlayer>();
|
||||
|
||||
var moveToward = boss.GlobalPosition.MoveToward(playerPosition, (float)delta * 3f);
|
||||
boss.GlobalPosition = moveToward;
|
||||
|
||||
var targetDirection = boss.GlobalPosition - player.CurrentPosition;
|
||||
boss.GlobalRotation = new Vector3(boss.GlobalRotation.X, Mathf.LerpAngle(boss.GlobalRotation.Y, Mathf.Atan2(-targetDirection.X, -targetDirection.Z), delta * 3f), boss.GlobalRotation.Z);
|
||||
return ToSelf();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
uid://crhdnddatd7ap
|
||||
@@ -1,19 +0,0 @@
|
||||
using Chickensoft.Introspection;
|
||||
|
||||
namespace Zennysoft.Game.Ma;
|
||||
|
||||
public partial class BossLogic
|
||||
{
|
||||
public partial record State
|
||||
{
|
||||
[Meta, Id("boss_logic_state_attacking")]
|
||||
public partial record Attacking : Activated, IGet<Input.StopMoving>
|
||||
{
|
||||
public Attacking()
|
||||
{
|
||||
OnAttach(() => Get<IEnemy>().StartAttackTimer());
|
||||
OnDetach(() => Get<IEnemy>().StopAttackTimer());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
uid://crsw8t5nr4ots
|
||||
@@ -1,14 +0,0 @@
|
||||
using Chickensoft.Introspection;
|
||||
|
||||
namespace Zennysoft.Game.Ma;
|
||||
|
||||
public partial class BossLogic
|
||||
{
|
||||
public partial record State
|
||||
{
|
||||
[Meta, Id("boss_logic_state_defeated")]
|
||||
public partial record Defeated : State
|
||||
{
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
uid://beymhvkyuay4h
|
||||
@@ -1,32 +0,0 @@
|
||||
using Chickensoft.Introspection;
|
||||
using Godot;
|
||||
using Zennysoft.Ma.Adapter;
|
||||
|
||||
namespace Zennysoft.Game.Ma;
|
||||
|
||||
public partial class BossLogic
|
||||
{
|
||||
public partial record State
|
||||
{
|
||||
[Meta, Id("boss_logic_state_engage_player")]
|
||||
public partial record EngagePlayer : Alive, IGet<Input.PhysicsTick>
|
||||
{
|
||||
public EngagePlayer()
|
||||
{
|
||||
}
|
||||
|
||||
public Transition On(in Input.PhysicsTick input)
|
||||
{
|
||||
var boss = Get<IBoss>();
|
||||
var player = Get<IPlayer>();
|
||||
var delta = (float)input.Delta;
|
||||
var targetDirection = boss.GlobalPosition - player.CurrentPosition;
|
||||
boss.GlobalRotation = new Vector3(boss.GlobalRotation.X, Mathf.LerpAngle(boss.GlobalRotation.Y, Mathf.Atan2(-targetDirection.X, -targetDirection.Z), delta * 3f), boss.GlobalRotation.Z);
|
||||
if (boss.GlobalPosition.DistanceTo(player.CurrentPosition) > 5.0f)
|
||||
return To<ApproachPlayer>();
|
||||
|
||||
return ToSelf();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
uid://bvwwpwvlou5gg
|
||||
@@ -1,23 +0,0 @@
|
||||
using Chickensoft.Introspection;
|
||||
using Zennysoft.Ma.Adapter;
|
||||
|
||||
namespace Zennysoft.Game.Ma;
|
||||
|
||||
public partial class BossLogic
|
||||
{
|
||||
public partial record State
|
||||
{
|
||||
[Meta, Id("boss_logic_state_followplayer")]
|
||||
public partial record FollowPlayer : Activated, IGet<Input.PhysicsTick>
|
||||
{
|
||||
public Transition On(in Input.PhysicsTick input)
|
||||
{
|
||||
var enemy = Get<IEnemy>();
|
||||
var player = Get<IPlayer>();
|
||||
var target = player.CurrentPosition;
|
||||
enemy.SetTarget(target);
|
||||
return ToSelf();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
uid://0uou7c2gl6jr
|
||||
@@ -1,28 +0,0 @@
|
||||
using Chickensoft.Introspection;
|
||||
|
||||
namespace Zennysoft.Game.Ma;
|
||||
|
||||
public partial class BossLogic
|
||||
{
|
||||
public partial record State
|
||||
{
|
||||
[Meta, Id("boss_logic_state_idle")]
|
||||
public partial record Idle : Alive, IGet<Input.Activate>
|
||||
{
|
||||
public Idle()
|
||||
{
|
||||
OnDetach(() =>
|
||||
{
|
||||
var boss = Get<IBoss>();
|
||||
boss.SetPhysicsProcess(true);
|
||||
boss.Show();
|
||||
}
|
||||
);
|
||||
}
|
||||
public Transition On(in Input.Activate input)
|
||||
{
|
||||
return To<ApproachPlayer>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
uid://h4f0q5o6nolw
|
||||
@@ -1,18 +0,0 @@
|
||||
using Chickensoft.Introspection;
|
||||
|
||||
namespace Zennysoft.Game.Ma;
|
||||
|
||||
public partial class BossLogic
|
||||
{
|
||||
public partial record State
|
||||
{
|
||||
[Meta, Id("boss_logic_state_unactivated")]
|
||||
public partial record Unactivated : State, IGet<Input.Activate>
|
||||
{
|
||||
public Transition On(in Input.Activate input)
|
||||
{
|
||||
return To<Activated>();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1 +0,0 @@
|
||||
uid://hns6fpqiyru2
|
||||
@@ -1,59 +0,0 @@
|
||||
[gd_resource type="ShaderMaterial" load_steps=2 format=3 uid="uid://cf3an7cx1vjm6"]
|
||||
|
||||
[sub_resource type="Shader" id="Shader_veoq4"]
|
||||
code = "// NOTE: Shader automatically converted from Godot Engine 4.4.dev1.mono's StandardMaterial3D.
|
||||
|
||||
shader_type spatial;
|
||||
render_mode blend_mix, depth_draw_opaque, cull_back, diffuse_burley, specular_schlick_ggx;
|
||||
|
||||
uniform vec4 albedo : source_color;
|
||||
uniform sampler2D texture_albedo : source_color, filter_linear_mipmap, repeat_enable;
|
||||
uniform float point_size : hint_range(0.1, 128.0, 0.1);
|
||||
|
||||
uniform float roughness : hint_range(0.0, 1.0);
|
||||
uniform sampler2D texture_metallic : hint_default_white, filter_linear_mipmap, repeat_enable;
|
||||
uniform vec4 metallic_texture_channel;
|
||||
uniform sampler2D texture_roughness : hint_roughness_r, filter_linear_mipmap, repeat_enable;
|
||||
|
||||
uniform float specular : hint_range(0.0, 1.0, 0.01);
|
||||
uniform float metallic : hint_range(0.0, 1.0, 0.01);
|
||||
|
||||
uniform vec3 uv1_scale;
|
||||
uniform vec3 uv1_offset;
|
||||
uniform vec3 uv2_scale;
|
||||
uniform vec3 uv2_offset;
|
||||
|
||||
void vertex() {
|
||||
UV = UV * uv1_scale.xy + uv1_offset.xy;
|
||||
}
|
||||
|
||||
void fragment() {
|
||||
vec2 base_uv = UV;
|
||||
|
||||
vec4 albedo_tex = texture(texture_albedo, base_uv);
|
||||
ALBEDO = albedo.rgb * albedo_tex.rgb;
|
||||
|
||||
float metallic_tex = dot(texture(texture_metallic, base_uv), metallic_texture_channel);
|
||||
METALLIC = metallic_tex * metallic;
|
||||
SPECULAR = specular;
|
||||
|
||||
vec4 roughness_texture_channel = vec4(1.0, 0.0, 0.0, 0.0);
|
||||
float roughness_tex = dot(texture(texture_roughness, base_uv), roughness_texture_channel);
|
||||
ROUGHNESS = roughness_tex * roughness;
|
||||
ALPHA *= albedo.a * albedo_tex.a;
|
||||
}
|
||||
"
|
||||
|
||||
[resource]
|
||||
render_priority = 0
|
||||
shader = SubResource("Shader_veoq4")
|
||||
shader_parameter/albedo = Color(0.784314, 0, 0, 0.00784314)
|
||||
shader_parameter/point_size = 1.0
|
||||
shader_parameter/roughness = 1.0
|
||||
shader_parameter/metallic_texture_channel = Vector4(0, 0, 0, 0)
|
||||
shader_parameter/specular = 0.5
|
||||
shader_parameter/metallic = 0.0
|
||||
shader_parameter/uv1_scale = Vector3(1, 1, 1)
|
||||
shader_parameter/uv1_offset = Vector3(0, 0, 0)
|
||||
shader_parameter/uv2_scale = Vector3(1, 1, 1)
|
||||
shader_parameter/uv2_offset = Vector3(0, 0, 0)
|
||||
134
Zennysoft.Game.Ma/src/enemy/BossTypeA.cs
Normal file
@@ -0,0 +1,134 @@
|
||||
using Chickensoft.AutoInject;
|
||||
using Chickensoft.Introspection;
|
||||
using Godot;
|
||||
using System.Collections.Generic;
|
||||
using System;
|
||||
using Zennysoft.Ma.Adapter;
|
||||
|
||||
namespace Zennysoft.Game.Ma;
|
||||
|
||||
[Meta(typeof(IAutoNode))]
|
||||
public partial class BossTypeA : Enemy, IHasPrimaryAttack, IHasSecondaryAttack, ICanActivate
|
||||
{
|
||||
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;
|
||||
|
||||
public ElementType SecondaryAttackElementalType { get; set; } = ElementType.None;
|
||||
|
||||
public double SecondaryAttackElementalDamageBonus { get; set; } = 1.0;
|
||||
|
||||
[Node] public new EnemyModelView3D _enemyModelView { get; set; }
|
||||
|
||||
[Node] public CollisionShape3D EnemyHitbox { get; set; } = default!;
|
||||
|
||||
private Vector3 _target;
|
||||
|
||||
public void OnReady()
|
||||
{
|
||||
_target = GlobalPosition;
|
||||
SetPhysicsProcess(true);
|
||||
_enemyModelView.Hitbox.AreaEntered += Hitbox_AreaEntered;
|
||||
_attackTimer.Start();
|
||||
}
|
||||
|
||||
public void OnPhysicsProcess(double delta)
|
||||
{
|
||||
_enemyLogic.Input(new EnemyLogic.Input.PhysicsTick(delta));
|
||||
|
||||
if (_enemyLogic.Value is not EnemyLogic.State.Activated)
|
||||
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());
|
||||
|
||||
var direction = (_target - GlobalPosition).Normalized();
|
||||
LookAt(direction);
|
||||
|
||||
if (_enemyLogic.Value is EnemyLogic.State.FollowPlayer)
|
||||
{
|
||||
Velocity = direction * _movementSpeed;
|
||||
MoveAndSlide();
|
||||
_enemyModelView.PlayWalkAnimation();
|
||||
}
|
||||
else
|
||||
{
|
||||
_enemyModelView.PlayIdleAnimation();
|
||||
}
|
||||
}
|
||||
|
||||
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 override void StartAttackTimer()
|
||||
{
|
||||
_attackTimer.Timeout += OnAttackTimeout;
|
||||
}
|
||||
|
||||
public override void StopAttackTimer()
|
||||
{
|
||||
_attackTimer.Timeout -= OnAttackTimeout;
|
||||
}
|
||||
|
||||
public override void SetTarget(Vector3 target) => _target = target;
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
|
||||
public void Activate()
|
||||
{
|
||||
Show();
|
||||
_enemyLogic.Input(new EnemyLogic.Input.Alerted());
|
||||
EnemyHitbox.SetDeferred(CollisionShape3D.PropertyName.Disabled, false);
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
public override void _ExitTree()
|
||||
{
|
||||
CurrentHP.OnCompleted();
|
||||
StopAttackTimer();
|
||||
_enemyModelView.Hitbox.AreaEntered -= Hitbox_AreaEntered;
|
||||
}
|
||||
}
|
||||
1
Zennysoft.Game.Ma/src/enemy/BossTypeA.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://dveonnhcxcp08
|
||||
@@ -31,7 +31,7 @@ public partial class Enemy : CharacterBody3D, IEnemy, IProvide<IEnemyLogic>
|
||||
[Export] protected EnemyStatResource _enemyStatResource { get; set; } = default!;
|
||||
|
||||
[Export]
|
||||
private float _movementSpeed = 2f;
|
||||
protected float _movementSpeed = 2f;
|
||||
#endregion
|
||||
|
||||
#region Node Dependencies
|
||||
@@ -39,16 +39,14 @@ public partial class Enemy : CharacterBody3D, IEnemy, IProvide<IEnemyLogic>
|
||||
|
||||
[Node] private Area3D _lineOfSight { get; set; } = default!;
|
||||
|
||||
[Node] private Timer _attackTimer { get; set; } = default!;
|
||||
[Node] protected Timer _attackTimer { get; set; } = default!;
|
||||
|
||||
[Node] private RayCast3D _raycast { get; set; } = default!;
|
||||
|
||||
[Node] protected IEnemyModelView _enemyModelView { get; set; } = default!;
|
||||
#endregion
|
||||
|
||||
public double CurrentHP => _currentHP.Value;
|
||||
|
||||
private AutoProp<double> _currentHP { get; set; }
|
||||
public AutoProp<double> CurrentHP { get; set; }
|
||||
|
||||
private float _knockbackStrength = 0.0f;
|
||||
|
||||
@@ -80,14 +78,14 @@ public partial class Enemy : CharacterBody3D, IEnemy, IProvide<IEnemyLogic>
|
||||
|
||||
_enemyLogic.Start();
|
||||
|
||||
_currentHP = new AutoProp<double>(_enemyStatResource.MaximumHP);
|
||||
_currentHP.Sync += OnHPChanged;
|
||||
CurrentHP = new AutoProp<double>(_enemyStatResource.MaximumHP);
|
||||
CurrentHP.Sync += OnHPChanged;
|
||||
_lineOfSight.BodyEntered += LineOfSight_BodyEntered;
|
||||
}
|
||||
|
||||
public override void _PhysicsProcess(double delta)
|
||||
{
|
||||
if (CurrentHP <= 0)
|
||||
if (CurrentHP.Value <= 0)
|
||||
return;
|
||||
|
||||
var lookDir = GlobalPosition + Velocity;
|
||||
@@ -118,7 +116,7 @@ public partial class Enemy : CharacterBody3D, IEnemy, IProvide<IEnemyLogic>
|
||||
|
||||
public virtual void TakeDamage(double damage, ElementType elementType, bool isCriticalHit = false, bool ignoreDefense = false, bool ignoreElementalResistance = false)
|
||||
{
|
||||
if (_currentHP.Value > 0)
|
||||
if (CurrentHP.Value > 0)
|
||||
{
|
||||
if (!ignoreElementalResistance)
|
||||
damage = CalculateElementalResistance(damage, elementType);
|
||||
@@ -127,10 +125,10 @@ public partial class Enemy : CharacterBody3D, IEnemy, IProvide<IEnemyLogic>
|
||||
if (isCriticalHit)
|
||||
damage *= 2;
|
||||
GD.Print($"Enemy Hit for {damage} damage.");
|
||||
_currentHP.OnNext(_currentHP.Value - damage);
|
||||
GD.Print("Current HP: " + _currentHP.Value);
|
||||
CurrentHP.OnNext(CurrentHP.Value - damage);
|
||||
GD.Print("Current HP: " + CurrentHP.Value);
|
||||
|
||||
if (_currentHP.Value <= 0)
|
||||
if (CurrentHP.Value <= 0)
|
||||
return;
|
||||
|
||||
_enemyModelView.PlayHitAnimation();
|
||||
@@ -149,7 +147,7 @@ public partial class Enemy : CharacterBody3D, IEnemy, IProvide<IEnemyLogic>
|
||||
|
||||
public void Die()
|
||||
{
|
||||
_currentHP.OnNext(0);
|
||||
CurrentHP.OnNext(0);
|
||||
_enemyLogic.Input(new EnemyLogic.Input.EnemyDefeated());
|
||||
_collisionShape.SetDeferred("disabled", true);
|
||||
_enemyModelView.PlayDeathAnimation();
|
||||
@@ -161,7 +159,7 @@ public partial class Enemy : CharacterBody3D, IEnemy, IProvide<IEnemyLogic>
|
||||
|
||||
public void SetCurrentHP(int targetHP)
|
||||
{
|
||||
_currentHP.OnNext(targetHP);
|
||||
CurrentHP.OnNext(targetHP);
|
||||
}
|
||||
|
||||
public int GetMaximumHP()
|
||||
@@ -169,12 +167,12 @@ public partial class Enemy : CharacterBody3D, IEnemy, IProvide<IEnemyLogic>
|
||||
return _enemyStatResource.MaximumHP;
|
||||
}
|
||||
|
||||
public void StartAttackTimer()
|
||||
public virtual void StartAttackTimer()
|
||||
{
|
||||
_attackTimer.Timeout += OnAttackTimeout;
|
||||
}
|
||||
|
||||
public void StopAttackTimer()
|
||||
public virtual void StopAttackTimer()
|
||||
{
|
||||
_attackTimer.Timeout -= OnAttackTimeout;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Chickensoft.AutoInject;
|
||||
using Chickensoft.AutoInject;
|
||||
using Chickensoft.Introspection;
|
||||
using Godot;
|
||||
|
||||
@@ -10,12 +10,8 @@ public partial class EnemyModelView3D : Node3D, IEnemyModelView
|
||||
private const string PRIMARY_ATTACK = "primary_attack";
|
||||
private const string SECONDARY_ATTACK = "secondary_attack";
|
||||
private const string PRIMARY_SKILL = "primary_skill";
|
||||
private const string IDLE_FORWARD = "idle_front";
|
||||
private const string IDLE_LEFT = "idle_left";
|
||||
private const string IDLE_BACK = "idle_back";
|
||||
private const string IDLE_FORWARD_WALK = "idle_front_walk";
|
||||
private const string IDLE_LEFT_WALK = "idle_left_walk";
|
||||
private const string IDLE_BACK_WALK = "idle_back_walk";
|
||||
private const string WALK = "walk";
|
||||
private const string IDLE = "idle";
|
||||
private const string TAKE_DAMAGE = "take_damage";
|
||||
private const string DEFEATED = "defeated";
|
||||
private const string PARAMETERS_PLAYBACK = "parameters/playback";
|
||||
@@ -24,34 +20,48 @@ public partial class EnemyModelView3D : Node3D, IEnemyModelView
|
||||
|
||||
[Export] public EnemyLoreInfo EnemyLoreInfo { get; set; } = default!;
|
||||
|
||||
[Node] private MeshInstance3D _meshInstance { get; set; } = default!;
|
||||
|
||||
[Node] private AnimationPlayer _animationPlayer { get; set; } = default!;
|
||||
|
||||
[Node] public AnimationTree AnimationTree { get; set; } = default!;
|
||||
|
||||
[Node] public IHitbox Hitbox { get; set; } = default!;
|
||||
|
||||
public void Setup()
|
||||
{
|
||||
}
|
||||
|
||||
public void PlayPrimaryAttackAnimation()
|
||||
{
|
||||
_animationPlayer.Play(PRIMARY_ATTACK);
|
||||
AnimationTree.Get(PARAMETERS_PLAYBACK).As<AnimationNodeStateMachinePlayback>().Travel(PRIMARY_ATTACK);
|
||||
}
|
||||
|
||||
public void PlaySecondaryAttackAnimation()
|
||||
{
|
||||
AnimationTree.Get(PARAMETERS_PLAYBACK).As<AnimationNodeStateMachinePlayback>().Travel(SECONDARY_ATTACK);
|
||||
}
|
||||
|
||||
public void PlayPrimarySkillAnimation()
|
||||
{
|
||||
AnimationTree.Get(PARAMETERS_PLAYBACK).As<AnimationNodeStateMachinePlayback>().Travel(PRIMARY_SKILL);
|
||||
}
|
||||
|
||||
public void PlayHitAnimation()
|
||||
{
|
||||
_animationPlayer.Play(TAKE_DAMAGE);
|
||||
_animationPlayer.Play(TAKE_DAMAGE);
|
||||
}
|
||||
|
||||
public void PlayDeathAnimation()
|
||||
{
|
||||
_animationPlayer.Play(DEFEATED);
|
||||
_animationPlayer.Play(DEFEATED);
|
||||
}
|
||||
|
||||
public void PlayWalkAnimation()
|
||||
{
|
||||
AnimationTree.Get(PARAMETERS_PLAYBACK).As<AnimationNodeStateMachinePlayback>().Travel(WALK);
|
||||
}
|
||||
|
||||
public void PlayIdleAnimation()
|
||||
{
|
||||
AnimationTree.Get(PARAMETERS_PLAYBACK).As<AnimationNodeStateMachinePlayback>().Travel(IDLE);
|
||||
}
|
||||
}
|
||||
|
||||
6
Zennysoft.Game.Ma/src/enemy/ICanActivate.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
namespace Zennysoft.Game.Ma;
|
||||
|
||||
public interface ICanActivate
|
||||
{
|
||||
public void Activate();
|
||||
}
|
||||
1
Zennysoft.Game.Ma/src/enemy/ICanActivate.cs.uid
Normal file
@@ -0,0 +1 @@
|
||||
uid://btxdf6ncxkk02
|
||||
@@ -1,4 +1,5 @@
|
||||
using Godot;
|
||||
using Chickensoft.Collections;
|
||||
using Godot;
|
||||
using Zennysoft.Game.Abstractions;
|
||||
using Zennysoft.Ma.Adapter;
|
||||
|
||||
@@ -14,7 +15,7 @@ public interface IEnemy : IKillable
|
||||
|
||||
public void Knockback(float impulse, Vector3 direction);
|
||||
|
||||
public double CurrentHP { get; }
|
||||
public AutoProp<double> CurrentHP { get; }
|
||||
|
||||
public void SetCurrentHP(int newHP);
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ using Zennysoft.Ma.Adapter;
|
||||
namespace Zennysoft.Game.Ma;
|
||||
|
||||
[Meta(typeof(IAutoNode))]
|
||||
public partial class Chinthe : Enemy, IHasPrimaryAttack, ICanPatrol
|
||||
public partial class Chinthe : Enemy, IHasPrimaryAttack, ICanPatrol, ICanActivate
|
||||
{
|
||||
public override void _Notification(int what) => this.Notify(what);
|
||||
|
||||
@@ -24,72 +24,72 @@ public partial class Chinthe : Enemy, IHasPrimaryAttack, ICanPatrol
|
||||
|
||||
public void OnReady()
|
||||
{
|
||||
SetPhysicsProcess(true);
|
||||
EnemyModelView.Hitbox.AreaEntered += Hitbox_AreaEntered;
|
||||
SetPhysicsProcess(true);
|
||||
EnemyModelView.Hitbox.AreaEntered += Hitbox_AreaEntered;
|
||||
}
|
||||
|
||||
public void OnPhysicsProcess(double delta)
|
||||
{
|
||||
_enemyLogic.Input(new EnemyLogic.Input.PhysicsTick(delta));
|
||||
_enemyLogic.Input(new EnemyLogic.Input.PhysicsTick(delta));
|
||||
|
||||
if (_enemyLogic.Value is not EnemyLogic.State.Activated)
|
||||
return;
|
||||
if (_enemyLogic.Value is not EnemyLogic.State.Activated)
|
||||
return;
|
||||
|
||||
if (_enemyLogic.Value is EnemyLogic.State.FollowPlayer && GlobalPosition.DistanceTo(_player.CurrentPosition) < 2.5f)
|
||||
_enemyLogic.Input(new EnemyLogic.Input.StartAttacking());
|
||||
if (_enemyLogic.Value is EnemyLogic.State.FollowPlayer && GlobalPosition.DistanceTo(_player.CurrentPosition) > 45f)
|
||||
_enemyLogic.Input(new EnemyLogic.Input.LostPlayer());
|
||||
if (_enemyLogic.Value is EnemyLogic.State.Attacking && GlobalPosition.DistanceTo(_player.CurrentPosition) > 2.5f)
|
||||
_enemyLogic.Input(new EnemyLogic.Input.Alerted());
|
||||
if (_enemyLogic.Value is EnemyLogic.State.FollowPlayer && GlobalPosition.DistanceTo(_player.CurrentPosition) < 2.5f)
|
||||
_enemyLogic.Input(new EnemyLogic.Input.StartAttacking());
|
||||
if (_enemyLogic.Value is EnemyLogic.State.FollowPlayer && GlobalPosition.DistanceTo(_player.CurrentPosition) > 45f)
|
||||
_enemyLogic.Input(new EnemyLogic.Input.LostPlayer());
|
||||
if (_enemyLogic.Value is EnemyLogic.State.Attacking && GlobalPosition.DistanceTo(_player.CurrentPosition) > 2.5f)
|
||||
_enemyLogic.Input(new EnemyLogic.Input.Alerted());
|
||||
|
||||
_navigationAgentClient.CalculateVelocity(GlobalPosition, EnemyModelView.CanMove);
|
||||
_navigationAgentClient.CalculateVelocity(GlobalPosition, EnemyModelView.CanMove);
|
||||
|
||||
base._PhysicsProcess(delta);
|
||||
base._PhysicsProcess(delta);
|
||||
}
|
||||
|
||||
public override void TakeAction()
|
||||
{
|
||||
PrimaryAttack();
|
||||
PrimaryAttack();
|
||||
}
|
||||
|
||||
public void PrimaryAttack()
|
||||
{
|
||||
_enemyModelView.PlayPrimaryAttackAnimation();
|
||||
_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());
|
||||
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()
|
||||
{
|
||||
if (EnemyModelView.AnimationTree.Get(PARAMETERS_PLAYBACK).As<AnimationNodeStateMachinePlayback>().GetCurrentNode().ToString().Contains("front"))
|
||||
EnemyModelView.PlayActivateFrontAnimation();
|
||||
if (EnemyModelView.AnimationTree.Get(PARAMETERS_PLAYBACK).As<AnimationNodeStateMachinePlayback>().GetCurrentNode().ToString().Contains("left"))
|
||||
EnemyModelView.PlayActivateLeftAnimation();
|
||||
if (EnemyModelView.AnimationTree.Get(PARAMETERS_PLAYBACK).As<AnimationNodeStateMachinePlayback>().GetCurrentNode().ToString().Contains("back"))
|
||||
EnemyModelView.PlayActivateBackAnimation();
|
||||
if (EnemyModelView.AnimationTree.Get(PARAMETERS_PLAYBACK).As<AnimationNodeStateMachinePlayback>().GetCurrentNode().ToString().Contains("front"))
|
||||
EnemyModelView.PlayActivateFrontAnimation();
|
||||
if (EnemyModelView.AnimationTree.Get(PARAMETERS_PLAYBACK).As<AnimationNodeStateMachinePlayback>().GetCurrentNode().ToString().Contains("left"))
|
||||
EnemyModelView.PlayActivateLeftAnimation();
|
||||
if (EnemyModelView.AnimationTree.Get(PARAMETERS_PLAYBACK).As<AnimationNodeStateMachinePlayback>().GetCurrentNode().ToString().Contains("back"))
|
||||
EnemyModelView.PlayActivateBackAnimation();
|
||||
}
|
||||
|
||||
public void Teleport()
|
||||
{
|
||||
EnemyModelView.PlayTeleportAnimation();
|
||||
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));
|
||||
}
|
||||
var target = area.GetOwner();
|
||||
if (target is IPlayer player)
|
||||
{
|
||||
var damage = _enemyStatResource.CurrentAttack * PrimaryAttackElementalDamageBonus;
|
||||
player.TakeDamage(damage, PrimaryAttackElementalType, BattleExtensions.IsCriticalHit(_enemyStatResource.Luck));
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -3,7 +3,7 @@
|
||||
[ext_resource type="Texture2D" uid="uid://cvst7yhbw0sxt" path="res://src/enemy/enemy_types/10. Eden Pillar/model/PILLAR EXPORT 1_ENEMY_PILLAR_TEXTURE2.jpg" id="1_1kpl1"]
|
||||
[ext_resource type="Script" uid="uid://ckv5dmrw6pvn6" path="res://src/enemy/EnemyModelView3D.cs" id="1_11eq8"]
|
||||
[ext_resource type="Texture2D" uid="uid://bnbveonobhyhc" path="res://src/enemy/enemy_types/10. Eden Pillar/model/PILLAR EXPORT 1_cannon_edge.png" id="2_11eq8"]
|
||||
[ext_resource type="Material" uid="uid://brwu51ylevbmg" path="res://src/enemy/enemy_types/14. horse_head/BossHit.tres" id="2_xf2ga"]
|
||||
[ext_resource type="Material" uid="uid://brwu51ylevbmg" path="res://src/enemy/enemy_types/14. horse_head/animation/BossHit.tres" id="2_xf2ga"]
|
||||
[ext_resource type="Texture2D" uid="uid://by7k6crx6fmpv" path="res://src/enemy/enemy_types/10. Eden Pillar/model/PILLAR EXPORT 1_floral_single_tile.jpg" id="3_oxjs8"]
|
||||
[ext_resource type="Texture2D" uid="uid://cc1tenm6p3pca" path="res://src/enemy/enemy_types/10. Eden Pillar/model/PILLAR EXPORT 1_ENEMY_PILLAR_TEXTURE.jpg" id="4_xf2ga"]
|
||||
[ext_resource type="Texture2D" uid="uid://bydfevqfagpq8" path="res://src/enemy/enemy_types/10. Eden Pillar/model/PILLAR EXPORT 1_concrete_0025_height_1k.png" id="5_qhmtu"]
|
||||
|
||||
@@ -0,0 +1 @@
|
||||
uid://clt3sfgb5m3xq
|
||||
@@ -1,7 +0,0 @@
|
||||
using Godot;
|
||||
|
||||
namespace Zennysoft.Game.Ma;
|
||||
|
||||
public partial class HorseHead : Node3D
|
||||
{
|
||||
}
|
||||
@@ -1,6 +1,6 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://8yaqqojv4nuv"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://c4uus6q6c4ocg" path="res://src/enemy/enemy_types/14. horse_head/Horse Head 1.0 STATUE.glb" id="1_ktvqd"]
|
||||
[ext_resource type="PackedScene" uid="uid://c4uus6q6c4ocg" path="res://src/enemy/enemy_types/14. horse_head/animation/Horse Head 1.0 STATUE.glb" id="1_ktvqd"]
|
||||
|
||||
[node name="Node3D" type="Node3D"]
|
||||
|
||||
|
||||
@@ -4,12 +4,12 @@ importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://cvqaccot8ltrf"
|
||||
path="res://.godot/imported/HORSE-FACE 1.1.glb-a095bfe8da5727e267e8a8185cd8aaee.scn"
|
||||
path="res://.godot/imported/HORSE-FACE 1.1.glb-8c0ac9d603554b32a1666de9bd9b1814.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://src/enemy/enemy_types/14. horse_head/HORSE-FACE 1.1.glb"
|
||||
dest_files=["res://.godot/imported/HORSE-FACE 1.1.glb-a095bfe8da5727e267e8a8185cd8aaee.scn"]
|
||||
source_file="res://src/enemy/enemy_types/14. horse_head/animation/HORSE-FACE 1.1.glb"
|
||||
dest_files=["res://.godot/imported/HORSE-FACE 1.1.glb-8c0ac9d603554b32a1666de9bd9b1814.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
|
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 1.6 MiB |
@@ -3,7 +3,7 @@
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://csj3kjwyn3s2u"
|
||||
path="res://.godot/imported/HORSE-FACE 1_Metal054C_1K-JPG_Color.jpg-dd3ebefab63cbbf56a2e0e52b75bc691.ctex"
|
||||
path="res://.godot/imported/HORSE-FACE 1_Metal054C_1K-JPG_Color.jpg-1f1a8445de0bb81a567077e5aec18194.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
@@ -13,8 +13,8 @@ generator_parameters={
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://src/enemy/enemy_types/14. horse_head/HORSE-FACE 1_Metal054C_1K-JPG_Color.jpg"
|
||||
dest_files=["res://.godot/imported/HORSE-FACE 1_Metal054C_1K-JPG_Color.jpg-dd3ebefab63cbbf56a2e0e52b75bc691.ctex"]
|
||||
source_file="res://src/enemy/enemy_types/14. horse_head/animation/HORSE-FACE 1_Metal054C_1K-JPG_Color.jpg"
|
||||
dest_files=["res://.godot/imported/HORSE-FACE 1_Metal054C_1K-JPG_Color.jpg-1f1a8445de0bb81a567077e5aec18194.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
|
Before Width: | Height: | Size: 311 KiB After Width: | Height: | Size: 311 KiB |
@@ -3,7 +3,7 @@
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dd7ocxanos2o7"
|
||||
path="res://.godot/imported/HORSE-FACE 1_Metal054C_1K-JPG_Displacement.jpg-5771feccb31b5f0e73720e8a3e7a5725.ctex"
|
||||
path="res://.godot/imported/HORSE-FACE 1_Metal054C_1K-JPG_Displacement.jpg-386dacafcbff768209a8a638cefcea95.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
@@ -13,8 +13,8 @@ generator_parameters={
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://src/enemy/enemy_types/14. horse_head/HORSE-FACE 1_Metal054C_1K-JPG_Displacement.jpg"
|
||||
dest_files=["res://.godot/imported/HORSE-FACE 1_Metal054C_1K-JPG_Displacement.jpg-5771feccb31b5f0e73720e8a3e7a5725.ctex"]
|
||||
source_file="res://src/enemy/enemy_types/14. horse_head/animation/HORSE-FACE 1_Metal054C_1K-JPG_Displacement.jpg"
|
||||
dest_files=["res://.godot/imported/HORSE-FACE 1_Metal054C_1K-JPG_Displacement.jpg-386dacafcbff768209a8a638cefcea95.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
@@ -4,12 +4,12 @@ importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://c4uus6q6c4ocg"
|
||||
path="res://.godot/imported/Horse Head 1.0 STATUE.glb-e0c74b4195e0fc27f4658130960774ea.scn"
|
||||
path="res://.godot/imported/Horse Head 1.0 STATUE.glb-0ffebc243c4ceff8865a777101feadec.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://src/enemy/enemy_types/14. horse_head/Horse Head 1.0 STATUE.glb"
|
||||
dest_files=["res://.godot/imported/Horse Head 1.0 STATUE.glb-e0c74b4195e0fc27f4658130960774ea.scn"]
|
||||
source_file="res://src/enemy/enemy_types/14. horse_head/animation/Horse Head 1.0 STATUE.glb"
|
||||
dest_files=["res://.godot/imported/Horse Head 1.0 STATUE.glb-0ffebc243c4ceff8865a777101feadec.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
@@ -8,7 +8,7 @@ valid=false
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://src/enemy/enemy_types/14. horse_head/Horse Head 1.0 WALK.glb"
|
||||
source_file="res://src/enemy/enemy_types/14. horse_head/animation/Horse Head 1.0 WALK.glb"
|
||||
|
||||
[params]
|
||||
|
||||
@@ -4,12 +4,12 @@ importer="scene"
|
||||
importer_version=1
|
||||
type="PackedScene"
|
||||
uid="uid://b8xs07d4ycm3s"
|
||||
path="res://.godot/imported/Horse Head 1.0.glb-17c7ea9ea5d193e3a003f25b69f74650.scn"
|
||||
path="res://.godot/imported/Horse Head 1.0.glb-1e71651be336a86299c334b8540d0758.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://src/enemy/enemy_types/14. horse_head/Horse Head 1.0.glb"
|
||||
dest_files=["res://.godot/imported/Horse Head 1.0.glb-17c7ea9ea5d193e3a003f25b69f74650.scn"]
|
||||
source_file="res://src/enemy/enemy_types/14. horse_head/animation/Horse Head 1.0.glb"
|
||||
dest_files=["res://.godot/imported/Horse Head 1.0.glb-1e71651be336a86299c334b8540d0758.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
|
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 1.6 MiB |
@@ -3,7 +3,7 @@
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://2e4cp477ex0t"
|
||||
path="res://.godot/imported/Horse Head 1_Metal054C_1K-JPG_Color.jpg-309c20056c641a0bf0fe6eabcff04ca0.ctex"
|
||||
path="res://.godot/imported/Horse Head 1_Metal054C_1K-JPG_Color.jpg-0c0531e87bbeff5b38eafc2808887dbf.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
@@ -13,8 +13,8 @@ generator_parameters={
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://src/enemy/enemy_types/14. horse_head/Horse Head 1_Metal054C_1K-JPG_Color.jpg"
|
||||
dest_files=["res://.godot/imported/Horse Head 1_Metal054C_1K-JPG_Color.jpg-309c20056c641a0bf0fe6eabcff04ca0.ctex"]
|
||||
source_file="res://src/enemy/enemy_types/14. horse_head/animation/Horse Head 1_Metal054C_1K-JPG_Color.jpg"
|
||||
dest_files=["res://.godot/imported/Horse Head 1_Metal054C_1K-JPG_Color.jpg-0c0531e87bbeff5b38eafc2808887dbf.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
|
Before Width: | Height: | Size: 311 KiB After Width: | Height: | Size: 311 KiB |
@@ -3,7 +3,7 @@
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://kdnitbg4n87p"
|
||||
path="res://.godot/imported/Horse Head 1_Metal054C_1K-JPG_Displacement.jpg-22bf3780ddcc88b6230d35aebd896c20.ctex"
|
||||
path="res://.godot/imported/Horse Head 1_Metal054C_1K-JPG_Displacement.jpg-0a8cfa8420b016a4333949db03bcf0e5.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
@@ -13,8 +13,8 @@ generator_parameters={
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://src/enemy/enemy_types/14. horse_head/Horse Head 1_Metal054C_1K-JPG_Displacement.jpg"
|
||||
dest_files=["res://.godot/imported/Horse Head 1_Metal054C_1K-JPG_Displacement.jpg-22bf3780ddcc88b6230d35aebd896c20.ctex"]
|
||||
source_file="res://src/enemy/enemy_types/14. horse_head/animation/Horse Head 1_Metal054C_1K-JPG_Displacement.jpg"
|
||||
dest_files=["res://.godot/imported/Horse Head 1_Metal054C_1K-JPG_Displacement.jpg-0a8cfa8420b016a4333949db03bcf0e5.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
|
Before Width: | Height: | Size: 622 KiB After Width: | Height: | Size: 622 KiB |
@@ -3,15 +3,15 @@
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://bv60n7epxktw5"
|
||||
path="res://.godot/imported/Metal054C_1K-JPG_NormalDX.jpg-1cf1bfec87fee44ad90f8f8942ca9cd1.ctex"
|
||||
path="res://.godot/imported/Metal054C_1K-JPG_NormalDX.jpg-8f619f53486c72857b0b0ce2b4ae98bd.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://src/enemy/enemy_types/14. horse_head/Metal054C_1K-JPG_NormalDX.jpg"
|
||||
dest_files=["res://.godot/imported/Metal054C_1K-JPG_NormalDX.jpg-1cf1bfec87fee44ad90f8f8942ca9cd1.ctex"]
|
||||
source_file="res://src/enemy/enemy_types/14. horse_head/animation/Metal054C_1K-JPG_NormalDX.jpg"
|
||||
dest_files=["res://.godot/imported/Metal054C_1K-JPG_NormalDX.jpg-8f619f53486c72857b0b0ce2b4ae98bd.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
@@ -1,57 +0,0 @@
|
||||
using Chickensoft.AutoInject;
|
||||
using Chickensoft.Introspection;
|
||||
using Godot;
|
||||
using Zennysoft.Ma.Adapter;
|
||||
|
||||
namespace Zennysoft.Game.Ma;
|
||||
|
||||
[Meta(typeof(IAutoNode))]
|
||||
public partial class OxFace : Node3D
|
||||
{
|
||||
public override void _Notification(int what) => this.Notify(what);
|
||||
|
||||
[Node] public AnimationTree AnimationTree { get; set; } = default!;
|
||||
|
||||
[Dependency] public IPlayer Player => this.DependOn<IPlayer>();
|
||||
|
||||
[Node] public Timer AttackTimer { get; set; } = default!;
|
||||
|
||||
private bool isInRange = false;
|
||||
|
||||
public void Setup()
|
||||
{
|
||||
SetPhysicsProcess(false);
|
||||
}
|
||||
|
||||
public void EngagePlayer()
|
||||
{
|
||||
SetPhysicsProcess(true);
|
||||
Visible = true;
|
||||
AttackTimer.Timeout += AttackTimer_Timeout;
|
||||
AttackTimer.Start();
|
||||
}
|
||||
|
||||
private void AttackTimer_Timeout()
|
||||
{
|
||||
AnimationTree.Get("parameters/playback").As<AnimationNodeStateMachinePlayback>().Travel("SPEAR");
|
||||
}
|
||||
|
||||
public override void _PhysicsProcess(double delta)
|
||||
{
|
||||
var playerPosition = new Vector3(Player.CurrentPosition.X, GlobalPosition.Y, Player.CurrentPosition.Z);
|
||||
var targetDirection = GlobalPosition - Player.CurrentPosition;
|
||||
GlobalRotation = new Vector3(GlobalRotation.X, Mathf.LerpAngle(GlobalRotation.Y, Mathf.Atan2(-targetDirection.X, -targetDirection.Z), (float)delta * 3f), GlobalRotation.Z);
|
||||
if (GlobalPosition.DistanceTo(Player.CurrentPosition) > 5.0f)
|
||||
{
|
||||
isInRange = false;
|
||||
var moveToward = GlobalPosition.MoveToward(playerPosition, (float)delta * 3f);
|
||||
GlobalPosition = moveToward;
|
||||
AnimationTree.Get("parameters/playback").As<AnimationNodeStateMachinePlayback>().Travel("WALK");
|
||||
}
|
||||
else if (!isInRange)
|
||||
{
|
||||
isInRange = true;
|
||||
AnimationTree.Get("parameters/playback").As<AnimationNodeStateMachinePlayback>().Travel("IDLE");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -98,7 +98,7 @@ public class EffectService
|
||||
var currentEnemies = currentRoom.EnemiesInRoom;
|
||||
var hpToAbsorb = 0.0;
|
||||
foreach (var enemy in currentEnemies)
|
||||
hpToAbsorb += enemy.CurrentHP * 0.05;
|
||||
hpToAbsorb += enemy.CurrentHP.Value * 0.05;
|
||||
_player.Stats.SetCurrentHP(_player.Stats.CurrentHP.Value + (int)hpToAbsorb);
|
||||
GD.Print("HP to absorb: " + hpToAbsorb);
|
||||
}
|
||||
|
||||
@@ -31,8 +31,12 @@ public partial class ThrownItem : RigidBody3D
|
||||
private void ThrownItem_BodyEntered(Node body)
|
||||
{
|
||||
if (body is IEnemy enemy)
|
||||
{
|
||||
CalculateEffect(enemy);
|
||||
QueueFree();
|
||||
QueueFree();
|
||||
}
|
||||
else if (ItemThatIsThrown is ThrowableItem)
|
||||
QueueFree();
|
||||
}
|
||||
|
||||
public void Throw(EffectService effectService)
|
||||
@@ -85,7 +89,7 @@ public partial class ThrownItem : RigidBody3D
|
||||
switch (throwableItem.ThrowableItemTag)
|
||||
{
|
||||
case ThrowableItemTag.LowerTargetTo1HP:
|
||||
enemy.TakeDamage(enemy.CurrentHP - 1, ignoreDefense: true, ignoreElementalResistance: true);
|
||||
enemy.TakeDamage(enemy.CurrentHP.Value - 1, ignoreDefense: true, ignoreElementalResistance: true);
|
||||
break;
|
||||
case ThrowableItemTag.TeleportToRandomLocation:
|
||||
_effectService.TeleportToRandomRoom(enemy);
|
||||
@@ -99,6 +103,6 @@ public partial class ThrownItem : RigidBody3D
|
||||
}
|
||||
}
|
||||
else
|
||||
enemy.TakeDamage(((InventoryItem)ItemThatIsThrown).ThrowDamage);
|
||||
enemy.TakeDamage(ItemThatIsThrown.ThrowDamage);
|
||||
}
|
||||
}
|
||||
@@ -10,8 +10,8 @@ size = Vector3(0.46632, 0.507293, 0.586082)
|
||||
viewport_path = NodePath("Sprite3D/SubViewportContainer/SubViewport")
|
||||
|
||||
[node name="Hitbox" type="RigidBody3D"]
|
||||
collision_layer = 1040
|
||||
collision_mask = 25
|
||||
collision_layer = 3072
|
||||
collision_mask = 2049
|
||||
mass = 0.1
|
||||
gravity_scale = 0.2
|
||||
contact_monitor = true
|
||||
|
||||
@@ -1,8 +1,9 @@
|
||||
[gd_scene load_steps=12 format=3 uid="uid://by67pn7fdsg1m"]
|
||||
[gd_scene load_steps=13 format=3 uid="uid://by67pn7fdsg1m"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://14e8mu48ed4" path="res://src/map/Map.cs" id="1_bw70o"]
|
||||
[ext_resource type="PackedScene" uid="uid://dl6h1djc27ddl" path="res://src/map/dungeon/floors/Floor00.tscn" id="2_0m8h8"]
|
||||
[ext_resource type="PackedScene" uid="uid://bc1sp6xwe0j65" path="res://src/map/dungeon/floors/Floor01.tscn" id="2_merfv"]
|
||||
[ext_resource type="PackedScene" uid="uid://g28xmp6cn16h" path="res://src/map/dungeon/floors/Floor10.tscn" id="3_caf7v"]
|
||||
[ext_resource type="PackedScene" uid="uid://dmiqwmivkjgmq" path="res://src/map/dungeon/floors/Floor02.tscn" id="4_8y0oy"]
|
||||
[ext_resource type="PackedScene" uid="uid://dl1scvkp8r5sw" path="res://src/map/dungeon/floors/Floor03.tscn" id="5_uag72"]
|
||||
[ext_resource type="PackedScene" uid="uid://cikq7vuorlpbl" path="res://src/map/dungeon/floors/Floor04.tscn" id="6_55rmo"]
|
||||
@@ -14,6 +15,6 @@
|
||||
|
||||
[node name="Map" type="Node3D"]
|
||||
script = ExtResource("1_bw70o")
|
||||
_floors = Array[PackedScene]([ExtResource("2_0m8h8"), ExtResource("11_y74f3"), ExtResource("2_merfv"), ExtResource("4_8y0oy"), ExtResource("5_uag72"), ExtResource("6_55rmo"), ExtResource("7_f6kwn"), ExtResource("8_ne2vg"), ExtResource("9_abpbr"), ExtResource("10_caf7v")])
|
||||
_floors = Array[PackedScene]([ExtResource("2_0m8h8"), ExtResource("3_caf7v"), ExtResource("11_y74f3"), ExtResource("2_merfv"), ExtResource("4_8y0oy"), ExtResource("5_uag72"), ExtResource("6_55rmo"), ExtResource("7_f6kwn"), ExtResource("8_ne2vg"), ExtResource("9_abpbr"), ExtResource("10_caf7v")])
|
||||
|
||||
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
|
||||
|
||||
@@ -10,18 +10,16 @@ public partial class BossFloor : Node3D, IDungeonFloor
|
||||
{
|
||||
public override void _Notification(int what) => this.Notify(what);
|
||||
|
||||
private BossRoomA BossRoom;
|
||||
[Node] private BossRoomA BossFloorA { get; set; } = default!;
|
||||
|
||||
public ImmutableList<IDungeonRoom> Rooms => [];
|
||||
|
||||
public void InitializeDungeon()
|
||||
{
|
||||
var bossRoomScene = GD.Load<PackedScene>($"res://src/map/dungeon/scenes/BossRoom.tscn");
|
||||
BossRoom = bossRoomScene.Instantiate<BossRoomA>();
|
||||
FloorIsLoaded = true;
|
||||
FloorIsLoaded = true;
|
||||
}
|
||||
|
||||
public bool FloorIsLoaded { get; set; }
|
||||
|
||||
public Transform3D GetPlayerSpawnPoint() => BossRoom.PlayerSpawn.GlobalTransform;
|
||||
public Transform3D GetPlayerSpawnPoint() => BossFloorA.PlayerSpawn.GlobalTransform;
|
||||
}
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
using Chickensoft.AutoInject;
|
||||
using Chickensoft.AutoInject;
|
||||
using Chickensoft.Introspection;
|
||||
using Godot;
|
||||
|
||||
@@ -9,28 +9,37 @@ public partial class BossRoomA : Node3D, IBossRoom
|
||||
{
|
||||
public override void _Notification(int what) => this.Notify(what);
|
||||
|
||||
[Dependency] public IGame Game => this.DependOn<IGame>();
|
||||
|
||||
[Node] public Marker3D PlayerSpawn { get; set; } = default!;
|
||||
|
||||
[Node] public Node3D HorseHeadStatue { get; set; } = default!;
|
||||
|
||||
[Node] public Node3D OxFaceStatue { get; set; } = default!;
|
||||
|
||||
[Node] public Boss OxFace { get; set; } = default!;
|
||||
[Node] public BossTypeA OxFace { get; set; } = default!;
|
||||
|
||||
[Node] public Boss HorseFace { get; set; } = default!;
|
||||
[Node] public BossTypeA HorseFace { get; set; } = default!;
|
||||
|
||||
[Node] public Area3D ActivateTrap { get; set; } = default!;
|
||||
|
||||
[Node] public Node3D GateCollision { get; set; } = default!;
|
||||
|
||||
[Node] private Area3D _exit { get; set; } = default!;
|
||||
|
||||
public void Setup()
|
||||
{
|
||||
ActivateTrap.BodyEntered += ActivateTrap_BodyEntered;
|
||||
OxFace.IsDefeated.Sync += BossStatusUpdate;
|
||||
HorseFace.IsDefeated.Sync += BossStatusUpdate;
|
||||
OxFace.CurrentHP.Sync += BossStatusUpdate;
|
||||
HorseFace.CurrentHP.Sync += BossStatusUpdate;
|
||||
_exit.AreaEntered += Exit_AreaEntered;
|
||||
}
|
||||
|
||||
private void ActivateTrap_BodyEntered(Node3D body) => StartBossFight();
|
||||
private void ActivateTrap_BodyEntered(Node3D body)
|
||||
{
|
||||
ActivateTrap.BodyEntered -= ActivateTrap_BodyEntered;
|
||||
StartBossFight();
|
||||
}
|
||||
|
||||
public void StartBossFight()
|
||||
{
|
||||
@@ -45,9 +54,14 @@ public partial class BossRoomA : Node3D, IBossRoom
|
||||
GateCollision.CallDeferred(MethodName.QueueFree);
|
||||
}
|
||||
|
||||
private void BossStatusUpdate(bool obj)
|
||||
private void BossStatusUpdate(double hp)
|
||||
{
|
||||
if (OxFace.IsDefeated.Value && HorseFace.IsDefeated.Value)
|
||||
if (OxFace.CurrentHP.Value <= 0 && HorseFace.CurrentHP.Value <= 0)
|
||||
OnBossFightEnded();
|
||||
}
|
||||
|
||||
public void ExitReached()
|
||||
=> Game.FloorExitReached();
|
||||
|
||||
private void Exit_AreaEntered(Area3D area) => ExitReached();
|
||||
}
|
||||
|
||||
@@ -12,7 +12,6 @@ public partial class ExitRoom : DungeonRoom
|
||||
[Dependency] public IGame Game => this.DependOn<IGame>();
|
||||
|
||||
[Node] private Area3D _exit { get; set; } = default!;
|
||||
[Node] private Area3D _room { get; set; } = default!;
|
||||
|
||||
public override void _Ready()
|
||||
{
|
||||
|
||||
@@ -6,4 +6,5 @@
|
||||
[node name="Floor10" type="Node3D"]
|
||||
script = ExtResource("1_0xjxw")
|
||||
|
||||
[node name="Boss Floor A" parent="." instance=ExtResource("2_rbjfi")]
|
||||
[node name="BossFloorA" parent="." instance=ExtResource("2_rbjfi")]
|
||||
unique_name_in_owner = true
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
[gd_scene load_steps=132 format=4 uid="uid://5ja3qxn8h7iw"]
|
||||
[gd_scene load_steps=134 format=4 uid="uid://5ja3qxn8h7iw"]
|
||||
|
||||
[ext_resource type="Script" uid="uid://tqyybt313web" path="res://src/map/dungeon/code/BossRoomA.cs" id="1_0h3lb"]
|
||||
[ext_resource type="Texture2D" uid="uid://vjbe1lg810gh" path="res://src/map/dungeon/models/Set A/15. Boss Floor A/15_A1_BOSS FLOOR A_VER_swirled_column.png" id="2_06eum"]
|
||||
@@ -758,6 +758,9 @@ _surfaces = [{
|
||||
blend_shape_mode = 0
|
||||
shadow_mesh = SubResource("ArrayMesh_d6sdf")
|
||||
|
||||
[sub_resource type="BoxShape3D" id="BoxShape3D_01whq"]
|
||||
size = Vector3(3.42822, 1.92615, 1.85886)
|
||||
|
||||
[sub_resource type="ArrayMesh" id="ArrayMesh_2xh7e"]
|
||||
_surfaces = [{
|
||||
"aabb": AABB(-10.8214, -29.365, -6.19888e-06, 21.6429, 58.73, 1.14441e-05),
|
||||
@@ -1594,7 +1597,10 @@ size = Vector3(44.8284, 85.1827, 35.0648)
|
||||
size = Vector3(23.3561, 85.1827, 86.7729)
|
||||
|
||||
[sub_resource type="BoxShape3D" id="BoxShape3D_2y86l"]
|
||||
size = Vector3(57.711, 85.1827, 121.41)
|
||||
size = Vector3(24.4394, 85.1827, 121.41)
|
||||
|
||||
[sub_resource type="BoxShape3D" id="BoxShape3D_blf77"]
|
||||
size = Vector3(59.626, 85.1827, 51.1984)
|
||||
|
||||
[sub_resource type="BoxShape3D" id="BoxShape3D_ukub6"]
|
||||
size = Vector3(11.6031, 85.1827, 18.5975)
|
||||
@@ -1662,12 +1668,12 @@ data = PackedVector3Array(-1, -1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1,
|
||||
[sub_resource type="ConcavePolygonShape3D" id="ConcavePolygonShape3D_1txpk"]
|
||||
data = PackedVector3Array(-1, -1, 1, -1, 1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1, -1, -1, 1, 1, -1, -1, 1, -1, -1, -1, -1, 1, -1, -1, 1, 1, -1, 1, -1, -1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, 1, 1, 1, -1, 1, -1, 1, 1, 1, 1, 1, 1, -1, 1, -1, -1, 1, -1, 1, 1, -1, -1, -1, 1, -1, 1, 1, -1, -1, -1, -1, -1, -1, -1, 1, 1, -1, 1, 1, 1, -1, -1, 1, 1, -1, 1, -1, 1, 1, -1, 1, 1, 1, -1, 1, 1)
|
||||
|
||||
[sub_resource type="BoxShape3D" id="BoxShape3D_01whq"]
|
||||
size = Vector3(3.42822, 1.92615, 1.85886)
|
||||
|
||||
[sub_resource type="BoxShape3D" id="BoxShape3D_pkvyy"]
|
||||
size = Vector3(6.25977, 15.6429, 37.6357)
|
||||
|
||||
[sub_resource type="BoxShape3D" id="BoxShape3D_1qa0g"]
|
||||
size = Vector3(12.4734, 10.2039, 7.09571)
|
||||
|
||||
[node name="Boss Floor A" type="Node3D"]
|
||||
script = ExtResource("1_0h3lb")
|
||||
|
||||
@@ -1678,6 +1684,7 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.135796, -15.9299, 0)
|
||||
|
||||
[node name="COLLISION 2" type="MeshInstance3D" parent="Model/15_A1_BOSS FLOOR A_VER_2"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -88.3533, 14.402, -0.620708)
|
||||
visible = false
|
||||
mesh = SubResource("ArrayMesh_dyjpi")
|
||||
skeleton = NodePath("")
|
||||
|
||||
@@ -1688,11 +1695,13 @@ skeleton = NodePath("")
|
||||
|
||||
[node name="COLLISION 3" type="MeshInstance3D" parent="Model/15_A1_BOSS FLOOR A_VER_2"]
|
||||
transform = Transform3D(19.342, 0, 0, 0, 3.58, 0, 0, 0, 4.493, -118.368, 16.3753, 28.1552)
|
||||
visible = false
|
||||
mesh = SubResource("ArrayMesh_tyise")
|
||||
skeleton = NodePath("")
|
||||
|
||||
[node name="COLLISION" type="MeshInstance3D" parent="Model/15_A1_BOSS FLOOR A_VER_2"]
|
||||
transform = Transform3D(19.342, 0, 0, 0, 3.12, 0, 0, 0, 4.493, -118.368, 16.0099, 6.55423)
|
||||
visible = false
|
||||
mesh = SubResource("ArrayMesh_tyise")
|
||||
skeleton = NodePath("")
|
||||
|
||||
@@ -1702,6 +1711,13 @@ transform = Transform3D(0.816274, 0, 0, 0, 1.99383, 0, 0, 0, 1.99383, -145.76, 1
|
||||
mesh = SubResource("ArrayMesh_xlsos")
|
||||
skeleton = NodePath("")
|
||||
|
||||
[node name="StaticBody3D6" type="StaticBody3D" parent="Model/15_A1_BOSS FLOOR A_VER_2/GateCollision"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.000244141, 2.08616e-07, 0)
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="Model/15_A1_BOSS FLOOR A_VER_2/GateCollision/StaticBody3D6"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.337311, 0.0641477, -0.00570393)
|
||||
shape = SubResource("BoxShape3D_01whq")
|
||||
|
||||
[node name="BOSS FLOOR_001" type="MeshInstance3D" parent="Model/15_A1_BOSS FLOOR A_VER_2"]
|
||||
transform = Transform3D(-0.188597, 0, -3.10851e-08, 0, 0.0695013, 0, 5.62062e-08, 0, -0.104304, -81.6436, 15.2955, 15.4911)
|
||||
mesh = SubResource("ArrayMesh_31d0h")
|
||||
@@ -1822,9 +1838,13 @@ transform = Transform3D(1, 0, -4.26326e-14, 0, 1, 0, 3.41061e-13, 0, 1, -66.351,
|
||||
shape = SubResource("BoxShape3D_0u7qe")
|
||||
|
||||
[node name="CollisionShape3D7" type="CollisionShape3D" parent="Collision/StaticBody3D3"]
|
||||
transform = Transform3D(1, 0, -4.26326e-14, 0, 1, 0, 3.41061e-13, 0, 1, -26.3376, -8.11585, 60.0398)
|
||||
transform = Transform3D(1, 0, 1.84741e-12, 0, 1, 0, 4.54747e-13, 0, 1, -9.70184, 2.74369, 60.0398)
|
||||
shape = SubResource("BoxShape3D_2y86l")
|
||||
|
||||
[node name="CollisionShape3D29" type="CollisionShape3D" parent="Collision/StaticBody3D3"]
|
||||
transform = Transform3D(1, 0, 6.03961e-12, 0, 1, 0, 4.54747e-13, 0, 1, -27.2952, 2.74369, 24.934)
|
||||
shape = SubResource("BoxShape3D_blf77")
|
||||
|
||||
[node name="CollisionShape3D8" type="CollisionShape3D" parent="Collision/StaticBody3D3"]
|
||||
transform = Transform3D(1, 0, -4.26326e-14, 0, 1, 0, 3.41061e-13, 0, 1, 191.381, -8.11585, 25.5622)
|
||||
shape = SubResource("BoxShape3D_ukub6")
|
||||
@@ -1917,20 +1937,13 @@ transform = Transform3D(19.342, 0, 0, 0, 3.12, 0, 0, 0, 4.493, -118.232, 0.07999
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="Collision/StaticBody3D5"]
|
||||
shape = SubResource("ConcavePolygonShape3D_1txpk")
|
||||
|
||||
[node name="StaticBody3D6" type="StaticBody3D" parent="Collision"]
|
||||
transform = Transform3D(0.816274, 0, 0, 0, 1.99383, 0, 0, 0, 1.99383, -145.624, -0.6323, 17.4223)
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="Collision/StaticBody3D6"]
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.337311, 0.0641477, -0.00570393)
|
||||
shape = SubResource("BoxShape3D_01whq")
|
||||
|
||||
[node name="Doors" type="Node3D" parent="."]
|
||||
|
||||
[node name="Spawn Points" type="Node3D" parent="."]
|
||||
|
||||
[node name="PlayerSpawn" type="Marker3D" parent="Spawn Points"]
|
||||
unique_name_in_owner = true
|
||||
transform = Transform3D(-4.37114e-08, 0, -1, 0, 1, 0, 1, 0, -4.37114e-08, -53.747, -2.58899, 17.731)
|
||||
transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, -53.747, -2.5101, 17.731)
|
||||
|
||||
[node name="ItemDatabase" parent="Spawn Points" instance=ExtResource("23_gov56")]
|
||||
unique_name_in_owner = true
|
||||
@@ -1957,7 +1970,7 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -102.157, -2.30863, 13.0139)
|
||||
|
||||
[node name="HorseFace" parent="Room" instance=ExtResource("25_a482y")]
|
||||
unique_name_in_owner = true
|
||||
transform = Transform3D(0.15, 0, 0, 0, 0.15, 0, 0, 0, 0.15, -102.157, -2.30863, 13.0139)
|
||||
transform = Transform3D(0.15, 0, 0, 0, 0.15, 0, 0, 0, 0.15, -102.157, -0.510939, 13.0139)
|
||||
visible = false
|
||||
|
||||
[node name="OxFaceStatue" parent="Room" instance=ExtResource("26_futcf")]
|
||||
@@ -1966,7 +1979,17 @@ transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, -101.5
|
||||
|
||||
[node name="OxFace" parent="Room" instance=ExtResource("27_g6y6v")]
|
||||
unique_name_in_owner = true
|
||||
transform = Transform3D(-0.15, 0, -2.26494e-08, 0, 0.15, 0, 2.26494e-08, 0, -0.15, -101.703, -2.44182, 22.0955)
|
||||
transform = Transform3D(-0.15, 0, -2.26494e-08, 0, 0.15, 0, 2.26494e-08, 0, -0.15, -101.703, -0.479859, 22.0955)
|
||||
visible = false
|
||||
|
||||
[node name="Exit" type="Area3D" parent="Room"]
|
||||
unique_name_in_owner = true
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -250.02, -7.44633, 15.2627)
|
||||
collision_layer = 256
|
||||
collision_mask = 256
|
||||
|
||||
[node name="CollisionShape3D" type="CollisionShape3D" parent="Room/Exit"]
|
||||
transform = Transform3D(-4.37114e-08, 0, 1, 0, 1, 0, -1, 0, -4.37114e-08, 1.59105, 7.59621, 2.59402)
|
||||
shape = SubResource("BoxShape3D_1qa0g")
|
||||
|
||||
[node name="Minimap" type="Node3D" parent="."]
|
||||
|
||||