diff --git a/addons/SimpleDungeons/debug_visuals/WireframeColorMat.tres b/addons/SimpleDungeons/debug_visuals/WireframeColorMat.tres index c7f26a06..9078f816 100644 --- a/addons/SimpleDungeons/debug_visuals/WireframeColorMat.tres +++ b/addons/SimpleDungeons/debug_visuals/WireframeColorMat.tres @@ -1,44 +1,4 @@ -[gd_resource type="ShaderMaterial" load_steps=2 format=3 uid="uid://pq2fqq4ophsy"] - -[sub_resource type="Shader" id="Shader_xtf8b"] -resource_local_to_scene = true -code = "shader_type spatial; -render_mode unshaded, cull_disabled, depth_draw_always, depth_test_disabled; - -uniform vec3 color = vec3(0.0, 1.0, 0.0); -uniform float line_width : hint_range(0.0, 10.0) = 0.85; - -varying vec3 barycentric; - -void vertex() { - vec3 b_coords[3] = vec3[](vec3(1, 0, 0), vec3(0, 1, 0), vec3(0, 0, 1)); - int vertex_id = int(VERTEX_ID) % 3; - barycentric = b_coords[vertex_id]; -} - -void fragment() { - // Calculate the screen-space derivatives of the barycentric coordinates - vec3 dFdx_barycentric = dFdx(barycentric); - vec3 dFdy_barycentric = dFdy(barycentric); - - // Calculate the minimum barycentric coordinate value - float min_bary = min(min(barycentric.x, barycentric.y), barycentric.z); - - // Calculate the screen-space line width - float screen_line_width = line_width * length(vec2(dFdx_barycentric.x, dFdy_barycentric.x)); - - // Draw the line based on the calculated screen-space line width - if (min_bary < screen_line_width) { - ALBEDO = color; - } else { - discard; - } -} -" +[gd_resource type="ShaderMaterial" format=3 uid="uid://pq2fqq4ophsy"] [resource] resource_local_to_scene = true -render_priority = 0 -shader = SubResource("Shader_xtf8b") -shader_parameter/color = Vector3(0, 1, 0) -shader_parameter/line_width = 0.85 diff --git a/src/enemy/Enemy.cs b/src/enemy/Enemy.cs index 485595ae..14f65d22 100644 --- a/src/enemy/Enemy.cs +++ b/src/enemy/Enemy.cs @@ -34,6 +34,8 @@ public partial class Enemy : CharacterBody3D, IEnemy, IProvide [Dependency] IGameRepo GameRepo => this.DependOn(); + [Dependency] IGameEventDepot GameEventDepot => this.DependOn(); + [Export] public EnemyStatResource EnemyStatResource { get; set; } = default!; @@ -51,10 +53,12 @@ public partial class Enemy : CharacterBody3D, IEnemy, IProvide [Node] public Timer AttackTimer { get; set; } = default!; - [Node] public AnimatedSprite3D AnimatedSprite { get; set; } = default!; + [Node] public AnimatedSprite2D AnimatedSprite { get; set; } = default!; [Node] public RayCast3D Raycast { get; set; } = default!; + [Node] public AnimationPlayer AnimationPlayer { get; set; } = default!; + private const string IDLE_FORWARD = "idle_front_walk"; private const string IDLE_LEFT = "idle_left_walk"; private const string IDLE_BACK = "idle_back_walk"; @@ -67,17 +71,93 @@ public partial class Enemy : CharacterBody3D, IEnemy, IProvide EnemyLogic.Set(EnemyStatResource); EnemyLogic.Set(this as IEnemy); EnemyLogic.Set(GameRepo); + AnimationPlayer.AnimationFinished += AnimationPlayer_AnimationFinished; + } + + public void OnReady() + { + SetPhysicsProcess(true); + CollisionDetector = CollisionDetectorScene.Instantiate(); + CollisionDetector.AreaEntered += OnPlayerHitboxEntered; + AddChild(CollisionDetector); + } + + public void OnResolved() + { + EnemyBinding = EnemyLogic.Bind(); + + EnemyBinding + .Handle((in EnemyLogic.Output.MovementComputed output) => + { + MoveAndSlide(); + }) + .Handle((in EnemyLogic.Output.HitByPlayer output) => + { + if (CurrentHP.Value > 0) + AnimationPlayer.Play("hit"); + // TODO: Make this an event to notify game that player hit someone + if (GameRepo.PlayerData.Inventory.EquippedWeapon.Value.WeaponStats.WeaponTags.Contains(WeaponTag.SelfDamage)) + GameRepo.PlayerData.SetCurrentHP(GameRepo.PlayerData.CurrentHP.Value - 5); + }) + .Handle((in EnemyLogic.Output.Defeated output) => + { + AnimationPlayer.Play("defeated"); + }); + + this.Provide(); + + EnemyLogic.Start(); + + CurrentHP = new AutoProp(EnemyStatResource.MaximumHP); + CurrentHP.Sync += OnHPChanged; + LineOfSight.BodyEntered += LineOfSight_BodyEntered; + PatrolTimer.Timeout += OnPatrolTimeout; + AttackTimer.Timeout += OnAttackTimeout; + var rng = new RandomNumberGenerator(); + rng.Randomize(); + PatrolTimer.WaitTime = rng.RandfRange(7.0f, 15.0f); + } + + public void OnExitTree() + { + EnemyLogic.Stop(); + EnemyBinding.Dispose(); } private void OnPatrolTimeout() { var rng = new RandomNumberGenerator(); rng.Randomize(); - var randomizedSpot = new Vector3(rng.RandfRange(-3.0f, 3.0f), 0, rng.RandfRange(-3.0f, 3.0f)); + var randomizedSpot = new Vector3(rng.RandfRange(-7.0f, 7.0f), 0, rng.RandfRange(-7.0f, 7.0f)); EnemyLogic.Input(new EnemyLogic.Input.PatrolToRandomSpot(GlobalPosition + randomizedSpot)); PatrolTimer.WaitTime = rng.RandfRange(7.0f, 15.0f); } + public void OnPhysicsProcess(double delta) + { + EnemyLogic.Input(new EnemyLogic.Input.PhysicsTick(delta)); + RotateEnemy(-GameRepo.PlayerGlobalTransform.Value.Basis.Z); + } + + public void OnPlayerHitboxEntered(Area3D body) + { + if (body is IHitbox hitBox) + { + if (CurrentHP.Value > 0) + { + var isCriticalHit = false; + var rng = new RandomNumberGenerator(); + rng.Randomize(); + var roll = rng.Randf(); + if (roll <= GameRepo.PlayerData.Inventory.EquippedWeapon.Value.WeaponStats.Luck) + isCriticalHit = true; + var damage = DamageCalculator.CalculatePlayerDamage(GameRepo.PlayerData.CurrentAttack.Value + GameRepo.PlayerData.BonusAttack, EnemyStatResource, GameRepo.PlayerData.Inventory.EquippedWeapon.Value.WeaponStats, isCriticalHit); + GD.Print($"Enemy Hit for {damage} damage."); + EnemyLogic.Input(new EnemyLogic.Input.HitByPlayer(damage)); + } + } + } + private void OnAttackTimeout() { if (GlobalPosition.DistanceTo(GameRepo.PlayerGlobalPosition.Value) > 2.5f) @@ -92,54 +172,20 @@ public partial class Enemy : CharacterBody3D, IEnemy, IProvide private void LineOfSight_BodyEntered(Node3D body) { var overlappingBodies = LineOfSight.GetOverlappingBodies(); - //foreach (var overlap in overlappingBodies) - //{ - // Raycast.LookAt(GameRepo.PlayerGlobalPosition.Value, Vector3.Up); - // Raycast.ForceRaycastUpdate(); - // if (Raycast.IsColliding()) - // { - // var collider = Raycast.GetCollider(); - // if (collider is IPlayer player) - // { - // Raycast.DebugShapeCustomColor = Color.FromString("Purple", Colors.Purple); - // EnemyLogic.Input(new EnemyLogic.Input.Alerted()); - // } - // } - //} - } - - public void OnResolved() - { - EnemyBinding = EnemyLogic.Bind(); - - EnemyBinding - .Handle((in EnemyLogic.Output.MovementComputed output) => + foreach (var overlap in overlappingBodies) + { + Raycast.LookAt(GameRepo.PlayerGlobalPosition.Value, Vector3.Up); + Raycast.ForceRaycastUpdate(); + if (Raycast.IsColliding()) { - MoveAndSlide(); - }) - .Handle((in EnemyLogic.Output.Die output) => - { - QueueFree(); - }) - .Handle((in EnemyLogic.Output.HitByPlayer output) => - { - // TODO: Make this an event to notify game that player hit someone - if (GameRepo.PlayerData.Inventory.EquippedWeapon.Value.WeaponStats.WeaponTags.Contains(WeaponTag.SelfDamage)) - GameRepo.PlayerData.SetCurrentHP(GameRepo.PlayerData.CurrentHP.Value - 5); - }); - - this.Provide(); - - EnemyLogic.Start(); - - CurrentHP = new AutoProp(EnemyStatResource.MaximumHP); - CurrentHP.Sync += OnHPChanged; - LineOfSight.BodyEntered += LineOfSight_BodyEntered; - PatrolTimer.Timeout += OnPatrolTimeout; - AttackTimer.Timeout += OnAttackTimeout; - var rng = new RandomNumberGenerator(); - rng.Randomize(); - PatrolTimer.WaitTime = rng.RandfRange(7.0f, 15.0f); + var collider = Raycast.GetCollider(); + if (collider is IPlayer player) + { + Raycast.DebugShapeCustomColor = Color.FromString("Purple", Colors.Purple); + EnemyLogic.Input(new EnemyLogic.Input.Alerted()); + } + } + } } private void RotateEnemy(Vector3 cameraDirection) @@ -169,58 +215,18 @@ public partial class Enemy : CharacterBody3D, IEnemy, IProvide } } - public void OnPhysicsProcess(double delta) - { - EnemyLogic.Input(new EnemyLogic.Input.PhysicsTick(delta)); - RotateEnemy(-GameRepo.PlayerGlobalTransform.Value.Basis.Z); - } - - public void OnPlayerHitboxEntered(Area3D body) - { - if (body is IHitbox hitBox) - { - if (CurrentHP.Value > 0) - { - var isCriticalHit = false; - var rng = new RandomNumberGenerator(); - rng.Randomize(); - var roll = rng.Randf(); - if (roll <= GameRepo.PlayerData.Inventory.EquippedWeapon.Value.WeaponStats.Luck) - isCriticalHit = true; - var damage = DamageCalculator.CalculatePlayerDamage(GameRepo.PlayerData.CurrentAttack.Value + GameRepo.PlayerData.BonusAttack, EnemyStatResource, GameRepo.PlayerData.Inventory.EquippedWeapon.Value.WeaponStats, isCriticalHit); - GD.Print($"Enemy Hit for {damage} damage."); - EnemyLogic.Input(new EnemyLogic.Input.HitByPlayer(damage)); - } - } - } - private void OnHPChanged(double newHP) { if (newHP <= 0) { - EnemyLogic.Input(new EnemyLogic.Input.Killed()); + EnemyLogic.Input(new EnemyLogic.Input.EnemyDefeated()); + GameEventDepot.OnEnemyDefeated(EnemyStatResource); } } - public void OnReady() + private void AnimationPlayer_AnimationFinished(StringName animName) { - SetPhysicsProcess(true); - CollisionDetector = CollisionDetectorScene.Instantiate(); - CollisionDetector.AreaEntered += OnPlayerHitboxEntered; - AddChild(CollisionDetector); - } - - public void OnExitTree() - { - EnemyLogic.Stop(); - EnemyBinding.Dispose(); + if (animName == "defeated") + QueueFree(); } } - -public enum WeaponTag -{ - SelfDamage, - IgnoreAffinity, - Knockback, - BreaksOnChange -} diff --git a/src/enemy/WeaponTag.cs b/src/enemy/WeaponTag.cs new file mode 100644 index 00000000..f5770662 --- /dev/null +++ b/src/enemy/WeaponTag.cs @@ -0,0 +1,9 @@ +namespace GameJamDungeon; + +public enum WeaponTag +{ + SelfDamage, + IgnoreAffinity, + Knockback, + BreaksOnChange +} diff --git a/src/enemy/enemy_types/michael/Michael.tscn b/src/enemy/enemy_types/michael/Michael.tscn index f4cfa7d7..f71a4f33 100644 --- a/src/enemy/enemy_types/michael/Michael.tscn +++ b/src/enemy/enemy_types/michael/Michael.tscn @@ -1,10 +1,13 @@ -[gd_scene load_steps=81 format=3 uid="uid://dcgj5i52i76gj"] +[gd_scene load_steps=87 format=3 uid="uid://dcgj5i52i76gj"] [ext_resource type="Script" path="res://src/enemy/Enemy.cs" id="1_a6wro"] [ext_resource type="Script" path="res://src/enemy/EnemyStatResource.cs" id="2_x4pjh"] [ext_resource type="Script" path="res://src/hitbox/Hitbox.cs" id="3_aiftp"] +[ext_resource type="Shader" path="res://src/vfx/shaders/DamageHit.gdshader" id="3_ekj3e"] +[ext_resource type="Material" uid="uid://c0gpeve05njqq" path="res://src/vfx/shaders/DamageHit.tres" id="4_01npl"] [ext_resource type="Texture2D" uid="uid://clpqh2pyqljkn" path="res://src/enemy/enemy_types/michael/animations/IDLE_WALK/BACK/Michael_Walk_Idle_Back (1).png" id="4_7kurm"] [ext_resource type="Texture2D" uid="uid://b0dec8ak2bo5t" path="res://src/enemy/enemy_types/michael/animations/IDLE_WALK/BACK/Michael_Walk_Idle_Back (2).png" id="5_1a1hr"] +[ext_resource type="Shader" path="res://src/vfx/shaders/PixelMelt.gdshader" id="5_lw20o"] [ext_resource type="Texture2D" uid="uid://tnmyksd68vmv" path="res://src/enemy/enemy_types/michael/animations/IDLE_WALK/BACK/Michael_Walk_Idle_Back (3).png" id="6_p47tl"] [ext_resource type="Texture2D" uid="uid://duwipvc2kl6xa" path="res://src/enemy/enemy_types/michael/animations/IDLE_WALK/BACK/Michael_Walk_Idle_Back (4).png" id="7_efmib"] [ext_resource type="Texture2D" uid="uid://dcd4v7jjxr8x2" path="res://src/enemy/enemy_types/michael/animations/IDLE_WALK/BACK/Michael_Walk_Idle_Back (5).png" id="8_ujyav"] @@ -111,6 +114,42 @@ tracks/0/keys = { "update": 1, "values": [true] } +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("Sprite3D/SubViewport/AnimatedSprite:material") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [null] +} +tracks/2/type = "value" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath("Sprite3D/SubViewport/AnimatedSprite:material:shader") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [ExtResource("3_ekj3e")] +} +tracks/3/type = "value" +tracks/3/imported = false +tracks/3/enabled = true +tracks/3/path = NodePath("Sprite3D/SubViewport/AnimatedSprite:material:shader_parameter/progress") +tracks/3/interp = 1 +tracks/3/loop_wrap = true +tracks/3/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [-1.0] +} [sub_resource type="Animation" id="Animation_ce86e"] resource_name = "attack" @@ -128,16 +167,102 @@ tracks/0/keys = { "values": [true, false, true] } +[sub_resource type="Animation" id="Animation_8p0l1"] +resource_name = "defeated" +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("Sprite3D/SubViewport/AnimatedSprite:material") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [ExtResource("4_01npl")] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("Sprite3D/SubViewport/AnimatedSprite:material:shader") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [ExtResource("5_lw20o")] +} +tracks/2/type = "value" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath("Sprite3D/SubViewport/AnimatedSprite:material:shader_parameter/progress") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/keys = { +"times": PackedFloat32Array(0, 1), +"transitions": PackedFloat32Array(1, 1), +"update": 0, +"values": [0.0, 1.0] +} + +[sub_resource type="Animation" id="Animation_swr3w"] +resource_name = "hit" +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("Sprite3D/SubViewport/AnimatedSprite:material") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 1), +"transitions": PackedFloat32Array(1, 1), +"update": 1, +"values": [ExtResource("4_01npl"), ExtResource("4_01npl")] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("Sprite3D/SubViewport/AnimatedSprite:material:shader") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0, 1), +"transitions": PackedFloat32Array(1, 1), +"update": 1, +"values": [ExtResource("3_ekj3e"), null] +} +tracks/2/type = "value" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath("Sprite3D/SubViewport/AnimatedSprite:material:shader_parameter/progress") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/keys = { +"times": PackedFloat32Array(0, 1), +"transitions": PackedFloat32Array(1, 1), +"update": 0, +"values": [0.0, 1.0] +} + [sub_resource type="AnimationLibrary" id="AnimationLibrary_346xs"] _data = { "RESET": SubResource("Animation_kr7ax"), -"attack": SubResource("Animation_ce86e") +"attack": SubResource("Animation_ce86e"), +"defeated": SubResource("Animation_8p0l1"), +"hit": SubResource("Animation_swr3w") } [sub_resource type="BoxShape3D" id="BoxShape3D_0yire"] size = Vector3(1, 0.564941, 1.14453) -[sub_resource type="SpriteFrames" id="SpriteFrames_x7baa"] +[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_cwfph"] +height = 1.0 + +[sub_resource type="ViewportTexture" id="ViewportTexture_57rcc"] +viewport_path = NodePath("Sprite3D/SubViewport") + +[sub_resource type="SpriteFrames" id="SpriteFrames_8xwq0"] animations = [{ "frames": [{ "duration": 1.0, @@ -362,9 +487,6 @@ animations = [{ "speed": 12.0 }] -[sub_resource type="CapsuleShape3D" id="CapsuleShape3D_cwfph"] -height = 1.0 - [node name="Michael" type="CharacterBody3D"] process_mode = 1 collision_layer = 10 @@ -413,18 +535,9 @@ disabled = true [node name="Raycast" type="RayCast3D" parent="."] unique_name_in_owner = true transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0, 0) -visible = false target_position = Vector3(0, 0, -3) collision_mask = 3 -[node name="AnimatedSprite" type="AnimatedSprite3D" parent="."] -unique_name_in_owner = true -billboard = 2 -double_sided = false -texture_filter = 0 -sprite_frames = SubResource("SpriteFrames_x7baa") -animation = &"idle_back_walk" - [node name="CollisionShape3D" type="CollisionShape3D" parent="."] transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0, 0) shape = SubResource("CapsuleShape3D_cwfph") @@ -434,3 +547,17 @@ unique_name_in_owner = true avoidance_enabled = true debug_enabled = true debug_path_custom_color = Color(1, 0, 0, 1) + +[node name="Sprite3D" type="Sprite3D" parent="."] +billboard = 2 +texture = SubResource("ViewportTexture_57rcc") + +[node name="SubViewport" type="SubViewport" parent="Sprite3D"] +transparent_bg = true +size = Vector2i(95, 95) + +[node name="AnimatedSprite" type="AnimatedSprite2D" parent="Sprite3D/SubViewport"] +unique_name_in_owner = true +position = Vector2(45, 45) +sprite_frames = SubResource("SpriteFrames_8xwq0") +animation = &"idle_front_walk" diff --git a/src/enemy/enemy_types/michael/RotationTest.cs b/src/enemy/enemy_types/michael/RotationTest.cs index 417a3c28..8dbb31f5 100644 --- a/src/enemy/enemy_types/michael/RotationTest.cs +++ b/src/enemy/enemy_types/michael/RotationTest.cs @@ -1,6 +1,4 @@ -using GameJamDungeon; using Godot; -using System; public partial class RotationTest : CharacterBody3D { diff --git a/src/enemy/enemy_types/sproingy/Sproingy.tscn b/src/enemy/enemy_types/sproingy/Sproingy.tscn index 0641dfd5..bd4441ca 100644 --- a/src/enemy/enemy_types/sproingy/Sproingy.tscn +++ b/src/enemy/enemy_types/sproingy/Sproingy.tscn @@ -1,18 +1,9 @@ -[gd_scene load_steps=67 format=3 uid="uid://bksq62muhk3h5"] +[gd_scene load_steps=63 format=3 uid="uid://bksq62muhk3h5"] [ext_resource type="Script" path="res://src/enemy/Enemy.cs" id="1_7tinp"] [ext_resource type="Script" path="res://src/enemy/EnemyStatResource.cs" id="2_j3knd"] [ext_resource type="Script" path="res://src/hitbox/Hitbox.cs" id="3_usw2d"] -[ext_resource type="Texture2D" uid="uid://dd0ia6isdqg61" path="res://src/enemy/enemy_types/sproingy/animations/ATTACK/Layer 1.png" id="4_f5aht"] -[ext_resource type="Texture2D" uid="uid://bs4ico5ouo5d3" path="res://src/enemy/enemy_types/sproingy/animations/ATTACK/Layer 2.png" id="5_cla00"] -[ext_resource type="Texture2D" uid="uid://85ki5mc4h0vs" path="res://src/enemy/enemy_types/sproingy/animations/ATTACK/Layer 3.png" id="6_d23kt"] -[ext_resource type="Texture2D" uid="uid://bwt1m2frb3r0e" path="res://src/enemy/enemy_types/sproingy/animations/ATTACK/Layer 4.png" id="7_1s02c"] -[ext_resource type="Texture2D" uid="uid://ckssl1np6vnlu" path="res://src/enemy/enemy_types/sproingy/animations/ATTACK/Layer 5.png" id="8_nxv3t"] -[ext_resource type="Texture2D" uid="uid://c4sqc6i3xcfac" path="res://src/enemy/enemy_types/sproingy/animations/ATTACK/Layer 6.png" id="9_l4m8n"] -[ext_resource type="Texture2D" uid="uid://cawexe4wkosc8" path="res://src/enemy/enemy_types/sproingy/animations/ATTACK/Layer 7.png" id="10_l11ks"] -[ext_resource type="Texture2D" uid="uid://d0j1vrx7xdnxl" path="res://src/enemy/enemy_types/sproingy/animations/ATTACK/Layer 8.png" id="11_1h5et"] -[ext_resource type="Texture2D" uid="uid://d0qhndaukki7c" path="res://src/enemy/enemy_types/sproingy/animations/ATTACK/Layer 9.png" id="12_iu7dx"] -[ext_resource type="Texture2D" uid="uid://cnd08q34wa7ww" path="res://src/enemy/enemy_types/sproingy/animations/ATTACK/Layer 10.png" id="13_vbxxn"] +[ext_resource type="Material" uid="uid://c0gpeve05njqq" path="res://src/vfx/shaders/DamageHit.tres" id="4_0urvb"] [ext_resource type="Texture2D" uid="uid://b8ntr7hh6rr5h" path="res://src/enemy/enemy_types/sproingy/animations/IDLE_WALK_BACK/Layer 1.png" id="14_xn75i"] [ext_resource type="Texture2D" uid="uid://csgthlou2tnvb" path="res://src/enemy/enemy_types/sproingy/animations/IDLE_WALK_BACK/Layer 2.png" id="15_rgyxs"] [ext_resource type="Texture2D" uid="uid://cfyxuk85350gn" path="res://src/enemy/enemy_types/sproingy/animations/IDLE_WALK_BACK/Layer 3.png" id="16_nr3ob"] @@ -49,8 +40,10 @@ [ext_resource type="Texture2D" uid="uid://dlbt7lj5ryl0v" path="res://src/enemy/enemy_types/sproingy/animations/IDLE_WALK_SIDE/Layer 4.png" id="47_1j2ir"] [ext_resource type="Texture2D" uid="uid://bkhn4ck7bx6tt" path="res://src/enemy/enemy_types/sproingy/animations/IDLE_WALK_SIDE/Layer 5.png" id="48_864dg"] [ext_resource type="Texture2D" uid="uid://c8uw6qdsi1720" path="res://src/enemy/enemy_types/sproingy/animations/IDLE_WALK_SIDE/Layer 6.png" id="49_2r4d5"] +[ext_resource type="Shader" path="res://src/vfx/shaders/DamageHit.gdshader" id="49_mrxyf"] [ext_resource type="Texture2D" uid="uid://cnoouhy7p3gi3" path="res://src/enemy/enemy_types/sproingy/animations/IDLE_WALK_SIDE/Layer 7.png" id="50_m33xn"] [ext_resource type="Texture2D" uid="uid://b1xldymngql00" path="res://src/enemy/enemy_types/sproingy/animations/IDLE_WALK_SIDE/Layer 8.png" id="51_68cc3"] +[ext_resource type="Shader" path="res://src/vfx/shaders/PixelMelt.gdshader" id="51_y2own"] [ext_resource type="Texture2D" uid="uid://btuxhmmb6ikvf" path="res://src/enemy/enemy_types/sproingy/animations/IDLE_WALK_SIDE/Layer 9.png" id="52_2xmc3"] [ext_resource type="Texture2D" uid="uid://dlrn3cbdubg5s" path="res://src/enemy/enemy_types/sproingy/animations/IDLE_WALK_SIDE/Layer 10.png" id="53_bgv07"] [ext_resource type="Texture2D" uid="uid://oukshrxpgscg" path="res://src/enemy/enemy_types/sproingy/animations/IDLE_WALK_SIDE/Layer 11.png" id="54_7gaxo"] @@ -86,85 +79,16 @@ height = 1.0 height = 3.0 radius = 1.0 -[sub_resource type="Animation" id="Animation_kr7ax"] -length = 0.001 -tracks/0/type = "value" -tracks/0/imported = false -tracks/0/enabled = true -tracks/0/path = NodePath("Hitbox/CollisionShape3D:disabled") -tracks/0/interp = 1 -tracks/0/loop_wrap = true -tracks/0/keys = { -"times": PackedFloat32Array(0), -"transitions": PackedFloat32Array(1), -"update": 1, -"values": [true] -} - -[sub_resource type="Animation" id="Animation_ce86e"] -resource_name = "attack" -length = 0.7 -tracks/0/type = "value" -tracks/0/imported = false -tracks/0/enabled = true -tracks/0/path = NodePath("Hitbox/CollisionShape3D:disabled") -tracks/0/interp = 1 -tracks/0/loop_wrap = true -tracks/0/keys = { -"times": PackedFloat32Array(0, 0.3, 0.5), -"transitions": PackedFloat32Array(1, 1, 1), -"update": 1, -"values": [true, false, true] -} - -[sub_resource type="AnimationLibrary" id="AnimationLibrary_346xs"] -_data = { -"RESET": SubResource("Animation_kr7ax"), -"attack": SubResource("Animation_ce86e") -} - [sub_resource type="BoxShape3D" id="BoxShape3D_0yire"] size = Vector3(1, 0.564941, 1.14453) -[sub_resource type="SpriteFrames" id="SpriteFrames_x7baa"] +[sub_resource type="ViewportTexture" id="ViewportTexture_moptw"] +viewport_path = NodePath("Sprite3D/SubViewport") + +[sub_resource type="SpriteFrames" id="SpriteFrames_mi16l"] animations = [{ "frames": [{ "duration": 1.0, -"texture": ExtResource("4_f5aht") -}, { -"duration": 1.0, -"texture": ExtResource("5_cla00") -}, { -"duration": 1.0, -"texture": ExtResource("6_d23kt") -}, { -"duration": 1.0, -"texture": ExtResource("7_1s02c") -}, { -"duration": 1.0, -"texture": ExtResource("8_nxv3t") -}, { -"duration": 1.0, -"texture": ExtResource("9_l4m8n") -}, { -"duration": 1.0, -"texture": ExtResource("10_l11ks") -}, { -"duration": 1.0, -"texture": ExtResource("11_1h5et") -}, { -"duration": 1.0, -"texture": ExtResource("12_iu7dx") -}, { -"duration": 1.0, -"texture": ExtResource("13_vbxxn") -}], -"loop": true, -"name": &"attack", -"speed": 12.0 -}, { -"frames": [{ -"duration": 1.0, "texture": ExtResource("14_xn75i") }, { "duration": 1.0, @@ -314,6 +238,159 @@ animations = [{ "speed": 12.0 }] +[sub_resource type="Animation" id="Animation_kr7ax"] +length = 0.001 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("Hitbox/CollisionShape3D:disabled") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [true] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("Sprite3D/SubViewport/AnimatedSprite:material") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [null] +} +tracks/2/type = "value" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath("Sprite3D/SubViewport/AnimatedSprite:material:shader") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [ExtResource("49_mrxyf")] +} +tracks/3/type = "value" +tracks/3/imported = false +tracks/3/enabled = true +tracks/3/path = NodePath("Sprite3D/SubViewport/AnimatedSprite:material:shader_parameter/progress") +tracks/3/interp = 1 +tracks/3/loop_wrap = true +tracks/3/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 0, +"values": [-1.0] +} + +[sub_resource type="Animation" id="Animation_ce86e"] +resource_name = "attack" +length = 0.7 +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("Hitbox/CollisionShape3D:disabled") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 0.3, 0.5), +"transitions": PackedFloat32Array(1, 1, 1), +"update": 1, +"values": [true, false, true] +} + +[sub_resource type="Animation" id="Animation_8p0l1"] +resource_name = "defeated" +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("Sprite3D/SubViewport/AnimatedSprite:material") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [ExtResource("4_0urvb")] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("Sprite3D/SubViewport/AnimatedSprite:material:shader") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0), +"transitions": PackedFloat32Array(1), +"update": 1, +"values": [ExtResource("51_y2own")] +} +tracks/2/type = "value" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath("Sprite3D/SubViewport/AnimatedSprite:material:shader_parameter/progress") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/keys = { +"times": PackedFloat32Array(0, 1), +"transitions": PackedFloat32Array(1, 1), +"update": 0, +"values": [0.0, 1.0] +} + +[sub_resource type="Animation" id="Animation_swr3w"] +resource_name = "hit" +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("Sprite3D/SubViewport/AnimatedSprite:material") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 1), +"transitions": PackedFloat32Array(1, 1), +"update": 1, +"values": [ExtResource("4_0urvb"), ExtResource("4_0urvb")] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("Sprite3D/SubViewport/AnimatedSprite:material:shader") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0, 1), +"transitions": PackedFloat32Array(1, 1), +"update": 1, +"values": [ExtResource("49_mrxyf"), null] +} +tracks/2/type = "value" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath("Sprite3D/SubViewport/AnimatedSprite:material:shader_parameter/progress") +tracks/2/interp = 1 +tracks/2/loop_wrap = true +tracks/2/keys = { +"times": PackedFloat32Array(0, 1), +"transitions": PackedFloat32Array(1, 1), +"update": 0, +"values": [0.0, 1.0] +} + +[sub_resource type="AnimationLibrary" id="AnimationLibrary_346xs"] +_data = { +"RESET": SubResource("Animation_kr7ax"), +"attack": SubResource("Animation_ce86e"), +"defeated": SubResource("Animation_8p0l1"), +"hit": SubResource("Animation_swr3w") +} + [node name="Sproingy" type="CharacterBody3D"] process_mode = 1 collision_layer = 10 @@ -352,12 +429,6 @@ unique_name_in_owner = true wait_time = 1.8 autostart = true -[node name="AnimationPlayer" type="AnimationPlayer" parent="."] -unique_name_in_owner = true -libraries = { -"": SubResource("AnimationLibrary_346xs") -} - [node name="Hitbox" type="Area3D" parent="."] transform = Transform3D(-1, 0, -8.74228e-08, 0, 1, 0, 8.74228e-08, 0, -1, 0, 0, 0) collision_layer = 64 @@ -376,9 +447,25 @@ visible = false target_position = Vector3(0, 0, -3) collision_mask = 3 -[node name="AnimatedSprite" type="AnimatedSprite3D" parent="."] -unique_name_in_owner = true +[node name="Sprite3D" type="Sprite3D" parent="."] +transform = Transform3D(1.5, 0, 0, 0, 1.5, 0, 0, 0, 1.5, 0, 0, 0) +flip_v = true billboard = 2 -texture_filter = 0 -sprite_frames = SubResource("SpriteFrames_x7baa") -animation = &"attack" +texture = SubResource("ViewportTexture_moptw") + +[node name="SubViewport" type="SubViewport" parent="Sprite3D"] +transparent_bg = true +size = Vector2i(95, 95) + +[node name="AnimatedSprite" type="AnimatedSprite2D" parent="Sprite3D/SubViewport"] +unique_name_in_owner = true +position = Vector2(45, 45) +sprite_frames = SubResource("SpriteFrames_mi16l") +animation = &"idle_left_walk" +flip_v = true + +[node name="AnimationPlayer" type="AnimationPlayer" parent="."] +unique_name_in_owner = true +libraries = { +"": SubResource("AnimationLibrary_346xs") +} diff --git a/src/enemy/state/EnemyLogic.Input.cs b/src/enemy/state/EnemyLogic.Input.cs index 0f1b89f3..dfac1ae8 100644 --- a/src/enemy/state/EnemyLogic.Input.cs +++ b/src/enemy/state/EnemyLogic.Input.cs @@ -15,7 +15,7 @@ namespace GameJamDungeon public readonly record struct HitByPlayer(double Damage); - public readonly record struct Killed(); + public readonly record struct EnemyDefeated(); public readonly record struct PatrolToRandomSpot(Vector3 PatrolTarget); diff --git a/src/enemy/state/EnemyLogic.Output.cs b/src/enemy/state/EnemyLogic.Output.cs index 5fe8d8ca..3c417bce 100644 --- a/src/enemy/state/EnemyLogic.Output.cs +++ b/src/enemy/state/EnemyLogic.Output.cs @@ -12,7 +12,7 @@ namespace GameJamDungeon public readonly record struct HitByPlayer(double CurrentHP); - public readonly record struct Die(); + public readonly record struct Defeated(); } } } diff --git a/src/enemy/state/EnemyLogic.g.puml b/src/enemy/state/EnemyLogic.g.puml index c23970e7..9b3e0cc1 100644 --- a/src/enemy/state/EnemyLogic.g.puml +++ b/src/enemy/state/EnemyLogic.g.puml @@ -7,6 +7,7 @@ state "EnemyLogic State" as GameJamDungeon_EnemyLogic_State { } } +GameJamDungeon_EnemyLogic_State_Alive --> GameJamDungeon_EnemyLogic_State_Alive : EnemyDefeated GameJamDungeon_EnemyLogic_State_Alive --> GameJamDungeon_EnemyLogic_State_Alive : HitByPlayer GameJamDungeon_EnemyLogic_State_Alive --> GameJamDungeon_EnemyLogic_State_Attack : AttackTimer GameJamDungeon_EnemyLogic_State_Attack --> GameJamDungeon_EnemyLogic_State_FollowPlayer : Alerted @@ -16,6 +17,7 @@ GameJamDungeon_EnemyLogic_State_Idle --> GameJamDungeon_EnemyLogic_State_FollowP GameJamDungeon_EnemyLogic_State_Idle --> GameJamDungeon_EnemyLogic_State_Idle : PatrolToRandomSpot GameJamDungeon_EnemyLogic_State_Idle --> GameJamDungeon_EnemyLogic_State_Idle : PhysicsTick +GameJamDungeon_EnemyLogic_State_Alive : OnEnemyDefeated → Defeated GameJamDungeon_EnemyLogic_State_Alive : OnHitByPlayer → HitByPlayer GameJamDungeon_EnemyLogic_State_Idle : OnPhysicsTick → MovementComputed diff --git a/src/enemy/state/states/EnemyLogic.State.Alive.cs b/src/enemy/state/states/EnemyLogic.State.Alive.cs index b74aec0d..96a84425 100644 --- a/src/enemy/state/states/EnemyLogic.State.Alive.cs +++ b/src/enemy/state/states/EnemyLogic.State.Alive.cs @@ -8,7 +8,7 @@ namespace GameJamDungeon public partial record State { [Meta, Id("enemy_logic_state_alive")] - public abstract partial record Alive : State, IGet, IGet + public abstract partial record Alive : State, IGet, IGet, IGet { public Transition On(in Input.HitByPlayer input) { @@ -26,6 +26,12 @@ namespace GameJamDungeon { return To(); } + + public Transition On(in Input.EnemyDefeated input) + { + Output(new Output.Defeated()); + return ToSelf(); + } } } } diff --git a/src/items/weapons/resources/Jiblett.tres b/src/items/weapons/resources/Jiblett.tres index 18b0f0b4..012366a9 100644 --- a/src/items/weapons/resources/Jiblett.tres +++ b/src/items/weapons/resources/Jiblett.tres @@ -1,4 +1,4 @@ -[gd_resource type="Resource" script_class="WeaponInfo" load_steps=3 format=3 uid="uid://c1bg0o7nmu2xw"] +[gd_resource type="Resource" script_class="WeaponStats" load_steps=3 format=3 uid="uid://c1bg0o7nmu2xw"] [ext_resource type="Texture2D" uid="uid://cil3xe3jq82r6" path="res://src/items/weapons/textures/JIBLETT.PNG" id="1_ifm43"] [ext_resource type="Script" path="res://src/items/weapons/WeaponStats.cs" id="1_re512"] diff --git a/src/items/weapons/resources/Sword Sword Odette.tres b/src/items/weapons/resources/Sword Sword Odette.tres index 98e55ea2..e60fd69e 100644 --- a/src/items/weapons/resources/Sword Sword Odette.tres +++ b/src/items/weapons/resources/Sword Sword Odette.tres @@ -1,4 +1,4 @@ -[gd_resource type="Resource" script_class="WeaponInfo" load_steps=3 format=3 uid="uid://bpdbuf0k0exb5"] +[gd_resource type="Resource" script_class="WeaponStats" load_steps=3 format=3 uid="uid://bpdbuf0k0exb5"] [ext_resource type="Script" path="res://src/items/weapons/WeaponStats.cs" id="1_cik6n"] [ext_resource type="Texture2D" uid="uid://cvtcsi2sagfwm" path="res://src/items/weapons/textures/SWAN SWORD.PNG" id="1_qc4eu"] @@ -20,4 +20,4 @@ Ignores Affinity. The blade of a thousand faced heroine." Texture = ExtResource("1_qc4eu") -SpawnRate = 0.5 +SpawnRate = 0.01 diff --git a/src/map/Map.tscn b/src/map/Map.tscn index 21b70dd4..e2e87ca8 100644 --- a/src/map/Map.tscn +++ b/src/map/Map.tscn @@ -1,8 +1,9 @@ -[gd_scene load_steps=5 format=3 uid="uid://by67pn7fdsg1m"] +[gd_scene load_steps=6 format=3 uid="uid://by67pn7fdsg1m"] [ext_resource type="PackedScene" uid="uid://dvnc26rebk6o0" path="res://src/map/overworld/Overworld.tscn" id="1_ope1x"] [ext_resource type="PackedScene" uid="uid://bc1sp6xwe0j65" path="res://src/map/dungeon/floors/Floor1.tscn" id="2_merfv"] [ext_resource type="PackedScene" uid="uid://dcgj5i52i76gj" path="res://src/enemy/enemy_types/michael/Michael.tscn" id="4_d4sw2"] +[ext_resource type="PackedScene" uid="uid://bksq62muhk3h5" path="res://src/enemy/enemy_types/sproingy/Sproingy.tscn" id="5_cvt4a"] [ext_resource type="PackedScene" uid="uid://bjqgl5u05ia04" path="res://src/map/dungeon/Teleport.tscn" id="5_jiohg"] [node name="Map" type="Node3D"] @@ -27,3 +28,6 @@ disable_mode = 2 [node name="Michael" parent="." instance=ExtResource("4_d4sw2")] transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 4.75859, 2.28929, -4.31558) + +[node name="Sproingy" parent="." instance=ExtResource("5_cvt4a")] +transform = Transform3D(1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 2.56057, 0) diff --git a/src/player/Player.cs b/src/player/Player.cs index 599fb50a..3bf2e2cf 100644 --- a/src/player/Player.cs +++ b/src/player/Player.cs @@ -1,8 +1,6 @@ using Chickensoft.AutoInject; -using Chickensoft.Collections; using Chickensoft.GodotNodeInterfaces; using Chickensoft.Introspection; -using Chickensoft.LogicBlocks; using Chickensoft.SaveFileBuilder; using Godot; diff --git a/src/player/Player.tscn b/src/player/Player.tscn index 12e5e9df..d3eba223 100644 --- a/src/player/Player.tscn +++ b/src/player/Player.tscn @@ -285,4 +285,3 @@ position = Vector2(900, 565) scale = Vector2(1.8, 1.8) sprite_frames = SubResource("SpriteFrames_ywvvo") animation = &"attack" -frame = 6 diff --git a/src/vfx/shaders/DamageHit.gdshader b/src/vfx/shaders/DamageHit.gdshader new file mode 100644 index 00000000..cb322244 --- /dev/null +++ b/src/vfx/shaders/DamageHit.gdshader @@ -0,0 +1,29 @@ +shader_type canvas_item; + +/** +Color to use for the shock. +*/ +uniform vec3 shock_color : source_color = vec3(1.0, 0.0, 0.0); +/** +Initial amplitude of the shock. This will start at this amplitude and +gradually attenuate. +*/ +uniform float amplitude = 30.0; + +uniform float progress: hint_range(0.0, 1.0) = -1.0; +/** +How fast shold it move side to side, more frequency means it'll move more quickly +side to side. +*/ +uniform float frequecy = 10.0; + +void vertex() { + float exponent = mod(progress, 3.0); + VERTEX.x += amplitude * exp(-3.0*exponent) * sin(frequecy*exponent); +} + +void fragment() { + float exponent = mod(progress, 3.0); + vec3 normal_color = texture(TEXTURE, UV).rgb; + COLOR.rgb = normal_color + shock_color * exp(-3.0*exponent); +} diff --git a/src/vfx/shaders/DamageHit.tres b/src/vfx/shaders/DamageHit.tres new file mode 100644 index 00000000..213dbe26 --- /dev/null +++ b/src/vfx/shaders/DamageHit.tres @@ -0,0 +1,8 @@ +[gd_resource type="ShaderMaterial" load_steps=2 format=3 uid="uid://c0gpeve05njqq"] + +[ext_resource type="Shader" path="res://src/vfx/shaders/PixelMelt.gdshader" id="1_uy7cv"] + +[resource] +shader = ExtResource("1_uy7cv") +shader_parameter/progress = 0.0 +shader_parameter/meltiness = 1.0 diff --git a/src/vfx/shaders/PixelMelt.gdshader b/src/vfx/shaders/PixelMelt.gdshader new file mode 100644 index 00000000..d5183d0b --- /dev/null +++ b/src/vfx/shaders/PixelMelt.gdshader @@ -0,0 +1,28 @@ +shader_type canvas_item; + +// Animate from 0 to 1 +uniform float progress: hint_range(0.0, 1.0) = 0.0; + +// How jagged each band of melting pixels are +uniform float meltiness: hint_range(0.0, 16.0) = 1.0; + +float psuedo_rand(float x) { + return mod(x * 2048103.0 + cos(x * 1912.0), 1.0); +} + +void fragment() { + vec2 uv = UV; + + // Move pixels near the top faster + uv.y -= progress / UV.y; + + // Created jagged edges for each pixel on the x-axis + uv.y -= progress * meltiness * psuedo_rand(UV.x - mod(UV.x, TEXTURE_PIXEL_SIZE.x)); + + COLOR = texture(TEXTURE, uv); + + // "delete" pixels out of range + if (uv.y <= 0.0) { + COLOR.a = 0.0; + } +} diff --git a/src/vfx/shaders/PixelMelt.tres b/src/vfx/shaders/PixelMelt.tres new file mode 100644 index 00000000..bf0adf56 --- /dev/null +++ b/src/vfx/shaders/PixelMelt.tres @@ -0,0 +1,10 @@ +[gd_resource type="ShaderMaterial" load_steps=2 format=3 uid="uid://yvt4d1xnftll"] + +[ext_resource type="Shader" path="res://src/vfx/shaders/DamageHit.gdshader" id="1_a8e00"] + +[resource] +shader = ExtResource("1_a8e00") +shader_parameter/shock_color = Color(1, 0, 0, 1) +shader_parameter/amplitude = 30.0 +shader_parameter/progress = 0.0 +shader_parameter/frequecy = 10.0