rework spike attack; added unwanted frequency

pull/4/head
HumanoidSandvichDispenser 2023-07-19 00:17:25 -07:00
parent 9dd4525fcd
commit 2979f69405
Signed by: sandvich
GPG Key ID: 9A39BE37E602B22D
23 changed files with 594 additions and 72 deletions

Binary file not shown.

View File

@ -0,0 +1,19 @@
[remap]
importer="mp3"
type="AudioStreamMP3"
uid="uid://cn2wop7rfxku8"
path="res://.godot/imported/karabast.mp3-5fc63c29a959da213d2ed9b8b4c20280.mp3str"
[deps]
source_file="res://Assets/Sounds/karabast.mp3"
dest_files=["res://.godot/imported/karabast.mp3-5fc63c29a959da213d2ed9b8b4c20280.mp3str"]
[params]
loop=false
loop_offset=0
bpm=0
beat_count=0
bar_beats=4

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 458 B

After

Width:  |  Height:  |  Size: 818 B

View File

@ -80,7 +80,6 @@ public partial class Hitbox : BoundingBox
{ {
if (area is BoundingBox box) if (area is BoundingBox box)
{ {
GD.Print("hit");
// we don't want to hurt teammates // we don't want to hurt teammates
if (Faction != box.Faction) if (Faction != box.Faction)
{ {

View File

@ -1,4 +1,4 @@
[gd_scene load_steps=36 format=3 uid="uid://d2skjvvx6fal0"] [gd_scene load_steps=38 format=3 uid="uid://d2skjvvx6fal0"]
[ext_resource type="Script" path="res://Characters/Doc.cs" id="2_3elet"] [ext_resource type="Script" path="res://Characters/Doc.cs" id="2_3elet"]
[ext_resource type="Shader" path="res://Shaders/Flash.gdshader" id="2_5jxom"] [ext_resource type="Shader" path="res://Shaders/Flash.gdshader" id="2_5jxom"]
@ -17,6 +17,8 @@
[ext_resource type="AudioStream" uid="uid://k6kpdj1kv0jg" path="res://Assets/Sounds/splat.ogg" id="9_stm0e"] [ext_resource type="AudioStream" uid="uid://k6kpdj1kv0jg" path="res://Assets/Sounds/splat.ogg" id="9_stm0e"]
[ext_resource type="Script" path="res://State/NPC/Doc/DocShungiteSpikeState.cs" id="10_bgs6o"] [ext_resource type="Script" path="res://State/NPC/Doc/DocShungiteSpikeState.cs" id="10_bgs6o"]
[ext_resource type="Script" path="res://State/NPC/Doc/DocChooseAttackState.cs" id="12_45x13"] [ext_resource type="Script" path="res://State/NPC/Doc/DocChooseAttackState.cs" id="12_45x13"]
[ext_resource type="Script" path="res://State/NPC/Doc/DocUnwantedFrequencyState.cs" id="12_d51jv"]
[ext_resource type="PackedScene" uid="uid://1y5r6sklwgrp" path="res://Entities/UnwantedFrequency.tscn" id="13_lpj21"]
[sub_resource type="ShaderMaterial" id="ShaderMaterial_7n7iy"] [sub_resource type="ShaderMaterial" id="ShaderMaterial_7n7iy"]
resource_local_to_scene = true resource_local_to_scene = true
@ -257,10 +259,20 @@ ChooseAttackState = NodePath("../ChooseAttack")
Doc = NodePath("../..") Doc = NodePath("../..")
NPC = NodePath("../..") NPC = NodePath("../..")
[node name="ChooseAttack" type="Node" parent="BossStateMachine" node_paths=PackedStringArray("DartState", "SpikeState", "ExitState", "NPC")] [node name="UnwantedFrequency" type="Node" parent="BossStateMachine" node_paths=PackedStringArray("ChooseAttackState", "Doc", "NPC")]
script = ExtResource("12_d51jv")
Duration = 4.0
AttackDuration = 1.0
Projectile = ExtResource("13_lpj21")
ChooseAttackState = NodePath("../ChooseAttack")
Doc = NodePath("../..")
NPC = NodePath("../..")
[node name="ChooseAttack" type="Node" parent="BossStateMachine" node_paths=PackedStringArray("DartState", "SpikeState", "UnwantedFrequencyState", "ExitState", "NPC")]
script = ExtResource("12_45x13") script = ExtResource("12_45x13")
DartState = NodePath("../Dart") DartState = NodePath("../Dart")
SpikeState = NodePath("../Spike") SpikeState = NodePath("../Spike")
UnwantedFrequencyState = NodePath("../UnwantedFrequency")
ExitState = NodePath("../Exit") ExitState = NodePath("../Exit")
NPC = NodePath("../..") NPC = NodePath("../..")

View File

@ -84,6 +84,25 @@ public sealed partial class Player : Character
// TODO: implement visual effects for stun // TODO: implement visual effects for stun
} }
public override void OnReceivedDamage(
float damage,
Character inflictor,
float knockback,
Vector2 knockbackOrigin = default,
Vector2 knockbackVector = default)
{
if (damage >= 10 && IsAlive)
{
Camera.Shake(2, 0.5f);
}
base.OnReceivedDamage(
damage,
inflictor,
knockback,
knockbackOrigin,
knockbackVector);
}
public override void Die() public override void Die()
{ {
GD.Print("died"); GD.Print("died");

View File

@ -8,6 +8,9 @@ public partial class Projectile : RigidBody2D
{ {
public Vector2 Velocity => Direction * Speed; public Vector2 Velocity => Direction * Speed;
[Export]
public string ProjectileName { get; set; }
[Export] [Export]
public float Speed { get; set; } public float Speed { get; set; }
@ -25,6 +28,8 @@ public partial class Projectile : RigidBody2D
public Character Character { get; set; } public Character Character { get; set; }
public bool IsDead { get; set; }
public override void _Ready() public override void _Ready()
{ {
Hitbox.Hit += OnHit; Hitbox.Hit += OnHit;
@ -35,15 +40,26 @@ public partial class Projectile : RigidBody2D
if (Delay > 0) if (Delay > 0)
{ {
Delay -= delta; Delay -= delta;
if (Delay <= 0)
{
OnDelayEnd();
}
} }
if ((Lifetime -= delta) <= 0) if ((Lifetime -= delta) <= 0)
{ {
QueueFree(); if (!IsDead)
{
Die();
}
} }
} }
public override void _PhysicsProcess(double delta) public override void _PhysicsProcess(double delta)
{ {
if (IsDead)
{
return;
}
Vector2 velocity = Delay <= 0 ? Velocity : Vector2.Zero; Vector2 velocity = Delay <= 0 ? Velocity : Vector2.Zero;
MoveAndCollide(velocity * (float)delta); MoveAndCollide(velocity * (float)delta);
} }
@ -60,4 +76,14 @@ public partial class Projectile : RigidBody2D
); );
} }
} }
public virtual void Die()
{
QueueFree();
}
public virtual void OnDelayEnd()
{
}
} }

View File

@ -277,8 +277,10 @@ _data = {
[node name="ShungiteDart" type="RigidBody2D" node_paths=PackedStringArray("Hitbox")] [node name="ShungiteDart" type="RigidBody2D" node_paths=PackedStringArray("Hitbox")]
script = ExtResource("1_jbgb8") script = ExtResource("1_jbgb8")
ProjectileName = "Shungite Dart"
Speed = 256.0 Speed = 256.0
Hitbox = NodePath("Hitbox") Hitbox = NodePath("Hitbox")
Lifetime = 5.0
Delay = 1.0 Delay = 1.0
[node name="Sprite2D" type="AnimatedSprite2D" parent="."] [node name="Sprite2D" type="AnimatedSprite2D" parent="."]
@ -290,7 +292,7 @@ speed_scale = 4.0
[node name="Hitbox" parent="." instance=ExtResource("3_gdyk8")] [node name="Hitbox" parent="." instance=ExtResource("3_gdyk8")]
Damage = 25.0 Damage = 25.0
Knockback = 128.0 Knockback = 64.0
[node name="CollisionShape2D" parent="Hitbox" index="0"] [node name="CollisionShape2D" parent="Hitbox" index="0"]
shape = SubResource("RectangleShape2D_fa7yf") shape = SubResource("RectangleShape2D_fa7yf")

View File

@ -9,9 +9,6 @@ public partial class ShungiteSpike : Projectile
[Export] [Export]
public PackedScene Dart { get; set; } public PackedScene Dart { get; set; }
[Export]
public double ExplodeTime { get; set; } = 6;
[Export] [Export]
public BoundingBoxes.Hurtbox Hurtbox { get; set; } public BoundingBoxes.Hurtbox Hurtbox { get; set; }
@ -21,18 +18,44 @@ public partial class ShungiteSpike : Projectile
[Export] [Export]
public AnimationPlayer AnimationPlayer { get; set; } public AnimationPlayer AnimationPlayer { get; set; }
public Vector2 Target { get; set; }
public double ExplodeTime { get; set; } = 2;
public double FreezeTime
{
get => _freezeTime;
set
{
_currentFreezeTime = _freezeTime = value;
}
}
private double _currentExplodeTime; private double _currentExplodeTime;
private double _freezeTime;
private double _currentFreezeTime;
private bool _hasFrozen = false;
private bool _hasExploded = false;
private Scenes.Map _map; private Scenes.Map _map;
public override void _Ready() public override void _Ready()
{ {
_currentFreezeTime = FreezeTime;
_currentExplodeTime = ExplodeTime; _currentExplodeTime = ExplodeTime;
_map = this.GetAncestor<Scenes.Map>(); _map = this.GetAncestor<Scenes.Map>();
Hurtbox.ReceivedDamage += OnReceivedDamage; Hurtbox.ReceivedDamage += OnReceivedDamage;
AnimationPlayer.Play("spin"); AnimationPlayer.Play("spin");
AnimationPlayer.AnimationFinished += (StringName anim) =>
{
if (anim == "explode")
{
QueueFree();
}
};
base._Ready(); base._Ready();
} }
@ -43,8 +66,21 @@ public partial class ShungiteSpike : Projectile
Vector2 knockbackOrigin = default, Vector2 knockbackOrigin = default,
Vector2 knockbackVector = default) Vector2 knockbackVector = default)
{ {
// if we were hit by the player before the spike freezes,
// spawn a dart towards where the player is aiming
if (inflictor is Characters.Player player)
{
var mousePos = GetGlobalMousePosition();
var dart = CreateDart(GlobalPosition.DirectionTo(mousePos));
dart.Hitbox.Faction = player.Faction;
Hitbox.IsDisabled = true;
}
HitSound.At(GlobalPosition).PlayOneShot(); HitSound.At(GlobalPosition).PlayOneShot();
QueueFree(); _hasExploded = true;
_hasFrozen = true;
AnimationPlayer.Stop();
AnimationPlayer.Play("explode");
} }
@ -60,13 +96,27 @@ public partial class ShungiteSpike : Projectile
public override void _Process(double delta) public override void _Process(double delta)
{ {
if ((_currentFreezeTime -= delta) <= 0)
{
if (!_hasFrozen)
{
Speed = 0;
_hasFrozen = true;
AnimationPlayer.Stop();
}
if ((_currentExplodeTime -= delta) <= 0) if ((_currentExplodeTime -= delta) <= 0)
{
if (!_hasExploded)
{ {
CreateDart(Vector2.Up); CreateDart(Vector2.Up);
CreateDart(Vector2.Down); CreateDart(Vector2.Down);
CreateDart(Vector2.Left); CreateDart(Vector2.Left);
CreateDart(Vector2.Right); CreateDart(Vector2.Right);
QueueFree(); AnimationPlayer.Play("explode");
_hasExploded = true;
}
}
} }
} }
} }

View File

@ -1,4 +1,4 @@
[gd_scene load_steps=12 format=3 uid="uid://1tiswf3gtyvv"] [gd_scene load_steps=13 format=3 uid="uid://1tiswf3gtyvv"]
[ext_resource type="Script" path="res://Entities/ShungiteSpike.cs" id="1_pclpe"] [ext_resource type="Script" path="res://Entities/ShungiteSpike.cs" id="1_pclpe"]
[ext_resource type="PackedScene" uid="uid://djaljmco3xo4g" path="res://Entities/ShungiteDart.tscn" id="2_hinxt"] [ext_resource type="PackedScene" uid="uid://djaljmco3xo4g" path="res://Entities/ShungiteDart.tscn" id="2_hinxt"]
@ -7,10 +7,90 @@
[ext_resource type="PackedScene" uid="uid://cjgxyhgcyvsv7" path="res://BoundingBoxes/Hurtbox.tscn" id="4_d8dl4"] [ext_resource type="PackedScene" uid="uid://cjgxyhgcyvsv7" path="res://BoundingBoxes/Hurtbox.tscn" id="4_d8dl4"]
[ext_resource type="AudioStream" uid="uid://c4n7ioxpukdwi" path="res://Assets/Sounds/parry.wav" id="6_fepye"] [ext_resource type="AudioStream" uid="uid://c4n7ioxpukdwi" path="res://Assets/Sounds/parry.wav" id="6_fepye"]
[sub_resource type="RectangleShape2D" id="RectangleShape2D_konb7"] [sub_resource type="CircleShape2D" id="CircleShape2D_hrev2"]
radius = 8.0
[sub_resource type="CircleShape2D" id="CircleShape2D_kumrg"] [sub_resource type="CircleShape2D" id="CircleShape2D_kumrg"]
radius = 20.0 radius = 12.0
[sub_resource type="Animation" id="Animation_0vfvo"]
length = 0.001
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("Sprite2D:rotation")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [0.0]
}
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath("Sprite2D:frame")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 1,
"values": [0]
}
tracks/2/type = "value"
tracks/2/imported = false
tracks/2/enabled = true
tracks/2/path = NodePath("Sprite2D:self_modulate")
tracks/2/interp = 1
tracks/2/loop_wrap = true
tracks/2/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [Color(1, 1, 1, 1)]
}
[sub_resource type="Animation" id="Animation_0a85j"]
resource_name = "explode"
length = 0.5
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("Sprite2D:frame")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0, 0.1, 0.2),
"transitions": PackedFloat32Array(1, 1, 1),
"update": 1,
"values": [0, 2, 4]
}
tracks/1/type = "value"
tracks/1/imported = false
tracks/1/enabled = true
tracks/1/path = NodePath("Sprite2D:rotation")
tracks/1/interp = 1
tracks/1/loop_wrap = true
tracks/1/keys = {
"times": PackedFloat32Array(0, 0.4),
"transitions": PackedFloat32Array(1, 1),
"update": 0,
"values": [0.0, 6.28319]
}
tracks/2/type = "value"
tracks/2/imported = false
tracks/2/enabled = true
tracks/2/path = NodePath("Sprite2D:self_modulate")
tracks/2/interp = 1
tracks/2/loop_wrap = true
tracks/2/keys = {
"times": PackedFloat32Array(0, 0.5),
"transitions": PackedFloat32Array(1, 1),
"update": 0,
"values": [Color(1, 1, 1, 1), Color(1, 1, 1, 0)]
}
[sub_resource type="Animation" id="Animation_dlpaa"] [sub_resource type="Animation" id="Animation_dlpaa"]
resource_name = "spin" resource_name = "spin"
@ -29,46 +109,34 @@ tracks/0/keys = {
"values": [0.0, 6.28319] "values": [0.0, 6.28319]
} }
[sub_resource type="Animation" id="Animation_0vfvo"]
length = 0.001
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("Sprite2D:rotation")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [0.0]
}
[sub_resource type="AnimationLibrary" id="AnimationLibrary_jj1qe"] [sub_resource type="AnimationLibrary" id="AnimationLibrary_jj1qe"]
_data = { _data = {
"RESET": SubResource("Animation_0vfvo"), "RESET": SubResource("Animation_0vfvo"),
"explode": SubResource("Animation_0a85j"),
"spin": SubResource("Animation_dlpaa") "spin": SubResource("Animation_dlpaa")
} }
[node name="ShungiteSpike" type="RigidBody2D" node_paths=PackedStringArray("Hurtbox", "HitSound", "Hitbox")] [node name="ShungiteSpike" type="RigidBody2D" node_paths=PackedStringArray("Hurtbox", "HitSound", "AnimationPlayer", "Hitbox")]
script = ExtResource("1_pclpe") script = ExtResource("1_pclpe")
Dart = ExtResource("2_hinxt") Dart = ExtResource("2_hinxt")
Hurtbox = NodePath("Hurtbox") Hurtbox = NodePath("Hurtbox")
HitSound = NodePath("AudioStreamPlayer2D") HitSound = NodePath("AudioStreamPlayer2D")
AnimationPlayer = NodePath("AnimationPlayer")
ProjectileName = "Shungite Spike"
Speed = 96.0
Hitbox = NodePath("Hitbox") Hitbox = NodePath("Hitbox")
[node name="Sprite2D" type="Sprite2D" parent="."] [node name="Sprite2D" type="Sprite2D" parent="."]
modulate = Color(1.4, 0, 1.2, 1)
self_modulate = Color(2, 2, 2, 1)
texture_filter = 1 texture_filter = 1
texture = ExtResource("2_klp8v") texture = ExtResource("2_klp8v")
hframes = 5
[node name="Hitbox" parent="." instance=ExtResource("3_kojrj")] [node name="Hitbox" parent="." instance=ExtResource("3_kojrj")]
Damage = 10.0 Damage = 14.0
Knockback = 256.0 Knockback = 200.0
[node name="CollisionShape2D" parent="Hitbox" index="0"] [node name="CollisionShape2D" parent="Hitbox" index="0"]
shape = SubResource("RectangleShape2D_konb7") shape = SubResource("CircleShape2D_hrev2")
[node name="Hurtbox" parent="." instance=ExtResource("4_d8dl4")] [node name="Hurtbox" parent="." instance=ExtResource("4_d8dl4")]

View File

@ -0,0 +1,75 @@
using Godot;
using SupaLidlGame.Extensions;
namespace SupaLidlGame.Entities;
public partial class UnwantedFrequency : Projectile
{
[Export]
public Characters.Character Homing { get; set; }
[Export]
public float HomingVelocity { get; set; } = 1;
public Utils.Trail Trail { get; set; }
public Node2D TrailRotation { get; set; }
public Node2D TrailPosition { get; set; }
public GpuParticles2D DeathParticles { get; set; }
public Timer DeferDeathTimer { get; set; }
private double _currentLifetime = 0;
public override void _Ready()
{
TrailRotation = GetNode<Node2D>("TrailRotation");
TrailPosition = TrailRotation.GetNode<Node2D>("TrailPosition");
Trail = TrailPosition.GetNode<Utils.Trail>("Trail");
DeferDeathTimer = GetNode<Timer>("DeferDeath");
DeathParticles = GetNode<GpuParticles2D>("DeathParticles");
Hitbox.Hit += (BoundingBoxes.BoundingBox box) =>
{
if (box is BoundingBoxes.Hurtbox && box.Faction != Hitbox.Faction)
{
Die();
}
};
base._Ready();
}
public override void _Process(double delta)
{
_currentLifetime += delta;
float radians = (float)_currentLifetime * 24;
TrailRotation.Rotation = Direction.Angle();
TrailPosition.Position = new Vector2(0, 4 * Mathf.Sin(radians));
// home towards player
if (Homing is not null)
{
var desired = GlobalPosition.DirectionTo(Homing.GlobalPosition);
//var steer = (desired - Direction) * HomingVelocity * (float)delta;
//float dTheta = Direction.AngleTo(dirToHoming);
//float dTheta = Mathf.Acos(Direction.Dot(dirToHoming));
//float max = (float)(delta * HomingRotationalVelocity);
//float rotVel = Mathf.Clamp(dTheta, -max, max);
Direction += (desired - Direction) * HomingVelocity * (float)delta;
}
base._Process(delta);
}
public override void Die()
{
IsDead = Trail.IsDead = Hitbox.IsDisabled = true;
DeferDeathTimer.Timeout += () =>
{
QueueFree();
};
DeferDeathTimer.Start();
DeathParticles.Emitting = true;
GetNode<AudioStreamPlayer2D>("Sound").Stop();
GetNode<AnimationPlayer>("AnimationPlayer").Play("death");
}
}

View File

@ -0,0 +1,130 @@
[gd_scene load_steps=14 format=3 uid="uid://1y5r6sklwgrp"]
[ext_resource type="Script" path="res://Entities/UnwantedFrequency.cs" id="1_6sbe0"]
[ext_resource type="PackedScene" uid="uid://du5vhccg75nrq" path="res://BoundingBoxes/Hitbox.tscn" id="2_gxtvd"]
[ext_resource type="PackedScene" uid="uid://cojxmcin13ihm" path="res://Utils/Trail.tscn" id="3_67uhs"]
[ext_resource type="AudioStream" uid="uid://cn2wop7rfxku8" path="res://Assets/Sounds/karabast.mp3" id="4_pbgsi"]
[sub_resource type="RectangleShape2D" id="RectangleShape2D_30y8q"]
size = Vector2(8, 8)
[sub_resource type="Curve" id="Curve_eu273"]
_data = [Vector2(0.0618557, 0), 0.0, 0.0, 0, 0, Vector2(0.489691, 1), 0.0, 0.0, 0, 0]
point_count = 2
[sub_resource type="Gradient" id="Gradient_dyqhb"]
offsets = PackedFloat32Array(0.00662252, 0.715232, 1)
colors = PackedColorArray(0.996078, 0, 0.164706, 0, 0.996045, 0, 0.166638, 1, 1, 1, 1, 1)
[sub_resource type="Gradient" id="Gradient_2q0ut"]
offsets = PackedFloat32Array(0.525926, 0.740741, 1)
colors = PackedColorArray(1, 1, 1, 1, 1, 0.00784314, 0.215686, 0.784314, 1, 0, 0, 0)
[sub_resource type="GradientTexture1D" id="GradientTexture1D_yfhnr"]
gradient = SubResource("Gradient_2q0ut")
[sub_resource type="ParticleProcessMaterial" id="ParticleProcessMaterial_eh1hw"]
particle_flag_disable_z = true
spread = 180.0
gravity = Vector3(0, 0, 0)
initial_velocity_max = 100.0
angular_velocity_min = 45.0
angular_velocity_max = 90.0
orbit_velocity_min = 0.0
orbit_velocity_max = 2.0
color_ramp = SubResource("GradientTexture1D_yfhnr")
[sub_resource type="Animation" id="Animation_0brc4"]
resource_name = "death"
length = 4.0
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("Sound:volume_db")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0, 4),
"transitions": PackedFloat32Array(1, 1),
"update": 0,
"values": [-16.0, -64.0]
}
[sub_resource type="Animation" id="Animation_w1abs"]
length = 0.001
tracks/0/type = "value"
tracks/0/imported = false
tracks/0/enabled = true
tracks/0/path = NodePath("Sound:volume_db")
tracks/0/interp = 1
tracks/0/loop_wrap = true
tracks/0/keys = {
"times": PackedFloat32Array(0),
"transitions": PackedFloat32Array(1),
"update": 0,
"values": [-16.0]
}
[sub_resource type="AnimationLibrary" id="AnimationLibrary_v8fdt"]
_data = {
"RESET": SubResource("Animation_w1abs"),
"death": SubResource("Animation_0brc4")
}
[node name="UnwantedFrequency" type="RigidBody2D" node_paths=PackedStringArray("Hitbox")]
script = ExtResource("1_6sbe0")
HomingVelocity = 1.4
ProjectileName = "Unwanted Frequency"
Speed = 140.0
Direction = Vector2(1, 1)
Hitbox = NodePath("Hitbox")
Lifetime = 8.0
[node name="Hitbox" parent="." instance=ExtResource("2_gxtvd")]
collision_layer = 0
monitorable = false
Damage = 12.0
Knockback = 324.0
[node name="CollisionShape2D" parent="Hitbox" index="0"]
shape = SubResource("RectangleShape2D_30y8q")
[node name="TrailRotation" type="Node2D" parent="."]
[node name="TrailPosition" type="Node2D" parent="TrailRotation"]
[node name="Trail" parent="TrailRotation/TrailPosition" instance=ExtResource("3_67uhs")]
self_modulate = Color(2, 2, 2, 1)
width = 2.0
width_curve = SubResource("Curve_eu273")
default_color = Color(1, 0.0862745, 0.207843, 1)
gradient = SubResource("Gradient_dyqhb")
joint_mode = 2
begin_cap_mode = 2
end_cap_mode = 2
MaximumPoints = 64
Frequency = 30
[node name="DeferDeath" type="Timer" parent="."]
wait_time = 4.0
[node name="Sound" type="AudioStreamPlayer2D" parent="."]
stream = ExtResource("4_pbgsi")
volume_db = -16.0
autoplay = true
max_distance = 64.0
attenuation = 8.0
[node name="DeathParticles" type="GPUParticles2D" parent="."]
emitting = false
process_material = SubResource("ParticleProcessMaterial_eh1hw")
lifetime = 2.0
one_shot = true
explosiveness = 0.8
[node name="AnimationPlayer" type="AnimationPlayer" parent="."]
libraries = {
"": SubResource("AnimationLibrary_v8fdt")
}
[editable path="Hitbox"]

View File

@ -377,12 +377,11 @@ environment = SubResource("Environment_72txp")
y_sort_enabled = true y_sort_enabled = true
rotation = -1.5708 rotation = -1.5708
[node name="Trail" parent="Anchor" node_paths=PackedStringArray("Tracking") instance=ExtResource("4_pt6lq")] [node name="Trail" parent="Anchor" instance=ExtResource("4_pt6lq")]
position = Vector2(2.40734, -0.55655) position = Vector2(2.40734, -0.55655)
rotation = 0.945464 rotation = 0.945464
width_curve = SubResource("Curve_4cxtp") width_curve = SubResource("Curve_4cxtp")
gradient = SubResource("Gradient_2ablm") gradient = SubResource("Gradient_2ablm")
Tracking = NodePath("../Node2D/Sprite2D")
[node name="Node2D" type="Node2D" parent="Anchor"] [node name="Node2D" type="Node2D" parent="Anchor"]
y_sort_enabled = true y_sort_enabled = true

View File

@ -3,7 +3,7 @@
[ext_resource type="PackedScene" uid="uid://clwv2owvk6abe" path="res://Scenes/BaseMap.tscn" id="1_ifiic"] [ext_resource type="PackedScene" uid="uid://clwv2owvk6abe" path="res://Scenes/BaseMap.tscn" id="1_ifiic"]
[ext_resource type="Texture2D" uid="uid://b0yiy7w8nxmas" path="res://Assets/Sprites/arena-tileset.png" id="2_wnjm0"] [ext_resource type="Texture2D" uid="uid://b0yiy7w8nxmas" path="res://Assets/Sprites/arena-tileset.png" id="2_wnjm0"]
[ext_resource type="Texture2D" uid="uid://5k0o7d7j65a4" path="res://Assets/Sprites/arena-tileset-normal.png" id="3_iitgk"] [ext_resource type="Texture2D" uid="uid://5k0o7d7j65a4" path="res://Assets/Sprites/arena-tileset-normal.png" id="3_iitgk"]
[ext_resource type="PackedScene" uid="uid://dsr5kkbthpwpl" path="res://Characters/Doc.tscn" id="4_c0csw"] [ext_resource type="PackedScene" uid="uid://d2skjvvx6fal0" path="res://Characters/Doc.tscn" id="4_c0csw"]
[ext_resource type="Shader" path="res://Shaders/Flash.gdshader" id="5_aevwf"] [ext_resource type="Shader" path="res://Shaders/Flash.gdshader" id="5_aevwf"]
[sub_resource type="OccluderPolygon2D" id="OccluderPolygon2D_8jil2"] [sub_resource type="OccluderPolygon2D" id="OccluderPolygon2D_8jil2"]
@ -252,7 +252,7 @@ physics_layer_0/collision_layer = 1
sources/2 = SubResource("TileSetAtlasSource_5yxvt") sources/2 = SubResource("TileSetAtlasSource_5yxvt")
sources/0 = SubResource("TileSetAtlasSource_fcd6d") sources/0 = SubResource("TileSetAtlasSource_fcd6d")
[sub_resource type="ShaderMaterial" id="ShaderMaterial_4g4ap"] [sub_resource type="ShaderMaterial" id="ShaderMaterial_h6m08"]
resource_local_to_scene = true resource_local_to_scene = true
shader = ExtResource("5_aevwf") shader = ExtResource("5_aevwf")
shader_parameter/color = Quaternion(1, 1, 1, 1) shader_parameter/color = Quaternion(1, 1, 1, 1)
@ -268,6 +268,6 @@ layer_4/tile_data = PackedInt32Array(-524296, 327680, 0, -589818, 262144, 0, -58
color = Color(0.753984, 0.753984, 0.753984, 1) color = Color(0.753984, 0.753984, 0.753984, 1)
[node name="Doc" parent="Entities" index="0" instance=ExtResource("4_c0csw")] [node name="Doc" parent="Entities" index="0" instance=ExtResource("4_c0csw")]
material = SubResource("ShaderMaterial_4g4ap") material = SubResource("ShaderMaterial_h6m08")
PreferredWeightDistance = 256.0 PreferredWeightDistance = 256.0
MaxWeightDistance = 32.0 MaxWeightDistance = 32.0

View File

@ -1,4 +1,5 @@
using Godot; using Godot;
using System.Linq;
using System.Collections.Generic; using System.Collections.Generic;
namespace SupaLidlGame.State.NPC.Doc; namespace SupaLidlGame.State.NPC.Doc;
@ -11,22 +12,31 @@ public partial class DocChooseAttackState : NPCState
[Export] [Export]
public DocShungiteSpikeState SpikeState { get; set; } public DocShungiteSpikeState SpikeState { get; set; }
[Export]
public DocUnwantedFrequencyState UnwantedFrequencyState { get; set; }
[Export] [Export]
public DocExitState ExitState { get; set; } public DocExitState ExitState { get; set; }
public Characters.Doc Doc => NPC as Characters.Doc; public Characters.Doc Doc => NPC as Characters.Doc;
private List<NPCState> _states = new List<NPCState>(); private HashSet<NPCState> _possibleStates = new HashSet<NPCState>();
private int _consecutiveAttacks = 0; private int _consecutiveAttacks = 0;
public override void _Ready() public override void _Ready()
{ {
_states.Add(DartState); ResetStates();
_states.Add(SpikeState);
base._Ready(); base._Ready();
} }
private void ResetStates()
{
//_possibleStates.Add(DartState);
//_possibleStates.Add(SpikeState);
_possibleStates.Add(UnwantedFrequencyState);
}
public override NPCState Enter(IState<NPCState> previous) public override NPCState Enter(IState<NPCState> previous)
{ {
if (previous is not DocTelegraphState) if (previous is not DocTelegraphState)
@ -44,8 +54,16 @@ public partial class DocChooseAttackState : NPCState
return ExitState; return ExitState;
} }
if (_possibleStates.Count == 0)
{
ResetStates();
}
// choose random attack // choose random attack
var random = new System.Random(); var random = new System.Random();
return _states[random.Next(_states.Count)]; int index = random.Next(_possibleStates.Count);
var state = _possibleStates.ElementAt(index);
_possibleStates.Remove(state);
return state;
} }
} }

View File

@ -47,8 +47,6 @@ public partial class DocShungiteDartState : DocAttackState
{ {
var projectile = _map.SpawnEntity<Projectile>(Projectile); var projectile = _map.SpawnEntity<Projectile>(Projectile);
projectile.Hitbox.Faction = NPC.Faction; projectile.Hitbox.Faction = NPC.Faction;
// global position is (from npc to player) * 2 = (2 * npc) - player
//projectile.GlobalPosition = 2 * NPC.GlobalPosition - playerPos;
projectile.GlobalPosition = position; projectile.GlobalPosition = position;
projectile.Direction = direction; projectile.Direction = direction;
projectile.GlobalRotation = direction.Angle(); projectile.GlobalRotation = direction.Angle();
@ -60,6 +58,8 @@ public partial class DocShungiteDartState : DocAttackState
{ {
var player = _world.CurrentPlayer; var player = _world.CurrentPlayer;
var playerPos = player.GlobalPosition; var playerPos = player.GlobalPosition;
// global position is (from npc to player) * 2 = (2 * npc) - player
//projectile.GlobalPosition = 2 * NPC.GlobalPosition - playerPos;
Vector2 position1 = 2 * NPC.GlobalPosition - playerPos; Vector2 position1 = 2 * NPC.GlobalPosition - playerPos;
Vector2 position2 = 2 * playerPos - NPC.GlobalPosition; Vector2 position2 = 2 * playerPos - NPC.GlobalPosition;
Vector2 direction1 = position1.DirectionTo(playerPos); Vector2 direction1 = position1.DirectionTo(playerPos);

View File

@ -5,12 +5,12 @@ namespace SupaLidlGame.State.NPC.Doc;
public partial class DocShungiteSpikeState : DocShungiteDartState public partial class DocShungiteSpikeState : DocShungiteDartState
{ {
private float _intensity = 1; protected int _currentAttacks = 0;
public override NPCState Enter(IState<NPCState> previous) public override NPCState Enter(IState<NPCState> previous)
{ {
// subtract from total state time by intensity _currentAttacks = 0;
Duration = _currentDuration = 9 - 2 * Doc.Intensity; _currentAttackDuration = 1;
return base.Enter(previous); return base.Enter(previous);
} }
@ -22,7 +22,6 @@ public partial class DocShungiteSpikeState : DocShungiteDartState
as ShungiteSpike; as ShungiteSpike;
projectile.GlobalRotation = 0; projectile.GlobalRotation = 0;
projectile.Delay = 0; projectile.Delay = 0;
projectile.ExplodeTime = 6 - 2 * Doc.Intensity;
projectile.Hitbox.Faction = projectile.Hurtbox.Faction = Doc.Faction; projectile.Hitbox.Faction = projectile.Hurtbox.Faction = Doc.Faction;
return projectile; return projectile;
} }
@ -31,31 +30,37 @@ public partial class DocShungiteSpikeState : DocShungiteDartState
{ {
var player = _world.CurrentPlayer; var player = _world.CurrentPlayer;
var playerPos = player.GlobalPosition; var playerPos = player.GlobalPosition;
Vector2 left = playerPos + Vector2.Left * 64; var docPos = NPC.GlobalPosition;
Vector2 right = playerPos + Vector2.Right * 64; var projectile = SpawnProjectile(docPos, Vector2.Zero) as ShungiteSpike;
Vector2 up = playerPos + Vector2.Up * 64; var projectileSpeed = projectile.Speed = 96;
Vector2 down = playerPos + Vector2.Down * 64;
SpawnProjectile(left, Vector2.Zero);
SpawnProjectile(right, Vector2.Zero);
SpawnProjectile(up, Vector2.Zero);
SpawnProjectile(down, Vector2.Zero);
// only attack once and stop (but keep in this state for 8 seconds) // predict to player's position
_currentAttackDuration += 8; var targetPos = Utils.Physics.PredictNewPosition(
docPos,
projectileSpeed,
playerPos,
player.Velocity,
out float time);
projectile.Direction = projectile.GlobalPosition.DirectionTo(targetPos);
projectile.FreezeTime = time + 0.25; // freeze when it reaches target
GD.Print("projectile velocity: " + projectile.Velocity);
_currentAttackDuration = 1;
_currentAttacks++;
} }
public override NPCState Process(double delta) public override NPCState Process(double delta)
{ {
if ((_currentDuration -= delta) <= 0)
{
return ChooseAttackState;
}
if ((_currentAttackDuration -= delta) <= 0) if ((_currentAttackDuration -= delta) <= 0)
{ {
Attack(); Attack();
} }
if (_currentAttacks >= Doc.Intensity)
{
return ChooseAttackState;
}
return null; return null;
} }
} }

View File

@ -0,0 +1,32 @@
using Godot;
using SupaLidlGame.Entities;
namespace SupaLidlGame.State.NPC.Doc;
public partial class DocUnwantedFrequencyState : DocShungiteSpikeState
{
protected override Projectile SpawnProjectile(
Vector2 position,
Vector2 direction)
{
var projectile = _map.SpawnEntity<UnwantedFrequency>(Projectile);
projectile.Hitbox.Faction = NPC.Faction;
projectile.GlobalPosition = position;
projectile.Direction = direction;
projectile.Delay = 1.0 / Doc.Intensity;
return projectile;
}
protected override void Attack()
{
var player = _world.CurrentPlayer;
var playerPos = player.GlobalPosition;
var docPos = NPC.GlobalPosition;
var projectile = SpawnProjectile(docPos, docPos.DirectionTo(playerPos))
as UnwantedFrequency;
projectile.Homing = player;
_currentAttackDuration = 1;
_currentAttacks++;
}
}

36
Utils/Physics.cs 100644
View File

@ -0,0 +1,36 @@
using Godot;
namespace SupaLidlGame.Utils;
public static class Physics
{
/// <summary>
/// Returns the predicted position of a target after a time t at which a
/// projectile is predicted to hit a target.
/// </summary>
public static Vector2 PredictNewPosition(
Vector2 position,
float speed,
Vector2 targetPosition,
Vector2 targetVelocity,
out float time)
{
// relative velocity is sum of projectile's relative velocity when idle
// plus its relative velocity when heading towards target (as origin)
var relIdle = -targetVelocity;
var relDir = (position - targetPosition).DirectionTo(Vector2.Zero);
var relVel = relIdle + relDir * speed;
var relSpeed = relVel.Length();
GD.Print("Relative velocity: " + relVel);
// get t = time to travel
// = (|s2| - |s1|)/(|v2| - |v1|)
time = position.DistanceTo(targetPosition) / relSpeed;
GD.Print("Time: " + time);
// predict target's position after time t
return targetPosition + targetVelocity * time;
}
}

View File

@ -1,17 +1,48 @@
using Godot; using Godot;
namespace SupaLidlGame.Utils;
public partial class Trail : Line2D public partial class Trail : Line2D
{ {
[Export] [Export]
public int MaximumPoints { get; set; } public int MaximumPoints { get; set; }
[Export] [Export]
public Node2D Tracking { get; set; } protected uint Frequency { get; set; }
public bool IsDead { get; set; } = false;
protected double _currentTrailSleepTime = 0;
protected double _trailSleepTime = 0;
protected Node2D _parent;
public override void _Ready()
{
_trailSleepTime = 1.0 / Frequency;
_parent = GetParent() as Node2D;
}
public override void _Process(double delta) public override void _Process(double delta)
{ {
Vector2 point = Tracking.Position; if (IsDead && Points.Length > 0)
AddPoint(point); {
RemovePoint(0);
return;
}
//Vector2 point = GlobalPosition;
if ((_currentTrailSleepTime -= delta) > 0)
{
if (Points.Length > 0)
{
SetPointPosition(Points.Length - 1, _parent.GlobalPosition);
}
return;
}
_currentTrailSleepTime = _trailSleepTime;
AddPoint(_parent.GlobalPosition);
while (Points.Length > MaximumPoints) while (Points.Length > MaximumPoints)
{ {

View File

@ -4,5 +4,6 @@
[node name="Trail" type="Line2D"] [node name="Trail" type="Line2D"]
show_behind_parent = true show_behind_parent = true
top_level = true
script = ExtResource("1_t42kk") script = ExtResource("1_t42kk")
MaximumPoints = 16 MaximumPoints = 16