public void Craft() { // should only be called while CRAFTING and if recipe still valid // (no one should touch 'craftingRecipe', but let's just be sure. // -> we already validated everything in CmdCraft. let's just craft. if (player.state == "CRAFTING" && currentRecipe != null && currentRecipe.result != null) { // enough space? Item result = new Item(currentRecipe.result); if (inventory.CanAdd(result, 1)) { // remove the ingredients from inventory in any case foreach (ScriptableItemAndAmount ingredient in currentRecipe.ingredients) { if (ingredient.amount > 0 && ingredient.item != null) { inventory.Remove(new Item(ingredient.item), ingredient.amount); } } // roll the dice to decide if we add the result or not // IMPORTANT: we use rand() < probability to decide. // => UnityEngine.Random.value is [0,1] inclusive: // for 0% probability it's fine because it's never '< 0' // for 100% probability it's not because it's not always '< 1', it might be == 1 // and if we use '<=' instead then it won't work for 0% // => C#'s Random value is [0,1) exclusive like most random // functions. this works fine. if (new System.Random().NextDouble() < currentRecipe.probability) { // add result item to inventory inventory.Add(new Item(currentRecipe.result), 1); TargetCraftingSuccess(); } else { TargetCraftingFailed(); } // clear indices afterwards // note: we set all to -1 instead of calling .Clear because // that would clear all the slots in host mode. // (don't clear in host mode, otherwise it clears the crafting // UI for the player and we have to drag items into it again) if (!isLocalPlayer) { for (int i = 0; i < ScriptableRecipe.recipeSize; ++i) { indices[i] = -1; } } // clear recipe currentRecipe = null; } } }
[HideInInspector] public CraftingState craftingState = CraftingState.None; // // client sided // craft the current combination of items and put result into inventory public void Craft(int[] indices) { // validate: between 1 and 6, all valid, no duplicates? if (health.current > 0 && 0 < indices.Length && indices.Length <= ScriptableRecipe.recipeSize && indices.All(index => 0 <= index && index < inventory.slots.Count && inventory.slots[index].amount > 0) && !indices.ToList().HasDuplicates()) { // build list of item templates from indices List <ItemSlot> items = indices.Select(index => inventory.slots[index]).ToList(); // find recipe ScriptableRecipe recipe = ScriptableRecipe.dict.Values.ToList().Find(r => r.CanCraftWith(items)); // good enough for now if (recipe != null && recipe.result != null) { // enough space? Item result = new Item(recipe.result); if (inventory.CanAdd(result, 1)) { // remove the ingredients from inventory in any case foreach (ScriptableItemAndAmount ingredient in recipe.ingredients) { if (ingredient.amount > 0 && ingredient.item != null) { inventory.Remove(new Item(ingredient.item), ingredient.amount); } } // roll the dice to decide if we add the result or not // IMPORTANT: we use rand() < probability to decide. // => UnityEngine.Random.value is [0,1] inclusive: // for 0% probability it's fine because it's never '< 0' // for 100% probability it's not because it's not always '< 1', it might be == 1 // and if we use '<=' instead then it won't work for 0% // => C#'s Random value is [0,1) exclusive like most random // functions. this works fine. if (new System.Random().NextDouble() < recipe.probability) { // add result item to inventory inventory.Add(result, 1); craftingState = CraftingState.Success; } else { craftingState = CraftingState.Failed; } } } } }
public void SetCrafterRecipe(ScriptableRecipe rec) { if (typeSelected == 4) { //instead of directly setting the recipe, call a function on the crafter so that I can activate other shit when it changes recipe. if (obj) { obj.GetComponent <Crafting>().ChangeRecipe(rec); } } else { //if you don't have a crafter selected, say "No crater selected. This message should never be seen" print("No crafter selected. This message should never be seen"); } }
public void CmdCraft(string recipeName, int[] clientIndices) { // validate: between 1 and 6, all valid, no duplicates? // -> can be IDLE or MOVING (in which case we reset the movement) if ((player.state == "IDLE" || player.state == "MOVING") && clientIndices.Length == ScriptableRecipe.recipeSize) { // find valid indices that are not '-1' and make sure there are no // duplicates List <int> validIndices = clientIndices.Where(index => 0 <= index && index < inventory.slots.Count && inventory.slots[index].amount > 0).ToList(); if (validIndices.Count > 0 && !validIndices.HasDuplicates()) { // find recipe if (ScriptableRecipe.All.TryGetValue(recipeName, out ScriptableRecipe recipe) && recipe.result != null) { // enough space? Item result = new Item(recipe.result); if (inventory.CanAdd(result, 1)) { // cache recipe so we don't have to search for it again // in Craft() currentRecipe = recipe; // store the crafting indices on the server. no need for // a SyncList and unnecessary broadcasting. // we already have a 'craftingIndices' variable anyway. indices = clientIndices.ToList(); // start crafting requestPending = true; endTime = NetworkTime.time + recipe.craftingTime; } } } } }
static void CreateRecipes() { XmlDocument xData = new XmlDocument(); xData.Load("Assets/XML/RecipesXML.xml"); XmlNodeList xRecipeList = xData.SelectNodes("root/recipe"); foreach (XmlNode xRecipe in xRecipeList) { ScriptableRecipe recipeObj = new ScriptableRecipe(); recipeObj.name = xRecipe.Attributes["name"].Value.Replace(" ", "_"); recipeObj.description = xRecipe.Attributes["description"].Value; XmlNodeList xIngredients = xRecipe.SelectNodes("ingredient"); foreach (XmlNode xIngredient in xIngredients) { RecipeIngredients recipeIngr = new RecipeIngredients(); recipeIngr.ingredient = (Ingredients)Enum.Parse(typeof(Ingredients), xIngredient.Attributes["name"].Value.Replace(" ", "_")); recipeIngr.quantity = Convert.ToSingle(xIngredient.Attributes["quantity"].Value); recipeObj.ingredients.Add(recipeIngr); ScriptableIngredient scriptableIngr = AssetDatabase.LoadAssetAtPath("Assets/ScriptableObjects/Ingredients/" + xIngredient.Attributes["name"].Value.Replace(" ", "_") + ".asset", typeof(ScriptableIngredient)) as ScriptableIngredient; recipeObj.value += scriptableIngr.cost * recipeIngr.quantity; } string codeText = File.ReadAllText("Assets/Scripts/Recipes.cs"); int markerIndex = codeText.LastIndexOf("//#RECIPE#"); File.WriteAllText("Assets/Scripts/Recipes.cs", codeText.Insert(markerIndex, recipeObj.name + ",\n\t")); AssetDatabase.CreateAsset(recipeObj, "Assets/ScriptableObjects/Recipes/" + recipeObj.name + ".asset"); AssetDatabase.Refresh(); } }
public void ChangeRecipe(ScriptableRecipe r) { recipe = r; recipeDisplay.sprite = r.img; recipeDisplay.color = r.imgColor; //also set a stored default recipe so that it is easier to make a lot of the same machine if (r) { info.storedRecipe = r; } //set recipe string list and int list currentRecipe = new List <string> (); currentAmounts = new List <int> (); currentRecipe.Capacity = recipe.input.Count; currentAmounts.Capacity = recipe.input.Count; for (int i = 0; i < recipe.input.Count; i++) { string temp = recipe.input[i].Split(',')[0]; currentAmounts.Add(int.Parse(temp) + 1); currentRecipe.Add(recipe.input[i].Split(',')[1]); } //the way this works means that you must order your recipes as "1,sand 2,rock etc" }
void Update() { Player player = Player.localPlayer; if (player) { // hotkey (not while typing in chat, etc.) if (Input.GetKeyDown(hotKey) && !UIUtils.AnyInputActive()) { panel.SetActive(!panel.activeSelf); } // only update the panel if it's active if (panel.activeSelf) { // instantiate/destroy enough slots UIUtils.BalancePrefabs(ingredientSlotPrefab.gameObject, player.crafting.indices.Count, ingredientContent); // refresh all for (int i = 0; i < player.crafting.indices.Count; ++i) { UICraftingIngredientSlot slot = ingredientContent.GetChild(i).GetComponent <UICraftingIngredientSlot>(); slot.dragAndDropable.name = i.ToString(); // drag and drop index int itemIndex = player.crafting.indices[i]; if (0 <= itemIndex && itemIndex < player.inventory.slots.Count && player.inventory.slots[itemIndex].amount > 0) { ItemSlot itemSlot = player.inventory.slots[itemIndex]; // refresh valid item // only build tooltip while it's actually shown. this // avoids MASSIVE amounts of StringBuilder allocations. slot.tooltip.enabled = true; if (slot.tooltip.IsVisible()) { slot.tooltip.text = itemSlot.ToolTip(); } slot.dragAndDropable.dragable = true; // use durability colors? if (itemSlot.item.maxDurability > 0) { if (itemSlot.item.durability == 0) { slot.image.color = brokenDurabilityColor; } else if (itemSlot.item.DurabilityPercent() < lowDurabilityThreshold) { slot.image.color = lowDurabilityColor; } else { slot.image.color = Color.white; } } else { slot.image.color = Color.white; // reset for no-durability items } slot.image.sprite = itemSlot.item.image; slot.amountOverlay.SetActive(itemSlot.amount > 1); slot.amountText.text = itemSlot.amount.ToString(); } else { // reset the index because it's invalid player.crafting.indices[i] = -1; // refresh invalid item slot.tooltip.enabled = false; slot.dragAndDropable.dragable = false; slot.image.color = Color.clear; slot.image.sprite = null; slot.amountOverlay.SetActive(false); } } // find valid indices => item templates => matching recipe List <int> validIndices = player.crafting.indices.Where( index => 0 <= index && index < player.inventory.slots.Count && player.inventory.slots[index].amount > 0 ).ToList(); List <ItemSlot> items = validIndices.Select(index => player.inventory.slots[index]).ToList(); ScriptableRecipe recipe = ScriptableRecipe.Find(items); if (recipe != null) { // refresh valid recipe Item item = new Item(recipe.result); // only build tooltip while it's actually shown. this // avoids MASSIVE amounts of StringBuilder allocations. resultSlotToolTip.enabled = true; if (resultSlotToolTip.IsVisible()) { resultSlotToolTip.text = new ItemSlot(item).ToolTip(); // ItemSlot so that {AMOUNT} is replaced too } resultSlotImage.color = Color.white; resultSlotImage.sprite = recipe.result.image; // show progress bar while crafting // (show 100% if craft time = 0 because it's just better feedback) progressSlider.gameObject.SetActive(player.state == "CRAFTING"); double startTime = player.crafting.endTime - recipe.craftingTime; double elapsedTime = NetworkTime.time - startTime; progressSlider.value = recipe.craftingTime > 0 ? (float)elapsedTime / recipe.craftingTime : 1; } else { // refresh invalid recipe resultSlotToolTip.enabled = false; resultSlotImage.color = Color.clear; resultSlotImage.sprite = null; progressSlider.gameObject.SetActive(false); } // craft result // (no recipe != null check because it will be null if those were // the last two ingredients in our inventory) if (player.crafting.state == CraftingState.Success) { resultText.color = successColor; resultText.text = "Success!"; } else if (player.crafting.state == CraftingState.Failed) { resultText.color = failedColor; resultText.text = "Failed :("; } else { resultText.text = ""; } // craft button with 'Try' prefix to let people know that it might fail // (disabled while in progress) craftButton.GetComponentInChildren <Text>().text = recipe != null && recipe.probability < 1 ? "Try Craft" : "Craft"; craftButton.interactable = recipe != null && player.state != "CRAFTING" && player.crafting.state != CraftingState.InProgress && player.inventory.CanAdd(new Item(recipe.result), 1); craftButton.onClick.SetListener(() => { player.crafting.state = CraftingState.InProgress; // wait for result // pass original array so server can copy it to it's own // craftingIndices. we pass original one and not only the valid // indicies because then in host mode we would make the crafting // indices array smaller by only copying the valid indices, // hence losing crafting slots player.crafting.CmdCraft(recipe.name, player.crafting.indices.ToArray()); }); } } else { panel.SetActive(false); } }
void Update() { GameObject player = Player.player; if (!player) { return; } PlayerCrafting crafting = player.GetComponent <PlayerCrafting>(); Inventory inventory = player.GetComponent <Inventory>(); // instantiate/destroy enough slots UIUtils.BalancePrefabs(ingredientSlotPrefab.gameObject, crafting.indices.Count, ingredientContent); // refresh all for (int i = 0; i < crafting.indices.Count; ++i) { UICraftingIngredientSlot slot = ingredientContent.GetChild(i).GetComponent <UICraftingIngredientSlot>(); slot.dragAndDropable.name = i.ToString(); // drag and drop index int itemIndex = crafting.indices[i]; if (0 <= itemIndex && itemIndex < inventory.slots.Count && inventory.slots[itemIndex].amount > 0) { ItemSlot itemSlot = inventory.slots[itemIndex]; // refresh valid item slot.tooltip.enabled = true; slot.tooltip.text = itemSlot.ToolTip(); slot.dragAndDropable.dragable = true; slot.image.color = Color.white; slot.image.sprite = itemSlot.item.image; slot.amountOverlay.SetActive(itemSlot.amount > 1); slot.amountText.text = itemSlot.amount.ToString(); } else { // reset the index because it's invalid crafting.indices[i] = -1; // refresh invalid item slot.tooltip.enabled = false; slot.dragAndDropable.dragable = false; slot.image.color = Color.clear; slot.image.sprite = null; slot.amountOverlay.SetActive(false); } } // find valid indices => item templates => matching recipe List <int> validIndices = crafting.indices.Where( index => 0 <= index && index < inventory.slots.Count && inventory.slots[index].amount > 0 ).ToList(); List <ItemSlot> items = validIndices.Select(index => inventory.slots[index]).ToList(); ScriptableRecipe recipe = ScriptableRecipe.dict.Values.ToList().Find(r => r.CanCraftWith(items)); // good enough for now if (recipe != null) { // refresh valid recipe Item item = new Item(recipe.result); resultSlotToolTip.enabled = true; resultSlotToolTip.text = new ItemSlot(item).ToolTip(); // ItemSlot so that {AMOUNT} is replaced too resultSlotImage.color = Color.white; resultSlotImage.sprite = recipe.result.image; } else { // refresh invalid recipe resultSlotToolTip.enabled = false; resultSlotImage.color = Color.clear; resultSlotImage.sprite = null; } // craft result // (no recipe != null check because it will be null if those were // the last two ingredients in our inventory) if (crafting.craftingState == CraftingState.Success) { resultText.color = successColor; resultText.text = "Success!"; } else if (crafting.craftingState == CraftingState.Failed) { resultText.color = failedColor; resultText.text = "Failed :("; } else { resultText.text = ""; } // craft button with 'Try' prefix to let people know that it might fail // (disabled while in progress) craftButton.GetComponentInChildren <Text>().text = recipe != null && recipe.probability < 1 ? "Try Craft" : "Craft"; craftButton.interactable = recipe != null && crafting.craftingState != CraftingState.InProgress && inventory.CanAdd(new Item(recipe.result), 1); craftButton.onClick.SetListener(() => { crafting.craftingState = CraftingState.InProgress; // wait for result crafting.Craft(validIndices.ToArray()); }); }
void Update() { Player player = Utils.ClientLocalPlayer(); if (!player) return; // hotkey (not while typing in chat, etc.) if (Input.GetKeyDown(hotKey) && !UIUtils.AnyInputActive()) panel.SetActive(!panel.activeSelf); // only update the panel if it's active if (panel.activeSelf) { // instantiate/destroy enough slots UIUtils.BalancePrefabs(ingredientSlotPrefab.gameObject, player.craftingIndices.Count, ingredientContent); // refresh all for (int i = 0; i < player.craftingIndices.Count; ++i) { UICraftingIngredientSlot slot = ingredientContent.GetChild(i).GetComponent<UICraftingIngredientSlot>(); slot.dragAndDropable.name = i.ToString(); // drag and drop index int itemIndex = player.craftingIndices[i]; if (0 <= itemIndex && itemIndex < player.inventory.Count && player.inventory[itemIndex].amount > 0) { ItemSlot itemSlot = player.inventory[itemIndex]; // refresh valid item slot.tooltip.enabled = true; slot.tooltip.text = itemSlot.ToolTip(); slot.dragAndDropable.dragable = true; slot.image.color = Color.white; slot.image.sprite = itemSlot.item.image; } else { // reset the index because it's invalid player.craftingIndices[i] = -1; // refresh invalid item slot.tooltip.enabled = false; slot.dragAndDropable.dragable = false; slot.image.color = Color.clear; slot.image.sprite = null; } } // find valid indices => item templates => matching recipe List<int> validIndices = player.craftingIndices.Where( index => 0 <= index && index < player.inventory.Count && player.inventory[index].amount > 0 ).ToList(); List<ScriptableItem> items = validIndices.Select(index => player.inventory[index].item.data).ToList(); ScriptableRecipe recipe = ScriptableRecipe.dict.Values.ToList().Find(r => r.CanCraftWith(items)); // good enough for now if (recipe != null) { // refresh valid recipe resultSlotToolTip.enabled = true; resultSlotToolTip.text = new Item(recipe.result).ToolTip(); resultSlotImage.color = Color.white; resultSlotImage.sprite = recipe.result.image; } else { // refresh invalid recipe resultSlotToolTip.enabled = false; resultSlotImage.color = Color.clear; resultSlotImage.sprite = null; } // craft button craftButton.interactable = recipe != null && player.InventoryCanAdd(new Item(recipe.result), 1); craftButton.onClick.SetListener(() => { player.CmdCraft(validIndices.ToArray()); }); } // addon system hooks Utils.InvokeMany(typeof(UICrafting), this, "Update_"); }