diff --git a/Zennysoft.Game.Ma.Implementation/Components/ISigil.cs b/Zennysoft.Game.Ma.Implementation/Components/ISigil.cs new file mode 100644 index 00000000..81471df7 --- /dev/null +++ b/Zennysoft.Game.Ma.Implementation/Components/ISigil.cs @@ -0,0 +1,26 @@ +using Godot; +using Zennysoft.Ma.Adapter; + +namespace Zennysoft.Ma; + public interface ISigil : IEntityComponent + { + public double AttackModifier { get; set; } + + [Export] + public double DefenseModifier { get; set; } + + [Export] + public int HealthModifier { get; set; } + + [Export] + public int VTModifier { get; set; } + + [Export] + public double LuckModifier { get; set; } + + [Export] + public double ElementalModifier { get; set; } + + [Export] + public ElementType ElementType { get; set; } +} diff --git a/Zennysoft.Game.Ma.Implementation/Components/ISigilComponent.cs b/Zennysoft.Game.Ma.Implementation/Components/ISigilComponent.cs new file mode 100644 index 00000000..a2837d0a --- /dev/null +++ b/Zennysoft.Game.Ma.Implementation/Components/ISigilComponent.cs @@ -0,0 +1,8 @@ +namespace Zennysoft.Ma; + +public interface ISigilComponent +{ + ISigil Sigil { get; set; } + + public void Reset(); +} diff --git a/Zennysoft.Game.Ma.Implementation/Player/IPlayer.cs b/Zennysoft.Game.Ma.Implementation/Player/IPlayer.cs index 8a5219a9..e28f8d00 100644 --- a/Zennysoft.Game.Ma.Implementation/Player/IPlayer.cs +++ b/Zennysoft.Game.Ma.Implementation/Player/IPlayer.cs @@ -28,6 +28,8 @@ public interface IPlayer : IKillable, ICharacterBody3D public void PlayJumpScareAnimation(); + public void SetSigil(ISigil sigil); + public void ApplyNewAugment(IAugmentItem jewel, IAugmentableItem equipableItem); public IBaseInventoryItem IdentifyItem(IBaseInventoryItem unidentifiedItem); @@ -50,6 +52,8 @@ public interface IPlayer : IKillable, ICharacterBody3D public IStatusEffectComponent StatusEffectComponent { get; } + public ISigilComponent SigilComponent { get; } + public void SetHealthTimerStatus(bool isActive); public void ModifyHealthTimerSpeed(float newModifier); diff --git a/Zennysoft.Game.Ma/src/items/Barrier.tscn b/Zennysoft.Game.Ma/src/items/Barrier.tscn index 915d387b..3f276f27 100644 --- a/Zennysoft.Game.Ma/src/items/Barrier.tscn +++ b/Zennysoft.Game.Ma/src/items/Barrier.tscn @@ -208,45 +208,6 @@ tracks/2/keys = { "values": [1.0] } -[sub_resource type="Animation" id="Animation_dvre5"] -resource_name = "fade_out" -tracks/0/type = "value" -tracks/0/imported = false -tracks/0/enabled = true -tracks/0/path = NodePath("BOTTOM:transparency") -tracks/0/interp = 1 -tracks/0/loop_wrap = true -tracks/0/keys = { -"times": PackedFloat32Array(0, 1), -"transitions": PackedFloat32Array(1, 1), -"update": 0, -"values": [0.0, 1.0] -} -tracks/1/type = "value" -tracks/1/imported = false -tracks/1/enabled = true -tracks/1/path = NodePath("MIDDLE:transparency") -tracks/1/interp = 1 -tracks/1/loop_wrap = true -tracks/1/keys = { -"times": PackedFloat32Array(0, 1), -"transitions": PackedFloat32Array(1, 1), -"update": 0, -"values": [0.0, 1.0] -} -tracks/2/type = "value" -tracks/2/imported = false -tracks/2/enabled = true -tracks/2/path = NodePath("TOP:transparency") -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_5ayrc"] resource_name = "fade_in" tracks/0/type = "value" @@ -286,6 +247,45 @@ tracks/2/keys = { "values": [1.0, 0.0] } +[sub_resource type="Animation" id="Animation_dvre5"] +resource_name = "fade_out" +tracks/0/type = "value" +tracks/0/imported = false +tracks/0/enabled = true +tracks/0/path = NodePath("BOTTOM:transparency") +tracks/0/interp = 1 +tracks/0/loop_wrap = true +tracks/0/keys = { +"times": PackedFloat32Array(0, 1), +"transitions": PackedFloat32Array(1, 1), +"update": 0, +"values": [0.0, 1.0] +} +tracks/1/type = "value" +tracks/1/imported = false +tracks/1/enabled = true +tracks/1/path = NodePath("MIDDLE:transparency") +tracks/1/interp = 1 +tracks/1/loop_wrap = true +tracks/1/keys = { +"times": PackedFloat32Array(0, 1), +"transitions": PackedFloat32Array(1, 1), +"update": 0, +"values": [0.0, 1.0] +} +tracks/2/type = "value" +tracks/2/imported = false +tracks/2/enabled = true +tracks/2/path = NodePath("TOP:transparency") +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_5ayrc"] _data = { &"RESET": SubResource("Animation_ijggi"), diff --git a/Zennysoft.Game.Ma/src/menu/DebugMenu.tscn b/Zennysoft.Game.Ma/src/menu/DebugMenu.tscn index ec7b0bbd..b65b870f 100644 --- a/Zennysoft.Game.Ma/src/menu/DebugMenu.tscn +++ b/Zennysoft.Game.Ma/src/menu/DebugMenu.tscn @@ -1,4 +1,4 @@ -[gd_scene load_steps=5 format=3 uid="uid://8f3dk16nj0dn"] +[gd_scene load_steps=6 format=3 uid="uid://8f3dk16nj0dn"] [ext_resource type="Script" uid="uid://l1v4ppubryd3" path="res://src/ui/pause_menu/PauseDebugMenu.cs" id="1_a7f7f"] [ext_resource type="FontFile" uid="uid://dp1k143v7cppw" path="res://src/ui/fonts/Lust_Sans_Regular.otf" id="1_dan2i"] @@ -6,6 +6,8 @@ [sub_resource type="StyleBoxFlat" id="StyleBoxFlat_1ctjd"] +[sub_resource type="StyleBoxFlat" id="StyleBoxFlat_dan2i"] + [node name="Control" type="Control"] process_mode = 3 top_level = true @@ -81,8 +83,8 @@ label_settings = ExtResource("2_a7f7f") unique_name_in_owner = true layout_mode = 2 focus_neighbor_top = NodePath("../SpawnItemDropDown") -focus_neighbor_bottom = NodePath(".") -theme_override_styles/normal = SubResource("StyleBoxFlat_1ctjd") +focus_neighbor_bottom = NodePath("../SigilDropDown") +theme_override_styles/normal = SubResource("StyleBoxFlat_dan2i") item_count = 15 popup/item_0/text = "Sproingy" popup/item_0/id = 0 @@ -115,6 +117,23 @@ popup/item_13/id = 13 popup/item_14/text = "Gold Sproingy" popup/item_14/id = 14 +[node name="Label3" type="Label" parent="MarginContainer/VBoxContainer/HBoxContainer/VFlowContainer"] +layout_mode = 2 +text = "Sigil:" +label_settings = ExtResource("2_a7f7f") + +[node name="SigilDropDown" type="OptionButton" parent="MarginContainer/VBoxContainer/HBoxContainer/VFlowContainer"] +unique_name_in_owner = true +layout_mode = 2 +focus_neighbor_top = NodePath("../SpawnEnemyDropDown") +focus_neighbor_bottom = NodePath("../LoadNextFloorButton") +theme_override_styles/normal = SubResource("StyleBoxFlat_1ctjd") +flat = true +allow_reselect = true +item_count = 1 +popup/item_0/text = "None" +popup/item_0/id = 0 + [node name="LoadNextFloorButton" type="Button" parent="MarginContainer/VBoxContainer/HBoxContainer/VFlowContainer"] unique_name_in_owner = true layout_mode = 2 diff --git a/Zennysoft.Game.Ma/src/player/DummyPlayer.cs b/Zennysoft.Game.Ma/src/player/DummyPlayer.cs index 82e7edfc..00a61cda 100644 --- a/Zennysoft.Game.Ma/src/player/DummyPlayer.cs +++ b/Zennysoft.Game.Ma/src/player/DummyPlayer.cs @@ -1,5 +1,6 @@ using Godot; using System; +using Zennysoft.Ma; using Zennysoft.Ma.Adapter; namespace Zennysoft.Game.Ma; @@ -24,6 +25,10 @@ public partial class DummyPlayer : CharacterBody3D, IPlayer public IStatusEffectComponent StatusEffectComponent { get; } public bool BriefImmunity { get => throw new NotImplementedException(); set => throw new NotImplementedException(); } + public ISigil SigilComponent => throw new NotImplementedException(); + + ISigilComponent IPlayer.SigilComponent => throw new NotImplementedException(); + public event Action PlayerDied; public void Activate() => throw new NotImplementedException(); @@ -43,4 +48,5 @@ public partial class DummyPlayer : CharacterBody3D, IPlayer public void Unequip(IEquipableItem equipable) => throw new NotImplementedException(); public IBaseInventoryItem IdentifyItem(IBaseInventoryItem unidentifiedItem) => throw new NotImplementedException(); public void EnactBriefImmunity() => throw new NotImplementedException(); + public void SetSigil(ISigil sigil) => throw new NotImplementedException(); } diff --git a/Zennysoft.Game.Ma/src/player/Player.cs b/Zennysoft.Game.Ma/src/player/Player.cs index 58a451fc..e2306eae 100644 --- a/Zennysoft.Game.Ma/src/player/Player.cs +++ b/Zennysoft.Game.Ma/src/player/Player.cs @@ -5,6 +5,7 @@ using Godot; using SimpleInjector; using System; using System.Linq; +using Zennysoft.Ma; using Zennysoft.Ma.Adapter; using Zennysoft.Ma.Adapter.Entity; @@ -39,6 +40,8 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide public IStatusEffectComponent StatusEffectComponent { get; private set; } + public ISigilComponent SigilComponent { get; set; } + public Vector3 CurrentPosition => GlobalPosition; public Basis CurrentBasis => Transform.Basis; @@ -167,6 +170,7 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide LuckComponent = new LuckComponent(InitialLuck); EquipmentComponent = new EquipmentComponent(); StatusEffectComponent = new StatusEffectComponent(RustDuration); + SigilComponent = new SigilComponent(); _itemReroller = new ItemReroller(ItemDatabase.Instance); _playerEffectService = new PlayerEffectService(this); @@ -200,6 +204,7 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide ExperiencePointsComponent.Reset(); LuckComponent.Reset(); EquipmentComponent.Reset(); + SigilComponent.Reset(); HealthTimer.Timeout += OnHealthTimerTimeout; } @@ -292,7 +297,8 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide _camera3D.AddShake(1.0f); TakeDamageAnimationPlayer.Play("take_damage"); - var damageReceived = DamageCalculator.CalculateDamage(damage, TotalDefense, EquipmentComponent.ElementalResistance); + var defense = TotalDefense * SigilComponent.Sigil.DefenseModifier; + var damageReceived = DamageCalculator.CalculateDamage(damage, defense, EquipmentComponent.ElementalResistance); HealthComponent.Damage(damageReceived, damage.ElementType); SfxDatabase.Instance.Play(SoundEffect.TakeDamage); @@ -303,6 +309,11 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide } } + public void SetSigil(ISigil sigil) + { + SigilComponent.Sigil = sigil; + } + public void Knockback(float impulse) { _knockbackStrength = impulse; @@ -899,14 +910,20 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide var isCriticalHit = BattleExtensions.IsCriticalHit(TotalLuck); var totalDamage = TotalAttack; - if (isCriticalHit) + if (SigilComponent.Sigil.ElementType == weapon.WeaponElement) + totalDamage = Mathf.RoundToInt(totalDamage * 1.15f); + + totalDamage = Mathf.RoundToInt(totalDamage * SigilComponent.Sigil.AttackModifier); + + if (isCriticalHit) { totalDamage += (int)(totalDamage * 0.5f); SfxDatabase.Instance.Play(SoundEffect.Crit); } var baseAttack = new AttackData(totalDamage, weapon.WeaponElement, weapon.WeaponTag == WeaponTag.IgnoreDefense, weapon.WeaponTag == WeaponTag.IgnoreAffinity); - var damageDealt = DamageCalculator.CalculateDamage(baseAttack, enemy.DefenseComponent.CurrentDefense.Value, enemy.ElementalResistanceSet); + + var damageDealt = DamageCalculator.CalculateDamage(baseAttack, enemy.DefenseComponent.CurrentDefense.Value, enemy.ElementalResistanceSet); enemy.HealthComponent.Damage(damageDealt, weapon.WeaponElement); if (weapon.WeaponTag == WeaponTag.Knockback && enemy is IKnockbackable knockbackable) diff --git a/Zennysoft.Game.Ma/src/sigil/FireSigil.tres b/Zennysoft.Game.Ma/src/sigil/FireSigil.tres new file mode 100644 index 00000000..cc936ee8 --- /dev/null +++ b/Zennysoft.Game.Ma/src/sigil/FireSigil.tres @@ -0,0 +1,14 @@ +[gd_resource type="Resource" script_class="Sigil" load_steps=2 format=3 uid="uid://bxuswp8ril05h"] + +[ext_resource type="Script" uid="uid://cp5pnpqbde161" path="res://src/sigil/Sigil.cs" id="1_ib6yo"] + +[resource] +script = ExtResource("1_ib6yo") +AttackModifier = 1.25 +DefenseModifier = 0.75 +HealthModifier = 0 +VTModifier = 0 +LuckModifier = 0.0 +ElementalModifier = 0.0 +ElementType = 4 +metadata/_custom_type_script = "uid://cp5pnpqbde161" diff --git a/Zennysoft.Game.Ma/src/sigil/Sigil.cs b/Zennysoft.Game.Ma/src/sigil/Sigil.cs new file mode 100644 index 00000000..9a02902d --- /dev/null +++ b/Zennysoft.Game.Ma/src/sigil/Sigil.cs @@ -0,0 +1,39 @@ +using Godot; +using Zennysoft.Ma; +using Zennysoft.Ma.Adapter; + +[GlobalClass] +public partial class Sigil : Resource, ISigil +{ + [Export] + public double AttackModifier { get; set; } = 1f; + + [Export] + public double DefenseModifier { get; set; } = 1f; + + [Export] + public int HealthModifier { get; set; } + + [Export] + public int VTModifier { get; set; } + + [Export] + public double LuckModifier { get; set; } + + [Export] + public double ElementalModifier { get; set; } = 1; + + [Export] + public ElementType ElementType { get; set; } = ElementType.None; + + public void Reset() + { + AttackModifier = 1; + DefenseModifier = 1; + HealthModifier = 0; + VTModifier = 0; + LuckModifier = 0; + ElementalModifier = 1; + ElementType = ElementType.None; + } + } diff --git a/Zennysoft.Game.Ma/src/sigil/Sigil.cs.uid b/Zennysoft.Game.Ma/src/sigil/Sigil.cs.uid new file mode 100644 index 00000000..b222b789 --- /dev/null +++ b/Zennysoft.Game.Ma/src/sigil/Sigil.cs.uid @@ -0,0 +1 @@ +uid://cp5pnpqbde161 diff --git a/Zennysoft.Game.Ma/src/sigil/SigilComponent.cs b/Zennysoft.Game.Ma/src/sigil/SigilComponent.cs new file mode 100644 index 00000000..2b70f679 --- /dev/null +++ b/Zennysoft.Game.Ma/src/sigil/SigilComponent.cs @@ -0,0 +1,11 @@ +using Zennysoft.Ma; + +public class SigilComponent : ISigilComponent +{ + public ISigil Sigil { get; set; } + + public void Reset() + { + Sigil = new Sigil(); + } +} diff --git a/Zennysoft.Game.Ma/src/sigil/SigilComponent.cs.uid b/Zennysoft.Game.Ma/src/sigil/SigilComponent.cs.uid new file mode 100644 index 00000000..d2d45950 --- /dev/null +++ b/Zennysoft.Game.Ma/src/sigil/SigilComponent.cs.uid @@ -0,0 +1 @@ +uid://dr6mlubpgujk5 diff --git a/Zennysoft.Game.Ma/src/ui/pause_menu/PauseDebugMenu.cs b/Zennysoft.Game.Ma/src/ui/pause_menu/PauseDebugMenu.cs index 78a6bdb4..6ac4ef84 100644 --- a/Zennysoft.Game.Ma/src/ui/pause_menu/PauseDebugMenu.cs +++ b/Zennysoft.Game.Ma/src/ui/pause_menu/PauseDebugMenu.cs @@ -24,6 +24,8 @@ public partial class PauseDebugMenu : Control, IDebugMenu [Node] public OptionButton SpawnEnemyDropDown { get; set; } = default!; + [Node] public OptionButton SigilDropDown { get; set; } = default!; + [Node] public Button LoadNextFloorButton { get; set; } = default!; [Node] public Button RustButton { get; set; } = default!; @@ -34,6 +36,7 @@ public partial class PauseDebugMenu : Control, IDebugMenu private readonly string _floorFilePath = @"res://src/map/dungeon/floors/"; private readonly string _enemyFilePath = @"res://src/enemy/enemy_types"; + private readonly string _sigilFilePath = @"res://src/sigil"; private ImmutableList _spawnableItems; private ImmutableList _spawnableEnemies; @@ -80,6 +83,11 @@ public partial class PauseDebugMenu : Control, IDebugMenu FloorSelectDropDown.AddItem(folder + "/" + file); } + var sigils = DirAccess.GetFilesAt(_sigilFilePath).Where(x => x.EndsWith(".tres")); + foreach (var sigil in sigils) + SigilDropDown.AddItem(sigil); + SigilDropDown.Select(0); + FloorSelectDropDown.AllowReselect = true; SpawnItemDropDown.AllowReselect = true; SpawnEnemyDropDown.AllowReselect = true; @@ -88,9 +96,25 @@ public partial class PauseDebugMenu : Control, IDebugMenu SpawnItemDropDown.ItemSelected += SpawnItemDropDown_ItemSelected; SpawnEnemyDropDown.ItemSelected += SpawnEnemyDropDown_ItemSelected; + SigilDropDown.ItemSelected += SigilDropDown_ItemSelected; + DebugInfoCheckbox.Pressed += DebugInfoCheckbox_Pressed; } + private void SigilDropDown_ItemSelected(long index) + { + var sigilName = SigilDropDown.GetItemText((int)index); + if (sigilName == "None") + { + _player.SigilComponent.Reset(); + } + else + { + var sigilComponent = ResourceLoader.Load(_sigilFilePath + "/" + sigilName); + _player.SetSigil(sigilComponent); + } + } + private void DebugInfoCheckbox_Pressed() => _game.ShowDebugInfo(DebugInfoCheckbox.ButtonPressed); public bool DebugOverlayVisible => DebugInfoCheckbox.ButtonPressed;