using System; using System.Collections; using System.Collections.Generic; using System.Linq; using System.Reflection; using System.Threading.Tasks; using Nullframes.Intrigues.Graph; using Nullframes.Intrigues.SaveSystem; using Nullframes.Intrigues.UI; using Nullframes.Intrigues.Utils; using Nullframes.Intrigues.XML; using Nullframes.Threading; #if UNITY_EDITOR using UnityEditor.SceneManagement; #endif using UnityEngine; using UnityEngine.Audio; namespace Nullframes.Intrigues { [DefaultExecutionOrder(-400)] [ExecuteInEditMode] public abstract class IM : MonoBehaviour { #region PRIVATE private IReadOnlyDictionary< string, string > ITEXT => IEDatabase.localisationTexts[ CurrentLanguage ]; private List< Clan > clans; private List< Family > families; private List< NVar > variables; private List< Culture > cultures; private List< Scheme > schemes; private List< Policy > policies; private List< Role > roles; private Actor player; #endregion #region SERIALIZEFIELD [SerializeField] private IEDatabase ieDatabase; [SerializeField] private DialogueManager dialogueManager; [SerializeField] private string currentLanguage; [SerializeField] private SerializableDictionary< string, Actor > _actors = new(); #endregion #region EVENTS /// /// Triggered based on specific conditions, passing the scheme, a string, and a boolean value. /// public static event Action< Scheme, string, bool > onTrigger; public static event Action< KeyCode, KeyType, float > keyHandler; /// /// Triggered when the language setting is changed. /// public static event Action< string > onLanguageChanged; /// /// Triggered when a signal is received. /// public static event Action< Signal > onSignalReceive; /// /// Triggered when there's a change in the player's character. /// public static event Action< Actor, Actor > onPlayerIsChanged; /// /// Triggered upon the death of a player. /// public static Action< Actor > onActorDeath; /// /// Triggered when a runtime actor is created in the game world. /// public static Action< Actor, GameObject > onRuntimeActorCreated; #endregion #region METHOD_INFO private static Dictionary< string, MethodInfo > _schemerInvokeMethods; public static IReadOnlyDictionary< string, MethodInfo > SchemerInvokeMethods => _schemerInvokeMethods; private static Dictionary< string, MethodInfo > _rulerInvokeMethods; public static IReadOnlyDictionary< string, MethodInfo > RulerInvokeMethods => _rulerInvokeMethods; private static Dictionary< string, MethodInfo > _schemerGetActorMethods; public static IReadOnlyDictionary< string, MethodInfo > SchemerGetActorMethods => _schemerGetActorMethods; private static Dictionary< string, MethodInfo > _rulerGetActorMethods; public static IReadOnlyDictionary< string, MethodInfo > RulerGetActorMethods => _rulerGetActorMethods; private static Dictionary< string, MethodInfo > _schemerDualActorMethods; public static IReadOnlyDictionary< string, MethodInfo > SchemerDualActorMethods => _schemerDualActorMethods; private static Dictionary< string, MethodInfo > _rulerDualActorMethods; public static IReadOnlyDictionary< string, MethodInfo > RulerDualActorMethods => _rulerDualActorMethods; private static Dictionary< string, MethodInfo > _schemerGetClanMethods; public static IReadOnlyDictionary< string, MethodInfo > SchemerGetClanMethods => _schemerGetClanMethods; private static Dictionary< string, MethodInfo > _rulerGetClanMethods; public static IReadOnlyDictionary< string, MethodInfo > RulerGetClanMethods => _rulerGetClanMethods; private static Dictionary< string, MethodInfo > _schemerGetFamilyMethods; public static IReadOnlyDictionary< string, MethodInfo > SchemerGetFamilyMethods => _schemerGetFamilyMethods; private static Dictionary< string, MethodInfo > _rulerGetFamilyMethods; public static IReadOnlyDictionary< string, MethodInfo > RulerGetFamilyMethods => _rulerGetFamilyMethods; public static int LOADED_ATTRIBUTES => _schemerInvokeMethods.Count + _rulerInvokeMethods.Count + _schemerGetActorMethods.Count + _rulerGetActorMethods.Count + _schemerDualActorMethods.Count + _rulerDualActorMethods.Count + _schemerGetClanMethods.Count + _rulerGetClanMethods.Count; public static bool IsSynced { get; private set; } private static Dictionary< string, MethodInfo > systemMethods; public static IReadOnlyDictionary< string, MethodInfo > SystemMethods => systemMethods; #endregion #region PROPERTIES /// /// Provides access to the transform where the IntrigueManager component is attached. /// public static Transform Transform => Instance.transform; /// /// Provides access to the GameObject where the IntrigueManager component is attached. /// public static GameObject GameObject => Instance.gameObject; /// /// Checks if the IEDatabase exists. /// public static bool DatabaseExists => instance.ieDatabase != null; /// /// Provides access to the dictionary of actors. /// public static IReadOnlyDictionary< string, Actor > ActorDictionary => Instance._actors; /// /// Provides access to the collection of actor values. /// public static IEnumerable< Actor > Actors => Instance._actors.Values; /// /// Gets the currently active language setting. /// public static string CurrentLanguage => Instance.currentLanguage; /// /// Accessor for the database. /// public static IEDatabase IEDatabase => Instance.ieDatabase; /// /// Accessor for the dialogue manager. /// public static DialogueManager DialogueManager => Instance.dialogueManager; /// /// Provides access to the collection of clans. /// public static IEnumerable< Clan > Clans => Instance.clans; /// /// Provides access to the collection of schemes. /// public static IEnumerable< Scheme > Schemes => Instance.schemes; /// /// Provides access to the collection of families. /// public static IEnumerable< Family > Families => Instance.families; /// /// Provides access to the collection of game variables. /// public static IEnumerable< NVar > Variables => Instance.variables; /// /// Provides access to the collection of cultures. /// public static IEnumerable< Culture > Cultures => Instance.cultures; /// /// Provides access to the collection of policies. /// public static IEnumerable< Policy > Policies => Instance.policies; /// /// Provides access to the collection of roles. /// public static IEnumerable< Role > Roles => Instance.roles; /// /// Gets the current player actor. /// public static Actor Player => Instance.player; #endregion #region INSTANCE private static IntrigueManager instance; public BatchMatchEngine MatchEngine { get; private set; } public static IntrigueManager Instance => instance != null ? instance : throw new ArgumentException(STATIC.MANAGER_NOT_EXISTS); public static bool Exists => instance != null; #endregion #region RESOURCES public static Sprite Approval_Icon; public static Sprite Cancel_Icon; private Dictionary< string, AudioSource > playlist; #endregion #region METHODS private static readonly List< KeyCode > keyBuffer = new(); private void Update() { #if UNITY_EDITOR if ( PrefabStageUtility.GetPrefabStage(gameObject) != null ) return; instance ??= (IntrigueManager)this; #endif if ( !Application.isPlaying ) return; OnUpdate(); #region KEY_DOWN keyBuffer.Clear(); NullUtils.GetCurrentKeysDown(keyBuffer); for ( int i = 0; i < keyBuffer.Count; i++ ) keyHandler?.Invoke(keyBuffer[ i ], KeyType.Down, 0f); #endregion #region KEY_UP keyBuffer.Clear(); NullUtils.GetCurrentKeysUp(keyBuffer); for ( int i = 0; i < keyBuffer.Count; i++ ) keyHandler?.Invoke(keyBuffer[ i ], KeyType.Up, 0f); #endregion } private void Awake() { #if UNITY_EDITOR if ( PrefabStageUtility.GetPrefabStage(gameObject) != null ) return; #endif if ( instance == null ) { instance = (IntrigueManager)this; if ( Application.isPlaying ) DontDestroyOnLoad(this); } else { if ( Application.isPlaying ) { Destroy(gameObject); } else { DestroyImmediate(gameObject); NDebug.Log(STATIC.CONTAINS_MANAGER, NLogType.Error); } return; } if ( !Application.isPlaying ) return; if ( !Exists ) { enabled = false; return; } Init(); LoadSystemMethods(); LoadAttributes(); XMLUtils.Init(IEDatabase); LoadResources(); LoadVariables(); LoadCultures(); LoadSchemes(); LoadPolicies(); LoadRoles(); LoadFamilies(); LoadClans(); LoadDialogueManager(); OnAwake(); } private void Start() { if ( !Application.isPlaying ) return; OnStart(); } private void LoadVariables() { foreach ( var variable in ieDatabase.variablePool ) { variables.Add(variable.Duplicate()); } } private void LoadCultures() { foreach ( var culture in ieDatabase.culturalProfiles ) { cultures.Add(culture.Duplicate()); } } private void LoadSchemes() { foreach ( var scheme in ieDatabase.schemeLibrary ) { schemes.Add(scheme.Duplicate()); } } private void LoadPolicies() { foreach ( var policy in ieDatabase.policyCatalog ) { policies.Add(policy.Duplicate()); } } private void LoadRoles() { foreach ( var role in ieDatabase.roleDefinitions ) { var duplicatedRole = role.Duplicate(); roles.Add(duplicatedRole); duplicatedRole.HeirFilter = HeirFilter(duplicatedRole.FilterID); } } public static HeirFilter HeirFilter(string filterNameOrId) { var filter = IEDatabase.nodeDataList.OfType< HeirFilterData >() .FirstOrDefault(n => n.FilterName == filterNameOrId || n.ID == filterNameOrId); if ( filter == null ) return null; var newFilter = new HeirFilter(); var gender = (GenderFilter)filter.Gender; switch ( gender ) { case GenderFilter.Female: newFilter.FilterAbsolute += (_, heir) => heir.Gender == Actor.IGender.Female; break; case GenderFilter.Male: newFilter.FilterAbsolute += (_, heir) => heir.Gender == Actor.IGender.Male; break; case GenderFilter.FemaleMale: newFilter.OrderFilter += (_, heir) => heir.Gender is Actor.IGender.Female; newFilter.OrderFilter += (_, heir) => heir.Gender is Actor.IGender.Male; break; case GenderFilter.MaleFemale: newFilter.OrderFilter += (_, heir) => heir.Gender is Actor.IGender.Male; newFilter.OrderFilter += (_, heir) => heir.Gender is Actor.IGender.Female; break; } var sameClan = filter.Clan; if ( sameClan == 1 ) { newFilter.FilterAbsolute += (decedent, heir) => decedent.Clan == heir.Clan; } var age = (AgeFilter)filter.Age; switch ( age ) { case AgeFilter.Oldest: newFilter.OrderByDesc += (_, heir) => heir.Age; break; case AgeFilter.Youngest: newFilter.OrderBy += (_, heir) => heir.Age; break; } foreach ( var relative in filter.Relatives.Select(relativeId => (RelativeFilter)relativeId) ) { switch ( relative ) { case RelativeFilter.Child: newFilter.Filter += (decedent, heir) => decedent.IsChild(heir); break; case RelativeFilter.Parent: newFilter.Filter += (decedent, heir) => decedent.IsParent(heir); break; case RelativeFilter.Sibling: newFilter.Filter += (decedent, heir) => decedent.IsSibling(heir); break; case RelativeFilter.Spouse: newFilter.Filter += (decedent, heir) => decedent.IsSpouse(heir); break; case RelativeFilter.Uncle: newFilter.Filter += (decedent, heir) => decedent.IsUncle(heir); break; case RelativeFilter.Aunt: newFilter.Filter += (decedent, heir) => decedent.IsAunt(heir); break; case RelativeFilter.Grandparent: newFilter.Filter += (decedent, heir) => decedent.IsGrandparent(heir); break; case RelativeFilter.Grandchild: newFilter.Filter += (decedent, heir) => decedent.IsGrandchild(heir); break; case RelativeFilter.Nephew: newFilter.Filter += (decedent, heir) => decedent.IsNephew(heir); break; case RelativeFilter.Niece: newFilter.Filter += (decedent, heir) => decedent.IsNiece(heir); break; case RelativeFilter.BrotherInLaw: newFilter.Filter += (decedent, heir) => decedent.IsBrotherInLaw(heir); break; case RelativeFilter.SisterInLaw: newFilter.Filter += (decedent, heir) => decedent.IsSisterInLaw(heir); break; } } return newFilter; } private void LoadResources() { Approval_Icon = Resources.Load< Sprite >("Nullframes/approval"); Cancel_Icon = Resources.Load< Sprite >("Nullframes/cancel"); } private void LoadFamilies() { FamilyFlow.LoadActors(); families = new List< Family >(); var varList = variables.Select(varr => varr.Duplicate()).ToList(); foreach ( var family in FamilyFlow.Families.Keys ) { // CONTINUE var policyList = Policies.Where(policy => policy.Type is PolicyType.Family or PolicyType.Generic && family.Policies.Contains(policy.ID)) .ToList(); families.Add(new Family(family.ID, family.Title, family.Story, family.Emblem, Cultures.FirstOrDefault(c => c.ID == family.CultureID), policyList, varList)); } } private void LoadClans() { ClanFlow.LoadClans(); clans = new List< Clan >(); var varList = variables.Select(varr => varr.Duplicate()).ToList(); foreach ( var clan in ClanFlow.Clans.Keys ) { var policyList = Policies.Where(policy => policy.Type is PolicyType.Clan or PolicyType.Generic && clan.Policies.Contains(policy.ID)).ToList(); clans.Add(new Clan(clan.ID, clan.Title, clan.Story, clan.Emblem, Cultures.FirstOrDefault(c => c.ID == clan.CultureID), policyList, varList)); } } public static void AddFamily(Family family) { if ( Instance.families.Contains(family) ) return; Instance.families.Add(family); } public static void AddClan(Clan clan) { if ( Instance.clans.Contains(clan) ) return; Instance.clans.Add(clan); } public static void AddRole(Role role) { if ( Instance.roles.Contains(role) ) return; Instance.roles.Add(role); } public static void AddCulture(Culture culture) { if ( Instance.cultures.Contains(culture) ) return; Instance.cultures.Add(culture); } public static void AddPolicy(Policy policy) { if ( Instance.policies.Contains(policy) ) return; Instance.policies.Add(policy); } /// /// Changes the current language based on the provided language key. /// /// Key representing the language to switch to. public static void ChangeLanguage(string languageKey) { if ( CurrentLanguage == languageKey ) return; if ( IEDatabase.localisationTexts.ContainsKey(languageKey) ) { Instance.currentLanguage = languageKey; onLanguageChanged?.Invoke(languageKey); return; } NDebug.Log(string.Format(STATIC.DEBUG_INVALID_LANGUAGE_KEY, languageKey), NLogType.Error); } private static string getText(string key) { if ( Instance.ITEXT.ContainsKey(key) ) return Variables != null ? Variables.Aggregate(Instance.ITEXT[ key ], (current, variable) => current.BracesIn(variable.name)) : Instance.ITEXT[ key ]; NDebug.Log(string.Format(STATIC.INVALID_LOCALISATION_KEY, key), NLogType.Error); return $"{{null:{key}}}"; } /// /// Retrieves the corresponding string for the given key in the current language. /// /// The key associated with the desired string. /// Returns the corresponding string in the current language. public static string GetText(string key) { #if UNITY_EDITOR if ( !Application.isPlaying ) return getText(key); #endif var text = XMLUtils.GetKey(IEDatabase, key, CurrentLanguage); if ( !string.IsNullOrEmpty(text) ) return Variables != null ? Variables.Aggregate(text, (current, variable) => current.BracesIn(variable.name)) : text; return getText(key); } private void LoadDialogueManager() { _ = dialogueManager ?? throw new ArgumentException(STATIC.DEBUG_DIALOGUE_MANAGER_NOT_FOUND); DialogueManager.name = "Dialogue Canvas"; } private void Init() { MatchEngine ??= new BatchMatchEngine(); _actors = new SerializableDictionary< string, Actor >(); clans = new List< Clan >(); families = new List< Family >(); variables = new List< NVar >(); cultures = new List< Culture >(); schemes = new List< Scheme >(); policies = new List< Policy >(); roles = new List< Role >(); playlist = new Dictionary< string, AudioSource >(); _schemerInvokeMethods = new Dictionary< string, MethodInfo >(); _rulerInvokeMethods = new Dictionary< string, MethodInfo >(); _schemerGetActorMethods = new Dictionary< string, MethodInfo >(); _rulerGetActorMethods = new Dictionary< string, MethodInfo >(); _schemerDualActorMethods = new Dictionary< string, MethodInfo >(); _rulerDualActorMethods = new Dictionary< string, MethodInfo >(); _schemerGetClanMethods = new Dictionary< string, MethodInfo >(); _rulerGetClanMethods = new Dictionary< string, MethodInfo >(); _schemerGetFamilyMethods = new Dictionary< string, MethodInfo >(); _rulerGetFamilyMethods = new Dictionary< string, MethodInfo >(); systemMethods = new Dictionary< string, MethodInfo >(); } private static void LoadAttributes() { IsSynced = false; var thread = new System.Threading.Thread(LoadAssemblies) { Name = "Nullframes_Intrigues" }; thread.Start(); NDebug.Log(STATIC.DEBUG_LOADING_METHODS); void LoadAssemblies() { var assemblies = AppDomain.CurrentDomain.GetAssemblies(); foreach ( var assembly in assemblies ) { var types = assembly .GetTypes() .Where(t => t.IsClass && t.IsSubclassOf(typeof( MonoBehaviour ))) .ToList(); foreach ( var method in types.SelectMany(t => t.GetMethods(BindingFlags.Public | BindingFlags.NonPublic | BindingFlags.Instance)) ) { if ( !method.IsDefined(typeof( IInvokeAttribute )) || method.ReturnType != typeof( IResult ) ) { HandleAttributeMethod(method); continue; } var parameters = method.GetParameters(); if ( parameters.Length == 1 && parameters[ 0 ].ParameterType == typeof( Scheme ) ) { RegisterInvokeMethod(method, _schemerInvokeMethods, SchemerInvokeMethods); } else if ( parameters.Length == 2 && parameters[ 0 ].ParameterType == typeof( Actor ) && parameters[ 1 ].ParameterType == typeof( Actor ) ) { RegisterInvokeMethod(method, _rulerInvokeMethods, RulerInvokeMethods); } } } IsSynced = true; NDebug.Log(string.Format(STATIC.DEBUG_LOADED_ATTRIBUTES, LOADED_ATTRIBUTES), true); } } private static void HandleAttributeMethod(MethodInfo method) { _ = method switch { _ when TryRegisterMethod(_schemerGetActorMethods, method, typeof(Scheme)) => true, _ when TryRegisterMethod(_rulerGetActorMethods, method, typeof(Actor), typeof(Actor)) => true, _ when TryRegisterMethod<(Actor, Actor), GetDualActorAttribute>(_schemerDualActorMethods, method, typeof(Scheme)) => true, _ when TryRegisterMethod<(Actor, Actor), GetDualActorAttribute>(_rulerDualActorMethods, method, typeof(Actor), typeof(Actor)) => true, _ when TryRegisterMethod(_schemerGetClanMethods, method, typeof(Scheme)) => true, _ when TryRegisterMethod(_rulerGetClanMethods, method, typeof(Actor), typeof(Actor)) => true, _ when TryRegisterMethod(_schemerGetFamilyMethods, method, typeof(Scheme)) => true, _ => TryRegisterMethod(_rulerGetFamilyMethods, method, typeof(Actor), typeof(Actor)) }; } private static bool TryRegisterMethod< TReturn, TAttribute >(Dictionary< string, MethodInfo > dict, MethodInfo method, params Type[ ] expectedParams) where TAttribute : Attribute { if ( !method.IsDefined(typeof( TAttribute )) && method.ReturnType != typeof( TReturn ) ) return false; var parameters = method.GetParameters(); if ( parameters.Length != expectedParams.Length || !parameters.Select((p, i) => p.ParameterType == expectedParams[ i ]).All(b => b) ) return false; var attribute = method.GetCustomAttribute(typeof(TAttribute)) as INamedAttribute; var methodName = string.IsNullOrEmpty(attribute?.Name) ? method.Name : attribute.Name; if ( !dict.TryAdd(methodName, method) ) { Debug.LogError( $"There is a method with this name(\"{methodName}\"). Please change the method name or customize the name using the attribute name parameter. [{typeof( TAttribute ).Name.Replace("Attribute", "")}(\"Name\")]\nMethod: {method.Name}"); return false; } return true; } private static void RegisterInvokeMethod(MethodInfo method, Dictionary< string, MethodInfo > targetDict, IReadOnlyDictionary< string, MethodInfo > publicDict) { var attribute = method.GetCustomAttribute(typeof( IInvokeAttribute )) as IInvokeAttribute; var name = string.IsNullOrEmpty(attribute?.Name) ? method.Name : attribute.Name; if ( publicDict.ContainsKey(name) ) { Debug.LogError( $"There is a method with this name(\"{name}\"). Please change the method name or customize the name using the attribute name parameter. [Scheme(\"Name\")]\nMethod: {method.Name}"); return; } targetDict.Add(name, method); } private void LoadSystemMethods() { var schemeType = typeof( Scheme ); foreach ( var methodName in new[ ] { "Scheme_Init", "Scheme_SetObjective" } ) { var method = schemeType.GetMethod(methodName, BindingFlags.NonPublic | BindingFlags.Public | BindingFlags.Instance); if ( method != null ) { systemMethods.Add(method.Name, method); } } } public static void AddActor(Actor actor) { if ( ActorDictionary.ContainsKey(actor.ID) ) return; Instance._actors.Add(actor.ID, actor); if ( Player == null && actor.IsPlayer ) { instance.player = actor; } } public static void DestroyActor(Actor actor) { if ( !Exists ) return; Instance._actors.Remove(actor.ID); } /// /// Sets the current player to the given actor. /// /// Actor to set as the current player. public static void SetPlayer(Actor actor) { var oldPlayer = Player; instance.player = actor; if ( oldPlayer != actor ) onPlayerIsChanged?.Invoke(oldPlayer, actor); } /// /// Invokes the onTrigger event. /// /// Related scheme. /// Name of the trigger. /// Value to pass to the trigger. public static void Trigger(Scheme scheme, string triggerName, bool value = true) { onTrigger?.Invoke(scheme, triggerName, value); } /// /// Calls a method that is defined with a [Scheme] attribute. /// /// Related scheme. /// Name of the method to invoke. public static IResult Invoke(Scheme scheme, string invokeName) { return scheme.Invoke(invokeName); } /// /// Checks if a given actor is the player. /// /// The actor to check. /// Returns true if the provided actor is the player. public static bool IsPlayer(Actor actor) => Player == actor; protected abstract void OnAwake(); protected abstract void OnStart(); protected abstract void OnUpdate(); #endregion #region VARIABLE_SYSTEM /// /// Retrieves the public variable associated with the given name or ID. /// /// Name or ID of the desired variable. /// Returns the corresponding variable. public static NVar GetVariable(string variableNameOrId) { var variable = Instance.variables.Find(v => v.name == variableNameOrId || v.id == variableNameOrId); return variable ?? new NString($"{{null:{variableNameOrId}}}"); } /// /// Retrieves the public variable associated with the given name or ID, cast to the specified type. /// /// Name or ID of the desired variable. /// Returns the corresponding variable of type T. public static T GetVariable< T >(string variableNameOrId) where T : NVar { var variable = Instance.variables.Find(v => v.name == variableNameOrId || v.id == variableNameOrId) as T; return variable ?? new NString($"{{null:{variableNameOrId}}}") as T; } /// /// Sets the value of a public variable. /// /// Name or ID of the variable to set. /// New value for the variable. public static void SetVariable(string varNameOrId, object value) { var variable = Variables.FirstOrDefault(v => v.name == varNameOrId || v.id == varNameOrId); if ( variable == null ) return; variable.value = value; } /// /// Sets the value of a public variable, returning the updated value cast to the specified type. /// /// Name or ID of the variable to set. /// New value for the variable. /// Returns the updated variable of type T. public static T SetVariable< T >(string varNameOrId, object value) where T : NVar { var variable = Variables.FirstOrDefault(v => v.name == varNameOrId || v.id == varNameOrId); if ( variable == null ) return null; variable.value = value; return variable as T; } // /// // /// Resets all global variables to their initial states. // /// public static void Load(SaveData saveData) { DialogueManager.Close(true, true); Instance._actors = new SerializableDictionary< string, Actor >(); Instance.clans = new List< Clan >(); Instance.families = new List< Family >(); Instance.variables = new List< NVar >(); Instance.cultures = new List< Culture >(); Instance.schemes = new List< Scheme >(); Instance.policies = new List< Policy >(); Instance.roles = new List< Role >(); Instance.playlist = new Dictionary< string, AudioSource >(); if ( saveData != null ) { foreach ( var variableData in saveData.gameData.variableData ) { switch ( variableData.type ) { case NType.String: instance.variables.Add(NVar.Create(variableData.id, variableData.name, variableData.stringValue, variableData.type)); break; case NType.Integer: instance.variables.Add(NVar.Create(variableData.id, variableData.name, variableData.integerValue, variableData.type)); break; case NType.Bool: instance.variables.Add(NVar.Create(variableData.id, variableData.name, variableData.boolValue, variableData.type)); break; case NType.Float: instance.variables.Add(NVar.Create(variableData.id, variableData.name, variableData.floatValue, variableData.type)); break; case NType.Object: instance.variables.Add(NVar.Create(variableData.id, variableData.name, variableData.objectValue, variableData.type)); break; case NType.Enum: instance.variables.Add(NVar.Create(variableData.id, variableData.name, variableData.enumIndex, variableData.type)); break; } } foreach ( var cultureData in saveData.gameData.cultureData ) { instance.cultures.Add(new Culture(cultureData.id, cultureData.name, cultureData.description, cultureData.icon, cultureData.femaleNameList, cultureData.maleNameList)); } foreach ( var policyData in saveData.gameData.policyData ) { instance.policies.Add(new Policy(policyData.id, policyData.name, policyData.description, policyData.type, policyData.icon)); } foreach ( var roleData in saveData.gameData.roleData ) { var role = new Role(roleData.id, roleData.name, roleData.titleForMale, roleData.titleForFemale, roleData.description, roleData.heirFilterId, roleData.capacity, roleData.inheritance, roleData.icon, roleData.priority); instance.roles.Add(role); role.HeirFilter = HeirFilter(roleData.heirFilterId); } foreach ( var familyData in saveData.gameData.familyData ) { // CONTINUE var policyList = familyData.policies .Select(policyId => Policies.FirstOrDefault(p => p.Type is PolicyType.Family or PolicyType.Generic && p.ID == policyId)) .Where(policy => policy != null).ToList(); var vars = instance.variables.Select(varr => varr.Duplicate()).ToList(); //Load Variables foreach ( var variableData in familyData.variableData ) { var variable = vars.FirstOrDefault(v => v.id == variableData.id); if ( variable == null ) continue; switch ( variable ) { case NString: variable.value = variableData.stringValue; break; case NInt: variable.value = variableData.integerValue; break; case NBool: variable.value = variableData.boolValue; break; case NFloat: variable.value = variableData.floatValue; break; case NObject: variable.value = variableData.objectValue; break; case NEnum: variable.value = variableData.enumIndex; break; } } instance.families.Add(new Family(familyData.id, familyData.name, familyData.description, familyData.icon, Cultures.FirstOrDefault(c => c.ID == familyData.cultureId), policyList, vars)); } foreach ( var clanData in saveData.gameData.clanData ) { var policyList = clanData.policies .Select(policyId => Policies.FirstOrDefault(p => p.Type is PolicyType.Clan or PolicyType.Generic && p.ID == policyId)) .Where(policy => policy != null).ToList(); var vars = instance.variables.Select(varr => varr.Duplicate()).ToList(); //Load Variables foreach ( var variableData in clanData.variableData ) { var variable = vars.FirstOrDefault(v => v.id == variableData.id); if ( variable == null ) continue; switch ( variable ) { case NString: variable.value = variableData.stringValue; break; case NInt: variable.value = variableData.integerValue; break; case NBool: variable.value = variableData.boolValue; break; case NFloat: variable.value = variableData.floatValue; break; case NObject: variable.value = variableData.objectValue; break; case NEnum: variable.value = variableData.enumIndex; break; } } instance.clans.Add(new Clan(clanData.id, clanData.name, clanData.description, clanData.icon, Cultures.FirstOrDefault(c => c.ID == clanData.cultureId), policyList, vars)); } // Load Schemes Instance.LoadSchemes(); return; } Instance.LoadVariables(); Instance.LoadCultures(); Instance.LoadSchemes(); Instance.LoadPolicies(); Instance.LoadRoles(); Instance.LoadFamilies(); Instance.LoadClans(); } #endregion #region CLAN /// /// Retrieves the clan associated with the given name or ID. /// /// Name or ID of the desired clan. /// Returns the corresponding Clan object. public static Clan GetClan(string clanNameOrID) { return Clans.FirstOrDefault(c => c.ClanName == clanNameOrID || c.ID == clanNameOrID); } /// /// Retrieves the role associated with the given name or ID. /// /// Name or ID of the desired role. /// Returns the corresponding Role object. public static Role GetRole(string roleNameOrId) { return Roles.FirstOrDefault(c => c.RoleName == roleNameOrId || c.ID == roleNameOrId); } /// /// Retrieves the policy associated with the given name or ID. /// /// Name or ID of the desired policy. /// Returns the corresponding Policy object. public static Policy GetPolicy(string policyNameOrId) { return Policies.FirstOrDefault(c => c.PolicyName == policyNameOrId || c.ID == policyNameOrId); } /// /// Retrieves the family associated with the given name or ID. /// /// Name or ID of the desired family. /// Returns the corresponding Family object. public static Family GetFamily(string familyNameOrId) { return Families.FirstOrDefault(c => c.FamilyName == familyNameOrId || c.ID == familyNameOrId); } /// /// Retrieves the culture associated with the given name or ID. /// /// Name or ID of the desired culture. /// Returns the corresponding Culture object. public static Culture GetCulture(string cultureNameOrId) { return Cultures.FirstOrDefault(c => c.CultureName == cultureNameOrId || c.ID == cultureNameOrId); } #endregion #region SCHEME /// /// Lists the schemes that a conspirator can initiate against a target. /// /// The actor initiating the scheme. /// The target actor of the scheme. /// Returns a list of compatible schemes. public static IEnumerable< Scheme > GetCompatibleSchemes(Actor conspirator, Actor target = null) => ( from scheme in Schemes where !conspirator.SchemeIsActive(scheme.SchemeName, target) where Schemes.Any(e => e.ID == scheme.ID) where Ruler.StartGraph(scheme.RuleID, conspirator, target) select scheme ).ToList(); /// /// Checks if a conspirator meets a specific rule against a target. /// /// Name or ID of the rule to check against. /// The actor initiating the scheme. /// The target actor of the scheme. /// Returns true if the conspirator satisfies the rule against the target. public static RuleResult IsCompatible(string ruleNameOrId, Actor conspirator, Actor target = null) => Ruler.StartGraph(ruleNameOrId, conspirator, target); public static Task< RuleResult > IsCompatibleAsync(string ruleNameOrId, Actor conspirator, Actor target = null) => Ruler.StartGraphAsync(ruleNameOrId, conspirator, target); #endregion #region AUDIO_SYSTEM public static AudioSource SetupAudio(string audioid = null, AudioClip clip = null, float volume = 1f, float destroyTime = 0f, bool autoPlay = false, AudioMixerGroup audioMixerGroup = null) { if ( clip == null ) return null; if ( !string.IsNullOrEmpty(audioid) ) { if ( instance.playlist.ContainsKey(audioid) ) return null; } var audioObject = new GameObject("Sound"); var audioSource = audioObject.AddComponent< AudioSource >(); audioSource.outputAudioMixerGroup = audioMixerGroup; audioSource.clip = clip; audioSource.volume = volume; audioObject.transform.SetParent(Instance.transform); if ( !string.IsNullOrEmpty(audioid) ) { instance.playlist.Add(audioid, audioSource); } if ( autoPlay ) { audioSource.Play(); } if ( destroyTime == 0f ) { NullUtils.DelayedCall(new DelayedCallParams { WaitUntil = () => audioObject == null || audioSource.isPlaying, Call = () => { NullUtils.DelayedCall(new DelayedCallParams { WaitUntil = () => audioObject == null || ( !audioSource.isPlaying && audioSource.time == 0 ), Call = () => { if ( audioSource != null ) Destroy(audioObject.gameObject); if ( !string.IsNullOrEmpty(audioid) ) instance.playlist.Remove(audioid); } }); } }); // NullUtils.DelayedCall( () => audioObject == null || audioSource.isPlaying, // () => { // NullUtils.DelayedCall(() => audioObject == null || (!audioSource.isPlaying && audioSource.time == 0), () => { // if(audioSource != null) // Destroy(audioObject.gameObject); // // if (!string.IsNullOrEmpty(audioid)) // instance.playlist.Remove(audioid); // }); // }); } else { NullUtils.DelayedCall(new DelayedCallParams { Delay = destroyTime, Call = () => { if ( audioObject != null ) Destroy(audioObject.gameObject); if ( !string.IsNullOrEmpty(audioid) ) instance.playlist.Remove(audioid); } }); // NullUtils.DelayedCall(destroyTime, // () => { // if(audioObject != null) // Destroy(audioObject.gameObject); // // if (!string.IsNullOrEmpty(audioid)) // instance.playlist.Remove(audioid); // }); } return audioSource; } public static void RemoveAudio(string audioid, bool dontDestroyAudio = false) { if ( instance.playlist.ContainsKey(audioid) ) { if ( instance.playlist[ audioid ] != null ) { if ( !dontDestroyAudio ) { Destroy(instance.playlist[ audioid ].gameObject); } } } instance.playlist.Remove(audioid); } private static Dictionary< AudioSource, Coroutine > fadeList = new(); public static AudioSource SetupAudio(AudioClip clip = null, float volume = 1f, float destroyTime = 0f, bool autoPlay = false) { return SetupAudio(null, clip, volume, destroyTime, autoPlay); } public static void AudioFade(AudioSource audioSource, float duration, float startVolume, float targetVolume, Action onComplete = null) { if ( fadeList.ContainsKey(audioSource) ) { CoroutineManager.StopRoutine(fadeList[ audioSource ]); fadeList.Remove(audioSource); } fadeList.Add(audioSource, CoroutineManager.StartRoutine(StartFade(audioSource, duration, startVolume, targetVolume, onComplete))); } private static IEnumerator StartFade(AudioSource audioSource, float duration, float startVolume, float targetVolume, Action onComplete = null) { float currentTime = 0; audioSource.volume = startVolume; while ( currentTime < duration ) { if ( audioSource == null ) break; currentTime += Time.deltaTime; audioSource.volume = Mathf.Lerp(startVolume, targetVolume, currentTime / duration); yield return null; } fadeList.Remove(audioSource); onComplete?.Invoke(); } #endregion #region SIGNAL /// /// Sends a signal, which can be caught by listeners of the onSignalReceive event. /// /// The signal to be sent. public static void Signal(Signal signal) => onSignalReceive?.Invoke(signal); #endregion #region SpriteDb public static List< Sprite > GetAssets(string category, params string[ ] tags) { List< Sprite > result = new(); if ( !IEDatabase.spriteDatabase.TryGetValue(category, out var assetDb) ) return result; foreach ( var kvp in assetDb.Sprites ) { var obj = kvp.Key; var objTags = kvp.Value; bool allMatch = tags.All(_tag => objTags.Any(tag => tag.Equals(_tag, StringComparison.OrdinalIgnoreCase))); if ( !allMatch ) continue; result.Add(obj); } return result; } public static Sprite GetAsset(string category, params string[ ] tags) { if ( !IEDatabase.spriteDatabase.TryGetValue(category, out var assetDb) ) return null; foreach ( var kvp in assetDb.Sprites ) { var obj = kvp.Key; var objTags = kvp.Value; bool allMatch = tags.All(_tag => objTags.Any(tag => tag.Equals(_tag, StringComparison.OrdinalIgnoreCase))); if ( allMatch ) return obj; } return null; } #endregion } }