Basic sigil system implementation

This commit is contained in:
2026-05-02 00:35:49 -07:00
parent 9e7678ce2a
commit 4460fd28f5
13 changed files with 215 additions and 45 deletions

View File

@@ -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; }
}

View File

@@ -0,0 +1,8 @@
namespace Zennysoft.Ma;
public interface ISigilComponent
{
ISigil Sigil { get; set; }
public void Reset();
}

View File

@@ -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);

View File

@@ -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"),

View File

@@ -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

View File

@@ -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();
}

View File

@@ -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<IPlayer>
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<IPlayer>
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<IPlayer>
ExperiencePointsComponent.Reset();
LuckComponent.Reset();
EquipmentComponent.Reset();
SigilComponent.Reset();
HealthTimer.Timeout += OnHealthTimerTimeout;
}
@@ -292,7 +297,8 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide<IPlayer>
_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<IPlayer>
}
}
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<IPlayer>
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)

View File

@@ -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"

View File

@@ -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;
}
}

View File

@@ -0,0 +1 @@
uid://cp5pnpqbde161

View File

@@ -0,0 +1,11 @@
using Zennysoft.Ma;
public class SigilComponent : ISigilComponent
{
public ISigil Sigil { get; set; }
public void Reset()
{
Sigil = new Sigil();
}
}

View File

@@ -0,0 +1 @@
uid://dr6mlubpgujk5

View File

@@ -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<IBaseInventoryItem> _spawnableItems;
private ImmutableList<string> _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<Sigil>(_sigilFilePath + "/" + sigilName);
_player.SetSigil(sigilComponent);
}
}
private void DebugInfoCheckbox_Pressed() => _game.ShowDebugInfo(DebugInfoCheckbox.ButtonPressed);
public bool DebugOverlayVisible => DebugInfoCheckbox.ButtonPressed;