private IEnumerator WaitForAIAndFollow(Character ai, Character owner) { if (!ai || !owner) { yield break; } var aiWander = ai.GetComponentInChildren <AISWander>(); if (!aiWander) { var time = 5f; var wait = new WaitForEndOfFrame(); while (!aiWander && time > 0f) { time -= Time.deltaTime; yield return(wait); aiWander = ai.GetComponentInChildren <AISWander>(); } } if (aiWander) { aiWander.FollowTransform = owner.transform; } else { SL.LogWarning("WaitForAIAndFollow timeout"); } }
// Finalize the clip (name / SLPack), and try to replace global game audio if one has the same name. internal static AudioClip FinalizeAudioClip(AudioClip clip, string name, SLPack pack = null) { clip.name = name; if (pack != null) { if (pack.AudioClips.ContainsKey(name)) { SL.LogWarning("Replacing clip '" + name + "' in pack '" + pack.Name + "'"); if (pack.AudioClips[name]) { GameObject.Destroy(pack.AudioClips[name]); } pack.AudioClips.Remove(name); } pack.AudioClips.Add(name, clip); } if (Enum.TryParse(name, out GlobalAudioManager.Sounds sound)) { ReplaceAudio(sound, clip); } return(clip); }
private static IEnumerator LoadAudioFromFileCoroutine(string filePath, SLPack pack, Action <AudioClip> onClipLoaded) { var name = Path.GetFileNameWithoutExtension(filePath); var request = UnityWebRequestMultimedia.GetAudioClip($@"file://{Path.GetFullPath(filePath)}", AudioType.WAV); yield return(request.SendWebRequest()); while (!request.isDone) { yield return(null); } if (!string.IsNullOrEmpty(request.error)) { SL.LogWarning(request.error); yield break; } SL.Log($"Loaded audio clip: {Path.GetFileName(filePath)}"); var clip = DownloadHandlerAudioClip.GetContent(request); FinalizeAudioClip(clip, name, pack); onClipLoaded?.Invoke(clip); }
// internal death callback internal void OnDeath(Character character) { if (this.DestroyOnDeath) { SLPlugin.Instance.StartCoroutine(DestroyOnDeathCoroutine(character)); return; } if (LootableOnDeath && this.DropTableUIDs != null && DropTableUIDs.Length > 0) { if (character.GetComponent <LootableOnDeath>() is LootableOnDeath lootable && (bool)At.GetField(lootable, "m_wasAlive")) { foreach (var tableUID in this.DropTableUIDs) { if (SL_DropTable.s_registeredTables.TryGetValue(tableUID, out SL_DropTable table)) { table.GenerateDrops(character.Inventory.Pouch.transform); } else { SL.LogWarning($"Trying to generate drops for '{UID}', " + $"but could not find any registered SL_DropTable with the UID '{tableUID}'"); } } character.Inventory.MakeLootable(this.DropWeapons, this.DropPouchContents, true, false); } } }
protected override void ActivateLocally(Character _affectedCharacter, object[] _infos) { if (PhotonNetwork.isNonMasterClientInRoom) { return; } if (m_charTemplate != null) { var pos = this.OwnerCharacter ? this.OwnerCharacter.transform.position : (Vector3)_infos[0]; var ai = m_charTemplate.Spawn(pos, Vector3.zero, this.GenerateRandomUIDForSpawn ? (string)UID.Generate() : this.SLCharacter_UID, this.ExtraRpcData); if (!ai) { SL.LogWarning("SpawnSLCharacter.ActivateLocally - spawn failed!"); } else if (TryFollowCaster) { StartCoroutine(WaitForAIAndFollow(ai, this.OwnerCharacter)); } } }
internal void Internal_Apply() { // add uid to CustomCharacters callback dictionary if (!string.IsNullOrEmpty(this.UID)) { if (CustomCharacters.Templates.ContainsKey(this.UID)) { SL.LogError("Trying to register an SL_Character Template, but one is already registered with this UID: " + UID); return; } CustomCharacters.Templates.Add(this.UID, this); } if (this.LootableOnDeath && this.DropTableUIDs?.Length > 0 && !this.DropPouchContents) { SL.LogWarning($"SL_Character '{UID}' has LootableOnDeath=true and DropTableUIDs set, but DropPouchContents is false!" + $"You should set DropPouchContents to true in this case or change your template behaviour. Forcing DropPouchContents to true."); this.DropPouchContents = true; } OnPrepare(); SL.Log("Prepared SL_Character '" + Name + "' (" + UID + ")."); }
public void GenerateDrops(Transform container) { if (!container) { SL.LogWarning($"Trying to generate drops from '{UID}' but target container is null!"); return; } if (this.GuaranteedDrops != null && GuaranteedDrops.Count > 0) { //SL.Log("Generating Guaranteed drops..."); foreach (var drop in this.GuaranteedDrops) { drop.GenerateDrop(container); } } if (this.RandomTables != null && RandomTables.Count > 0) { //SL.Log("Generating Random Tables..."); foreach (var table in this.RandomTables) { table.GenerateDrops(container); } } }
public override SkillSlot ApplyToRow(Transform row, int treeID) { var col = new GameObject("Col" + this.ColumnIndex); col.transform.parent = row; var comp = col.AddComponent <SkillSlot>(); comp.IsBreakthrough = Breakthrough; At.SetField(comp, "m_requiredMoney", SilverCost); At.SetField(comp as BaseSkillSlot, "m_columnIndex", ColumnIndex); var skill = ResourcesPrefabManager.Instance.GetItemPrefab(SkillID) as Skill; if (!skill) { SL.LogWarning("SL_SkillSlot: Could not find skill by id '" + SkillID + "'"); return(comp); } At.SetField(comp, "m_skill", skill); At.SetField(skill, "m_schoolIndex", treeID); //SL.LogWarning("Set " + treeID + " for " + skill.Name + "'s treeID"); if (this.RequiredSkillSlot != Vector2.zero) { SetRequiredSlot(comp); } return(comp); }
public override void ApplyToCharacter(Character trainer, bool loadingFromSave) { base.ApplyToCharacter(trainer, loadingFromSave); var trainertemplate = GameObject.Instantiate(Resources.Load <GameObject>("editor/templates/TrainerTemplate")); trainertemplate.transform.parent = trainer.transform; trainertemplate.transform.position = trainer.transform.position; // set Dialogue Actor name var trainerActor = trainertemplate.GetComponentInChildren <DialogueActor>(); trainerActor.SetName(this.Name); // get "Trainer" component, and set the SkillTreeUID to our custom tree UID var trainerComp = trainertemplate.GetComponentInChildren <Trainer>(); if (this.SkillTree != null) { At.SetField(trainerComp, "m_skillTreeUID", new UID(SkillTree.UID)); } else { SL.LogWarning("Setting up an SL_CharacterTrainer (" + this.UID + ") but no SL_SkillTree has been created for it!"); } // setup dialogue tree var graphController = trainertemplate.GetComponentInChildren <DialogueTreeController>(); var graph = graphController.graph; // the template comes with an empty ActorParameter, we can use that for our NPC actor. var actors = At.GetField(graph as DialogueTree, "_actorParameters") as List <DialogueTree.ActorParameter>; actors[0].actor = trainerActor; actors[0].name = this.Name; // setup the actual dialogue now var rootStatement = graph.allNodes[0] as StatementNodeExt; rootStatement.statement = new Statement(this.InitialDialogue); rootStatement.SetActorName(this.Name); // the template already has an action node for opening the Train menu. // Let's grab that and change the trainer to our custom Trainer component (setup above). var openTrainer = graph.allNodes[1] as ActionNode; (openTrainer.action as TrainDialogueAction).Trainer = new BBParameter <Trainer>(trainerComp); // ===== finalize nodes ===== graph.allNodes.Clear(); // add the nodes we want to use graph.allNodes.Add(rootStatement); graph.allNodes.Add(openTrainer); graph.primeNode = rootStatement; graph.ConnectNodes(rootStatement, openTrainer); // set the trainer active trainer.gameObject.SetActive(true); }
protected override void AwakeInit() { CustomCharacters.Templates.TryGetValue(this.SLCharacter_UID, out m_charTemplate); if (m_charTemplate == null) { SL.LogWarning("SpawnSLCharacter.Awake - m_charTemplate is null, could not find from UID '" + this.SLCharacter_UID + "'"); } }
public void ApplyToCharacter(Character character) { if (!CustomCharacters.Templates.TryGetValue(this.TemplateUID, out SL_Character template)) { SL.LogWarning($"Trying to apply an SL_CharacterSaveData to a Character, but could not get any template with the UID '{this.TemplateUID}'"); return; } SLPlugin.Instance.StartCoroutine(ApplyCoroutine(character, template)); }
// Normal template apply method internal void Internal_ApplyTemplate() { if (!StatusEffectFamilyLibrary.Instance) { return; } var library = StatusEffectFamilyLibrary.Instance; StatusEffectFamily family; if (library.StatusEffectFamilies.Where(it => (string)it.UID == this.UID).Any()) { family = library.StatusEffectFamilies.First(it => (string)it.UID == this.UID); } else { family = new StatusEffectFamily(); library.StatusEffectFamilies.Add(family); } if (family == null) { SL.LogWarning("Applying SL_StatusEffectFamily template, null error"); return; } if (this.UID != null) { At.SetField(family, "m_uid", new UID(this.UID)); } if (this.Name != null) { family.Name = this.Name; } if (this.StackBehaviour != null) { family.StackBehavior = (StatusEffectFamily.StackBehaviors) this.StackBehaviour; } if (this.MaxStackCount != null) { family.MaxStackCount = (int)this.MaxStackCount; } if (this.LengthType != null) { family.LengthType = (StatusEffectFamily.LengthTypes) this.LengthType; } }
private IEnumerator DelayedStopCoroutine() { yield return(new WaitForSeconds(AutoStopTime)); if (At.GetField(this as PlayVFX, "m_startVFX") is VFXSystem vfx) { vfx.Stop(); } else { SL.LogWarning("SL_PlayTimedVFX.DelayedStopCoroutine - vfx was null after delay"); } }
// invoked when character is being saved internal string INTERNAL_OnPrepareSave(Character character) { string ret = null; try { ret = OnCharacterBeingSaved?.Invoke(character); } catch (Exception e) { SL.LogWarning("Exception invoking OnCharacterBeingSaved for template '" + this.UID + "'"); SL.LogInnerException(e); } return(ret); }
internal void Internal_ApplyTemplate() { if (string.IsNullOrEmpty(this.UID)) { SL.LogWarning("Cannot prepare an SL_DropTable with a null or empty UID!"); return; } if (s_registeredTables.ContainsKey(this.UID)) { SL.LogWarning("Trying to register an SL_DropTable but one already exists with this UID: " + this.UID); return; } s_registeredTables.Add(this.UID, this); SL.Log("Registered SL_DropTable '" + this.UID + "'"); }
/// <summary>Use this to check if a key is held this frame, and get the local ID of the player who pressed it.</summary> /// <param name="keyName">The name of the key which you registered with.</param> /// <param name="playerID">If the key is pressed, this is the local split-player ID that pressed it.</param> /// <returns>True if pressed, false if not.</returns> public static bool GetKey(string keyName, out int playerID) { if (s_customKeyDict.TryGetValue(keyName, out KeybindInfo key)) { return(key.GetKey(out playerID)); } if (!s_loggedMissingKeyNames.Contains(keyName)) { SL.LogWarning($"Attempting to get custom keybinding state, but no custom keybinding " + $"with the name '{keyName}' was registered, this will not be logged again."); s_loggedMissingKeyNames.Add(keyName); } playerID = -1; return(false); }
internal bool GetShouldSpawn() { if (this.ShouldSpawn != null) { try { return(ShouldSpawn.Invoke()); } catch (Exception ex) { SL.LogWarning("Exception invoking ShouldSpawn callback for " + this.Name + " (" + this.UID + "), not spawning."); SL.LogInnerException(ex); return(false); } } return(true); }
public override void ApplyActualTemplate() { base.ApplyActualTemplate(); if (string.IsNullOrEmpty(this.IdentifierName)) { SL.LogWarning("Cannot register an SL_ItemSpawn without an InternalName set!"); return; } if (s_registeredSpawnSources.ContainsKey(this.IdentifierName)) { SL.LogWarning($"An SL_ItemSpawn with the UID '{IdentifierName}' has already been registered!"); return; } s_registeredSpawnSources.Add(this.IdentifierName, this); }
/// <summary>Use this to add a new Keybinding to the game.</summary> /// <param name="name">The name for the keybinding displayed in the menu.</param> /// <param name="category">The category to add to</param> /// <param name="controlType">What type of control this is</param> /// <param name="type">What type(s) of input it will accept</param> public static void AddAction(string name, KeybindingsCategory category, ControlType controlType = ControlType.Keyboard, InputType type = InputType.Button) { bool initialized = (bool)At.GetPropertyStatic(typeof(ReInput), "initialized"); if (initialized) { SL.LogWarning("Tried to add Custom Keybinding too late. Add your keybinding earlier, such as in your BaseUnityPlugin.Awake() method."); return; } if (s_customKeyDict.ContainsKey(name)) { SL.LogWarning($"Attempting to add a keybind '{name}', but one with this name has already been registered."); return; } var customKey = new KeybindInfo(name, category, controlType, type); s_customKeyDict.Add(name, customKey); }
/// <summary>Replace a global sound with the provided AudioClip.</summary> public static void ReplaceAudio(GlobalAudioManager.Sounds sound, AudioClip clip) { if (!GAMInstance) { SL.LogWarning("Cannot find GlobalAudioManager Instance!"); return; } if (ReplacedClips.Contains(sound)) { SL.Log($"The Sound clip '{sound}' has already been replaced, replacing again..."); } try { DoReplaceClip(sound, clip); } catch (Exception e) { SL.LogError($"Exception replacing clip '{sound}'.\r\nMessage: {e.Message}\r\nStack: {e.StackTrace}"); } }
public static object LoadFromXml(Stream stream, Type baseType) { try { var xml = GetXmlSerializer(baseType); object obj = null; using (var reader = new StreamReader(stream)) { obj = xml.Deserialize(reader); } return(obj); } catch (Exception ex) { SL.LogWarning("Exception loading XML stream!"); SL.LogInnerException(ex); return(null); } }
/// <summary> /// Load an Audio Clip from a given byte array, and optionally put it in the provided SL Pack.<br/><br/> /// WARNING: AudioClips loaded from byte arrays are currently unreliable and may be glitched, use at own risk! /// </summary> /// <param name="data">The byte[] array from <see cref="File.ReadAllBytes(string)"/> on the wav file path.</param> /// <param name="name">The name to give to the audio clip.</param> /// <param name="pack">Optional SL Pack to put the audio clip inside.</param> /// <returns>The loaded audio clip, if successful.</returns> public static AudioClip LoadAudioClip(byte[] data, string name, SLPack pack = null) { SL.LogWarning("WARNING: AudioClips loaded from embedded .zip archives are currently unreliable and may be glitched, use at own risk!"); try { var clip = ConvertByteArrayToAudioClip(data, name); if (!clip) { return(null); } return(FinalizeAudioClip(clip, name, pack)); } catch (Exception ex) { SL.LogWarning("Exception loading AudioClip!"); SL.LogInnerException(ex); return(null); } }
public void GenerateItems(Transform container) { if (this.DropTableUIDsToAdd == null) { SL.LogWarning($"Trying to generate drops from an SL_DropTableAddition '{IdentifierName}', but the DropTableUIDsToAdd is null!"); return; } foreach (string tableUID in this.DropTableUIDsToAdd) { SL_DropTable.s_registeredTables.TryGetValue(tableUID, out SL_DropTable table); if (table == null) { SL.LogWarning($"SL_DropTableAddition: Could not find any SL_DropTable with UID '{tableUID}'!"); continue; } //SL.Log("Generating from '" + table.UID + "'"); table.GenerateDrops(container); } }
public static Type GetBaseTypeOfXmlDocument(Stream stream) { string typeName = ""; using (var reader = XmlReader.Create(stream)) { while (reader.Read()) // just get the first element (root) then break. { if (reader.NodeType == XmlNodeType.Element) { // the real type might be saved as an attribute if (!string.IsNullOrEmpty(reader.GetAttribute("type"))) { typeName = reader.GetAttribute("type"); } else { typeName = reader.Name; } break; } } } s_typesByName.TryGetValue(typeName, out Type type); if (type == null) { type = SLTypes.FirstOrDefault(it => it.Name == typeName); if (type == null) { SL.LogWarning("Could not get Type from document with base node '" + typeName + "'!"); return(null); } } return(type); }
/// <summary> /// Save an SL_Type object to xml. /// </summary> public static void SaveToXml(string dir, string saveName, object obj) { if (!string.IsNullOrEmpty(dir)) { if (!Directory.Exists(dir)) { Directory.CreateDirectory(dir); } dir += "/"; } saveName = ReplaceInvalidChars(saveName); string path = dir + saveName + ".xml"; if (File.Exists(path)) { //SL.LogWarning("SaveToXml: A file already exists at " + path + "! Deleting..."); File.Delete(path); } var xml = GetXmlSerializer(obj.GetType()); using (var file = File.OpenWrite(path)) { try { xml.Serialize(file, obj); } catch (Exception ex) { SL.LogWarning("Exception saving object to XML!"); SL.LogInnerException(ex); } } }
internal void GenerateItem() { if (PhotonNetwork.isNonMasterClientInRoom) { return; } if (s_activeSavableSpawns.Any(it => it.SpawnIdentifier == this.IdentifierName)) { SL.LogWarning("Trying to spawn two SL_ItemSpawns with the same Identifier: " + this.IdentifierName); return; } var prefab = ResourcesPrefabManager.Instance.GetItemPrefab(this.ItemID); if (!prefab) { SL.LogWarning($"SL_ItemSpawn: Could not find any item by ID '{ItemID}'!"); return; } SL.Log($"SL_ItemSpawn '{this.IdentifierName}' spawning..."); var item = ItemManager.Instance.GenerateItemNetwork(this.ItemID); if (!ForceNonPickable) { s_activeSavableSpawns.Add(new ItemSpawnInfo { SpawnIdentifier = this.IdentifierName, ItemID = this.ItemID, ItemUID = item.UID }); } ApplyToItem(item); }
internal static void Internal_SetField(Type type, string fieldName, object instance, object value) { if (type == null) { return; } var fi = GetFieldInfo(type, fieldName); if (fi == null) { SL.LogWarning($"Could not find FieldInfo for Type '{type?.FullName ?? "<null>"}', field '{fieldName}'!"); return; } if (fi.IsStatic) { fi.SetValue(null, value); } else { fi.SetValue(instance, value); } }
private static void DoReplaceClip(GlobalAudioManager.Sounds _sound, AudioClip _newClip) { if (!_newClip) { SL.LogWarning($"The replacement clip for '{_sound}' is null"); return; } //var path = GAMInstance.GetPrefabPath(_sound); var path = (string)At.Invoke(GAMInstance, "GetPrefabPath", _sound); var resource = Resources.Load <GameObject>("_Sounds/" + path); var component = resource.GetComponent <AudioSource>(); component.clip = _newClip; resource.hideFlags |= HideFlags.DontUnloadUnusedAsset; if (!ReplacedClips.Contains(_sound)) { ReplacedClips.Add(_sound); } SL.Log("Replaced " + _sound + " AudioSource with custom clip!"); }
internal IEnumerator ApplyCoroutine(Character character, SL_Character template) { yield return(new WaitForSeconds(0.5f)); if (this.Silver > 0) { character.Inventory.AddMoney(this.Silver); } if (!string.IsNullOrEmpty(FollowTargetUID)) { var followTarget = CharacterManager.Instance.GetCharacter(FollowTargetUID); var aisWander = character.GetComponentInChildren <AISWander>(); if (followTarget && aisWander) { aisWander.FollowTransform = followTarget.transform; } else { SL.LogWarning("Failed setting follow target!"); } } if (WasDead) { At.SetField(character, "m_loadedDead", true); if (character.GetComponentInChildren <LootableOnDeath>() is LootableOnDeath loot) { At.SetField(loot, "m_wasAlive", false); } } if (ItemSaves != null && ItemSaves.Count > 0) { foreach (var itemSave in ItemSaves) { switch (itemSave.Type) { case CharItemSaveData.EquipSaveType.Pouch: var item = ItemManager.Instance.GenerateItemNetwork(itemSave.ItemID); if (item) { item.ChangeParent(character.Inventory.Pouch.transform); item.RemainingAmount = itemSave.Quantity; } break; case CharItemSaveData.EquipSaveType.Equipped: SL_Character.TryEquipItem(character, itemSave.ItemID); break; case CharItemSaveData.EquipSaveType.Backpack: item = ItemManager.Instance.GenerateItemNetwork(itemSave.ItemID); if (item && character.Inventory.EquippedBag) { item.ChangeParent(character.Inventory.EquippedBag.Container.transform); item.RemainingAmount = itemSave.Quantity; } break; } } } if (character.GetComponent <CharacterStats>() is CharacterStats stats) { stats.SetHealth(this.Health); } if (this.StatusData != null) { var statusMgr = character.GetComponentInChildren <StatusEffectManager>(true); if (statusMgr) { foreach (var statusData in this.StatusData) { var data = statusData.Split('|'); var status = ResourcesPrefabManager.Instance.GetStatusEffectPrefab(data[0]); if (!status) { continue; } var dealer = CharacterManager.Instance.GetCharacter(data[1]); var effect = statusMgr.AddStatusEffect(status, dealer); var remaining = float.Parse(data[2]); At.SetField(effect, "m_remainingTime", remaining); if (effect.StatusData != null) { At.SetField(effect.StatusData, "m_remainingLifespan", remaining); } } } } template.INTERNAL_OnSaveApplied(character, this.ExtraRPCData, this.ExtraSaveData); }
// ========== parsing from CustomSpawnInfo =========== internal static SL_CharacterSaveData FromSpawnInfo(CustomSpawnInfo info) { // should probably debug this if it happens if (info.Template == null || !info.ActiveCharacter) { SL.LogWarning("Trying to save a CustomSpawnInfo, but template or activeCharacter is null!"); return(null); } var character = info.ActiveCharacter; var template = info.Template; // capture the save data in an instance var data = new SL_CharacterSaveData() { SaveType = template.SaveType, TemplateUID = template.UID, ExtraSaveData = template.INTERNAL_OnPrepareSave(character), ExtraRPCData = info.ExtraRPCData, CharacterUID = character.UID, WasDead = character.IsDead, Forward = character.transform.forward, Position = character.transform.position, Silver = character.Inventory.ContainedSilver, }; if (character.Inventory) { data.SetSavedItems(character); } if (character.GetComponentInChildren <AISWander>() is AISWander aiWander) { if (aiWander.FollowTransform && aiWander.FollowTransform.GetComponent <Character>() is Character followTarget) { data.FollowTargetUID = followTarget.UID.ToString(); } } try { data.Health = character.Health; if (character.StatusEffectMngr) { var statuses = character.StatusEffectMngr.Statuses.ToArray().Where(it => !string.IsNullOrEmpty(it.IdentifierName)); data.StatusData = new string[statuses.Count()]; int i = 0; foreach (var status in statuses) { var sourceChar = (UID)At.GetField(status, "m_sourceCharacterUID")?.ToString(); data.StatusData[i] = $"{status.IdentifierName}|{sourceChar}|{status.RemainingLifespan}"; i++; } } } catch { } return(data); }