rework spike attack; added unwanted frequency
							parent
							
								
									9dd4525fcd
								
							
						
					
					
						commit
						2979f69405
					
				
											
												Binary file not shown.
											
										
									
								|  | @ -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.
											
										
									
								
											
												Binary file not shown.
											
										
									
								| Before Width: | Height: | Size: 458 B After Width: | Height: | Size: 818 B | 
|  | @ -80,7 +80,6 @@ public partial class Hitbox : BoundingBox | |||
|     { | ||||
|         if (area is BoundingBox box) | ||||
|         { | ||||
|             GD.Print("hit"); | ||||
|             // we don't want to hurt teammates | ||||
|             if (Faction != box.Faction) | ||||
|             { | ||||
|  |  | |||
|  | @ -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="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="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/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"] | ||||
| resource_local_to_scene = true | ||||
|  | @ -257,10 +259,20 @@ ChooseAttackState = NodePath("../ChooseAttack") | |||
| Doc = 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") | ||||
| DartState = NodePath("../Dart") | ||||
| SpikeState = NodePath("../Spike") | ||||
| UnwantedFrequencyState = NodePath("../UnwantedFrequency") | ||||
| ExitState = NodePath("../Exit") | ||||
| NPC = NodePath("../..") | ||||
| 
 | ||||
|  |  | |||
|  | @ -84,6 +84,25 @@ public sealed partial class Player : Character | |||
|         // 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() | ||||
|     { | ||||
|         GD.Print("died"); | ||||
|  |  | |||
|  | @ -8,6 +8,9 @@ public partial class Projectile : RigidBody2D | |||
| { | ||||
|     public Vector2 Velocity => Direction * Speed; | ||||
| 
 | ||||
|     [Export] | ||||
|     public string ProjectileName { get; set; } | ||||
| 
 | ||||
|     [Export] | ||||
|     public float Speed { get; set; } | ||||
| 
 | ||||
|  | @ -25,6 +28,8 @@ public partial class Projectile : RigidBody2D | |||
| 
 | ||||
|     public Character Character { get; set; } | ||||
| 
 | ||||
|     public bool IsDead { get; set; } | ||||
| 
 | ||||
|     public override void _Ready() | ||||
|     { | ||||
|         Hitbox.Hit += OnHit; | ||||
|  | @ -35,15 +40,26 @@ public partial class Projectile : RigidBody2D | |||
|         if (Delay > 0) | ||||
|         { | ||||
|             Delay -= delta; | ||||
|             if (Delay <= 0) | ||||
|             { | ||||
|                 OnDelayEnd(); | ||||
|             } | ||||
|         } | ||||
|         if ((Lifetime -= delta) <= 0) | ||||
|         { | ||||
|             QueueFree(); | ||||
|             if (!IsDead) | ||||
|             { | ||||
|                 Die(); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public override void _PhysicsProcess(double delta) | ||||
|     { | ||||
|         if (IsDead) | ||||
|         { | ||||
|             return; | ||||
|         } | ||||
|         Vector2 velocity = Delay <= 0 ? Velocity : Vector2.Zero; | ||||
|         MoveAndCollide(velocity * (float)delta); | ||||
|     } | ||||
|  | @ -60,4 +76,14 @@ public partial class Projectile : RigidBody2D | |||
|             ); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     public virtual void Die() | ||||
|     { | ||||
|         QueueFree(); | ||||
|     } | ||||
| 
 | ||||
|     public virtual void OnDelayEnd() | ||||
|     { | ||||
| 
 | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -277,8 +277,10 @@ _data = { | |||
| 
 | ||||
| [node name="ShungiteDart" type="RigidBody2D" node_paths=PackedStringArray("Hitbox")] | ||||
| script = ExtResource("1_jbgb8") | ||||
| ProjectileName = "Shungite Dart" | ||||
| Speed = 256.0 | ||||
| Hitbox = NodePath("Hitbox") | ||||
| Lifetime = 5.0 | ||||
| Delay = 1.0 | ||||
| 
 | ||||
| [node name="Sprite2D" type="AnimatedSprite2D" parent="."] | ||||
|  | @ -290,7 +292,7 @@ speed_scale = 4.0 | |||
| 
 | ||||
| [node name="Hitbox" parent="." instance=ExtResource("3_gdyk8")] | ||||
| Damage = 25.0 | ||||
| Knockback = 128.0 | ||||
| Knockback = 64.0 | ||||
| 
 | ||||
| [node name="CollisionShape2D" parent="Hitbox" index="0"] | ||||
| shape = SubResource("RectangleShape2D_fa7yf") | ||||
|  |  | |||
|  | @ -9,9 +9,6 @@ public partial class ShungiteSpike : Projectile | |||
|     [Export] | ||||
|     public PackedScene Dart { get; set; } | ||||
| 
 | ||||
|     [Export] | ||||
|     public double ExplodeTime { get; set; } = 6; | ||||
| 
 | ||||
|     [Export] | ||||
|     public BoundingBoxes.Hurtbox Hurtbox { get; set; } | ||||
| 
 | ||||
|  | @ -21,18 +18,44 @@ public partial class ShungiteSpike : Projectile | |||
|     [Export] | ||||
|     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 _freezeTime; | ||||
|     private double _currentFreezeTime; | ||||
|     private bool _hasFrozen = false; | ||||
|     private bool _hasExploded = false; | ||||
| 
 | ||||
|     private Scenes.Map _map; | ||||
| 
 | ||||
|     public override void _Ready() | ||||
|     { | ||||
|         _currentFreezeTime = FreezeTime; | ||||
|         _currentExplodeTime = ExplodeTime; | ||||
|         _map = this.GetAncestor<Scenes.Map>(); | ||||
| 
 | ||||
|         Hurtbox.ReceivedDamage += OnReceivedDamage; | ||||
|         AnimationPlayer.Play("spin"); | ||||
| 
 | ||||
|         AnimationPlayer.AnimationFinished += (StringName anim) => | ||||
|         { | ||||
|             if (anim == "explode") | ||||
|             { | ||||
|                 QueueFree(); | ||||
|             } | ||||
|         }; | ||||
| 
 | ||||
|         base._Ready(); | ||||
|     } | ||||
| 
 | ||||
|  | @ -43,8 +66,21 @@ public partial class ShungiteSpike : Projectile | |||
|         Vector2 knockbackOrigin = 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(); | ||||
|         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) | ||||
|     { | ||||
|         if ((_currentExplodeTime -= delta) <= 0) | ||||
|         if ((_currentFreezeTime -= delta) <= 0) | ||||
|         { | ||||
|             CreateDart(Vector2.Up); | ||||
|             CreateDart(Vector2.Down); | ||||
|             CreateDart(Vector2.Left); | ||||
|             CreateDart(Vector2.Right); | ||||
|             QueueFree(); | ||||
|             if (!_hasFrozen) | ||||
|             { | ||||
|                 Speed = 0; | ||||
|                 _hasFrozen = true; | ||||
|                 AnimationPlayer.Stop(); | ||||
|             } | ||||
| 
 | ||||
|             if ((_currentExplodeTime -= delta) <= 0) | ||||
|             { | ||||
|                 if (!_hasExploded) | ||||
|                 { | ||||
|                     CreateDart(Vector2.Up); | ||||
|                     CreateDart(Vector2.Down); | ||||
|                     CreateDart(Vector2.Left); | ||||
|                     CreateDart(Vector2.Right); | ||||
|                     AnimationPlayer.Play("explode"); | ||||
|                     _hasExploded = true; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -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="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="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"] | ||||
| 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"] | ||||
| resource_name = "spin" | ||||
|  | @ -29,46 +109,34 @@ tracks/0/keys = { | |||
| "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"] | ||||
| _data = { | ||||
| "RESET": SubResource("Animation_0vfvo"), | ||||
| "explode": SubResource("Animation_0a85j"), | ||||
| "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") | ||||
| Dart = ExtResource("2_hinxt") | ||||
| Hurtbox = NodePath("Hurtbox") | ||||
| HitSound = NodePath("AudioStreamPlayer2D") | ||||
| AnimationPlayer = NodePath("AnimationPlayer") | ||||
| ProjectileName = "Shungite Spike" | ||||
| Speed = 96.0 | ||||
| Hitbox = NodePath("Hitbox") | ||||
| 
 | ||||
| [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 = ExtResource("2_klp8v") | ||||
| hframes = 5 | ||||
| 
 | ||||
| [node name="Hitbox" parent="." instance=ExtResource("3_kojrj")] | ||||
| Damage = 10.0 | ||||
| Knockback = 256.0 | ||||
| Damage = 14.0 | ||||
| Knockback = 200.0 | ||||
| 
 | ||||
| [node name="CollisionShape2D" parent="Hitbox" index="0"] | ||||
| shape = SubResource("RectangleShape2D_konb7") | ||||
| shape = SubResource("CircleShape2D_hrev2") | ||||
| 
 | ||||
| [node name="Hurtbox" parent="." instance=ExtResource("4_d8dl4")] | ||||
| 
 | ||||
|  |  | |||
|  | @ -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"); | ||||
|     } | ||||
| } | ||||
|  | @ -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"] | ||||
|  | @ -377,12 +377,11 @@ environment = SubResource("Environment_72txp") | |||
| y_sort_enabled = true | ||||
| 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) | ||||
| rotation = 0.945464 | ||||
| width_curve = SubResource("Curve_4cxtp") | ||||
| gradient = SubResource("Gradient_2ablm") | ||||
| Tracking = NodePath("../Node2D/Sprite2D") | ||||
| 
 | ||||
| [node name="Node2D" type="Node2D" parent="Anchor"] | ||||
| y_sort_enabled = true | ||||
|  |  | |||
|  | @ -3,7 +3,7 @@ | |||
| [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://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"] | ||||
| 
 | ||||
| [sub_resource type="OccluderPolygon2D" id="OccluderPolygon2D_8jil2"] | ||||
|  | @ -252,7 +252,7 @@ physics_layer_0/collision_layer = 1 | |||
| sources/2 = SubResource("TileSetAtlasSource_5yxvt") | ||||
| sources/0 = SubResource("TileSetAtlasSource_fcd6d") | ||||
| 
 | ||||
| [sub_resource type="ShaderMaterial" id="ShaderMaterial_4g4ap"] | ||||
| [sub_resource type="ShaderMaterial" id="ShaderMaterial_h6m08"] | ||||
| resource_local_to_scene = true | ||||
| shader = ExtResource("5_aevwf") | ||||
| 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) | ||||
| 
 | ||||
| [node name="Doc" parent="Entities" index="0" instance=ExtResource("4_c0csw")] | ||||
| material = SubResource("ShaderMaterial_4g4ap") | ||||
| material = SubResource("ShaderMaterial_h6m08") | ||||
| PreferredWeightDistance = 256.0 | ||||
| MaxWeightDistance = 32.0 | ||||
|  |  | |||
|  | @ -1,4 +1,5 @@ | |||
| using Godot; | ||||
| using System.Linq; | ||||
| using System.Collections.Generic; | ||||
| 
 | ||||
| namespace SupaLidlGame.State.NPC.Doc; | ||||
|  | @ -11,22 +12,31 @@ public partial class DocChooseAttackState : NPCState | |||
|     [Export] | ||||
|     public DocShungiteSpikeState SpikeState { get; set; } | ||||
| 
 | ||||
|     [Export] | ||||
|     public DocUnwantedFrequencyState UnwantedFrequencyState { get; set; } | ||||
| 
 | ||||
|     [Export] | ||||
|     public DocExitState ExitState { get; set; } | ||||
| 
 | ||||
|     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; | ||||
| 
 | ||||
|     public override void _Ready() | ||||
|     { | ||||
|         _states.Add(DartState); | ||||
|         _states.Add(SpikeState); | ||||
|         ResetStates(); | ||||
|         base._Ready(); | ||||
|     } | ||||
| 
 | ||||
|     private void ResetStates() | ||||
|     { | ||||
|         //_possibleStates.Add(DartState); | ||||
|         //_possibleStates.Add(SpikeState); | ||||
|         _possibleStates.Add(UnwantedFrequencyState); | ||||
|     } | ||||
| 
 | ||||
|     public override NPCState Enter(IState<NPCState> previous) | ||||
|     { | ||||
|         if (previous is not DocTelegraphState) | ||||
|  | @ -44,8 +54,16 @@ public partial class DocChooseAttackState : NPCState | |||
|             return ExitState; | ||||
|         } | ||||
| 
 | ||||
|         if (_possibleStates.Count == 0) | ||||
|         { | ||||
|             ResetStates(); | ||||
|         } | ||||
| 
 | ||||
|         // choose random attack | ||||
|         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; | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -47,8 +47,6 @@ public partial class DocShungiteDartState : DocAttackState | |||
|     { | ||||
|         var projectile = _map.SpawnEntity<Projectile>(Projectile); | ||||
|         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.Direction = direction; | ||||
|         projectile.GlobalRotation = direction.Angle(); | ||||
|  | @ -60,6 +58,8 @@ public partial class DocShungiteDartState : DocAttackState | |||
|     { | ||||
|         var player = _world.CurrentPlayer; | ||||
|         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 position2 = 2 * playerPos - NPC.GlobalPosition; | ||||
|         Vector2 direction1 = position1.DirectionTo(playerPos); | ||||
|  |  | |||
|  | @ -5,12 +5,12 @@ namespace SupaLidlGame.State.NPC.Doc; | |||
| 
 | ||||
| public partial class DocShungiteSpikeState : DocShungiteDartState | ||||
| { | ||||
|     private float _intensity = 1; | ||||
|     protected int _currentAttacks = 0; | ||||
| 
 | ||||
|     public override NPCState Enter(IState<NPCState> previous) | ||||
|     { | ||||
|         // subtract from total state time by intensity | ||||
|         Duration = _currentDuration = 9 - 2 * Doc.Intensity; | ||||
|         _currentAttacks = 0; | ||||
|         _currentAttackDuration = 1; | ||||
|         return base.Enter(previous); | ||||
|     } | ||||
| 
 | ||||
|  | @ -22,7 +22,6 @@ public partial class DocShungiteSpikeState : DocShungiteDartState | |||
|             as ShungiteSpike; | ||||
|         projectile.GlobalRotation = 0; | ||||
|         projectile.Delay = 0; | ||||
|         projectile.ExplodeTime = 6 - 2 * Doc.Intensity; | ||||
|         projectile.Hitbox.Faction = projectile.Hurtbox.Faction = Doc.Faction; | ||||
|         return projectile; | ||||
|     } | ||||
|  | @ -31,31 +30,37 @@ public partial class DocShungiteSpikeState : DocShungiteDartState | |||
|     { | ||||
|         var player = _world.CurrentPlayer; | ||||
|         var playerPos = player.GlobalPosition; | ||||
|         Vector2 left = playerPos + Vector2.Left * 64; | ||||
|         Vector2 right = playerPos + Vector2.Right * 64; | ||||
|         Vector2 up = playerPos + Vector2.Up * 64; | ||||
|         Vector2 down = playerPos + Vector2.Down * 64; | ||||
|         SpawnProjectile(left, Vector2.Zero); | ||||
|         SpawnProjectile(right, Vector2.Zero); | ||||
|         SpawnProjectile(up, Vector2.Zero); | ||||
|         SpawnProjectile(down, Vector2.Zero); | ||||
|         var docPos = NPC.GlobalPosition; | ||||
|         var projectile = SpawnProjectile(docPos, Vector2.Zero) as ShungiteSpike; | ||||
|         var projectileSpeed = projectile.Speed = 96; | ||||
| 
 | ||||
|         // only attack once and stop (but keep in this state for 8 seconds) | ||||
|         _currentAttackDuration += 8; | ||||
|         // predict to player's position | ||||
|         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) | ||||
|     { | ||||
|         if ((_currentDuration -= delta) <= 0) | ||||
|         { | ||||
|             return ChooseAttackState; | ||||
|         } | ||||
| 
 | ||||
|         if ((_currentAttackDuration -= delta) <= 0) | ||||
|         { | ||||
|             Attack(); | ||||
|         } | ||||
| 
 | ||||
|         if (_currentAttacks >= Doc.Intensity) | ||||
|         { | ||||
|             return ChooseAttackState; | ||||
|         } | ||||
| 
 | ||||
|         return null; | ||||
|     } | ||||
| } | ||||
|  |  | |||
|  | @ -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++; | ||||
|     } | ||||
| } | ||||
|  | @ -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; | ||||
|     } | ||||
| } | ||||
|  | @ -1,17 +1,48 @@ | |||
| using Godot; | ||||
| 
 | ||||
| namespace SupaLidlGame.Utils; | ||||
| 
 | ||||
| public partial class Trail : Line2D | ||||
| { | ||||
|     [Export] | ||||
|     public int MaximumPoints { get; set; } | ||||
| 
 | ||||
|     [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) | ||||
|     { | ||||
|         Vector2 point = Tracking.Position; | ||||
|         AddPoint(point); | ||||
|         if (IsDead && Points.Length > 0) | ||||
|         { | ||||
|             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) | ||||
|         { | ||||
|  |  | |||
|  | @ -4,5 +4,6 @@ | |||
| 
 | ||||
| [node name="Trail" type="Line2D"] | ||||
| show_behind_parent = true | ||||
| top_level = true | ||||
| script = ExtResource("1_t42kk") | ||||
| MaximumPoints = 16 | ||||
|  |  | |||
		Loading…
	
		Reference in New Issue