Add more NPCs, update dialogue manager to 4.4 compatible version
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
using Chickensoft.AutoInject;
|
||||
using Chickensoft.AutoInject;
|
||||
using Chickensoft.Introspection;
|
||||
using Godot;
|
||||
|
||||
@@ -31,105 +31,105 @@ public partial class EnemyModelView2D : Node3D, IEnemyModelView
|
||||
|
||||
public void Setup()
|
||||
{
|
||||
AnimationTree.AnimationFinished += AnimationTree_AnimationFinished;
|
||||
AnimationTree.Get(PARAMETERS_PLAYBACK).As<AnimationNodeStateMachinePlayback>().Start(IDLE_FORWARD);
|
||||
AnimationTree.AnimationFinished += AnimationTree_AnimationFinished;
|
||||
AnimationTree.Get(PARAMETERS_PLAYBACK).As<AnimationNodeStateMachinePlayback>().Start(IDLE_FORWARD);
|
||||
}
|
||||
|
||||
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()
|
||||
{
|
||||
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);
|
||||
tweener.TweenCallback(Callable.From(QueueFree));
|
||||
}
|
||||
|
||||
public void RotateModel(Basis enemyBasis, Vector3 cameraDirection, bool isWalking) => RotateModel(enemyBasis, cameraDirection, 0.55f, 0.45f, isWalking);
|
||||
|
||||
public virtual void RotateModel(
|
||||
Basis enemyBasis,
|
||||
Vector3 cameraDirection,
|
||||
float rotateUpperThreshold,
|
||||
float rotateLowerThreshold,
|
||||
bool isWalking)
|
||||
Basis enemyBasis,
|
||||
Vector3 cameraDirection,
|
||||
float rotateUpperThreshold,
|
||||
float rotateLowerThreshold,
|
||||
bool isWalking)
|
||||
{
|
||||
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 < -rotateUpperThreshold)
|
||||
{
|
||||
if (isWalking)
|
||||
AnimationTree.Get(PARAMETERS_PLAYBACK).As<AnimationNodeStateMachinePlayback>().Travel(IDLE_FORWARD_WALK);
|
||||
else
|
||||
AnimationTree.Get(PARAMETERS_PLAYBACK).As<AnimationNodeStateMachinePlayback>().Travel(IDLE_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)
|
||||
{
|
||||
if (isWalking)
|
||||
AnimationTree.Get(PARAMETERS_PLAYBACK).As<AnimationNodeStateMachinePlayback>().Travel(IDLE_BACK_WALK);
|
||||
else
|
||||
AnimationTree.Get(PARAMETERS_PLAYBACK).As<AnimationNodeStateMachinePlayback>().Travel(IDLE_BACK);
|
||||
}
|
||||
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).
|
||||
AnimatedSprite.FlipH = leftDotProduct > 0;
|
||||
// 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 (Mathf.Abs(forwardDotProduct) < rotateLowerThreshold)
|
||||
{
|
||||
if (isWalking)
|
||||
AnimationTree.Get(PARAMETERS_PLAYBACK).As<AnimationNodeStateMachinePlayback>().Travel(IDLE_LEFT_WALK);
|
||||
else
|
||||
AnimationTree.Get(PARAMETERS_PLAYBACK).As<AnimationNodeStateMachinePlayback>().Travel(IDLE_LEFT);
|
||||
}
|
||||
}
|
||||
// Check if forward facing. If the dot product is -1, the enemy is facing the camera.
|
||||
if (forwardDotProduct < -rotateUpperThreshold)
|
||||
{
|
||||
if (isWalking)
|
||||
AnimationTree.Get(PARAMETERS_PLAYBACK).As<AnimationNodeStateMachinePlayback>().Travel(IDLE_FORWARD_WALK);
|
||||
else
|
||||
AnimationTree.Get(PARAMETERS_PLAYBACK).As<AnimationNodeStateMachinePlayback>().Travel(IDLE_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)
|
||||
{
|
||||
if (isWalking)
|
||||
AnimationTree.Get(PARAMETERS_PLAYBACK).As<AnimationNodeStateMachinePlayback>().Travel(IDLE_BACK_WALK);
|
||||
else
|
||||
AnimationTree.Get(PARAMETERS_PLAYBACK).As<AnimationNodeStateMachinePlayback>().Travel(IDLE_BACK);
|
||||
}
|
||||
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).
|
||||
AnimatedSprite.FlipH = leftDotProduct > 0;
|
||||
// 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 (Mathf.Abs(forwardDotProduct) < rotateLowerThreshold)
|
||||
{
|
||||
if (isWalking)
|
||||
AnimationTree.Get(PARAMETERS_PLAYBACK).As<AnimationNodeStateMachinePlayback>().Travel(IDLE_LEFT_WALK);
|
||||
else
|
||||
AnimationTree.Get(PARAMETERS_PLAYBACK).As<AnimationNodeStateMachinePlayback>().Travel(IDLE_LEFT);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void LoadShader(string shaderPath)
|
||||
{
|
||||
var shader = GD.Load<Shader>(shaderPath);
|
||||
AnimatedSprite.Material = new ShaderMaterial();
|
||||
var shaderMaterial = (ShaderMaterial)AnimatedSprite.Material;
|
||||
shaderMaterial.Shader = shader;
|
||||
var shader = GD.Load<Shader>(shaderPath);
|
||||
AnimatedSprite.Material = new ShaderMaterial();
|
||||
var shaderMaterial = (ShaderMaterial)AnimatedSprite.Material;
|
||||
shaderMaterial.Shader = shader;
|
||||
}
|
||||
|
||||
private void AnimationTree_AnimationFinished(StringName animName)
|
||||
{
|
||||
if (animName == PRIMARY_ATTACK || animName == SECONDARY_ATTACK || animName == PRIMARY_SKILL)
|
||||
{
|
||||
AnimationTree.Get("parameters/playback").As<AnimationNodeStateMachinePlayback>().Travel(IDLE_FORWARD);
|
||||
}
|
||||
if (animName == PRIMARY_ATTACK || animName == SECONDARY_ATTACK || animName == PRIMARY_SKILL)
|
||||
{
|
||||
AnimationTree.Get("parameters/playback").As<AnimationNodeStateMachinePlayback>().Travel(IDLE_FORWARD);
|
||||
}
|
||||
}
|
||||
|
||||
private void SetShaderValue(float shaderValue)
|
||||
{
|
||||
var shaderMaterial = (ShaderMaterial)AnimatedSprite.Material;
|
||||
shaderMaterial.SetShaderParameter("progress", shaderValue);
|
||||
var shaderMaterial = (ShaderMaterial)AnimatedSprite.Material;
|
||||
shaderMaterial.SetShaderParameter("progress", shaderValue);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,46 +23,46 @@ public partial class Ballos : Enemy, IHasPrimaryAttack, IHasSecondaryAttack
|
||||
|
||||
public void OnReady()
|
||||
{
|
||||
SetPhysicsProcess(true);
|
||||
((EnemyModelView2D)_enemyModelView).Hitbox.AreaEntered += Hitbox_AreaEntered;
|
||||
SetPhysicsProcess(true);
|
||||
((EnemyModelView2D)_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 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());
|
||||
}
|
||||
|
||||
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();
|
||||
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();
|
||||
_enemyModelView.PlayPrimaryAttackAnimation();
|
||||
}
|
||||
|
||||
public void SecondaryAttack()
|
||||
{
|
||||
_enemyModelView.PlaySecondaryAttackAnimation();
|
||||
_enemyModelView.PlaySecondaryAttackAnimation();
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,8 +1,27 @@
|
||||
[gd_scene load_steps=6 format=3 uid="uid://feegakykn3fv"]
|
||||
[gd_scene load_steps=8 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="PackedScene" uid="uid://c5xijwxkg4pf6" path="res://src/enemy/enemy_types/05. ballos/BallosModelView.tscn" id="2_v2urn"]
|
||||
|
||||
[sub_resource type="Resource" id="Resource_ko6aj"]
|
||||
script = ExtResource("2_iy2fp")
|
||||
CurrentHP = 100.0
|
||||
MaximumHP = 100
|
||||
CurrentAttack = 20
|
||||
CurrentDefense = 12
|
||||
MaxAttack = 20
|
||||
MaxDefense = 12
|
||||
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"
|
||||
|
||||
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_cwfph"]
|
||||
radius = 0.717471
|
||||
height = 2.02807
|
||||
@@ -16,12 +35,12 @@ radius = 1.20703
|
||||
|
||||
[node name="Ballos" type="CharacterBody3D"]
|
||||
process_mode = 1
|
||||
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -1, 0)
|
||||
collision_layer = 10
|
||||
collision_mask = 11
|
||||
axis_lock_linear_y = true
|
||||
axis_lock_angular_x = true
|
||||
script = ExtResource("1_v2urn")
|
||||
_enemyStatResource = SubResource("Resource_ko6aj")
|
||||
|
||||
[node name="CollisionShape" type="CollisionShape3D" parent="."]
|
||||
unique_name_in_owner = true
|
||||
|
||||
@@ -796,6 +796,7 @@ size = Vector2i(400, 400)
|
||||
render_target_update_mode = 4
|
||||
|
||||
[node name="AnimatedSprite" type="AnimatedSprite2D" parent="Sprite3D/SubViewportContainer/SubViewport"]
|
||||
unique_name_in_owner = true
|
||||
sprite_frames = SubResource("SpriteFrames_mlptn")
|
||||
animation = &"idle_front_walk"
|
||||
offset = Vector2(200, 200)
|
||||
|
||||
Reference in New Issue
Block a user