OK so if we are passing the unique identifier down the chain, I’ll need @bixarrio 's help for how to assign that in ‘CraftingTable.cs’, because… well… the restoring system, and the ‘CraftRecipe’ function in ‘CratingTable.cs’ needs to know that value too
@bixarrio please help me out with this last part
Aside from that,this is one insanely lengthy chain to modify… I’m not even sure if what I’m doing is right or wrong, but I’ll post it here anyway:
- in ‘CraftingCompleteEstimationEvent.cs’:
using System;
public static class CraftingCompleteEstimationEvent
{
public static event Action<string[]> OnCraftingCompleteEstimation;
public static void InvokeOnCraftingCompleteEstimation(string[] plantGrowthUniqueIDs)
{
OnCraftingCompleteEstimation?.Invoke(plantGrowthUniqueIDs);
}
}
- in ‘CraftingTable.cs’:
// in 'RestoreFromJToken()':
// TEST - DELETE IF FAILED
string[] uniqueIdentifiers = new string[saveData.CurrentQuantity];
// Plant the plants that are supposed to be there, based on where they are in time:
if (CurrentRecipe != null && saveData.CurrentQuantity > 0)
{
for(int i = 0; i < saveData.CurrentQuantity; i++)
{
var availableSlots = GetComponentsInChildren<PlantGrowthHandler>().Where(handler => handler.GetCurrentPlant() == null);
var firstAvailableSlot = availableSlots.First();
firstAvailableSlot.SetCurrentPlant(CurrentRecipe.GetResult().Item.GetPickup().gameObject);
// TEST - DELETE IF FAILED (FOR IDENTIFYING PLANT GROWTH HANDLERS)
uniqueIdentifiers[i] = GetComponentsInChildren<PlantGrowthHandler>()[i].UniqueIdentifier;
}
}
// in 'CraftRecipe()':
// TEST - DELETE IF FAILED (FOR IDENTIFYING PLANT GROWTH HANDLERS)
string[] uniqueIdentifiers = new string[craftingQuantity.GetCurrentQuantity()];
if (UseQuantityUI) // If you're using the Quantity UI (The code below is dedicated for Farming only for this case)
{
for (int i = 0; i < craftingQuantity.GetCurrentQuantity(); i++)
{
// Plant quantity based on what the 'GetCurrentQuantity()' has available,
// which is controlled in 'CraftingSystem.OnCraftingInteraction()', and the
// value is also updated below, after you hit the 'Craft' button on the UI
var availableSlots = GetComponentsInChildren<PlantGrowthHandler>().Where(handler => handler.GetCurrentPlant() == null);
var firstAvailableSlot = availableSlots.First();
firstAvailableSlot.SetCurrentPlant(recipe.GetResult().Item.GetPickup().gameObject); // Just a way to indicate that this slot is occupied
// TEST - DELETE IF FAILED (FOR IDENTIFYING PLANT GROWTH HANDLERS)
uniqueIdentifiers[i] = GetComponentsInChildren<PlantGrowthHandler>()[i].UniqueIdentifier;
}
}
- in ‘ICraftingEventBus.cs’:
// there's two new 'string[] uniqueIdentifier' parameters in here:
using RPG.Crafting;
public interface ICraftingEventBus
{
void CraftingStarted(Recipe recipe, string[] uniqueIdentifier);
void CraftingResumed(Recipe recipe, float remainingTime, string[] uniqueIdentifier);
void RegisterObserver(ICraftingObserver observer);
void DeregisterObserver(ICraftingObserver observer);
}
- in ‘CraftingEventBus.cs’:
// again, new 'string[] uniqueIdentifiers' parameters here:
public void CraftingStarted(Recipe recipe, string[] uniqueIdentifier)
{
foreach (var observer in observers)
{
observer.OnCraftingStarted(recipe, uniqueIdentifier);
}
}
public void CraftingResumed(Recipe recipe, float remainingTime, string[] uniqueIdentifier)
{
foreach (var observer in observers)
{
observer.OnCraftingResumed(recipe, remainingTime, uniqueIdentifier);
}
}
- in ‘ICraftingObserver.cs’:
// you get the idea by now... new 'string[] uniqueIdentifier' parameters:
using RPG.Crafting;
using UnityEngine;
public interface ICraftingObserver
{
void OnCraftingStarted(Recipe recipe, string[] uniqueIdentifier);
void OnCraftingResumed(Recipe recipe, float remainingTime, string[] uniqueIdentifier);
}
- in ‘CraftingNotification.cs’:
// same drill...
public void OnCraftingStarted(Recipe recipe, string[] uniqueIdentifier)
{
StartCoroutine(CraftingCompleteEstimation(recipe, recipe.GetCraftDuration(), uniqueIdentifier));
numberOfOccupiedSlots = GetNumberOfOccupiedSlots();
}
public void OnCraftingResumed(Recipe recipe, float remainingTime, string[] uniqueIdentifier)
{
StopAllCoroutines();
StartCoroutine(CraftingCompleteEstimation(recipe, remainingTime, uniqueIdentifier));
numberOfOccupiedSlots = GetNumberOfOccupiedSlots();
}
private IEnumerator CraftingCompleteEstimation(Recipe recipe, float delay, string[] uniqueIdentifier)
{
yield return new WaitForSecondsRealtime(delay);
// FARMING ONLY, as farming is the only skill that will have 'recipe.GetStages' not be equal to zero
// (Other Crafting-UI Based skills will be covered in 'CraftingSystem.cs')
if (recipe.GetStages != null && recipe.GetStages.Length != 0)
{
GameObject.FindWithTag("Player").GetComponent<SkillExperience>().GainExperience(recipe.GetRequiredSkill(), recipe.GetXPReward() * numberOfOccupiedSlots);
// TEST - DELETE IF FAILED
CraftingCompleteEstimationEvent.InvokeOnCraftingCompleteEstimation(uniqueIdentifier);
}
MalbersAnimations.InventorySystem.NotificationManager.Instance.OpenNotification($"Crafting Complete:\n{recipe.name}");
}
AND 7. In ‘PlantGrowthHandler.cs’:
// TEST - DELETE IF FAILED
private GameObject currentInstance;
private Recipe currentRecipe;
// TEST FUNCTION - DELETE IF FAILED
public GameObject GetCurrentInstance()
{
return currentInstance;
}
// TEST FUNCTION - DELETE IF FAILED
public void SetCurrentInstance(GameObject currentInstance)
{
this.currentInstance = currentInstance;
}
// TEST FUNCTION - DELETE IF FAILED
public Recipe GetCurrentRecipe()
{
return currentRecipe;
}
// TEST FUNCTION - DELETE IF FAILED
public void SetCurrentRecipe(Recipe currentRecipe)
{
this.currentRecipe = currentRecipe;
}
void Awake()
{
eventBus = GetComponentInParent<ICraftingEventBus>();
eventBus.RegisterObserver(this);
// TEST - DELETE IF FAILED
CraftingCompleteEstimationEvent.OnCraftingCompleteEstimation += InstantiateFinalProduct;
}
void OnDestroy()
{
eventBus.DeregisterObserver(this);
// TEST - DELETE IF FAILED
CraftingCompleteEstimationEvent.OnCraftingCompleteEstimation -= InstantiateFinalProduct;
}
public void OnCraftingStarted(Recipe recipe, string[] uniqueIdentifier /* <-- TEST - DELETE IF FAILED */)
{
slicePoints = ComputeRandomGrowthSlices(recipe);
StartCoroutine(GrowthRoutine(recipe, recipe.GetCraftDuration()));
// TEST - DELETE IF FAILED
SetCurrentRecipe(recipe);
}
public void OnCraftingResumed(Recipe recipe, float remainingDuration, string[] uniqueIdentifier /* <-- TEST - DELETE IF FAILED */)
{
StopAllCoroutines();
slicePoints = ComputeRandomGrowthSlices(recipe); // Makes sure our Plants don't just grow at the exact same time simultaneously, with a little bit of Randomness
StartCoroutine(GrowthRoutine(recipe, remainingDuration));
// TEST - DELETE IF FAILED
SetCurrentRecipe(recipe);
}
private IEnumerator GrowthRoutine(Recipe recipe, float remainingDuration)
{
// Determine where to start the timer
var totalDuration = recipe.GetCraftDuration();
var startTime = totalDuration - remainingDuration;
Debug.Log($"Remaining duration is: {remainingDuration}");
// Determine the current stage, based on the elapsed time (if any)
var currentStage = DetermineStage(startTime);
// GameObject currentInstance = null; // TEST REPLACED THIS (MAKING IT GLOBAL)
if (currentPlant != null)
{
// Instantiate the first phase, only for plants that are currently being grown (empty slots get nothing)
currentInstance = Instantiate(recipe.GetStages[currentStage], transform.position, Quaternion.identity);
// TEST - DELETE IF FAILED
SetCurrentInstance(currentInstance);
}
for (var timer = startTime; timer / totalDuration <= 1f; timer += Time.unscaledDeltaTime /* <-- using 'unscaledDeltaTime' instead of 'deltaTime' because our notification, crafting and all systems work based on real time, so it has to match */)
{
var newStage = DetermineStage(timer);
if (currentStage != newStage)
{
// Update the current stage index
currentStage = newStage;
// Destroy the old instance
Destroy(currentInstance);
if (currentPlant != null)
{
// Instantiate next phase, only for plants that are currently being grown (empty slots get nothing)
currentInstance = Instantiate(recipe.GetStages[currentStage], transform.position, Quaternion.identity);
// TEST - DELETE IF FAILED
SetCurrentInstance(currentInstance);
}
}
yield return null;
}
/* Destroy(currentInstance); // TEMPORARY TEST IN 'INSTANTIATEFINALPRODUCT' REPLACED THIS - RESTORE THIS BLOCK IF FAILED
if (currentPlant != null)
{
// Instantiate the final phase, only for plants that are being grown (empty slots get nothing)
recipe.GetResult().Item.SpawnPickup(transform.position, 1 /* <-- one output per farming slot * /);
currentPlant = null;
} */
}
// TEST FUNCTION - DELETE IF FAILED
private void InstantiateFinalProduct(string[] uniqueIdentifiers)
{
if (uniqueIdentifiers.Contains(UniqueIdentifier))
{
Destroy(currentInstance);
if (currentPlant != null)
{
Destroy(currentPlant);
}
if (currentRecipe != null)
{
currentRecipe.GetResult().Item.SpawnPickup(transform.position, 1);
}
currentPlant = null;
}
}
and that should do the trick. Time to test it out