Work on hooking the character system into the character creation

This commit is contained in:
Sebastian Bularca
2026-04-05 15:55:48 +02:00
parent 43dc5e68be
commit 4d83483034
18 changed files with 107 additions and 180 deletions

View File

@@ -104,18 +104,9 @@ namespace Nox.Core {
var adventureData = new AdventureData();
var characterBaseSettings = Addressables.LoadAssetAsync<StarterCharacterSettings>("CharacterBaseSettings").WaitForCompletion();
var perKRegistry = Addressables.LoadAssetAsync<PerksRegistry>("PerksRegistry").WaitForCompletion();
var characterRegistry = Addressables.LoadAssetAsync<CharacterRegistry>("CharacterRegistry").WaitForCompletion();
var defaultPartySettings = Addressables.LoadAssetAsync<DefaultPartySettings>("DefaultPartySettings").WaitForCompletion();
var modifiersRegistry = Addressables.LoadAssetAsync<ModifiersRegistry>("ModifiersRegistry").WaitForCompletion();
var characterSystems = DefaultCharacterSystemsFactory.Create(characterBaseSettings, perKRegistry, characterRegistry, modifiersRegistry);
var partyCreatorModel = new PartyCreatorModel(characterSystems.CharacterFactory, characterSystems.PartyFactory, defaultPartySettings);
applicationStates = new Dictionary<GameState, IGameState> {
[GameState.BootState] = new SplashGameState(bootstrapReferences, gameDataState),
[GameState.MainMenu] = new MainMenuGameState(gameDataState, menuGameStateData, bootstrapReferences, saveSystem, partyCreatorModel, adventureData, gameLogStore),
[GameState.MainMenu] = new MainMenuGameState(gameDataState, menuGameStateData, bootstrapReferences, saveSystem, adventureData, gameLogStore),
[GameState.GameMode] = new GameModeGameState(gameDataState, bootstrapReferences, platform.PlatformSettings, saveSystem, sceneTransition, adventureData, gameLogStore),
};
createApplicationStateMarker.End();

View File

@@ -21,7 +21,6 @@ namespace Nox.Core {
private readonly MenuGameStateData menuGameStateData;
private readonly BootstrapReferences bootstrapReferences;
private readonly ISaveSystem saveSystem;
private readonly PartyCreatorModel partyCreatorModel;
private readonly IGameLogStore gameLogStore;
private AdventureData adventureData;
private Action<PlayMode> onStartGameRequested;
@@ -34,14 +33,12 @@ namespace Nox.Core {
MenuGameStateData menuGameStateData,
BootstrapReferences bootstrapReferences,
ISaveSystem saveSystem,
PartyCreatorModel partyCreatorModel,
AdventureData adventureData,
IGameLogStore gameLogStore) {
this.gameDataState = gameDataState;
this.menuGameStateData = menuGameStateData;
this.bootstrapReferences = bootstrapReferences;
this.saveSystem = saveSystem;
this.partyCreatorModel = partyCreatorModel;
this.adventureData = adventureData;
this.gameLogStore = gameLogStore;
}
@@ -49,8 +46,6 @@ namespace Nox.Core {
public void EnterGameState() {
IsGameStateInitialized = false;
onStartGameRequested = mode => {
var party = partyCreatorModel.CreatePartyForNewRun(companionCount: 4);
gameDataState.ActiveParty = party;
var sessions = saveSystem.GetAllSessions();
gameDataState.activeSessionId = sessions.Count > 0
? sessions[0].sessionId
@@ -67,6 +62,7 @@ namespace Nox.Core {
gameDataState.ChangeGameState(GameState.GameMode);
gameDataState.ChangePlayMode(saveData.activePlayMode);
gameDataState.ActiveParty = saveData.activeParty;
};
menuGameStateData.startGameRequests += onStartGameRequested;
@@ -80,7 +76,15 @@ namespace Nox.Core {
await sceneHandle.Result.ActivateAsync();
var assetHandle = Addressables.LoadAssetAsync<MenuPrefabsContainer>(bootstrapReferences.menuPrefabsContainer);
await assetHandle.Task;
mainMenuView = new MainMenuView(assetHandle.Result, menuGameStateData, saveSystem);
var characterBaseSettings = Addressables.LoadAssetAsync<StarterCharacterSettings>("CharacterBaseSettings").WaitForCompletion();
var perKRegistry = Addressables.LoadAssetAsync<PerksRegistry>("PerksRegistry").WaitForCompletion();
var characterRegistry = Addressables.LoadAssetAsync<CharacterRegistry>("CharacterRegistry").WaitForCompletion();
var modifiersRegistry = Addressables.LoadAssetAsync<ModifiersRegistry>("ModifiersRegistry").WaitForCompletion();
var partySettings = Addressables.LoadAssetAsync<PartySettings>("DefaultPartySettings").WaitForCompletion();
var characterSystems = CharacterSystemsFactory.Create(partySettings, characterBaseSettings, perKRegistry, characterRegistry, modifiersRegistry);
mainMenuView = new MainMenuView(assetHandle.Result, menuGameStateData, saveSystem, gameDataState, partySettings, characterSystems);
mainMenuView.Initialize();
IsGameStateInitialized = true;
}

View File

@@ -3,7 +3,7 @@ using ZLinq;
namespace Nox.Game {
public interface ICharacterFactory {
CharacterDefinition CreateCustomProtagonist(CustomCharacterCreationRequest request);
CharacterDefinition CreateProtagonist(CharacterCreationRequest request);
CharacterDefinition CreateFromTemplate(CharacterTemplate template, CharacterRole role = CharacterRole.Companion);
}
@@ -35,7 +35,7 @@ namespace Nox.Game {
}
[Serializable]
public sealed class CustomCharacterCreationRequest : IEntityDefinition {
public sealed class CharacterCreationRequest : IEntityDefinition {
public Guid Id { get; set; } = Guid.Empty;
public string Name { get; set; }
public CharacterRace Race { get; set; }
@@ -64,7 +64,7 @@ namespace Nox.Game {
this.modifiersFactory = modifiersFactory ?? throw new ArgumentNullException(nameof(modifiersFactory));
}
public CharacterDefinition CreateCustomProtagonist(CustomCharacterCreationRequest request) {
public CharacterDefinition CreateProtagonist(CharacterCreationRequest request) {
if(request == null) {
throw new ArgumentNullException(nameof(request));
}

View File

@@ -1,8 +1,9 @@
using System;
namespace Nox.Game {
public static class DefaultCharacterSystemsFactory {
public static class CharacterSystemsFactory {
public static ICharacterSystems Create(
PartySettings partySettings,
StarterCharacterSettings starterCharacterSettings,
PerksRegistry perksRegistry,
CharacterRegistry characterRegistry,
@@ -13,7 +14,7 @@ namespace Nox.Game {
ICharacterAttributesFactory attributesFactory = new CharacterAttributesFactory(modifierResolver);
ICharacterStatsFactory statsFactory = new CharacterStatsFactory(starterCharacterSettings, modifierResolver);
ICharacterFactory characterFactory = new CharacterFactory(attributesFactory, statsFactory, perkFactory, modifiersFactory);
IPartyFactory partyFactory = new PartyFactory(starterCharacterSettings);
IPartyFactory partyFactory = new PartyFactory(partySettings, starterCharacterSettings);
return new CharacterSystems(perkFactory, modifiersFactory, modifierResolver, characterFactory, partyFactory);
}

View File

@@ -1,52 +1,28 @@
using System.Collections.Generic;
using System.Linq;
namespace Nox.Game {
public class PartyCreatorModel {
private readonly ICharacterFactory characterFactory;
private readonly IPartyFactory partyFactory;
private readonly DefaultPartySettings defaultPartySettings;
public PartyCreatorModel(ICharacterFactory characterFactory, IPartyFactory partyFactory, DefaultPartySettings defaultPartySettings) {
private readonly List<CharacterCreationRequest> characterCreationRequests;
private readonly PartySettings partySettings;
public PartyCreatorModel(ICharacterFactory characterFactory,
IPartyFactory partyFactory,
List<CharacterCreationRequest> characterCreationRequests,
PartySettings partySettings) {
this.characterFactory = characterFactory;
this.partyFactory = partyFactory;
this.defaultPartySettings = defaultPartySettings;
this.characterCreationRequests = characterCreationRequests;
this.partySettings = partySettings;
}
public PartyDefinition CreatePartyForNewRun(int companionCount) {
var protagonist = characterFactory.CreateCustomProtagonist(new CustomCharacterCreationRequest {
Name = "John Doe",
Attributes = new EntityAttributes {
attributes = new[] {
new Attribute(AttributeType.Might, 3),
new Attribute(AttributeType.Reflex, 3),
new Attribute(AttributeType.Knowledge, 3),
new Attribute(AttributeType.Perception, 3)
}
}
});
var rookAttributes = new EntityAttributes {
attributes = new[] {
new Attribute(AttributeType.Might, 6),
new Attribute(AttributeType.Knowledge, 2),
new Attribute(AttributeType.Perception, 2),
new Attribute(AttributeType.Reflex, 4)
}
};
CharacterTemplate[] companionTemplates = {
new() {
Name = "Rook",
Attributes = rookAttributes,
Perks = new PerksData(){
perks = new List<PerkDefinition>()
}
}
};
var companions = new List<CharacterDefinition>();
for(var i = 0; i < companionCount && i < companionTemplates.Length; i++) {
companions.Add(characterFactory.CreateFromTemplate(companionTemplates[i], CharacterRole.Companion));
public PartyDefinition CreatePartyForNewRun() {
if(characterCreationRequests.Count > partySettings.maxPartySize) {
throw new System.ArgumentException("Too many characters requested.");
}
var protagonist = characterFactory.CreateProtagonist(characterCreationRequests.Find(r => r.Role == CharacterRole.Protagonist));
var companions = characterCreationRequests.FindAll(r => r.Role != CharacterRole.Protagonist).Select(r => characterFactory.CreateProtagonist(r));
return partyFactory.Create(protagonist, companions);
}
}

View File

@@ -8,9 +8,11 @@ namespace Nox.Game {
}
public sealed class PartyFactory : IPartyFactory {
private readonly PartySettings partySettings;
private readonly StarterCharacterSettings starterCharacterSettings;
public PartyFactory(StarterCharacterSettings starterCharacterSettings) {
public PartyFactory(PartySettings partySettings, StarterCharacterSettings starterCharacterSettings) {
this.partySettings = partySettings;
this.starterCharacterSettings = starterCharacterSettings;
}
@@ -20,7 +22,7 @@ namespace Nox.Game {
}
var party = new PartyDefinition {
maxPartySize = starterCharacterSettings.maxPartySize <= 0 ? int.MaxValue : starterCharacterSettings.maxPartySize
maxPartySize = partySettings.maxPartySize <= 0 ? int.MaxValue : partySettings.maxPartySize
};
var protagonistClone = protagonist.Clone();

View File

@@ -5,22 +5,25 @@ using UnityEngine;
namespace Nox.Game {
[CreateAssetMenu(fileName = "DefaultPartySettings", menuName = "Nox/Database/Entities/Default Party Settings")]
public class DefaultPartySettings : ScriptableObject {
public class PartySettings : ScriptableObject {
[Header("Party System Defaults")]
public int maxPartySize = 4;
[Header("This will be default starting set")]
public string startingSetId;
public string testStartingSetId;
[Header("Party Definition Sets")]
public List<PartyDefinitionSet> partyDefinitionSets;
public List<PartyDefinitionSet> testPartyDefinitionSets;
[Header("Testing Party Definition Sets")]
public StarterCharacterSettings starterCharacterSettings;
public StarterCharacterSettings testStarterCharacterSettings;
private void OnValidate() {
if(String.IsNullOrEmpty(startingSetId)) {
if(String.IsNullOrEmpty(testStartingSetId)) {
Debug.LogError("DefaultPartySettings: startingSetId cannot be null or empty");
return;
}
foreach(var partyDefinitionSet in partyDefinitionSets) {
foreach(var partyDefinitionSet in testPartyDefinitionSets) {
var partyDefinition = partyDefinitionSet.partyDefinition;
if(partyDefinition == null) {
return;
@@ -32,8 +35,8 @@ namespace Nox.Game {
}
}
if(partyDefinitionSets.AsValueEnumerable().FirstOrDefault(pds => pds.id == partyDefinitionSet.id && pds.isTestingSet) != null) {
var testingSet = partyDefinitionSets.AsValueEnumerable().FirstOrDefault(pds => pds.id == partyDefinitionSet.id && pds.isTestingSet);
if(testPartyDefinitionSets.AsValueEnumerable().FirstOrDefault(pds => pds.id == partyDefinitionSet.id && pds.isTestingSet) != null) {
var testingSet = testPartyDefinitionSets.AsValueEnumerable().FirstOrDefault(pds => pds.id == partyDefinitionSet.id && pds.isTestingSet);
ApplyClassAndRacialBonuses(testingSet);
}
@@ -47,9 +50,9 @@ namespace Nox.Game {
private void ApplyClassAndRacialBonuses(PartyDefinitionSet testingSet) {
var partyDefinition = testingSet.partyDefinition;
foreach(var member in partyDefinition.members) {
var baseSettings = starterCharacterSettings.defaultEntityAttributes;
var classAttributes = starterCharacterSettings.classBonuses.AsValueEnumerable().FirstOrDefault(c => c.@class == member.Class)?.bonusAttributes;
var racialAttributes = starterCharacterSettings.racialBonuses.AsValueEnumerable().FirstOrDefault(rb => rb.race == member.Race)?.bonusAttributes;
var baseSettings = testStarterCharacterSettings.defaultEntityAttributes;
var classAttributes = testStarterCharacterSettings.classBonuses.AsValueEnumerable().FirstOrDefault(c => c.@class == member.Class)?.bonusAttributes;
var racialAttributes = testStarterCharacterSettings.racialBonuses.AsValueEnumerable().FirstOrDefault(rb => rb.race == member.Race)?.bonusAttributes;
if (classAttributes != null && racialAttributes != null) {
member.Attributes += baseSettings + classAttributes + racialAttributes;
}

View File

@@ -14,9 +14,6 @@ namespace Nox.Game {
[Header("General Racial Bonuses and Perks per Class")]
public RacialBonuses [] racialBonuses;
public ClassBonuses [] classBonuses;
[Header("Party System Defaults")]
public int maxPartySize = 4;
}
[Serializable]

View File

@@ -35,7 +35,7 @@ namespace Nox.Game {
gameDataState.activeSessionId = latestSession.sessionId;
gameDataState.savedPartyPosition = saveData.partyPosition.ToVector3();
gameDataState.ActiveParty = saveData.partyDefinition;
gameDataState.ActiveParty = saveData.activeParty;
adventureData = saveData.adventureData;
if(gameLogStore != null && saveData.gameLogData != null) {
@@ -59,7 +59,7 @@ namespace Nox.Game {
public AdventureData adventureData;
// Party
public PartyDefinition partyDefinition;
public PartyDefinition activeParty;
public SerializableVector3 partyPosition;
// In-game log

View File

@@ -156,7 +156,7 @@ namespace Nox.Game {
public NoxSavedDataSet CaptureNoxSaveData() {
return new NoxSavedDataSet {
activePlayMode = PlayMode.Adventure,
partyDefinition = partyDefinition,
activeParty = partyDefinition,
partyPosition = partyRef ? SerializableVector3.FromVector3(partyRef.transform.position) : SerializableVector3.Zero
};
}

View File

@@ -1,16 +1,44 @@
using Jovian.SaveSystem;
using Nox.Core;
using Nox.Game;
using Nox.Game.UI;
using System.Collections.Generic;
namespace Nox.UI {
public class CharacterCreationView : IMenuView {
// we need prefab reference from the menu view, character creation data, save system, modifier calculation, gamemode state to start the game
// party creation data/system,
public CharacterCreationView(CharacterCreationReference characterCreationReference, MenuGameStateData menuGameStateData) {
characterCreationReference.startGameButton.onClick.AddListener(() => menuGameStateData.startGameRequests?.Invoke(PlayMode.Adventure));
public ISaveSystem SaveSystem { get; }
private readonly CharacterCreationReference characterCreationReference;
private readonly MenuGameStateData menuGameStateData;
private readonly GameDataState gameDataState;
private readonly PartySettings partySettings;
private readonly ICharacterSystems characterSystems;
private List<CharacterCreationRequest> characterCreationRequests;
public CharacterCreationView(
CharacterCreationReference characterCreationReference,
MenuGameStateData menuGameStateData,
ISaveSystem saveSystem,
GameDataState gameDataState,
PartySettings partySettings,
ICharacterSystems characterSystems) {
SaveSystem = saveSystem;
this.characterCreationReference = characterCreationReference;
this.menuGameStateData = menuGameStateData;
this.gameDataState = gameDataState;
this.partySettings = partySettings;
this.characterSystems = characterSystems;
}
public void Initialize() {
throw new System.NotImplementedException();
characterCreationReference.startGameButton.onClick.AddListener(() => menuGameStateData.startGameRequests?.Invoke(PlayMode.Adventure));
}
public void CreateParty() {
var partyCreatorModel = new PartyCreatorModel(characterSystems.CharacterFactory, characterSystems.PartyFactory, characterCreationRequests, partySettings);
var party = partyCreatorModel.CreatePartyForNewRun();
gameDataState.ActiveParty = party;
}
public void Tick() {

View File

@@ -4,7 +4,6 @@ using Jovian.SaveSystem;
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.AsyncOperations;
using PlayMode = Nox.Core.PlayMode;
namespace Nox.UI {
/// <summary>
@@ -14,15 +13,28 @@ namespace Nox.UI {
private readonly MenuPrefabsContainer menuPrefabsContainer;
private readonly MenuGameStateData menuGameStateData;
private readonly ISaveSystem saveSystem;
private readonly GameDataState gameDataState;
private readonly PartySettings partySettings;
private readonly ICharacterSystems characterSystems;
private MainMenuReference mainMenuReference;
private CharacterCreationReference characterCreationReference;
private CharacterCreationView characterCreationView;
private AsyncOperationHandle<GameObject> charCreationHandle;
public MainMenuView(MenuPrefabsContainer menuPrefabsContainer, MenuGameStateData menuGameStateData, ISaveSystem saveSystem) {
public MainMenuView(
MenuPrefabsContainer menuPrefabsContainer,
MenuGameStateData menuGameStateData,
ISaveSystem saveSystem,
GameDataState gameDataState,
PartySettings partySettings,
ICharacterSystems characterSystems
) {
this.menuPrefabsContainer = menuPrefabsContainer;
this.menuGameStateData = menuGameStateData;
this.saveSystem = saveSystem;
this.gameDataState = gameDataState;
this.partySettings = partySettings;
this.characterSystems = characterSystems;
}
public void Initialize() {
if(mainMenuReference == null) {
@@ -39,14 +51,14 @@ namespace Nox.UI {
}
}
public void InitializeCharacterCreation() {
private void InitializeCharacterCreation() {
charCreationHandle = Addressables.InstantiateAsync(menuPrefabsContainer.characterCreationReference);
charCreationHandle.Task.ContinueWith(t => {
if(t.IsFaulted) {
Debug.LogError(t.Exception);
}
characterCreationReference =t.Result.GetComponent<CharacterCreationReference>();
characterCreationView = new CharacterCreationView(characterCreationReference, menuGameStateData);
characterCreationView = new CharacterCreationView(characterCreationReference, menuGameStateData, saveSystem, gameDataState, partySettings, characterSystems);
characterCreationView.Show();
});
}

View File

@@ -1,6 +1,5 @@
using UnityEngine;
using UnityEngine.AddressableAssets;
using UnityEngine.ResourceManagement.ResourceLocations;
namespace Nox.UI {
[CreateAssetMenu(fileName = "MenuPrefabsContainer", menuName = "Nox/Database/UI/MenuPrefabContainer")]

View File

@@ -12,92 +12,6 @@ MonoBehaviour:
m_Script: {fileID: 11500000, guid: ff702898b3cd4100b9b8230f04711fa9, type: 3}
m_Name: DefaultPartySettings
m_EditorClassIdentifier: Assembly-CSharp::Nox.Game.DefaultPartySettings
partyDefinitionSets:
- id: startingParty1
partyDefinition:
maxPartySize: 4
members:
- id:
displayName:
race: 0
class: 0
role: 0
attributes:
might: 0
reflex: 0
knowledge: 0
perception: 0
stats:
health: 0
stamina: 0
level: 0
experience: 0
perksData:
perks: []
activeModifiers:
modifiers: []
- id:
displayName:
race: 0
class: 0
role: 0
attributes:
might: 0
reflex: 0
knowledge: 0
perception: 0
stats:
health: 0
stamina: 0
level: 0
experience: 0
perksData:
perks: []
activeModifiers:
modifiers: []
- id:
displayName:
race: 0
class: 0
role: 0
attributes:
might: 0
reflex: 0
knowledge: 0
perception: 0
stats:
health: 0
stamina: 0
level: 0
experience: 0
perksData:
perks: []
activeModifiers:
modifiers: []
- id:
displayName:
race: 0
class: 0
role: 0
attributes:
might: 0
reflex: 0
knowledge: 0
perception: 0
stats:
health: 0
stamina: 0
level: 0
experience: 0
perksData:
perks: []
activeModifiers:
modifiers: []
attributesPointDistribution:
might: 0
reflex: 0
knowledge: 0
perception: 0
distributionPointsPerClass:
class: 0
points: 0
startingSetId:
partyDefinitionSets: []
starterCharacterSettings: {fileID: 0}

View File

@@ -772,7 +772,7 @@ MonoBehaviour:
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_text: 5
m_text: -1
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: a0ee74bf6f853704a8a568d5ef638ee9, type: 2}
m_sharedMaterial: {fileID: 9074173216178389243, guid: a0ee74bf6f853704a8a568d5ef638ee9, type: 2}

View File

@@ -65,7 +65,7 @@ MonoBehaviour:
m_OnCullStateChanged:
m_PersistentCalls:
m_Calls: []
m_text: 100
m_text: -1
m_isRightToLeft: 0
m_fontAsset: {fileID: 11400000, guid: a0ee74bf6f853704a8a568d5ef638ee9, type: 2}
m_sharedMaterial: {fileID: 9074173216178389243, guid: a0ee74bf6f853704a8a568d5ef638ee9, type: 2}