public void RegisterRecipe() { List <RecipeTable.Recipe> GSORecipes = new List <RecipeTable.Recipe>(); foreach (var list in RecipeManager.inst.recipeTable.m_RecipeLists) { if (list.m_Name == "gsofab") { GSORecipes = list.m_Recipes; } } var Bacon = new RecipeTable.Recipe() { m_BuildTimeSeconds = 1, m_InputItems = new RecipeTable.Recipe.ItemSpec[] { new RecipeTable.Recipe.ItemSpec(new ItemTypeInfo(ObjectTypes.Chunk, (int)ChunkTypes.OleiteJelly), 4), new RecipeTable.Recipe.ItemSpec(new ItemTypeInfo(ObjectTypes.Chunk, (int)ChunkTypes.Wood), 4) }, m_OutputType = RecipeTable.Recipe.OutputType.Items, m_OutputItems = new RecipeTable.Recipe.ItemSpec[] { new RecipeTable.Recipe.ItemSpec(new ItemTypeInfo(ObjectTypes.Block, 10000), 1) } }; GSORecipes.Add(Bacon); }
internal static void RegisterRecipe(CustomRecipeStruct customRecipe) { List <RecipeTable.Recipe> recipeList = null; var recipeTable = Singleton.Manager <RecipeManager> .inst.recipeTable; foreach (RecipeTable.RecipeList list in recipeTable.m_RecipeLists) { if (list.m_Name == customRecipe.NameOfFabricator) { recipeList = list.m_Recipes; break; } } if (recipeList == null) { Console.WriteLine("Creating new recipe table '" + customRecipe.NameOfFabricator + "'..."); recipeList = new List <RecipeTable.Recipe>(); var NewRecipeItem = new RecipeTable.RecipeList() { m_Name = customRecipe.NameOfFabricator, m_Recipes = recipeList, }; recipeTable.m_RecipeLists.Add(NewRecipeItem); } var InputItems = new RecipeTable.Recipe.ItemSpec[customRecipe.Inputs.Length]; for (int i = 0; i < customRecipe.Inputs.Length; i++) { InputItems[i] = customRecipe.Inputs[i].ItemSpec(); } var OutputItems = new RecipeTable.Recipe.ItemSpec[customRecipe.Outputs.Length]; for (int j = 0; j < customRecipe.Outputs.Length; j++) { OutputItems[j] = customRecipe.Outputs[j].ItemSpec(); } var Recipe = new RecipeTable.Recipe() { m_BuildTimeSeconds = customRecipe.BuildTime, m_InputItems = InputItems, m_OutputType = customRecipe.OutputType, m_OutputItems = OutputItems }; recipeList.Add(Recipe); }
// This method should add a module to the TankBlock prefab public override bool CreateModuleForBlock(int blockID, ModdedBlockDefinition def, TankBlock block, JToken jToken) { try { Debug.Log("[Nuterra] Loading CustomBlock module"); if (jToken.Type == JTokenType.Object) { JObject jData = (JObject)jToken; // Get the mod contents so we can search for additional assets ModContainer container = ManMods.inst.FindMod(def); ModContents mod = container != null ? container.Contents : null; if (mod == null) { Debug.LogError("[Nuterra] Could not find mod that this unoffical block is part of"); return(false); } // ------------------------------------------------------ // Basics like name, desc etc. The Official Mod Tool lets us set these already, but we might want to override def.m_BlockDisplayName = TryParse(jData, "Name", def.m_BlockDisplayName); def.m_BlockDescription = TryParse(jData, "Description", def.m_BlockDescription); // Ignore block ID. Official loader handles IDs automatically. // Ignore corporation. Custom corps no longer have a fixed ID, so we should use the official tool to set corp IDs. //def.m_Corporation = TryParse(jData, "Corporation", def.m_Corporation); block.m_BlockCategory = def.m_Category = TryParseEnum(jData, "Category", def.m_Category); def.m_Rarity = TryParseEnum(jData, "Rarity", def.m_Rarity); def.m_Grade = TryParse(jData, "Grade", def.m_Grade); // Recipe if (jData.TryGetValue("Recipe", out JToken jRecipe)) { RecipeTable.Recipe recipe = new RecipeTable.Recipe(); Dictionary <ChunkTypes, RecipeTable.Recipe.ItemSpec> dictionary = new Dictionary <ChunkTypes, RecipeTable.Recipe.ItemSpec>(); int RecipePrice = 0; if (jRecipe is JValue rString) { string[] recipeString = rString.ToObject <string>().Replace(" ", "").Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries); foreach (string item in recipeString) { RecipePrice += AppendToRecipe(dictionary, item, 1); } } else if (jRecipe is JObject rObject) { foreach (var item in rObject) { RecipePrice += AppendToRecipe(dictionary, item.Key, item.Value.ToObject <int>()); } } else if (jRecipe is JArray rArray) { foreach (var item in rArray) { RecipePrice += AppendToRecipe(dictionary, item.ToString(), 1); } } recipe.m_InputItems = new RecipeTable.Recipe.ItemSpec[dictionary.Count]; dictionary.Values.CopyTo(recipe.m_InputItems, 0); recipe.m_OutputItems[0] = new RecipeTable.Recipe.ItemSpec(new ItemTypeInfo(ObjectTypes.Block, blockID), 1); Singleton.Manager <RecipeManager> .inst.RegisterCustomBlockFabricatorRecipe(blockID, def.m_Corporation, recipe); def.m_Price = RecipePrice; } // TODO: RecipeTable def.m_Price = TryParseEnum(jData, "Price", def.m_Price); def.m_MaxHealth = TryParse(jData, "HP", def.m_MaxHealth); // ------------------------------------------------------ #region Reference - Copy a vanilla block Debug.Log("[Nuterra] Starting references"); bool keepRenderers = TryParse(jData, "KeepRenderers", true); bool keepReferenceRenderers = TryParse(jData, "KeepReferenceRenderers", true); bool keepColliders = TryParse(jData, "KeepColliders", true); if (TryGetStringMultipleKeys(jData, out string referenceBlock, "GamePrefabReference", "PrefabReference")) { // This code block copies our chosen reference block // TTQMM REF: BlockPrefabBuilder.Initialize GameObject originalGameObject = TTReferences.FindBlockFromString(referenceBlock); if (originalGameObject != null) { GameObject newObject = UnityEngine.Object.Instantiate(originalGameObject); // Assign this back to block for further processing block = GetOrAddComponent <TankBlock>(newObject.transform); //TankBlock original = originalGameObject.GetComponent<TankBlock>(); //TankBlock copy = UnityEngine.Object.Instantiate(original); TankBlockTemplate fakeTemplate = newObject.AddComponent <TankBlockTemplate>(); // Cheeky hack to swap the prefab // The official block loader doesn't expect this to happen, but I will assume // for now that you are making 100% official or 100% unofficial JSONs def.m_PhysicalPrefab = fakeTemplate; Debug.Log($"[Nuterra] Found game prefab reference as {newObject}"); // TTQMM REF: DirectoryBlockLoader.CreateJSONBlock, the handling of these flags is a bit weird if (keepRenderers && !keepColliders) { RemoveChildren <Collider>(block); } if (!keepRenderers && !keepReferenceRenderers) { RemoveChildren <MeshRenderer>(block); RemoveChildren <TankTrack>(block); RemoveChildren <SkinnedMeshRenderer>(block); RemoveChildren <MeshFilter>(block); if (!keepColliders) { RemoveChildren <Collider>(block); } } newObject.layer = Globals.inst.layerTank; newObject.tag = "TankBlock"; bool hasRefOffset = TryGetTokenMultipleKeys(jData, out JToken jOffset, "ReferenceOffset", "PrefabOffset", "PrefabPosition"); bool hasRefRotation = TryGetTokenMultipleKeys(jData, out JToken jEuler, "ReferenceRotationOffset", "PrefabRotation"); bool hasRefScale = TryGetTokenMultipleKeys(jData, out JToken jScale, "ReferenceScale", "PrefabScale"); if (hasRefOffset || hasRefRotation || hasRefScale) { Vector3 offset = hasRefOffset ? GetVector3(jOffset) : Vector3.zero; Vector3 scale = hasRefScale ? GetVector3(jScale) : Vector3.one; Vector3 euler = hasRefRotation ? GetVector3(jEuler) : Vector3.zero; foreach (Transform child in newObject.transform) { if (hasRefOffset) { child.localPosition += offset; } if (hasRefRotation) { child.localEulerAngles += euler; } if (hasRefScale) { child.localScale += scale; } } } } else { Debug.LogError($"[Nuterra] Failed to find GamePrefabReference {referenceBlock}"); } } #endregion // ------------------------------------------------------ // ------------------------------------------------------ // Get some references set up for the next phase, now our prefab is setup Damageable damageable = GetOrAddComponent <Damageable>(block); ModuleDamage moduleDamage = GetOrAddComponent <ModuleDamage>(block); Visible visible = GetOrAddComponent <Visible>(block); Transform transform = block.transform; transform.position = Vector3.zero; transform.rotation = Quaternion.identity; transform.localScale = Vector3.one; // ------------------------------------------------------ #region Additional References if (TryGetStringMultipleKeys(jData, out string referenceExplosion, "DeathExplosionReference", "ExplosionReference")) { GameObject refBlock = TTReferences.FindBlockFromString(referenceExplosion); if (refBlock != null) { moduleDamage.deathExplosion = refBlock.GetComponent <ModuleDamage>().deathExplosion; Debug.Log($"[Nuterra] Swapped death explosion for {refBlock}"); } } #endregion // ------------------------------------------------------ // ------------------------------------------------------ #region Tweaks // BlockExtents is a way of quickly doing a cuboid Filled Cell setup if (jData.TryGetValue("BlockExtents", out JToken jExtents) && jExtents.Type == JTokenType.Object) { List <IntVector3> filledCells = new List <IntVector3>(); int x = ((JObject)jExtents).GetValue("x").ToObject <int>(); int y = ((JObject)jExtents).GetValue("y").ToObject <int>(); int z = ((JObject)jExtents).GetValue("z").ToObject <int>(); for (int i = 0; i < x; i++) { for (int j = 0; j < y; j++) { for (int k = 0; k < z; k++) { filledCells.Add(new IntVector3(i, j, k)); } } } block.filledCells = filledCells.ToArray(); Debug.Log("[Nuterra] Overwrote BlockExtents"); } // CellMap / CellsMap if (TryGetTokenMultipleKeys(jData, out JToken jCellMap, "CellMap", "CellsMap")) { string[][] ZYXCells = jCellMap.ToObject <string[][]>(); List <IntVector3> cells = new List <IntVector3>(); for (int z = 0; z < ZYXCells.Length; z++) { string[] YXslice = ZYXCells[z]; if (YXslice == null) { continue; } for (int y = 0, ry = YXslice.Length - 1; ry >= 0; y++, ry--) { string Xline = YXslice[ry]; if (Xline == null) { continue; } for (int x = 0; x < Xline.Length; x++) { char cell = Xline[x]; if (cell != ' ') { cells.Add(new IntVector3(x, y, z)); } } } } block.filledCells = cells.ToArray(); } // TODO: APsOnlyAtBottom / MakeAPsAtBottom if (jData.TryGetValue("Cells", out JToken jCells) && jCells.Type == JTokenType.Array) { List <IntVector3> filledCells = new List <IntVector3>(); foreach (JObject jCell in (JArray)jCells) { filledCells.Add(GetVector3Int(jCell)); } block.filledCells = filledCells.ToArray(); } // APs if (jData.TryGetValue("APs", out JToken jAPList) && jAPList.Type == JTokenType.Array) { List <Vector3> aps = new List <Vector3>(); foreach (JToken token in (JArray)jAPList) { aps.Add(GetVector3(token)); } block.attachPoints = aps.ToArray(); } // Some basic block stats damageable.DamageableType = (ManDamage.DamageableType)TryParse(jData, "DamageableType", (int)damageable.DamageableType); if (TryGetFloatMultipleKeys(jData, out float fragility, moduleDamage.m_DamageDetachFragility, "DetachFragility", "Fragility")) { moduleDamage.m_DamageDetachFragility = fragility; } block.m_DefaultMass = TryParse(jData, "Mass", block.m_DefaultMass); // Center of Mass JArray jComVector = null; if (jData.TryGetValue("CenterOfMass", out JToken com1) && com1.Type == JTokenType.Array) { jComVector = (JArray)com1; } if (jData.TryGetValue("CentreOfMass ", out JToken com2) && com2.Type == JTokenType.Array) { jComVector = (JArray)com2; } if (jComVector != null) { Transform comTrans = block.transform.Find("CentreOfMass"); if (comTrans == null) { comTrans = new GameObject("CentreOfMass").transform; comTrans.SetParent(block.transform); comTrans.localScale = Vector3.one; comTrans.localRotation = Quaternion.identity; } comTrans.localPosition = new Vector3(jComVector[0].ToObject <float>(), jComVector[1].ToObject <float>(), jComVector[2].ToObject <float>()); // TODO: Weird thing about offseting colliders from Nuterra //for (int i = 0; i < Prefab.transform.childCount; i++) //{ // transform = Prefab.transform.GetChild(i); // if (transform.name.Length < 5 && transform.name.EndsWith("col")) // "[a-z]col" // transform.localPosition = CenterOfMass; //} } // TODO: RotationGroup // IconName override if (jData.TryGetValue("IconName", out JToken jIconName) && jIconName.Type == JTokenType.String) { UnityEngine.Object obj = mod.FindAsset(jIconName.ToString()); if (obj != null) { if (obj is Sprite sprite) { def.m_Icon = sprite.texture; } else if (obj is Texture2D texture) { def.m_Icon = texture; } else { Debug.LogWarning($"Found unknown object type {obj.GetType()} for icon override for {block.name}"); } } } // TODO: Emission Mode // Filepath? For reparse? #endregion // ------------------------------------------------------ // Start recursively adding objects with the root. // Calling it this way and treating the root as a sub-object prevents a lot of code duplication RecursivelyAddSubObject(block, mod, block.transform, jData, TTReferences.kMissingTextureTankBlock, false); // Weird export fix up for meshes // Flip everything in x foreach (MeshRenderer mr in block.GetComponentsInChildren <MeshRenderer>()) { mr.transform.localScale = new Vector3(-mr.transform.localScale.x, mr.transform.localScale.y, mr.transform.localScale.z); } foreach (MeshCollider mc in block.GetComponentsInChildren <MeshCollider>()) { if (mc.GetComponent <MeshRenderer>() == null) // Skip ones with both. Don't want to double flip { mc.transform.localScale = new Vector3(-mc.transform.localScale.x, mc.transform.localScale.y, mc.transform.localScale.z); } } return(true); } return(false); } catch (Exception e) { Debug.LogError($"[Nuterra] Caught exception {e}"); return(false); } }