Refactor stats

This commit is contained in:
2025-10-22 16:24:07 -07:00
parent 6ec45c4805
commit f0c4e65783
77 changed files with 565 additions and 372 deletions

View File

@@ -1,15 +1,14 @@
using System.Text.Json; using System.Text.Json.Serialization.Metadata;
using System.Text.Json.Serialization.Metadata;
namespace Zennysoft.Game.Abstractions; namespace Zennysoft.Game.Abstractions;
public interface ISaveFileManager<T> public interface ISaveFileManager
{ {
public Task WriteToFile(T gameData, params IJsonTypeInfoResolver?[] resolvers); public Task WriteToFile<T>(T gameData, params IJsonTypeInfoResolver?[] resolvers);
public Task WriteToFile(T gameData, string filePath, params IJsonTypeInfoResolver?[] resolvers); public Task WriteToFile<T>(T gameData, string filePath, params IJsonTypeInfoResolver?[] resolvers);
public Task<T?> ReadFromFile(params IJsonTypeInfoResolver?[] resolvers); public Task<object?> ReadFromFile(params IJsonTypeInfoResolver?[] resolvers);
public Task<T?> ReadFromFile(string filePath, params IJsonTypeInfoResolver?[] resolvers); public Task<object?> ReadFromFile(string filePath, params IJsonTypeInfoResolver?[] resolvers);
} }

View File

@@ -9,7 +9,7 @@ using Zennysoft.Game.Abstractions;
namespace Zennysoft.Game.Implementation; namespace Zennysoft.Game.Implementation;
public class SaveFileManager<T> : ISaveFileManager<T> public class SaveFileManager : ISaveFileManager
{ {
private readonly IFileSystem _fileSystem; private readonly IFileSystem _fileSystem;
private readonly JsonSerializerOptions _jsonOptions; private readonly JsonSerializerOptions _jsonOptions;
@@ -36,14 +36,14 @@ public class SaveFileManager<T> : ISaveFileManager<T>
} }
public Task<T?> ReadFromFile(params IJsonTypeInfoResolver?[] resolvers) public Task<object?> ReadFromFile(params IJsonTypeInfoResolver?[] resolvers)
{ {
if (!_fileSystem.File.Exists(_defaultSaveLocation)) if (!_fileSystem.File.Exists(_defaultSaveLocation))
throw new FileNotFoundException(); throw new FileNotFoundException();
return ReadFromFile(_defaultSaveLocation, resolvers); return ReadFromFile(_defaultSaveLocation, resolvers);
} }
public async Task<T?> ReadFromFile(string filePath, params IJsonTypeInfoResolver?[] resolvers) public async Task<object?> ReadFromFile(string filePath, params IJsonTypeInfoResolver?[] resolvers)
{ {
if (!_fileSystem.File.Exists(filePath)) if (!_fileSystem.File.Exists(filePath))
throw new FileNotFoundException(); throw new FileNotFoundException();
@@ -52,15 +52,15 @@ public class SaveFileManager<T> : ISaveFileManager<T>
var resolver = new SerializableTypeResolver(); var resolver = new SerializableTypeResolver();
_jsonOptions.TypeInfoResolver = JsonTypeInfoResolver.Combine([resolver, .. resolvers]); _jsonOptions.TypeInfoResolver = JsonTypeInfoResolver.Combine([resolver, .. resolvers]);
return JsonSerializer.Deserialize<T?>(json, _jsonOptions); return JsonSerializer.Deserialize<object?>(json, _jsonOptions);
} }
public Task WriteToFile(T gameData, params IJsonTypeInfoResolver?[] resolvers) public Task WriteToFile<T>(T gameData, params IJsonTypeInfoResolver?[] resolvers)
{ {
return WriteToFile(gameData, _defaultSaveLocation, resolvers); return WriteToFile(gameData, _defaultSaveLocation, resolvers);
} }
public async Task WriteToFile(T gameData, string filePath, params IJsonTypeInfoResolver?[] resolvers) public async Task WriteToFile<T>(T gameData, string filePath, params IJsonTypeInfoResolver?[] resolvers)
{ {
var resolver = new SerializableTypeResolver(); var resolver = new SerializableTypeResolver();
_jsonOptions.TypeInfoResolver = JsonTypeInfoResolver.Combine([resolver, .. resolvers]); _jsonOptions.TypeInfoResolver = JsonTypeInfoResolver.Combine([resolver, .. resolvers]);

View File

@@ -7,7 +7,7 @@ public partial class AppLogic
public partial record State public partial record State
{ {
[Meta] [Meta]
public partial record MainMenu : State, IGet<Input.NewGame>, IGet<Input.EnemyViewerOpened> public partial record MainMenu : State, IGet<Input.NewGame>, IGet<Input.LoadGame>, IGet<Input.EnemyViewerOpened>
{ {
public MainMenu() public MainMenu()
{ {
@@ -16,6 +16,7 @@ public partial class AppLogic
public Transition On(in Input.NewGame input) => To<GameStarted>(); public Transition On(in Input.NewGame input) => To<GameStarted>();
public Transition On(in Input.EnemyViewerOpened input) => To<EnemyViewer>(); public Transition On(in Input.EnemyViewerOpened input) => To<EnemyViewer>();
public Transition On(in Input.LoadGame input) => To<LoadingSaveFile>();
} }
} }
} }

View File

@@ -0,0 +1,26 @@
using Chickensoft.Collections;
namespace Zennysoft.Ma.Adapter;
public interface IAttackComponent
{
public IAutoProp<int> CurrentAttack { get; }
public IAutoProp<int> MaximumAttack { get; }
public IAutoProp<int> BonusAttack { get; }
public int TotalAttack { get; }
public void Restore(int restoreAmount);
public void Reduce(int reduceAmount);
public void SetAttack(int attack);
public void RaiseMaximumAttack(int raiseAmount);
public void RaiseBonusAttack(int raiseAmount);
public void ResetBonusAttack();
}

View File

@@ -0,0 +1,26 @@
using Chickensoft.Collections;
namespace Zennysoft.Ma.Adapter;
public interface IDefenseComponent
{
public IAutoProp<int> CurrentDefense { get; }
public IAutoProp<int> MaximumDefense { get; }
public IAutoProp<int> BonusDefense { get; }
public int TotalDefense { get; }
public void Restore(int restoreAmount);
public void Reduce(int reduceAmount);
public void SetDefense(int attack);
public void RaiseMaximumDefense(int raiseAmount);
public void RaiseBonusDefense(int raiseAmount);
public void ResetBonusDefense();
}

View File

@@ -0,0 +1,16 @@
using Chickensoft.Collections;
using Zennysoft.Ma.Adapter;
namespace Zennysoft.Ma.Adapter;
public interface IEquipmentComponent
{
public IAutoProp<EquipableItem> EquippedWeapon { get; }
public IAutoProp<EquipableItem> EquippedArmor { get; }
public IAutoProp<EquipableItem> EquippedAccessory { get; }
public void Equip(EquipableItem equipable);
public void Unequip(EquipableItem equipable);
}

View File

@@ -0,0 +1,18 @@
using Chickensoft.Collections;
namespace Zennysoft.Ma.Adapter;
public interface IExperiencePointsComponent
{
public IAutoProp<int> CurrentExp { get; }
public IAutoProp<int> ExpToNextLevel { get; }
public IAutoProp<double> ExpGainRate { get; }
public IAutoProp<int> Level { get; }
public void Gain(int baseExpGain);
public void LevelUp();
}

View File

@@ -0,0 +1,26 @@
using Chickensoft.Collections;
using Chickensoft.Serialization;
namespace Zennysoft.Ma.Adapter;
public interface IHealthComponent
{
[Save("current_hp")]
public IAutoProp<int> CurrentHP { get; }
[Save("maximum_hp")]
public IAutoProp<int> MaximumHP { get; }
public event Action? HealthReachedZero;
public event Action? DamageTaken;
public bool AtFullHealth { get; }
public void Heal(int healAmount);
public void Damage(int damageAmount);
public void SetHealth(int health);
public void RaiseMaximumHP(int raiseAmount);
}

View File

@@ -0,0 +1,10 @@
using Chickensoft.Collections;
namespace Zennysoft.Ma.Adapter;
public interface ILuckComponent
{
public IAutoProp<int> Luck { get; }
public void SetLuck(int value);
}

View File

@@ -0,0 +1,20 @@
using Chickensoft.Collections;
namespace Zennysoft.Ma.Adapter;
public interface IVTComponent
{
public IAutoProp<int> CurrentVT { get; }
public IAutoProp<int> MaximumVT { get; }
public bool AtFullVT { get; }
public void Restore(int restoreAmount);
public void Reduce(int reduceAmount);
public void SetVT(int vt);
public void RaiseMaximumVT(int raiseAmount);
}

View File

@@ -1,11 +1,10 @@
using Godot; using Chickensoft.GodotNodeInterfaces;
using System.Collections.Immutable; using System.Collections.Immutable;
using Zennysoft.Game.Implementation.Components;
using Zennysoft.Game.Ma; using Zennysoft.Game.Ma;
namespace Zennysoft.Ma.Adapter.Entity namespace Zennysoft.Ma.Adapter.Entity
{ {
public interface IEnemy public interface IEnemy : ICharacterBody3D
{ {
public void Activate(); public void Activate();
@@ -17,19 +16,15 @@ namespace Zennysoft.Ma.Adapter.Entity
public void ReturnToDefaultState(); public void ReturnToDefaultState();
public void TakeDamage(int damage);
public void SetTarget(Vector3 targetPosition);
public void SetEnemyPosition(Vector3 position);
public void LookAtTarget(Vector3 target);
public IDungeonRoom GetCurrentRoom(ImmutableList<IDungeonRoom> dungeonRooms); public IDungeonRoom GetCurrentRoom(ImmutableList<IDungeonRoom> dungeonRooms);
public AttackComponent AttackComponent { get; } public void MoveEnemyToNewRoom(IDungeonRoom newRoom);
public DefenseComponent DefenseComponent { get; } public IHealthComponent HealthComponent { get; }
public IAttackComponent AttackComponent { get; }
public IDefenseComponent DefenseComponent { get; }
public int InitialHP { get; } public int InitialHP { get; }

View File

@@ -1,18 +0,0 @@
using Chickensoft.Introspection;
using Chickensoft.Serialization;
using Zennysoft.Game.Ma;
namespace Zennysoft.Ma.Adapter;
[Meta, Id("game_data")]
public partial record GameData
{
[Save("player_data")]
public required PlayerData PlayerData { get; init; }
[Save("map_data")]
public required MapData MapData { get; init; }
[Save("rescued_items")]
public required RescuedItemDatabase RescuedItems { get; init; }
}

View File

@@ -1,6 +1,7 @@
using Chickensoft.Collections; using Chickensoft.Collections;
using Godot; using Godot;
using Zennysoft.Game.Abstractions; using Zennysoft.Game.Abstractions;
using Zennysoft.Game.Implementation;
namespace Zennysoft.Ma.Adapter; namespace Zennysoft.Ma.Adapter;

View File

@@ -19,11 +19,13 @@ public partial class GameState
public Transition On(in Input.ContinueGame input) public Transition On(in Input.ContinueGame input)
{ {
Output(new Output.InitializeGame()); Output(new Output.InitializeGame());
Output(new Output.LoadGameFromFile());
return To<InGame>(); return To<InGame>();
} }
public Transition On(in Input.LoadGame input) public Transition On(in Input.LoadGame input)
{ {
Output(new Output.InitializeGame());
Output(new Output.LoadGameFromFile()); Output(new Output.LoadGameFromFile());
return To<InGame>(); return To<InGame>();
} }

View File

@@ -1,4 +1,6 @@
namespace Zennysoft.Ma.Adapter; using Zennysoft.Game.Implementation;
namespace Zennysoft.Ma.Adapter;
public interface IInventory public interface IInventory
{ {

View File

@@ -1,6 +1,7 @@
using Chickensoft.Introspection; using Chickensoft.Introspection;
using Chickensoft.Serialization; using Chickensoft.Serialization;
using Zennysoft.Game.Abstractions; using Zennysoft.Game.Abstractions;
using Zennysoft.Game.Implementation;
namespace Zennysoft.Ma.Adapter; namespace Zennysoft.Ma.Adapter;

View File

@@ -22,3 +22,24 @@ public partial class BoxItemTagEnumContext : JsonSerializerContext;
[JsonSerializable(typeof(ElementType))] [JsonSerializable(typeof(ElementType))]
public partial class ElementTypeEnumContext : JsonSerializerContext; public partial class ElementTypeEnumContext : JsonSerializerContext;
[JsonSerializable(typeof(IHealthComponent))]
public partial class HealthComponentContext : JsonSerializerContext;
[JsonSerializable(typeof(IVTComponent))]
public partial class VTComponentContext : JsonSerializerContext;
[JsonSerializable(typeof(IAttackComponent))]
public partial class AttackComponentContext : JsonSerializerContext;
[JsonSerializable(typeof(IDefenseComponent))]
public partial class DefenseComponentContext : JsonSerializerContext;
[JsonSerializable(typeof(IExperiencePointsComponent))]
public partial class ExperiencePointsComponentContext : JsonSerializerContext;
[JsonSerializable(typeof(ILuckComponent))]
public partial class LuckComponentContext : JsonSerializerContext;
[JsonSerializable(typeof(IEquipmentComponent))]
public partial class EquipmentComponentContext : JsonSerializerContext;

View File

@@ -1,9 +1,7 @@
using SimpleInjector; using SimpleInjector;
using System.IO.Abstractions; using System.IO.Abstractions;
using Zennysoft.Game.Abstractions; using Zennysoft.Game.Abstractions;
using Zennysoft.Game.Abstractions.Entity;
using Zennysoft.Game.Implementation; using Zennysoft.Game.Implementation;
using Zennysoft.Ma.Adapter.Entity;
namespace Zennysoft.Ma.Adapter; namespace Zennysoft.Ma.Adapter;
@@ -12,8 +10,8 @@ public class Module
public static void Bootstrap(Container container) public static void Bootstrap(Container container)
{ {
container.RegisterSingleton<IFileSystem, FileSystem>(); container.RegisterSingleton<IFileSystem, FileSystem>();
container.RegisterSingleton<ISaveFileManager<GameData>, SaveFileManager<GameData>>(); container.RegisterSingleton<ISaveFileManager, SaveFileManager>();
container.RegisterSingleton<IMaSaveFileManager<GameData>, MaSaveFileManager<GameData>>(); container.RegisterSingleton<IMaSaveFileManager, MaSaveFileManager>();
container.RegisterSingleton<IGameRepo, GameRepo>(); container.RegisterSingleton<IGameRepo, GameRepo>();
container.RegisterSingleton<IGameState, GameState>(); container.RegisterSingleton<IGameState, GameState>();
container.RegisterSingleton<IDimmableAudioStreamPlayer, DimmableAudioStreamPlayer>(); container.RegisterSingleton<IDimmableAudioStreamPlayer, DimmableAudioStreamPlayer>();

View File

@@ -1,57 +1,39 @@
using Chickensoft.Collections; using Godot;
using Godot;
using Zennysoft.Game.Abstractions; using Zennysoft.Game.Abstractions;
using Zennysoft.Game.Implementation.Components;
namespace Zennysoft.Ma.Adapter; namespace Zennysoft.Ma.Adapter;
public interface IPlayer : IKillable public interface IPlayer : IKillable
{ {
public void InitializePlayerState();
public void Activate(); public void Activate();
public void Deactivate(); public void Deactivate();
public void Attack();
public void TakeDamage(Damage damage); public void TakeDamage(Damage damage);
public void Knockback(float impulse); public void Knockback(float impulse);
public void LevelUp(); public void LevelUp();
public void Move(float delta);
public void TeleportPlayer(Transform3D newTransform); public void TeleportPlayer(Transform3D newTransform);
public void SetHealthTimerStatus(bool isActive);
public IInventory Inventory { get; } public IInventory Inventory { get; }
public Vector3 CurrentPosition { get; } public Vector3 CurrentPosition { get; }
public Basis CurrentBasis { get; } public Basis CurrentBasis { get; }
public AutoProp<EquipableItem> EquippedWeapon { get; } public IHealthComponent HealthComponent { get; }
public AutoProp<EquipableItem> EquippedArmor { get; } public IVTComponent VTComponent { get; }
public AutoProp<EquipableItem> EquippedAccessory { get; } public IAttackComponent AttackComponent { get; }
public void Equip(EquipableItem equipable); public IDefenseComponent DefenseComponent { get; }
public void Unequip(EquipableItem equipable); public IExperiencePointsComponent ExperiencePointsComponent { get; }
public HealthComponent HealthComponent { get; } public ILuckComponent LuckComponent { get; }
public VTComponent VTComponent { get; } public IEquipmentComponent EquipmentComponent { get; }
public AttackComponent AttackComponent { get; }
public DefenseComponent DefenseComponent { get; }
public ExperiencePointsComponent ExperiencePointsComponent { get; }
public LuckComponent LuckComponent { get; }
} }

View File

@@ -1,17 +0,0 @@
using Chickensoft.Introspection;
using Chickensoft.Serialization;
using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma;
[Meta, Id("player_data")]
public partial record PlayerData
{
[Save("player_inventory")]
public required IInventory Inventory { get; init; }
}
[Meta, Id("map_data")]
public partial record MapData
{
}

View File

@@ -4,28 +4,28 @@ using Zennysoft.Game.Abstractions;
namespace Zennysoft.Ma.Adapter; namespace Zennysoft.Ma.Adapter;
public interface IMaSaveFileManager<T> public interface IMaSaveFileManager
{ {
Task Save(T gameData); Task Save<T>(T gameData);
Task<T?> Load(); Task<object?> Load();
} }
public sealed class MaSaveFileManager<T> : IMaSaveFileManager<T> public sealed class MaSaveFileManager : IMaSaveFileManager
{ {
private readonly ISaveFileManager<T> _saveFileManager; private readonly ISaveFileManager _saveFileManager;
private ImmutableList<IJsonTypeInfoResolver> _converters; private ImmutableList<IJsonTypeInfoResolver> _converters;
public MaSaveFileManager(ISaveFileManager<T> saveFileManager) public MaSaveFileManager(ISaveFileManager saveFileManager)
{ {
_saveFileManager = saveFileManager; _saveFileManager = saveFileManager;
_converters = [WeaponTagEnumContext.Default, ItemTagEnumContext.Default, ElementTypeEnumContext.Default, AccessoryTagEnumContext.Default, ThrowableItemTagEnumContext.Default, UsableItemTagEnumContext.Default, BoxItemTagEnumContext.Default]; _converters = [HealthComponentContext.Default, WeaponTagEnumContext.Default, ItemTagEnumContext.Default, ElementTypeEnumContext.Default, AccessoryTagEnumContext.Default, ThrowableItemTagEnumContext.Default, UsableItemTagEnumContext.Default, BoxItemTagEnumContext.Default];
} }
public async Task Save(T gameData) public async Task Save<T>(T gameData)
{ {
await _saveFileManager.WriteToFile(gameData, [.. _converters]); await _saveFileManager.WriteToFile(gameData, [.. _converters]);
} }
public async Task<T?> Load() => await _saveFileManager.ReadFromFile([.. _converters]); public async Task<object?> Load() => await _saveFileManager.ReadFromFile([.. _converters]);
} }

View File

@@ -1,5 +1,6 @@
using Chickensoft.Introspection; using Chickensoft.Introspection;
using Chickensoft.LogicBlocks; using Chickensoft.LogicBlocks;
using Zennysoft.Game.Implementation;
namespace Zennysoft.Ma.Adapter; namespace Zennysoft.Ma.Adapter;

View File

@@ -1,4 +1,5 @@
using Zennysoft.Game.Ma; using Zennysoft.Game.Implementation;
using Zennysoft.Game.Ma;
namespace Zennysoft.Ma.Adapter; namespace Zennysoft.Ma.Adapter;
public partial class InGameUILogic public partial class InGameUILogic

View File

@@ -1,9 +1,10 @@
using Chickensoft.Collections; using Chickensoft.Collections;
using Godot; using System;
using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Implementation.Components; namespace Zennysoft.Game.Ma;
public class AttackComponent public class AttackComponent : IAttackComponent
{ {
public IAutoProp<int> CurrentAttack => _currentAttack; public IAutoProp<int> CurrentAttack => _currentAttack;
@@ -49,4 +50,14 @@ public class AttackComponent
_maximumAttack.OnNext(raiseAmount); _maximumAttack.OnNext(raiseAmount);
Restore(raiseAmount); Restore(raiseAmount);
} }
public void RaiseBonusAttack(int raiseAmount)
{
_bonusAttack.OnNext(_bonusAttack.Value + raiseAmount);
}
public void ResetBonusAttack()
{
_bonusAttack.OnNext(0);
}
} }

View File

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

View File

@@ -1,9 +1,10 @@
using Chickensoft.Collections; using Chickensoft.Collections;
using Godot; using System;
using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Implementation.Components; namespace Zennysoft.Game.Ma;
public class DefenseComponent public class DefenseComponent : IDefenseComponent
{ {
public IAutoProp<int> CurrentDefense => _currentDefense; public IAutoProp<int> CurrentDefense => _currentDefense;

View File

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

View File

@@ -0,0 +1,45 @@
using Chickensoft.Collections;
using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma;
public class EquipmentComponent : IEquipmentComponent
{
public IAutoProp<EquipableItem> EquippedWeapon => _equippedWeapon;
public IAutoProp<EquipableItem> EquippedArmor => _equippedArmor;
public IAutoProp<EquipableItem> EquippedAccessory => _equippedAccessory;
public AutoProp<EquipableItem> _equippedWeapon;
public AutoProp<EquipableItem> _equippedArmor;
public AutoProp<EquipableItem> _equippedAccessory;
public EquipmentComponent()
{
_equippedWeapon = new AutoProp<EquipableItem>(new Weapon());
_equippedArmor = new AutoProp<EquipableItem>(new Armor());
_equippedAccessory = new AutoProp<EquipableItem>(new Accessory());
}
public void Equip(EquipableItem equipable)
{
if (equipable is Weapon weapon)
_equippedWeapon.OnNext(weapon);
if (equipable is Armor armor)
_equippedArmor.OnNext(armor);
if (equipable is Accessory accessory)
_equippedAccessory.OnNext(accessory);
}
public void Unequip(EquipableItem equipable)
{
if (equipable is Weapon weapon)
_equippedWeapon.OnNext(new Weapon());
if (equipable is Armor armor)
_equippedArmor.OnNext(new Armor());
if (equipable is Accessory accessory)
_equippedAccessory.OnNext(new Accessory());
}
}

View File

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

View File

@@ -1,8 +1,10 @@
using Chickensoft.Collections; using Chickensoft.Collections;
using System;
using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Implementation.Components; namespace Zennysoft.Game.Ma;
public class ExperiencePointsComponent public class ExperiencePointsComponent : IExperiencePointsComponent
{ {
public IAutoProp<int> CurrentExp => _currentExp; public IAutoProp<int> CurrentExp => _currentExp;

View File

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

View File

@@ -1,11 +1,16 @@
using Chickensoft.Collections; using Chickensoft.Collections;
using Chickensoft.Serialization;
using System;
using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Implementation.Components; namespace Zennysoft.Game.Ma;
public class HealthComponent public class HealthComponent : IHealthComponent
{ {
[Save("current_hp")]
public IAutoProp<int> CurrentHP => _currentHP; public IAutoProp<int> CurrentHP => _currentHP;
[Save("maximum_hp")]
public IAutoProp<int> MaximumHP => _maximumHP; public IAutoProp<int> MaximumHP => _maximumHP;
private readonly AutoProp<int> _currentHP; private readonly AutoProp<int> _currentHP;

View File

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

View File

@@ -1,8 +1,9 @@
using Chickensoft.Collections; using Chickensoft.Collections;
using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Implementation.Components; namespace Zennysoft.Game.Ma;
public class LuckComponent public class LuckComponent : ILuckComponent
{ {
public IAutoProp<int> Luck => _luck; public IAutoProp<int> Luck => _luck;

View File

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

View File

@@ -1,8 +1,10 @@
using Chickensoft.Collections; using Chickensoft.Collections;
using System;
using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Implementation.Components; namespace Zennysoft.Game.Ma;
public class VTComponent public class VTComponent : IVTComponent
{ {
public IAutoProp<int> CurrentVT => _currentVT; public IAutoProp<int> CurrentVT => _currentVT;

View File

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

View File

@@ -1,9 +1,9 @@
using Chickensoft.AutoInject; using Chickensoft.AutoInject;
using Chickensoft.Introspection; using Chickensoft.Introspection;
using Godot; using Godot;
using System;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Linq; using System.Linq;
using Zennysoft.Game.Implementation.Components;
using Zennysoft.Ma.Adapter; using Zennysoft.Ma.Adapter;
using Zennysoft.Ma.Adapter.Entity; using Zennysoft.Ma.Adapter.Entity;
@@ -26,11 +26,11 @@ public abstract partial class Enemy : CharacterBody3D, IEnemy, IProvide<IEnemyLo
[Dependency] protected IPlayer _player => this.DependOn(() => GetParent().GetChildren().OfType<IPlayer>().Single()); [Dependency] protected IPlayer _player => this.DependOn(() => GetParent().GetChildren().OfType<IPlayer>().Single());
#endregion #endregion
public HealthComponent HealthComponent { get; private set; } public IHealthComponent HealthComponent { get; private set; }
public AttackComponent AttackComponent { get; private set; } public IAttackComponent AttackComponent { get; private set; }
public DefenseComponent DefenseComponent { get; private set; } public IDefenseComponent DefenseComponent { get; private set; }
public virtual IEnemyModelView EnemyModelView { get; set; } = default!; public virtual IEnemyModelView EnemyModelView { get; set; } = default!;
@@ -118,36 +118,6 @@ public abstract partial class Enemy : CharacterBody3D, IEnemy, IProvide<IEnemyLo
} }
public void SetTarget(Vector3 targetPosition) => TargetPosition = targetPosition;
public virtual void SetEnemyPosition(Vector3 newPosition)
{
GlobalPosition = newPosition;
if (this is IHavePatrolBehavior patrolEnemy)
patrolEnemy.PatrolBehavior.HomePosition = GlobalPosition;
_enemyLogic.Input(new EnemyLogic.Input.Reset());
}
public void LookAtTarget(Vector3 targetPosition)
{
var lookDirection = GlobalPosition - targetPosition;
if (lookDirection != GlobalPosition)
LookAt(new Vector3(lookDirection.X, GlobalPosition.Y, lookDirection.Z), Vector3.Up);
}
public virtual void TakeDamage(int damage)
{
GD.Print($"Enemy Hit for {damage} damage.");
HealthComponent.Damage(damage);
}
private void EnemyModelView_HitPlayer(object sender, System.EventArgs e)
{
_player.TakeDamage(new Damage(AttackComponent.TotalAttack, ElementType.None, false, false, false));
}
public virtual void TakeHit() public virtual void TakeHit()
{ {
_enemyLogic.Input(new EnemyLogic.Input.Alert()); _enemyLogic.Input(new EnemyLogic.Input.Alert());
@@ -181,4 +151,37 @@ public abstract partial class Enemy : CharacterBody3D, IEnemy, IProvide<IEnemyLo
_enemyLogic.Stop(); _enemyLogic.Stop();
EnemyBinding.Dispose(); EnemyBinding.Dispose();
} }
public virtual void MoveEnemyToNewRoom(IDungeonRoom newRoom)
{
if (newRoom is MonsterRoom monsterRoom)
{
var spawnPoints = monsterRoom.EnemySpawnPoints.GetChildren().OfType<Marker3D>().ToList();
var spawnPointsGodotCollection = new Godot.Collections.Array<Marker3D>(spawnPoints);
var randomSpawnPoint = spawnPointsGodotCollection.PickRandom();
GlobalPosition = randomSpawnPoint.GlobalPosition;
if (this is IHavePatrolBehavior patrolEnemy)
patrolEnemy.PatrolBehavior.HomePosition = GlobalPosition;
_enemyLogic.Input(new EnemyLogic.Input.Reset());
}
throw new NotImplementedException($"Only {nameof(MonsterRoom)} types are currently supported.");
}
protected void LookAtTarget(Vector3 targetPosition)
{
var lookDirection = GlobalPosition - targetPosition;
if (lookDirection != GlobalPosition)
LookAt(new Vector3(lookDirection.X, GlobalPosition.Y, lookDirection.Z), Vector3.Up);
}
protected void SetTarget(Vector3 targetPosition) => TargetPosition = targetPosition;
private void EnemyModelView_HitPlayer(object sender, System.EventArgs e)
{
_player.TakeDamage(new Damage(AttackComponent.TotalAttack, ElementType.None, false, false, false));
}
} }

View File

@@ -18,7 +18,7 @@ public abstract partial class Enemy2D : Enemy
[Node] private EnemyModelView2D _enemyModelView { get; set; } = default!; [Node] private EnemyModelView2D _enemyModelView { get; set; } = default!;
public void OnReady() public void OnEnterTree()
{ {
LineOfSight.BodyEntered += LineOfSight_BodyEntered; LineOfSight.BodyEntered += LineOfSight_BodyEntered;
} }

View File

@@ -23,12 +23,16 @@ public partial class FollowBehavior : Node3D, IBehavior
private NavigationAgent3D _navigationAgent; private NavigationAgent3D _navigationAgent;
private Timer _thinkTimer; private Timer _thinkTimer;
public void Init(NavigationAgent3D navigationAgent) public FollowBehavior()
{ {
_navigationAgent = navigationAgent;
_thinkTimer = new Timer() { WaitTime = _thinkTime }; _thinkTimer = new Timer() { WaitTime = _thinkTime };
_thinkTimer.Timeout += OnTimeout; _thinkTimer.Timeout += OnTimeout;
AddChild(_thinkTimer); AddChild(_thinkTimer);
}
public void Init(NavigationAgent3D navigationAgent)
{
_navigationAgent = navigationAgent;
SetPhysicsProcess(false); SetPhysicsProcess(false);
} }

View File

@@ -28,11 +28,15 @@ public partial class PatrolBehavior : Node3D, IBehavior
[Signal] public delegate void OnVelocityComputedEventHandler(Vector3 safeVelocity); [Signal] public delegate void OnVelocityComputedEventHandler(Vector3 safeVelocity);
public void OnReady() public PatrolBehavior()
{ {
_patrolTimer = new Timer() { WaitTime = _patrolTime }; _patrolTimer = new Timer() { WaitTime = _patrolTime };
_patrolTimer.Timeout += PatrolTimer_Timeout; _patrolTimer.Timeout += PatrolTimer_Timeout;
AddChild(_patrolTimer); AddChild(_patrolTimer);
}
public void OnReady()
{
SetPhysicsProcess(false); SetPhysicsProcess(false);
} }

View File

@@ -16,7 +16,7 @@ public partial class Sproingy : Enemy2D, IHavePatrolBehavior, IHaveEngagePlayerB
[Node] public Area3D PlayerDetector { get; set; } = default!; [Node] public Area3D PlayerDetector { get; set; } = default!;
public new void OnReady() public void OnReady()
{ {
FollowBehavior.Init(NavigationAgent); FollowBehavior.Init(NavigationAgent);
PatrolBehavior.Init(NavigationAgent); PatrolBehavior.Init(NavigationAgent);

View File

@@ -81,6 +81,8 @@ shape = SubResource("CylinderShape3D_drfkj")
[node name="EnemyModelView" parent="Visual" instance=ExtResource("4_o3b7p")] [node name="EnemyModelView" parent="Visual" instance=ExtResource("4_o3b7p")]
unique_name_in_owner = true unique_name_in_owner = true
_upperThreshold = 0.5
_lowerThreshold = -0.5
[node name="Components" type="Node3D" parent="."] [node name="Components" type="Node3D" parent="."]

View File

@@ -33,46 +33,22 @@ shape = SubResource("CapsuleShape3D_7uhtm")
unique_name_in_owner = true unique_name_in_owner = true
[node name="Skeleton3D" parent="EnemyModelView/Armature" index="0"] [node name="Skeleton3D" parent="EnemyModelView/Armature" index="0"]
bones/0/position = Vector3(-0.260271, -1.05324, -1.96773) bones/0/position = Vector3(-0.260254, -1.05135, -1.96786)
bones/0/rotation = Quaternion(0.0915277, -0.692111, -0.0341586, 0.715149)
bones/1/rotation = Quaternion(0.0828172, 0.0642671, -0.39627, 0.91213)
bones/2/rotation = Quaternion(-0.137837, 0.137086, 0.403643, 0.894025)
bones/3/rotation = Quaternion(-0.00338816, 0.00852271, 0.0152662, 0.999842)
bones/4/rotation = Quaternion(0.037164, 0.133882, 0.101977, 0.985036)
bones/5/rotation = Quaternion(-0.0397875, -0.0104688, 0.0235613, 0.998875)
bones/6/rotation = Quaternion(-0.0474983, -0.294201, -0.744151, 0.597854) bones/6/rotation = Quaternion(-0.0474983, -0.294201, -0.744151, 0.597854)
bones/7/rotation = Quaternion(0.0788712, -0.0306685, -0.220772, 0.971647) bones/11/rotation = Quaternion(-0.779814, -0.0573517, 0.0816353, 0.61801)
bones/8/rotation = Quaternion(-0.127286, 0.0273856, -0.425308, 0.895635) bones/15/rotation = Quaternion(-0.21544, 0.745303, 0.613567, -0.147118)
bones/9/rotation = Quaternion(-0.0931654, 0.0493592, -0.752794, 0.649757)
bones/10/rotation = Quaternion(0.0429966, 0.0102923, 0.363547, 0.930526)
bones/11/rotation = Quaternion(-0.779773, -0.0573165, 0.0817155, 0.618054)
bones/12/rotation = Quaternion(-0.607818, -0.670503, -0.284916, 0.31592)
bones/13/rotation = Quaternion(-0.255941, 0.586097, -0.127235, 0.758153)
bones/14/rotation = Quaternion(-0.513517, -0.227335, -0.228787, 0.795157)
bones/15/rotation = Quaternion(-0.215465, 0.745342, 0.613525, -0.147065)
bones/16/rotation = Quaternion(-0.486067, -0.16412, -0.362283, 0.778174) bones/16/rotation = Quaternion(-0.486067, -0.16412, -0.362283, 0.778174)
bones/17/rotation = Quaternion(-0.0553629, -0.0361614, 0.62832, 0.77514) bones/17/rotation = Quaternion(-0.0553629, -0.0361614, 0.62832, 0.77514)
bones/18/rotation = Quaternion(-0.119289, 0.0998131, -0.0173011, 0.987678) bones/19/position = Vector3(-0.379519, -1.19848, -1.72293)
bones/19/position = Vector3(-0.381043, -1.19992, -1.71791) bones/19/rotation = Quaternion(0.627358, 0.293207, 0.545673, -0.471903)
bones/19/rotation = Quaternion(0.627663, 0.29282, 0.545153, -0.472338) bones/20/rotation = Quaternion(-0.327492, -0.423093, -0.300135, 0.789722)
bones/20/rotation = Quaternion(-0.327815, -0.422723, -0.300673, 0.789581) bones/21/rotation = Quaternion(-0.0604877, 0.00129843, 0.48965, 0.869818)
bones/21/rotation = Quaternion(-0.0604945, 0.00129838, 0.489705, 0.869786) bones/26/position = Vector3(-0.0278308, -1.11395, -2.01914)
bones/22/rotation = Quaternion(0.156218, 0.0483037, -0.624744, 0.763516) bones/27/rotation = Quaternion(-0.202309, 0.424634, 0.137996, 0.871616)
bones/23/rotation = Quaternion(0.123936, -0.00678731, -0.347765, 0.92933) bones/28/rotation = Quaternion(-0.0627943, -0.00116438, -0.501344, 0.862966)
bones/24/rotation = Quaternion(0.427621, 0.561851, 0.530083, 0.469549)
bones/25/position = Vector3(4.82744, -12.3397, 0.183847)
bones/25/rotation = Quaternion(-0.400051, 0.463947, -0.598439, 0.516317)
bones/26/position = Vector3(-0.0233502, -1.11395, -2.01916)
bones/26/rotation = Quaternion(0.608697, -0.3155, -0.575514, -0.445793)
bones/27/rotation = Quaternion(-0.202236, 0.424675, 0.137941, 0.871622)
bones/28/rotation = Quaternion(-0.0627838, -0.00116445, -0.50126, 0.863015)
bones/29/rotation = Quaternion(0.150998, -0.0515735, 0.668372, 0.726511)
bones/31/position = Vector3(-7.29038, -6.72226, -0.133983)
bones/31/rotation = Quaternion(-0.453784, 0.542292, 0.542291, -0.453784)
bones/32/rotation = Quaternion(0.456756, 0.539878, -0.539587, -0.456893)
[node name="BoneAttachment3D" parent="EnemyModelView/Armature/Skeleton3D" index="0"] [node name="BoneAttachment3D" parent="EnemyModelView/Armature/Skeleton3D" index="0"]
transform = Transform3D(-0.266252, -0.0359368, -0.963233, -0.333724, -0.934064, 0.127095, -0.904288, 0.355294, 0.236703, -1.68948, 8.20049, 4.9569) transform = Transform3D(-0.266252, -0.0359368, -0.963233, -0.333724, -0.934064, 0.127095, -0.904288, 0.355294, 0.236703, -1.68946, 8.20239, 4.95677)
[node name="Collision" type="Area3D" parent="."] [node name="Collision" type="Area3D" parent="."]
collision_layer = 2048 collision_layer = 2048

View File

@@ -11,7 +11,6 @@ using Zennysoft.Game.Abstractions;
using Zennysoft.Ma.Adapter; using Zennysoft.Ma.Adapter;
using System.IO; using System.IO;
using System.Threading.Tasks; using System.Threading.Tasks;
using Zennysoft.Game.Implementation.Components;
[Meta(typeof(IAutoNode))] [Meta(typeof(IAutoNode))]
public partial class Game : Node3D, IGame public partial class Game : Node3D, IGame
@@ -95,7 +94,8 @@ public partial class Game : Node3D, IGame
PlayerData = new PlayerData() PlayerData = new PlayerData()
{ {
Inventory = _player.Inventory Inventory = (Inventory)_player.Inventory,
HealthComponent = (HealthComponent)_player.HealthComponent,
}, },
MapData = new MapData() MapData = new MapData()
{ {
@@ -119,7 +119,7 @@ public partial class Game : Node3D, IGame
public void OnResolved() public void OnResolved()
{ {
var saveFileManager = _container.GetInstance<IMaSaveFileManager<GameData>>(); var saveFileManager = _container.GetInstance<IMaSaveFileManager>();
SaveFile = new SaveFile<GameData>( SaveFile = new SaveFile<GameData>(
root: GameChunk, root: GameChunk,
onSave: saveFileManager.Save, onSave: saveFileManager.Save,
@@ -127,7 +127,7 @@ public partial class Game : Node3D, IGame
{ {
try try
{ {
var gameData = await saveFileManager.Load(); var gameData = await saveFileManager.Load() as GameData;
return gameData; return gameData;
} }
catch (FileNotFoundException) catch (FileNotFoundException)
@@ -138,7 +138,6 @@ public partial class Game : Node3D, IGame
return null; return null;
} }
); );
GameBinding = GameState.Bind(); GameBinding = GameState.Bind();
GameBinding GameBinding
.Handle((in GameState.Output.InitializeGame _) => .Handle((in GameState.Output.InitializeGame _) =>
@@ -200,22 +199,22 @@ public partial class Game : Node3D, IGame
.Handle((in GameState.Output.LoadNextFloor _) => .Handle((in GameState.Output.LoadNextFloor _) =>
{ {
FloorClearMenu.FadeOut(); FloorClearMenu.FadeOut();
_map.LoadFloor(); Task.Run(() => _map.LoadFloor());
if (_player.EquippedWeapon.Value.ItemTag == ItemTag.BreaksOnChange) if (_player.EquipmentComponent.EquippedWeapon.Value.ItemTag == ItemTag.BreaksOnChange)
{ {
var itemToDestroy = _player.EquippedWeapon.Value; var itemToDestroy = _player.EquipmentComponent.EquippedWeapon.Value;
_player.Unequip(itemToDestroy); _player.Unequip(itemToDestroy);
_player.Inventory.Remove(itemToDestroy); _player.Inventory.Remove(itemToDestroy);
} }
if (_player.EquippedArmor.Value.ItemTag == ItemTag.BreaksOnChange) if (_player.EquipmentComponent.EquippedArmor.Value.ItemTag == ItemTag.BreaksOnChange)
{ {
var itemToDestroy = _player.EquippedArmor.Value; var itemToDestroy = _player.EquipmentComponent.EquippedArmor.Value;
_player.Unequip(itemToDestroy); _player.Unequip(itemToDestroy);
_player.Inventory.Remove(itemToDestroy); _player.Inventory.Remove(itemToDestroy);
} }
if (_player.EquippedAccessory.Value.ItemTag == ItemTag.BreaksOnChange) if (_player.EquipmentComponent.EquippedAccessory.Value.ItemTag == ItemTag.BreaksOnChange)
{ {
var itemToDestroy = _player.EquippedAccessory.Value; var itemToDestroy = _player.EquipmentComponent.EquippedAccessory.Value;
_player.Unequip(itemToDestroy); _player.Unequip(itemToDestroy);
_player.Inventory.Remove(itemToDestroy); _player.Inventory.Remove(itemToDestroy);
} }
@@ -245,6 +244,7 @@ public partial class Game : Node3D, IGame
_effectService = new EffectService(this, _player, _map); _effectService = new EffectService(this, _player, _map);
} }
#pragma warning disable CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
public void OnReady() public void OnReady()
{ {
InitializeGame(); InitializeGame();
@@ -254,22 +254,22 @@ public partial class Game : Node3D, IGame
InGameUI.PlayerInfoUI.Activate(); InGameUI.PlayerInfoUI.Activate();
_player.Activate(); _player.Activate();
} }
#pragma warning restore CS4014 // Because this call is not awaited, execution of the current method continues before the call is completed
private void FloorClearMenu_SaveAndExit() private void FloorClearMenu_SaveAndExit()
{ {
//SaveFile.Save();
_player.Deactivate(); _player.Deactivate();
GameState.Input(new GameState.Input.ReturnToMainMenu()); GameState.Input(new GameState.Input.ReturnToMainMenu());
InGameUI.Hide(); InGameUI.Hide();
} }
private void FloorClearMenu_GoToNextFloor() => GameState.Input(new GameState.Input.LoadNextFloor()); private void FloorClearMenu_GoToNextFloor() => GameState.Input(new GameState.Input.LoadNextFloor());
public void LoadExistingGame() => SaveFile.Load().ContinueWith((_) => CallDeferred(nameof(FinishedLoadingSaveFile))); public void LoadExistingGame() => SaveFile.Load().ContinueWith((_) => CallDeferred(nameof(FinishedLoadingSaveFile)));
public void InitializeGame() public void InitializeGame()
{ {
_map.InitializeMapData(); _map.InitializeMapData();
_player.InitializePlayerState();
} }
public void FloorExitReached() public void FloorExitReached()

View File

@@ -0,0 +1,43 @@
using Chickensoft.Introspection;
using Chickensoft.Serialization;
using Zennysoft.Game.Ma;
namespace Zennysoft.Ma.Adapter;
[Meta, Id("game_data")]
public partial record GameData
{
[Save("player_data")]
public required PlayerData PlayerData { get; init; }
[Save("map_data")]
public required MapData MapData { get; init; }
[Save("rescued_items")]
public required RescuedItemDatabase RescuedItems { get; init; }
}
public partial record PlayerData
{
[Save("inventory")]
public required Inventory Inventory { get; init; }
[Save("health_component")]
public required HealthComponent HealthComponent { get; init; }
[Save("vt_component")]
public VTComponent VTComponent { get; init; }
[Save("attack_component")]
public AttackComponent AttackComponent { get; init; }
[Save("defense_component")]
public DefenseComponent DefenseComponent { get; init; }
[Save("experience_points_component")]
public ExperiencePointsComponent ExperiencePointsComponent { get; init; }
[Save("luck_component")]
public LuckComponent LuckComponent { get; init; }
[Save("equipment_component")]
public EquipmentComponent EquipmentComponent { get; init; }
}
public partial record MapData
{
}

View File

@@ -4,8 +4,8 @@ namespace Zennysoft.Game.Ma;
using Chickensoft.AutoInject; using Chickensoft.AutoInject;
using Chickensoft.GodotNodeInterfaces; using Chickensoft.GodotNodeInterfaces;
using Chickensoft.SaveFileBuilder; using Chickensoft.SaveFileBuilder;
using Godot;
using System.Threading.Tasks; using System.Threading.Tasks;
using Zennysoft.Game.Implementation;
using Zennysoft.Ma.Adapter; using Zennysoft.Ma.Adapter;
public interface IGame : IProvide<IGame>, IProvide<IGameRepo>, IProvide<IPlayer>, IProvide<IMap>, IProvide<ISaveChunk<GameData>>, INode3D public interface IGame : IProvide<IGame>, IProvide<IGameRepo>, IProvide<IPlayer>, IProvide<IMap>, IProvide<ISaveChunk<GameData>>, INode3D

View File

@@ -3,6 +3,7 @@ using System.Linq;
using System; using System;
using Zennysoft.Ma.Adapter; using Zennysoft.Ma.Adapter;
using Zennysoft.Ma.Adapter.Entity; using Zennysoft.Ma.Adapter.Entity;
using Zennysoft.Game.Implementation;
namespace Zennysoft.Game.Ma; namespace Zennysoft.Game.Ma;
@@ -37,12 +38,7 @@ public class EffectService
var enemyList = validRooms.SelectMany(x => x.GetEnemiesInCurrentRoom()); var enemyList = validRooms.SelectMany(x => x.GetEnemiesInCurrentRoom());
foreach (var enemy in enemyList) foreach (var enemy in enemyList)
{ enemy.MoveEnemyToNewRoom(currentMonsterRoom);
var spawnPoints = currentMonsterRoom.EnemySpawnPoints.GetChildren().OfType<Marker3D>().ToList();
var spawnPointsGodotCollection = new Godot.Collections.Array<Marker3D>(spawnPoints);
var randomSpawnPoint = spawnPointsGodotCollection.PickRandom();
enemy.SetEnemyPosition(randomSpawnPoint.GlobalPosition);
}
} }
public void KillHalfEnemiesInRoom() public void KillHalfEnemiesInRoom()
@@ -54,8 +50,8 @@ public class EffectService
var currentMonsterRoom = (MonsterRoom)currentRoom; var currentMonsterRoom = (MonsterRoom)currentRoom;
var enemyList = currentMonsterRoom.GetEnemiesInCurrentRoom().ToList(); var enemyList = currentMonsterRoom.GetEnemiesInCurrentRoom().ToList();
var enemiesToKill = enemyList.Count / 2; var enemiesToKill = enemyList.Count / 2;
//for (var i = 0; i < enemiesToKill; i++) for (var i = 0; i < enemiesToKill; i++)
// enemyList[i].Die(); enemyList[i].Die();
} }
public void TurnAllEnemiesInRoomIntoHealingItem() public void TurnAllEnemiesInRoomIntoHealingItem()
@@ -68,8 +64,8 @@ public class EffectService
var currentEnemies = currentRoom.EnemiesInRoom; var currentEnemies = currentRoom.EnemiesInRoom;
foreach (var enemy in currentEnemies) foreach (var enemy in currentEnemies)
{ {
//enemy.Die(); enemy.Die();
//DropHealingItem(enemy.GetEnemyGlobalPosition()); DropHealingItem(enemy.GlobalPosition);
} }
} }
@@ -95,9 +91,7 @@ public class EffectService
if (currentRoom is not MonsterRoom) if (currentRoom is not MonsterRoom)
return; return;
var currentEnemies = currentRoom.EnemiesInRoom; currentRoom.EnemiesInRoom.ForEach(e => e.HealthComponent.SetHealth(e.HealthComponent.MaximumHP.Value));
//foreach (var enemy in currentEnemies)
// enemy.SetCurrentHP(enemy.GetMaximumHP());
_player.HealthComponent.SetHealth(_player.HealthComponent.MaximumHP.Value); _player.HealthComponent.SetHealth(_player.HealthComponent.MaximumHP.Value);
} }
@@ -110,8 +104,12 @@ public class EffectService
var currentEnemies = currentRoom.EnemiesInRoom; var currentEnemies = currentRoom.EnemiesInRoom;
var hpToAbsorb = 0.0; var hpToAbsorb = 0.0;
//foreach (var enemy in currentEnemies) foreach (var enemy in currentEnemies)
// hpToAbsorb += enemy.CurrentHP.Value * 0.05; {
var absorbAmount = enemy.HealthComponent.MaximumHP.Value * 0.05;
enemy.HealthComponent.Damage((int)absorbAmount);
hpToAbsorb += absorbAmount;
}
_player.HealthComponent.Heal((int)hpToAbsorb); _player.HealthComponent.Heal((int)hpToAbsorb);
GD.Print("HP to absorb: " + hpToAbsorb); GD.Print("HP to absorb: " + hpToAbsorb);
} }
@@ -127,7 +125,7 @@ public class EffectService
foreach (var enemy in currentEnemies) foreach (var enemy in currentEnemies)
{ {
var damageDealt = DamageCalculator.CalculateDamage(new Damage(20, elementType, false, false, false), 10, new ElementalResistanceSet(0, 0, 0, 0, 0)); var damageDealt = DamageCalculator.CalculateDamage(new Damage(20, elementType, false, false, false), 10, new ElementalResistanceSet(0, 0, 0, 0, 0));
enemy.TakeDamage(damageDealt); enemy.HealthComponent.Damage(damageDealt);
} }
} }
@@ -152,28 +150,25 @@ public class EffectService
public void RaiseCurrentWeaponAttack() public void RaiseCurrentWeaponAttack()
{ {
if (string.IsNullOrEmpty(_player.EquippedWeapon.Value.ItemName)) if (string.IsNullOrEmpty(_player.EquipmentComponent.EquippedWeapon.Value.ItemName))
return; return;
var currentWeapon = (Weapon)_player.EquippedWeapon.Value; var currentWeapon = (Weapon)_player.EquipmentComponent.EquippedWeapon.Value;
currentWeapon.IncreaseWeaponAttack(1); currentWeapon.IncreaseWeaponAttack(1);
//_player.ModifyBonusAttack(1); _player.AttackComponent.RaiseBonusAttack(1);
} }
public void RaiseCurrentArmorDefense() public void RaiseCurrentArmorDefense()
{ {
if (string.IsNullOrEmpty(_player.EquippedArmor.Value.ItemName)) if (string.IsNullOrEmpty(_player.EquipmentComponent.EquippedArmor.Value.ItemName))
return; return;
var currentArmor = (Armor)_player.EquippedArmor.Value; var currentArmor = (Armor)_player.EquipmentComponent.EquippedArmor.Value;
currentArmor.IncreaseArmorDefense(1); currentArmor.IncreaseArmorDefense(1);
_player.DefenseComponent.RaiseBonusDefense(1); _player.DefenseComponent.RaiseBonusDefense(1);
} }
public void RaiseLevel() public void RaiseLevel() => _player.LevelUp();
{
_player.LevelUp();
}
public void TeleportToRandomRoom(IEnemy enemy) public void TeleportToRandomRoom(IEnemy enemy)
{ {
@@ -189,11 +184,8 @@ public class EffectService
var roomsGodotCollection = new Godot.Collections.Array<MonsterRoom>(validRooms); var roomsGodotCollection = new Godot.Collections.Array<MonsterRoom>(validRooms);
var randomRoom = roomsGodotCollection.PickRandom(); var randomRoom = roomsGodotCollection.PickRandom();
var spawnPoints = randomRoom.EnemySpawnPoints.GetChildren().OfType<Marker3D>().ToList();
var spawnPointsGodotCollection = new Godot.Collections.Array<Marker3D>(spawnPoints);
var randomSpawnPoint = spawnPointsGodotCollection.PickRandom();
enemy.SetEnemyPosition(randomSpawnPoint.GlobalPosition); enemy.MoveEnemyToNewRoom(randomRoom);
} }
public void TeleportToRandomRoom(IPlayer player) public void TeleportToRandomRoom(IPlayer player)

View File

@@ -5,6 +5,7 @@ using Godot;
using System.Collections.Generic; using System.Collections.Generic;
using System.Linq; using System.Linq;
using Zennysoft.Game.Abstractions; using Zennysoft.Game.Abstractions;
using Zennysoft.Game.Implementation;
using Zennysoft.Ma.Adapter; using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma; namespace Zennysoft.Game.Ma;

View File

@@ -1,6 +1,7 @@
using Chickensoft.Introspection; using Chickensoft.Introspection;
using Chickensoft.Serialization; using Chickensoft.Serialization;
using Godot; using Godot;
using Zennysoft.Game.Implementation;
using Zennysoft.Ma.Adapter; using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma; namespace Zennysoft.Game.Ma;

View File

@@ -1,6 +1,7 @@
using Chickensoft.Introspection; using Chickensoft.Introspection;
using Chickensoft.Serialization; using Chickensoft.Serialization;
using Godot; using Godot;
using Zennysoft.Game.Implementation;
using Zennysoft.Ma.Adapter; using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma; namespace Zennysoft.Game.Ma;

View File

@@ -2,6 +2,7 @@
using Chickensoft.GodotNodeInterfaces; using Chickensoft.GodotNodeInterfaces;
using Chickensoft.Introspection; using Chickensoft.Introspection;
using Godot; using Godot;
using Zennysoft.Game.Implementation;
using Zennysoft.Ma.Adapter; using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma; namespace Zennysoft.Game.Ma;

View File

@@ -1,6 +1,7 @@
using Chickensoft.Introspection; using Chickensoft.Introspection;
using Chickensoft.Serialization; using Chickensoft.Serialization;
using Godot; using Godot;
using Zennysoft.Game.Implementation;
using Zennysoft.Ma.Adapter; using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma; namespace Zennysoft.Game.Ma;

View File

@@ -2,7 +2,6 @@ using Chickensoft.AutoInject;
using Chickensoft.Introspection; using Chickensoft.Introspection;
using Godot; using Godot;
using Zennysoft.Game.Abstractions; using Zennysoft.Game.Abstractions;
using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma; namespace Zennysoft.Game.Ma;

View File

@@ -3,6 +3,7 @@ using Chickensoft.Introspection;
using Chickensoft.Serialization; using Chickensoft.Serialization;
using Godot; using Godot;
using Zennysoft.Game.Abstractions; using Zennysoft.Game.Abstractions;
using Zennysoft.Game.Implementation;
using Zennysoft.Ma.Adapter; using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma; namespace Zennysoft.Game.Ma;

View File

@@ -1,6 +1,7 @@
using Chickensoft.Introspection; using Chickensoft.Introspection;
using Chickensoft.Serialization; using Chickensoft.Serialization;
using Godot; using Godot;
using Zennysoft.Game.Implementation;
using Zennysoft.Ma.Adapter; using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma; namespace Zennysoft.Game.Ma;

View File

@@ -1,6 +1,7 @@
using Chickensoft.AutoInject; using Chickensoft.AutoInject;
using Chickensoft.Introspection; using Chickensoft.Introspection;
using Godot; using Godot;
using Zennysoft.Game.Implementation;
using Zennysoft.Ma.Adapter; using Zennysoft.Ma.Adapter;
using Zennysoft.Ma.Adapter.Entity; using Zennysoft.Ma.Adapter.Entity;
@@ -108,22 +109,22 @@ public partial class ThrownItem : RigidBody3D
{ {
switch (throwableItem.ThrowableItemTag) switch (throwableItem.ThrowableItemTag)
{ {
//case ThrowableItemTag.LowerTargetTo1HP: case ThrowableItemTag.LowerTargetTo1HP:
// enemy.TakeDamage(enemy.CurrentHP.Value - 1, ignoreDefense: true, ignoreElementalResistance: true); enemy.HealthComponent.SetHealth(1);
// break; break;
case ThrowableItemTag.TeleportToRandomLocation: case ThrowableItemTag.TeleportToRandomLocation:
_effectService.TeleportToRandomRoom(enemy); _effectService.TeleportToRandomRoom(enemy);
break; break;
default: default:
var damageDealt = DamageCalculator.CalculateDamage(new Damage(throwableItem.ThrowDamage, throwableItem.ElementType, false, false, false), 10, new ElementalResistanceSet(0, 0, 0, 0, 0)); var damageDealt = DamageCalculator.CalculateDamage(new Damage(throwableItem.ThrowDamage, throwableItem.ElementType, false, false, false), 10, new ElementalResistanceSet(0, 0, 0, 0, 0));
enemy.TakeDamage(damageDealt); enemy.HealthComponent.Damage(damageDealt);
break; break;
} }
} }
else else
{ {
var damageDealt = DamageCalculator.CalculateDamage(new Damage(ItemThatIsThrown.ThrowDamage, ElementType.None, false, false, false), 10, new ElementalResistanceSet(0, 0, 0, 0, 0)); var damageDealt = DamageCalculator.CalculateDamage(new Damage(ItemThatIsThrown.ThrowDamage, ElementType.None, false, false, false), 10, new ElementalResistanceSet(0, 0, 0, 0, 0));
enemy.TakeDamage(damageDealt); enemy.HealthComponent.Damage(damageDealt);
} }
} }
} }

View File

@@ -1,6 +1,7 @@
using Chickensoft.Introspection; using Chickensoft.Introspection;
using Chickensoft.Serialization; using Chickensoft.Serialization;
using Godot; using Godot;
using Zennysoft.Game.Implementation;
using Zennysoft.Ma.Adapter; using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma; namespace Zennysoft.Game.Ma;

View File

@@ -3,9 +3,7 @@ using Chickensoft.Collections;
using Chickensoft.GodotNodeInterfaces; using Chickensoft.GodotNodeInterfaces;
using Chickensoft.SaveFileBuilder; using Chickensoft.SaveFileBuilder;
using Godot; using Godot;
using System.Collections.Immutable;
using System.Threading.Tasks; using System.Threading.Tasks;
using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma; namespace Zennysoft.Game.Ma;

View File

@@ -2,6 +2,7 @@ using Chickensoft.AutoInject;
using Chickensoft.Introspection; using Chickensoft.Introspection;
using Godot; using Godot;
using System.Linq; using System.Linq;
using Zennysoft.Game.Implementation;
using Zennysoft.Ma.Adapter; using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma; namespace Zennysoft.Game.Ma;

View File

@@ -1,12 +1,10 @@
using Chickensoft.AutoInject; using Chickensoft.AutoInject;
using Chickensoft.Collections;
using Chickensoft.GodotNodeInterfaces; using Chickensoft.GodotNodeInterfaces;
using Chickensoft.Introspection; using Chickensoft.Introspection;
using Chickensoft.SaveFileBuilder; using Chickensoft.SaveFileBuilder;
using Godot; using Godot;
using SimpleInjector; using SimpleInjector;
using System; using System;
using Zennysoft.Game.Implementation.Components;
using Zennysoft.Ma.Adapter; using Zennysoft.Ma.Adapter;
using Zennysoft.Ma.Adapter.Entity; using Zennysoft.Ma.Adapter.Entity;
@@ -27,17 +25,19 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide<ISaveChunk<Play
public ISaveChunk<PlayerData> PlayerChunk { get; set; } = default!; public ISaveChunk<PlayerData> PlayerChunk { get; set; } = default!;
#endregion #endregion
public HealthComponent HealthComponent { get; private set; } public IHealthComponent HealthComponent { get; private set; }
public VTComponent VTComponent { get; private set; } public IVTComponent VTComponent { get; private set; }
public AttackComponent AttackComponent { get; private set; } public IAttackComponent AttackComponent { get; private set; }
public DefenseComponent DefenseComponent { get; private set; } public IDefenseComponent DefenseComponent { get; private set; }
public ExperiencePointsComponent ExperiencePointsComponent { get; private set; } public IExperiencePointsComponent ExperiencePointsComponent { get; private set; }
public LuckComponent LuckComponent { get; private set; } public ILuckComponent LuckComponent { get; private set; }
public IEquipmentComponent EquipmentComponent { get; private set; }
public Vector3 CurrentPosition => GlobalPosition; public Vector3 CurrentPosition => GlobalPosition;
@@ -45,15 +45,6 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide<ISaveChunk<Play
public IInventory Inventory { get; private set; } = default!; public IInventory Inventory { get; private set; } = default!;
public AutoProp<EquipableItem> EquippedWeapon => _equippedWeapon;
private AutoProp<EquipableItem> _equippedWeapon { get; set; } = new AutoProp<EquipableItem>(new Weapon());
public AutoProp<EquipableItem> EquippedArmor => _equippedArmor;
private AutoProp<EquipableItem> _equippedArmor { get; set; } = new AutoProp<EquipableItem>(new Armor());
public AutoProp<EquipableItem> EquippedAccessory => _equippedAccessory;
private AutoProp<EquipableItem> _equippedAccessory { get; set; } = new AutoProp<EquipableItem>(new Accessory());
private PlayerLogic.Settings Settings { get; set; } = default!; private PlayerLogic.Settings Settings { get; set; } = default!;
private IPlayerLogic PlayerLogic { get; set; } = default!; private IPlayerLogic PlayerLogic { get; set; } = default!;
@@ -119,14 +110,16 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide<ISaveChunk<Play
private Vector3 _knockbackDirection = Vector3.Zero; private Vector3 _knockbackDirection = Vector3.Zero;
#region Initialization #region Initialization
public void InitializePlayerState() public Player()
{ {
Inventory = new Inventory(); Inventory = new Inventory();
SetProcessInput(false); HealthComponent = new HealthComponent(InitialHP);
SetPhysicsProcess(false); VTComponent = new VTComponent(InitialVT);
AttackComponent = new AttackComponent(InitialAttack);
HealthTimer.WaitTime = _healthTimerWaitTime; DefenseComponent = new DefenseComponent(InitialDefense);
HealthTimer.Timeout += OnHealthTimerTimeout; ExperiencePointsComponent = new ExperiencePointsComponent();
LuckComponent = new LuckComponent(InitialLuck);
EquipmentComponent = new EquipmentComponent();
} }
public void Setup() public void Setup()
@@ -138,9 +131,6 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide<ISaveChunk<Play
PlayerLogic.Set(this as IPlayer); PlayerLogic.Set(this as IPlayer);
PlayerLogic.Set(Settings); PlayerLogic.Set(Settings);
PlayerLogic.Set(_gameRepo); PlayerLogic.Set(_gameRepo);
Hitbox.AreaEntered += Hitbox_AreaEntered;
CollisionDetector.AreaEntered += CollisionDetector_AreaEntered;
} }
public void OnResolved() public void OnResolved()
@@ -150,11 +140,13 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide<ISaveChunk<Play
PlayerChunk = new SaveChunk<PlayerData>( PlayerChunk = new SaveChunk<PlayerData>(
onSave: (chunk) => new PlayerData() onSave: (chunk) => new PlayerData()
{ {
Inventory = Inventory Inventory = (Inventory)Inventory,
HealthComponent = (HealthComponent)HealthComponent
}, },
onLoad: (chunk, data) => onLoad: (chunk, data) =>
{ {
Inventory = data.Inventory; Inventory = data.Inventory;
HealthComponent = data.HealthComponent;
} }
); );
@@ -173,22 +165,18 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide<ISaveChunk<Play
PlayerLogic.Start(); PlayerLogic.Start();
this.Provide(); this.Provide();
SetProcessInput(false);
SetPhysicsProcess(false);
} }
public void OnReady() public void OnReady()
{ {
Hitbox.AreaEntered += Hitbox_AreaEntered;
CollisionDetector.AreaEntered += CollisionDetector_AreaEntered;
SwordSlashAnimation.Position = GetViewport().GetVisibleRect().Size / 2; SwordSlashAnimation.Position = GetViewport().GetVisibleRect().Size / 2;
HealthComponent = new HealthComponent(InitialHP);
HealthComponent.HealthReachedZero += Die; HealthComponent.HealthReachedZero += Die;
HealthTimer.WaitTime = _healthTimerWaitTime;
VTComponent = new VTComponent(InitialVT); HealthTimer.Timeout += OnHealthTimerTimeout;
AttackComponent = new AttackComponent(InitialAttack); SetProcessInput(false);
DefenseComponent = new DefenseComponent(InitialDefense); SetPhysicsProcess(false);
ExperiencePointsComponent = new ExperiencePointsComponent();
LuckComponent = new LuckComponent(InitialLuck);
} }
#endregion #endregion
@@ -206,20 +194,7 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide<ISaveChunk<Play
SetHealthTimerStatus(false); SetHealthTimerStatus(false);
} }
public void Attack() private void SetHealthTimerStatus(bool isActive)
{
if (PlayerIsHittingGeometry())
{
AnimationPlayer.Play("hit_wall");
_gameRepo.OnPlayerAttackedWall();
}
else
{
PlayAttackAnimation();
}
}
public void SetHealthTimerStatus(bool isActive)
{ {
if (isActive) if (isActive)
HealthTimer.Start(); HealthTimer.Start();
@@ -227,22 +202,6 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide<ISaveChunk<Play
HealthTimer.Stop(); HealthTimer.Stop();
} }
public void Move(float delta)
{
var rawInput = GlobalInputVector;
var strafeLeftInput = LeftStrafeInputVector;
var strafeRightInput = RightStrafeInputVector;
var transform = Transform;
transform.Basis = new Basis(Vector3.Up, Settings.RotationSpeed * -rawInput.X * delta) * transform.Basis;
var moveDirection = new Vector3(strafeRightInput - strafeLeftInput, 0, rawInput.Z).Normalized();
var velocity = Basis * moveDirection * Settings.MoveSpeed * Settings.Acceleration;
_knockbackStrength *= 0.9f;
Transform = Transform with { Basis = transform.Basis };
Velocity = velocity + (_knockbackDirection * _knockbackStrength);
MoveAndSlide();
}
public void TeleportPlayer(Transform3D newTransform) public void TeleportPlayer(Transform3D newTransform)
{ {
Transform = newTransform; Transform = newTransform;
@@ -250,7 +209,7 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide<ISaveChunk<Play
public void TakeDamage(Damage damage) public void TakeDamage(Damage damage)
{ {
var damageReceived = DamageCalculator.CalculateDamage(damage, DefenseComponent.TotalDefense, ((Armor)_equippedArmor.Value).Stats.ElementalResistanceSet); var damageReceived = DamageCalculator.CalculateDamage(damage, DefenseComponent.TotalDefense, ((Armor)EquipmentComponent.EquippedArmor.Value).Stats.ElementalResistanceSet);
HealthComponent.Damage(damageReceived); HealthComponent.Damage(damageReceived);
} }
@@ -306,21 +265,21 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide<ISaveChunk<Play
if (equipable is Weapon weapon) if (equipable is Weapon weapon)
{ {
Unequip(_equippedWeapon.Value); Unequip(EquipmentComponent.EquippedWeapon.Value);
weapon.IsEquipped = true; weapon.IsEquipped = true;
_equippedWeapon.OnNext(weapon); EquipmentComponent.Equip(weapon);
} }
else if (equipable is Armor armor) else if (equipable is Armor armor)
{ {
Unequip(_equippedArmor.Value); Unequip(EquipmentComponent.EquippedArmor.Value);
armor.IsEquipped = true; armor.IsEquipped = true;
_equippedArmor.OnNext(armor); EquipmentComponent.Equip(armor);
} }
else if (equipable is Accessory accessory) else if (equipable is Accessory accessory)
{ {
Unequip(_equippedAccessory.Value); Unequip(EquipmentComponent.EquippedAccessory.Value);
accessory.IsEquipped = true; accessory.IsEquipped = true;
_equippedAccessory.OnNext(accessory); EquipmentComponent.Equip(accessory);
} }
else else
throw new NotImplementedException("Item type is not supported."); throw new NotImplementedException("Item type is not supported.");
@@ -331,17 +290,17 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide<ISaveChunk<Play
if (equipable is Weapon weapon) if (equipable is Weapon weapon)
{ {
weapon.IsEquipped = false; weapon.IsEquipped = false;
_equippedWeapon.OnNext(new Weapon()); EquipmentComponent.Unequip(weapon);
} }
else if (equipable is Armor armor) else if (equipable is Armor armor)
{ {
armor.IsEquipped = false; armor.IsEquipped = false;
_equippedArmor.OnNext(new Armor()); EquipmentComponent.Unequip(armor);
} }
else if (equipable is Accessory accessory) else if (equipable is Accessory accessory)
{ {
accessory.IsEquipped = false; accessory.IsEquipped = false;
_equippedAccessory.OnNext(new Accessory()); EquipmentComponent.Unequip(accessory);
} }
else else
throw new NotImplementedException("Item type is not supported."); throw new NotImplementedException("Item type is not supported.");
@@ -365,6 +324,19 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide<ISaveChunk<Play
private static float RightStrafeInputVector => Input.GetActionStrength(GameInputs.StrafeRight); private static float RightStrafeInputVector => Input.GetActionStrength(GameInputs.StrafeRight);
private void Attack()
{
if (PlayerIsHittingGeometry())
{
AnimationPlayer.Play("hit_wall");
_gameRepo.OnPlayerAttackedWall();
}
else
{
PlayAttackAnimation();
}
}
private void ThrowItem() private void ThrowItem()
{ {
var itemScene = GD.Load<PackedScene>("res://src/items/throwable/ThrowableItem.tscn"); var itemScene = GD.Load<PackedScene>("res://src/items/throwable/ThrowableItem.tscn");
@@ -376,7 +348,7 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide<ISaveChunk<Play
private void PlayAttackAnimation() private void PlayAttackAnimation()
{ {
var attackSpeed = ((Weapon)EquippedWeapon.Value).AttackSpeed; var attackSpeed = ((Weapon)EquipmentComponent.EquippedWeapon.Value).AttackSpeed;
AnimationPlayer.SetSpeedScale((float)attackSpeed); AnimationPlayer.SetSpeedScale((float)attackSpeed);
AnimationPlayer.Play("attack"); AnimationPlayer.Play("attack");
_gameRepo.OnPlayerAttack(); _gameRepo.OnPlayerAttack();
@@ -388,13 +360,29 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide<ISaveChunk<Play
PlayerBinding.Dispose(); PlayerBinding.Dispose();
} }
private void Move(float delta)
{
var rawInput = GlobalInputVector;
var strafeLeftInput = LeftStrafeInputVector;
var strafeRightInput = RightStrafeInputVector;
var transform = Transform;
transform.Basis = new Basis(Vector3.Up, Settings.RotationSpeed * -rawInput.X * delta) * transform.Basis;
var moveDirection = new Vector3(strafeRightInput - strafeLeftInput, 0, rawInput.Z).Normalized();
var velocity = Basis * moveDirection * Settings.MoveSpeed * Settings.Acceleration;
_knockbackStrength *= 0.9f;
Transform = Transform with { Basis = transform.Basis };
Velocity = velocity + (_knockbackDirection * _knockbackStrength);
MoveAndSlide();
}
private void OnPlayerPositionUpdated(Vector3 globalPosition) => GlobalPosition = globalPosition; private void OnPlayerPositionUpdated(Vector3 globalPosition) => GlobalPosition = globalPosition;
private void OnHealthTimerTimeout() private void OnHealthTimerTimeout()
{ {
if (VTComponent.CurrentVT.Value > 0) if (VTComponent.CurrentVT.Value > 0)
{ {
if (((Accessory)EquippedAccessory.Value).AccessoryTag == AccessoryTag.HalfVTConsumption) if (((Accessory)EquipmentComponent.EquippedAccessory.Value).AccessoryTag == AccessoryTag.HalfVTConsumption)
reduceOnTick = !reduceOnTick; reduceOnTick = !reduceOnTick;
HealthComponent.Heal(1); HealthComponent.Heal(1);
@@ -415,21 +403,21 @@ public partial class Player : CharacterBody3D, IPlayer, IProvide<ISaveChunk<Play
private void HitEnemy(IEnemy enemy) private void HitEnemy(IEnemy enemy)
{ {
var ignoreElementalResistance = ((Weapon)EquippedWeapon.Value).WeaponTag == WeaponTag.IgnoreAffinity; var ignoreElementalResistance = ((Weapon)EquipmentComponent.EquippedWeapon.Value).WeaponTag == WeaponTag.IgnoreAffinity;
var isCriticalHit = BattleExtensions.IsCriticalHit(LuckComponent.Luck.Value); var isCriticalHit = BattleExtensions.IsCriticalHit(LuckComponent.Luck.Value);
var element = ((Weapon)EquippedWeapon.Value).WeaponElement; var element = ((Weapon)EquipmentComponent.EquippedWeapon.Value).WeaponElement;
var baseAttack = new Damage( var baseAttack = new Damage(
(int)(AttackComponent.TotalAttack * ((Weapon)EquippedWeapon.Value).ElementalDamageBonus), (int)(AttackComponent.TotalAttack * ((Weapon)EquipmentComponent.EquippedWeapon.Value).ElementalDamageBonus),
element, element,
false, false,
false, false,
ignoreElementalResistance); ignoreElementalResistance);
var damageDealt = DamageCalculator.CalculateDamage(baseAttack, enemy.DefenseComponent.TotalDefense, new ElementalResistanceSet(0, 0, 0, 0, 0)); var damageDealt = DamageCalculator.CalculateDamage(baseAttack, enemy.DefenseComponent.TotalDefense, new ElementalResistanceSet(0, 0, 0, 0, 0));
enemy.TakeDamage(damageDealt); enemy.HealthComponent.Damage(damageDealt);
if (((Weapon)EquippedWeapon.Value).WeaponTag == WeaponTag.Knockback && enemy is IKnockbackable knockbackable) if (((Weapon)EquipmentComponent.EquippedWeapon.Value).WeaponTag == WeaponTag.Knockback && enemy is IKnockbackable knockbackable)
knockbackable.Knockback(0.3f, -CurrentBasis.Z.Normalized()); knockbackable.Knockback(0.3f, -CurrentBasis.Z.Normalized());
if (((Weapon)EquippedWeapon.Value).WeaponTag == WeaponTag.SelfDamage) if (((Weapon)EquipmentComponent.EquippedWeapon.Value).WeaponTag == WeaponTag.SelfDamage)
TakeDamage(new Damage(5, ElementType.None, false, true, true)); TakeDamage(new Damage(5, ElementType.None, false, true, true));
_gameRepo.OnPlayerAttackedEnemy(); _gameRepo.OnPlayerAttackedEnemy();

View File

@@ -1,19 +0,0 @@
using Chickensoft.Collections;
using Zennysoft.Game.Implementation.Components;
namespace Zennysoft.Game.Ma;
public interface ICharacterStats
{
public HealthComponent HP { get; }
public IAutoProp<int> CurrentAttack { get; }
public IAutoProp<int> CurrentDefense { get; }
public IAutoProp<int> MaxAttack { get; }
public IAutoProp<int> MaxDefense { get; }
public IAutoProp<double> Luck { get; }
}

View File

@@ -0,0 +1,18 @@
using Chickensoft.GodotNodeInterfaces;
using Zennysoft.Game.Implementation;
using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma;
public interface IItemSlot : IHBoxContainer
{
public InventoryItem Item { get; set; }
public void SetItemStyle();
public void SetSelectedItemStyle();
public void SetEquippedItemStyle();
public void SetEquippedSelectedItemStyle();
}

View File

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

View File

@@ -4,6 +4,7 @@ using Chickensoft.Introspection;
using Godot; using Godot;
using System.Linq; using System.Linq;
using System.Threading.Tasks; using System.Threading.Tasks;
using Zennysoft.Game.Implementation;
using Zennysoft.Ma.Adapter; using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma; namespace Zennysoft.Game.Ma;
@@ -344,7 +345,7 @@ public partial class InventoryMenu : Control, IInventoryMenu
if (equipableItem.IsEquipped) if (equipableItem.IsEquipped)
{ {
ItemEffectLabel.Text = $"{itemSlot.Item.GetType()} unequipped."; ItemEffectLabel.Text = $"{itemSlot.Item.GetType()} unequipped.";
Player.Unequip(equipableItem); Player.EquipmentComponent.Unequip(equipableItem);
itemSlot.SetSelectedItemStyle(); itemSlot.SetSelectedItemStyle();
if (equipableItem.ItemTag == ItemTag.BreaksOnChange) if (equipableItem.ItemTag == ItemTag.BreaksOnChange)
Player.Inventory.Remove(equipableItem); Player.Inventory.Remove(equipableItem);
@@ -352,7 +353,7 @@ public partial class InventoryMenu : Control, IInventoryMenu
else else
{ {
ItemEffectLabel.Text = $"{itemSlot.Item.GetType()} equipped."; ItemEffectLabel.Text = $"{itemSlot.Item.GetType()} equipped.";
Player.Equip(equipableItem); Player.EquipmentComponent.Equip(equipableItem);
itemSlot.SetEquippedSelectedItemStyle(); itemSlot.SetEquippedSelectedItemStyle();
} }

View File

@@ -1,25 +1,12 @@
using Chickensoft.AutoInject; using Chickensoft.AutoInject;
using Chickensoft.GodotNodeInterfaces;
using Chickensoft.Introspection; using Chickensoft.Introspection;
using Godot; using Godot;
using Zennysoft.Game.Abstractions; using Zennysoft.Game.Abstractions;
using Zennysoft.Game.Implementation;
using Zennysoft.Ma.Adapter; using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma; namespace Zennysoft.Game.Ma;
public interface IItemSlot : IHBoxContainer
{
public InventoryItem Item { get; set; }
public void SetItemStyle();
public void SetSelectedItemStyle();
public void SetEquippedItemStyle();
public void SetEquippedSelectedItemStyle();
}
[Meta(typeof(IAutoNode))] [Meta(typeof(IAutoNode))]
public partial class ItemSlot : HBoxContainer, IItemSlot public partial class ItemSlot : HBoxContainer, IItemSlot
{ {
@@ -27,8 +14,6 @@ public partial class ItemSlot : HBoxContainer, IItemSlot
[Dependency] public IPlayer Player => this.DependOn<IPlayer>(); [Dependency] public IPlayer Player => this.DependOn<IPlayer>();
//[Node] public Label EquipBonus { get; set; } = default!;
[Node] public TextureRect ItemTexture { get; set; } = default!; [Node] public TextureRect ItemTexture { get; set; } = default!;
[Node] public Label ItemName { get; set; } = default!; [Node] public Label ItemName { get; set; } = default!;
@@ -36,7 +21,9 @@ public partial class ItemSlot : HBoxContainer, IItemSlot
[Node] public Label ItemCount { get; set; } = default!; [Node] public Label ItemCount { get; set; } = default!;
private static LabelSettings ItemFont => GD.Load<LabelSettings>("res://src/ui/label_settings/MainTextBold.tres"); private static LabelSettings ItemFont => GD.Load<LabelSettings>("res://src/ui/label_settings/MainTextBold.tres");
private static LabelSettings SelectedItemFont => GD.Load<LabelSettings>("res://src/ui/label_settings/MainTextFontItalicized.tres"); private static LabelSettings SelectedItemFont => GD.Load<LabelSettings>("res://src/ui/label_settings/MainTextFontItalicized.tres");
private static LabelSettings EquippedItemFont => GD.Load<LabelSettings>("res://src/ui/label_settings/MainTextFontEquipped.tres"); private static LabelSettings EquippedItemFont => GD.Load<LabelSettings>("res://src/ui/label_settings/MainTextFontEquipped.tres");
private static LabelSettings SelectedEquippedItemFont => GD.Load<LabelSettings>("res://src/ui/label_settings/MainTextFontSelectedEquipped.tres"); private static LabelSettings SelectedEquippedItemFont => GD.Load<LabelSettings>("res://src/ui/label_settings/MainTextFontSelectedEquipped.tres");
@@ -45,9 +32,9 @@ public partial class ItemSlot : HBoxContainer, IItemSlot
{ {
ItemName.Text = Item.ItemName; ItemName.Text = Item.ItemName;
ItemTexture.Texture = Item.GetTexture(); ItemTexture.Texture = Item.GetTexture();
Player.EquippedWeapon.Sync += EquipableItem_Sync; Player.EquipmentComponent.EquippedWeapon.Sync += EquipableItem_Sync;
Player.EquippedArmor.Sync += EquipableItem_Sync; Player.EquipmentComponent.EquippedArmor.Sync += EquipableItem_Sync;
Player.EquippedAccessory.Sync += EquipableItem_Sync; Player.EquipmentComponent.EquippedAccessory.Sync += EquipableItem_Sync;
if (Item is IStackable stackableItem) if (Item is IStackable stackableItem)
{ {

View File

@@ -4,6 +4,7 @@ using Godot;
using System; using System;
using System.Collections.Immutable; using System.Collections.Immutable;
using System.Linq; using System.Linq;
using Zennysoft.Game.Implementation;
using Zennysoft.Ma.Adapter; using Zennysoft.Ma.Adapter;
namespace Zennysoft.Game.Ma; namespace Zennysoft.Game.Ma;