improved AI

pull/6/head
John Montagu, the 4th Earl of Sandvich 2023-08-16 21:02:37 -07:00
parent b18b011133
commit dc5c06dafa
Signed by: sandvich
GPG Key ID: 9A39BE37E602B22D
12 changed files with 245 additions and 105 deletions

Binary file not shown.

View File

@ -0,0 +1,27 @@
[gd_resource type="ParticleProcessMaterial" load_steps=3 format=3 uid="uid://b64pfv5ocwegv"]
[sub_resource type="Gradient" id="Gradient_p5otp"]
offsets = PackedFloat32Array(0, 0.2, 0.5, 0.6, 0.7, 0.8, 1)
colors = PackedColorArray(0.105882, 0.0470588, 0.117647, 0, 0.105882, 0.0470588, 0.117647, 1, 0.105882, 0.0470588, 0.117647, 1, 0.0509804, 0.258824, 0.109804, 1, 0.396078, 0.658824, 0.309804, 1, 0.745098, 0.85098, 0.513726, 1, 0.745098, 0.85098, 0.513726, 0)
[sub_resource type="GradientTexture1D" id="GradientTexture1D_4a2pn"]
gradient = SubResource("Gradient_p5otp")
[resource]
lifetime_randomness = 0.5
emission_shape = 6
emission_ring_axis = Vector3(0, 0, 1)
emission_ring_height = 1.0
emission_ring_radius = 128.0
emission_ring_inner_radius = 64.0
particle_flag_disable_z = true
direction = Vector3(2, 1, 0)
gravity = Vector3(32, 32, 0)
initial_velocity_min = 32.0
initial_velocity_max = 64.0
angular_velocity_max = 30.0
orbit_velocity_min = 0.0
orbit_velocity_max = 0.0
color_ramp = SubResource("GradientTexture1D_4a2pn")
turbulence_enabled = true
turbulence_noise_speed = Vector3(1, 0, 0)

View File

@ -19,6 +19,15 @@ public partial class Doc : Boss
get => base.IsActive;
set
{
// if player not alive or doesn't exist then don't activate
if (!this.GetWorld()?.CurrentPlayer?.IsAlive ?? true)
{
if (value)
{
return;
}
}
base.IsActive = value;
var introState = BossStateMachine
.GetNode<State.NPC.Doc.DocIntroState>("Intro");
@ -101,10 +110,12 @@ public partial class Doc : Boss
};
CanAttack = false;
// when we are hurt, start the boss fight
Hurt += (Events.HurtArgs args) =>
{
if (!IsActive)
if (this.GetWorld().CurrentPlayer.IsAlive && !IsActive)
{
IsActive = true;
Inventory.SelectedItem = Lance;

View File

@ -1,4 +1,4 @@
[gd_scene load_steps=69 format=3 uid="uid://d2skjvvx6fal0"]
[gd_scene load_steps=68 format=3 uid="uid://d2skjvvx6fal0"]
[ext_resource type="Script" path="res://Characters/Doc.cs" id="2_3elet"]
[ext_resource type="Shader" path="res://Shaders/Flash.gdshader" id="2_5jxom"]
@ -34,7 +34,6 @@
[ext_resource type="Script" path="res://State/Thinker/AttackState.cs" id="21_ij3bp"]
[ext_resource type="Animation" uid="uid://8e8r3y1imvsx" path="res://Assets/Animations/stun.res" id="21_ixn4k"]
[ext_resource type="PackedScene" uid="uid://p7oijq6dbvvk" path="res://Items/Weapons/DocLance.tscn" id="24_2es2r"]
[ext_resource type="PackedScene" uid="uid://bauucuqvjwbxp" path="res://Items/Weapons/DocLanceHold.tscn" id="26_0tntj"]
[ext_resource type="AudioStream" uid="uid://cqj44je3mvk60" path="res://Assets/Sounds/rauuul.wav" id="26_js7p2"]
[ext_resource type="PackedScene" uid="uid://dldnp8eunxj3q" path="res://BoundingBoxes/InteractionTrigger.tscn" id="33_08dyq"]
@ -555,7 +554,7 @@ size = Vector2(16, 19)
[sub_resource type="CircleShape2D" id="CircleShape2D_8hwat"]
radius = 16.0
[node name="Doc" type="CharacterBody2D" node_paths=PackedStringArray("Lance", "BossStateMachine", "ThinkerStateMachine", "Sprite", "Inventory", "StateMachine", "Hurtbox")]
[node name="Doc" type="CharacterBody2D" node_paths=PackedStringArray("Lance", "BossStateMachine", "DefaultSelectedItem", "ThinkerStateMachine", "Sprite", "Inventory", "StateMachine", "Hurtbox")]
y_sort_enabled = true
texture_filter = 3
material = SubResource("ShaderMaterial_7n7iy")
@ -566,6 +565,7 @@ Lance = NodePath("Inventory/DocLance")
BossStateMachine = NodePath("BossStateMachine")
BossName = "Doc, The Two-Time"
Music = ExtResource("3_eo4lg")
DefaultSelectedItem = NodePath("Inventory/DocLance")
ThinkerStateMachine = NodePath("ThinkerStateMachine")
HandTexture = ExtResource("4_8lqj6")
Health = 900.0
@ -669,12 +669,16 @@ NPC = NodePath("../..")
script = ExtResource("20_dy57x")
InitialState = NodePath("Attack")
[node name="Idle" type="Node" parent="ThinkerStateMachine"]
[node name="Attack" type="Node" parent="ThinkerStateMachine" node_paths=PackedStringArray("NPC")]
script = ExtResource("21_ij3bp")
NPC = NodePath("../..")
[node name="DashDefensive" type="Node" parent="ThinkerStateMachine" node_paths=PackedStringArray("NPC")]
script = ExtResource("20_12htp")
MaxDistanceToTarget = 256.0
UseItemDistance = 64.0
NPC = NodePath("../..")
[node name="Animations" type="Node" parent="."]
@ -750,7 +754,6 @@ attenuation = 0.5
stream = ExtResource("9_stm0e")
[node name="Sprite" type="Sprite2D" parent="."]
modulate = Color(1, 1, 1, 0.5)
y_sort_enabled = true
use_parent_material = true
position = Vector2(-0.5, 0)
@ -777,8 +780,6 @@ script = ExtResource("8_r8ejq")
[node name="DocLance" parent="Inventory" instance=ExtResource("24_2es2r")]
unique_name_in_owner = true
[node name="DocLanceHold" parent="Inventory" instance=ExtResource("26_0tntj")]
[node name="InteractionTrigger" parent="." instance=ExtResource("33_08dyq")]
[node name="CollisionShape2D" parent="InteractionTrigger" index="0"]

View File

@ -6,7 +6,7 @@
[ext_resource type="Script" path="res://State/Character/NPCIdleState.cs" id="4_47f2d"]
[ext_resource type="Script" path="res://State/Character/NPCMoveState.cs" id="5_iphm1"]
[ext_resource type="Script" path="res://State/Thinker/ThinkerStateMachine.cs" id="6_6516i"]
[ext_resource type="Script" path="res://State/Thinker/BlockAttackState.cs" id="7_vgjig"]
[ext_resource type="Script" path="res://State/Thinker/CenturionAttackState.cs" id="7_n2slg"]
[ext_resource type="Script" path="res://State/Thinker/IdleState.cs" id="8_5neew"]
[ext_resource type="Script" path="res://Utils/AnimationManager.cs" id="9_fgnr2"]
[ext_resource type="Animation" uid="uid://8e8r3y1imvsx" path="res://Assets/Animations/stun.res" id="10_1erll"]
@ -308,8 +308,11 @@ Character = NodePath("../..")
script = ExtResource("6_6516i")
InitialState = NodePath("Idle")
[node name="Attack" type="Node" parent="ThinkerStateMachine" node_paths=PackedStringArray("PassiveState", "PursueState", "NPC")]
script = ExtResource("7_vgjig")
[node name="Attack" type="Node" parent="ThinkerStateMachine" node_paths=PackedStringArray("NavigationAgent", "PassiveState", "PursueState", "NPC")]
script = ExtResource("7_n2slg")
FollowTeammate = true
NavigationAgent = NodePath("../../NavigationAgent2D")
PathfindingDistance = 4.0
PreferredWeightDistance = 24.0
MaxDistanceToTarget = 128.0
UseItemDistance = 48.0

View File

@ -1,4 +1,4 @@
[gd_scene load_steps=41 format=3 uid="uid://cdj50hb84aujp"]
[gd_scene load_steps=43 format=3 uid="uid://cdj50hb84aujp"]
[ext_resource type="Shader" path="res://Shaders/Flash.gdshader" id="1_hvgeb"]
[ext_resource type="Script" path="res://Characters/Enemy.cs" id="2_h5w5n"]
@ -6,9 +6,11 @@
[ext_resource type="Script" path="res://State/Character/NPCIdleState.cs" id="4_1ngkf"]
[ext_resource type="Script" path="res://State/Character/NPCMoveState.cs" id="5_wcpa1"]
[ext_resource type="Script" path="res://State/Thinker/ThinkerStateMachine.cs" id="6_121gp"]
[ext_resource type="Script" path="res://State/Thinker/AttackState.cs" id="8_8a6v2"]
[ext_resource type="Script" path="res://State/Character/CharacterDashState.cs" id="6_pwguk"]
[ext_resource type="Script" path="res://State/Thinker/DashDefensive.cs" id="8_n8t7f"]
[ext_resource type="Script" path="res://State/Thinker/IdleState.cs" id="9_ewnq3"]
[ext_resource type="Script" path="res://Utils/AnimationManager.cs" id="9_ssmee"]
[ext_resource type="Script" path="res://State/Thinker/PursueState.cs" id="9_u7gxx"]
[ext_resource type="Animation" uid="uid://8e8r3y1imvsx" path="res://Assets/Animations/stun.res" id="10_oplmj"]
[ext_resource type="Material" uid="uid://bat28samf7ukd" path="res://Assets/Sprites/Particles/NPCDamageProcessMaterial.tres" id="11_qcw5x"]
[ext_resource type="Texture2D" uid="uid://bd8l8kafb42dt" path="res://Assets/Sprites/Particles/circle.png" id="12_rlelw"]
@ -142,18 +144,6 @@ tracks/3/keys = {
"update": 1,
"values": [false]
}
tracks/4/type = "value"
tracks/4/imported = false
tracks/4/enabled = true
tracks/4/path = NodePath("Sprites/Node2D/Character:frame")
tracks/4/interp = 1
tracks/4/loop_wrap = true
tracks/4/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [8]
}
[sub_resource type="Animation" id="Animation_lhc4c"]
resource_name = "death"
@ -225,18 +215,6 @@ tracks/2/keys = {
"method": &"restart"
}]
}
tracks/3/type = "value"
tracks/3/imported = false
tracks/3/enabled = false
tracks/3/path = NodePath("Sprites/Node2D/Character:frame")
tracks/3/interp = 1
tracks/3/loop_wrap = true
tracks/3/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [8]
}
[sub_resource type="AnimationLibrary" id="AnimationLibrary_rc55s"]
_data = {
@ -306,11 +284,12 @@ script = ExtResource("2_h5w5n")
DefaultSelectedItem = NodePath("Inventory/DocLance")
ThinkerStateMachine = NodePath("ThinkerStateMachine")
Speed = 56.0
Health = 170.0
Health = 130.0
Sprite = NodePath("Sprites/Node2D/Character")
Inventory = NodePath("Inventory")
StateMachine = NodePath("StateMachine")
Hurtbox = NodePath("Hurtbox")
metadata/_edit_vertical_guides_ = []
[node name="StateMachine" type="Node" parent="." node_paths=PackedStringArray("InitialState", "Character")]
script = ExtResource("3_04p3j")
@ -327,21 +306,28 @@ script = ExtResource("5_wcpa1")
IdleState = NodePath("../Idle")
Character = NodePath("../..")
[node name="Dash" type="Node" parent="StateMachine" node_paths=PackedStringArray("IdleState", "Character")]
script = ExtResource("6_pwguk")
IdleState = NodePath("../Idle")
TimeToDash = 0.1
VelocityModifier = 4.0
Character = NodePath("../..")
[node name="ThinkerStateMachine" type="Node" parent="." node_paths=PackedStringArray("InitialState")]
script = ExtResource("6_121gp")
InitialState = NodePath("Idle")
[node name="Attack" type="Node" parent="ThinkerStateMachine" node_paths=PackedStringArray("PassiveState", "PursueState", "NPC")]
script = ExtResource("8_8a6v2")
script = ExtResource("8_n8t7f")
MaxDistanceToTarget = 128.0
UseItemDistance = 64.0
PassiveState = NodePath("../Idle")
PursueState = NodePath("../Idle")
PursueState = NodePath("../Pursue")
NPC = NodePath("../..")
[node name="Idle" type="Node" parent="ThinkerStateMachine" node_paths=PackedStringArray("PursueState", "NavigationAgent", "NPC")]
script = ExtResource("9_ewnq3")
PursueState = NodePath("../Attack")
PursueState = NodePath("../Pursue")
MinTargetDistance = 24.0
PursueOnLineOfSight = true
MinLineOfSightDistance = 256.0
@ -349,9 +335,17 @@ ShouldReturnToOriginalPosition = true
NavigationAgent = NodePath("../../NavigationAgent2D")
NPC = NodePath("../..")
[node name="Pursue" type="Node" parent="ThinkerStateMachine" node_paths=PackedStringArray("NavigationAgent", "AttackState", "PassiveState", "NPC")]
script = ExtResource("9_u7gxx")
NavigationAgent = NodePath("../../NavigationAgent2D")
AttackState = NodePath("../Attack")
PassiveState = NodePath("../Idle")
MinDistanceToTarget = 72.0
MaxDistanceFromOrigin = 128.0
NPC = NodePath("../..")
[node name="NavigationAgent2D" type="NavigationAgent2D" parent="."]
target_desired_distance = 16.0
debug_enabled = true
[node name="Animations" type="Node" parent="."]
script = ExtResource("9_ssmee")

View File

@ -48,6 +48,15 @@ public sealed partial class Player : Character
};
}
public override void _Process(double delta)
{
base._Process(delta);
var mod = Sprite.SelfModulate;
mod.A = 1 - (Stealth / 2);
Sprite.SelfModulate = mod;
}
public override void _Input(InputEvent @event)
{
if (StateMachine != null)

View File

@ -38,4 +38,9 @@ public static class Node2DExtensions
node.GlobalPosition = position;
return node;
}
public static float DistanceTo(this Node2D node, Node2D other)
{
return node.GlobalPosition.DistanceTo(other.GlobalPosition);
}
}

File diff suppressed because one or more lines are too long

View File

@ -23,7 +23,7 @@ public partial class CharacterDashState : CharacterState
_timeLeftToDash = TimeToDash;
// dash the direction we were previously moving in
DashDirection = Character.Direction;
Character.MovementAnimation.Play("dash");
Character.MovementAnimation.TryPlay("dash");
Character.MovementAnimation.Queue("idle");
// create ghost effect

View File

@ -0,0 +1,93 @@
using Godot;
using SupaLidlGame.Extensions;
namespace SupaLidlGame.State.Thinker;
public partial class CenturionAttackState : BlockAttackState
{
[Export]
public bool FollowTeammate { get; set; } = false;
[Export]
public NavigationAgent2D NavigationAgent { get; set; }
[Export]
public float PathfindingDistance { get; set; } = 64;
protected Characters.Character _bestTeammate = null;
public Characters.Character FindBestTeammate()
{
float bestScore = float.MaxValue;
Characters.Character bestChar = null;
var world = this.GetWorld();
foreach (Node node in world.CurrentMap.Entities.GetChildren())
{
if (node != NPC && node is Characters.Character character)
{
if (!IsInstanceValid(character) || !character.IsAlive)
{
continue;
}
if (character.Faction == NPC.Faction)
{
float score = NPC.Position.DistanceTo(character.Position);
if (score < bestScore)
{
bestScore = score;
bestChar = character;
}
}
}
}
return _bestTeammate = bestChar;
}
public override ThinkerState Think()
{
base.Think();
var teammate = FindBestTeammate();
if (teammate is not null)
{
UpdateWeights(teammate.GlobalPosition);
NavigationAgent.TargetPosition = teammate.GlobalPosition;
}
return null;
}
public override ThinkerState Process(double delta)
{
if (_bestTeammate is null)
{
return null;
}
float dist = NPC.DistanceTo(_bestTeammate);
if (NPC.DistanceTo(_bestTeammate) < PathfindingDistance)
{
// move to weighted position
return base.Process(delta);
}
return null;
}
public override ThinkerState PhysicsProcess(double delta)
{
if (_bestTeammate is not null)
{
float dist = NPC.DistanceTo(_bestTeammate);
if (dist >= PathfindingDistance)
{
var nextPos = NavigationAgent.GetNextPathPosition();
NPC.Direction = NPC.GlobalPosition.DirectionTo(nextPos);
}
}
return base.PhysicsProcess(delta);
}
}

View File

@ -25,9 +25,22 @@ public partial class DashDefensive : AttackState
Vector2 pos = bestTarget.GlobalPosition;
NPC.Target = pos - NPC.GlobalPosition;
Vector2 dir = NPC.GlobalPosition.DirectionTo(pos);
float dist = NPC.GlobalPosition.DistanceSquaredTo(pos);
float dist = NPC.GlobalPosition.DistanceTo(pos);
UpdateWeights(pos);
if (dist > MaxDistanceToTarget)
{
if (PursueState is not null)
{
return PursueState;
}
if (PassiveState is not null)
{
return PassiveState;
}
}
if (NPC.CanAttack && NPC.StunTime <= 0)
{
bool isTargetStunned = bestTarget.StunTime > 0;
@ -48,17 +61,16 @@ public partial class DashDefensive : AttackState
// doc will still dash if you are farther than normal but
// moving towards him
float distThreshold = 2500 - (dot * 400);
// or just directly dash towards you if you are too far
float distTowardsThreshold = 22500;
float distThreshold = 50 - (dot * 20);
// dash towards if lance in anticipate state
// or just directly dash towards you if you are too far
shouldDashTowards = (isTargetStunned || _dashedAway) &&
swordState is State.Weapon.SwordAnticipateState ||
dist > distTowardsThreshold;
dist > MaxDistanceToTarget;
shouldDashAway = dist < distThreshold && !isTargetStunned;
shouldDashAway = dist < distThreshold && !isTargetStunned &&
swordState is not State.Weapon.SwordAnticipateState;
//if (!isTargetStunned && dist < 2500 && !_dashedAway)
if (shouldDashAway && !shouldDashTowards)
@ -83,10 +95,14 @@ public partial class DashDefensive : AttackState
DashTo(NPC.GlobalPosition.DirectionTo(newPos));
_dashedAway = false;
}
else if (isTargetStunned)
{
NPC.UseCurrentItem();
}
}
}
return null;
return PursueState ?? PassiveState;
}
private void DashTo(Vector2 direction)