Start restructure of game data to support save/load
This commit is contained in:
@@ -13,10 +13,12 @@
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Chickensoft.AutoInject" Version="2.5.0" />
|
||||
<PackageReference Include="Chickensoft.GodotNodeInterfaces" Version="2.4.0" />
|
||||
<PackageReference Include="Chickensoft.Introspection" Version="2.2.0" />
|
||||
<PackageReference Include="Chickensoft.Introspection.Generator" Version="2.2.0" />
|
||||
<PackageReference Include="Chickensoft.LogicBlocks" Version="5.15.0" />
|
||||
<PackageReference Include="Chickensoft.LogicBlocks.DiagramGenerator" Version="5.15.0" />
|
||||
<PackageReference Include="Chickensoft.SaveFileBuilder" Version="1.1.0" />
|
||||
<PackageReference Include="Chickensoft.Serialization.Godot" Version="0.7.6" />
|
||||
<PackageReference Include="GodotSharp.SourceGenerators" Version="2.5.0" />
|
||||
<PackageReference Include="SSH.NET" Version="2024.2.0" />
|
||||
<PackageReference Include="System.IO.Abstractions" Version="21.2.1" />
|
||||
|
||||
9
GlobalSuppressions.cs
Normal file
9
GlobalSuppressions.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
// This file is used by Code Analysis to maintain SuppressMessage
|
||||
// attributes that are applied to this project.
|
||||
// Project-level suppressions either have no target or are given
|
||||
// a specific target and scoped to a namespace, type, member, etc.
|
||||
|
||||
using System.Diagnostics.CodeAnalysis;
|
||||
|
||||
[assembly: SuppressMessage("Trimming", "IL2026:Members annotated with 'RequiresUnreferencedCodeAttribute' require dynamic access otherwise can break functionality when trimming application code", Justification = "<Pending>", Scope = "member", Target = "~M:GameJamDungeon.Game.OnResolved")]
|
||||
[assembly: SuppressMessage("AOT", "IL3050:Calling members annotated with 'RequiresDynamicCodeAttribute' may break functionality when AOT compiling.", Justification = "<Pending>", Scope = "member", Target = "~M:GameJamDungeon.Game.OnResolved")]
|
||||
@@ -2,9 +2,16 @@
|
||||
namespace GameJamDungeon;
|
||||
|
||||
using Chickensoft.AutoInject;
|
||||
using Chickensoft.Collections;
|
||||
using Chickensoft.Introspection;
|
||||
using Chickensoft.SaveFileBuilder;
|
||||
using Chickensoft.Serialization;
|
||||
using Chickensoft.Serialization.Godot;
|
||||
using Godot;
|
||||
using Org.BouncyCastle.Asn1.Pkcs;
|
||||
using System;
|
||||
using System.IO.Abstractions;
|
||||
using System.Text.Json;
|
||||
using static GameJamDungeon.GameLogic.State;
|
||||
|
||||
[Meta(typeof(IAutoNode))]
|
||||
@@ -53,10 +60,30 @@ public partial class Game : Node3D, IGame
|
||||
[Node] private IPlayer Player { get; set; } = default!;
|
||||
#endregion
|
||||
|
||||
#region Save
|
||||
public JsonSerializerOptions JsonOptions { get; set; } = default!;
|
||||
|
||||
public const string SAVE_FILE_NAME = "game.json";
|
||||
|
||||
public IFileSystem FileSystem { get; set; } = default!;
|
||||
|
||||
public string SaveFilePath { get; set; } = default!;
|
||||
|
||||
public ISaveFile<GameData> SaveFile { get; set; } = default!;
|
||||
|
||||
public ISaveChunk<GameData> GameChunk { get; set; } = default!;
|
||||
|
||||
ISaveChunk<GameData> IProvide<ISaveChunk<GameData>>.Value() => GameChunk;
|
||||
#endregion
|
||||
|
||||
public RescuedItemDatabase RescuedItems { get; set; } = default!;
|
||||
|
||||
public void Setup()
|
||||
{
|
||||
FileSystem = new FileSystem();
|
||||
|
||||
SaveFilePath = FileSystem.Path.Join(OS.GetUserDataDir(), SAVE_FILE_NAME);
|
||||
|
||||
GameRepo = new GameRepo();
|
||||
GameLogic = new GameLogic();
|
||||
GameEventDepot = new GameEventDepot();
|
||||
@@ -66,10 +93,78 @@ public partial class Game : Node3D, IGame
|
||||
GameLogic.Set(Player);
|
||||
Instantiator = new Instantiator(GetTree());
|
||||
RescuedItems = new RescuedItemDatabase();
|
||||
|
||||
var resolver = new SerializableTypeResolver();
|
||||
GodotSerialization.Setup();
|
||||
|
||||
var upgradeDependencies = new Blackboard();
|
||||
|
||||
JsonOptions = new JsonSerializerOptions
|
||||
{
|
||||
Converters = {
|
||||
new SerializableTypeConverter(upgradeDependencies)
|
||||
},
|
||||
TypeInfoResolver = resolver,
|
||||
WriteIndented = true
|
||||
};
|
||||
|
||||
GameChunk = new SaveChunk<GameData>(
|
||||
(chunk) =>
|
||||
{
|
||||
var gameData = new GameData()
|
||||
{
|
||||
PlayerData = new PlayerStats
|
||||
{
|
||||
CurrentHP = Player.Stats.CurrentHP.Value,
|
||||
MaximumHP = Player.Stats.MaximumHP.Value,
|
||||
CurrentVT = Player.Stats.CurrentVT.Value,
|
||||
MaximumVT = Player.Stats.MaximumVT.Value,
|
||||
CurrentAttack = Player.Stats.CurrentAttack.Value,
|
||||
BonusAttack = Player.Stats.BonusAttack.Value,
|
||||
MaxAttack = Player.Stats.MaxAttack.Value,
|
||||
CurrentDefense = Player.Stats.CurrentDefense.Value,
|
||||
BonusDefense = Player.Stats.BonusDefense.Value,
|
||||
MaxDefense = Player.Stats.MaxDefense.Value,
|
||||
CurrentExp = Player.Stats.CurrentExp.Value,
|
||||
CurrentLevel = Player.Stats.CurrentLevel.Value,
|
||||
ExpToNextLevel = Player.Stats.ExpToNextLevel.Value,
|
||||
Luck = Player.Stats.Luck.Value
|
||||
},
|
||||
};
|
||||
|
||||
return gameData;
|
||||
},
|
||||
onLoad: (chunk, data) =>
|
||||
{
|
||||
chunk.LoadChunkSaveData(data.PlayerData);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
public void OnResolved()
|
||||
{
|
||||
SaveFile = new SaveFile<GameData>(
|
||||
root: GameChunk,
|
||||
onSave: async (GameData data) =>
|
||||
{
|
||||
// Save the game data to disk.
|
||||
var json = JsonSerializer.Serialize(data, JsonOptions);
|
||||
await FileSystem.File.WriteAllTextAsync(SaveFilePath, json);
|
||||
},
|
||||
onLoad: async () =>
|
||||
{
|
||||
// Load the game data from disk.
|
||||
if (!FileSystem.File.Exists(SaveFilePath))
|
||||
{
|
||||
GD.Print("No save file to load");
|
||||
return null;
|
||||
}
|
||||
|
||||
var json = await FileSystem.File.ReadAllTextAsync(SaveFilePath);
|
||||
return JsonSerializer.Deserialize<GameData>(json, JsonOptions);
|
||||
}
|
||||
);
|
||||
|
||||
GameBinding = GameLogic.Bind();
|
||||
GameBinding
|
||||
.Handle((in GameLogic.Output.StartGame _) =>
|
||||
@@ -131,6 +226,8 @@ public partial class Game : Node3D, IGame
|
||||
|
||||
public void ToggleInventory()
|
||||
{
|
||||
SaveFile.Load();
|
||||
|
||||
if (GameLogic.Value is InventoryOpened)
|
||||
GameLogic.Input(new GameLogic.Input.CloseInventory());
|
||||
else
|
||||
|
||||
@@ -3,10 +3,11 @@ namespace GameJamDungeon;
|
||||
|
||||
using Chickensoft.AutoInject;
|
||||
using Chickensoft.GodotNodeInterfaces;
|
||||
using Chickensoft.SaveFileBuilder;
|
||||
using Godot;
|
||||
using System;
|
||||
|
||||
public interface IGame : IProvide<IGameRepo>, IProvide<IGameEventDepot>, IProvide<IGame>, IProvide<IPlayer>, INode3D
|
||||
public interface IGame : IProvide<IGameRepo>, IProvide<IGameEventDepot>, IProvide<IGame>, IProvide<IPlayer>, IProvide<ISaveChunk<GameData>>, INode3D
|
||||
{
|
||||
event Game.StatRaisedAlertEventHandler StatRaisedAlert;
|
||||
|
||||
|
||||
@@ -37,14 +37,14 @@ public partial class ConsumableItem : Node3D, IUsableItem
|
||||
|
||||
public void Use()
|
||||
{
|
||||
if (Player.Stats.CurrentHP.Value == Player.Stats.MaximumHP.Value && _consumableItemStats.RaiseHPAmount > 0)
|
||||
if (Player.Stats.CurrentHP == Player.Stats.MaximumHP && _consumableItemStats.RaiseHPAmount > 0)
|
||||
Player.RaiseHP(_consumableItemStats.RaiseHPAmount);
|
||||
if (Player.Stats.CurrentVT.Value == Player.Stats.MaximumVT.Value && _consumableItemStats.RaiseVTAmount > 0)
|
||||
if (Player.Stats.CurrentVT == Player.Stats.MaximumVT && _consumableItemStats.RaiseVTAmount > 0)
|
||||
Player.RaiseVT(_consumableItemStats.RaiseVTAmount);
|
||||
|
||||
if (_consumableItemStats.HealHPAmount > 0 && Player.Stats.CurrentHP.Value != Player.Stats.MaximumHP.Value)
|
||||
if (_consumableItemStats.HealHPAmount > 0 && Player.Stats.CurrentHP != Player.Stats.MaximumHP)
|
||||
Player.HealHP(_consumableItemStats.HealHPAmount);
|
||||
if (_consumableItemStats.HealVTAmount > 0 && Player.Stats.CurrentVT.Value != Player.Stats.MaximumVT.Value)
|
||||
if (_consumableItemStats.HealVTAmount > 0 && Player.Stats.CurrentVT != Player.Stats.MaximumVT)
|
||||
Player.HealVT(_consumableItemStats.HealVTAmount);
|
||||
}
|
||||
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
using Chickensoft.Collections;
|
||||
using Chickensoft.AutoInject;
|
||||
using Chickensoft.Collections;
|
||||
using Chickensoft.SaveFileBuilder;
|
||||
using Godot;
|
||||
|
||||
namespace GameJamDungeon;
|
||||
|
||||
public interface IPlayer : IKillable
|
||||
public interface IPlayer : IKillable, IProvide<ISaveChunk<PlayerStats>>
|
||||
{
|
||||
public void Attack();
|
||||
|
||||
@@ -47,7 +49,7 @@ public interface IPlayer : IKillable
|
||||
|
||||
public IInventory Inventory { get; }
|
||||
|
||||
public PlayerStats Stats { get; }
|
||||
public PlayerStatController Stats { get; }
|
||||
|
||||
public Vector3 CurrentPosition { get; }
|
||||
|
||||
|
||||
@@ -19,12 +19,16 @@ public partial class Player : CharacterBody3D, IPlayer
|
||||
private PlayerLogic.IBinding PlayerBinding { get; set; } = default!;
|
||||
#endregion
|
||||
|
||||
#region Save
|
||||
public ISaveChunk<PlayerStats> PlayerChunk { get; set; } = default!;
|
||||
#endregion
|
||||
|
||||
public double CurrentHP => Stats.CurrentHP.Value;
|
||||
|
||||
public Vector3 CurrentPosition => GlobalPosition;
|
||||
|
||||
public Basis CurrentBasis => Transform.Basis;
|
||||
public PlayerStats Stats { get; set; } = default!;
|
||||
public PlayerStatController Stats { get; set; } = default!;
|
||||
|
||||
public IInventory Inventory { get; private set; } = default!;
|
||||
|
||||
@@ -48,6 +52,8 @@ public partial class Player : CharacterBody3D, IPlayer
|
||||
|
||||
[Dependency]
|
||||
public ISaveChunk<GameData> GameChunk => this.DependOn<ISaveChunk<GameData>>();
|
||||
|
||||
ISaveChunk<PlayerStats> IProvide<ISaveChunk<PlayerStats>>.Value() => PlayerChunk;
|
||||
#endregion
|
||||
|
||||
#region Event Signals
|
||||
@@ -110,25 +116,28 @@ public partial class Player : CharacterBody3D, IPlayer
|
||||
public void Setup()
|
||||
{
|
||||
Settings = new PlayerLogic.Settings() { RotationSpeed = PlayerStatResource.RotationSpeed, MoveSpeed = PlayerStatResource.MoveSpeed, Acceleration = PlayerStatResource.Acceleration };
|
||||
Stats = new PlayerStatController();
|
||||
Stats.Init(
|
||||
new PlayerStats
|
||||
{
|
||||
CurrentHP = PlayerStatResource.CurrentHP,
|
||||
MaximumHP = PlayerStatResource.MaximumHP,
|
||||
CurrentVT = PlayerStatResource.CurrentVT,
|
||||
MaximumVT = PlayerStatResource.MaximumVT,
|
||||
CurrentAttack = PlayerStatResource.CurrentAttack,
|
||||
BonusAttack = PlayerStatResource.BonusAttack,
|
||||
MaxAttack = PlayerStatResource.MaxAttack,
|
||||
CurrentDefense = PlayerStatResource.CurrentDefense,
|
||||
BonusDefense = PlayerStatResource.BonusDefense,
|
||||
MaxDefense = PlayerStatResource.MaxDefense,
|
||||
CurrentExp = PlayerStatResource.CurrentExp,
|
||||
CurrentLevel = PlayerStatResource.CurrentLevel,
|
||||
ExpToNextLevel = PlayerStatResource.ExpToNextLevel,
|
||||
Luck = PlayerStatResource.Luck
|
||||
});
|
||||
|
||||
Stats = new PlayerStats();
|
||||
Inventory = new Inventory();
|
||||
|
||||
Stats.SetCurrentHP(PlayerStatResource.CurrentHP);
|
||||
Stats.SetMaximumHP(PlayerStatResource.MaximumHP);
|
||||
Stats.SetCurrentVT(PlayerStatResource.CurrentVT);
|
||||
Stats.SetMaximumVT(PlayerStatResource.MaximumVT);
|
||||
Stats.SetCurrentAttack(PlayerStatResource.CurrentAttack);
|
||||
Stats.SetBonusAttack(PlayerStatResource.BonusAttack);
|
||||
Stats.SetMaxAttack(PlayerStatResource.MaxAttack);
|
||||
Stats.SetCurrentDefense(PlayerStatResource.CurrentDefense);
|
||||
Stats.SetBonusDefense(PlayerStatResource.BonusDefense);
|
||||
Stats.SetMaxDefense(PlayerStatResource.MaxDefense);
|
||||
Stats.SetCurrentExp(PlayerStatResource.CurrentExp);
|
||||
Stats.SetCurrentLevel(PlayerStatResource.CurrentLevel);
|
||||
Stats.SetExpToNextLevel(PlayerStatResource.ExpToNextLevel);
|
||||
Stats.SetLuck(PlayerStatResource.Luck);
|
||||
|
||||
PlayerLogic = new PlayerLogic();
|
||||
PlayerLogic.Set(this as IPlayer);
|
||||
PlayerLogic.Set(Settings);
|
||||
@@ -155,6 +164,30 @@ public partial class Player : CharacterBody3D, IPlayer
|
||||
|
||||
public void OnResolved()
|
||||
{
|
||||
PlayerChunk = new SaveChunk<PlayerStats>(
|
||||
onSave: (chunk) => new PlayerStats
|
||||
{
|
||||
CurrentHP = Stats.CurrentHP.Value,
|
||||
MaximumHP = Stats.MaximumHP.Value,
|
||||
CurrentVT = Stats.CurrentVT.Value,
|
||||
MaximumVT = Stats.MaximumVT.Value,
|
||||
CurrentAttack = Stats.CurrentAttack.Value,
|
||||
BonusAttack = Stats.BonusAttack.Value,
|
||||
MaxAttack = Stats.MaxAttack.Value,
|
||||
CurrentDefense = Stats.CurrentDefense.Value,
|
||||
BonusDefense = Stats.BonusDefense.Value,
|
||||
MaxDefense = Stats.MaxDefense.Value,
|
||||
CurrentExp = Stats.CurrentExp.Value,
|
||||
CurrentLevel = Stats.CurrentLevel.Value,
|
||||
ExpToNextLevel = Stats.ExpToNextLevel.Value,
|
||||
Luck = Stats.Luck.Value
|
||||
},
|
||||
onLoad: (chunk, data) =>
|
||||
{
|
||||
Stats.Init(data);
|
||||
}
|
||||
);
|
||||
|
||||
PlayerBinding = PlayerLogic.Bind();
|
||||
|
||||
PlayerBinding
|
||||
@@ -169,8 +202,10 @@ public partial class Player : CharacterBody3D, IPlayer
|
||||
{
|
||||
});
|
||||
|
||||
this.Provide();
|
||||
GameChunk.AddChunk(PlayerChunk);
|
||||
|
||||
PlayerLogic.Start();
|
||||
this.Provide();
|
||||
HealthTimer.Timeout += OnHealthTimerTimeout;
|
||||
Hitbox.AreaEntered += Hitbox_AreaEntered;
|
||||
}
|
||||
@@ -225,7 +260,7 @@ public partial class Player : CharacterBody3D, IPlayer
|
||||
|
||||
public void RaiseVT(int amountToRaise)
|
||||
{
|
||||
if (Stats.CurrentVT.Value == Stats.MaximumVT.Value)
|
||||
if (Stats.CurrentVT == Stats.MaximumVT)
|
||||
{
|
||||
Stats.SetMaximumVT(Stats.MaximumVT.Value + amountToRaise);
|
||||
Stats.SetCurrentVT(Stats.MaximumVT.Value);
|
||||
|
||||
@@ -1,38 +1,76 @@
|
||||
using Chickensoft.Collections;
|
||||
using Chickensoft.Introspection;
|
||||
using Chickensoft.Serialization;
|
||||
using Godot;
|
||||
|
||||
namespace GameJamDungeon;
|
||||
|
||||
[Meta, Id("player_stats")]
|
||||
public partial record PlayerStats
|
||||
{
|
||||
[Save("currentHP")]
|
||||
public IAutoProp<int> CurrentHP => _currentHP;
|
||||
public int CurrentHP { get; init; }
|
||||
[Save("maximumHP")]
|
||||
public IAutoProp<int> MaximumHP => _maximumHP;
|
||||
public int MaximumHP { get; init; }
|
||||
[Save("currentVT")]
|
||||
public IAutoProp<int> CurrentVT => _currentVT;
|
||||
public int CurrentVT { get; init; }
|
||||
[Save("maximumVT")]
|
||||
public IAutoProp<int> MaximumVT => _maximumVT;
|
||||
public int MaximumVT { get; init; }
|
||||
[Save("currentExp")]
|
||||
public IAutoProp<int> CurrentExp => _currentExp;
|
||||
public int CurrentExp { get; init; }
|
||||
[Save("currentLevel")]
|
||||
public IAutoProp<int> CurrentLevel => _currentLevel;
|
||||
public int CurrentLevel { get; init; }
|
||||
[Save("currentAttack")]
|
||||
public IAutoProp<int> CurrentAttack => _currentAttack;
|
||||
public int CurrentAttack { get; init; }
|
||||
[Save("bonusAttack")]
|
||||
public IAutoProp<int> BonusAttack => _bonusAttack;
|
||||
public int BonusAttack { get; init; }
|
||||
[Save("maxAttack")]
|
||||
public IAutoProp<int> MaxAttack => _maxAttack;
|
||||
public int MaxAttack { get; init; }
|
||||
[Save("currentDefense")]
|
||||
public IAutoProp<int> CurrentDefense => _currentDefense;
|
||||
public int CurrentDefense { get; init; }
|
||||
[Save("bonusDefense")]
|
||||
public IAutoProp<int> BonusDefense => _bonusDefense;
|
||||
public int BonusDefense { get; init; }
|
||||
[Save("maxDefense")]
|
||||
public IAutoProp<int> MaxDefense => _maxDefense;
|
||||
public int MaxDefense { get; init; }
|
||||
[Save("expToNextLevel")]
|
||||
public IAutoProp<int> ExpToNextLevel => _expToNextLevel;
|
||||
public int ExpToNextLevel { get; init; }
|
||||
[Save("luck")]
|
||||
public double Luck { get; init; }
|
||||
}
|
||||
|
||||
public class PlayerStatController
|
||||
{
|
||||
public void Init(PlayerStats playerStats)
|
||||
{
|
||||
_currentHP.OnNext(playerStats.CurrentHP);
|
||||
_maximumHP.OnNext(playerStats.MaximumHP);
|
||||
_currentVT.OnNext(playerStats.CurrentVT);
|
||||
_maximumVT.OnNext(playerStats.MaximumVT);
|
||||
_currentExp.OnNext(playerStats.CurrentExp);
|
||||
_currentLevel.OnNext(playerStats.CurrentLevel);
|
||||
_currentAttack.OnNext(playerStats.CurrentAttack);
|
||||
_bonusAttack.OnNext(playerStats.BonusAttack);
|
||||
_maxAttack.OnNext(playerStats.MaxAttack);
|
||||
_currentDefense.OnNext(playerStats.CurrentDefense);
|
||||
_bonusDefense.OnNext(playerStats.BonusDefense);
|
||||
_maxDefense.OnNext(playerStats.MaxDefense);
|
||||
_expToNextLevel.OnNext(playerStats.ExpToNextLevel);
|
||||
_luck.OnNext(playerStats.Luck);
|
||||
}
|
||||
|
||||
public IAutoProp<int> CurrentHP => _currentHP;
|
||||
public IAutoProp<int> MaximumHP => _maximumHP;
|
||||
public IAutoProp<int> CurrentVT => _currentVT;
|
||||
public IAutoProp<int> MaximumVT => _maximumVT;
|
||||
public IAutoProp<int> CurrentAttack => _currentAttack;
|
||||
public IAutoProp<int> MaxAttack => _maxAttack;
|
||||
public IAutoProp<int> BonusAttack => _bonusAttack;
|
||||
public IAutoProp<int> CurrentDefense => _currentDefense;
|
||||
public IAutoProp<int> MaxDefense => _maxDefense;
|
||||
public IAutoProp<int> BonusDefense => _bonusDefense;
|
||||
public IAutoProp<int> CurrentExp => _currentExp;
|
||||
public IAutoProp<int> ExpToNextLevel => _expToNextLevel;
|
||||
public IAutoProp<int> CurrentLevel => _currentLevel;
|
||||
public IAutoProp<double> Luck => _luck;
|
||||
|
||||
public void SetCurrentHP(int newValue)
|
||||
@@ -97,18 +135,18 @@ public partial record PlayerStats
|
||||
_luck.OnNext(clampedValue);
|
||||
}
|
||||
|
||||
private readonly AutoProp<int> _currentHP = new(int.MaxValue);
|
||||
private readonly AutoProp<int> _maximumHP = new(int.MaxValue);
|
||||
private readonly AutoProp<int> _currentVT = new(int.MaxValue);
|
||||
private readonly AutoProp<int> _maximumVT = new(int.MaxValue);
|
||||
private readonly AutoProp<int> _currentExp = new(int.MaxValue);
|
||||
private readonly AutoProp<int> _currentLevel = new(int.MaxValue);
|
||||
private readonly AutoProp<int> _currentAttack = new(int.MaxValue);
|
||||
private readonly AutoProp<int> _bonusAttack = new(int.MaxValue);
|
||||
private readonly AutoProp<int> _maxAttack = new(int.MaxValue);
|
||||
private readonly AutoProp<int> _currentDefense = new(int.MaxValue);
|
||||
private readonly AutoProp<int> _bonusDefense = new(int.MaxValue);
|
||||
private readonly AutoProp<int> _maxDefense = new(int.MaxValue);
|
||||
private readonly AutoProp<int> _expToNextLevel = new(int.MaxValue);
|
||||
private readonly AutoProp<double> _luck = new(double.MaxValue);
|
||||
private readonly AutoProp<int> _currentHP = new(-1);
|
||||
private readonly AutoProp<int> _maximumHP = new(-1);
|
||||
private readonly AutoProp<int> _currentVT = new(-1);
|
||||
private readonly AutoProp<int> _maximumVT = new(-1);
|
||||
private readonly AutoProp<int> _currentExp = new(-1);
|
||||
private readonly AutoProp<int> _currentLevel = new(-1);
|
||||
private readonly AutoProp<int> _currentAttack = new(-1);
|
||||
private readonly AutoProp<int> _bonusAttack = new(-1);
|
||||
private readonly AutoProp<int> _maxAttack = new(-1);
|
||||
private readonly AutoProp<int> _currentDefense = new(-1);
|
||||
private readonly AutoProp<int> _bonusDefense = new(-1);
|
||||
private readonly AutoProp<int> _maxDefense = new(-1);
|
||||
private readonly AutoProp<int> _expToNextLevel = new(-1);
|
||||
private readonly AutoProp<double> _luck = new(-1);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user