forked from Shardstone/trail-into-darkness
First commit on my server, yey!
This commit is contained in:
8
Assets/Code/Core.meta
Normal file
8
Assets/Code/Core.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: dd7c9c995eb4c449481b84fe9a667c71
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
42
Assets/Code/Core/Boot.cs
Normal file
42
Assets/Code/Core/Boot.cs
Normal file
@@ -0,0 +1,42 @@
|
||||
using Nox.EditorCode;
|
||||
using System;
|
||||
using Unity.VectorGraphics;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.SceneManagement;
|
||||
|
||||
namespace Nox.Core {
|
||||
public class Boot {
|
||||
/// <summary>
|
||||
/// The is the first method called when the game starts. It will load the Initializer prefab which will initialize the project
|
||||
/// </summary>
|
||||
[RuntimeInitializeOnLoadMethod]
|
||||
private static void Initialize() {
|
||||
#if UNITY_EDITOR
|
||||
switch(BootMode.BootType) {
|
||||
case BootType.UnityDefault:
|
||||
return;
|
||||
case BootType.SceneBoot:
|
||||
Addressables.InstantiateAsync("Initializer").WaitForCompletion();
|
||||
break;
|
||||
case BootType.FullBoot:
|
||||
var loadOperation = SceneManager.LoadSceneAsync("Startup", LoadSceneMode.Single);
|
||||
if(loadOperation != null) {
|
||||
loadOperation.completed += OnCompleted;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
throw new ArgumentOutOfRangeException();
|
||||
}
|
||||
#else
|
||||
Addressables.InstantiateAsync("Initializer").WaitForCompletion();
|
||||
#endif
|
||||
}
|
||||
|
||||
private static void OnCompleted(AsyncOperation obj) {
|
||||
obj.allowSceneActivation = true;
|
||||
Addressables.InstantiateAsync("Initializer").WaitForCompletion();
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Code/Core/Boot.cs.meta
Normal file
2
Assets/Code/Core/Boot.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d7904792340a14b458474cbdea3bbb2e
|
||||
13
Assets/Code/Core/BootstrapReferences.cs
Normal file
13
Assets/Code/Core/BootstrapReferences.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using Nox.UI;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
|
||||
namespace Nox.Core {
|
||||
[CreateAssetMenu(menuName = "Nox/Database/General/BoostrapReferences", fileName = "BoostrapReferences")]
|
||||
public class BootstrapReferences : ScriptableObject {
|
||||
public AssetReference startMenuScene;
|
||||
public AssetReference splashUIReference;
|
||||
public AssetReference playModeSettings;
|
||||
public AssetReference menuPrefabsContainer;
|
||||
}
|
||||
}
|
||||
2
Assets/Code/Core/BootstrapReferences.cs.meta
Normal file
2
Assets/Code/Core/BootstrapReferences.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 55a600591b835403c8053e4ed4b581fa
|
||||
113
Assets/Code/Core/EntryPoint.cs
Normal file
113
Assets/Code/Core/EntryPoint.cs
Normal file
@@ -0,0 +1,113 @@
|
||||
using System.Collections;
|
||||
using System.Collections.Generic;
|
||||
using Nox.Game;
|
||||
using Nox.Game.UI;
|
||||
using Nox.Input;
|
||||
using Nox.Platform;
|
||||
using Jovian.SaveSystem;
|
||||
using Nox.EditorCode;
|
||||
using Unity.Profiling;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||
|
||||
namespace Nox.Core {
|
||||
/// <summary>
|
||||
/// The main boot class which is initializing the project, loads the platforms settings, initializes the platforms and creates the game states
|
||||
/// </summary>
|
||||
public class EntryPoint : MonoBehaviour {
|
||||
public AssetReferenceT<InitializerSettingsFile> initializerSettingsFile;
|
||||
|
||||
private IPlatform platform;
|
||||
private GameDataState gameDataState;
|
||||
private Dictionary<GameState, IGameState> applicationStates;
|
||||
private ProfilerMarker createApplicationStateMarker = new ProfilerMarker("createApplicationState");
|
||||
private ISceneTransition sceneTransition;
|
||||
private ISaveSystem saveSystem;
|
||||
|
||||
private InitializerSettingsFile initializerSettings;
|
||||
private BootstrapReferences bootstrapReferences;
|
||||
private MenuGameStateData menuGameStateData;
|
||||
|
||||
public IEnumerator Start() {
|
||||
DontDestroyOnLoad(this);
|
||||
|
||||
// scene authoring is helping with making each scene stand-alone playable
|
||||
var activeSceneReference = FindFirstObjectByType<SceneReference>();
|
||||
if(activeSceneReference == null) {
|
||||
Debug.LogWarning("The scene has no SceneReference script. Will start by default in Splash Application State");
|
||||
var sceneRef = new GameObject {
|
||||
name = "Scene Reference"
|
||||
};
|
||||
activeSceneReference = sceneRef.AddComponent<SceneReference>();
|
||||
activeSceneReference.gameState = GameState.BootState;
|
||||
yield return null;
|
||||
}
|
||||
|
||||
// Initialize platform
|
||||
gameDataState = new GameDataState {
|
||||
platformSelector = new PlatformSelector(PlatformSelector.GetDevicePlatform(), PlatformSelector.GetPlatformDefaultInputMode()),
|
||||
};
|
||||
gameDataState.ChangeGameState(activeSceneReference.gameState);
|
||||
|
||||
// Load Initialization Settings
|
||||
var initSettingsHandle = Addressables.LoadAssetAsync<InitializerSettingsFile>(initializerSettingsFile);
|
||||
yield return new WaitUntil(() => initSettingsHandle.IsDone);
|
||||
initializerSettings = initSettingsHandle.Result;
|
||||
yield return CreatePlatformFactory();
|
||||
|
||||
var bootStrapsSettingsHandle = Addressables.LoadAssetAsync<BootstrapReferences>(initializerSettings.bootstrapSettings);
|
||||
yield return new WaitUntil(() => bootStrapsSettingsHandle.IsDone);
|
||||
bootstrapReferences = bootStrapsSettingsHandle.Result;
|
||||
menuGameStateData = new MenuGameStateData();
|
||||
|
||||
var fadeObject = new GameObject("ScreenFadeTransition");
|
||||
sceneTransition = fadeObject.AddComponent<ScreenFadeTransition>();
|
||||
|
||||
CreateApplicationStates();
|
||||
|
||||
var gameStateRunner = gameObject.AddComponent<GameStateRunner>();
|
||||
gameStateRunner.Initialize(applicationStates, gameDataState, platform, sceneTransition);
|
||||
}
|
||||
|
||||
private IEnumerator CreatePlatformFactory() {
|
||||
platform = null;
|
||||
var devicePlatform = gameDataState.platformSelector.devicePlatform;
|
||||
platform = devicePlatform switch {
|
||||
DevicePlatform.Desktop => new DesktopPlatform(initializerSettings.desktopPlatformSettings),
|
||||
DevicePlatform.UnityEditor => new UnityEditorPlatform(initializerSettings.unityEditorPlatformSettings),
|
||||
_ => platform
|
||||
};
|
||||
|
||||
if(!Equals(gameDataState.platformSelector.devicePlatform, DevicePlatform.Desktop) &&
|
||||
PlatformSelector.GetPlatformDefaultInputMode() == InputMode.Desktop) {
|
||||
platform = new DesktopPlatform(initializerSettings.desktopPlatformSettings);
|
||||
}
|
||||
yield return platform?.Initialize(gameDataState);
|
||||
}
|
||||
|
||||
private void CreateApplicationStates() {
|
||||
createApplicationStateMarker.Begin();
|
||||
|
||||
// Save System
|
||||
var saveSettings = SaveSystemSettings.Load();
|
||||
ISaveSerializer saveSerializer = saveSettings.saveFormat == SaveFormat.Json
|
||||
? new JsonSaveSerializer()
|
||||
: new BinarySaveSerializer(saveSettings.obfuscationKey);
|
||||
ISaveStorage saveStorage = new FileSystemSaveStorage(Application.persistentDataPath, saveSettings.saveDirectoryName);
|
||||
ISaveSlotManager saveSlotManager = new SaveSlotManager(saveStorage, saveSettings);
|
||||
saveSystem = new SaveSystem(saveSerializer, saveStorage, saveSlotManager, saveSettings);
|
||||
|
||||
var adventuredata = new AdventureData();
|
||||
var characterSystems = DefaultCharacterSystemsFactory.Create(maxPartySize: 8);
|
||||
var partyCreatorModel = new PartyCreatorModel(characterSystems.CharacterFactory, characterSystems.PartyFactory);
|
||||
|
||||
applicationStates = new Dictionary<GameState, IGameState> {
|
||||
[GameState.BootState] = new SplashGameState(bootstrapReferences, gameDataState),
|
||||
[GameState.MainMenu] = new MainMenuGameState(gameDataState, menuGameStateData, bootstrapReferences, saveSystem, partyCreatorModel, adventuredata),
|
||||
[GameState.GameMode] = new GameModeGameState(gameDataState, bootstrapReferences, platform.PlatformSettings, saveSystem, sceneTransition, adventuredata),
|
||||
};
|
||||
createApplicationStateMarker.End();
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Code/Core/EntryPoint.cs.meta
Normal file
2
Assets/Code/Core/EntryPoint.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6fd8cc6910b9e44718d0bd7733e0b83e
|
||||
31
Assets/Code/Core/GameDataState.cs
Normal file
31
Assets/Code/Core/GameDataState.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using Nox.Game;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Nox.Core {
|
||||
/// <summary>
|
||||
/// This class is meant to hold application essential gameplay state
|
||||
/// </summary>
|
||||
public class GameDataState {
|
||||
public PlatformSelector platformSelector;
|
||||
public string activeSessionId;
|
||||
public Vector3? savedPartyPosition;
|
||||
public GameState ActiveGameState { get; set; }
|
||||
public PlayMode ActivePlayMode { get; set; }
|
||||
public PartyData ActiveParty { get; set; }
|
||||
|
||||
public PlayMode PreviousPlayMode { get; private set; }
|
||||
|
||||
public void ChangeGameState(GameState gameState) {
|
||||
ActiveGameState = gameState;
|
||||
}
|
||||
|
||||
public void ChangePlayMode(PlayMode playMode) {
|
||||
if (ActiveGameState != GameState.GameMode && playMode != PlayMode.None){
|
||||
Debug.LogError("Cannot change Game Mode state in any other Application State than ApplicationState.GameMode");
|
||||
return;
|
||||
}
|
||||
PreviousPlayMode = ActivePlayMode;
|
||||
ActivePlayMode = playMode;
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Code/Core/GameDataState.cs.meta
Normal file
2
Assets/Code/Core/GameDataState.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e6ac548e723e24ae2a81ac2d2e4cd571
|
||||
208
Assets/Code/Core/GameModeGameState.cs
Normal file
208
Assets/Code/Core/GameModeGameState.cs
Normal file
@@ -0,0 +1,208 @@
|
||||
#nullable enable
|
||||
using Nox.Game;
|
||||
using Nox.Platform;
|
||||
using Nox.Game.UI;
|
||||
using Jovian.SaveSystem;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||
|
||||
namespace Nox.Core {
|
||||
/// <summary>
|
||||
/// The Game Mode Game State which is the base state in which all GameModes need to run.
|
||||
/// Game Modes are any modes that involve gameplay and gameplay related scenes. This state is where the game runs.
|
||||
/// </summary>
|
||||
public class GameModeGameState : IGameState {
|
||||
private readonly GameDataState gameDataState;
|
||||
private readonly PlatformSettings platformSettings;
|
||||
private readonly PlayModeSettings bootstrapSettings;
|
||||
private readonly ISaveSystem saveSystem;
|
||||
private readonly ISceneTransition sceneTransition;
|
||||
private readonly AdventureData adventuredata;
|
||||
|
||||
private readonly Dictionary<PlayMode, IPlayMode?> playModeCache = new();
|
||||
|
||||
private IMenuView? pauseMenuView;
|
||||
private IPlayMode? playMode;
|
||||
private IPlayMode? suspendedPlayMode;
|
||||
private bool isTransitioning;
|
||||
|
||||
private PlayMode currentPlaymode;
|
||||
private bool stateEntered;
|
||||
private AsyncOperationHandle<AdventureSettings> advSettingsHandler;
|
||||
private AdventureSettings? adventureSettings;
|
||||
|
||||
public bool IsGameStateInitialized {
|
||||
get => stateEntered && (playMode == null || playMode.IsGameModeInitialized);
|
||||
}
|
||||
|
||||
public GameModeGameState(GameDataState gameDataState,
|
||||
BootstrapReferences bootstrapReferences,
|
||||
PlatformSettings platformSettings,
|
||||
ISaveSystem saveSystem,
|
||||
ISceneTransition sceneTransition,
|
||||
AdventureData adventuredata) {
|
||||
this.gameDataState = gameDataState;
|
||||
this.platformSettings = platformSettings;
|
||||
this.saveSystem = saveSystem;
|
||||
this.sceneTransition = sceneTransition;
|
||||
this.adventuredata = adventuredata;
|
||||
|
||||
bootstrapSettings = Addressables.LoadAssetAsync<PlayModeSettings>(bootstrapReferences.playModeSettings).WaitForCompletion();
|
||||
}
|
||||
|
||||
public void EnterGameState() {
|
||||
if(gameDataState.ActivePlayMode == PlayMode.None) {
|
||||
var sceneReference = Object.FindFirstObjectByType<SceneReference>();
|
||||
if(bootstrapSettings.gameModeData.Any(g => g.playMode == sceneReference.playMode)) {
|
||||
gameDataState.ChangePlayMode(sceneReference.playMode);
|
||||
}
|
||||
}
|
||||
advSettingsHandler = Addressables.LoadAssetAsync<AdventureSettings>("Assets/Database/Game/AdventureSettings.asset");
|
||||
adventureSettings = advSettingsHandler.WaitForCompletion();
|
||||
InitializeGameViews();
|
||||
currentPlaymode = gameDataState.ActivePlayMode;
|
||||
playMode?.EnterPlayMode();
|
||||
Debug.Log($"Initialized Game Mode: {gameDataState.ActivePlayMode}");
|
||||
stateEntered = true;
|
||||
}
|
||||
private void InitializeGameViews() {
|
||||
SwitchGameMode();
|
||||
pauseMenuView ??= new PauseMenuView(gameDataState, saveSystem, CaptureNoxSaveData);
|
||||
}
|
||||
|
||||
private NoxSavedDataSet? CaptureNoxSaveData() {
|
||||
var adventure = FindAdventurePlayMode();
|
||||
return adventure?.CaptureNoxSaveData();
|
||||
}
|
||||
|
||||
private AdventurePlayMode? FindAdventurePlayMode() {
|
||||
if(playMode is AdventurePlayMode active) {
|
||||
return active;
|
||||
}
|
||||
if(suspendedPlayMode is AdventurePlayMode suspended) {
|
||||
return suspended;
|
||||
}
|
||||
if(playModeCache.TryGetValue(PlayMode.Adventure, out var cached) && cached is AdventurePlayMode fromCache) {
|
||||
return fromCache;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
private void SwitchGameMode() {
|
||||
var activeParty = gameDataState.ActiveParty;
|
||||
|
||||
if(playModeCache.TryGetValue(gameDataState.ActivePlayMode, out var cached)) {
|
||||
playMode = cached;
|
||||
return;
|
||||
}
|
||||
|
||||
playMode = gameDataState.ActivePlayMode switch {
|
||||
PlayMode.Adventure => new AdventurePlayMode(platformSettings, activeParty, bootstrapSettings, gameDataState, saveSystem, adventureSettings, adventuredata),
|
||||
PlayMode.PauseMenu => new PauseMenuPlayMode(platformSettings, gameDataState, pauseMenuView!),
|
||||
PlayMode.Town => new TownPlayMode(platformSettings, activeParty),
|
||||
PlayMode.Rest => new RestPlayMode(platformSettings, activeParty),
|
||||
PlayMode.Combat => new CombatPlayMode(platformSettings, activeParty),
|
||||
_ => playMode
|
||||
};
|
||||
|
||||
playModeCache[gameDataState.ActivePlayMode] = playMode;
|
||||
}
|
||||
|
||||
public GameState Tick() {
|
||||
if(isTransitioning) {
|
||||
return GameState.GameMode;
|
||||
}
|
||||
|
||||
if(ShouldTransition()) {
|
||||
HandlePlayModeTransition();
|
||||
return GameState.GameMode;
|
||||
}
|
||||
|
||||
if(playMode is { IsGameModeInitialized: true }) {
|
||||
playMode.Tick();
|
||||
}
|
||||
|
||||
return GameState.GameMode;
|
||||
}
|
||||
|
||||
private bool ShouldTransition() {
|
||||
return currentPlaymode != gameDataState.ActivePlayMode
|
||||
&& gameDataState.ActivePlayMode != PlayMode.None;
|
||||
}
|
||||
|
||||
private bool IsPauseTransition() {
|
||||
return gameDataState.ActivePlayMode == PlayMode.PauseMenu
|
||||
|| (currentPlaymode == PlayMode.PauseMenu && suspendedPlayMode != null);
|
||||
}
|
||||
|
||||
private void HandlePlayModeTransition() {
|
||||
if(IsPauseTransition()) {
|
||||
ExecutePlayModeSwitch();
|
||||
return;
|
||||
}
|
||||
|
||||
isTransitioning = true;
|
||||
sceneTransition.FadeOut(() => {
|
||||
ExecutePlayModeSwitch();
|
||||
sceneTransition.FadeIn(() => {
|
||||
isTransitioning = false;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
private void ExecutePlayModeSwitch() {
|
||||
var enteringPause = gameDataState.ActivePlayMode == PlayMode.PauseMenu;
|
||||
var resumingFromPause = currentPlaymode == PlayMode.PauseMenu
|
||||
&& suspendedPlayMode != null;
|
||||
|
||||
if(enteringPause) {
|
||||
suspendedPlayMode = playMode;
|
||||
}
|
||||
|
||||
playMode?.ExitGameMode();
|
||||
currentPlaymode = gameDataState.ActivePlayMode;
|
||||
|
||||
if(resumingFromPause) {
|
||||
playMode = suspendedPlayMode;
|
||||
suspendedPlayMode = null;
|
||||
playMode?.EnterPlayMode();
|
||||
return;
|
||||
}
|
||||
|
||||
SwitchGameMode();
|
||||
playMode?.EnterPlayMode();
|
||||
}
|
||||
|
||||
public void LateTick() {
|
||||
if(playMode is { IsGameModeInitialized: true }) {
|
||||
playMode.LateTick();
|
||||
}
|
||||
}
|
||||
public void Dispose() {
|
||||
suspendedPlayMode?.Dispose();
|
||||
playMode?.Dispose();
|
||||
}
|
||||
public void ExitGameState() {
|
||||
suspendedPlayMode?.ExitGameMode();
|
||||
suspendedPlayMode?.Dispose();
|
||||
suspendedPlayMode = null;
|
||||
playMode?.ExitGameMode();
|
||||
playMode?.Dispose();
|
||||
playMode = null;
|
||||
|
||||
foreach(var cached in playModeCache.Values) {
|
||||
if(cached != null && cached != playMode && cached != suspendedPlayMode) {
|
||||
cached.Dispose();
|
||||
}
|
||||
}
|
||||
playModeCache.Clear();
|
||||
|
||||
stateEntered = false;
|
||||
gameDataState.ChangePlayMode(PlayMode.None);
|
||||
advSettingsHandler.Release();
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Code/Core/GameModeGameState.cs.meta
Normal file
2
Assets/Code/Core/GameModeGameState.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 306626ccbd8594138af9911a5743c82e
|
||||
110
Assets/Code/Core/GameStateRunner.cs
Normal file
110
Assets/Code/Core/GameStateRunner.cs
Normal file
@@ -0,0 +1,110 @@
|
||||
using System.Collections.Generic;
|
||||
using Nox.Platform;
|
||||
using Unity.Profiling;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Nox.Core {
|
||||
/// <summary>
|
||||
/// This is the class that will execute the update cycles for each game state. This should be the only place using Unity's Monobehaviour specific classes
|
||||
/// </summary>
|
||||
public class GameStateRunner : MonoBehaviour {
|
||||
private IGameState currentState;
|
||||
private GameState currentStateType;
|
||||
private Dictionary<GameState, IGameState> applicationStates;
|
||||
private GameDataState gameDataState;
|
||||
private bool isRunnerUpdated;
|
||||
private bool isUnloading;
|
||||
private IPlatform platform;
|
||||
private ISceneTransition sceneTransition;
|
||||
private GameState pendingStateTransition;
|
||||
private bool isStateTransitioning;
|
||||
private bool waitingForFadeIn;
|
||||
|
||||
public void Initialize(Dictionary<GameState, IGameState> stateLookUp, GameDataState gameDataState, IPlatform selectedPlatform, ISceneTransition sceneTransition) {
|
||||
DontDestroyOnLoad(gameObject);
|
||||
this.platform = selectedPlatform;
|
||||
this.sceneTransition = sceneTransition;
|
||||
applicationStates = stateLookUp;
|
||||
this.gameDataState = gameDataState;
|
||||
ChangeApplicationState(gameDataState.ActiveGameState);
|
||||
}
|
||||
|
||||
public void FixedUpdate() {
|
||||
if(!currentState.IsGameStateInitialized) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
public void Update() {
|
||||
// Pre-ticks area
|
||||
platform.Tick();
|
||||
|
||||
if(waitingForFadeIn) {
|
||||
if(currentState.IsGameStateInitialized) {
|
||||
waitingForFadeIn = false;
|
||||
sceneTransition.FadeIn(() => {
|
||||
isStateTransitioning = false;
|
||||
});
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if(isStateTransitioning) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(!isRunnerUpdated && currentState.IsGameStateInitialized) {
|
||||
isRunnerUpdated = true;
|
||||
}
|
||||
|
||||
var nextState = currentStateType;
|
||||
if(isRunnerUpdated || isUnloading) {
|
||||
nextState = currentState.Tick();
|
||||
if(gameDataState.ActiveGameState != currentStateType) {
|
||||
nextState = gameDataState.ActiveGameState;
|
||||
}
|
||||
}
|
||||
|
||||
if(nextState != currentStateType) {
|
||||
BeginStateTransition(nextState);
|
||||
}
|
||||
|
||||
// Ticks area
|
||||
|
||||
// Post-ticks area
|
||||
}
|
||||
|
||||
public void LateUpdate() {
|
||||
if(isStateTransitioning || !currentState.IsGameStateInitialized) {
|
||||
return;
|
||||
}
|
||||
currentState.LateTick();
|
||||
}
|
||||
|
||||
public void OnDestroy() {
|
||||
currentState.Dispose();
|
||||
}
|
||||
|
||||
private void BeginStateTransition(GameState newGameState) {
|
||||
isStateTransitioning = true;
|
||||
pendingStateTransition = newGameState;
|
||||
sceneTransition.FadeOut(() => {
|
||||
ChangeApplicationState(pendingStateTransition);
|
||||
waitingForFadeIn = true;
|
||||
});
|
||||
}
|
||||
|
||||
private void ChangeApplicationState(GameState newGameState) {
|
||||
if(currentState != null) {
|
||||
currentState.ExitGameState();
|
||||
}
|
||||
|
||||
currentState = applicationStates[newGameState];
|
||||
currentState.EnterGameState();
|
||||
currentStateType = newGameState;
|
||||
gameDataState.ChangeGameState(newGameState);
|
||||
isRunnerUpdated = false;
|
||||
Debug.Log($"Entered {newGameState} ApplicationState");
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Code/Core/GameStateRunner.cs.meta
Normal file
2
Assets/Code/Core/GameStateRunner.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: a039e3a8262654d2b9428ec8aa6d3140
|
||||
11
Assets/Code/Core/IGameLifecycle.cs
Normal file
11
Assets/Code/Core/IGameLifecycle.cs
Normal file
@@ -0,0 +1,11 @@
|
||||
namespace Nox.Core {
|
||||
/// <summary>
|
||||
/// Prototype for every behaviour that needs to run tick/frame updates
|
||||
/// </summary>
|
||||
public interface IGameLifecycle {
|
||||
public void Initialize();
|
||||
public void Tick();
|
||||
|
||||
public void Dispose();
|
||||
}
|
||||
}
|
||||
2
Assets/Code/Core/IGameLifecycle.cs.meta
Normal file
2
Assets/Code/Core/IGameLifecycle.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 55887b4438f5b42b891be19046c0c963
|
||||
24
Assets/Code/Core/IGameState.cs
Normal file
24
Assets/Code/Core/IGameState.cs
Normal file
@@ -0,0 +1,24 @@
|
||||
namespace Nox.Core {
|
||||
/// <summary>
|
||||
/// Add or modify project specific game states
|
||||
/// </summary>
|
||||
public enum GameState {
|
||||
Invalid,
|
||||
BootState,
|
||||
Loading,
|
||||
MainMenu,
|
||||
GameMode
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prototype for creating application states
|
||||
/// </summary>
|
||||
public interface IGameState {
|
||||
public void EnterGameState();
|
||||
public GameState Tick();
|
||||
public void LateTick();
|
||||
public void ExitGameState();
|
||||
void Dispose();
|
||||
bool IsGameStateInitialized { get; }
|
||||
}
|
||||
}
|
||||
2
Assets/Code/Core/IGameState.cs.meta
Normal file
2
Assets/Code/Core/IGameState.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d01b97d5164454406931704f451edba9
|
||||
23
Assets/Code/Core/IPlayMode.cs
Normal file
23
Assets/Code/Core/IPlayMode.cs
Normal file
@@ -0,0 +1,23 @@
|
||||
namespace Nox.Core {
|
||||
public enum PlayMode {
|
||||
None,
|
||||
PauseMenu,
|
||||
Adventure,
|
||||
Town,
|
||||
Rest,
|
||||
Combat
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Prototype for crating new game modes
|
||||
/// </summary>
|
||||
public interface IPlayMode {
|
||||
bool IsGameModeInitialized { get;}
|
||||
|
||||
public void EnterPlayMode();
|
||||
public void Tick();
|
||||
public void LateTick();
|
||||
void ExitGameMode();
|
||||
void Dispose();
|
||||
}
|
||||
}
|
||||
2
Assets/Code/Core/IPlayMode.cs.meta
Normal file
2
Assets/Code/Core/IPlayMode.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7fb4f9f9705394782b9ed1852e8f015c
|
||||
9
Assets/Code/Core/ISceneTransition.cs
Normal file
9
Assets/Code/Core/ISceneTransition.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using System;
|
||||
|
||||
namespace Nox.Core {
|
||||
public interface ISceneTransition {
|
||||
bool IsTransitioning { get; }
|
||||
void FadeOut(Action onComplete = null);
|
||||
void FadeIn(Action onComplete = null);
|
||||
}
|
||||
}
|
||||
2
Assets/Code/Core/ISceneTransition.cs.meta
Normal file
2
Assets/Code/Core/ISceneTransition.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 0b503206ce8521d469bf99fd4b0df611
|
||||
12
Assets/Code/Core/InitializerSettingsFile.cs
Normal file
12
Assets/Code/Core/InitializerSettingsFile.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using Nox.Platform;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
|
||||
namespace Nox.Core {
|
||||
[CreateAssetMenu(menuName = "Nox/Database/General/InitializerSettingsFile", fileName = "InitializerSettingsFile")]
|
||||
public class InitializerSettingsFile : ScriptableObject {
|
||||
public AssetReferenceT<DesktopPlatformSettings> desktopPlatformSettings;
|
||||
public AssetReferenceT<BootstrapReferences> bootstrapSettings;
|
||||
public AssetReferenceT<UnityEditorPlatformSettings> unityEditorPlatformSettings;
|
||||
}
|
||||
}
|
||||
2
Assets/Code/Core/InitializerSettingsFile.cs.meta
Normal file
2
Assets/Code/Core/InitializerSettingsFile.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 593adee4e2b3d4537b4d956077816eec
|
||||
20
Assets/Code/Core/LoadingGameState.cs
Normal file
20
Assets/Code/Core/LoadingGameState.cs
Normal file
@@ -0,0 +1,20 @@
|
||||
namespace Nox.Core {
|
||||
/// <summary>
|
||||
/// This is a state which would normally consist of a load screen scene or anything similar
|
||||
/// </summary>
|
||||
public class LoadingGameState : IGameState {
|
||||
public bool IsGameStateInitialized { get; set; }
|
||||
|
||||
public void EnterGameState() {
|
||||
}
|
||||
public GameState Tick() {
|
||||
return GameState.Loading;
|
||||
}
|
||||
public void LateTick() { }
|
||||
public void ExitGameState() {
|
||||
}
|
||||
public void Dispose() {
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
2
Assets/Code/Core/LoadingGameState.cs.meta
Normal file
2
Assets/Code/Core/LoadingGameState.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e20613474534045658e4e2a0b9657ade
|
||||
104
Assets/Code/Core/MainMenuGameState.cs
Normal file
104
Assets/Code/Core/MainMenuGameState.cs
Normal file
@@ -0,0 +1,104 @@
|
||||
using Nox.Game;
|
||||
using Jovian.SaveSystem;
|
||||
using Nox.UI;
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.ResourceManagement.AsyncOperations;
|
||||
using UnityEngine.ResourceManagement.ResourceProviders;
|
||||
|
||||
namespace Nox.Core {
|
||||
|
||||
public class MenuGameStateData {
|
||||
public Action<PlayMode> startGameRequests;
|
||||
public Action continueGameRequest;
|
||||
}
|
||||
|
||||
public class MainMenuGameState : IGameState {
|
||||
private readonly GameDataState gameDataState;
|
||||
private readonly MenuGameStateData menuGameStateData;
|
||||
private readonly BootstrapReferences bootstrapReferences;
|
||||
private readonly ISaveSystem saveSystem;
|
||||
private readonly PartyCreatorModel partyCreatorModel;
|
||||
private AdventureData adventureData;
|
||||
private Action<PlayMode> onStartGameRequested;
|
||||
private Action onContinueRequested;
|
||||
private MainMenuView mainMenuView;
|
||||
private AsyncOperationHandle<SceneInstance> loadSceneAsync;
|
||||
public bool IsGameStateInitialized { get; set; }
|
||||
|
||||
public MainMenuGameState(GameDataState gameDataState,
|
||||
MenuGameStateData menuGameStateData,
|
||||
BootstrapReferences bootstrapReferences,
|
||||
ISaveSystem saveSystem,
|
||||
PartyCreatorModel partyCreatorModel,
|
||||
AdventureData adventureData) {
|
||||
this.gameDataState = gameDataState;
|
||||
this.menuGameStateData = menuGameStateData;
|
||||
this.bootstrapReferences = bootstrapReferences;
|
||||
this.saveSystem = saveSystem;
|
||||
this.partyCreatorModel = partyCreatorModel;
|
||||
this.adventureData = adventureData;
|
||||
}
|
||||
|
||||
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
|
||||
: saveSystem.CreateSession();
|
||||
gameDataState.savedPartyPosition = new Vector3(7.285129f, 0, 0.4297419f);
|
||||
gameDataState.ChangeGameState(GameState.GameMode);
|
||||
gameDataState.ChangePlayMode(mode);
|
||||
};
|
||||
onContinueRequested = () => {
|
||||
var saveData = NoxSaveData.RestoreSavedData(saveSystem, gameDataState, ref adventureData);
|
||||
if(saveData == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
gameDataState.ChangeGameState(GameState.GameMode);
|
||||
gameDataState.ChangePlayMode(saveData.activePlayMode);
|
||||
|
||||
};
|
||||
menuGameStateData.startGameRequests += onStartGameRequested;
|
||||
menuGameStateData.continueGameRequest += onContinueRequested;
|
||||
_ = InitializeGameState();
|
||||
}
|
||||
|
||||
private async Task InitializeGameState() {
|
||||
var sceneHandle = Addressables.LoadSceneAsync(bootstrapReferences.startMenuScene);
|
||||
await sceneHandle.Task;
|
||||
await sceneHandle.Result.ActivateAsync();
|
||||
var assetHandle = Addressables.LoadAssetAsync<MenuPrefabsContainer>(bootstrapReferences.menuPrefabsContainer);
|
||||
await assetHandle.Task;
|
||||
mainMenuView = new MainMenuView(assetHandle.Result, menuGameStateData, saveSystem);
|
||||
mainMenuView.Initialize();
|
||||
IsGameStateInitialized = true;
|
||||
}
|
||||
|
||||
public GameState Tick() {
|
||||
mainMenuView?.Tick();
|
||||
return gameDataState.ActiveGameState;
|
||||
}
|
||||
public void LateTick() { }
|
||||
public void Dispose() { }
|
||||
|
||||
public void ExitGameState() {
|
||||
if(onStartGameRequested != null) {
|
||||
menuGameStateData.startGameRequests -= onStartGameRequested;
|
||||
onStartGameRequested = null;
|
||||
}
|
||||
if(onContinueRequested != null) {
|
||||
menuGameStateData.continueGameRequest -= onContinueRequested;
|
||||
onContinueRequested = null;
|
||||
}
|
||||
mainMenuView?.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
2
Assets/Code/Core/MainMenuGameState.cs.meta
Normal file
2
Assets/Code/Core/MainMenuGameState.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 9453c712e94474abe8eea1a900eb12c4
|
||||
37
Assets/Code/Core/PlatformSelector.cs
Normal file
37
Assets/Code/Core/PlatformSelector.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using Nox.Input;
|
||||
using Nox.Platform;
|
||||
|
||||
namespace Nox.Core {
|
||||
/// <summary>
|
||||
/// Base class used by the platform factory to select the current platform and the input
|
||||
/// </summary>
|
||||
public class PlatformSelector {
|
||||
public readonly DevicePlatform devicePlatform;
|
||||
private InputMode inputMode;
|
||||
|
||||
public PlatformSelector(DevicePlatform devicePlatform, InputMode inputMode) {
|
||||
this.devicePlatform = devicePlatform;
|
||||
this.inputMode = inputMode;
|
||||
}
|
||||
|
||||
public void SetInputMode(InputMode newInputMode) {
|
||||
inputMode = newInputMode;
|
||||
}
|
||||
|
||||
public static InputMode GetPlatformDefaultInputMode() {
|
||||
#if UNITY_EDITOR
|
||||
return InputMode.Editor;
|
||||
#else
|
||||
return InputMode.Desktop;
|
||||
#endif
|
||||
}
|
||||
|
||||
public static DevicePlatform GetDevicePlatform() {
|
||||
#if UNITY_EDITOR
|
||||
return DevicePlatform.UnityEditor;
|
||||
#else
|
||||
return DevicePlatform.Desktop;
|
||||
#endif
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Code/Core/PlatformSelector.cs.meta
Normal file
2
Assets/Code/Core/PlatformSelector.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d92359b567a9c4b67882fd1c63a2d28a
|
||||
16
Assets/Code/Core/PlayModeSettings.cs
Normal file
16
Assets/Code/Core/PlayModeSettings.cs
Normal file
@@ -0,0 +1,16 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
|
||||
namespace Nox.Core {
|
||||
[CreateAssetMenu(fileName = "GameModeSettings", menuName = "Nox/Database/General/GameModeSettings")]
|
||||
public class PlayModeSettings : ScriptableObject {
|
||||
public PlayModeData[] gameModeData;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class PlayModeData {
|
||||
public PlayMode playMode;
|
||||
public AssetReference scene;
|
||||
}
|
||||
}
|
||||
2
Assets/Code/Core/PlayModeSettings.cs.meta
Normal file
2
Assets/Code/Core/PlayModeSettings.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 00bbbdf0f374243d986d909bc14c6773
|
||||
12
Assets/Code/Core/SceneReference.cs
Normal file
12
Assets/Code/Core/SceneReference.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Nox.Core {
|
||||
/// <summary>
|
||||
/// Scene generic authoring/reference collection. Can also contain the settings for booting up a particular scene
|
||||
/// </summary>
|
||||
public class SceneReference : MonoBehaviour {
|
||||
public GameState gameState = GameState.Invalid;
|
||||
public PlayMode playMode;
|
||||
}
|
||||
|
||||
}
|
||||
2
Assets/Code/Core/SceneReference.cs.meta
Normal file
2
Assets/Code/Core/SceneReference.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 96fc2eb19de9a4236b7ca7d6a7ebd9a8
|
||||
37
Assets/Code/Core/SplashGameState.cs
Normal file
37
Assets/Code/Core/SplashGameState.cs
Normal file
@@ -0,0 +1,37 @@
|
||||
using Nox.UI;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
|
||||
namespace Nox.Core {
|
||||
/// <summary>
|
||||
/// This is an example for the first game state which in our case would usually contain a gdpr
|
||||
/// This is an example to show how it could be initialized and also how it could boot into the next game state
|
||||
/// Ideally a SceneHandler/Manager of sorts should handle loading and unloading scenes.
|
||||
/// </summary>
|
||||
public class SplashGameState : IGameState {
|
||||
private readonly BootstrapReferences bootStrapInitializer;
|
||||
private readonly GameDataState gameDataState;
|
||||
public bool IsGameStateInitialized { get; set; } = true;
|
||||
|
||||
public SplashGameState(BootstrapReferences bootStrapInitializer, GameDataState gameDataState) {
|
||||
this.bootStrapInitializer = bootStrapInitializer;
|
||||
this.gameDataState = gameDataState;
|
||||
}
|
||||
|
||||
public void EnterGameState() {
|
||||
DisclaimerReference gdprReference = Addressables.InstantiateAsync(bootStrapInitializer.splashUIReference).WaitForCompletion().GetComponent<DisclaimerReference>();
|
||||
gdprReference.continueButton.onClick.AddListener(() => {
|
||||
gameDataState.ChangeGameState(GameState.MainMenu);
|
||||
Object.Destroy(gdprReference.gameObject);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
public GameState Tick() {
|
||||
return GameState.BootState;
|
||||
}
|
||||
public void LateTick() { }
|
||||
public void Dispose() { }
|
||||
public void ExitGameState() { }
|
||||
}
|
||||
}
|
||||
2
Assets/Code/Core/SplashGameState.cs.meta
Normal file
2
Assets/Code/Core/SplashGameState.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 607a080faa19b492baa5c1b9ad13302b
|
||||
8
Assets/Code/GameState.meta
Normal file
8
Assets/Code/GameState.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e9a24b538d250f84b9b14564ccb4768d
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
8
Assets/Code/GameState/Camera.meta
Normal file
8
Assets/Code/GameState/Camera.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 780f3ab23ddb9c24988bc45c1a8e1377
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
171
Assets/Code/GameState/Camera/CameraController.cs
Normal file
171
Assets/Code/GameState/Camera/CameraController.cs
Normal file
@@ -0,0 +1,171 @@
|
||||
#nullable enable
|
||||
using Nox.Platform;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
namespace Nox.Game {
|
||||
public class CameraController : ICameraController {
|
||||
private readonly PlatformSettings? platformSettings;
|
||||
private readonly InputSystem_Actions? inputActions;
|
||||
private readonly MapReference? mapReference;
|
||||
|
||||
private CameraSettings? cameraSettings;
|
||||
|
||||
private InputAction? zoomAction;
|
||||
private InputAction? panAction;
|
||||
private InputAction? holdToDragAction;
|
||||
private Bounds planeBounds;
|
||||
private Vector3 dragWorldOrigin;
|
||||
private bool isDragging;
|
||||
private readonly Plane groundPlane = new Plane(Vector3.up, Vector3.zero);
|
||||
private readonly Texture2D cursorPoint;
|
||||
private readonly Texture2D cursorDrag;
|
||||
|
||||
public CameraReference? CameraReference { get; private set; }
|
||||
|
||||
public CameraController(PlatformSettings? platformSettings, MapReference? mapReference) {
|
||||
this.platformSettings = platformSettings;
|
||||
this.mapReference = mapReference;
|
||||
inputActions = platformSettings?.inputSettings.inputActions;
|
||||
|
||||
cursorPoint = Addressables.LoadAssetAsync<Texture2D>("Assets/Art/UI/Cursor_Pointer").WaitForCompletion();
|
||||
cursorDrag = Addressables.LoadAssetAsync<Texture2D>("Assets/Art/UI/Cursor_Drag").WaitForCompletion();
|
||||
Cursor.SetCursor(cursorPoint, new Vector2(12,4), CursorMode.Auto);
|
||||
}
|
||||
|
||||
public void Initialize() {
|
||||
if(inputActions == null || !mapReference) {
|
||||
return;
|
||||
}
|
||||
zoomAction = inputActions.Player.Zoom;
|
||||
panAction = inputActions.Player.PanMap;
|
||||
holdToDragAction = inputActions.Player.HoldToDrag;
|
||||
planeBounds = mapReference.mapPlane.GetComponent<BoxCollider>().bounds;
|
||||
SetupCamera();
|
||||
}
|
||||
|
||||
private void SetupCamera() {
|
||||
CameraReference = Object.FindAnyObjectByType<CameraReference>();
|
||||
|
||||
if(!CameraReference) {
|
||||
if(platformSettings?.cameraPrefab != null) {
|
||||
GameObject? go = Object.Instantiate(platformSettings.cameraPrefab);
|
||||
CameraReference = go.GetComponentInChildren<CameraReference>();
|
||||
if(!CameraReference) {
|
||||
Debug.LogError("Camera prefab does not contain a CameraReference component");
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
Debug.LogError("No camera prefab found and no camera reference found");
|
||||
}
|
||||
}
|
||||
|
||||
cameraSettings = CameraReference!.cameraSettings;
|
||||
if(!cameraSettings) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Set initial zoom to maximum allowed by map boundaries
|
||||
var camera = CameraReference.mainCamera;
|
||||
if(camera.orthographic) {
|
||||
float maxHalfWidth = planeBounds.size.x * 0.5f;
|
||||
float maxHalfHeight = planeBounds.size.z * 0.5f;
|
||||
float maxOrthoSize = Mathf.Min(maxHalfHeight, maxHalfWidth / camera.aspect);
|
||||
camera.orthographicSize = Mathf.Min(cameraSettings.maxZoom, maxOrthoSize);
|
||||
}
|
||||
else {
|
||||
// Perspective camera: set height so the map fits in view
|
||||
float mapWidth = planeBounds.size.x;
|
||||
float mapHeight = planeBounds.size.z;
|
||||
float aspect = camera.aspect;
|
||||
float fovRad = camera.fieldOfView * Mathf.Deg2Rad;
|
||||
float tanFov = Mathf.Tan(fovRad / 2f);
|
||||
float requiredHeightByWidth = mapWidth / (2f * tanFov * aspect);
|
||||
float requiredHeightByHeight = mapHeight / (2f * tanFov);
|
||||
float requiredHeight = Mathf.Max(requiredHeightByWidth, requiredHeightByHeight);
|
||||
// Center camera on map and set height
|
||||
Vector3 camPos = planeBounds.center;
|
||||
camPos.y = requiredHeight;
|
||||
camera.transform.position = camPos;
|
||||
// Look straight down
|
||||
camera.transform.rotation = Quaternion.Euler(90f, 0f, 0f);
|
||||
}
|
||||
}
|
||||
|
||||
public void Tick() {
|
||||
PanCamera();
|
||||
ZoomCamera();
|
||||
}
|
||||
|
||||
private void ZoomCamera() {
|
||||
if(zoomAction == null) {
|
||||
return;
|
||||
}
|
||||
float zoomDelta = zoomAction.ReadValue<Vector2>().y;
|
||||
if(zoomDelta == 0) {
|
||||
return;
|
||||
}
|
||||
var camera = CameraReference!.mainCamera;
|
||||
if(camera.orthographic) {
|
||||
camera.orthographicSize = Mathf.Clamp(camera.orthographicSize - (zoomDelta * cameraSettings!.zoomSpeed * Time.deltaTime), cameraSettings.minZoom, cameraSettings.maxZoom);
|
||||
}
|
||||
else {
|
||||
camera.transform.Translate(Vector3.forward * zoomDelta * cameraSettings!.zoomSpeed * Time.deltaTime, Space.Self);
|
||||
}
|
||||
}
|
||||
|
||||
private void PanCamera() {
|
||||
if(holdToDragAction == null || CameraReference == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(holdToDragAction.IsInProgress()) {
|
||||
Camera camera = CameraReference.mainCamera;
|
||||
Vector2 screenPos = inputActions!.Player.Point.ReadValue<Vector2>();
|
||||
|
||||
if(!isDragging) {
|
||||
isDragging = true;
|
||||
Cursor.lockState = CursorLockMode.Confined;
|
||||
Cursor.SetCursor(cursorDrag, Vector2.zero, CursorMode.Auto);
|
||||
dragWorldOrigin = ScreenToGroundPoint(camera, screenPos);
|
||||
return;
|
||||
}
|
||||
|
||||
Vector3 currentWorldPoint = ScreenToGroundPoint(camera, screenPos);
|
||||
Vector3 offset = dragWorldOrigin - currentWorldPoint;
|
||||
|
||||
Vector3 camPos = camera.transform.position;
|
||||
camPos.x += offset.x;
|
||||
camPos.z += offset.z;
|
||||
|
||||
camPos.x = Mathf.Clamp(camPos.x, planeBounds.min.x, planeBounds.max.x);
|
||||
camPos.z = Mathf.Clamp(camPos.z, planeBounds.min.z, planeBounds.max.z);
|
||||
camera.transform.position = camPos;
|
||||
|
||||
dragWorldOrigin = ScreenToGroundPoint(camera, screenPos);
|
||||
}
|
||||
else {
|
||||
if(!isDragging) {
|
||||
return;
|
||||
}
|
||||
isDragging = false;
|
||||
Cursor.lockState = CursorLockMode.None;
|
||||
Cursor.SetCursor(cursorPoint, new Vector2(12,4), CursorMode.Auto);
|
||||
}
|
||||
}
|
||||
|
||||
private Vector3 ScreenToGroundPoint(Camera camera, Vector2 screenPos) {
|
||||
Ray ray = camera.ScreenPointToRay(new Vector3(screenPos.x, screenPos.y, 0f));
|
||||
if(groundPlane.Raycast(ray, out float distance)) {
|
||||
return ray.GetPoint(distance);
|
||||
}
|
||||
return camera.transform.position;
|
||||
}
|
||||
public void Dispose() {
|
||||
inputActions?.Disable();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
3
Assets/Code/GameState/Camera/CameraController.cs.meta
Normal file
3
Assets/Code/GameState/Camera/CameraController.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 517c20f5b3ec4700afd07917ea316092
|
||||
timeCreated: 1771075174
|
||||
9
Assets/Code/GameState/Camera/CameraReference.cs
Normal file
9
Assets/Code/GameState/Camera/CameraReference.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Nox.Game {
|
||||
public class CameraReference : MonoBehaviour {
|
||||
public Camera mainCamera;
|
||||
public CameraSettings cameraSettings;
|
||||
}
|
||||
}
|
||||
2
Assets/Code/GameState/Camera/CameraReference.cs.meta
Normal file
2
Assets/Code/GameState/Camera/CameraReference.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c55bcf874b547124a861f2b9494f991e
|
||||
10
Assets/Code/GameState/Camera/CameraSettings.cs
Normal file
10
Assets/Code/GameState/Camera/CameraSettings.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Nox.Game {
|
||||
[CreateAssetMenu(fileName = "CameraSettings", menuName = "Nox/Camera Settings")]
|
||||
public class CameraSettings : ScriptableObject {
|
||||
public float zoomSpeed = 2f;
|
||||
public float minZoom = 5f;
|
||||
public float maxZoom = 20f;
|
||||
}
|
||||
}
|
||||
3
Assets/Code/GameState/Camera/CameraSettings.cs.meta
Normal file
3
Assets/Code/GameState/Camera/CameraSettings.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b508138d992c4f7280afcc0e1d6c4e8f
|
||||
timeCreated: 1771153846
|
||||
10
Assets/Code/GameState/Camera/ICameraController.cs
Normal file
10
Assets/Code/GameState/Camera/ICameraController.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Nox.Game {
|
||||
public interface ICameraController {
|
||||
CameraReference CameraReference {get;}
|
||||
void Initialize();
|
||||
void Tick();
|
||||
void Dispose();
|
||||
}
|
||||
}
|
||||
3
Assets/Code/GameState/Camera/ICameraController.cs.meta
Normal file
3
Assets/Code/GameState/Camera/ICameraController.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e0a1d5f367bb4b3fb740541f9a0ba163
|
||||
timeCreated: 1771152354
|
||||
8
Assets/Code/GameState/Entities.meta
Normal file
8
Assets/Code/GameState/Entities.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b7671be7321935f46b97f6859276be8b
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
469
Assets/Code/GameState/Entities/CharacterAndPartyFactories.cs
Normal file
469
Assets/Code/GameState/Entities/CharacterAndPartyFactories.cs
Normal file
@@ -0,0 +1,469 @@
|
||||
using Newtonsoft.Json;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Nox.Game {
|
||||
public enum CharacterRole {
|
||||
Protagonist,
|
||||
Companion
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public sealed class CharacterAttributes {
|
||||
public int might;
|
||||
public int reflex;
|
||||
public int knowledge;
|
||||
|
||||
public int Total => might + reflex + knowledge;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public sealed class CharacterStats {
|
||||
public int maxHealth;
|
||||
public int maxStamina;
|
||||
public float dodgeStaminaLossMultiplier;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public sealed class PerkDefinition {
|
||||
public string id;
|
||||
public string name;
|
||||
public string mechanicalBonus;
|
||||
public string thematicPenalty;
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public sealed class CharacterData {
|
||||
public string id;
|
||||
public string displayName;
|
||||
public CharacterRole role;
|
||||
public int level;
|
||||
public int experience;
|
||||
public CharacterAttributes attributes;
|
||||
public CharacterStats stats;
|
||||
public List<PerkDefinition> perks = new List<PerkDefinition>();
|
||||
|
||||
public CharacterData Clone() {
|
||||
return new CharacterData {
|
||||
id = id,
|
||||
displayName = displayName,
|
||||
role = role,
|
||||
level = level,
|
||||
experience = experience,
|
||||
attributes = new CharacterAttributes {
|
||||
might = attributes?.might ?? 0,
|
||||
reflex = attributes?.reflex ?? 0,
|
||||
knowledge = attributes?.knowledge ?? 0
|
||||
},
|
||||
stats = new CharacterStats {
|
||||
maxHealth = stats?.maxHealth ?? 0,
|
||||
maxStamina = stats?.maxStamina ?? 0,
|
||||
dodgeStaminaLossMultiplier = stats?.dodgeStaminaLossMultiplier ?? 1f
|
||||
},
|
||||
perks = perks.Select(p => new PerkDefinition {
|
||||
id = p.id,
|
||||
name = p.name,
|
||||
mechanicalBonus = p.mechanicalBonus,
|
||||
thematicPenalty = p.thematicPenalty
|
||||
}).ToList()
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public sealed class CharacterTemplate {
|
||||
public string id;
|
||||
public string displayName;
|
||||
public CharacterAttributes attributes;
|
||||
public int level = 1;
|
||||
public int experience;
|
||||
public List<string> startingPerkIds = new List<string>();
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public sealed class CustomCharacterCreationRequest {
|
||||
public string id;
|
||||
public string displayName;
|
||||
public int mightPoints;
|
||||
public int reflexPoints;
|
||||
public int knowledgePoints;
|
||||
public List<string> startingPerkIds = new List<string>();
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public sealed class PartyData {
|
||||
public List<CharacterData> members = new List<CharacterData>();
|
||||
public int maxPartySize;
|
||||
|
||||
[JsonIgnore]
|
||||
public CharacterData Protagonist => members.FirstOrDefault(m => m.role == CharacterRole.Protagonist);
|
||||
[JsonIgnore]
|
||||
public IReadOnlyList<CharacterData> Companions => members.Where(m => m.role == CharacterRole.Companion).ToList();
|
||||
}
|
||||
|
||||
public interface ICharacterAttributesFactory {
|
||||
CharacterAttributes Create(int might, int reflex, int knowledge);
|
||||
CharacterAttributes CreateFromPointAllocation(int mightPoints, int reflexPoints, int knowledgePoints);
|
||||
}
|
||||
|
||||
public interface ICharacterStatsFactory {
|
||||
CharacterStats Create(CharacterAttributes attributes);
|
||||
}
|
||||
|
||||
public interface IPerkFactory {
|
||||
IReadOnlyCollection<PerkDefinition> GetAll();
|
||||
PerkDefinition GetById(string perkId);
|
||||
IReadOnlyCollection<PerkDefinition> GetAvailableFor(CharacterData character);
|
||||
bool TryAddPerk(CharacterData character, string perkId);
|
||||
}
|
||||
|
||||
public interface ICharacterFactory {
|
||||
CharacterData CreateCustomProtagonist(CustomCharacterCreationRequest request);
|
||||
CharacterData CreateFromTemplate(CharacterTemplate template, CharacterRole role = CharacterRole.Companion);
|
||||
}
|
||||
|
||||
public interface IPartyFactory {
|
||||
PartyData Create(CharacterData protagonist, IEnumerable<CharacterData> companions = null);
|
||||
}
|
||||
|
||||
public interface ICharacterSystems {
|
||||
IPerkFactory PerkFactory { get; }
|
||||
ICharacterFactory CharacterFactory { get; }
|
||||
IPartyFactory PartyFactory { get; }
|
||||
}
|
||||
|
||||
public sealed class CharacterFactoryOptions {
|
||||
public int baseMight = 1;
|
||||
public int baseReflex = 1;
|
||||
public int baseKnowledge = 1;
|
||||
public int customAttributePointBudget = 10;
|
||||
public int startingLevel = 1;
|
||||
}
|
||||
|
||||
public sealed class CharacterStatsFactoryOptions {
|
||||
public int baseHealth = 10;
|
||||
public int baseStamina = 5;
|
||||
public int mightHealthBonus = 3;
|
||||
public int mightStaminaBonus = 1;
|
||||
public int knowledgeStaminaBonus = 2;
|
||||
public float baseDodgeStaminaLossMultiplier = 1f;
|
||||
public float reflexDodgeStaminaLossReduction = 0.03f;
|
||||
public float minDodgeStaminaLossMultiplier = 0.4f;
|
||||
}
|
||||
|
||||
public sealed class PartyFactoryOptions {
|
||||
public int minPartySize = 1;
|
||||
public int maxPartySize = 4;
|
||||
public bool enforceUniqueCharacterIds = true;
|
||||
}
|
||||
|
||||
public sealed class CharacterSystems : ICharacterSystems {
|
||||
public CharacterSystems(IPerkFactory perkFactory, ICharacterFactory characterFactory, IPartyFactory partyFactory) {
|
||||
PerkFactory = perkFactory;
|
||||
CharacterFactory = characterFactory;
|
||||
PartyFactory = partyFactory;
|
||||
}
|
||||
|
||||
public IPerkFactory PerkFactory { get; }
|
||||
public ICharacterFactory CharacterFactory { get; }
|
||||
public IPartyFactory PartyFactory { get; }
|
||||
}
|
||||
|
||||
public static class DefaultCharacterSystemsFactory {
|
||||
public static ICharacterSystems Create(int maxPartySize = 8) {
|
||||
IPerkFactory perkFactory = new PerkFactory(CreateDefaultPerks());
|
||||
ICharacterAttributesFactory attributesFactory = new CharacterAttributesFactory(new CharacterFactoryOptions {
|
||||
baseMight = 1,
|
||||
baseReflex = 1,
|
||||
baseKnowledge = 1,
|
||||
customAttributePointBudget = 10,
|
||||
startingLevel = 1
|
||||
});
|
||||
ICharacterStatsFactory statsFactory = new CharacterStatsFactory();
|
||||
ICharacterFactory characterFactory = new CharacterFactory(attributesFactory, statsFactory, perkFactory);
|
||||
IPartyFactory partyFactory = new PartyFactory(new PartyFactoryOptions {
|
||||
minPartySize = 1,
|
||||
maxPartySize = maxPartySize,
|
||||
enforceUniqueCharacterIds = true
|
||||
});
|
||||
|
||||
return new CharacterSystems(perkFactory, characterFactory, partyFactory);
|
||||
}
|
||||
|
||||
private static IEnumerable<PerkDefinition> CreateDefaultPerks() {
|
||||
return new[] {
|
||||
new PerkDefinition { id = "iron-will", name = "Iron Will", mechanicalBonus = "+1 max health per level", thematicPenalty = "-1 social flexibility in dialogue checks" },
|
||||
new PerkDefinition { id = "steadfast", name = "Steadfast", mechanicalBonus = "-10% stamina loss when bracing", thematicPenalty = "-10% movement speed in retreat events" },
|
||||
new PerkDefinition { id = "nimble-step", name = "Nimble Step", mechanicalBonus = "-15% dodge stamina loss", thematicPenalty = "+10% stamina loss on heavy actions" },
|
||||
new PerkDefinition { id = "lorekeeper", name = "Lorekeeper", mechanicalBonus = "+15% knowledge event success", thematicPenalty = "-10% intimidation success chance" },
|
||||
new PerkDefinition { id = "bulwark", name = "Bulwark", mechanicalBonus = "+2 base defense checks", thematicPenalty = "-1 reflex in stealth checks" },
|
||||
new PerkDefinition { id = "pathfinder", name = "Pathfinder", mechanicalBonus = "+15% scouting event success", thematicPenalty = "-1 max health during ambush events" }
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class CharacterAttributesFactory : ICharacterAttributesFactory {
|
||||
private readonly CharacterFactoryOptions options;
|
||||
|
||||
public CharacterAttributesFactory(CharacterFactoryOptions options = null) {
|
||||
this.options = options ?? new CharacterFactoryOptions();
|
||||
}
|
||||
|
||||
public CharacterAttributes Create(int might, int reflex, int knowledge) {
|
||||
if(might < 0 || reflex < 0 || knowledge < 0) {
|
||||
throw new ArgumentOutOfRangeException(nameof(might), "attributes cannot be negative.");
|
||||
}
|
||||
|
||||
return new CharacterAttributes {
|
||||
might = might,
|
||||
reflex = reflex,
|
||||
knowledge = knowledge
|
||||
};
|
||||
}
|
||||
|
||||
public CharacterAttributes CreateFromPointAllocation(int mightPoints, int reflexPoints, int knowledgePoints) {
|
||||
if(mightPoints < 0 || reflexPoints < 0 || knowledgePoints < 0) {
|
||||
throw new ArgumentOutOfRangeException(nameof(mightPoints), "Point allocation cannot be negative.");
|
||||
}
|
||||
|
||||
int allocated = mightPoints + reflexPoints + knowledgePoints;
|
||||
if(allocated > options.customAttributePointBudget) {
|
||||
throw new ArgumentException($"Allocated {allocated} points but budget is {options.customAttributePointBudget}.");
|
||||
}
|
||||
|
||||
return new CharacterAttributes {
|
||||
might = options.baseMight + mightPoints,
|
||||
reflex = options.baseReflex + reflexPoints,
|
||||
knowledge = options.baseKnowledge + knowledgePoints
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class CharacterStatsFactory : ICharacterStatsFactory {
|
||||
private readonly CharacterStatsFactoryOptions options;
|
||||
|
||||
public CharacterStatsFactory(CharacterStatsFactoryOptions options = null) {
|
||||
this.options = options ?? new CharacterStatsFactoryOptions();
|
||||
}
|
||||
|
||||
public CharacterStats Create(CharacterAttributes attributes) {
|
||||
if(attributes == null) {
|
||||
throw new ArgumentNullException(nameof(attributes));
|
||||
}
|
||||
|
||||
int maxHealth = options.baseHealth + attributes.might * options.mightHealthBonus;
|
||||
int maxStamina = options.baseStamina +
|
||||
attributes.might * options.mightStaminaBonus +
|
||||
attributes.knowledge * options.knowledgeStaminaBonus;
|
||||
float dodgeMultiplier = options.baseDodgeStaminaLossMultiplier -
|
||||
attributes.reflex * options.reflexDodgeStaminaLossReduction;
|
||||
dodgeMultiplier = Math.Max(options.minDodgeStaminaLossMultiplier, dodgeMultiplier);
|
||||
|
||||
return new CharacterStats {
|
||||
maxHealth = maxHealth,
|
||||
maxStamina = maxStamina,
|
||||
dodgeStaminaLossMultiplier = dodgeMultiplier
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class PerkFactory : IPerkFactory {
|
||||
private readonly Dictionary<string, PerkDefinition> perkPool;
|
||||
|
||||
public PerkFactory(IEnumerable<PerkDefinition> perkPool) {
|
||||
if(perkPool == null) {
|
||||
throw new ArgumentNullException(nameof(perkPool));
|
||||
}
|
||||
|
||||
this.perkPool = perkPool
|
||||
.Where(p => p != null && !string.IsNullOrWhiteSpace(p.id))
|
||||
.GroupBy(p => p.id)
|
||||
.ToDictionary(g => g.Key, g => g.First());
|
||||
}
|
||||
|
||||
public IReadOnlyCollection<PerkDefinition> GetAll() {
|
||||
return perkPool.Values.ToList();
|
||||
}
|
||||
|
||||
public PerkDefinition GetById(string perkId) {
|
||||
if(string.IsNullOrWhiteSpace(perkId)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
perkPool.TryGetValue(perkId, out PerkDefinition perk);
|
||||
return perk;
|
||||
}
|
||||
|
||||
public IReadOnlyCollection<PerkDefinition> GetAvailableFor(CharacterData character) {
|
||||
if(character == null) {
|
||||
return perkPool.Values.ToList();
|
||||
}
|
||||
|
||||
HashSet<string> ownedPerkIds = character.perks
|
||||
.Where(p => p != null && !string.IsNullOrWhiteSpace(p.id))
|
||||
.Select(p => p.id)
|
||||
.ToHashSet();
|
||||
|
||||
return perkPool.Values.Where(p => !ownedPerkIds.Contains(p.id)).ToList();
|
||||
}
|
||||
|
||||
public bool TryAddPerk(CharacterData character, string perkId) {
|
||||
if(character == null || string.IsNullOrWhiteSpace(perkId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(character.perks.Any(p => p != null && p.id == perkId)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(!perkPool.TryGetValue(perkId, out PerkDefinition perk)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
character.perks.Add(new PerkDefinition {
|
||||
id = perk.id,
|
||||
name = perk.name,
|
||||
mechanicalBonus = perk.mechanicalBonus,
|
||||
thematicPenalty = perk.thematicPenalty
|
||||
});
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class CharacterFactory : ICharacterFactory {
|
||||
private readonly CharacterFactoryOptions options;
|
||||
private readonly ICharacterAttributesFactory attributesFactory;
|
||||
private readonly ICharacterStatsFactory statsFactory;
|
||||
private readonly IPerkFactory perkFactory;
|
||||
|
||||
public CharacterFactory(
|
||||
ICharacterAttributesFactory attributesFactory,
|
||||
ICharacterStatsFactory statsFactory,
|
||||
IPerkFactory perkFactory,
|
||||
CharacterFactoryOptions options = null) {
|
||||
this.attributesFactory = attributesFactory ?? throw new ArgumentNullException(nameof(attributesFactory));
|
||||
this.statsFactory = statsFactory ?? throw new ArgumentNullException(nameof(statsFactory));
|
||||
this.perkFactory = perkFactory ?? throw new ArgumentNullException(nameof(perkFactory));
|
||||
this.options = options ?? new CharacterFactoryOptions();
|
||||
}
|
||||
|
||||
public CharacterData CreateCustomProtagonist(CustomCharacterCreationRequest request) {
|
||||
if(request == null) {
|
||||
throw new ArgumentNullException(nameof(request));
|
||||
}
|
||||
|
||||
CharacterAttributes attributes = attributesFactory.CreateFromPointAllocation(
|
||||
request.mightPoints,
|
||||
request.reflexPoints,
|
||||
request.knowledgePoints);
|
||||
|
||||
CharacterData character = new CharacterData {
|
||||
id = string.IsNullOrWhiteSpace(request.id) ? Guid.NewGuid().ToString("N") : request.id,
|
||||
displayName = request.displayName,
|
||||
role = CharacterRole.Protagonist,
|
||||
level = options.startingLevel,
|
||||
experience = 0,
|
||||
attributes = attributes,
|
||||
stats = statsFactory.Create(attributes)
|
||||
};
|
||||
|
||||
AddStartingPerks(character, request.startingPerkIds);
|
||||
return character;
|
||||
}
|
||||
|
||||
public CharacterData CreateFromTemplate(CharacterTemplate template, CharacterRole role = CharacterRole.Companion) {
|
||||
if(template == null) {
|
||||
throw new ArgumentNullException(nameof(template));
|
||||
}
|
||||
|
||||
CharacterAttributes sourceAttributes = template.attributes ?? attributesFactory.Create(0, 0, 0);
|
||||
CharacterAttributes attributes = attributesFactory.Create(
|
||||
sourceAttributes.might,
|
||||
sourceAttributes.reflex,
|
||||
sourceAttributes.knowledge);
|
||||
|
||||
CharacterData character = new CharacterData {
|
||||
id = string.IsNullOrWhiteSpace(template.id) ? Guid.NewGuid().ToString("N") : template.id,
|
||||
displayName = template.displayName,
|
||||
role = role,
|
||||
level = template.level <= 0 ? options.startingLevel : template.level,
|
||||
experience = template.experience,
|
||||
attributes = attributes,
|
||||
stats = statsFactory.Create(attributes)
|
||||
};
|
||||
|
||||
AddStartingPerks(character, template.startingPerkIds);
|
||||
return character;
|
||||
}
|
||||
|
||||
private void AddStartingPerks(CharacterData character, IEnumerable<string> perkIds) {
|
||||
if(perkIds == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
foreach(string perkId in perkIds.Distinct()) {
|
||||
perkFactory.TryAddPerk(character, perkId);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public sealed class PartyFactory : IPartyFactory {
|
||||
private readonly PartyFactoryOptions options;
|
||||
|
||||
public PartyFactory(PartyFactoryOptions options = null) {
|
||||
this.options = options ?? new PartyFactoryOptions();
|
||||
}
|
||||
|
||||
public PartyData Create(CharacterData protagonist, IEnumerable<CharacterData> companions = null) {
|
||||
if(protagonist == null) {
|
||||
throw new ArgumentNullException(nameof(protagonist));
|
||||
}
|
||||
|
||||
PartyData party = new PartyData {
|
||||
maxPartySize = options.maxPartySize <= 0 ? int.MaxValue : options.maxPartySize
|
||||
};
|
||||
|
||||
CharacterData protagonistClone = protagonist.Clone();
|
||||
protagonistClone.role = CharacterRole.Protagonist;
|
||||
party.members.Add(protagonistClone);
|
||||
|
||||
if(companions != null) {
|
||||
foreach(CharacterData companion in companions.Where(c => c != null)) {
|
||||
CharacterData companionClone = companion.Clone();
|
||||
companionClone.role = CharacterRole.Companion;
|
||||
party.members.Add(companionClone);
|
||||
}
|
||||
}
|
||||
|
||||
ValidateParty(party);
|
||||
return party;
|
||||
}
|
||||
|
||||
private void ValidateParty(PartyData party) {
|
||||
if(party.members.Count < options.minPartySize) {
|
||||
throw new ArgumentException($"Party size {party.members.Count} is below minimum {options.minPartySize}.");
|
||||
}
|
||||
|
||||
if(party.members.Count > party.maxPartySize) {
|
||||
throw new ArgumentException($"Party size {party.members.Count} exceeds max {party.maxPartySize}.");
|
||||
}
|
||||
|
||||
int protagonistCount = party.members.Count(m => m.role == CharacterRole.Protagonist);
|
||||
if(protagonistCount != 1) {
|
||||
throw new ArgumentException($"Party must contain exactly one protagonist, found {protagonistCount}.");
|
||||
}
|
||||
|
||||
if(options.enforceUniqueCharacterIds) {
|
||||
int uniqueIds = party.members
|
||||
.Where(m => !string.IsNullOrWhiteSpace(m.id))
|
||||
.Select(m => m.id)
|
||||
.Distinct()
|
||||
.Count();
|
||||
if(uniqueIds != party.members.Count) {
|
||||
throw new ArgumentException("Party contains duplicate or missing character ids.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: ec5c743399d71cd41b2db4f9a7e5dec1
|
||||
5
Assets/Code/GameState/Entities/IEntity.cs
Normal file
5
Assets/Code/GameState/Entities/IEntity.cs
Normal file
@@ -0,0 +1,5 @@
|
||||
namespace v {
|
||||
public interface IEntity {
|
||||
|
||||
}
|
||||
}
|
||||
3
Assets/Code/GameState/Entities/IEntity.cs.meta
Normal file
3
Assets/Code/GameState/Entities/IEntity.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 98b50e969ea0412ca3575cd28b6018b7
|
||||
timeCreated: 1772574533
|
||||
60
Assets/Code/GameState/Entities/PartyCreatorModel.cs
Normal file
60
Assets/Code/GameState/Entities/PartyCreatorModel.cs
Normal file
@@ -0,0 +1,60 @@
|
||||
namespace Nox.Game {
|
||||
public class PartyCreatorModel {
|
||||
private readonly ICharacterFactory characterFactory;
|
||||
private readonly IPartyFactory partyFactory;
|
||||
public PartyCreatorModel(ICharacterFactory characterFactory, IPartyFactory partyFactory) {
|
||||
this.characterFactory = characterFactory;
|
||||
this.partyFactory = partyFactory;
|
||||
}
|
||||
public PartyData CreatePartyForNewRun(int companionCount) {
|
||||
var protagonist = characterFactory.CreateCustomProtagonist(new CustomCharacterCreationRequest {
|
||||
id = "protagonist",
|
||||
displayName = "The Warden",
|
||||
mightPoints = 4,
|
||||
reflexPoints = 3,
|
||||
knowledgePoints = 3,
|
||||
startingPerkIds = new System.Collections.Generic.List<string> { "iron-will" }
|
||||
});
|
||||
|
||||
CharacterTemplate[] companionTemplates = {
|
||||
new() {
|
||||
id = "companion-bruiser",
|
||||
displayName = "Rook",
|
||||
attributes = new CharacterAttributes { might = 5, reflex = 2, knowledge = 1 },
|
||||
startingPerkIds = new System.Collections.Generic.List<string> { "steadfast" }
|
||||
},
|
||||
new() {
|
||||
id = "companion-scout",
|
||||
displayName = "Sable",
|
||||
attributes = new CharacterAttributes { might = 2, reflex = 5, knowledge = 1 },
|
||||
startingPerkIds = new System.Collections.Generic.List<string> { "nimble-step" }
|
||||
},
|
||||
new() {
|
||||
id = "companion-scholar",
|
||||
displayName = "Quill",
|
||||
attributes = new CharacterAttributes { might = 1, reflex = 2, knowledge = 5 },
|
||||
startingPerkIds = new System.Collections.Generic.List<string> { "lorekeeper" }
|
||||
},
|
||||
new() {
|
||||
id = "companion-vanguard",
|
||||
displayName = "Brant",
|
||||
attributes = new CharacterAttributes { might = 4, reflex = 3, knowledge = 2 },
|
||||
startingPerkIds = new System.Collections.Generic.List<string> { "bulwark" }
|
||||
},
|
||||
new() {
|
||||
id = "companion-tracker",
|
||||
displayName = "Mira",
|
||||
attributes = new CharacterAttributes { might = 2, reflex = 4, knowledge = 3 },
|
||||
startingPerkIds = new System.Collections.Generic.List<string> { "pathfinder" }
|
||||
}
|
||||
};
|
||||
|
||||
var companions = new System.Collections.Generic.List<CharacterData>();
|
||||
for(var i = 0; i < companionCount && i < companionTemplates.Length; i++) {
|
||||
companions.Add(characterFactory.CreateFromTemplate(companionTemplates[i], CharacterRole.Companion));
|
||||
}
|
||||
|
||||
return partyFactory.Create(protagonist, companions);
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Code/GameState/Entities/PartyCreatorModel.cs.meta
Normal file
3
Assets/Code/GameState/Entities/PartyCreatorModel.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: f231d6e487bc4577847de98936498bde
|
||||
timeCreated: 1772644731
|
||||
6
Assets/Code/GameState/Entities/PartyReference.cs
Normal file
6
Assets/Code/GameState/Entities/PartyReference.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Nox.Game {
|
||||
public class PartyReference : MonoBehaviour {
|
||||
}
|
||||
}
|
||||
2
Assets/Code/GameState/Entities/PartyReference.cs.meta
Normal file
2
Assets/Code/GameState/Entities/PartyReference.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 067750df0e87b834bbbb239b7c03abb4
|
||||
79
Assets/Code/GameState/NoxSaveData.cs
Normal file
79
Assets/Code/GameState/NoxSaveData.cs
Normal file
@@ -0,0 +1,79 @@
|
||||
using Jovian.SaveSystem;
|
||||
using Nox.Core;
|
||||
using System;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using PlayMode = Nox.Core.PlayMode;
|
||||
|
||||
namespace Nox.Game {
|
||||
|
||||
public class NoxSaveData {
|
||||
public static NoxSavedDataSet RestoreSavedData(
|
||||
ISaveSystem saveSystem,
|
||||
GameDataState gameDataState,
|
||||
ref AdventureData adventureData) {
|
||||
var sessions = saveSystem.GetAllSessions().OrderByDescending(s => s.lastSaveDateUtc).ToList();
|
||||
if(sessions.Count == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var latestSession = sessions[0];
|
||||
var slots = saveSystem.GetSlots(latestSession.sessionId).OrderByDescending(s => s.timestampUtc).ToList();
|
||||
if(slots.Count == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
var latestSlot = slots[0];
|
||||
var saveData = saveSystem.Load<NoxSavedDataSet>(latestSlot);
|
||||
Debug.Log($"Loaded save {latestSlot.DisplayLabel}");
|
||||
if(saveData == null) {
|
||||
Debug.LogError("Failed to load save data");
|
||||
return null;
|
||||
}
|
||||
|
||||
gameDataState.activeSessionId = latestSession.sessionId;
|
||||
gameDataState.savedPartyPosition = saveData.partyPosition.ToVector3();
|
||||
gameDataState.ActiveParty = saveData.partyData;
|
||||
adventureData = saveData.adventureData;
|
||||
|
||||
return saveData;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The game's save data snapshot. Contains all state needed to restore a game session.
|
||||
/// This is the TData passed to the save system package.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public sealed class NoxSavedDataSet {
|
||||
// game state
|
||||
public PlayMode activePlayMode;
|
||||
|
||||
//game mode specific data
|
||||
public AdventureData adventureData;
|
||||
|
||||
// Party
|
||||
public PartyData partyData;
|
||||
public SerializableVector3 partyPosition;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// JSON-friendly Vector3 representation for save data.
|
||||
/// </summary>
|
||||
[Serializable]
|
||||
public struct SerializableVector3 {
|
||||
public float x;
|
||||
public float y;
|
||||
public float z;
|
||||
|
||||
public static SerializableVector3 Zero => new SerializableVector3 { x = 0, y = 0, z = 0 };
|
||||
|
||||
public static SerializableVector3 FromVector3(Vector3 value) {
|
||||
return new SerializableVector3 { x = value.x, y = value.y, z = value.z };
|
||||
}
|
||||
|
||||
public Vector3 ToVector3() {
|
||||
return new Vector3(x, y, z);
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Code/GameState/NoxSaveData.cs.meta
Normal file
2
Assets/Code/GameState/NoxSaveData.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: b62368c672d7fde4d94dc19ae74e133d
|
||||
3
Assets/Code/GameState/PlayModes.meta
Normal file
3
Assets/Code/GameState/PlayModes.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 35a9294689c9454fba6e3cd8d818c38f
|
||||
timeCreated: 1772367730
|
||||
12
Assets/Code/GameState/PlayModes/AdventureModePrefabs.cs
Normal file
12
Assets/Code/GameState/PlayModes/AdventureModePrefabs.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using Nox.Game.UI;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Nox.Game {
|
||||
[CreateAssetMenu(fileName = "GameModePrefabs", menuName = "Nox/AdventureMapPrefabs")]
|
||||
public class AdventureModePrefabs: ScenePrefabs {
|
||||
public GuiReferences guiReferencesPrefab;
|
||||
public MapReference mapReferencePrefab;
|
||||
public MapLocationsReference mapLocationsReferencePrefab;
|
||||
public PartyReference partyReferencePrefab;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e7cce6bb84bb20741a560389ab887769
|
||||
174
Assets/Code/GameState/PlayModes/AdventurePlayMode.cs
Normal file
174
Assets/Code/GameState/PlayModes/AdventurePlayMode.cs
Normal file
@@ -0,0 +1,174 @@
|
||||
using Jovian.SaveSystem;
|
||||
using Jovian.ZoneSystem;
|
||||
using Nox.Core;
|
||||
using Nox.Platform;
|
||||
using Nox.Game.UI;
|
||||
using System.Linq;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using PlayMode = Nox.Core.PlayMode;
|
||||
|
||||
namespace Nox.Game {
|
||||
public class AdventureData {
|
||||
public bool isPartyMoving;
|
||||
public int currentDay = 0;
|
||||
public int suppliesAvailable = -1;
|
||||
public float currentTime = -1f;
|
||||
public DayPhase currentDayPhase = DayPhase.Morning;
|
||||
}
|
||||
|
||||
public class AdventurePlayMode : IPlayMode {
|
||||
private readonly PlatformSettings platformSettings;
|
||||
private readonly PlayModeSettings bootstrapSettings;
|
||||
private readonly GameDataState gameDataState;
|
||||
private readonly ISaveSystem saveSystem;
|
||||
private PartyData partyData;
|
||||
private AdventureData adventureData;
|
||||
private AdventureModePrefabs scenePrefabs;
|
||||
private ICameraController cameraController;
|
||||
private MapReference mapRef;
|
||||
private PartyMovementHandler partyMovementHandler;
|
||||
private PartyReference partyRef;
|
||||
private MapLocationsReference mapLocationsReference;
|
||||
private InputSystem_Actions inputActions;
|
||||
private AdventureView adventureView;
|
||||
private ZoneSystem zoneSystem;
|
||||
private GuiReferences guiReferences;
|
||||
private AdventureSettings adventureSettings;
|
||||
private TimeHandler timeHandler;
|
||||
private PartyInventoryHandler partyInventoryHandler;
|
||||
|
||||
public AdventurePlayMode(
|
||||
PlatformSettings platformSettings,
|
||||
PartyData partyData,
|
||||
PlayModeSettings bootstrapSettings,
|
||||
GameDataState gameDataState,
|
||||
ISaveSystem saveSystem,
|
||||
AdventureSettings adventureSettings,
|
||||
AdventureData adventureData) {
|
||||
this.platformSettings = platformSettings;
|
||||
this.partyData = partyData;
|
||||
this.bootstrapSettings = bootstrapSettings;
|
||||
this.gameDataState = gameDataState;
|
||||
this.saveSystem = saveSystem;
|
||||
this.adventureSettings = adventureSettings;
|
||||
this.adventureData = adventureData;
|
||||
}
|
||||
|
||||
public bool IsGameModeInitialized { get; private set; }
|
||||
|
||||
public void EnterPlayMode() {
|
||||
inputActions = platformSettings.inputSettings.inputActions;
|
||||
if(IsGameModeInitialized) {
|
||||
inputActions.Player.Enable();
|
||||
inputActions.UI.PauseMenu.Enable();
|
||||
partyMovementHandler.ConsumeNextClick();
|
||||
return;
|
||||
}
|
||||
Addressables.LoadSceneAsync(bootstrapSettings.gameModeData.FirstOrDefault(g => g.playMode == PlayMode.Adventure)?.scene)
|
||||
.WaitForCompletion().ActivateAsync().completed += InitializeGameMode;
|
||||
}
|
||||
|
||||
private void InitializeGameMode(AsyncOperation obj) {
|
||||
inputActions.Player.Enable();
|
||||
inputActions.UI.PauseMenu.Enable();
|
||||
Debug.Log("Entering Adventure Play Mode");
|
||||
if(partyData == null) {
|
||||
var sessions = saveSystem.GetAllSessions().OrderByDescending(s => s.lastSaveDateUtc).ToList();
|
||||
if(sessions.Count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var latestSession = sessions[0];
|
||||
var slots = saveSystem.GetSlots(latestSession.sessionId).OrderByDescending(s => s.timestampUtc).ToList();
|
||||
if(slots.Count == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
var latestSlot = slots[0];
|
||||
var saveData = saveSystem.Load<NoxSaveData>(latestSlot);
|
||||
Debug.Log($"Loaded save {latestSlot.DisplayLabel}");
|
||||
if(saveData == null) {
|
||||
Debug.LogError("Failed to load save data");
|
||||
return;
|
||||
}
|
||||
|
||||
NoxSaveData.RestoreSavedData(saveSystem, gameDataState, ref adventureData);
|
||||
Debug.LogWarning("AdventurePlayMode started from the Adventure Scene. Loading the last Autosave");
|
||||
}
|
||||
|
||||
scenePrefabs ??= Addressables.LoadAssetAsync<AdventureModePrefabs>("AdventureMapPrefabs").WaitForCompletion();
|
||||
mapRef ??= Object.FindFirstObjectByType<MapReference>();
|
||||
partyRef ??= Object.FindFirstObjectByType<PartyReference>();
|
||||
if(partyRef && gameDataState.savedPartyPosition.HasValue) {
|
||||
partyRef.transform.position = gameDataState.savedPartyPosition.Value;
|
||||
}
|
||||
mapLocationsReference ??= Object.FindFirstObjectByType<MapLocationsReference>();
|
||||
if(!mapRef) {
|
||||
mapRef ??= Object.Instantiate(scenePrefabs.mapReferencePrefab);
|
||||
}
|
||||
cameraController ??= new CameraController(platformSettings, mapRef);
|
||||
cameraController.Initialize();
|
||||
|
||||
if(adventureData.suppliesAvailable == -1) {
|
||||
adventureData.suppliesAvailable = adventureSettings.maxSupplies;
|
||||
}
|
||||
if(Mathf.Approximately(adventureData.currentTime, -1f)) {
|
||||
adventureData.currentTime = 0.25f;
|
||||
}
|
||||
|
||||
partyInventoryHandler ??= new PartyInventoryHandler(adventureData, adventureSettings);
|
||||
partyInventoryHandler.Initialize();
|
||||
|
||||
timeHandler ??= new TimeHandler(adventureSettings, adventureData);
|
||||
zoneSystem ??= new ZoneSystem(mapRef.zonesObjectHolder);
|
||||
|
||||
partyMovementHandler ??= new PartyMovementHandler(partyRef, cameraController, mapLocationsReference, platformSettings.inputSettings, zoneSystem, adventureData, adventureSettings);
|
||||
partyMovementHandler.Initialize();
|
||||
|
||||
guiReferences ??= Object.FindFirstObjectByType<GuiReferences>();
|
||||
adventureView ??= new AdventureView(gameDataState, guiReferences, inputActions, adventureData, adventureSettings);
|
||||
adventureView.Initialize();
|
||||
|
||||
IsGameModeInitialized = true;
|
||||
}
|
||||
|
||||
public void Tick() {
|
||||
if(!IsGameModeInitialized) {
|
||||
return;
|
||||
}
|
||||
|
||||
timeHandler.Tick();
|
||||
partyInventoryHandler.Tick();
|
||||
partyMovementHandler.Tick();
|
||||
adventureView.Tick();
|
||||
|
||||
if(inputActions.UI.PauseMenu.WasPerformedThisFrame()) {
|
||||
gameDataState.ChangePlayMode(PlayMode.PauseMenu);
|
||||
}
|
||||
}
|
||||
public void LateTick() {
|
||||
if(!IsGameModeInitialized) {
|
||||
return;
|
||||
}
|
||||
cameraController.Tick();
|
||||
}
|
||||
public NoxSavedDataSet CaptureNoxSaveData() {
|
||||
return new NoxSavedDataSet {
|
||||
activePlayMode = PlayMode.Adventure,
|
||||
partyData = partyData,
|
||||
partyPosition = partyRef ? SerializableVector3.FromVector3(partyRef.transform.position) : SerializableVector3.Zero
|
||||
};
|
||||
}
|
||||
|
||||
public void ExitGameMode() {
|
||||
inputActions.Player.Disable();
|
||||
inputActions.UI.PauseMenu.Disable();
|
||||
}
|
||||
public void Dispose() {
|
||||
cameraController.Dispose();
|
||||
partyMovementHandler.Dispose();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eb1c247bb12a45a587e8ae7d013038d7
|
||||
timeCreated: 1771156737
|
||||
12
Assets/Code/GameState/PlayModes/AdventureSettings.cs
Normal file
12
Assets/Code/GameState/PlayModes/AdventureSettings.cs
Normal file
@@ -0,0 +1,12 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Nox.Game {
|
||||
[CreateAssetMenu(fileName = "AdventureSettings", menuName = "Nox/AdventureSettings")]
|
||||
public class AdventureSettings : ScriptableObject {
|
||||
public int dayLength = 10;
|
||||
public int maxSupplies = 20;
|
||||
|
||||
[Header("Party Data")]
|
||||
public float partyBaseSpeed = 3f;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1d2424482c4843c0b7a33a9cc67411b2
|
||||
timeCreated: 1771786400
|
||||
31
Assets/Code/GameState/PlayModes/CombatPlayMode.cs
Normal file
31
Assets/Code/GameState/PlayModes/CombatPlayMode.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using Nox.Core;
|
||||
using Nox.Platform;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Nox.Game {
|
||||
public class CombatPlayMode : IPlayMode {
|
||||
private readonly PlatformSettings platformSettings;
|
||||
private readonly PartyData partyData;
|
||||
|
||||
public CombatPlayMode(PlatformSettings platformSettings, PartyData partyData) {
|
||||
this.platformSettings = platformSettings;
|
||||
this.partyData = partyData;
|
||||
}
|
||||
|
||||
public bool IsGameModeInitialized { get; private set; }
|
||||
|
||||
public void EnterPlayMode() {
|
||||
if(partyData == null) {
|
||||
Debug.LogWarning("CombatPlayMode started without PartyData.");
|
||||
}
|
||||
|
||||
Debug.Log("Entering Combat Play Mode");
|
||||
IsGameModeInitialized = true;
|
||||
}
|
||||
|
||||
public void Tick() { }
|
||||
public void LateTick() { }
|
||||
public void ExitGameMode() { }
|
||||
public void Dispose() { }
|
||||
}
|
||||
}
|
||||
3
Assets/Code/GameState/PlayModes/CombatPlayMode.cs.meta
Normal file
3
Assets/Code/GameState/PlayModes/CombatPlayMode.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 087dc559ae894cd8b9a9dee5c6e3b64c
|
||||
timeCreated: 1772367690
|
||||
10
Assets/Code/GameState/PlayModes/IMenuView.cs
Normal file
10
Assets/Code/GameState/PlayModes/IMenuView.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
#nullable enable
|
||||
|
||||
namespace Nox.Game.UI {
|
||||
public interface IMenuView {
|
||||
void Initialize();
|
||||
void Show();
|
||||
void Hide();
|
||||
void Tick();
|
||||
}
|
||||
}
|
||||
3
Assets/Code/GameState/PlayModes/IMenuView.cs.meta
Normal file
3
Assets/Code/GameState/PlayModes/IMenuView.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: e99c535c23cc449984dcd145f4c1bec8
|
||||
timeCreated: 1772374016
|
||||
10
Assets/Code/GameState/PlayModes/MapLocation.cs
Normal file
10
Assets/Code/GameState/PlayModes/MapLocation.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Nox.Game {
|
||||
public class MapLocation : MonoBehaviour {
|
||||
public SpriteRenderer icon;
|
||||
public SpriteRenderer highlight;
|
||||
public TextMeshProUGUI nameText;
|
||||
}
|
||||
}
|
||||
2
Assets/Code/GameState/PlayModes/MapLocation.cs.meta
Normal file
2
Assets/Code/GameState/PlayModes/MapLocation.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 436b68ae9b9d347469b78e52a5aa7b53
|
||||
7
Assets/Code/GameState/PlayModes/MapLocationsReference.cs
Normal file
7
Assets/Code/GameState/PlayModes/MapLocationsReference.cs
Normal file
@@ -0,0 +1,7 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Nox.Game {
|
||||
public class MapLocationsReference : MonoBehaviour {
|
||||
public NoxLocationInfo [] mapLocations;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bdd66cc132efc3640b46bf53dedcbcb4
|
||||
9
Assets/Code/GameState/PlayModes/MapReference.cs
Normal file
9
Assets/Code/GameState/PlayModes/MapReference.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using Jovian.ZoneSystem;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Nox.Game {
|
||||
public class MapReference : MonoBehaviour {
|
||||
public GameObject mapPlane;
|
||||
public ZonesObjectHolder zonesObjectHolder;
|
||||
}
|
||||
}
|
||||
2
Assets/Code/GameState/PlayModes/MapReference.cs.meta
Normal file
2
Assets/Code/GameState/PlayModes/MapReference.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 7c173f913efc6004faf5dd75e96b32fe
|
||||
21
Assets/Code/GameState/PlayModes/NoxMapData.cs
Normal file
21
Assets/Code/GameState/PlayModes/NoxMapData.cs
Normal file
@@ -0,0 +1,21 @@
|
||||
using System;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Nox.Game {
|
||||
public enum NoxMapLocations {
|
||||
None,
|
||||
City1,
|
||||
City2,
|
||||
Village1,
|
||||
Village2,
|
||||
Ruin1,
|
||||
Ruin2
|
||||
}
|
||||
|
||||
[Serializable]
|
||||
public class NoxLocationInfo {
|
||||
public NoxMapLocations locationId;
|
||||
public MapLocation mapLocation;
|
||||
}
|
||||
|
||||
}
|
||||
3
Assets/Code/GameState/PlayModes/NoxMapData.cs.meta
Normal file
3
Assets/Code/GameState/PlayModes/NoxMapData.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 535e7fdf7ddb4c1f9edf7254c3bb0acd
|
||||
timeCreated: 1771763769
|
||||
25
Assets/Code/GameState/PlayModes/PartyInventoryHandler.cs
Normal file
25
Assets/Code/GameState/PlayModes/PartyInventoryHandler.cs
Normal file
@@ -0,0 +1,25 @@
|
||||
namespace Nox.Game {
|
||||
public class PartyInventoryHandler {
|
||||
private readonly AdventureData adventureData;
|
||||
private readonly AdventureSettings adventureSettings;
|
||||
private int currentDay;
|
||||
|
||||
public PartyInventoryHandler(AdventureData adventureData, AdventureSettings adventureSettings) {
|
||||
this.adventureData = adventureData;
|
||||
this.adventureSettings = adventureSettings;
|
||||
}
|
||||
|
||||
public void Initialize() {
|
||||
currentDay = adventureData.currentDay;
|
||||
}
|
||||
|
||||
public void Tick() {
|
||||
if(currentDay != adventureData.currentDay) {
|
||||
currentDay = adventureData.currentDay;
|
||||
if(adventureData.suppliesAvailable > 0) {
|
||||
adventureData.suppliesAvailable--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c4a1ab7b529042ca8bf42714b383f918
|
||||
timeCreated: 1773614576
|
||||
160
Assets/Code/GameState/PlayModes/PartyMovementHandler.cs
Normal file
160
Assets/Code/GameState/PlayModes/PartyMovementHandler.cs
Normal file
@@ -0,0 +1,160 @@
|
||||
using Jovian.ZoneSystem;
|
||||
using UnityEngine;
|
||||
using UnityEngine.EventSystems;
|
||||
using UnityEngine.InputSystem;
|
||||
|
||||
namespace Nox.Game {
|
||||
public class PartyMovementHandler {
|
||||
private readonly PartyReference partyReference;
|
||||
private readonly ICameraController cameraController;
|
||||
private readonly InputSystem_Actions inputActions;
|
||||
private readonly MapLocationsReference mapLocationsReference;
|
||||
private readonly ZoneSystem zoneSystem;
|
||||
private readonly AdventureSettings adventureSettings;
|
||||
|
||||
private readonly float maxDistance = 100f;
|
||||
|
||||
private AdventureData adventureData;
|
||||
private bool partyCanMove;
|
||||
private Vector3 targetPosition;
|
||||
private bool shouldHover;
|
||||
private MapLocation currentSelectedPoi;
|
||||
private string previousZoneId;
|
||||
private bool hasClicked;
|
||||
private bool skipNextClick;
|
||||
|
||||
public LayerMask clickToMoveMask = LayerMask.GetMask("Clickable");
|
||||
public LayerMask hoverOverMask = LayerMask.GetMask("Hoverable");
|
||||
|
||||
public PartyMovementHandler(
|
||||
PartyReference partyReference,
|
||||
ICameraController cameraController,
|
||||
MapLocationsReference mapLocationsReference,
|
||||
Input.InputSettings inputSettings,
|
||||
ZoneSystem zoneSystem,
|
||||
AdventureData adventureData,
|
||||
AdventureSettings adventureSettings) {
|
||||
this.partyReference = partyReference;
|
||||
this.cameraController = cameraController;
|
||||
this.mapLocationsReference = mapLocationsReference;
|
||||
this.zoneSystem = zoneSystem;
|
||||
this.adventureData = adventureData;
|
||||
this.adventureSettings = adventureSettings;
|
||||
|
||||
inputActions = inputSettings.inputActions;
|
||||
}
|
||||
|
||||
public void Initialize() {
|
||||
inputActions.Player.ClickOnMap.performed += OnClickOnMap;
|
||||
}
|
||||
|
||||
public void ConsumeNextClick() {
|
||||
skipNextClick = true;
|
||||
}
|
||||
|
||||
private void OnClickOnMap(InputAction.CallbackContext obj) {
|
||||
if(!obj.action.WasReleasedThisFrame()) {
|
||||
return;
|
||||
}
|
||||
hasClicked = true;
|
||||
}
|
||||
|
||||
private void HandleHoverableLocation(RaycastHit hitInfo) {
|
||||
var go = hitInfo.collider.gameObject;
|
||||
if(!go.transform.parent.TryGetComponent(out MapLocation mapLocation)) {
|
||||
return;
|
||||
}
|
||||
currentSelectedPoi = mapLocation;
|
||||
mapLocation.highlight.gameObject.SetActive(true);
|
||||
}
|
||||
|
||||
private void HandleClickableLocation() {
|
||||
var screenPos = inputActions.Player.Point.ReadValue<Vector2>();
|
||||
var ray = cameraController.CameraReference.mainCamera.ScreenPointToRay(screenPos);
|
||||
if(!Physics.Raycast(ray, out var clickHit, maxDistance, clickToMoveMask, QueryTriggerInteraction.Ignore)) {
|
||||
return;
|
||||
}
|
||||
if(!clickHit.collider.gameObject.transform.parent.TryGetComponent(out MapReference mapReference)) {
|
||||
return;
|
||||
}
|
||||
//if(mapReference.ValidateMoveLocation(hitInfoPoint)) {
|
||||
targetPosition = clickHit.point;
|
||||
partyCanMove = true;
|
||||
//}
|
||||
}
|
||||
|
||||
public void Tick() {
|
||||
HandleHover();
|
||||
|
||||
if(hasClicked) {
|
||||
hasClicked = false;
|
||||
// should it skip next click to avoid click-through from the pause menu?
|
||||
if(skipNextClick) {
|
||||
skipNextClick = false;
|
||||
return;
|
||||
}
|
||||
if(EventSystem.current.IsPointerOverGameObject()) {
|
||||
return;
|
||||
}
|
||||
HandleClickableLocation();
|
||||
}
|
||||
|
||||
if(!partyCanMove) {
|
||||
adventureData.isPartyMoving = false;
|
||||
return;
|
||||
}
|
||||
|
||||
if(Vector3.Distance(partyReference.transform.position, targetPosition) < 0.05f) {
|
||||
partyReference.transform.position = targetPosition;
|
||||
VerifyPointsOfInterest();
|
||||
partyCanMove = false;
|
||||
}
|
||||
adventureData.isPartyMoving = true;
|
||||
partyReference.transform.position = Vector3.MoveTowards(new Vector3(partyReference.transform.position.x, 0, partyReference.transform.position.z), targetPosition, Time.deltaTime * adventureSettings.partyBaseSpeed);
|
||||
VerifyZones(partyReference.transform.position);
|
||||
}
|
||||
|
||||
private void HandleHover() {
|
||||
if(EventSystem.current.IsPointerOverGameObject()) {
|
||||
return;
|
||||
}
|
||||
var screenPos = inputActions.Player.Point.ReadValue<Vector2>();
|
||||
var ray = cameraController.CameraReference.mainCamera.ScreenPointToRay(screenPos);
|
||||
if(Physics.Raycast(ray, out var hoverHit, maxDistance, hoverOverMask, QueryTriggerInteraction.Ignore)) {
|
||||
HandleHoverableLocation(hoverHit);
|
||||
}
|
||||
else {
|
||||
currentSelectedPoi?.highlight.gameObject.SetActive(false);
|
||||
currentSelectedPoi = null;
|
||||
}
|
||||
}
|
||||
private void VerifyZones(Vector3 position) {
|
||||
var zoneContext = zoneSystem.QueryZone(position);
|
||||
var currentZoneId = zoneContext.resolvedZoneId;
|
||||
|
||||
if(currentZoneId != previousZoneId) {
|
||||
if(!string.IsNullOrEmpty(currentZoneId)) {
|
||||
Debug.Log($"Entered zone: {currentZoneId} (encounter: {zoneContext.encounterTableId}, safe: {zoneContext.isSafe})");
|
||||
}
|
||||
else if(!string.IsNullOrEmpty(previousZoneId)) {
|
||||
Debug.Log($"Left zone: {previousZoneId}");
|
||||
}
|
||||
previousZoneId = currentZoneId;
|
||||
}
|
||||
}
|
||||
|
||||
private void VerifyPointsOfInterest() {
|
||||
foreach(var location in mapLocationsReference.mapLocations) {
|
||||
if(Vector3.Distance(location.mapLocation.transform.position, partyReference.transform.position) < 0.4f) {
|
||||
Debug.Log($"Arrived at {location.locationId}");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Dispose() {
|
||||
inputActions.Player.ClickOnMap.performed -= OnClickOnMap;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d2f697207298f2242aa2a8d36ef6a9b4
|
||||
47
Assets/Code/GameState/PlayModes/PauseMenuPlayMode.cs
Normal file
47
Assets/Code/GameState/PlayModes/PauseMenuPlayMode.cs
Normal file
@@ -0,0 +1,47 @@
|
||||
#nullable enable
|
||||
using Nox.Core;
|
||||
using Nox.Platform;
|
||||
using Nox.Game.UI;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Nox.Game {
|
||||
public class PauseMenuPlayMode : IPlayMode {
|
||||
private readonly PlatformSettings platformSettings;
|
||||
private readonly GameDataState gameDataState;
|
||||
private PauseMenuReferences? pauseMenuReference;
|
||||
private readonly IMenuView? pauseMenuView;
|
||||
private readonly InputSystem_Actions inputActions;
|
||||
|
||||
public PauseMenuPlayMode(PlatformSettings platformSettings, GameDataState gameDataState, IMenuView pauseMenuView) {
|
||||
this.platformSettings = platformSettings;
|
||||
this.gameDataState = gameDataState;
|
||||
this.pauseMenuView = pauseMenuView;
|
||||
inputActions = platformSettings.inputSettings.inputActions;
|
||||
inputActions.UI.Enable();
|
||||
}
|
||||
|
||||
public bool IsGameModeInitialized { get; private set; }
|
||||
|
||||
public void EnterPlayMode() {
|
||||
Debug.Log("Entering PauseMenu Play Mode");
|
||||
pauseMenuView?.Initialize();
|
||||
IsGameModeInitialized = true;
|
||||
}
|
||||
|
||||
public void Tick() {
|
||||
if(inputActions.UI.PauseMenu.WasPerformedThisFrame()) {
|
||||
pauseMenuView?.Hide();
|
||||
gameDataState.ChangePlayMode(gameDataState.PreviousPlayMode);
|
||||
}
|
||||
pauseMenuView?.Tick();
|
||||
}
|
||||
public void LateTick() { }
|
||||
public void ExitGameMode() {
|
||||
pauseMenuView?.Hide();
|
||||
}
|
||||
public void Dispose() {
|
||||
inputActions.UI.Disable();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: eab5b722cabf4ec7b79af55838d24ed4
|
||||
timeCreated: 1772367657
|
||||
31
Assets/Code/GameState/PlayModes/RestPlayMode.cs
Normal file
31
Assets/Code/GameState/PlayModes/RestPlayMode.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using Nox.Core;
|
||||
using Nox.Platform;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Nox.Game {
|
||||
public class RestPlayMode : IPlayMode {
|
||||
private readonly PlatformSettings platformSettings;
|
||||
private readonly PartyData partyData;
|
||||
|
||||
public RestPlayMode(PlatformSettings platformSettings, PartyData partyData) {
|
||||
this.platformSettings = platformSettings;
|
||||
this.partyData = partyData;
|
||||
}
|
||||
|
||||
public bool IsGameModeInitialized { get; private set; }
|
||||
|
||||
public void EnterPlayMode() {
|
||||
if(partyData == null) {
|
||||
Debug.LogWarning("RestPlayMode started without PartyData.");
|
||||
}
|
||||
|
||||
Debug.Log("Entering Rest Play Mode");
|
||||
IsGameModeInitialized = true;
|
||||
}
|
||||
|
||||
public void Tick() { }
|
||||
public void LateTick() { }
|
||||
public void ExitGameMode() { }
|
||||
public void Dispose() { }
|
||||
}
|
||||
}
|
||||
3
Assets/Code/GameState/PlayModes/RestPlayMode.cs.meta
Normal file
3
Assets/Code/GameState/PlayModes/RestPlayMode.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 67cb7d9e318d474694bc0addab3e8511
|
||||
timeCreated: 1772367678
|
||||
58
Assets/Code/GameState/PlayModes/TimeHandler.cs
Normal file
58
Assets/Code/GameState/PlayModes/TimeHandler.cs
Normal file
@@ -0,0 +1,58 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Nox.Game {
|
||||
public enum DayPhase {
|
||||
Midnight,
|
||||
Dawn,
|
||||
Morning,
|
||||
Afternoon,
|
||||
Dusk,
|
||||
Night
|
||||
}
|
||||
|
||||
public class TimeHandler {
|
||||
private readonly AdventureSettings adventureSettings;
|
||||
private readonly AdventureData adventureData;
|
||||
|
||||
private float localTime;
|
||||
|
||||
private static readonly (float start, DayPhase phase)[] PhaseThresholds = {
|
||||
(0.00f, DayPhase.Midnight),
|
||||
(0.05f, DayPhase.Night),
|
||||
(0.17f, DayPhase.Dawn),
|
||||
(0.25f, DayPhase.Morning),
|
||||
(0.50f, DayPhase.Afternoon),
|
||||
(0.75f, DayPhase.Dusk),
|
||||
(0.90f, DayPhase.Night),
|
||||
};
|
||||
|
||||
public TimeHandler(AdventureSettings adventureSettings, AdventureData adventureData) {
|
||||
this.adventureSettings = adventureSettings;
|
||||
this.adventureData = adventureData;
|
||||
localTime = adventureData.currentTime * adventureSettings.dayLength;
|
||||
}
|
||||
|
||||
public void Tick() {
|
||||
if (!adventureData.isPartyMoving) return;
|
||||
|
||||
localTime += Time.deltaTime;
|
||||
|
||||
if (localTime >= adventureSettings.dayLength) {
|
||||
localTime -= adventureSettings.dayLength;
|
||||
adventureData.currentDay++;
|
||||
}
|
||||
|
||||
adventureData.currentTime = localTime / adventureSettings.dayLength;
|
||||
adventureData.currentDayPhase = GetPhase(adventureData.currentTime);
|
||||
}
|
||||
|
||||
private static DayPhase GetPhase(float t) {
|
||||
var phase = DayPhase.Midnight;
|
||||
foreach (var (start, p) in PhaseThresholds) {
|
||||
if (t >= start) phase = p;
|
||||
else break;
|
||||
}
|
||||
return phase;
|
||||
}
|
||||
}
|
||||
}
|
||||
2
Assets/Code/GameState/PlayModes/TimeHandler.cs.meta
Normal file
2
Assets/Code/GameState/PlayModes/TimeHandler.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 071a61f838db8e640b460ce029a8d5a5
|
||||
31
Assets/Code/GameState/PlayModes/TownPlayMode.cs
Normal file
31
Assets/Code/GameState/PlayModes/TownPlayMode.cs
Normal file
@@ -0,0 +1,31 @@
|
||||
using Nox.Core;
|
||||
using Nox.Platform;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Nox.Game {
|
||||
public class TownPlayMode : IPlayMode {
|
||||
private readonly PlatformSettings platformSettings;
|
||||
private readonly PartyData partyData;
|
||||
|
||||
public TownPlayMode(PlatformSettings platformSettings, PartyData partyData) {
|
||||
this.platformSettings = platformSettings;
|
||||
this.partyData = partyData;
|
||||
}
|
||||
|
||||
public bool IsGameModeInitialized { get; private set; }
|
||||
|
||||
public void EnterPlayMode() {
|
||||
if(partyData == null) {
|
||||
Debug.LogWarning("TownPlayMode started without PartyData.");
|
||||
}
|
||||
|
||||
Debug.Log("Entering Town Play Mode");
|
||||
IsGameModeInitialized = true;
|
||||
}
|
||||
|
||||
public void Tick() { }
|
||||
public void LateTick() { }
|
||||
public void ExitGameMode() { }
|
||||
public void Dispose() { }
|
||||
}
|
||||
}
|
||||
3
Assets/Code/GameState/PlayModes/TownPlayMode.cs.meta
Normal file
3
Assets/Code/GameState/PlayModes/TownPlayMode.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: bd57598925a54bad93fe730e15023d94
|
||||
timeCreated: 1772367666
|
||||
6
Assets/Code/GameState/ScenePrefabs.cs
Normal file
6
Assets/Code/GameState/ScenePrefabs.cs
Normal file
@@ -0,0 +1,6 @@
|
||||
using UnityEngine;
|
||||
|
||||
namespace Nox.Game {
|
||||
public class ScenePrefabs : ScriptableObject {
|
||||
}
|
||||
}
|
||||
3
Assets/Code/GameState/ScenePrefabs.cs.meta
Normal file
3
Assets/Code/GameState/ScenePrefabs.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: c8ae54c0cc79451c8aa69e22800962a4
|
||||
timeCreated: 1771160087
|
||||
8
Assets/Code/GameState/UI.meta
Normal file
8
Assets/Code/GameState/UI.meta
Normal file
@@ -0,0 +1,8 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 6db6b719820b72d42912937d4b633a05
|
||||
folderAsset: yes
|
||||
DefaultImporter:
|
||||
externalObjects: {}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
56
Assets/Code/GameState/UI/AdventureView.cs
Normal file
56
Assets/Code/GameState/UI/AdventureView.cs
Normal file
@@ -0,0 +1,56 @@
|
||||
using Nox.Core;
|
||||
|
||||
namespace Nox.Game.UI {
|
||||
public class AdventureView : IMenuView {
|
||||
private readonly GameDataState gameDataState;
|
||||
private readonly GuiReferences guiReferences;
|
||||
private readonly InputSystem_Actions inputActions;
|
||||
private readonly AdventureData adventureData;
|
||||
private readonly AdventureSettings adventureSettings;
|
||||
private DayPhase dayPhase = DayPhase.Midnight;
|
||||
private int currentDay;
|
||||
|
||||
public AdventureView(
|
||||
GameDataState gameDataState,
|
||||
GuiReferences guiReferences,
|
||||
InputSystem_Actions inputActions,
|
||||
AdventureData adventureData,
|
||||
AdventureSettings adventureSettings) {
|
||||
this.gameDataState = gameDataState;
|
||||
this.guiReferences = guiReferences;
|
||||
this.inputActions = inputActions;
|
||||
this.adventureData = adventureData;
|
||||
this.adventureSettings = adventureSettings;
|
||||
}
|
||||
private void InvokePauseMenu() {
|
||||
gameDataState.ChangePlayMode(PlayMode.PauseMenu);
|
||||
}
|
||||
|
||||
public void Initialize() {
|
||||
guiReferences.pauseMenuButton.onClick.AddListener(InvokePauseMenu);
|
||||
guiReferences.dayText.text = $"Day {adventureData.currentDay}, {adventureData.currentDayPhase.ToString()}";
|
||||
guiReferences.suppliesBar.fillAmount = (float)adventureData.suppliesAvailable / adventureSettings.maxSupplies;
|
||||
guiReferences.suppliesText.text = $"{adventureData.suppliesAvailable}/{adventureSettings.maxSupplies}";
|
||||
}
|
||||
|
||||
public void Show() {
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
public void Hide() {
|
||||
throw new System.NotImplementedException();
|
||||
}
|
||||
public void Tick() {
|
||||
if(dayPhase != adventureData.currentDayPhase) {
|
||||
dayPhase = adventureData.currentDayPhase;
|
||||
guiReferences.dayText.text = $"Day {adventureData.currentDay}, {adventureData.currentDayPhase.ToString()}";
|
||||
}
|
||||
|
||||
if(currentDay != adventureData.currentDay) {
|
||||
currentDay = adventureData.currentDay;
|
||||
guiReferences.dayText.text = $"Day {adventureData.currentDay}, {adventureData.currentDayPhase.ToString()}";
|
||||
guiReferences.suppliesBar.fillAmount = (float)adventureData.suppliesAvailable / adventureSettings.maxSupplies;
|
||||
guiReferences.suppliesText.text = $"{adventureData.suppliesAvailable}/{adventureSettings.maxSupplies}";
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
3
Assets/Code/GameState/UI/AdventureView.cs.meta
Normal file
3
Assets/Code/GameState/UI/AdventureView.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: cd45a685ea554bd7ad15745f836a6de9
|
||||
timeCreated: 1772572300
|
||||
13
Assets/Code/GameState/UI/GuiReferences.cs
Normal file
13
Assets/Code/GameState/UI/GuiReferences.cs
Normal file
@@ -0,0 +1,13 @@
|
||||
using TMPro;
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Nox.Game.UI {
|
||||
public class GuiReferences : MonoBehaviour {
|
||||
public Transform portraitsContainer;
|
||||
public Image suppliesBar;
|
||||
public TextMeshProUGUI suppliesText;
|
||||
public Button pauseMenuButton;
|
||||
public TextMeshProUGUI dayText;
|
||||
}
|
||||
}
|
||||
2
Assets/Code/GameState/UI/GuiReferences.cs.meta
Normal file
2
Assets/Code/GameState/UI/GuiReferences.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 1ba89f0d55be1ed4bb61a320344f706d
|
||||
9
Assets/Code/GameState/UI/PauseMenuPrefabs.cs
Normal file
9
Assets/Code/GameState/UI/PauseMenuPrefabs.cs
Normal file
@@ -0,0 +1,9 @@
|
||||
using Nox.Game.UI;
|
||||
using UnityEngine;
|
||||
|
||||
namespace Nox.Game {
|
||||
[CreateAssetMenu(fileName = "PauseMenuPrefabs", menuName = "Nox/PauseMenuPrefabs")]
|
||||
public class PauseMenuPrefabs : ScenePrefabs {
|
||||
public PauseMenuReferences pauseMenuReferencesPrefab;
|
||||
}
|
||||
}
|
||||
3
Assets/Code/GameState/UI/PauseMenuPrefabs.cs.meta
Normal file
3
Assets/Code/GameState/UI/PauseMenuPrefabs.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: d58af5e98665467fb7d66e4822775e75
|
||||
timeCreated: 1772571122
|
||||
10
Assets/Code/GameState/UI/PauseMenuReferences.cs
Normal file
10
Assets/Code/GameState/UI/PauseMenuReferences.cs
Normal file
@@ -0,0 +1,10 @@
|
||||
using UnityEngine;
|
||||
using UnityEngine.UI;
|
||||
|
||||
namespace Nox.Game.UI {
|
||||
public class PauseMenuReferences : MonoBehaviour {
|
||||
public Button resumeButton;
|
||||
public Button exitButton;
|
||||
public Button saveButton;
|
||||
}
|
||||
}
|
||||
2
Assets/Code/GameState/UI/PauseMenuReferences.cs.meta
Normal file
2
Assets/Code/GameState/UI/PauseMenuReferences.cs.meta
Normal file
@@ -0,0 +1,2 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 8c881c3395da5194b926ef10f819edd7
|
||||
68
Assets/Code/GameState/UI/PauseMenuView.cs
Normal file
68
Assets/Code/GameState/UI/PauseMenuView.cs
Normal file
@@ -0,0 +1,68 @@
|
||||
#nullable enable
|
||||
using System;
|
||||
using Nox.Core;
|
||||
using Jovian.SaveSystem;
|
||||
using UnityEngine;
|
||||
using UnityEngine.AddressableAssets;
|
||||
using Object = UnityEngine.Object;
|
||||
using PlayMode = Nox.Core.PlayMode;
|
||||
|
||||
namespace Nox.Game.UI {
|
||||
public class PauseMenuView: IMenuView {
|
||||
private readonly GameDataState gameDataState;
|
||||
private readonly ISaveSystem saveSystem;
|
||||
private readonly Func<NoxSavedDataSet?> captureSaveData;
|
||||
private PauseMenuReferences? pauseMenu;
|
||||
private PauseMenuPrefabs? pauseMenuPrefabs;
|
||||
|
||||
public PauseMenuView(GameDataState gameDataState, ISaveSystem saveSystem, Func<NoxSavedDataSet?> captureSaveData) {
|
||||
this.gameDataState = gameDataState;
|
||||
this.saveSystem = saveSystem;
|
||||
this.captureSaveData = captureSaveData;
|
||||
pauseMenu ??= Object.FindFirstObjectByType<PauseMenuReferences>();
|
||||
if(pauseMenu) {
|
||||
Hide();
|
||||
}
|
||||
}
|
||||
|
||||
public void Initialize() {
|
||||
pauseMenuPrefabs ??= Addressables.LoadAssetAsync<PauseMenuPrefabs>("PauseMenuPrefabs").WaitForCompletion();
|
||||
if(!pauseMenu) {
|
||||
pauseMenu = Object.Instantiate(pauseMenuPrefabs.pauseMenuReferencesPrefab);
|
||||
pauseMenu?.resumeButton.onClick.AddListener(() => {gameDataState.ChangePlayMode(gameDataState.PreviousPlayMode);});
|
||||
pauseMenu?.exitButton.onClick.AddListener(() => {
|
||||
OnSaveRequested();
|
||||
gameDataState.ChangeGameState(GameState.MainMenu);
|
||||
});
|
||||
pauseMenu?.saveButton.onClick.AddListener(OnSaveRequested);
|
||||
}
|
||||
Show();
|
||||
}
|
||||
|
||||
private void OnSaveRequested() {
|
||||
if(string.IsNullOrEmpty(gameDataState.activeSessionId)) {
|
||||
Debug.LogWarning("[PauseMenuView] No active session. Cannot save.");
|
||||
return;
|
||||
}
|
||||
|
||||
var data = captureSaveData();
|
||||
if(data == null) {
|
||||
Debug.LogWarning("[PauseMenuView] No save data to capture.");
|
||||
return;
|
||||
}
|
||||
|
||||
saveSystem.Save(gameDataState.activeSessionId, data, SaveSlotType.Auto);
|
||||
}
|
||||
|
||||
public void Show() {
|
||||
pauseMenu?.gameObject.SetActive(true);
|
||||
}
|
||||
|
||||
public void Hide() {
|
||||
pauseMenu?.gameObject.SetActive(false);
|
||||
}
|
||||
|
||||
public void Tick() { }
|
||||
}
|
||||
|
||||
}
|
||||
3
Assets/Code/GameState/UI/PauseMenuView.cs.meta
Normal file
3
Assets/Code/GameState/UI/PauseMenuView.cs.meta
Normal file
@@ -0,0 +1,3 @@
|
||||
fileFormatVersion: 2
|
||||
guid: 83affb8078c54010ab278ed4be7f15ef
|
||||
timeCreated: 1772373065
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user