Chinthe animations

This commit is contained in:
2025-02-28 03:10:16 -08:00
parent bcb3262743
commit 2439791d05
15 changed files with 627 additions and 132 deletions

View File

@@ -11,7 +11,7 @@ config_version=5
[application]
config/name="GameJamDungeon"
run/main_scene="uid://d1gjaijijd5ot"
run/main_scene="uid://bc1sp6xwe0j65"
run/print_header=false
config/features=PackedStringArray("4.4", "C#", "GL Compatibility")
boot_splash/show_image=false

View File

@@ -0,0 +1,145 @@
using Chickensoft.AutoInject;
using Chickensoft.Introspection;
using Godot;
namespace GameJamDungeon;
[Meta(typeof(IAutoNode))]
public partial class ChintheModelView : EnemyModelView2D
{
private const string INACTIVE_FRONT = "inactive_front";
private const string INACTIVE_LEFT = "inactive_left";
private const string INACTIVE_BACK = "inactive_back";
private const string ACTIVATE_FRONT = "activate_front";
private const string ACTIVATE_LEFT = "activate_left";
private const string ACTIVATE_BACK = "activate_back";
public const string TELEPORT = "teleport";
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 PARAMETERS_PLAYBACK = "parameters/playback";
public override void _Notification(int what) => this.Notify(what);
[Export] public bool CanMove = false;
private bool _activated = false;
public void OnReady()
{
AnimationTree.AnimationFinished += AnimationTree_AnimationFinished1;
}
private void AnimationTree_AnimationFinished1(StringName animName)
{
if (animName == IDLE_FORWARD_WALK)
AnimationTree.Get(PARAMETERS_PLAYBACK).As<AnimationNodeStateMachinePlayback>().Travel(IDLE_FORWARD);
if (animName == IDLE_LEFT_WALK)
AnimationTree.Get(PARAMETERS_PLAYBACK).As<AnimationNodeStateMachinePlayback>().Travel(IDLE_LEFT);
if (animName == IDLE_BACK_WALK)
AnimationTree.Get(PARAMETERS_PLAYBACK).As<AnimationNodeStateMachinePlayback>().Travel(IDLE_BACK);
}
public void PlayTeleportAnimation()
{
AnimationTree.Get(PARAMETERS_PLAYBACK).As<AnimationNodeStateMachinePlayback>().Travel(TELEPORT);
}
public void PlayActivateFrontAnimation()
{
AnimationTree.Get(PARAMETERS_PLAYBACK).As<AnimationNodeStateMachinePlayback>().Travel(ACTIVATE_FRONT);
_activated = true;
}
public void PlayActivateLeftAnimation()
{
AnimationTree.Get(PARAMETERS_PLAYBACK).As<AnimationNodeStateMachinePlayback>().Travel(ACTIVATE_LEFT);
_activated = true;
}
public void PlayActivateBackAnimation()
{
AnimationTree.Get(PARAMETERS_PLAYBACK).As<AnimationNodeStateMachinePlayback>().Travel(ACTIVATE_BACK);
_activated = true;
}
public override void RotateModel(
Basis enemyBasis,
Vector3 cameraDirection,
float rotateUpperThreshold,
float rotateLowerThreshold,
bool isWalking)
{
var enemyForwardDirection = enemyBasis.Z;
var enemyLeftDirection = enemyBasis.X;
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 (!_activated)
AnimationTree.Get(PARAMETERS_PLAYBACK).As<AnimationNodeStateMachinePlayback>().Travel(INACTIVE_FRONT);
else 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 (!_activated)
AnimationTree.Get(PARAMETERS_PLAYBACK).As<AnimationNodeStateMachinePlayback>().Travel(INACTIVE_BACK);
else 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 (!_activated)
AnimationTree.Get(PARAMETERS_PLAYBACK).As<AnimationNodeStateMachinePlayback>().Travel(INACTIVE_LEFT);
else 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;
}
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);
}
}
private void SetShaderValue(float shaderValue)
{
var shaderMaterial = (ShaderMaterial)AnimatedSprite.Material;
shaderMaterial.SetShaderParameter("progress", shaderValue);
}
}

View File

@@ -0,0 +1 @@
uid://tcupay5n2quq

View File

@@ -67,7 +67,7 @@ public partial class EnemyModelView2D : Node3D, IEnemyModelView
public void RotateModel(Basis enemyBasis, Vector3 cameraDirection, bool isWalking) => RotateModel(enemyBasis, cameraDirection, 0.55f, 0.45f, isWalking);
public void RotateModel(
public virtual void RotateModel(
Basis enemyBasis,
Vector3 cameraDirection,
float rotateUpperThreshold,

View File

@@ -4,7 +4,7 @@ namespace GameJamDungeon;
public interface INavigationAgentClient
{
public void CalculateVelocity(Vector3 currentPosition);
public void CalculateVelocity(Vector3 currentPosition, bool canMove);
public void SetTarget(Vector3 target);

View File

@@ -18,6 +18,7 @@ public partial class NavigationAgentClient : Node3D, INavigationAgentClient
private Vector3 _currentTarget = Vector3.Zero;
private Timer _thinkTimer;
private bool _canMove = false;
public void Setup()
{
@@ -40,12 +41,16 @@ public partial class NavigationAgentClient : Node3D, INavigationAgentClient
private void NavAgent_VelocityComputed(Vector3 safeVelocity)
{
if (!_canMove)
return;
var enemy = GetParent() as IEnemy;
enemy.Move(safeVelocity);
}
public void CalculateVelocity(Vector3 currentPosition)
public void CalculateVelocity(Vector3 currentPosition, bool canMove)
{
_canMove = canMove;
var nextPathPosition = NavAgent.GetNextPathPosition();
var newVelocity = currentPosition.DirectionTo(nextPathPosition) * 2f;

View File

@@ -27,6 +27,9 @@ public partial class Sproingy : Enemy, IHasPrimaryAttack, ICanPatrol
{
_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) < 3.5f)
_enemyLogic.Input(new EnemyLogic.Input.StartAttacking());
if (_enemyLogic.Value is EnemyLogic.State.FollowPlayer && GlobalPosition.DistanceTo(_player.CurrentPosition) > 30f)
@@ -34,7 +37,7 @@ public partial class Sproingy : Enemy, IHasPrimaryAttack, ICanPatrol
if (_enemyLogic.Value is EnemyLogic.State.Attacking && GlobalPosition.DistanceTo(_player.CurrentPosition) > 5f)
_enemyLogic.Input(new EnemyLogic.Input.Alerted());
_navigationAgentClient.CalculateVelocity(GlobalPosition);
_navigationAgentClient.CalculateVelocity(GlobalPosition, true);
base._PhysicsProcess(delta);
}

View File

@@ -32,7 +32,7 @@ public partial class Michael : Enemy, IHasPrimaryAttack, ICanPatrol
if (_enemyLogic.Value is EnemyLogic.State.Attacking && GlobalPosition.DistanceTo(_player.CurrentPosition) > 3f)
_enemyLogic.Input(new EnemyLogic.Input.Alerted());
_navigationAgentClient.CalculateVelocity(GlobalPosition);
_navigationAgentClient.CalculateVelocity(GlobalPosition, true);
base._PhysicsProcess(delta);
}

File diff suppressed because one or more lines are too long

View File

@@ -1,46 +1,87 @@
using Chickensoft.AutoInject;
using Chickensoft.Introspection;
using GameJamDungeon.src.enemy;
using Godot;
namespace GameJamDungeon;
[Meta(typeof(IAutoNode))]
public partial class Chinte : Enemy, IHasPrimaryAttack
public partial class Chinthe : Enemy, IHasPrimaryAttack, ICanPatrol
{
public override void _Notification(int what) => this.Notify(what);
private const string PARAMETERS_PLAYBACK = "parameters/playback";
[Export]
public ElementType PrimaryAttackElementalType { get; set; } = ElementType.None;
[Export]
public double PrimaryAttackElementalDamageBonus { get; set; } = 1.0;
[Node] private INavigationAgentClient _navigationAgentClient { get; set; } = default!;
[Node] private ChintheModelView EnemyModelView { get; set; } = default!;
public void OnReady()
{
SetPhysicsProcess(true);
((EnemyModelView2D)_enemyModelView).Hitbox.AreaEntered += Hitbox_AreaEntered;
EnemyModelView.Hitbox.AreaEntered += Hitbox_AreaEntered;
}
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) < 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);
base._PhysicsProcess(delta);
}
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()
{
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();
}
private void Hitbox_AreaEntered(Area3D area)
{
var target = area.GetOwner();

View File

@@ -1,8 +1,29 @@
[gd_scene load_steps=6 format=3 uid="uid://c6tqt27ql8s35"]
[gd_scene load_steps=11 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="Script" uid="uid://dnkmr0eq1sij0" path="res://src/enemy/EnemyStatResource.cs" id="2_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"]
[sub_resource type="Resource" id="Resource_d665t"]
script = ExtResource("2_120m2")
CurrentHP = 200.0
MaximumHP = 200.0
CurrentAttack = 10
CurrentDefense = 10
MaxAttack = 10
MaxDefense = 10
ExpFromDefeat = 100
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.226425
height = 2.02807
@@ -11,6 +32,12 @@ height = 2.02807
height = 5.0
radius = 1.0
[sub_resource type="Resource" id="Resource_120m2"]
script = ExtResource("3_567xa")
Name = "Chinthe"
Description = "pupy"
metadata/_custom_type_script = "uid://dlsgyx4i1jmp3"
[sub_resource type="SphereShape3D" id="SphereShape3D_8vcnq"]
radius = 1.20703
@@ -20,21 +47,19 @@ collision_layer = 10
collision_mask = 11
axis_lock_linear_y = true
axis_lock_angular_x = true
axis_lock_angular_z = true
motion_mode = 1
script = ExtResource("1_vw2ww")
_enemyStatResource = SubResource("Resource_d665t")
[node name="NavigationAgentClient" parent="." instance=ExtResource("3_d665t")]
unique_name_in_owner = true
[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="NavAgent" type="NavigationAgent3D" parent="."]
unique_name_in_owner = true
path_max_distance = 3.01
simplify_path = true
avoidance_enabled = true
radius = 2.0
debug_path_custom_color = Color(1, 0, 0, 1)
[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)
@@ -61,6 +86,8 @@ target_position = Vector3(0, 0, -5)
collision_mask = 3
[node name="EnemyModelView" parent="." instance=ExtResource("3_ncr2e")]
unique_name_in_owner = true
EnemyLoreInfo = SubResource("Resource_120m2")
[node name="Collision" type="Area3D" parent="."]
collision_layer = 2048

View File

@@ -0,0 +1,11 @@
[gd_scene load_steps=3 format=3 uid="uid://byqhrq73p1y7v"]
[ext_resource type="PackedScene" uid="uid://c6tqt27ql8s35" path="res://src/enemy/enemy_types/07. chinthe/Chinthe.tscn" id="1_un7uu"]
[ext_resource type="PackedScene" uid="uid://cfecvvav8kkp6" path="res://src/player/Player.tscn" id="2_w0g4y"]
[node name="ChintheTest" type="Node3D"]
[node name="Chinthe" parent="." instance=ExtResource("1_un7uu")]
[node name="Player" parent="." instance=ExtResource("2_w0g4y")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, -1.12146, 4.44506)

View File

@@ -10,6 +10,9 @@ public partial class EnemyLogic
[Meta, Id("enemy_logic_state_activated")]
public partial record Activated : Alive
{
public Activated()
{
}
}
}
}

View File

@@ -36,6 +36,8 @@ public partial class EnemyLogic
var enemy = Get<IEnemy>();
if (enemy is IHasRangedAttack rangedAttacker)
rangedAttacker.RangedAttack();
if (enemy is Chinthe chinthe)
chinthe.Activate();
return To<FollowPlayer>();
}
}

View File

@@ -1,10 +1,12 @@
[gd_scene load_steps=13 format=3 uid="uid://bc1sp6xwe0j65"]
[gd_scene load_steps=15 format=3 uid="uid://bc1sp6xwe0j65"]
[ext_resource type="Script" uid="uid://dwt6302nsf4vq" path="res://src/map/dungeon/code/DungeonFloor.cs" id="1_0ecnn"]
[ext_resource type="Script" uid="uid://b1x125h0tya2w" path="res://addons/SimpleDungeons/DungeonGenerator3D.gd" id="2_cxmwa"]
[ext_resource type="PackedScene" uid="uid://dpec2lbt83dhe" path="res://src/map/dungeon/rooms/Set A/03. Antechamber A.tscn" id="3_gkkr3"]
[ext_resource type="PackedScene" uid="uid://b82dx66mgs2d7" path="res://src/map/dungeon/rooms/Set A/08. Basin Room.tscn" id="8_5rblf"]
[ext_resource type="PackedScene" uid="uid://cmvimr0pvsgqy" path="res://src/enemy/enemy_types/10. Eden Pillar/Eden Pillar.tscn" id="10_atq1f"]
[ext_resource type="PackedScene" uid="uid://cfecvvav8kkp6" path="res://src/player/Player.tscn" id="11_sdyti"]
[ext_resource type="PackedScene" uid="uid://c6tqt27ql8s35" path="res://src/enemy/enemy_types/07. chinthe/Chinthe.tscn" id="12_1l8yt"]
[ext_resource type="PackedScene" uid="uid://dbvr8ewajja6a" path="res://src/enemy/EnemyDatabase.tscn" id="12_aw26s"]
[ext_resource type="PackedScene" uid="uid://cihbmyo0ltq4m" path="res://src/map/dungeon/rooms/Set A/19. Floor Exit A.tscn" id="12_n02rw"]
[ext_resource type="PackedScene" uid="uid://bksq62muhk3h5" path="res://src/enemy/enemy_types/01. sproingy/Sproingy.tscn" id="13_kwaga"]
@@ -176,3 +178,13 @@ EnemyList = Array[PackedScene]([ExtResource("13_kwaga"), ExtResource("14_gkkr3")
[node name="Eden Pillar" parent="." instance=ExtResource("10_atq1f")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -29.7976, -1.33866, 30.3232)
[node name="Player" parent="." instance=ExtResource("11_sdyti")]
unique_name_in_owner = true
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2.07588, -1.71359, 12.7507)
[node name="Chinthe" parent="." instance=ExtResource("12_1l8yt")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 8.2934, -0.81355, 21.6535)
PrimaryAttackElementalType = 0
PrimaryAttackElementalDamageBonus = 1.0
_movementSpeed = 2.0