/// <summary> /// Performs a delayed init on the sent list of sosigs. If a sosig fails to init, any character using that sosig will be removed /// </summary> /// <param name="sosigs"></param> private static void InitSosigs(List <SosigTemplate> sosigs) { for (int i = 0; i < sosigs.Count; i++) { SosigTemplate sosig = sosigs[i]; try { sosig.DelayedInit(); } catch (Exception e) { TNHTweakerLogger.LogError("TNHTweaker -- Failed to load sosig: " + sosig.DisplayName + ". Error Output:\n" + e.ToString()); //Find any characters that use this sosig, and remove them for (int j = 0; j < LoadedTemplateManager.LoadedCharactersDict.Values.Count; j++) { //This is probably monsterously inefficient, but if you're at this point you're already f****d :) KeyValuePair <TNH_CharacterDef, CustomCharacter> value_pair = LoadedTemplateManager.LoadedCharactersDict.ToList()[j]; if (value_pair.Value.CharacterUsesSosig(sosig.SosigEnemyID)) { TNHTweakerLogger.LogError("TNHTweaker -- Removing character that used removed sosig: " + value_pair.Value.DisplayName); LoadedTemplateManager.LoadedCharactersDict.Remove(value_pair.Key); j -= 1; } } } } }
/// <summary> /// Takes a custom SosigTemplate object, and adds it to the necessary dictionaries. This method assumes that you are sending a template for a custom sosig, and that it should be given a new the SosigEnemyID /// </summary> /// <param name="template">A template for a custom sosig (Loaded at runtime)</param> public static void AddSosigTemplate(SosigTemplate template) { SosigEnemyTemplate realTemplate = template.GetSosigEnemyTemplate(); //Since this template is for a custom sosig, we should give it a brand new SosigEnemyID if (!SosigIDDict.ContainsKey(template.SosigEnemyID)) { SosigIDDict.Add(template.SosigEnemyID, NewSosigID); NewSosigID += 1; } else { TNHTweakerLogger.LogError("TNHTweaker -- Loaded sosig had same SosigEnemyID as another sosig -- SosigEnemyID : " + template.SosigEnemyID); return; } //Now fill out the SosigEnemyIDs values for the real sosig template (These will effectively be ints, but this is ok since enums are just ints in disguise) realTemplate.SosigEnemyID = (SosigEnemyID)SosigIDDict[template.SosigEnemyID]; //Finally add the templates to our global dictionary CustomSosigs.Add(template); LoadedSosigsDict.Add(realTemplate, template); TNHTweakerLogger.Log("TNHTweaker -- Sosig added successfuly : " + template.DisplayName, TNHTweakerLogger.LogType.Character); }
//TODO To choose spawn based on IFF, we need to basically generate spawn points on our own in this method! public static TNH_Manager.SosigPatrolSquad GeneratePatrol(TNH_Manager instance, Patrol patrol, List <Vector3> SpawnPoints, List <Vector3> ForwardVectors, List <Vector3> PatrolPoints, int patrolIndex) { TNH_Manager.SosigPatrolSquad squad = new TNH_Manager.SosigPatrolSquad(); squad.PatrolPoints = new List <Vector3>(PatrolPoints); for (int i = 0; i < patrol.PatrolSize && i < SpawnPoints.Count; i++) { SosigEnemyTemplate template; bool allowAllWeapons; //If this is a boss, then we can only spawn it once, so add it to the list of spawned bosses if (patrol.IsBoss) { TNHTweaker.SpawnedBossIndexes.Add(patrolIndex); } //Select a sosig template from the custom character patrol if (i == 0) { template = IM.Instance.odicSosigObjsByID[(SosigEnemyID)LoadedTemplateManager.SosigIDDict[patrol.LeaderType]]; allowAllWeapons = true; } else { template = IM.Instance.odicSosigObjsByID[(SosigEnemyID)LoadedTemplateManager.SosigIDDict[patrol.EnemyType.GetRandom()]]; allowAllWeapons = false; } CustomCharacter character = LoadedTemplateManager.LoadedCharactersDict[instance.C]; SosigTemplate customTemplate = LoadedTemplateManager.LoadedSosigsDict[template]; FVRObject droppedObject = instance.Prefab_HealthPickupMinor; //If squad is set to swarm, the first point they path to should be the players current position Sosig sosig; if (patrol.SwarmPlayer) { squad.PatrolPoints[0] = GM.CurrentPlayerBody.transform.position; sosig = SpawnEnemy(customTemplate, character, SpawnPoints[i], Quaternion.LookRotation(ForwardVectors[i], Vector3.up), instance.AI_Difficulty, patrol.IFFUsed, true, squad.PatrolPoints[0], allowAllWeapons); sosig.SetAssaultSpeed(patrol.AssualtSpeed); } else { sosig = SpawnEnemy(customTemplate, character, SpawnPoints[i], Quaternion.LookRotation(ForwardVectors[i], Vector3.up), instance.AI_Difficulty, patrol.IFFUsed, true, squad.PatrolPoints[0], allowAllWeapons); sosig.SetAssaultSpeed(patrol.AssualtSpeed); } //Handle patrols dropping health if (i == 0 && UnityEngine.Random.value < patrol.DropChance) { sosig.Links[1].RegisterSpawnOnDestroy(droppedObject); } squad.Squad.Add(sosig); } return(squad); }
public void LoadAsset(SetupStage stage, Mod mod, IHandle handle) { if (handle is not IFileHandle file) { throw new ArgumentException("Could not load sosig! Make sure you're pointing to a sosig template json file in the manifest"); } try { SosigTemplate sosig = stage.ImmediateReaders.Get <JToken>()(file).ToObject <SosigTemplate>(); TNHTweakerLogger.Log("TNHTweaker -- Sosig loaded successfuly : " + sosig.DisplayName, TNHTweakerLogger.LogType.File); LoadedTemplateManager.AddSosigTemplate(sosig); } catch (Exception e) { TNHTweakerLogger.LogError("Failed to load setup assets for sosig file! Caused Error: " + e.ToString()); } }
public static void AddSosigTemplate(SosigEnemyTemplate realTemplate) { SosigTemplate template = new SosigTemplate(realTemplate); //This template is from a sogig that already has a valid SosigEnemyID, so we can just add that to the dictionary casted as an int if (!SosigIDDict.ContainsKey(template.SosigEnemyID)) { SosigIDDict.Add(template.SosigEnemyID, (int)realTemplate.SosigEnemyID); } else { TNHTweakerLogger.LogError("TNHTweaker -- Loaded sosig had same SosigEnemyID as another sosig -- SosigEnemyID : " + template.SosigEnemyID); return; } //Since the real template already had a valid SosigEnemyID, we can skip the part where we reassign them DefaultSosigs.Add(realTemplate); LoadedSosigsDict.Add(realTemplate, template); TNHTweakerLogger.Log("TNHTweaker -- Sosig added successfuly : " + template.DisplayName, TNHTweakerLogger.LogType.Character); }
public static void CreateDefaultSosigTemplateFiles(List <SosigEnemyTemplate> sosigs, string path) { try { TNHTweakerLogger.Log("TNHTweaker -- Creating default sosig template files", TNHTweakerLogger.LogType.File); path = path + "/DefaultSosigTemplates"; if (!Directory.Exists(path)) { Directory.CreateDirectory(path); } foreach (SosigEnemyTemplate template in sosigs) { if (File.Exists(path + "/" + template.SosigEnemyID + ".json")) { File.Delete(path + "/" + template.SosigEnemyID + ".json"); } // Create a new file using (StreamWriter sw = File.CreateText(path + "/" + template.SosigEnemyID + ".json")) { SosigTemplate sosig = new SosigTemplate(template); string characterString = JsonConvert.SerializeObject(sosig, Formatting.Indented, new StringEnumConverter()); sw.WriteLine(characterString); sw.Close(); } } } catch (Exception ex) { TNHTweakerLogger.LogError(ex.ToString()); } }
public static Sosig SpawnEnemy(SosigTemplate template, CustomCharacter character, Vector3 spawnLocation, Quaternion spawnRotation, TNHModifier_AIDifficulty difficulty, int IFF, bool isAssault, Vector3 pointOfInterest, bool allowAllWeapons) { if (character.ForceAllAgentWeapons) { allowAllWeapons = true; } TNHTweakerLogger.Log("TNHTWEAKER -- Spawning sosig: " + template.SosigEnemyID, TNHTweakerLogger.LogType.TNH); //Create the sosig object GameObject sosigPrefab = UnityEngine.Object.Instantiate(IM.OD[template.SosigPrefabs.GetRandom <string>()].GetGameObject(), spawnLocation, spawnRotation); Sosig sosigComponent = sosigPrefab.GetComponentInChildren <Sosig>(); //Fill out the sosigs config based on the difficulty SosigConfig config; if (difficulty == TNHModifier_AIDifficulty.Arcade && template.ConfigsEasy.Count > 0) { config = template.ConfigsEasy.GetRandom <SosigConfig>(); } else if (template.Configs.Count > 0) { config = template.Configs.GetRandom <SosigConfig>(); } else { TNHTweakerLogger.LogError("TNHTweaker -- Sosig did not have normal difficulty config when playing on normal difficulty! Not spawning this enemy!"); return(null); } sosigComponent.Configure(config.GetConfigTemplate()); sosigComponent.SetIFF(IFF); //Setup the sosigs inventory sosigComponent.Inventory.Init(); sosigComponent.Inventory.FillAllAmmo(); sosigComponent.InitHands(); //Equip the sosigs weapons if (template.WeaponOptions.Count > 0) { GameObject weaponPrefab = IM.OD[template.WeaponOptions.GetRandom <string>()].GetGameObject(); EquipSosigWeapon(sosigComponent, weaponPrefab, difficulty); } if (template.WeaponOptionsSecondary.Count > 0 && allowAllWeapons && template.SecondaryChance >= UnityEngine.Random.value) { GameObject weaponPrefab = IM.OD[template.WeaponOptionsSecondary.GetRandom <string>()].GetGameObject(); EquipSosigWeapon(sosigComponent, weaponPrefab, difficulty); } if (template.WeaponOptionsTertiary.Count > 0 && allowAllWeapons && template.TertiaryChance >= UnityEngine.Random.value) { GameObject weaponPrefab = IM.OD[template.WeaponOptionsTertiary.GetRandom <string>()].GetGameObject(); EquipSosigWeapon(sosigComponent, weaponPrefab, difficulty); } //Equip clothing to the sosig OutfitConfig outfitConfig = template.OutfitConfigs.GetRandom <OutfitConfig>(); if (outfitConfig.Chance_Headwear >= UnityEngine.Random.value) { EquipSosigClothing(outfitConfig.Headwear, sosigComponent.Links[0], outfitConfig.ForceWearAllHead); } if (outfitConfig.Chance_Facewear >= UnityEngine.Random.value) { EquipSosigClothing(outfitConfig.Facewear, sosigComponent.Links[0], outfitConfig.ForceWearAllFace); } if (outfitConfig.Chance_Eyewear >= UnityEngine.Random.value) { EquipSosigClothing(outfitConfig.Eyewear, sosigComponent.Links[0], outfitConfig.ForceWearAllEye); } if (outfitConfig.Chance_Torsowear >= UnityEngine.Random.value) { EquipSosigClothing(outfitConfig.Torsowear, sosigComponent.Links[1], outfitConfig.ForceWearAllTorso); } if (outfitConfig.Chance_Pantswear >= UnityEngine.Random.value) { EquipSosigClothing(outfitConfig.Pantswear, sosigComponent.Links[2], outfitConfig.ForceWearAllPants); } if (outfitConfig.Chance_Pantswear_Lower >= UnityEngine.Random.value) { EquipSosigClothing(outfitConfig.Pantswear_Lower, sosigComponent.Links[3], outfitConfig.ForceWearAllPantsLower); } if (outfitConfig.Chance_Backpacks >= UnityEngine.Random.value) { EquipSosigClothing(outfitConfig.Backpacks, sosigComponent.Links[1], outfitConfig.ForceWearAllBackpacks); } //Setup the sosigs orders if (isAssault) { sosigComponent.CurrentOrder = Sosig.SosigOrder.Assault; sosigComponent.FallbackOrder = Sosig.SosigOrder.Assault; sosigComponent.CommandAssaultPoint(pointOfInterest); } else { sosigComponent.CurrentOrder = Sosig.SosigOrder.Wander; sosigComponent.FallbackOrder = Sosig.SosigOrder.Wander; sosigComponent.CommandGuardPoint(pointOfInterest, true); sosigComponent.SetDominantGuardDirection(UnityEngine.Random.onUnitSphere); } sosigComponent.SetGuardInvestigateDistanceThreshold(25f); //Handle sosig dropping custom loot if (UnityEngine.Random.value < template.DroppedLootChance && template.DroppedObjectPool != null) { SosigLinkLootWrapper component = sosigComponent.Links[2].gameObject.AddComponent <SosigLinkLootWrapper>(); component.shouldDropOnCleanup = !character.DisableCleanupSosigDrops; component.group = template.DroppedObjectPool; } return(sosigComponent); }
public static Sosig SpawnEnemy(SosigTemplate template, CustomCharacter character, Transform spawnLocation, TNHModifier_AIDifficulty difficulty, int IFF, bool isAssault, Vector3 pointOfInterest, bool allowAllWeapons) { return(SpawnEnemy(template, character, spawnLocation.position, spawnLocation.rotation, difficulty, IFF, isAssault, pointOfInterest, allowAllWeapons)); }
public static void RemoveUnloadedObjectIDs(SosigTemplate template) { //Loop through all outfit configs and remove any clothing objects that don't exist foreach (OutfitConfig config in template.OutfitConfigs) { for (int i = 0; i < config.Headwear.Count; i++) { if (!IM.OD.ContainsKey(config.Headwear[i])) { TNHTweakerLogger.LogWarning("TNHTweaker -- Clothing item not loaded, removing it from clothing config! ObjectID : " + config.Headwear[i]); config.Headwear.RemoveAt(i); i -= 1; } } if (config.Headwear.Count == 0) { config.Chance_Headwear = 0; } for (int i = 0; i < config.Facewear.Count; i++) { if (!IM.OD.ContainsKey(config.Facewear[i])) { TNHTweakerLogger.LogWarning("TNHTweaker -- Clothing item not loaded, removing it from clothing config! ObjectID : " + config.Facewear[i]); config.Facewear.RemoveAt(i); i -= 1; } } if (config.Facewear.Count == 0) { config.Chance_Facewear = 0; } for (int i = 0; i < config.Eyewear.Count; i++) { if (!IM.OD.ContainsKey(config.Eyewear[i])) { TNHTweakerLogger.LogWarning("TNHTweaker -- Clothing item not loaded, removing it from clothing config! ObjectID : " + config.Eyewear[i]); config.Eyewear.RemoveAt(i); i -= 1; } } if (config.Eyewear.Count == 0) { config.Chance_Eyewear = 0; } for (int i = 0; i < config.Torsowear.Count; i++) { if (!IM.OD.ContainsKey(config.Torsowear[i])) { TNHTweakerLogger.LogWarning("TNHTweaker -- Clothing item not loaded, removing it from clothing config! ObjectID : " + config.Torsowear[i]); config.Torsowear.RemoveAt(i); i -= 1; } } if (config.Torsowear.Count == 0) { config.Chance_Torsowear = 0; } for (int i = 0; i < config.Pantswear.Count; i++) { if (!IM.OD.ContainsKey(config.Pantswear[i])) { TNHTweakerLogger.LogWarning("TNHTweaker -- Clothing item not loaded, removing it from clothing config! ObjectID : " + config.Pantswear[i]); config.Pantswear.RemoveAt(i); i -= 1; } } if (config.Pantswear.Count == 0) { config.Chance_Pantswear = 0; } for (int i = 0; i < config.Pantswear_Lower.Count; i++) { if (!IM.OD.ContainsKey(config.Pantswear_Lower[i])) { TNHTweakerLogger.LogWarning("TNHTweaker -- Clothing item not loaded, removing it from clothing config! ObjectID : " + config.Pantswear_Lower[i]); config.Pantswear_Lower.RemoveAt(i); i -= 1; } } if (config.Pantswear_Lower.Count == 0) { config.Chance_Pantswear_Lower = 0; } for (int i = 0; i < config.Backpacks.Count; i++) { if (!IM.OD.ContainsKey(config.Backpacks[i])) { TNHTweakerLogger.LogWarning("TNHTweaker -- Clothing item not loaded, removing it from clothing config! ObjectID : " + config.Backpacks[i]); config.Backpacks.RemoveAt(i); i -= 1; } } if (config.Backpacks.Count == 0) { config.Chance_Backpacks = 0; } } }