Compare commits

..

8 Commits

Author SHA1 Message Date
18c8b741dd can plant weapon in targetables, dash towards it, jump in the air.
Some checks failed
Create tag and build when new code gets to main / BumpTag (push) Successful in 28s
Create tag and build when new code gets to main / Export (push) Failing after 1m47s
2026-01-24 15:22:16 +01:00
b84b7e4dd5 aim dashing through targetable entities now possible 2026-01-24 13:49:16 +01:00
4d419b9010 basic healthbars for enemies 2026-01-23 13:31:11 +01:00
8b2bf3e32e fixed a going through wall issue 2026-01-21 17:31:24 +01:00
db49703326 added fixed dash targets and can dash towards enemies to hit them, get a knockback or dash through if killed
Some checks failed
Create tag and build when new code gets to main / BumpTag (push) Successful in 22s
Create tag and build when new code gets to main / Export (push) Failing after 1m51s
2026-01-21 16:46:20 +01:00
fb78add739 export target variables and made a targetable interface 2026-01-21 14:21:47 +01:00
04121f18a4 basic targeting system 2026-01-21 12:32:58 +01:00
fa029b9e53 fixed resources constructors
All checks were successful
Create tag and build when new code gets to main / BumpTag (push) Successful in 24s
Create tag and build when new code gets to main / Export (push) Successful in 15m37s
2026-01-21 10:13:00 +01:00
40 changed files with 885 additions and 216 deletions

View File

@@ -1,5 +1,9 @@
<wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation"> <wpf:ResourceDictionary xml:space="preserve" xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:s="clr-namespace:System;assembly=mscorlib" xmlns:ss="urn:shemas-jetbrains-com:settings-storage-xaml" xmlns:wpf="http://schemas.microsoft.com/winfx/2006/xaml/presentation">
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAction_00601_002Ecs_002Fl_003AC_0021_003FUsers_003FMinimata_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F7c0f83388bfc4d2c9d09befcec9dd79bc90908_003Fb8_003F4d300c4d_003FAction_00601_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAction_00601_002Ecs_002Fl_003AC_0021_003FUsers_003FMinimata_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F7c0f83388bfc4d2c9d09befcec9dd79bc90908_003Fb8_003F4d300c4d_003FAction_00601_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AAction_00602_002Ecs_002Fl_003AC_0021_003FUsers_003FMinimata_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FDecompilerCache_003Fdecompiler_003F7c0f83388bfc4d2c9d09befcec9dd79bc90908_003F87_003Fded27e2d_003FAction_00602_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ACharacterBody3D_002Ecs_002Fl_003AC_0021_003FUsers_003FMinimata_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003Fe56b84c3fa498fb86fc1eba376f62f482127e3fe80415c5fb2acde2bf6d89793_003FCharacterBody3D_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003AEnemy_005FScriptMethods_002Egenerated_002Ecs_002Fl_003AC_0021_003FUsers_003FMinimata_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003F8e71dc81611862c01a2cb998a1f327de14747655_003FEnemy_005FScriptMethods_002Egenerated_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ANode_002Ecs_002Fl_003AC_0021_003FUsers_003FMinimata_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003F716d154fef5cbe863cd637bd32beda6e3cec5f12e8fed2dc5b2d8149a0d558ab_003FNode_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ANode_002Ecs_002Fl_003AC_0021_003FUsers_003FMinimata_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003Fdf73a4db74df89d59655c5fb6326406f47fbfa9af1fa81518fe0a07c49d34133_003FNode_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ANode_002Ecs_002Fl_003AC_0021_003FUsers_003FMinimata_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003Fdf73a4db74df89d59655c5fb6326406f47fbfa9af1fa81518fe0a07c49d34133_003FNode_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASceneTree_002Ecs_002Fl_003AC_0021_003FUsers_003FMinimata_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003F8d6960554e939a669841b1ece03d27df4ab42f92bb80be3767eaec8cdaccf84b_003FSceneTree_002Ecs/@EntryIndexedValue">ForceIncluded</s:String> <s:String x:Key="/Default/CodeInspection/ExcludedFiles/FilesAndFoldersToSkip2/=7020124F_002D9FFC_002D4AC3_002D8F3D_002DAAB8E0240759_002Ff_003ASceneTree_002Ecs_002Fl_003AC_0021_003FUsers_003FMinimata_003FAppData_003FRoaming_003FJetBrains_003FRider2025_002E1_003Fresharper_002Dhost_003FSourcesCache_003F8d6960554e939a669841b1ece03d27df4ab42f92bb80be3767eaec8cdaccf84b_003FSceneTree_002Ecs/@EntryIndexedValue">ForceIncluded</s:String>
<s:Boolean x:Key="/Default/UserDictionary/Words/=floorplane/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary> <s:Boolean x:Key="/Default/UserDictionary/Words/=floorplane/@EntryIndexedValue">True</s:Boolean></wpf:ResourceDictionary>

BIN
assets/ui/white-square-100px.png (Stored with Git LFS) Normal file

Binary file not shown.

View File

@@ -0,0 +1,42 @@
[remap]
importer="texture"
type="CompressedTexture2D"
uid="uid://vpcxswwn1mt6"
path.s3tc="res://.godot/imported/white-square-100px.png-45d5a1e60348740b59c87085347e502a.s3tc.ctex"
path.etc2="res://.godot/imported/white-square-100px.png-45d5a1e60348740b59c87085347e502a.etc2.ctex"
metadata={
"imported_formats": ["s3tc_bptc", "etc2_astc"],
"vram_texture": true
}
[deps]
source_file="res://assets/ui/white-square-100px.png"
dest_files=["res://.godot/imported/white-square-100px.png-45d5a1e60348740b59c87085347e502a.s3tc.ctex", "res://.godot/imported/white-square-100px.png-45d5a1e60348740b59c87085347e502a.etc2.ctex"]
[params]
compress/mode=2
compress/high_quality=false
compress/lossy_quality=0.7
compress/uastc_level=0
compress/rdo_quality_loss=0.0
compress/hdr_compression=1
compress/normal_map=0
compress/channel_pack=0
mipmaps/generate=true
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
process/channel_remap/red=0
process/channel_remap/green=1
process/channel_remap/blue=2
process/channel_remap/alpha=3
process/fix_alpha_border=true
process/premult_alpha=false
process/normal_map_invert_y=false
process/hdr_as_srgb=false
process/hdr_clamp_exposure=false
process/size_limit=0
detect_3d/compress_to=0

View File

@@ -20,4 +20,12 @@ public partial class CDamageable : Node, IDamageable
DamageTaken?.Invoke(this, finalDamageRecord); DamageTaken?.Invoke(this, finalDamageRecord);
return finalDamageRecord; return finalDamageRecord;
} }
public DamageRecord ComputeDamage(DamageRecord damageRecord)
{
var finalDamage = 0f;
foreach (var damageable in DamageModifiers.ToIDamageables())
finalDamage += damageable.ComputeDamage(damageRecord).Damage.DamageDealt;
return damageRecord with { Damage = new RDamage(finalDamage, damageRecord.Damage.DamageType) };
}
} }

View File

@@ -6,16 +6,12 @@ using Movementtests.systems.damage;
public partial class RDamage : Resource public partial class RDamage : Resource
{ {
[Export(PropertyHint.Range, "0,10,0.1,or_greater")] [Export(PropertyHint.Range, "0,10,0.1,or_greater")]
public float DamageDealt = 1.0f; public float DamageDealt { get; set;}
[Export] [Export]
public EDamageTypes DamageType = EDamageTypes.Normal; public EDamageTypes DamageType { get; set;}
public RDamage() public RDamage() : this(1.0f, EDamageTypes.Normal) {}
{
DamageDealt = 1.0f;
DamageType = EDamageTypes.Normal;
}
public RDamage(float damageDealt, EDamageTypes damageType) public RDamage(float damageDealt, EDamageTypes damageType)
{ {
DamageDealt = damageDealt; DamageDealt = damageDealt;

View File

@@ -9,15 +9,11 @@ public partial class RDamageModifier : Resource, IDamageable
public event Action<IDamageable, DamageRecord> DamageTaken; public event Action<IDamageable, DamageRecord> DamageTaken;
[Export] [Export]
public EDamageTypes DamageType = EDamageTypes.Normal; public EDamageTypes DamageType { get; set;}
[Export] [Export]
public float Modifier = 1.0f; public float Modifier { get; set;}
public RDamageModifier() public RDamageModifier() : this(EDamageTypes.Normal, 1.0f) {}
{
Modifier = 1.0f;
DamageType = EDamageTypes.Normal;
}
public RDamageModifier(EDamageTypes damageType, float modifier) public RDamageModifier(EDamageTypes damageType, float modifier)
{ {
Modifier = modifier; Modifier = modifier;
@@ -34,4 +30,13 @@ public partial class RDamageModifier : Resource, IDamageable
DamageTaken?.Invoke(this, finalDamageRecord); DamageTaken?.Invoke(this, finalDamageRecord);
return finalDamageRecord; return finalDamageRecord;
} }
public DamageRecord ComputeDamage(DamageRecord damageRecord)
{
if (damageRecord.Damage.DamageType != DamageType)
return damageRecord with { Damage = new RDamage(0, damageRecord.Damage.DamageType) };
var finalDamage = damageRecord.Damage.DamageDealt * Modifier;
return damageRecord with { Damage = new RDamage(finalDamage, damageRecord.Damage.DamageType) };
}
} }

View File

@@ -5,7 +5,7 @@ using Movementtests.interfaces;
[GlobalClass] [GlobalClass]
public partial class CHealth : Node, IHealthable public partial class CHealth : Node, IHealthable
{ {
public event Action<IHealthable, float> HealthChanged; public event Action<IHealthable, HealthChangedRecord> HealthChanged;
public event Action<IHealthable> HealthDepleted; public event Action<IHealthable> HealthDepleted;
[Export] [Export]
@@ -18,15 +18,18 @@ public partial class CHealth : Node, IHealthable
CurrentHealth = RHealth.StartingHealth; CurrentHealth = RHealth.StartingHealth;
} }
public void ReduceHealth(IDamageable source, DamageRecord damageRecord) public HealthChangedRecord ReduceHealth(IDamageable source, DamageRecord damageRecord)
{ {
var previousHealth = CurrentHealth;
CurrentHealth -= damageRecord.Damage.DamageDealt; CurrentHealth -= damageRecord.Damage.DamageDealt;
HealthChanged?.Invoke(this, CurrentHealth); var record = new HealthChangedRecord(CurrentHealth, previousHealth, RHealth.StartingHealth);
HealthChanged?.Invoke(this, record);
if (CurrentHealth <= 0) if (CurrentHealth <= 0)
{ {
CurrentHealth = 0; CurrentHealth = 0;
HealthDepleted?.Invoke(this); HealthDepleted?.Invoke(this);
} }
return record;
} }
} }

View File

@@ -0,0 +1,26 @@
using Godot;
using System;
using Movementtests.interfaces;
[GlobalClass]
public partial class CHealthbar : Node3D
{
private Sprite3D _currentHealth;
private float _initialXScale;
public override void _Ready()
{
Visible = false;
_currentHealth = GetNode<Sprite3D>("Health");
_initialXScale = _currentHealth.Scale.X;
}
public void OnHealthChanged(HealthChangedRecord healthChanged)
{
if (healthChanged.MaxHealth == 0) return;
Visible = true;
var healthPercentage = healthChanged.CurrentHealth / healthChanged.MaxHealth;
_currentHealth.Scale = new Vector3(_initialXScale * healthPercentage, _currentHealth.Scale.Y, _currentHealth.Scale.Z);
}
}

View File

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

View File

@@ -0,0 +1,27 @@
[gd_scene load_steps=3 format=3 uid="uid://bwx2um43k0ou4"]
[ext_resource type="Texture2D" uid="uid://vpcxswwn1mt6" path="res://assets/ui/white-square-100px.png" id="1_jlvej"]
[ext_resource type="Script" uid="uid://dve6vg6yvg4y8" path="res://components/health/CHealthbar.cs" id="1_w5itk"]
[node name="CHealthBar" type="Node3D"]
script = ExtResource("1_w5itk")
[node name="Bar" type="Sprite3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 0.1, 0, 0, 0, 1, 0, 0, 0)
cast_shadow = 0
billboard = 1
transparent = false
double_sided = false
no_depth_test = true
texture = ExtResource("1_jlvej")
[node name="Health" type="Sprite3D" parent="."]
transform = Transform3D(0.99, 0, 0, 0, 0.09, 0, 0, 0, 1, 0, 0, 0)
cast_shadow = 0
modulate = Color(0.7191989, 0.19340843, 1.92523e-07, 1)
billboard = 1
transparent = false
double_sided = false
no_depth_test = true
render_priority = 10
texture = ExtResource("1_jlvej")

View File

@@ -6,13 +6,9 @@ using Movementtests.interfaces;
public partial class RHealth : Resource public partial class RHealth : Resource
{ {
[Export] [Export]
public float StartingHealth = 100.0f; public float StartingHealth { get; set;}
public RHealth()
{
StartingHealth = 100.0f;
}
public RHealth() : this(100.0f) {}
public RHealth(float startingHealth) public RHealth(float startingHealth)
{ {
StartingHealth = startingHealth; StartingHealth = startingHealth;

View File

@@ -20,6 +20,7 @@ public partial class CKnockback : Node3D, IKnockbackable
var knockbackDirection = GlobalPosition - _damageRecord.Source.GlobalPosition; var knockbackDirection = GlobalPosition - _damageRecord.Source.GlobalPosition;
_damageRecord = null; _damageRecord = null;
return knockbackDirection.Normalized() * RKnockback.Modifier; var finalKnockback = knockbackDirection.Normalized() * RKnockback.Modifier;
return finalKnockback;
} }
} }

View File

@@ -6,12 +6,9 @@ using Movementtests.interfaces;
public partial class RKnockback : Resource public partial class RKnockback : Resource
{ {
[Export] [Export]
public float Modifier = 1.0f; public float Modifier { get; set;}
public RKnockback() public RKnockback() : this(1.0f) {}
{
Modifier = 1.0f;
}
public RKnockback(float modifier) public RKnockback(float modifier)
{ {
Modifier = modifier; Modifier = modifier;

View File

@@ -14,13 +14,7 @@ public partial class RMovement : Resource
[Export(PropertyHint.Range, "0,20,1,or_greater")] [Export(PropertyHint.Range, "0,20,1,or_greater")]
public float TargetHeight { get; set;} public float TargetHeight { get; set;}
public RMovement() public RMovement() : this(1.0f, 0.0f, 0.0f, 0.0f) {}
{
Speed = 3.0f;
Acceleration = 1.0f;
GravityModifier = 1.0f;
TargetHeight = 10.0f;
}
public RMovement(float speed, float acceleration, float gravityModifier, float targetHeight) public RMovement(float speed, float acceleration, float gravityModifier, float targetHeight)
{ {
Speed = speed; Speed = speed;

7
global.json Normal file
View File

@@ -0,0 +1,7 @@
{
"sdk": {
"version": "9.0.0",
"rollForward": "latestMajor",
"allowPrerelease": true
}
}

View File

@@ -8,5 +8,7 @@ public record DamageRecord(Node3D Source, RDamage Damage);
public interface IDamageable public interface IDamageable
{ {
event Action<IDamageable, DamageRecord> DamageTaken; event Action<IDamageable, DamageRecord> DamageTaken;
DamageRecord TakeDamage(DamageRecord damageRecord); DamageRecord TakeDamage(DamageRecord damageRecord);
DamageRecord ComputeDamage(DamageRecord damageRecord);
} }

View File

@@ -3,14 +3,16 @@ using Godot;
namespace Movementtests.interfaces; namespace Movementtests.interfaces;
public record HealthChangedRecord(float CurrentHealth, float PreviousHealth, float MaxHealth);
public interface IHealthable public interface IHealthable
{ {
event Action<IHealthable, float> HealthChanged; event Action<IHealthable, HealthChangedRecord> HealthChanged;
event Action<IHealthable> HealthDepleted; event Action<IHealthable> HealthDepleted;
[Export] RHealth RHealth { get; set; } [Export] RHealth RHealth { get; set; }
float CurrentHealth { get; set; } float CurrentHealth { get; set; }
void ReduceHealth(IDamageable source, DamageRecord damageRecord); HealthChangedRecord ReduceHealth(IDamageable source, DamageRecord damageRecord);
} }

12
interfaces/IStunnable.cs Normal file
View File

@@ -0,0 +1,12 @@
using Godot;
namespace Movementtests.interfaces;
public interface IStunnable
{
bool IsStunned { get; set; }
[Export] float StunDuration { get; set; }
void Stun();
void Unstun();
}

View File

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

View File

@@ -0,0 +1,8 @@
using Godot;
namespace Movementtests.interfaces;
public interface ITargetable
{
Vector3 GetTargetGlobalPosition();
}

View File

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

View File

@@ -1,11 +1,11 @@
[gd_scene load_steps=20 format=3 uid="uid://q7uc1h2jpbd2"] [gd_scene load_steps=21 format=3 uid="uid://q7uc1h2jpbd2"]
[ext_resource type="PackedScene" uid="uid://bei4nhkf8lwdo" path="res://player_controller/PlayerController.tscn" id="1_62kkh"] [ext_resource type="PackedScene" uid="uid://bei4nhkf8lwdo" path="res://player_controller/PlayerController.tscn" id="1_62kkh"]
[ext_resource type="Material" uid="uid://31aulub2nqov" path="res://assets/greybox/m_greybox.tres" id="2_3uydm"] [ext_resource type="Material" uid="uid://31aulub2nqov" path="res://assets/greybox/m_greybox.tres" id="2_3uydm"]
[ext_resource type="Script" uid="uid://jitubgv6judn" path="res://components/damage/RDamage.cs" id="2_sysok"]
[ext_resource type="PackedScene" uid="uid://dxt0e2ugmttqq" path="res://scenes/enemies/grounded_enemy/grounded_enemy.tscn" id="3_3uydm"] [ext_resource type="PackedScene" uid="uid://dxt0e2ugmttqq" path="res://scenes/enemies/grounded_enemy/grounded_enemy.tscn" id="3_3uydm"]
[ext_resource type="Script" uid="uid://b4cwruitopcee" path="res://components/health/RDeathEffect.cs" id="5_7m3bq"]
[ext_resource type="PackedScene" uid="uid://cmlud1hwkd6sv" path="res://scenes/enemies/flying_enemy/flying_enemy.tscn" id="5_8fd2t"] [ext_resource type="PackedScene" uid="uid://cmlud1hwkd6sv" path="res://scenes/enemies/flying_enemy/flying_enemy.tscn" id="5_8fd2t"]
[ext_resource type="Script" uid="uid://dtpxijlnb2c5" path="res://components/movement/RMovement.cs" id="5_ybosk"]
[ext_resource type="Script" uid="uid://b44cse62qru7j" path="res://components/knockback/RKnockback.cs" id="6_1hrkh"]
[ext_resource type="PackedScene" uid="uid://c305mfrtumcyq" path="res://scenes/spawners/spawner.tscn" id="6_7m3bq"] [ext_resource type="PackedScene" uid="uid://c305mfrtumcyq" path="res://scenes/spawners/spawner.tscn" id="6_7m3bq"]
[ext_resource type="Resource" uid="uid://bqq6uukbdfysr" path="res://scenes/enemies/grounded_enemy/grounded_enemy_movement.tres" id="7_caohq"] [ext_resource type="Resource" uid="uid://bqq6uukbdfysr" path="res://scenes/enemies/grounded_enemy/grounded_enemy_movement.tres" id="7_caohq"]
[ext_resource type="Script" uid="uid://baiapod3csndf" path="res://components/health/RHealth.cs" id="9_2e4ci"] [ext_resource type="Script" uid="uid://baiapod3csndf" path="res://components/health/RHealth.cs" id="9_2e4ci"]
@@ -13,10 +13,7 @@
[ext_resource type="Resource" uid="uid://dgo65k2ceqfvy" path="res://scenes/enemies/flying_enemy/flying_enemy_damage.tres" id="9_gp7s3"] [ext_resource type="Resource" uid="uid://dgo65k2ceqfvy" path="res://scenes/enemies/flying_enemy/flying_enemy_damage.tres" id="9_gp7s3"]
[ext_resource type="Resource" uid="uid://bwqjaom4k7rc3" path="res://scenes/enemies/flying_enemy/flying_enemy_movement.tres" id="10_spw1u"] [ext_resource type="Resource" uid="uid://bwqjaom4k7rc3" path="res://scenes/enemies/flying_enemy/flying_enemy_movement.tres" id="10_spw1u"]
[ext_resource type="Resource" uid="uid://dg1xbjhyhgnnk" path="res://scenes/enemies/flying_enemy/flying_enemy_health.tres" id="11_2e4ci"] [ext_resource type="Resource" uid="uid://dg1xbjhyhgnnk" path="res://scenes/enemies/flying_enemy/flying_enemy_health.tres" id="11_2e4ci"]
[ext_resource type="PackedScene" uid="uid://qup00a7x2sji" path="res://scenes/FixedDashTarget/fixed_dashthrough_target.tscn" id="15_5fa36"]
[sub_resource type="Resource" id="Resource_2e4ci"]
script = ExtResource("2_sysok")
metadata/_custom_type_script = "uid://jitubgv6judn"
[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_0xm2m"] [sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_0xm2m"]
sky_horizon_color = Color(0.662243, 0.671743, 0.686743, 1) sky_horizon_color = Color(0.662243, 0.671743, 0.686743, 1)
@@ -40,9 +37,14 @@ ssil_radius = 8.4
sdfgi_use_occlusion = true sdfgi_use_occlusion = true
glow_enabled = true glow_enabled = true
[sub_resource type="Resource" id="Resource_sysok"] [sub_resource type="Resource" id="Resource_q21h6"]
script = ExtResource("5_7m3bq") script = ExtResource("6_1hrkh")
metadata/_custom_type_script = "uid://b4cwruitopcee" metadata/_custom_type_script = "uid://b44cse62qru7j"
[sub_resource type="Resource" id="Resource_5fa36"]
script = ExtResource("5_ybosk")
GravityModifier = 5.0
metadata/_custom_type_script = "uid://dtpxijlnb2c5"
[sub_resource type="Resource" id="Resource_ybosk"] [sub_resource type="Resource" id="Resource_ybosk"]
script = ExtResource("9_2e4ci") script = ExtResource("9_2e4ci")
@@ -54,7 +56,6 @@ metadata/_custom_type_script = "uid://baiapod3csndf"
[node name="Player" parent="." instance=ExtResource("1_62kkh")] [node name="Player" parent="." instance=ExtResource("1_62kkh")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 7.5) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 7.5)
TutorialDone = true TutorialDone = true
RDamage = SubResource("Resource_2e4ci")
[node name="WorldEnvironment" type="WorldEnvironment" parent="."] [node name="WorldEnvironment" type="WorldEnvironment" parent="."]
environment = SubResource("Environment_1bvp3") environment = SubResource("Environment_1bvp3")
@@ -92,6 +93,12 @@ use_collision = true
size = Vector3(6.5, 4, 17) size = Vector3(6.5, 4, 17)
material = ExtResource("2_3uydm") material = ExtResource("2_3uydm")
[node name="CSGBox3D10" type="CSGBox3D" parent="Greybox"]
transform = Transform3D(1, 0, 0, 0, 0.9659258, 0.25881904, 0, -0.25881904, 0.9659258, 13.653999, 0.9705714, -5.336278)
use_collision = true
size = Vector3(6.5, 4, 24.5)
material = ExtResource("2_3uydm")
[node name="CSGBox3D8" type="CSGBox3D" parent="Greybox"] [node name="CSGBox3D8" type="CSGBox3D" parent="Greybox"]
transform = Transform3D(0.81915206, 0, 0.57357645, 0, 1, 0, -0.57357645, 0, 0.81915206, -7.3460007, 0, -3.9585) transform = Transform3D(0.81915206, 0, 0.57357645, 0, 1, 0, -0.57357645, 0, 0.81915206, -7.3460007, 0, -3.9585)
use_collision = true use_collision = true
@@ -104,6 +111,12 @@ use_collision = true
size = Vector3(6.5, 11, 5.5) size = Vector3(6.5, 11, 5.5)
material = ExtResource("2_3uydm") material = ExtResource("2_3uydm")
[node name="CSGBox3D9" type="CSGBox3D" parent="Greybox"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 50.154, 0, -20.4585)
use_collision = true
size = Vector3(6.5, 2, 116)
material = ExtResource("2_3uydm")
[node name="CSGBox3D4" type="CSGBox3D" parent="Greybox"] [node name="CSGBox3D4" type="CSGBox3D" parent="Greybox"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 18.154, 4.5, -14.2085) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 18.154, 4.5, -14.2085)
use_collision = true use_collision = true
@@ -116,26 +129,34 @@ use_collision = true
size = Vector3(6.5, 11, 5.5) size = Vector3(6.5, 11, 5.5)
material = ExtResource("2_3uydm") material = ExtResource("2_3uydm")
[node name="FixedDashthroughTarget" parent="." instance=ExtResource("15_5fa36")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 9, 3.5, 2.5)
[node name="Enemy" parent="." node_paths=PackedStringArray("Target") instance=ExtResource("3_3uydm")] [node name="Enemy" parent="." node_paths=PackedStringArray("Target") instance=ExtResource("3_3uydm")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -16.83681) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 13.5, 2.5, -8.336809)
Target = NodePath("../Player") Target = NodePath("../Player")
DeathEffects = Array[Object]([SubResource("Resource_sysok")]) RKnockback = SubResource("Resource_q21h6")
RMovement = SubResource("Resource_5fa36")
[node name="Enemy2" parent="." node_paths=PackedStringArray("Target") instance=ExtResource("3_3uydm")] [node name="Enemy2" parent="." node_paths=PackedStringArray("Target") instance=ExtResource("3_3uydm")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -3, 0, -16.83681) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 13, 0, -17.33681)
Target = NodePath("../Player") Target = NodePath("../Player")
RMovement = SubResource("Resource_5fa36")
[node name="Enemy3" parent="." node_paths=PackedStringArray("Target") instance=ExtResource("3_3uydm")] [node name="Enemy3" parent="." node_paths=PackedStringArray("Target") instance=ExtResource("3_3uydm")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 2.5, 0, -16.83681) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 14, 4, -3.3368092)
Target = NodePath("../Player") Target = NodePath("../Player")
RMovement = SubResource("Resource_5fa36")
[node name="FlyingEnemy" parent="." node_paths=PackedStringArray("Target") instance=ExtResource("5_8fd2t")] [node name="FlyingEnemy" parent="." node_paths=PackedStringArray("Target") instance=ExtResource("5_8fd2t")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 8, 7, -16) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 8, 7, -16)
Target = NodePath("../Player") Target = NodePath("../Player")
RMovement = SubResource("Resource_5fa36")
[node name="FlyingEnemy2" parent="." node_paths=PackedStringArray("Target") instance=ExtResource("5_8fd2t")] [node name="FlyingEnemy2" parent="." node_paths=PackedStringArray("Target") instance=ExtResource("5_8fd2t")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 10, 7, -16) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 10, 7, -16)
Target = NodePath("../Player") Target = NodePath("../Player")
RMovement = SubResource("Resource_5fa36")
[node name="GroundedSpawner" parent="." node_paths=PackedStringArray("Target") instance=ExtResource("6_7m3bq")] [node name="GroundedSpawner" parent="." node_paths=PackedStringArray("Target") instance=ExtResource("6_7m3bq")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 12, 2.5, -15) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 12, 2.5, -15)

View File

@@ -1,4 +1,4 @@
[gd_scene load_steps=45 format=3 uid="uid://dmkw8cmalm5k"] [gd_scene load_steps=50 format=3 uid="uid://dmkw8cmalm5k"]
[ext_resource type="PackedScene" uid="uid://bei4nhkf8lwdo" path="res://player_controller/PlayerController.tscn" id="1_2vsi6"] [ext_resource type="PackedScene" uid="uid://bei4nhkf8lwdo" path="res://player_controller/PlayerController.tscn" id="1_2vsi6"]
[ext_resource type="Script" uid="uid://blenis2y55fmg" path="res://tools/city_helpers.gd" id="1_qwuk2"] [ext_resource type="Script" uid="uid://blenis2y55fmg" path="res://tools/city_helpers.gd" id="1_qwuk2"]
@@ -27,6 +27,11 @@
[ext_resource type="PackedScene" uid="uid://dxt0e2ugmttqq" path="res://scenes/enemies/grounded_enemy/grounded_enemy.tscn" id="25_nrosh"] [ext_resource type="PackedScene" uid="uid://dxt0e2ugmttqq" path="res://scenes/enemies/grounded_enemy/grounded_enemy.tscn" id="25_nrosh"]
[ext_resource type="PackedScene" uid="uid://cmlud1hwkd6sv" path="res://scenes/enemies/flying_enemy/flying_enemy.tscn" id="25_x7fl1"] [ext_resource type="PackedScene" uid="uid://cmlud1hwkd6sv" path="res://scenes/enemies/flying_enemy/flying_enemy.tscn" id="25_x7fl1"]
[ext_resource type="Script" uid="uid://dtpxijlnb2c5" path="res://components/movement/RMovement.cs" id="26_lu3yt"] [ext_resource type="Script" uid="uid://dtpxijlnb2c5" path="res://components/movement/RMovement.cs" id="26_lu3yt"]
[ext_resource type="Resource" uid="uid://dg1xbjhyhgnnk" path="res://scenes/enemies/flying_enemy/flying_enemy_health.tres" id="27_lgco8"]
[ext_resource type="Resource" uid="uid://dgo65k2ceqfvy" path="res://scenes/enemies/flying_enemy/flying_enemy_damage.tres" id="28_51ivn"]
[ext_resource type="Resource" uid="uid://bqq6uukbdfysr" path="res://scenes/enemies/grounded_enemy/grounded_enemy_movement.tres" id="30_3w3wd"]
[ext_resource type="Resource" uid="uid://bohbojc68j7y1" path="res://scenes/enemies/grounded_enemy/grounded_enemy_health.tres" id="31_5hbxb"]
[ext_resource type="Resource" uid="uid://otfc2snh8umc" path="res://scenes/enemies/grounded_enemy/grounded_enemy_damage.tres" id="32_hmdts"]
[sub_resource type="LabelSettings" id="LabelSettings_2k3fr"] [sub_resource type="LabelSettings" id="LabelSettings_2k3fr"]
font_size = 30 font_size = 30
@@ -450,11 +455,16 @@ spot_angle_attenuation = 10.556052
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 19, 43, -111.5) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 19, 43, -111.5)
EnemyToSpawn = ExtResource("25_x7fl1") EnemyToSpawn = ExtResource("25_x7fl1")
MovementInputs = SubResource("Resource_pxspk") MovementInputs = SubResource("Resource_pxspk")
HealthInputs = ExtResource("27_lgco8")
DamageInputs = ExtResource("28_51ivn")
Target = NodePath("../Player") Target = NodePath("../Player")
[node name="Spawner2" parent="." node_paths=PackedStringArray("Target") instance=ExtResource("24_qwuk2")] [node name="Spawner2" parent="." node_paths=PackedStringArray("Target") instance=ExtResource("24_qwuk2")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 31, 7.5, -88) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 31, 7.5, -88)
EnemyToSpawn = ExtResource("25_nrosh") EnemyToSpawn = ExtResource("25_nrosh")
MovementInputs = ExtResource("30_3w3wd")
HealthInputs = ExtResource("31_5hbxb")
DamageInputs = ExtResource("32_hmdts")
Target = NodePath("../Player") Target = NodePath("../Player")
[connection signal="timeout" from="TutorialController/WaitToShowBlockingTuto" to="TutorialController" method="_show_weapon_tutorial"] [connection signal="timeout" from="TutorialController/WaitToShowBlockingTuto" to="TutorialController" method="_show_weapon_tutorial"]

View File

@@ -1,9 +1,12 @@
[gd_scene load_steps=56 format=3 uid="uid://bei4nhkf8lwdo"] [gd_scene load_steps=64 format=3 uid="uid://bei4nhkf8lwdo"]
[ext_resource type="Script" uid="uid://bbbrf5ckydfna" path="res://player_controller/Scripts/PlayerController.cs" id="1_poq2x"] [ext_resource type="Script" uid="uid://bbbrf5ckydfna" path="res://player_controller/Scripts/PlayerController.cs" id="1_poq2x"]
[ext_resource type="PackedScene" uid="uid://cf3rrgr1imvv4" path="res://scenes/path/path.tscn" id="2_6lejt"] [ext_resource type="PackedScene" uid="uid://cf3rrgr1imvv4" path="res://scenes/path/path.tscn" id="2_6lejt"]
[ext_resource type="Script" uid="uid://jitubgv6judn" path="res://components/damage/RDamage.cs" id="2_x835q"]
[ext_resource type="Script" uid="uid://b44cse62qru7j" path="res://components/knockback/RKnockback.cs" id="3_cb2lu"]
[ext_resource type="Resource" uid="uid://bl5crtu1gkrtr" path="res://systems/inputs/base_mode/base_mode.tres" id="3_cresl"] [ext_resource type="Resource" uid="uid://bl5crtu1gkrtr" path="res://systems/inputs/base_mode/base_mode.tres" id="3_cresl"]
[ext_resource type="PackedScene" uid="uid://c4ikbhojckpnc" path="res://components/health/CHealth.tscn" id="3_q7bng"] [ext_resource type="PackedScene" uid="uid://c4ikbhojckpnc" path="res://components/health/CHealth.tscn" id="3_q7bng"]
[ext_resource type="Script" uid="uid://baiapod3csndf" path="res://components/health/RHealth.cs" id="4_abfq8"]
[ext_resource type="Resource" uid="uid://bjyd801wvverk" path="res://player_controller/resources/player_health.tres" id="4_m8gvy"] [ext_resource type="Resource" uid="uid://bjyd801wvverk" path="res://player_controller/resources/player_health.tres" id="4_m8gvy"]
[ext_resource type="Resource" uid="uid://cpdaw41ah5gic" path="res://systems/inputs/base_mode/rotate_y.tres" id="4_rxwoh"] [ext_resource type="Resource" uid="uid://cpdaw41ah5gic" path="res://systems/inputs/base_mode/rotate_y.tres" id="4_rxwoh"]
[ext_resource type="Resource" uid="uid://ccrb5xsnphc8" path="res://systems/inputs/base_mode/rotate_floorplane.tres" id="5_4u7i3"] [ext_resource type="Resource" uid="uid://ccrb5xsnphc8" path="res://systems/inputs/base_mode/rotate_floorplane.tres" id="5_4u7i3"]
@@ -47,6 +50,22 @@
[ext_resource type="Texture2D" uid="uid://bnwj7ltdfximr" path="res://icon.svg" id="30_h23go"] [ext_resource type="Texture2D" uid="uid://bnwj7ltdfximr" path="res://icon.svg" id="30_h23go"]
[ext_resource type="Texture2D" uid="uid://chvt6g0xn5c2m" path="res://systems/dash/light-ring.jpg" id="32_lgpc8"] [ext_resource type="Texture2D" uid="uid://chvt6g0xn5c2m" path="res://systems/dash/light-ring.jpg" id="32_lgpc8"]
[ext_resource type="Script" uid="uid://b4dwolbvt8our" path="res://addons/godot_state_charts/history_state.gd" id="41_ruloh"] [ext_resource type="Script" uid="uid://b4dwolbvt8our" path="res://addons/godot_state_charts/history_state.gd" id="41_ruloh"]
[ext_resource type="Texture2D" uid="uid://buu21kg4kkhiw" path="res://guide_examples/shared/fireball/fireball.svg" id="42_cmijs"]
[sub_resource type="Resource" id="Resource_cb2lu"]
script = ExtResource("2_x835q")
DamageDealt = 30.0
metadata/_custom_type_script = "uid://jitubgv6judn"
[sub_resource type="Resource" id="Resource_abfq8"]
script = ExtResource("3_cb2lu")
Modifier = 10.0
metadata/_custom_type_script = "uid://b44cse62qru7j"
[sub_resource type="Resource" id="Resource_ue7xq"]
script = ExtResource("4_abfq8")
StartingHealth = 10.0
metadata/_custom_type_script = "uid://baiapod3csndf"
[sub_resource type="CapsuleMesh" id="CapsuleMesh_xc2g5"] [sub_resource type="CapsuleMesh" id="CapsuleMesh_xc2g5"]
height = 1.7 height = 1.7
@@ -67,6 +86,9 @@ top_radius = 0.2
bottom_radius = 0.2 bottom_radius = 0.2
height = 1.0 height = 1.0
[sub_resource type="SphereShape3D" id="SphereShape3D_cmijs"]
radius = 1.0
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_6lejt"] [sub_resource type="CapsuleShape3D" id="CapsuleShape3D_6lejt"]
radius = 1.0 radius = 1.0
height = 3.5 height = 3.5
@@ -80,6 +102,10 @@ blend_mode = 1
[node name="Player" type="CharacterBody3D"] [node name="Player" type="CharacterBody3D"]
collision_mask = 272 collision_mask = 272
script = ExtResource("1_poq2x") script = ExtResource("1_poq2x")
RDamage = SubResource("Resource_cb2lu")
RKnockback = SubResource("Resource_abfq8")
RHealth = SubResource("Resource_ue7xq")
TargetingDistance = 5.0
WalkSpeed = 7.5 WalkSpeed = 7.5
AccelerationFloor = 4.0 AccelerationFloor = 4.0
DecelerationFloor = 3.0 DecelerationFloor = 3.0
@@ -296,6 +322,13 @@ visible = false
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 0, 0, -1) transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 0, 0, -1)
mesh = SubResource("CylinderMesh_nodcl") mesh = SubResource("CylinderMesh_nodcl")
[node name="CloseEnemyDetector" type="ShapeCast3D" parent="."]
unique_name_in_owner = true
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.6, 0)
shape = SubResource("SphereShape3D_cmijs")
target_position = Vector3(0, 0, -5)
collision_mask = 48
[node name="GroundDetector" type="ShapeCast3D" parent="."] [node name="GroundDetector" type="ShapeCast3D" parent="."]
shape = SubResource("CapsuleShape3D_6lejt") shape = SubResource("CapsuleShape3D_6lejt")
collision_mask = 256 collision_mask = 256
@@ -344,7 +377,6 @@ enabled = false
initial_node_to_watch = NodePath("../StateChart") initial_node_to_watch = NodePath("../StateChart")
[node name="UI" type="Control" parent="."] [node name="UI" type="Control" parent="."]
visible = false
layout_mode = 3 layout_mode = 3
anchors_preset = 15 anchors_preset = 15
anchor_right = 1.0 anchor_right = 1.0
@@ -434,6 +466,24 @@ unique_name_in_owner = true
custom_minimum_size = Vector2(100, 10) custom_minimum_size = Vector2(100, 10)
layout_mode = 2 layout_mode = 2
[node name="EnemyTarget" type="TextureRect" parent="UI"]
unique_name_in_owner = true
modulate = Color(0, 0.61278194, 0.56044877, 1)
layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
anchor_right = 0.5
anchor_bottom = 0.5
offset_left = -20.0
offset_top = -20.0
offset_right = 20.0
offset_bottom = 20.0
grow_horizontal = 2
grow_vertical = 2
texture = ExtResource("42_cmijs")
expand_mode = 1
[node name="StateChart" type="Node" parent="."] [node name="StateChart" type="Node" parent="."]
script = ExtResource("25_wv70j") script = ExtResource("25_wv70j")
metadata/_custom_type_script = "uid://couw105c3bde4" metadata/_custom_type_script = "uid://couw105c3bde4"
@@ -509,6 +559,49 @@ to = NodePath("../../AtLeastOneCharge")
event = &"power_used" event = &"power_used"
delay_in_seconds = "0.0" delay_in_seconds = "0.0"
[node name="Attack" type="Node" parent="StateChart/Root"]
script = ExtResource("26_infe6")
initial_state = NodePath("Ready")
[node name="Ready" type="Node" parent="StateChart/Root/Attack"]
script = ExtResource("27_34snm")
[node name="ToStandardAttack" type="Node" parent="StateChart/Root/Attack/Ready"]
script = ExtResource("28_n7qhm")
to = NodePath("../../StandardAttack")
event = &"standard_attack"
delay_in_seconds = "0.0"
[node name="ToDashAttack" type="Node" parent="StateChart/Root/Attack/Ready"]
script = ExtResource("28_n7qhm")
to = NodePath("../../DashAttack")
event = &"dash_attack"
delay_in_seconds = "0.0"
[node name="StandardAttack" type="Node" parent="StateChart/Root/Attack"]
script = ExtResource("27_34snm")
[node name="ToReady" type="Node" parent="StateChart/Root/Attack/StandardAttack"]
script = ExtResource("28_n7qhm")
to = NodePath("../../Ready")
event = &"attack_finished"
delay_in_seconds = "0.0"
[node name="ToDashAttack" type="Node" parent="StateChart/Root/Attack/StandardAttack"]
script = ExtResource("28_n7qhm")
to = NodePath("../../DashAttack")
event = &"dash_attack"
delay_in_seconds = "0.0"
[node name="DashAttack" type="Node" parent="StateChart/Root/Attack"]
script = ExtResource("27_34snm")
[node name="ToReady" type="Node" parent="StateChart/Root/Attack/DashAttack"]
script = ExtResource("28_n7qhm")
to = NodePath("../../Ready")
event = &"attack_finished"
delay_in_seconds = "0.0"
[node name="Movement" type="Node" parent="StateChart/Root"] [node name="Movement" type="Node" parent="StateChart/Root"]
script = ExtResource("26_infe6") script = ExtResource("26_infe6")
initial_state = NodePath("Grounded") initial_state = NodePath("Grounded")

View File

@@ -4,16 +4,53 @@ using System;
public partial class PlayerUi : Control public partial class PlayerUi : Control
{ {
private TextureRect[] _dashIcons = new TextureRect[3]; private TextureRect[] _dashIcons = new TextureRect[3];
private TextureRect _enemyTarget;
public enum TargetState
{
NoTarget,
TargetTooFar,
TargetInRange,
TargetDashThrough
}
public record TargetProperties(TargetState State, Vector2 Position);
[Export]
public Color DashThroughColor { get; set; } = new Color("009c8f");
[Export]
public Color DashBlockedColor { get; set; } = new Color("fc001c");
[Export]
public Color DashOutOfRangeColor { get; set; } = new Color("ffffff");
public override void _Ready() public override void _Ready()
{ {
base._Ready();
_dashIcons[0] = GetNode<TextureRect>("%Dash1"); _dashIcons[0] = GetNode<TextureRect>("%Dash1");
_dashIcons[1] = GetNode<TextureRect>("%Dash2"); _dashIcons[1] = GetNode<TextureRect>("%Dash2");
_dashIcons[2] = GetNode<TextureRect>("%Dash3"); _dashIcons[2] = GetNode<TextureRect>("%Dash3");
_enemyTarget = GetNode<TextureRect>("%EnemyTarget");
} }
public void SetEnemyTargetProperties(TargetProperties targetProperties)
{
var (state, position) = targetProperties;
var visible = state != TargetState.NoTarget;
var modulation = state switch
{
TargetState.TargetTooFar => DashOutOfRangeColor,
TargetState.TargetInRange => DashBlockedColor,
TargetState.TargetDashThrough => DashThroughColor,
_ => DashOutOfRangeColor
};
_enemyTarget.SetVisible(visible);
_enemyTarget.SetPosition(position - _enemyTarget.Size / 2);
_enemyTarget.SetModulate(modulation);
}
public void SetNumberOfDashesLeft(int numberOfDashes) public void SetNumberOfDashesLeft(int numberOfDashes)
{ {
int index = 1; int index = 1;

View File

@@ -7,6 +7,7 @@ using Movementtests.addons.godot_state_charts.csharp;
using Movementtests.interfaces; using Movementtests.interfaces;
using Movementtests.systems; using Movementtests.systems;
using Movementtests.player_controller.Scripts; using Movementtests.player_controller.Scripts;
using Movementtests.systems.damage;
using RustyOptions; using RustyOptions;
public partial class PlayerController : CharacterBody3D, public partial class PlayerController : CharacterBody3D,
@@ -40,7 +41,7 @@ public partial class PlayerController : CharacterBody3D,
/////////////////////////// ///////////////////////////
public event Action<IDamageable, DamageRecord> DamageTaken; public event Action<IDamageable, DamageRecord> DamageTaken;
public event Action<IHealthable, float> HealthChanged; public event Action<IHealthable, HealthChangedRecord> HealthChanged;
public event Action<IHealthable> HealthDepleted; public event Action<IHealthable> HealthDepleted;
/////////////////////////// ///////////////////////////
@@ -71,16 +72,18 @@ public partial class PlayerController : CharacterBody3D,
[Export] public Marker3D TutorialWeaponTarget; [Export] public Marker3D TutorialWeaponTarget;
[Export] public bool TutorialDone { get; set; } [Export] public bool TutorialDone { get; set; }
// Combat stuff
[ExportCategory("Combat")] [ExportCategory("Combat")]
[ExportGroup("Damage")] [ExportGroup("Damage")]
[Export] [Export] public RDamage RDamage { get; set; }
public RDamage RDamage { get; set; } [Export] public RKnockback RKnockback { get; set; }
[Export] public RHealth RHealth { get; set; }
[Export] [Export(PropertyHint.Range, "0,20,0.1,or_greater")]
public RKnockback RKnockback { get; set; } public float TargetingDistance { get; set; } = 10.0f;
[Export] [Export(PropertyHint.Range, "0,20,0.1,or_greater")]
public RHealth RHealth { get; set; } public float TargetInRangeDistance { get; set; } = 5.0f;
// Movement stuff // Movement stuff
[ExportCategory("Movement")] [ExportCategory("Movement")]
@@ -310,6 +313,7 @@ public partial class PlayerController : CharacterBody3D,
private StateChartState _mantling; private StateChartState _mantling;
private StateChartState _simpleDash; private StateChartState _simpleDash;
private StateChartState _aimedDash; private StateChartState _aimedDash;
private StateChartState _weaponDash;
private StateChartState _sliding; private StateChartState _sliding;
private StateChartState _groundSliding; private StateChartState _groundSliding;
private StateChartState _airGliding; private StateChartState _airGliding;
@@ -321,6 +325,11 @@ public partial class PlayerController : CharacterBody3D,
private StateChartState _onWallHanging; private StateChartState _onWallHanging;
private StateChartState _onWallRunning; private StateChartState _onWallRunning;
private StateChartState _attack;
private StateChartState _attackReady;
private StateChartState _attackStandard;
private StateChartState _attackDash;
private Transition _onDashEnded; private Transition _onDashEnded;
private Transition _onJumpFromWall; private Transition _onJumpFromWall;
@@ -338,9 +347,11 @@ public partial class PlayerController : CharacterBody3D,
public float CurrentHealth { get; set; } public float CurrentHealth { get; set; }
private bool _isInvincible; private bool _isInvincible;
private bool _canAttack = true;
private readonly List<IDamageable> _hitEnemies = new List<IDamageable>(); private readonly List<IDamageable> _hitEnemies = new List<IDamageable>();
private ShapeCast3D _closeEnemyDetector;
private Camera3D _camera;
public override void _Ready() public override void _Ready()
{ {
LoadSettings(); LoadSettings();
@@ -351,6 +362,9 @@ public partial class PlayerController : CharacterBody3D,
// General use stuff // General use stuff
PlayerUi = GetNode<PlayerUi>("UI"); PlayerUi = GetNode<PlayerUi>("UI");
_closeEnemyDetector = GetNode<ShapeCast3D>("%CloseEnemyDetector");
_closeEnemyDetector.TargetPosition = _closeEnemyDetector.TargetPosition.Normalized() * TargetingDistance;
// DashIndicator = GetNode<TextureRect>("%DashIndicator"); // DashIndicator = GetNode<TextureRect>("%DashIndicator");
PowerCooldownIndicator = GetNode<ColorRect>("%DashCooldownIndicator"); PowerCooldownIndicator = GetNode<ColorRect>("%DashCooldownIndicator");
PowerCooldownIndicator.Visible = false; PowerCooldownIndicator.Visible = false;
@@ -366,7 +380,7 @@ public partial class PlayerController : CharacterBody3D,
// Camera stuff // Camera stuff
HeadSystem = GetNode<HeadSystem>("HeadSystem"); HeadSystem = GetNode<HeadSystem>("HeadSystem");
Camera3D camera = GetNode<Camera3D>("HeadSystem/CameraSmooth/Camera3D"); _camera = GetNode<Camera3D>("HeadSystem/CameraSmooth/Camera3D");
Node3D cameraSmooth = GetNode<Node3D>("HeadSystem/CameraSmooth"); Node3D cameraSmooth = GetNode<Node3D>("HeadSystem/CameraSmooth");
// Movement stuff // Movement stuff
@@ -413,7 +427,7 @@ public partial class PlayerController : CharacterBody3D,
} }
if (RKnockback != null) CKnockback!.RKnockback = RKnockback; if (RKnockback != null) CKnockback!.RKnockback = RKnockback;
CDamageable.DamageTaken += ReduceHealth; CDamageable.DamageTaken += (source, record) => ReduceHealth(source, record);
CDamageable.DamageTaken += RegisterKnockback; CDamageable.DamageTaken += RegisterKnockback;
CHealth.HealthDepleted += Kill; CHealth.HealthDepleted += Kill;
@@ -423,6 +437,7 @@ public partial class PlayerController : CharacterBody3D,
_aiming = StateChartState.Of(GetNode("StateChart/Root/Aim/On")); _aiming = StateChartState.Of(GetNode("StateChart/Root/Aim/On"));
_simpleDash = StateChartState.Of(GetNode("StateChart/Root/Movement/Dashing/Dash")); _simpleDash = StateChartState.Of(GetNode("StateChart/Root/Movement/Dashing/Dash"));
_aimedDash = StateChartState.Of(GetNode("StateChart/Root/Movement/Dashing/AimedDash")); _aimedDash = StateChartState.Of(GetNode("StateChart/Root/Movement/Dashing/AimedDash"));
_weaponDash = StateChartState.Of(GetNode("StateChart/Root/Movement/Dashing/ToWeaponDash"));
_slamming = StateChartState.Of(GetNode("StateChart/Root/Movement/Slamming")); _slamming = StateChartState.Of(GetNode("StateChart/Root/Movement/Slamming"));
_sliding = StateChartState.Of(GetNode("StateChart/Root/Movement/Sliding")); _sliding = StateChartState.Of(GetNode("StateChart/Root/Movement/Sliding"));
@@ -456,6 +471,12 @@ public partial class PlayerController : CharacterBody3D,
_onLeaveWallFromRun = Transition.Of(GetNode("StateChart/Root/Movement/OnWall/Running/OnLeaveWall")); _onLeaveWallFromRun = Transition.Of(GetNode("StateChart/Root/Movement/OnWall/Running/OnLeaveWall"));
_onAirborneToGrounded = Transition.Of(GetNode("StateChart/Root/Movement/Airborne/OnGrounded")); _onAirborneToGrounded = Transition.Of(GetNode("StateChart/Root/Movement/Airborne/OnGrounded"));
// Attack states
_attack = StateChartState.Of(GetNode("StateChart/Root/Attack"));
_attackReady = StateChartState.Of(GetNode("StateChart/Root/Attack/Ready"));
_attackStandard = StateChartState.Of(GetNode("StateChart/Root/Attack/StandardAttack"));
_attackDash = StateChartState.Of(GetNode("StateChart/Root/Attack/DashAttack"));
// State timers // State timers
_powerCooldownTimer = GetNode<Timer>("PowerCooldown"); _powerCooldownTimer = GetNode<Timer>("PowerCooldown");
_timeScaleAimInAirTimer = GetNode<Timer>("TimeScaleAimInAir"); _timeScaleAimInAirTimer = GetNode<Timer>("TimeScaleAimInAir");
@@ -479,8 +500,8 @@ public partial class PlayerController : CharacterBody3D,
_gravity = (float)ProjectSettings.GetSetting("physics/3d/default_gravity"); _gravity = (float)ProjectSettings.GetSetting("physics/3d/default_gravity");
MantleSystem.Init(); MantleSystem.Init();
StairsSystem.Init(stairsBelowRayCast3D, stairsAheadRayCast3D, cameraSmooth); StairsSystem.Init(stairsBelowRayCast3D, stairsAheadRayCast3D, cameraSmooth);
DashSystem.Init(HeadSystem, camera); DashSystem.Init(HeadSystem, _camera);
WeaponSystem.Init(HeadSystem, camera); WeaponSystem.Init();
WallHugSystem.Init(); WallHugSystem.Init();
EmpoweredActionsLeft = MaxNumberOfEmpoweredActions; EmpoweredActionsLeft = MaxNumberOfEmpoweredActions;
@@ -529,6 +550,8 @@ public partial class PlayerController : CharacterBody3D,
_aimedDash.StateEntered += OnAimedDashStarted; _aimedDash.StateEntered += OnAimedDashStarted;
_aimedDash.StateExited += OnAimedDashFinished; _aimedDash.StateExited += OnAimedDashFinished;
_weaponDash.StateExited += OnWeaponDashFinished;
_sliding.StateEntered += SlideStarted; _sliding.StateEntered += SlideStarted;
_sliding.StateExited += SlideEnded; _sliding.StateExited += SlideEnded;
_slideCanceled.StateEntered += OnSlideCanceled; _slideCanceled.StateEntered += OnSlideCanceled;
@@ -557,12 +580,16 @@ public partial class PlayerController : CharacterBody3D,
_onWallRunning.StatePhysicsProcessing += HandleWallRunning; _onWallRunning.StatePhysicsProcessing += HandleWallRunning;
_onWallHanging.StateExited += RecoverWeapon; _onWallHanging.StateExited += RecoverWeapon;
_onDashEnded.Taken += RecoverWeapon; // _onDashEnded.Taken += RecoverWeapon;
_onJumpFromWall.Taken += OnJumpFromWall; _onJumpFromWall.Taken += OnJumpFromWall;
_onJumpFromWallFalling.Taken += OnJumpFromWall; _onJumpFromWallFalling.Taken += OnJumpFromWall;
_onLeaveWallFromRun.Taken += OnLeaveWallFromRun; _onLeaveWallFromRun.Taken += OnLeaveWallFromRun;
_onAirborneToGrounded.Taken += OnAirborneToGrounded; _onAirborneToGrounded.Taken += OnAirborneToGrounded;
// Attack states
_attackStandard.StateEntered += OnStandardAttackStarted;
_attackDash.StateEntered += OnDashAttackStarted;
} }
/////////////////////////// ///////////////////////////
@@ -952,7 +979,7 @@ public partial class PlayerController : CharacterBody3D,
} }
public void RecoverChildNode(Node3D node) public void RecoverChildNode(Node3D node)
{ {
GetTree().GetRoot().RemoveChild(node); node.GetParent().RemoveChild(node);
AddChild(node); AddChild(node);
node.SetGlobalPosition(GlobalPosition); node.SetGlobalPosition(GlobalPosition);
} }
@@ -1630,15 +1657,14 @@ public partial class PlayerController : CharacterBody3D,
if (!CanPerformEmpoweredAction()) if (!CanPerformEmpoweredAction())
return; return;
DashSystem.StartPreparingDash(); // DashIndicatorMesh.Visible = true;
DashIndicatorMesh.Visible = true;
if (!isOnFloorCustom()) if (!isOnFloorCustom())
ReduceTimeScaleWhileAiming(); ReduceTimeScaleWhileAiming();
} }
public void HandleAiming(float delta) public void HandleAiming(float delta)
{ {
DashIndicatorMeshCylinder.Height = DashSystem.PlannedLocation.DistanceTo(GlobalPosition); // DashIndicatorMeshCylinder.Height = DashSystem.PlannedLocation.DistanceTo(GlobalPosition);
DashIndicatorNode.LookAt(DashSystem.PlannedLocation); // DashIndicatorNode.LookAt(DashSystem.PlannedLocation);
if (CanPerformEmpoweredAction()) if (CanPerformEmpoweredAction())
DashSystem.PrepareDash(); DashSystem.PrepareDash();
@@ -1647,7 +1673,7 @@ public partial class PlayerController : CharacterBody3D,
{ {
DashSystem.StopPreparingDash(); DashSystem.StopPreparingDash();
DashIndicatorMesh.Visible = false; // DashIndicatorMesh.Visible = false;
} }
/////////////////////////// ///////////////////////////
@@ -1679,6 +1705,11 @@ public partial class PlayerController : CharacterBody3D,
var correction = DashSystem.CollisionNormal == Vector3.Down ? _playerHeight : DashSystem.DashCastRadius; var correction = DashSystem.CollisionNormal == Vector3.Down ? _playerHeight : DashSystem.DashCastRadius;
var correctedLocation = DashSystem.PlannedLocation + Vector3.Down * correction; var correctedLocation = DashSystem.PlannedLocation + Vector3.Down * correction;
if (DashSystem.CanDashThroughTarget && DashSystem.CollidedObject is ITargetable targetable)
correctedLocation = ComputePositionAfterTargetedDash(targetable.GetTargetGlobalPosition(), DashSystem.CollisionPoint);
// Start invincibility timer for the duration of the dash and a bit more afterwards
OnHitInvincibility();
_preDashVelocity = Velocity; _preDashVelocity = Velocity;
_dashDirection = (correctedLocation - GlobalPosition).Normalized(); _dashDirection = (correctedLocation - GlobalPosition).Normalized();
@@ -1697,9 +1728,16 @@ public partial class PlayerController : CharacterBody3D,
} }
public void OnAimedDashFinished() public void OnAimedDashFinished()
{ {
var postDashVelocity = _preDashVelocity.Length() > PostDashSpeed ? PostDashSpeed : _preDashVelocity.Length(); ManageAttackedEnemyPostDash(DashSystem.CollidedObject as Node);
DashSystem.CollidedObject = null;
if (_customMantle)
{
_playerState.SendEvent("mantle");
return;
}
var postDashVelocity = _preDashVelocity.Length() > PostDashSpeed ? _preDashVelocity.Length() : PostDashSpeed;
Velocity = _dashDirection * postDashVelocity; Velocity = _dashDirection * postDashVelocity;
if (_customMantle) _playerState.SendEvent("mantle");
} }
// Weapon dashing // Weapon dashing
@@ -1716,23 +1754,27 @@ public partial class PlayerController : CharacterBody3D,
weaponTargetLocation, weaponTargetLocation,
DashSystem.HasHit, DashSystem.HasHit,
DashSystem.CollisionPoint, DashSystem.CollisionPoint,
DashSystem.CollisionNormal); DashSystem.CollisionNormal,
DashSystem.CollidedObject as Node);
} }
public void RecoverWeapon() public void RecoverWeapon()
{ {
if (WeaponSystem.GetParent() == this) return; if (WeaponSystem.GetParent() == this) return;
HeadSystem.ShowWeapon(); HeadSystem.ShowWeapon();
RecoverChildNode(WeaponSystem);
WeaponSystem.ResetWeapon(); WeaponSystem.ResetWeapon();
RecoverChildNode(WeaponSystem);
} }
public void DashToFlyingWeapon() public void DashToFlyingWeapon()
{ {
_playerState.SendEvent("cancel_aim"); _playerState.SendEvent("cancel_aim");
_playerState.SendEvent("weapon_dash"); _playerState.SendEvent("weapon_dash");
PerformEmpoweredAction(); PerformEmpoweredAction();
_audioStream.SwitchToClipByName("dash"); _audioStream.SwitchToClipByName("dash");
// Start invincibility timer for the duration of the dash and a bit more afterwards
OnHitInvincibility();
DashSystem.ShouldMantle = false; DashSystem.ShouldMantle = false;
_dashDirection = (WeaponSystem.GlobalPosition - GlobalPosition).Normalized(); _dashDirection = (WeaponSystem.GlobalPosition - GlobalPosition).Normalized();
@@ -1753,8 +1795,11 @@ public partial class PlayerController : CharacterBody3D,
{ {
_playerState.SendEvent("cancel_aim"); _playerState.SendEvent("cancel_aim");
_playerState.SendEvent("weapon_dash"); _playerState.SendEvent("weapon_dash");
PerformEmpoweredAction(); PerformEmpoweredAction();
_audioStream.SwitchToClipByName("dash"); _audioStream.SwitchToClipByName("dash");
// Start invincibility timer for the duration of the dash and a bit more afterwards
OnHitInvincibility();
DashSystem.ShouldMantle = false; DashSystem.ShouldMantle = false;
var dashLocation = WeaponSystem.PlantLocation; var dashLocation = WeaponSystem.PlantLocation;
@@ -1762,6 +1807,8 @@ public partial class PlayerController : CharacterBody3D,
dashLocation += WeaponSystem.PlantNormal * _playerRadius; dashLocation += WeaponSystem.PlantNormal * _playerRadius;
if (WeaponSystem.IsPlantedUnderPlatform()) if (WeaponSystem.IsPlantedUnderPlatform())
dashLocation += Vector3.Down * _playerHeight; dashLocation += Vector3.Down * _playerHeight;
if (WeaponSystem.PlantObject is ITargetable targetable)
dashLocation = targetable.GetTargetGlobalPosition();
_wallHugStartNormal = WeaponSystem.PlantNormal; _wallHugStartNormal = WeaponSystem.PlantNormal;
_currentWallContactPoint = WeaponSystem.PlantLocation; _currentWallContactPoint = WeaponSystem.PlantLocation;
@@ -1776,40 +1823,131 @@ public partial class PlayerController : CharacterBody3D,
// Store the weapon state before resetting it // Store the weapon state before resetting it
var isPlantedOnWall = WeaponSystem.IsPlantedInWall(); var isPlantedOnWall = WeaponSystem.IsPlantedInWall();
var isPlantedUnderPlatform = WeaponSystem.IsPlantedUnderPlatform(); var isPlantedUnderPlatform = WeaponSystem.IsPlantedUnderPlatform();
var shouldDashToHanging = isPlantedOnWall || isPlantedUnderPlatform; var isPlantedInTarget = WeaponSystem.PlantObject is ITargetable;
var shouldDashToHanging = (isPlantedOnWall || isPlantedUnderPlatform) && !isPlantedInTarget;
var resultingEvent = shouldDashToHanging ? "dash_to_planted" : "dash_finished"; var resultingEvent = shouldDashToHanging ? "dash_to_planted" : "dash_finished";
if (!shouldDashToHanging) RecoverWeapon(); // Manually recover weapon before enemy is freed in case it owns the weapon object
if (WeaponSystem.PlantObject is ITargetable targetable)
{
HeadSystem.OnMantle(); // Recycle mantle animation
SetVerticalVelocity(PostDashSpeed);
}
ManageAttackedEnemyPostDash(WeaponSystem.PlantObject);
WeaponSystem.PlantObject = null;
_playerState.SendEvent(resultingEvent); _playerState.SendEvent(resultingEvent);
} }
public void ManageAttackedEnemyPostDash(Node enemy)
{
if (enemy is IDamageable damageable)
{
_hitEnemies.Add(damageable);
TriggerDamage();
}
if (enemy is IStunnable stunnable)
stunnable.Stun();
}
public void OnWeaponDashFinished()
{
}
/////////////////////////// ///////////////////////////
// Processes ////////////// // Processes //////////////
/////////////////////////// ///////////////////////////
public override void _PhysicsProcess(double delta) public override void _PhysicsProcess(double delta)
{ {
_spaceState = GetWorld3D().DirectSpaceState;
if (_currentInputBufferFrames > 0) _currentInputBufferFrames -= 1; if (_currentInputBufferFrames > 0) _currentInputBufferFrames -= 1;
// Manage head and camera movement
LookAround(delta); LookAround(delta);
// Manage general movement
Velocity += ComputeKnockback(); Velocity += ComputeKnockback();
MoveSlideAndHandleStairs((float) delta); MoveSlideAndHandleStairs((float) delta);
// Manage gameplay systems
MantleSystem.ProcessMantle(_grounded.Active); MantleSystem.ProcessMantle(_grounded.Active);
HandleEnemyTargeting();
if (WeaponSystem.InHandState.Active && !_aiming.Active && TutorialDone) // Manage dash target and tutorial specific stuff
{ // if (WeaponSystem.InHandState.Active && !_aiming.Active && TutorialDone)
DashIndicatorMesh.Visible = false; // {
} // DashIndicatorMesh.Visible = false;
if (!WeaponSystem.InHandState.Active && TutorialDone) // }
{ // if (!WeaponSystem.InHandState.Active && TutorialDone)
DashIndicatorMesh.Visible = true; // {
// DashIndicatorMesh.Visible = true;
DashIndicatorMeshCylinder.Height = WeaponSystem.GlobalPosition.DistanceTo(GlobalPosition) * 2; //
DashIndicatorNode.LookAt(WeaponSystem.GlobalPosition); // DashIndicatorMeshCylinder.Height = WeaponSystem.GlobalPosition.DistanceTo(GlobalPosition) * 2;
} // DashIndicatorNode.LookAt(WeaponSystem.GlobalPosition);
// }
} }
/////////////////////////// ///////////////////////////
// Hit Management /////// // Hit Management ///////
/////////////////////////// ///////////////////////////
private bool _isEnemyInDashAttackRange;
private Vector3 _targetHitLocation;
private Vector3 _targetLocation;
private Object _targetObject;
public void HandleEnemyTargeting()
{
_isEnemyInDashAttackRange = false;
_closeEnemyDetector.SetRotation(HeadSystem.GetGlobalLookRotation());
var enemyTargetState = PlayerUi.TargetState.NoTarget;
var positionOnScreen = Vector2.Zero;
if (DashSystem.CanDashThroughTarget && DashSystem.CollidedObject is ITargetable dashTarget)
{
enemyTargetState = PlayerUi.TargetState.TargetDashThrough;
_targetLocation = dashTarget.GetTargetGlobalPosition();
positionOnScreen = _camera.UnprojectPosition(_targetLocation);
PlayerUi.SetEnemyTargetProperties(new PlayerUi.TargetProperties(enemyTargetState, positionOnScreen));
return;
}
if (!_closeEnemyDetector.IsColliding())
{
PlayerUi.SetEnemyTargetProperties(new PlayerUi.TargetProperties(enemyTargetState, positionOnScreen));
return;
}
_targetHitLocation = _closeEnemyDetector.GetCollisionPoint(0);
_targetObject = _closeEnemyDetector.GetCollider(0);
if (_targetObject is not ITargetable target)
{
PlayerUi.SetEnemyTargetProperties(new PlayerUi.TargetProperties(enemyTargetState, positionOnScreen));
return;
}
_targetLocation = target.GetTargetGlobalPosition();
var targetDistance = _targetLocation.DistanceTo(GlobalPosition);
positionOnScreen = _camera.UnprojectPosition(_targetLocation);
_isEnemyInDashAttackRange = true; //targetDistance < TargetInRangeDistance; // Removing the "almost dash" UI
if (_isEnemyInDashAttackRange)
{
enemyTargetState = PlayerUi.TargetState.TargetDashThrough;
if (_targetObject is IDamageable damageable && _targetObject is IHealthable healthable)
{
var wouldBeDamage = damageable.ComputeDamage(new DamageRecord(this, RDamage));
if (wouldBeDamage.Damage.DamageDealt < healthable.CurrentHealth)
enemyTargetState = PlayerUi.TargetState.TargetInRange;
}
}
else enemyTargetState = PlayerUi.TargetState.TargetTooFar;
PlayerUi.SetEnemyTargetProperties(new PlayerUi.TargetProperties(enemyTargetState, positionOnScreen));
}
public DamageRecord TakeDamage(DamageRecord damageRecord) public DamageRecord TakeDamage(DamageRecord damageRecord)
{ {
if (_isInvincible) if (_isInvincible)
@@ -1826,37 +1964,102 @@ public partial class PlayerController : CharacterBody3D,
return finalDamage; return finalDamage;
} }
public DamageRecord ComputeDamage(DamageRecord damageRecord)
{
return CDamageable.ComputeDamage(damageRecord);
}
public void OnHitInvincibility() public void OnHitInvincibility()
{ {
_isInvincible = true; _isInvincible = true;
_invincibilityTimer.Start(); _invincibilityTimer.Start();
} }
public void OnStandardAttackStarted()
{
_attackCooldown.Start();
HeadSystem.OnHit();
_audioStream!.SwitchToClipByName("attacks");
}
private PhysicsDirectSpaceState3D _spaceState;
public void OnDashAttackStarted()
{
_audioStream!.SwitchToClipByName("attacks");
_isInvincible = true;
var plannedDashLocation = _targetLocation + Vector3.Down*HeadSystem.Position.Y;
var query = PhysicsRayQueryParameters3D.Create(HeadSystem.GlobalPosition, plannedDashLocation, DashSystem.DashCast3D.CollisionMask);
var result = _spaceState.IntersectRay(query);
if (result.Count > 0)
{
plannedDashLocation = (Vector3) result["position"];
}
var travel = plannedDashLocation - GlobalPosition;
_preDashVelocity = Velocity;
_dashDirection = travel.Normalized();
var dashTween = CreatePositionTween(plannedDashLocation, AimedDashTime);
dashTween.Finished += OnDashAttackEnded;
}
public void OnDashAttackEnded()
{
if (_targetObject is IDamageable damageable)
{
_hitEnemies.Add(damageable);
TriggerDamage();
}
if (_targetObject is IStunnable stunnable)
{
stunnable.Stun();
}
var shouldKnockback = false;
if (_targetObject is IHealthable healthable)
{
if (healthable.CurrentHealth > 0) shouldKnockback = true;
}
if (shouldKnockback)
{
Velocity = -_dashDirection*RKnockback.Modifier;
}
else
{
GlobalPosition = ComputePositionAfterTargetedDash(_targetLocation, _targetHitLocation);
var postDashVelocity = _preDashVelocity.Length() > PostDashSpeed ? _preDashVelocity.Length() : PostDashSpeed;
Velocity = _dashDirection * postDashVelocity;
}
_isInvincible = false;
_playerState.SendEvent("attack_finished");
}
public Vector3 ComputePositionAfterTargetedDash(Vector3 targetLocation, Vector3 targetHitLocation)
{
return targetLocation + (targetLocation - targetHitLocation);
}
public void OnInputHitPressed() public void OnInputHitPressed()
{ {
if (_aiming.Active && WeaponSystem.InHandState.Active) if (_aiming.Active && WeaponSystem.InHandState.Active)
{ {
ThrowWeapon(); ThrowWeapon();
return;
} }
if (!WeaponSystem.InHandState.Active) return; var attackToDo = _isEnemyInDashAttackRange ? "dash_attack" : "standard_attack";
if (!_canAttack) return; _playerState.SendEvent(attackToDo);
_canAttack = false;
_attackCooldown.Start();
PerformHit();
} }
public void ResetAttackCooldown() public void ResetAttackCooldown()
{ {
_canAttack = true; _playerState.SendEvent("attack_finished");
} }
public void PerformHit()
{
HeadSystem.OnHit();
_audioStream!.SwitchToClipByName("attacks");
}
public void OnHitboxActivated() public void OnHitboxActivated()
{ {
@@ -1898,10 +2101,12 @@ public partial class PlayerController : CharacterBody3D,
{ {
ResetTimeScale(); ResetTimeScale();
} }
public void ReduceHealth(IDamageable source, DamageRecord damageRecord) public HealthChangedRecord ReduceHealth(IDamageable source, DamageRecord damageRecord)
{ {
CHealth.ReduceHealth(source, damageRecord); GD.Print("That's NOT fine");
HealthChanged?.Invoke(this, CHealth.CurrentHealth); var record = CHealth.ReduceHealth(source, damageRecord);
HealthChanged?.Invoke(this, record);
return record;
} }
public void RegisterKnockback(IDamageable source, DamageRecord damageRecord) public void RegisterKnockback(IDamageable source, DamageRecord damageRecord)
{ {
@@ -1910,7 +2115,8 @@ public partial class PlayerController : CharacterBody3D,
public Vector3 ComputeKnockback() public Vector3 ComputeKnockback()
{ {
return CKnockback.ComputeKnockback(); var kb = CKnockback.ComputeKnockback();
return kb;
} }
public void Kill(IHealthable source) public void Kill(IHealthable source)

View File

@@ -15,7 +15,7 @@ warnings/check_invalid_track_paths=false
[application] [application]
config/name="Movement tests" config/name="Movement tests"
run/main_scene="uid://vm22i5sv3p3s" run/main_scene="uid://q7uc1h2jpbd2"
config/features=PackedStringArray("4.5", "C#", "Forward Plus") config/features=PackedStringArray("4.5", "C#", "Forward Plus")
config/icon="res://icon.svg" config/icon="res://icon.svg"
@@ -37,6 +37,10 @@ window/size/viewport_height=1080
project/assembly_name="Movement tests" project/assembly_name="Movement tests"
[editor]
movie_writer/movie_file="D:/Godot/Projects/movement-tests/communication/movie.avi"
[editor_plugins] [editor_plugins]
enabled=PackedStringArray("res://addons/godot_state_charts/plugin.cfg", "res://addons/guide/plugin.cfg", "res://addons/maaacks_game_template/plugin.cfg", "res://addons/shaker/plugin.cfg") enabled=PackedStringArray("res://addons/godot_state_charts/plugin.cfg", "res://addons/guide/plugin.cfg", "res://addons/maaacks_game_template/plugin.cfg", "res://addons/shaker/plugin.cfg")
@@ -155,6 +159,7 @@ locale/translations=PackedStringArray("res://addons/maaacks_game_template/base/t
3d_physics/layer_3="3" 3d_physics/layer_3="3"
3d_physics/layer_4="4" 3d_physics/layer_4="4"
3d_physics/layer_5="enemies" 3d_physics/layer_5="enemies"
3d_physics/layer_6="InteractiveGeo"
3d_physics/layer_9="terrain" 3d_physics/layer_9="terrain"
3d_physics/layer_17="weapon" 3d_physics/layer_17="weapon"

View File

@@ -0,0 +1,31 @@
using Godot;
using System;
using Movementtests.interfaces;
[GlobalClass]
public partial class FixedDashthroughTarget : AnimatableBody3D, ITargetable, IStunnable
{
public Vector3 GetTargetGlobalPosition()
{
return GlobalPosition;
}
private uint _defaultCollisionMask;
public override void _Ready()
{
_defaultCollisionMask = CollisionMask;
}
public bool IsStunned { get; set; }
public float StunDuration { get; set; } = 0.1f;
public void Stun()
{
_defaultCollisionMask = 0;
GetTree().CreateTimer(StunDuration).Timeout += Unstun;
}
public void Unstun()
{
_defaultCollisionMask = CollisionMask;
}
}

View File

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

View File

@@ -0,0 +1,30 @@
[gd_scene load_steps=5 format=3 uid="uid://qup00a7x2sji"]
[ext_resource type="Script" uid="uid://c10qfkvmrm6uq" path="res://scenes/FixedDashTarget/FixedDashthroughTarget.cs" id="1_r0j7a"]
[sub_resource type="SphereShape3D" id="SphereShape3D_nkm8n"]
radius = 1.0
[sub_resource type="SphereMesh" id="SphereMesh_r0j7a"]
radius = 1.0
height = 2.0
[sub_resource type="StandardMaterial3D" id="StandardMaterial3D_kgb3i"]
albedo_color = Color(5.1018596e-06, 0.6818665, 0.7627612, 1)
metallic = 0.8
metallic_specular = 0.6
roughness = 0.1
emission_enabled = true
emission = Color(0, 0.68968636, 0.7473501, 1)
[node name="FixedDashthroughTarget" type="AnimatableBody3D"]
collision_layer = 288
collision_mask = 0
script = ExtResource("1_r0j7a")
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
shape = SubResource("SphereShape3D_nkm8n")
[node name="MeshInstance3D" type="MeshInstance3D" parent="."]
mesh = SubResource("SphereMesh_r0j7a")
surface_material_override/0 = SubResource("StandardMaterial3D_kgb3i")

View File

@@ -1,6 +1,7 @@
using System; using System;
using Godot; using Godot;
using Movementtests.interfaces; using Movementtests.interfaces;
using Movementtests.systems;
[GlobalClass] [GlobalClass]
public partial class Enemy : CharacterBody3D, public partial class Enemy : CharacterBody3D,
@@ -10,47 +11,53 @@ public partial class Enemy : CharacterBody3D,
IKillable, IKillable,
IMoveable, IMoveable,
ISpawnable, ISpawnable,
IKnockbackable IKnockbackable,
ITargetable,
IStunnable
{ {
// Signals and events // Signals and events
public event Action<IDamageable, DamageRecord> DamageTaken; public event Action<IDamageable, DamageRecord> DamageTaken;
public event Action<IHealthable, float> HealthChanged; public event Action<IHealthable, HealthChangedRecord> HealthChanged;
public event Action<IHealthable> HealthDepleted; public event Action<IHealthable> HealthDepleted;
// Public export components // Public export components
[Export] [Export]
public Node3D Target { get; set; } public Node3D Target { get; set; }
[Export]
public float EnemyHeight { get; set; } = 1f;
[ExportGroup("Health")] [ExportGroup("Health")]
[Export] [Export]
public Node CHealth { get; set; }
[Export]
public RHealth RHealth { get; set; } public RHealth RHealth { get; set; }
[Export] [Export]
public RDeathEffect[] DeathEffects { get; set; } public RDeathEffect[] DeathEffects { get; set; }
public IHealthable CHealth { get; set; }
[ExportGroup("Damage")] [ExportGroup("Damage")]
[Export] [Export]
public Node CDamage { get; set; }
[Export]
public RDamage RDamage { get; set; } public RDamage RDamage { get; set; }
[Export] public IDamageable CDamageable { get; set; }
public Node CKnockback { get; set; }
[Export] [Export]
public RKnockback RKnockback { get; set; } public RKnockback RKnockback { get; set; }
public IKnockbackable CKnockback { get; set; }
[ExportGroup("Movement")] [ExportGroup("Movement")]
[Export] [Export]
public Node CMovement { get; set; }
[Export]
public RMovement RMovement { get; set; } public RMovement RMovement { get; set; }
public IMoveable CMovement { get; set; }
// Public stuff // Public stuff
public float CurrentHealth { get; set; } public float CurrentHealth
{
get => CHealth.CurrentHealth;
set => CHealth.CurrentHealth = value;
}
// Private stuff // Private stuff
private Area3D _damageBox; private Area3D _damageBox;
private Node3D _target;
private CHealthbar _healthbar;
public override void _Ready() public override void _Ready()
{ {
@@ -61,30 +68,41 @@ public partial class Enemy : CharacterBody3D,
public void Initialize() public void Initialize()
{ {
_damageBox = GetNode<Area3D>("DamageBox"); _damageBox = GetNode<Area3D>("DamageBox");
_target = GetNode<Node3D>("CTarget");
if (CMovement is IMoveable moveable && RMovement != null) moveable.RMovement = RMovement; CDamageable = GetNode<Node>("CDamageable") as IDamageable;
if (CHealth is IHealthable healthable && RHealth != null) CMovement = GetNode<Node>("CMovement") as IMoveable;
CHealth = GetNode<Node>("CHealth") as IHealthable;
CKnockback = GetNode<Node>("CKnockback") as IKnockbackable;
if (CDamageable is null) GD.PrintErr("This node needs a 'CDamage' child of type IDamageable!");
if (CMovement is null) GD.PrintErr("This node needs a 'CMovement' child of type IMoveable!");
if (CHealth is null) GD.PrintErr("This node needs a 'CHealth' child of type IHealthable!");
if (CKnockback is null) GD.PrintErr("This node needs a 'CKnockback' child of type IKnockbackable!");
_healthbar = GetNode<CHealthbar>("CHealthBar");
if (RMovement != null) CMovement!.RMovement = RMovement;
if (RHealth != null)
{ {
healthable.RHealth = RHealth; CHealth!.RHealth = RHealth;
healthable.CurrentHealth = RHealth.StartingHealth; CHealth.CurrentHealth = RHealth.StartingHealth;
} }
if (CKnockback is IKnockbackable knockbackable && RKnockback != null) knockbackable.RKnockback = RKnockback; if (RKnockback != null) CKnockback!.RKnockback = RKnockback;
} }
public void SetupSignals() public void SetupSignals()
{ {
if (CDamage is IDamageable damageable) // Anonymous function call to erase return values of ReduceHealth
{ CDamageable.DamageTaken += (source, record) => ReduceHealth(source, record);
damageable.DamageTaken += ReduceHealth; CDamageable.DamageTaken += RegisterKnockback;
damageable.DamageTaken += RegisterKnockback; CHealth.HealthDepleted += Kill;
} HealthChanged += (source, record) => _healthbar.OnHealthChanged(record);
if (CHealth is IHealthable healthable) healthable.HealthDepleted += Kill;
} }
public override void _PhysicsProcess(double delta) public override void _PhysicsProcess(double delta)
{ {
// Only trigger gameplay related effects 4 times per second // Only trigger gameplay related effects on specific frames
if(Engine.GetPhysicsFrames() % 15 == 0) ProcessGameplay(delta); if(Engine.GetPhysicsFrames() % 10 == 0) ProcessGameplay(delta);
var targetPlanar = new Vector3(Target.GlobalPosition.X, GlobalPosition.Y, Target.GlobalPosition.Z); var targetPlanar = new Vector3(Target.GlobalPosition.X, GlobalPosition.Y, Target.GlobalPosition.Z);
LookAt(targetPlanar); LookAt(targetPlanar);
@@ -103,6 +121,8 @@ public partial class Enemy : CharacterBody3D,
public void ProcessGameplay(double delta) public void ProcessGameplay(double delta)
{ {
if (IsStunned) return;
var bodies = _damageBox.GetOverlappingBodies(); var bodies = _damageBox.GetOverlappingBodies();
foreach (var body in bodies) foreach (var body in bodies)
{ {
@@ -113,45 +133,87 @@ public partial class Enemy : CharacterBody3D,
public Vector3 ComputeVelocity(MovementInputs inputs) public Vector3 ComputeVelocity(MovementInputs inputs)
{ {
if (CMovement is not IMoveable movement) return Vector3.Zero; if (CMovement is null) return Vector3.Zero;
return movement!.ComputeVelocity(inputs); return CMovement.ComputeVelocity(inputs);
} }
public DamageRecord TakeDamage(DamageRecord damageRecord) public DamageRecord TakeDamage(DamageRecord damageRecord)
{ {
if (CDamage is not IDamageable damageable) if (CDamageable is null)
return damageRecord with { Damage = new RDamage(0, damageRecord.Damage.DamageType) }; return damageRecord with { Damage = new RDamage(0, damageRecord.Damage.DamageType) };
var finalDamage = damageable.TakeDamage(damageRecord); var finalDamage = CDamageable.TakeDamage(damageRecord);
DamageTaken?.Invoke(this, finalDamage); DamageTaken?.Invoke(this, finalDamage);
return finalDamage; return finalDamage;
} }
public void ReduceHealth(IDamageable source, DamageRecord damageRecord) public DamageRecord ComputeDamage(DamageRecord damageRecord)
{ {
if (CHealth is not IHealthable healthable) return; if (CDamageable is null)
healthable.ReduceHealth(source, damageRecord); return damageRecord with { Damage = new RDamage(0, damageRecord.Damage.DamageType) };
HealthChanged?.Invoke(this, healthable.CurrentHealth);
return CDamageable.ComputeDamage(damageRecord);
}
public HealthChangedRecord ReduceHealth(IDamageable source, DamageRecord damageRecord)
{
if (CHealth is null) return new HealthChangedRecord(0, 0, 0);
var record = CHealth.ReduceHealth(source, damageRecord);
HealthChanged?.Invoke(this, record);
return record;
} }
public void Kill(IHealthable source) public void Kill(IHealthable source)
{ {
// Remove weapon that might be planted there
foreach (var child in GetChildren())
{
if (child is WeaponSystem system)
{
CallDeferred(Node.MethodName.RemoveChild, system);
GetTree().GetRoot().CallDeferred(Node.MethodName.AddChild, system);
system.CallDeferred(Node3D.MethodName.SetGlobalPosition, GlobalPosition + Vector3.Up*EnemyHeight);
system.CallDeferred(WeaponSystem.MethodName.RethrowWeapon);
}
}
foreach (var killable in DeathEffects.ToIKillables()) foreach (var killable in DeathEffects.ToIKillables())
{ {
killable.Kill(source); killable.Kill(source);
} }
QueueFree(); CallDeferred(Node.MethodName.QueueFree);
} }
public void RegisterKnockback(IDamageable source, DamageRecord damageRecord) public void RegisterKnockback(IDamageable source, DamageRecord damageRecord)
{ {
if (CKnockback is not IKnockbackable knockbackable) return; if (CKnockback is null) return;
knockbackable.RegisterKnockback(source, damageRecord); CKnockback.RegisterKnockback(source, damageRecord);
} }
public Vector3 ComputeKnockback() public Vector3 ComputeKnockback()
{ {
if (CKnockback is not IKnockbackable knockbackable) return Vector3.Zero; if (CKnockback is null) return Vector3.Zero;
return knockbackable.ComputeKnockback(); return CKnockback.ComputeKnockback();
}
public Vector3 GetTargetGlobalPosition()
{
if (_target is null) return GlobalPosition;
return _target.GlobalPosition;
}
// Stun management
public bool IsStunned { get; set; } = false;
[Export(PropertyHint.Range, "0.1, 2, 0.1, or_greater")]
public float StunDuration { get; set; } = 1f;
public void Stun()
{
IsStunned = true;
GetTree().CreateTimer(StunDuration).Timeout += Unstun;
}
public void Unstun()
{
IsStunned = false;
} }
} }

View File

@@ -1,4 +1,4 @@
[gd_scene load_steps=20 format=3 uid="uid://cmlud1hwkd6sv"] [gd_scene load_steps=21 format=3 uid="uid://cmlud1hwkd6sv"]
[ext_resource type="Script" uid="uid://bn7sc6id7n166" path="res://scenes/enemies/Enemy.cs" id="1_q8l7o"] [ext_resource type="Script" uid="uid://bn7sc6id7n166" path="res://scenes/enemies/Enemy.cs" id="1_q8l7o"]
[ext_resource type="Script" uid="uid://b6y3ugfydvch0" path="res://components/damage/RDamageModifier.cs" id="2_1bsgx"] [ext_resource type="Script" uid="uid://b6y3ugfydvch0" path="res://components/damage/RDamageModifier.cs" id="2_1bsgx"]
@@ -7,6 +7,7 @@
[ext_resource type="Resource" uid="uid://bwqjaom4k7rc3" path="res://scenes/enemies/flying_enemy/flying_enemy_movement.tres" id="4_dejyg"] [ext_resource type="Resource" uid="uid://bwqjaom4k7rc3" path="res://scenes/enemies/flying_enemy/flying_enemy_movement.tres" id="4_dejyg"]
[ext_resource type="Script" uid="uid://bjwrpv3jpsc1e" path="res://components/health/CHealth.cs" id="4_ys4jv"] [ext_resource type="Script" uid="uid://bjwrpv3jpsc1e" path="res://components/health/CHealth.cs" id="4_ys4jv"]
[ext_resource type="PackedScene" uid="uid://dmw5ibwrb483f" path="res://components/movement/CFlyingMovement.tscn" id="7_vaeds"] [ext_resource type="PackedScene" uid="uid://dmw5ibwrb483f" path="res://components/movement/CFlyingMovement.tscn" id="7_vaeds"]
[ext_resource type="PackedScene" uid="uid://bwx2um43k0ou4" path="res://components/health/CHealthbar.tscn" id="7_ykkxn"]
[ext_resource type="Script" uid="uid://dtpxijlnb2c5" path="res://components/movement/RMovement.cs" id="8_on7rt"] [ext_resource type="Script" uid="uid://dtpxijlnb2c5" path="res://components/movement/RMovement.cs" id="8_on7rt"]
[ext_resource type="Script" uid="uid://b0u23nkpaimyc" path="res://components/damage/CDamageable.cs" id="8_uotso"] [ext_resource type="Script" uid="uid://b0u23nkpaimyc" path="res://components/damage/CDamageable.cs" id="8_uotso"]
[ext_resource type="PackedScene" uid="uid://bctpe34ddamg5" path="res://components/knockback/CKnockback.tscn" id="10_dejyg"] [ext_resource type="PackedScene" uid="uid://bctpe34ddamg5" path="res://components/knockback/CKnockback.tscn" id="10_dejyg"]
@@ -42,19 +43,16 @@ albedo_color = Color(0.06469653, 0.06469653, 0.06469653, 1)
[sub_resource type="BoxShape3D" id="BoxShape3D_4yfjf"] [sub_resource type="BoxShape3D" id="BoxShape3D_4yfjf"]
[node name="FlyingEnemy" type="CharacterBody3D" node_paths=PackedStringArray("CHealth", "CDamage", "CKnockback", "CMovement")] [node name="FlyingEnemy" type="CharacterBody3D"]
collision_layer = 16 collision_layer = 16
collision_mask = 273 collision_mask = 273
motion_mode = 1 motion_mode = 1
script = ExtResource("1_q8l7o") script = ExtResource("1_q8l7o")
CHealth = NodePath("CHealth") EnemyHeight = 0.5
RHealth = ExtResource("2_ma2bq") RHealth = ExtResource("2_ma2bq")
DeathEffects = Array[Object]([]) DeathEffects = Array[Object]([])
CDamage = NodePath("CDamageable")
RDamage = ExtResource("2_on7rt") RDamage = ExtResource("2_on7rt")
CKnockback = NodePath("CKnockback")
RKnockback = ExtResource("11_mpa2u") RKnockback = ExtResource("11_mpa2u")
CMovement = NodePath("CFlyingMovement")
RMovement = ExtResource("4_dejyg") RMovement = ExtResource("4_dejyg")
[node name="CHealth" type="Node" parent="."] [node name="CHealth" type="Node" parent="."]
@@ -62,18 +60,23 @@ script = ExtResource("4_ys4jv")
RHealth = ExtResource("2_ma2bq") RHealth = ExtResource("2_ma2bq")
metadata/_custom_type_script = "uid://bjwrpv3jpsc1e" metadata/_custom_type_script = "uid://bjwrpv3jpsc1e"
[node name="CHealthBar" parent="." instance=ExtResource("7_ykkxn")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0.70000005, 0)
[node name="CDamageable" type="Node" parent="."] [node name="CDamageable" type="Node" parent="."]
script = ExtResource("8_uotso") script = ExtResource("8_uotso")
DamageModifiers = Array[Object]([SubResource("Resource_jnv07"), SubResource("Resource_53j1c")]) DamageModifiers = Array[Object]([SubResource("Resource_jnv07"), SubResource("Resource_53j1c")])
metadata/_custom_type_script = "uid://b0u23nkpaimyc" metadata/_custom_type_script = "uid://b0u23nkpaimyc"
[node name="CFlyingMovement" parent="." instance=ExtResource("7_vaeds")] [node name="CMovement" parent="." instance=ExtResource("7_vaeds")]
RMovement = SubResource("Resource_on7rt") RMovement = SubResource("Resource_on7rt")
TerrainCollision = 256 TerrainCollision = 256
[node name="CKnockback" parent="." instance=ExtResource("10_dejyg")] [node name="CKnockback" parent="." instance=ExtResource("10_dejyg")]
RKnockback = ExtResource("11_mpa2u") RKnockback = ExtResource("11_mpa2u")
[node name="CTarget" type="Marker3D" parent="."]
[node name="CollisionShape3D" type="CollisionShape3D" parent="."] [node name="CollisionShape3D" type="CollisionShape3D" parent="."]
shape = SubResource("SphereShape3D_b46rq") shape = SubResource("SphereShape3D_b46rq")

View File

@@ -4,5 +4,5 @@
[resource] [resource]
script = ExtResource("1_jht15") script = ExtResource("1_jht15")
StartingHealth = 50.0 StartingHealth = 10.0
metadata/_custom_type_script = "uid://baiapod3csndf" metadata/_custom_type_script = "uid://baiapod3csndf"

View File

@@ -1,4 +1,4 @@
[gd_scene load_steps=19 format=3 uid="uid://dxt0e2ugmttqq"] [gd_scene load_steps=20 format=3 uid="uid://dxt0e2ugmttqq"]
[ext_resource type="Script" uid="uid://bn7sc6id7n166" path="res://scenes/enemies/Enemy.cs" id="1_r6506"] [ext_resource type="Script" uid="uid://bn7sc6id7n166" path="res://scenes/enemies/Enemy.cs" id="1_r6506"]
[ext_resource type="Resource" uid="uid://otfc2snh8umc" path="res://scenes/enemies/grounded_enemy/grounded_enemy_damage.tres" id="2_bn56u"] [ext_resource type="Resource" uid="uid://otfc2snh8umc" path="res://scenes/enemies/grounded_enemy/grounded_enemy_damage.tres" id="2_bn56u"]
@@ -7,6 +7,7 @@
[ext_resource type="Resource" uid="uid://bohbojc68j7y1" path="res://scenes/enemies/grounded_enemy/grounded_enemy_health.tres" id="2_w4lm8"] [ext_resource type="Resource" uid="uid://bohbojc68j7y1" path="res://scenes/enemies/grounded_enemy/grounded_enemy_health.tres" id="2_w4lm8"]
[ext_resource type="Resource" uid="uid://bqq6uukbdfysr" path="res://scenes/enemies/grounded_enemy/grounded_enemy_movement.tres" id="4_na24f"] [ext_resource type="Resource" uid="uid://bqq6uukbdfysr" path="res://scenes/enemies/grounded_enemy/grounded_enemy_movement.tres" id="4_na24f"]
[ext_resource type="Script" uid="uid://b0u23nkpaimyc" path="res://components/damage/CDamageable.cs" id="7_1tw73"] [ext_resource type="Script" uid="uid://b0u23nkpaimyc" path="res://components/damage/CDamageable.cs" id="7_1tw73"]
[ext_resource type="PackedScene" uid="uid://bwx2um43k0ou4" path="res://components/health/CHealthbar.tscn" id="7_18xwy"]
[ext_resource type="PackedScene" uid="uid://dbr7ioio158ew" path="res://components/movement/CGroundedMovement.tscn" id="7_qyswd"] [ext_resource type="PackedScene" uid="uid://dbr7ioio158ew" path="res://components/movement/CGroundedMovement.tscn" id="7_qyswd"]
[ext_resource type="Script" uid="uid://dtpxijlnb2c5" path="res://components/movement/RMovement.cs" id="8_6d4gl"] [ext_resource type="Script" uid="uid://dtpxijlnb2c5" path="res://components/movement/RMovement.cs" id="8_6d4gl"]
[ext_resource type="PackedScene" uid="uid://bctpe34ddamg5" path="res://components/knockback/CKnockback.tscn" id="10_jqqi6"] [ext_resource type="PackedScene" uid="uid://bctpe34ddamg5" path="res://components/knockback/CKnockback.tscn" id="10_jqqi6"]
@@ -14,7 +15,7 @@
[sub_resource type="Resource" id="Resource_qj0ob"] [sub_resource type="Resource" id="Resource_qj0ob"]
script = ExtResource("2_r3cnf") script = ExtResource("2_r3cnf")
Modifier = 3.0 Modifier = 1.0
metadata/_custom_type_script = "uid://b6y3ugfydvch0" metadata/_custom_type_script = "uid://b6y3ugfydvch0"
[sub_resource type="Resource" id="Resource_6d4gl"] [sub_resource type="Resource" id="Resource_6d4gl"]
@@ -38,18 +39,15 @@ albedo_color = Color(0.06469653, 0.06469653, 0.06469653, 1)
[sub_resource type="BoxShape3D" id="BoxShape3D_4yfjf"] [sub_resource type="BoxShape3D" id="BoxShape3D_4yfjf"]
size = Vector3(1, 2, 1.5) size = Vector3(1, 2, 1.5)
[node name="GroundedEnemy" type="CharacterBody3D" node_paths=PackedStringArray("CHealth", "CDamage", "CKnockback", "CMovement")] [node name="GroundedEnemy" type="CharacterBody3D"]
collision_layer = 16 collision_layer = 16
collision_mask = 273 collision_mask = 273
script = ExtResource("1_r6506") script = ExtResource("1_r6506")
CHealth = NodePath("CHealth") EnemyHeight = 2.0
RHealth = ExtResource("2_w4lm8") RHealth = ExtResource("2_w4lm8")
DeathEffects = Array[Object]([]) DeathEffects = Array[Object]([])
CDamage = NodePath("CDamageable")
RDamage = ExtResource("2_bn56u") RDamage = ExtResource("2_bn56u")
CKnockback = NodePath("CKnockback")
RKnockback = ExtResource("11_8k3xb") RKnockback = ExtResource("11_8k3xb")
CMovement = NodePath("CGroundedMovement")
RMovement = ExtResource("4_na24f") RMovement = ExtResource("4_na24f")
[node name="CHealth" type="Node" parent="."] [node name="CHealth" type="Node" parent="."]
@@ -57,17 +55,23 @@ script = ExtResource("2_gsmti")
RHealth = ExtResource("2_w4lm8") RHealth = ExtResource("2_w4lm8")
metadata/_custom_type_script = "uid://bjwrpv3jpsc1e" metadata/_custom_type_script = "uid://bjwrpv3jpsc1e"
[node name="CHealthBar" parent="." instance=ExtResource("7_18xwy")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2.2, 0)
[node name="CDamageable" type="Node" parent="."] [node name="CDamageable" type="Node" parent="."]
script = ExtResource("7_1tw73") script = ExtResource("7_1tw73")
DamageModifiers = Array[Object]([SubResource("Resource_qj0ob")]) DamageModifiers = Array[Object]([SubResource("Resource_qj0ob")])
metadata/_custom_type_script = "uid://b0u23nkpaimyc" metadata/_custom_type_script = "uid://b0u23nkpaimyc"
[node name="CGroundedMovement" parent="." node_paths=PackedStringArray("WallInFrontRayCast") instance=ExtResource("7_qyswd")] [node name="CMovement" parent="." node_paths=PackedStringArray("WallInFrontRayCast") instance=ExtResource("7_qyswd")]
RMovement = SubResource("Resource_6d4gl") RMovement = SubResource("Resource_6d4gl")
WallInFrontRayCast = NodePath("../WallInFrontRayCast") WallInFrontRayCast = NodePath("../WallInFrontRayCast")
[node name="CKnockback" parent="." instance=ExtResource("10_jqqi6")] [node name="CKnockback" parent="." instance=ExtResource("10_jqqi6")]
[node name="CTarget" type="Marker3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0)
[node name="CollisionShape3D" type="CollisionShape3D" parent="."] [node name="CollisionShape3D" type="CollisionShape3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0) transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0)
shape = SubResource("CapsuleShape3D_62kkh") shape = SubResource("CapsuleShape3D_62kkh")

View File

@@ -4,4 +4,5 @@
[resource] [resource]
script = ExtResource("1_h6jgd") script = ExtResource("1_h6jgd")
StartingHealth = 10.0
metadata/_custom_type_script = "uid://baiapod3csndf" metadata/_custom_type_script = "uid://baiapod3csndf"

View File

@@ -1,10 +1,11 @@
using Godot; using Godot;
using Movementtests.interfaces;
namespace Movementtests.systems; namespace Movementtests.systems;
public partial class DashSystem: Node3D public partial class DashSystem: Node3D
{ {
public record DashLocation(bool HasHit, Vector3 TargetLocation, Vector3 CollisionPoint, Vector3 CollisionNormal); public record DashLocation(bool HasHit, Vector3 TargetLocation, Vector3 CollisionPoint, Vector3 CollisionNormal, GodotObject HitObject = null);
[Export(PropertyHint.Range, "0,0.2,0.01,or_greater")] [Export(PropertyHint.Range, "0,0.2,0.01,or_greater")]
@@ -13,17 +14,19 @@ public partial class DashSystem: Node3D
public float PostDashSpeed { get; set; } = 0f; public float PostDashSpeed { get; set; } = 0f;
public bool HasHit { get; set; } public bool HasHit { get; set; }
public bool CanDashThroughTarget { get; set; }
public Vector3 TargetLocation { get; set; } public Vector3 TargetLocation { get; set; }
public Vector3 CollisionPoint { get; set; } public Vector3 CollisionPoint { get; set; }
public Vector3 CollisionNormal { get; set; } public Vector3 CollisionNormal { get; set; }
public GodotObject CollidedObject { get; set; }
public Vector3 PlannedLocation { get; set; } public Vector3 PlannedLocation { get; set; }
public bool ShouldMantle { get; set; } public bool ShouldMantle { get; set; }
public Vector3 PlannedMantleLocation { get; set; } public Vector3 PlannedMantleLocation { get; set; }
public MantleSystem MantleSystem { get; set; } public MantleSystem MantleSystem { get; set; }
private Node3D _head; private HeadSystem _head;
private ShapeCast3D _dashCast3D; public ShapeCast3D DashCast3D;
private Camera3D _camera; private Camera3D _camera;
private Vector3 _dashDirection = Vector3.Zero; private Vector3 _dashDirection = Vector3.Zero;
@@ -49,10 +52,10 @@ public partial class DashSystem: Node3D
public float DashCastRadius { get; set; } public float DashCastRadius { get; set; }
public void Init(Node3D head, Camera3D camera) public void Init(HeadSystem head, Camera3D camera)
{ {
_dashCast3D = GetNode<ShapeCast3D>("DashCast3D"); DashCast3D = GetNode<ShapeCast3D>("DashCast3D");
var dashShape = _dashCast3D.GetShape() as SphereShape3D; var dashShape = DashCast3D.GetShape() as SphereShape3D;
DashCastRadius = dashShape!.Radius; DashCastRadius = dashShape!.Radius;
_dashCastDrop = GetNode<ShapeCast3D>("DashCastDrop"); _dashCastDrop = GetNode<ShapeCast3D>("DashCastDrop");
@@ -75,33 +78,36 @@ public partial class DashSystem: Node3D
private DashLocation ComputeDashLocation() private DashLocation ComputeDashLocation()
{ {
var targetLocation = _dashCast3D.ToGlobal(_dashCast3D.TargetPosition); var targetLocation = DashCast3D.ToGlobal(DashCast3D.TargetPosition);
var hasHit = _dashCast3D.IsColliding(); var hasHit = DashCast3D.IsColliding();
if (!hasHit) if (!hasHit)
{ {
return new DashLocation(false, targetLocation, Vector3.Zero, Vector3.Zero); return new DashLocation(false, targetLocation, Vector3.Zero, Vector3.Zero);
} }
var collisionPoint = _dashCast3D.GetCollisionPoint(0); var collisionPoint = DashCast3D.GetCollisionPoint(0);
var collisionNormal = _dashCast3D.GetCollisionNormal(0); var collisionNormal = DashCast3D.GetCollisionNormal(0);
var collidedObject = DashCast3D.GetCollider(0);
var fraction = _dashCast3D.GetClosestCollisionSafeFraction(); var fraction = DashCast3D.GetClosestCollisionSafeFraction();
var globalSweepPath = targetLocation - _dashCast3D.GlobalPosition; var globalSweepPath = targetLocation - DashCast3D.GlobalPosition;
var locationAlongPath = _dashCast3D.GlobalPosition + globalSweepPath * fraction; var locationAlongPath = DashCast3D.GlobalPosition + globalSweepPath * fraction;
return new DashLocation(true, locationAlongPath, collisionPoint, collisionNormal); return new DashLocation(true, locationAlongPath, collisionPoint, collisionNormal, collidedObject);
} }
public void PrepareDash() public void PrepareDash()
{ {
_dashCast3D.SetRotation(new Vector3( DashCast3D.SetRotation(_head.GetGlobalLookRotation());
_camera.Rotation.X,
_head.Rotation.Y,
_camera.Rotation.Z));
(HasHit, PlannedLocation, CollisionPoint, CollisionNormal) = ComputeDashLocation(); (HasHit, PlannedLocation, CollisionPoint, CollisionNormal, CollidedObject) = ComputeDashLocation();
CanDashThroughTarget = false;
if (CollidedObject is ITargetable targetable)
{
_dashTarget.SetVisible(false);
CanDashThroughTarget = true;
return;
}
// TODO: Position mantle system to planned location, aligned with ground planned and facing the same way as the dash
// Then query it being careful when dashing underneath a platform and such
MantleSystem.SetGlobalPosition(PlannedLocation); MantleSystem.SetGlobalPosition(PlannedLocation);
MantleSystem.SetRotation(new Vector3( MantleSystem.SetRotation(new Vector3(
MantleSystem.Rotation.X, MantleSystem.Rotation.X,
@@ -118,6 +124,7 @@ public partial class DashSystem: Node3D
_dashTarget.SetVisible(true); _dashTarget.SetVisible(true);
var targetLocation = ShouldMantle ? MantleSystem.FirstMantleProfilePoint : PlannedLocation; var targetLocation = ShouldMantle ? MantleSystem.FirstMantleProfilePoint : PlannedLocation;
_dashTarget.SetGlobalPosition(targetLocation); _dashTarget.SetGlobalPosition(targetLocation);
return;
var shouldShowDropIndicator = !HasHit && !ShouldMantle; var shouldShowDropIndicator = !HasHit && !ShouldMantle;
_dashDropIndicator.SetVisible(shouldShowDropIndicator); _dashDropIndicator.SetVisible(shouldShowDropIndicator);
@@ -129,7 +136,7 @@ public partial class DashSystem: Node3D
// End of the drop is either max cast distance or first collision // End of the drop is either max cast distance or first collision
var hasDropLocationHit = _dashCastDrop.IsColliding(); var hasDropLocationHit = _dashCastDrop.IsColliding();
var endDropLocation = hasDropLocationHit ? _dashCastDrop.GetCollisionPoint(0) : _dashCastDrop.ToGlobal(_dashCast3D.TargetPosition); var endDropLocation = hasDropLocationHit ? _dashCastDrop.GetCollisionPoint(0) : _dashCastDrop.ToGlobal(DashCast3D.TargetPosition);
// Only show drop location indicator if drop cast has hit // Only show drop location indicator if drop cast has hit
_dashDropLocationIndicator.SetVisible(hasDropLocationHit); _dashDropLocationIndicator.SetVisible(hasDropLocationHit);
@@ -144,13 +151,9 @@ public partial class DashSystem: Node3D
public void StopPreparingDash() public void StopPreparingDash()
{ {
CanDashThroughTarget = false;
_dashTarget.SetVisible(false); _dashTarget.SetVisible(false);
_dashDropIndicator.SetVisible(false); _dashDropIndicator.SetVisible(false);
_dashDropLocationIndicator.SetVisible(false); _dashDropLocationIndicator.SetVisible(false);
} }
public void StartPreparingDash()
{
_dashTarget.SetVisible(true);
}
} }

View File

@@ -28,7 +28,7 @@ transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1.6, 0)
shape = SubResource("SphereShape3D_jngg2") shape = SubResource("SphereShape3D_jngg2")
target_position = Vector3(0, 0, -12) target_position = Vector3(0, 0, -12)
max_results = 1 max_results = 1
collision_mask = 256 collision_mask = 304
debug_shape_custom_color = Color(0.911631, 0.11884, 0.656218, 1) debug_shape_custom_color = Color(0.911631, 0.11884, 0.656218, 1)
[node name="DashCastDrop" type="ShapeCast3D" parent="."] [node name="DashCastDrop" type="ShapeCast3D" parent="."]
@@ -36,7 +36,7 @@ transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 0, 1
shape = SubResource("SphereShape3D_jngg2") shape = SubResource("SphereShape3D_jngg2")
target_position = Vector3(0, 0, -50) target_position = Vector3(0, 0, -50)
max_results = 1 max_results = 1
collision_mask = 256 collision_mask = 304
debug_shape_custom_color = Color(0.911631, 0.11884, 0.656218, 1) debug_shape_custom_color = Color(0.911631, 0.11884, 0.656218, 1)
[node name="DashTarget" type="MeshInstance3D" parent="."] [node name="DashTarget" type="MeshInstance3D" parent="."]

View File

@@ -310,6 +310,14 @@ public partial class HeadSystem : Node3D
return GetGlobalTransform().Basis.Z; return GetGlobalTransform().Basis.Z;
} }
public Vector3 GetGlobalLookRotation()
{
return new Vector3(
_camera.Rotation.X,
Rotation.Y,
_camera.Rotation.Z);
}
public void SetHeight(float height) public void SetHeight(float height)
{ {
Position = new Vector3(Position.X, height, Position.Z); Position = new Vector3(Position.X, height, Position.Z);

View File

@@ -22,27 +22,23 @@ public partial class WeaponSystem : RigidBody3D
public StateChartState FlyingState; public StateChartState FlyingState;
public StateChartState PlantedState; public StateChartState PlantedState;
private Node3D _head;
private ShapeCast3D _dashCast3D; private ShapeCast3D _dashCast3D;
private Camera3D _camera;
private TweenQueueSystem _tweenQueueSystem; private TweenQueueSystem _tweenQueueSystem;
private Transform3D _startTransform; private Transform3D _startTransform;
private Transform3D _startMeshTransform; private Vector3 _startMeshRotation;
private Vector3 _throwDirection; private Vector3 _throwDirection;
public Vector3 PlantLocation { get; set; } public Vector3 PlantLocation { get; set; }
public Vector3 PlantNormal { get; set; } public Vector3 PlantNormal { get; set; }
public Node PlantObject { get; set; }
public MeshInstance3D WeaponLocationIndicator { get; set; } public MeshInstance3D WeaponLocationIndicator { get; set; }
public StandardMaterial3D WeaponLocationIndicatorMaterial { get; set; } public StandardMaterial3D WeaponLocationIndicatorMaterial { get; set; }
public MeshInstance3D WeaponMesh { get; set; } public MeshInstance3D WeaponMesh { get; set; }
public void Init(Node3D head, Camera3D camera) public void Init()
{ {
_head = head;
_camera = camera;
_weaponState = StateChart.Of(GetNode("StateChart")); _weaponState = StateChart.Of(GetNode("StateChart"));
InHandState = StateChartState.Of(GetNode("StateChart/Root/InHand")); InHandState = StateChartState.Of(GetNode("StateChart/Root/InHand"));
FlyingState = StateChartState.Of(GetNode("StateChart/Root/Flying")); FlyingState = StateChartState.Of(GetNode("StateChart/Root/Flying"));
@@ -53,7 +49,7 @@ public partial class WeaponSystem : RigidBody3D
WeaponLocationIndicatorMaterial = WeaponLocationIndicator.GetActiveMaterial(0) as StandardMaterial3D; WeaponLocationIndicatorMaterial = WeaponLocationIndicator.GetActiveMaterial(0) as StandardMaterial3D;
WeaponMesh = GetNode<MeshInstance3D>("Weapon"); WeaponMesh = GetNode<MeshInstance3D>("Weapon");
_startMeshTransform = WeaponMesh.Transform; _startMeshRotation = WeaponMesh.Rotation;
_tweenQueueSystem = GetNode<TweenQueueSystem>("TweenQueueSystem"); _tweenQueueSystem = GetNode<TweenQueueSystem>("TweenQueueSystem");
_tweenQueueSystem.Init(this); _tweenQueueSystem.Init(this);
@@ -90,7 +86,7 @@ public partial class WeaponSystem : RigidBody3D
PlantLocation = location; PlantLocation = location;
} }
public void ThrowWeapon(Vector3 end, bool hasHit, Vector3 collisionLocation, Vector3 collisionNormal) public void ThrowWeapon(Vector3 end, bool hasHit, Vector3 collisionLocation, Vector3 collisionNormal, Node collidedObject)
{ {
_weaponState.SendEvent("throw"); _weaponState.SendEvent("throw");
@@ -103,11 +99,21 @@ public partial class WeaponSystem : RigidBody3D
var tween = _tweenQueueSystem.TweenToLocation(new TweenQueueSystem.TweenInputs(end, StraightThrowDuration)); var tween = _tweenQueueSystem.TweenToLocation(new TweenQueueSystem.TweenInputs(end, StraightThrowDuration));
if (hasHit) if (hasHit)
{
PlantObject = collidedObject;
tween.Finished += PlantWeaponInWall; tween.Finished += PlantWeaponInWall;
}
else else
tween.Finished += ThrowWeaponOnCurve; tween.Finished += ThrowWeaponOnCurve;
} }
public void RethrowWeapon()
{
_weaponState.SendEvent("throw");
_throwDirection = Vector3.Up;
ThrowWeaponOnCurve();
}
public void ThrowWeaponOnCurve() public void ThrowWeaponOnCurve()
{ {
Freeze = false; Freeze = false;
@@ -118,16 +124,22 @@ public partial class WeaponSystem : RigidBody3D
{ {
_weaponState.SendEvent("plant"); _weaponState.SendEvent("plant");
// WeaponLocationIndicatorMaterial.StencilColor = new Color(1f, 0.2f, 0.2f);
Freeze = true; Freeze = true;
GlobalPosition = PlantLocation; WeaponMesh.Rotation = _startMeshRotation;
WeaponMesh.Transform = _startMeshTransform;
LookAt(GlobalTransform.Origin + PlantNormal, Vector3.Up, true); // WeaponLocationIndicatorMaterial.StencilColor = new Color(1f, 0.2f, 0.2f);
if (PlantObject is Node3D node)
{
GetTree().GetRoot().CallDeferred(Node.MethodName.RemoveChild, this);
node.CallDeferred(Node.MethodName.AddChild, this);
}
CallDeferred(Node3D.MethodName.SetGlobalPosition, PlantLocation);
CallDeferred(Node3D.MethodName.LookAt, GlobalTransform.Origin + PlantNormal, Vector3.Up, true);
} }
public void OnThrownWeaponReachesGround(Node other) public void OnThrownWeaponReachesGround(Node other)
{ {
PlantObject = other;
PlantWeaponInWall(); PlantWeaponInWall();
} }

View File

@@ -38,7 +38,7 @@ material = SubResource("StandardMaterial3D_m0v1h")
[node name="Weapon" type="RigidBody3D"] [node name="Weapon" type="RigidBody3D"]
collision_layer = 65536 collision_layer = 65536
collision_mask = 256 collision_mask = 304
continuous_cd = true continuous_cd = true
contact_monitor = true contact_monitor = true
max_contacts_reported = 1 max_contacts_reported = 1
@@ -54,6 +54,9 @@ shape = SubResource("CylinderShape3D_avini")
transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0, 0.8673003) transform = Transform3D(1, 0, 0, 0, -4.37114e-08, 1, 0, -1, -4.37114e-08, 0, 0, 0.8673003)
mesh = ExtResource("3_svc06") mesh = ExtResource("3_svc06")
[node name="WeaponLocationIndicator" type="MeshInstance3D" parent="."]
mesh = SubResource("SphereMesh_jpdh0")
[node name="StateChart" type="Node" parent="."] [node name="StateChart" type="Node" parent="."]
script = ExtResource("3_5owyf") script = ExtResource("3_5owyf")
metadata/_custom_type_script = "uid://couw105c3bde4" metadata/_custom_type_script = "uid://couw105c3bde4"
@@ -89,11 +92,14 @@ delay_in_seconds = "0.0"
[node name="Planted" type="Node" parent="StateChart/Root"] [node name="Planted" type="Node" parent="StateChart/Root"]
script = ExtResource("5_m0v1h") script = ExtResource("5_m0v1h")
[node name="ToFlying" type="Node" parent="StateChart/Root/Planted"]
script = ExtResource("6_jpdh0")
to = NodePath("../../Flying")
event = &"throw"
delay_in_seconds = "0.0"
[node name="ToHand" type="Node" parent="StateChart/Root/Planted"] [node name="ToHand" type="Node" parent="StateChart/Root/Planted"]
script = ExtResource("6_jpdh0") script = ExtResource("6_jpdh0")
to = NodePath("../../InHand") to = NodePath("../../InHand")
event = &"recover" event = &"recover"
delay_in_seconds = "0.0" delay_in_seconds = "0.0"
[node name="WeaponLocationIndicator" type="MeshInstance3D" parent="."]
mesh = SubResource("SphereMesh_jpdh0")