Newer
Older
Marco Zimmer
committed
using System.Collections;
using System.Collections.Generic;
using System.Linq;
Marco Zimmer
committed
using UnityEngine;
using static CommunicationEvents;
using System.IO;
Marco Zimmer
committed
/// <summary>
/// Keeps track of all available and current <see cref="Stage"/>
/// </summary>
Marco Zimmer
committed
public static class StageStatic
{
/// <summary>
/// - <c>Key</c>: stage name
/// - <c>Value</c>: stages created by KWARC
/// </summary>
public static IReadOnlyDictionary<string, Stage> StageOfficial => StageChapters[0];
/// <summary>
/// - <c>Key</c>: stage name
/// - <c>Value</c>: stages created by local user
/// </summary>
public static IReadOnlyDictionary<string, Stage> StageLocal => StageChapters[1];
private static List<Dictionary<string, Stage>> StageChapters = new() { new(), new() };
/// <summary>
/// Used to map <see cref="StageOfficial"/> <see cref="Stage.category">categories</see> into a ordered list for the StageMenue.
/// </summary>
Marco Zimmer
committed
public static Dictionary<string, int> Category = new Dictionary<string, int> {
{ "", -1 },
{ "Demo Category", 0 },
};
Marco Zimmer
committed
/// <summary>
/// <see cref="Stage.name"/> of current <see cref="stage"/> or one to be loaded.
/// <seealso cref="LoadInitStage(bool, GameObject)"/>
/// </summary>
Marco Zimmer
committed
public static string current_name;
/// <summary>
/// !<see cref="Stage.use_install_folder"/> of current <see cref="stage"/> or one to be loaded.
/// <seealso cref="LoadInitStage(bool, GameObject)"/>
/// </summary>
/// <summary>
/// Current <see cref="Mode"/>
/// </summary>
Marco Zimmer
committed
public static Mode mode;
/// <summary>
/// Loadable world scenes
/// </summary>
public static readonly List<string> Worlds = GenerateWorldList();
/// <summary>
/// Current <see cref="Stage"/>
/// </summary>
public static Stage stage
{
get
{
return (local_stage ? StageLocal : StageOfficial)?[current_name];
Marco Zimmer
committed
}
Marco Zimmer
committed
current_name = value.name;
local_stage = !value.use_install_folder;
StageChapters[local_stage ? 1 : 0].Remove(current_name);
StageChapters[local_stage ? 1 : 0].Add(current_name, value);
Marco Zimmer
committed
/// \copydoc PlayerRecord.seconds
public static double stage_time
=> ContainsKey(current_name)
? stage.player_record.seconds + Time.timeSinceLevelLoadAsDouble
: double.NaN;
/// <summary>
/// TODO: set when encountering an error
/// </summary>
public static StageErrorStruct last_error
{
Marco Zimmer
committed
get;
private set;
}
/// <summary>
/// Extracts all loadable scenes for <see cref="Worlds"/>.
/// </summary>
/// <returns><see cref="Worlds"/></returns>
private static List<string> GenerateWorldList()
{
string world = "World";
string ending = ".unity";
foreach (UnityEditor.EditorBuildSettingsScene scene in UnityEditor.EditorBuildSettings.scenes)
{
if (scene.enabled)
{
string name = new System.IO.FileInfo(scene.path).Name;
name = name.Substring(0, name.Length - ending.Length);
if (0 == string.Compare(name, name.Length - world.Length, world, 0, world.Length))
{
_Worlds.Add(name);
}
}
Marco Zimmer
committed
}
List<string> _Worlds = new List<string> {"TreeWorld", "RiverWorld"};
Debug.Log("WorldList might be incomplete or incorrect!");
#endif
Marco Zimmer
committed
}
/// <summary>
/// Available Modes a <see cref="Stage"/> to be selected and/ or loaded in.
/// </summary>
Marco Zimmer
committed
public enum Mode
{
Play,
Create,
}
/// <summary>
/// Created when an error (may) occures while a <see cref="Stage"/> is being created, because of incompatible variables.
/// </summary>
Marco Zimmer
committed
public struct StageErrorStruct
{
/// <summary> set iff <see cref="Stage.category"/> is incompatible </summary>
public bool category { get { return state[0]; } set { state[0] = value; } }
/// <summary> set iff <see cref="Stage.number"/> is incompatible </summary>
public bool id { get { return state[1]; } set { state[1] = value; } }
/// <summary> set iff <see cref="Stage.name"/> is incompatible </summary>
public bool name { get { return state[2]; } set { state[2] = value; } }
/// <summary> set iff <see cref="Stage.description"/> is incompatible </summary>
Marco Zimmer
committed
public bool description { get { return state[3]; } set { state[3] = value; } }
/// <summary> set iff <see cref="Stage.scene"/> is incompatible </summary>
public bool scene { get { return state[4]; } set { state[4] = value; } }
/// <summary> set iff !<see cref="Stage.use_install_folder"/> is incompatible </summary>
public bool local { get { return state[5]; } set { state[5] = value; } }
/// <summary> set iff <see cref="Stage.path"/> was not found </summary>
public bool load { get { return state[6]; } set { state[6] = value; } }
Marco Zimmer
committed
/// <summary>
/// stores all boolish members, to iterate over
/// </summary>
Marco Zimmer
committed
private bool[] state;
/// <summary>
/// <see langword="true"/> iff no error occures.
/// </summary>
Marco Zimmer
committed
public bool pass
{
get { return state.Aggregate(true, (last, next) => last && !next); }
}
public readonly static StageErrorStruct
InvalidFolder = new StageErrorStruct(false, false, false, false, false, true, false),
NotLoadable = new StageErrorStruct(false, false, false, false, false, false, true);
Marco Zimmer
committed
/// <summary>
/// Initiator <br/>
/// canonical
/// </summary>
Marco Zimmer
committed
public StageErrorStruct(bool category, bool id, bool name, bool description, bool scene, bool local, bool load)
{
state = new bool[7];
this.category = category;
this.id = id;
this.name = name;
this.description = description;
this.scene = scene;
this.local = local;
this.load = load;
}
}
/// <summary>
/// sets <see cref="mode"/> and en-/ disables children of <paramref name="gameObject"/> with certain Tags, only available in certain <see cref="Mode">Modes</see> (e.g. in Def_Stage)
/// </summary>
/// <param name="mode"><see cref="Mode"/> to set</param>
/// <param name="gameObject"> which children will be checked</param>
Marco Zimmer
committed
public static void SetMode(Mode mode, GameObject gameObject = null)
{
Jacktheholdy
committed
//gameObject ??= new GameObject();
StageStatic.mode = mode;
Marco Zimmer
committed
// handle StageStatic.mode
switch (mode)
Jacktheholdy
committed
{
case Mode.Play:
gameObject.SetActiveByTagRecursive("CreatorMode", false);
break;
case Mode.Create:
gameObject.SetActiveByTagRecursive("CreatorMode", true);
break;
}
Marco Zimmer
committed
}
// handle stage mode
switch (mode)
{
case Mode.Play:
case Mode.Create:
if (ContainsKey(current_name, local_stage))
stage.SetMode(mode == Mode.Create);
Marco Zimmer
committed
break;
}
}
Marco Zimmer
committed
public static StageErrorStruct Validate(string category, int id, string name, string description, string scene, bool local = true)
Marco Zimmer
committed
return new StageErrorStruct(
category.Length == 0,
ContainsNumber(category, id, true),
name.Length == 0 || ContainsKey(name, false) || ContainsKey(name, true),
Marco Zimmer
committed
false,
!Worlds.Contains(scene),
local == false,
false
);
Marco Zimmer
committed
}
public static StageErrorStruct LoadNewStage(string category, int id, string name, string description, string scene, bool local = true)
Marco Zimmer
committed
{
StageErrorStruct ret = Validate(category, id, name, description, scene, local);
Marco Zimmer
committed
if (!ret.pass)
Marco Zimmer
committed
stage = new Stage(category, id, name, description, scene, local);
Marco Zimmer
committed
stage.store(force_stage_file: true);
Marco Zimmer
committed
Marco Zimmer
committed
/// <summary>
/// Load current <see cref="stage"/> in <see cref="Mode.Create"/>
/// </summary>
Marco Zimmer
committed
}
/// <summary>
/// Finds first unused <see cref="Stage.number"/> in a certain <paramref name="category"/>.
/// </summary>
/// <param name="local">which kind of stage we are looking at</param>
/// <param name="category">the category in question</param>
/// <returns>first unused <see cref="Stage.number"/> in a certain <paramref name="category"/></returns>
public static int NextNumber(bool local, string category)
Marco Zimmer
committed
{
var numbers = (local ? StageLocal : StageOfficial).Values.Where(s => s.category == category).Select(s => s.number).ToList();
Marco Zimmer
committed
if (0 == numbers.Count)
return 1;
numbers.Sort();
int last = numbers[0];
foreach (int i in numbers)
{
if (i > last && i != last + 1)
return last + 1;
last = i;
}
return last + 1;
}
/// <summary>
/// Looks wether an <see cref="Stage.number"/> <paramref name="i"/> exists within a certain <see cref="Stage.category"/> <paramref name="category"/> in local saves (<paramref name="local"/> == <see langword="true"/>) or install path.
/// </summary>
/// <param name="category">to look in</param>
/// <param name="i">to look for</param>
/// <param name="local">where to look</param>
/// <returns></returns>
Marco Zimmer
committed
public static bool ContainsNumber(string category, int i, bool local)
Marco Zimmer
committed
{
Marco Zimmer
committed
return (local ? StageLocal : StageOfficial).Values
.Where(s => s.category == category)
.Select(s => s.number)
.Contains(i);
Marco Zimmer
committed
}
private static int MaxTriesOfShallowLoadStages = 2;
private static int TriesOfShallowLoadStages = 0;
Marco Zimmer
committed
/// Looks for and initial loads (see <see cref="Stage.ShallowLoad(out Stage, string)"/>) <see cref="Stage">Stages</see> in <see cref="local_stage"/> and !<see cref="local_stage"/>.
public static void ShallowLoadStages(bool force = false)
Marco Zimmer
committed
{
if (MaxTriesOfShallowLoadStages <= TriesOfShallowLoadStages++
&& !force)
return;
List<FileInfo>[] crawler = new List<FileInfo>[StageChapters.Count];
_ = new Stage(); // Init static members
var new_hierarchie = IJSONsavable<Stage>.Instance._IJGetHierarchie(null);
string
path = CreatePathToFile(out _, "", "", new_hierarchie, true);
crawler[0] = new DirectoryInfo(path).GetFiles().ToList();
path = CreatePathToFile(out _, "", "", new_hierarchie, false);
crawler[1] = new DirectoryInfo(path).GetFiles().ToList();
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
for (int i = 0; i < StageChapters.Count; i++)
foreach (FileInfo file in crawler[i])
{
if (file.Extension != ".JSON"
|| StageChapters[i].ContainsKey(Path.GetFileNameWithoutExtension(file.Name)))
continue;
try
{
if (Stage.ShallowLoad(out Stage tmp, file.FullName))
StageChapters[i].Add(tmp.name, tmp);
}
catch (Exception ex)
{
Debug.LogError("Could not load StageFile: " + file.FullName);
Debug.LogException(ex);
}
}
if (!force && GlobalBehaviour.InitiateContext.MoveNext()) // active wait at start instead?
GlobalBehaviour.Instance.StartCoroutine(_WaitForContext());
return;
static IEnumerator _WaitForContext()
{
yield return GlobalBehaviour.InitiateContext;
ShallowLoadStages(true);
}
Marco Zimmer
committed
}
/// <summary>
/// Sets parameters, defining what to load in <see cref="LoadInitStage(bool, GameObject)"/> and <see cref="LoadInitStage(string, bool, bool, GameObject)"/>.
/// </summary>
/// <param name="name">sets <see cref="current_name"/></param>
/// <param name="local">sets <see cref="local_stage"/></param>
public static void SetStage(string name, bool local)
Marco Zimmer
committed
{
local_stage = local;
current_name = name;
}
/// <summary>
/// Returns a <see cref="Stage"/> or throws <see cref="Exception"/> if not found.
/// </summary>
/// <param name="name"><see cref="Stage.name"/></param>
/// <param name="local">where to look</param>
/// <returns><c>(local ? StageLocal : StageOfficial)[name];</c></returns>
public static Stage GetStage(string name, bool local)
{
return (local ? StageLocal : StageOfficial)[name];
}
/// <summary>
/// Deletes a <see cref="Stage"/> and all its associated files (including save games).
Marco Zimmer
committed
/// <seealso cref="Stage.delete()"/>
/// </summary>
/// <param name="stage">to be deleted</param>
public static void Delete(Stage stage)
{
Marco Zimmer
committed
GetStage(stage.name, !stage.use_install_folder).delete();
StageChapters[stage.use_install_folder ? 0 : 1].Remove(stage.name);
/// <summary>
/// Wrapps <see cref="LoadInitStage(bool, GameObject)"/> with extra parameters.
/// Loads and initiates <see cref="Stage"/> defined by <paramref name="name"/> and <paramref name="local"/>.
/// </summary>
/// <param name="name">sets <see cref="current_name"/> iff succeedes</param>
/// <param name="local">sets <see cref="current_name"/> iff succeedes</param>
/// <param name="restore_session">wether to restore last (loaded) player session (<see langword="true"/>) or start from scratch (<see langword="false"/>).</param>
/// <param name="gameObject">(e.g. UI/ Def_Stage) toggles recursively children with tag "DevelopingMode" to <see cref="mode"/> == <see cref="Mode.Create"/>.</param>
/// <returns><see langword="false"/> iff <see cref="Stage"/> defined by <paramref name="name"/> and <paramref name="local"/> could not be *found* or *loaded*.</returns>
Marco Zimmer
committed
public static bool LoadInitStage(string name, bool local = false, bool restore_session = true, GameObject gameObject = null)
{
bool old_l = local_stage;
string old_n = current_name;
SetStage(name, local);
if (!LoadInitStage(restore_session, gameObject))
{
local_stage = old_l;
current_name = old_n;
return false;
}
return true;
}
/// <summary>
/// Loads and initiates <see cref="Stage"/> defined by <see cref="current_name"/> and <see cref="local_stage"/>.
/// </summary>
/// <param name="restore_session">wether to restore last (loaded) player session (<see langword="true"/>) or start from scratch (<see langword="false"/>).</param>
/// <param name="gameObject">(e.g. UI/ Def_Stage) toggles recursively children with tag "DevelopingMode" to <see cref="mode"/> == <see cref="Mode.Create"/>.</param>
/// <returns><see langword="false"/> iff <see cref="Stage"/> defined by <see cref="current_name"/> and <see cref="local_stage"/> could not be *found* or *loaded*.</returns>
public static bool LoadInitStage(bool restore_session, GameObject gameObject = null)
Marco Zimmer
committed
{
MaZiFAU
committed
if (!ContainsKey(current_name, local_stage))
Marco Zimmer
committed
return false;
MaZiFAU
committed
stage.LoadSavegames();
Marco Zimmer
committed
if (restore_session)
{
stage.factState.invoke = true;
stage.factState.Draw();
}
else
stage.ResetPlay();
Marco Zimmer
committed
//if(mode == Mode.Create) // block saving "player" progress
// stage.player_record.seconds = -1;
Marco Zimmer
committed
gameObject.SetActiveByTagRecursive("DevelopingMode", mode == Mode.Create);
Marco Zimmer
committed
SetMode(mode);
Marco Zimmer
committed
return true;
}
/// <summary>
/// Wrapps <see cref="ContainsKey(string, bool)"/>; defaulting local to <see cref="local_stage"/>
/// </summary>
Marco Zimmer
committed
public static bool ContainsKey(string key)
{
return ContainsKey(key, local_stage);
}
/// <summary>
/// Looks for a <see cref="Stage"/> <paramref name="key"/> in <see cref="StageLocal"/> (<paramref name="local"/>==<see langword="true"/>) or <see cref="StageOfficial"/> (<paramref name="local"/>==<see langword="false"/>).
/// </summary>
/// <returns><c>(local ? StageLocal : StageOfficial).ContainsKey(key)</c></returns>
public static bool ContainsKey(string key, bool local)
Marco Zimmer
committed
{
Marco Zimmer
committed
return (local ? StageLocal : StageOfficial)?.ContainsKey(key) ?? false;
Marco Zimmer
committed
}