Compare commits

...

3 Commits

Author SHA1 Message Date
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
24 changed files with 495 additions and 59 deletions

View File

@@ -1,5 +1,6 @@
<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_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_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>

View File

@@ -20,4 +20,12 @@ public partial class CDamageable : Node, IDamageable
DamageTaken?.Invoke(this, 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

@@ -30,4 +30,13 @@ public partial class RDamageModifier : Resource, IDamageable
DamageTaken?.Invoke(this, 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

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

7
global.json Normal file
View File

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

View File

@@ -9,4 +9,5 @@ public interface IDamageable
{
event Action<IDamageable, DamageRecord> DamageTaken;
DamageRecord TakeDamage(DamageRecord damageRecord);
DamageRecord ComputeDamage(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=24 format=3 uid="uid://q7uc1h2jpbd2"]
[gd_scene load_steps=25 format=3 uid="uid://q7uc1h2jpbd2"]
[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="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="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="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="Script" uid="uid://dtpxijlnb2c5" path="res://components/movement/RMovement.cs" id="6_ybosk"]
[ext_resource type="Resource" uid="uid://bqq6uukbdfysr" path="res://scenes/enemies/grounded_enemy/grounded_enemy_movement.tres" id="7_caohq"]
@@ -14,10 +14,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://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"]
[sub_resource type="Resource" id="Resource_2e4ci"]
script = ExtResource("2_sysok")
metadata/_custom_type_script = "uid://jitubgv6judn"
[ext_resource type="PackedScene" uid="uid://qup00a7x2sji" path="res://scenes/FixedDashTarget/fixed_dashthrough_target.tscn" id="15_5fa36"]
[sub_resource type="ProceduralSkyMaterial" id="ProceduralSkyMaterial_0xm2m"]
sky_horizon_color = Color(0.662243, 0.671743, 0.686743, 1)
@@ -45,12 +42,18 @@ glow_enabled = true
script = ExtResource("5_7m3bq")
metadata/_custom_type_script = "uid://b4cwruitopcee"
[sub_resource type="Resource" id="Resource_q21h6"]
script = ExtResource("6_1hrkh")
metadata/_custom_type_script = "uid://b44cse62qru7j"
[sub_resource type="Resource" id="Resource_5fa36"]
script = ExtResource("6_ybosk")
GravityModifier = 1.0
metadata/_custom_type_script = "uid://dtpxijlnb2c5"
[sub_resource type="Resource" id="Resource_blhrq"]
script = ExtResource("6_ybosk")
GravityModifier = 1.0
metadata/_custom_type_script = "uid://dtpxijlnb2c5"
[sub_resource type="Resource" id="Resource_1hrkh"]
@@ -68,7 +71,6 @@ metadata/_custom_type_script = "uid://baiapod3csndf"
[node name="Player" parent="." instance=ExtResource("1_62kkh")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 7.5)
TutorialDone = true
RDamage = SubResource("Resource_2e4ci")
[node name="WorldEnvironment" type="WorldEnvironment" parent="."]
environment = SubResource("Environment_1bvp3")
@@ -118,6 +120,12 @@ use_collision = true
size = Vector3(6.5, 11, 5.5)
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"]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 18.154, 4.5, -14.2085)
use_collision = true
@@ -134,16 +142,19 @@ material = ExtResource("2_3uydm")
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, -16.83681)
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")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, -3, 0, -16.83681)
Target = NodePath("../Player")
RKnockback = SubResource("Resource_q21h6")
RMovement = SubResource("Resource_blhrq")
[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, 7, 0, 0.16319084)
Target = NodePath("../Player")
RKnockback = SubResource("Resource_q21h6")
RMovement = SubResource("Resource_5fa36")
[node name="FlyingEnemy" parent="." node_paths=PackedStringArray("Target") instance=ExtResource("5_8fd2t")]
@@ -173,3 +184,6 @@ HealthInputs = ExtResource("11_2e4ci")
DamageInputs = ExtResource("9_gp7s3")
Target = NodePath("../Player")
IsActiveOnStart = false
[node name="FixedDashthroughTarget" parent="." instance=ExtResource("15_5fa36")]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0.5, 3.5, 0)

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="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="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://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"]
@@ -47,6 +50,22 @@
[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="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 = 3.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"]
height = 1.7
@@ -67,6 +86,9 @@ top_radius = 0.2
bottom_radius = 0.2
height = 1.0
[sub_resource type="SphereShape3D" id="SphereShape3D_cmijs"]
radius = 1.0
[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_6lejt"]
radius = 1.0
height = 3.5
@@ -80,6 +102,9 @@ blend_mode = 1
[node name="Player" type="CharacterBody3D"]
collision_mask = 272
script = ExtResource("1_poq2x")
RDamage = SubResource("Resource_cb2lu")
RKnockback = SubResource("Resource_abfq8")
RHealth = SubResource("Resource_ue7xq")
WalkSpeed = 7.5
AccelerationFloor = 4.0
DecelerationFloor = 3.0
@@ -296,6 +321,13 @@ visible = false
transform = Transform3D(1, 0, 0, 0, -4.371139e-08, 1, 0, -1, -4.371139e-08, 0, 0, -1)
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="."]
shape = SubResource("CapsuleShape3D_6lejt")
collision_mask = 256
@@ -344,7 +376,6 @@ enabled = false
initial_node_to_watch = NodePath("../StateChart")
[node name="UI" type="Control" parent="."]
visible = false
layout_mode = 3
anchors_preset = 15
anchor_right = 1.0
@@ -434,6 +465,24 @@ unique_name_in_owner = true
custom_minimum_size = Vector2(100, 10)
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="."]
script = ExtResource("25_wv70j")
metadata/_custom_type_script = "uid://couw105c3bde4"
@@ -509,6 +558,49 @@ to = NodePath("../../AtLeastOneCharge")
event = &"power_used"
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"]
script = ExtResource("26_infe6")
initial_state = NodePath("Grounded")

View File

@@ -4,16 +4,53 @@ using System;
public partial class PlayerUi : Control
{
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()
{
base._Ready();
_dashIcons[0] = GetNode<TextureRect>("%Dash1");
_dashIcons[1] = GetNode<TextureRect>("%Dash2");
_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)
{
int index = 1;

View File

@@ -7,6 +7,7 @@ using Movementtests.addons.godot_state_charts.csharp;
using Movementtests.interfaces;
using Movementtests.systems;
using Movementtests.player_controller.Scripts;
using Movementtests.systems.damage;
using RustyOptions;
public partial class PlayerController : CharacterBody3D,
@@ -71,16 +72,18 @@ public partial class PlayerController : CharacterBody3D,
[Export] public Marker3D TutorialWeaponTarget;
[Export] public bool TutorialDone { get; set; }
// Combat stuff
[ExportCategory("Combat")]
[ExportGroup("Damage")]
[Export]
public RDamage RDamage { get; set; }
[Export] public RDamage RDamage { get; set; }
[Export] public RKnockback RKnockback { get; set; }
[Export] public RHealth RHealth { get; set; }
[Export]
public RKnockback RKnockback { get; set; }
[Export(PropertyHint.Range, "0,20,0.1,or_greater")]
public float TargetingDistance { get; set; } = 10.0f;
[Export]
public RHealth RHealth { get; set; }
[Export(PropertyHint.Range, "0,20,0.1,or_greater")]
public float TargetInRangeDistance { get; set; } = 5.0f;
// Movement stuff
[ExportCategory("Movement")]
@@ -321,6 +324,11 @@ public partial class PlayerController : CharacterBody3D,
private StateChartState _onWallHanging;
private StateChartState _onWallRunning;
private StateChartState _attack;
private StateChartState _attackReady;
private StateChartState _attackStandard;
private StateChartState _attackDash;
private Transition _onDashEnded;
private Transition _onJumpFromWall;
@@ -338,9 +346,11 @@ public partial class PlayerController : CharacterBody3D,
public float CurrentHealth { get; set; }
private bool _isInvincible;
private bool _canAttack = true;
private readonly List<IDamageable> _hitEnemies = new List<IDamageable>();
private ShapeCast3D _closeEnemyDetector;
private Camera3D _camera;
public override void _Ready()
{
LoadSettings();
@@ -351,6 +361,9 @@ public partial class PlayerController : CharacterBody3D,
// General use stuff
PlayerUi = GetNode<PlayerUi>("UI");
_closeEnemyDetector = GetNode<ShapeCast3D>("%CloseEnemyDetector");
_closeEnemyDetector.TargetPosition = _closeEnemyDetector.TargetPosition.Normalized() * TargetingDistance;
// DashIndicator = GetNode<TextureRect>("%DashIndicator");
PowerCooldownIndicator = GetNode<ColorRect>("%DashCooldownIndicator");
PowerCooldownIndicator.Visible = false;
@@ -366,7 +379,7 @@ public partial class PlayerController : CharacterBody3D,
// Camera stuff
HeadSystem = GetNode<HeadSystem>("HeadSystem");
Camera3D camera = GetNode<Camera3D>("HeadSystem/CameraSmooth/Camera3D");
_camera = GetNode<Camera3D>("HeadSystem/CameraSmooth/Camera3D");
Node3D cameraSmooth = GetNode<Node3D>("HeadSystem/CameraSmooth");
// Movement stuff
@@ -456,6 +469,12 @@ public partial class PlayerController : CharacterBody3D,
_onLeaveWallFromRun = Transition.Of(GetNode("StateChart/Root/Movement/OnWall/Running/OnLeaveWall"));
_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
_powerCooldownTimer = GetNode<Timer>("PowerCooldown");
_timeScaleAimInAirTimer = GetNode<Timer>("TimeScaleAimInAir");
@@ -479,8 +498,8 @@ public partial class PlayerController : CharacterBody3D,
_gravity = (float)ProjectSettings.GetSetting("physics/3d/default_gravity");
MantleSystem.Init();
StairsSystem.Init(stairsBelowRayCast3D, stairsAheadRayCast3D, cameraSmooth);
DashSystem.Init(HeadSystem, camera);
WeaponSystem.Init(HeadSystem, camera);
DashSystem.Init(HeadSystem, _camera);
WeaponSystem.Init(HeadSystem, _camera);
WallHugSystem.Init();
EmpoweredActionsLeft = MaxNumberOfEmpoweredActions;
@@ -562,7 +581,11 @@ public partial class PlayerController : CharacterBody3D,
_onJumpFromWall.Taken += OnJumpFromWall;
_onJumpFromWallFalling.Taken += OnJumpFromWall;
_onLeaveWallFromRun.Taken += OnLeaveWallFromRun;
_onAirborneToGrounded.Taken += OnAirborneToGrounded;
_onAirborneToGrounded.Taken += OnAirborneToGrounded;
// Attack states
_attackStandard.StateEntered += OnStandardAttackStarted;
_attackDash.StateEntered += OnDashAttackStarted;
}
///////////////////////////
@@ -1697,9 +1720,13 @@ public partial class PlayerController : CharacterBody3D,
}
public void OnAimedDashFinished()
{
var postDashVelocity = _preDashVelocity.Length() > PostDashSpeed ? PostDashSpeed : _preDashVelocity.Length();
if (_customMantle)
{
_playerState.SendEvent("mantle");
return;
}
var postDashVelocity = _preDashVelocity.Length() > PostDashSpeed ? _preDashVelocity.Length() : PostDashSpeed;
Velocity = _dashDirection * postDashVelocity;
if (_customMantle) _playerState.SendEvent("mantle");
}
// Weapon dashing
@@ -1789,11 +1816,20 @@ public partial class PlayerController : CharacterBody3D,
{
if (_currentInputBufferFrames > 0) _currentInputBufferFrames -= 1;
// Manage head and camera movement
LookAround(delta);
// Manage general movement
Velocity += ComputeKnockback();
MoveSlideAndHandleStairs((float) delta);
MantleSystem.ProcessMantle(_grounded.Active);
// Manage gameplay systems
MantleSystem.ProcessMantle(_grounded.Active);
HandleEnemyTargeting();
if (_closeEnemyDetector.IsColliding())
// Manage dash target and tutorial specific stuff
if (WeaponSystem.InHandState.Active && !_aiming.Active && TutorialDone)
{
DashIndicatorMesh.Visible = false;
@@ -1810,6 +1846,52 @@ public partial class PlayerController : CharacterBody3D,
///////////////////////////
// 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 (!_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 = targetDistance < TargetInRangeDistance;
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)
{
if (_isInvincible)
@@ -1826,11 +1908,71 @@ public partial class PlayerController : CharacterBody3D,
return finalDamage;
}
public DamageRecord ComputeDamage(DamageRecord damageRecord)
{
return CDamageable.ComputeDamage(damageRecord);
}
public void OnHitInvincibility()
{
_isInvincible = true;
_invincibilityTimer.Start();
}
public void OnStandardAttackStarted()
{
_attackCooldown.Start();
HeadSystem.OnHit();
_audioStream!.SwitchToClipByName("attacks");
}
public void OnDashAttackStarted()
{
_audioStream!.SwitchToClipByName("attacks");
_isInvincible = true;
var actualDashLocation = _targetLocation + Vector3.Down*HeadSystem.Position.Y;
var travel = actualDashLocation - GlobalPosition;
_preDashVelocity = Velocity;
_dashDirection = travel.Normalized();
var dashTween = CreatePositionTween(actualDashLocation, 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
{
var locationOtherSide = _targetLocation + (_targetLocation - _targetHitLocation);
GlobalPosition = locationOtherSide;
var postDashVelocity = _preDashVelocity.Length() > PostDashSpeed ? _preDashVelocity.Length() : PostDashSpeed;
Velocity = _dashDirection * postDashVelocity;
}
_isInvincible = false;
_playerState.SendEvent("attack_finished");
}
public void OnInputHitPressed()
{
@@ -1839,24 +1981,15 @@ public partial class PlayerController : CharacterBody3D,
ThrowWeapon();
}
if (!WeaponSystem.InHandState.Active) return;
if (!_canAttack) return;
_canAttack = false;
_attackCooldown.Start();
PerformHit();
var attackToDo = _isEnemyInDashAttackRange ? "dash_attack" : "standard_attack";
_playerState.SendEvent(attackToDo);
}
public void ResetAttackCooldown()
{
_canAttack = true;
_playerState.SendEvent("attack_finished");
}
public void PerformHit()
{
HeadSystem.OnHit();
_audioStream!.SwitchToClipByName("attacks");
}
public void OnHitboxActivated()
{
@@ -1900,6 +2033,7 @@ public partial class PlayerController : CharacterBody3D,
}
public void ReduceHealth(IDamageable source, DamageRecord damageRecord)
{
GD.Print("That's NOT fine");
CHealth.ReduceHealth(source, damageRecord);
HealthChanged?.Invoke(this, CHealth.CurrentHealth);
}
@@ -1910,7 +2044,8 @@ public partial class PlayerController : CharacterBody3D,
public Vector3 ComputeKnockback()
{
return CKnockback.ComputeKnockback();
var kb = CKnockback.ComputeKnockback();
return kb;
}
public void Kill(IHealthable source)

View File

@@ -37,6 +37,10 @@ window/size/viewport_height=1080
project/assembly_name="Movement tests"
[editor]
movie_writer/movie_file="D:/Godot/Projects/movement-tests/communication/movie.avi"
[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")
@@ -155,6 +159,7 @@ locale/translations=PackedStringArray("res://addons/maaacks_game_template/base/t
3d_physics/layer_3="3"
3d_physics/layer_4="4"
3d_physics/layer_5="enemies"
3d_physics/layer_6="InteractiveGeo"
3d_physics/layer_9="terrain"
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

@@ -10,7 +10,9 @@ public partial class Enemy : CharacterBody3D,
IKillable,
IMoveable,
ISpawnable,
IKnockbackable
IKnockbackable,
ITargetable,
IStunnable
{
// Signals and events
public event Action<IDamageable, DamageRecord> DamageTaken;
@@ -43,10 +45,15 @@ public partial class Enemy : CharacterBody3D,
public IMoveable CMovement { get; set; }
// Public stuff
public float CurrentHealth { get; set; }
public float CurrentHealth
{
get => CHealth.CurrentHealth;
set => CHealth.CurrentHealth = value;
}
// Private stuff
private Area3D _damageBox;
private Node3D _target;
public override void _Ready()
{
@@ -57,6 +64,7 @@ public partial class Enemy : CharacterBody3D,
public void Initialize()
{
_damageBox = GetNode<Area3D>("DamageBox");
_target = GetNode<Node3D>("CTarget");
CDamageable = GetNode<Node>("CDamageable") as IDamageable;
CMovement = GetNode<Node>("CMovement") as IMoveable;
@@ -105,6 +113,8 @@ public partial class Enemy : CharacterBody3D,
public void ProcessGameplay(double delta)
{
if (IsStunned) return;
var bodies = _damageBox.GetOverlappingBodies();
foreach (var body in bodies)
{
@@ -129,6 +139,14 @@ public partial class Enemy : CharacterBody3D,
return finalDamage;
}
public DamageRecord ComputeDamage(DamageRecord damageRecord)
{
if (CDamageable is null)
return damageRecord with { Damage = new RDamage(0, damageRecord.Damage.DamageType) };
return CDamageable.ComputeDamage(damageRecord);
}
public void ReduceHealth(IDamageable source, DamageRecord damageRecord)
{
if (CHealth is null) return;
@@ -156,4 +174,25 @@ public partial class Enemy : CharacterBody3D,
if (CKnockback is null) return Vector3.Zero;
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

@@ -42,19 +42,15 @@ albedo_color = Color(0.06469653, 0.06469653, 0.06469653, 1)
[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_mask = 273
motion_mode = 1
script = ExtResource("1_q8l7o")
CHealth = NodePath("CHealth")
RHealth = ExtResource("2_ma2bq")
DeathEffects = Array[Object]([])
CDamage = NodePath("CDamageable")
RDamage = ExtResource("2_on7rt")
CKnockback = NodePath("CKnockback")
RKnockback = ExtResource("11_mpa2u")
CMovement = NodePath("CMovement")
RMovement = ExtResource("4_dejyg")
[node name="CHealth" type="Node" parent="."]
@@ -74,6 +70,8 @@ TerrainCollision = 256
[node name="CKnockback" parent="." instance=ExtResource("10_dejyg")]
RKnockback = ExtResource("11_mpa2u")
[node name="CTarget" type="Marker3D" parent="."]
[node name="CollisionShape3D" type="CollisionShape3D" parent="."]
shape = SubResource("SphereShape3D_b46rq")

View File

@@ -14,7 +14,7 @@
[sub_resource type="Resource" id="Resource_qj0ob"]
script = ExtResource("2_r3cnf")
Modifier = 3.0
Modifier = 1.0
metadata/_custom_type_script = "uid://b6y3ugfydvch0"
[sub_resource type="Resource" id="Resource_6d4gl"]
@@ -38,18 +38,14 @@ albedo_color = Color(0.06469653, 0.06469653, 0.06469653, 1)
[sub_resource type="BoxShape3D" id="BoxShape3D_4yfjf"]
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_mask = 273
script = ExtResource("1_r6506")
CHealth = NodePath("CHealth")
RHealth = ExtResource("2_w4lm8")
DeathEffects = Array[Object]([])
CDamage = NodePath("CDamageable")
RDamage = ExtResource("2_bn56u")
CKnockback = NodePath("CKnockback")
RKnockback = ExtResource("11_8k3xb")
CMovement = NodePath("CMovement")
RMovement = ExtResource("4_na24f")
[node name="CHealth" type="Node" parent="."]
@@ -68,6 +64,9 @@ WallInFrontRayCast = NodePath("../WallInFrontRayCast")
[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="."]
transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0)
shape = SubResource("CapsuleShape3D_62kkh")

View File

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

View File

@@ -22,7 +22,7 @@ public partial class DashSystem: Node3D
public Vector3 PlannedMantleLocation { get; set; }
public MantleSystem MantleSystem { get; set; }
private Node3D _head;
private HeadSystem _head;
private ShapeCast3D _dashCast3D;
private Camera3D _camera;
private Vector3 _dashDirection = Vector3.Zero;
@@ -49,7 +49,7 @@ public partial class DashSystem: Node3D
public float DashCastRadius { get; set; }
public void Init(Node3D head, Camera3D camera)
public void Init(HeadSystem head, Camera3D camera)
{
_dashCast3D = GetNode<ShapeCast3D>("DashCast3D");
var dashShape = _dashCast3D.GetShape() as SphereShape3D;
@@ -93,10 +93,7 @@ public partial class DashSystem: Node3D
public void PrepareDash()
{
_dashCast3D.SetRotation(new Vector3(
_camera.Rotation.X,
_head.Rotation.Y,
_camera.Rotation.Z));
_dashCast3D.SetRotation(_head.GetGlobalLookRotation());
(HasHit, PlannedLocation, CollisionPoint, CollisionNormal) = ComputeDashLocation();

View File

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