sure, here you go (P.S: the dead code is either comments, or was mathematical experiments for me to determine how I want this to go):
Here is my first one, ‘ResourceGathering.cs’ (which is placed on the resource source, like a tree or something):
using UnityEngine;
using GameDevTV.Inventories;
using RPG.Control;
using RPG.Skills;
using UnityEngine.AI;
using RPG.Core;
// Steps to get the experience:
// 1. Declare a 'SkillExperience' variable
// 2. get the component of the variable in 'Awake()'
// 3. Split the type of XP you get, based on the tag... for trees, it's "Tree", and for Rocks, it's "ResourceRocks"
// 4. When gathering, call 'skillExperience.GainExperience()', so you get the experience you are supposed to get when gathering resources
// 5. Play and test multiple times...
namespace RPG.ResourceManager
{
public class ResourceGathering : MonoBehaviour, ITarget//, IRaycastable
{
[SerializeField] InventoryItem resourceToAccumulate; // the loot we are getting from the tree, rock, or whatever we're harvesting
[SerializeField] int maxResourcesPerHit; // how many of that loot is being acquired, per mouse click
public int quantityLeft; // how many resources are in that source, before it's destroyed and re-instantiated
Inventory playerInventory; // the target inventory for our resources to go to (the player's inventory in this case)
internal bool isDestroyed = false; // flag, to kill the source if it's out of resources
bool isMovingTowardsResourceObject = false; // flag, to ensure that we are close to the source of the resources, before being able to gather resources from it
// skill experience link, to get the skill, and fine-tune it according to the trained skill
SkillExperience skillExperience;
[SerializeField] Skill associatedSkill;
[SerializeField] int experienceValue;
[SerializeField] CursorType cursorType;
[SerializeField] ResourceGatherer resourceGatherer;
[SerializeField] public int minSkillRequired;
[SerializeField] public int maxSkillRequired;
SkillStore skillStore;
private NavMeshAgent navMeshAgent;
private int levelToUnlock;
void Awake()
{
playerInventory = Inventory.GetPlayerInventory();
if (playerInventory == null) Debug.Log("Player Inventory Not Found");
// In very simple terms, when you got the playerInventory, you got a hold of the only in-game
// component that actually has an 'Inventory.cs' script on him, the player.
// As the player has a hold of the 'SkillExperience' script as well, you can catch it
// through his inventory as well, since you got a hold of him through his inventory,
// so that's what I did below:
skillExperience = playerInventory.GetComponent<SkillExperience>();
// Just like how we accumulated 'skillExperience' above, we accumulate
// 'skillStore' as well with the exact same method (getting the object (Player)
// that the only inventory in the game is attached to):
skillStore = playerInventory.GetComponent<SkillStore>();
isMovingTowardsResourceObject = false;
resourceGatherer = playerInventory.GetComponent<ResourceGatherer>();
}
public int GetMaxSkillRequired() {
return maxSkillRequired;
}
public int GetMinSkillRequired() {
return minSkillRequired;
}
public int GetMaxResourcesPerHit() {
return maxResourcesPerHit;
}
public int GetQuantityLeft() {
return quantityLeft;
}
public InventoryItem ResourceToAccumulate() {
return resourceToAccumulate;
}
public void DestroyResource() {
Destroy(gameObject);
isDestroyed = true;
}
public Skill GetAssociatedSkill() {
return associatedSkill;
}
public int GetExperienceValue() {
return experienceValue;
}
public void AccumulateXP()
{
skillExperience.GainExperience(associatedSkill, experienceValue);
}
public int LevelToUnlock()
{
return levelToUnlock = minSkillRequired;
}
bool ITarget.IsValid()
{
return quantityLeft > 0;
}
/* public CursorType GetCursorType()
{
if (isDestroyed == true) return CursorType.None;
return cursorType;
}
public bool HandleRaycast(PlayerController callingController) {
if (Input.GetMouseButtonDown(0)) {
// if you don't have enough space, don't start resource gathering
if(!Inventory.GetPlayerInventory().HasSpaceFor(resourceToAccumulate)) return false;
// if you're not high enough of a level to touch a resource, or don't have the right weapon, don't start
if (skillStore.GetSkillLevel(associatedSkill) < LevelToUnlock()) return false; // place an 'or' ("||")statement later to count for holding a weapon associated to the skill (hatchet for trees, pickaxe for mining, etc), by checking the 'equipLocation.Weapon'
// if all is well so far, start gathering the resources
resourceGatherer.Gather(this);
}
return true;
} */
}
}
‘ResourceGatherer.cs’ (this one goes on the player, so he can interact with the environment):
using GameDevTV.Inventories;
using RPG.Core;
using RPG.Movement;
using RPG.ResourceManager;
using RPG.Skills;
using UnityEngine;
public class ResourceGatherer : MonoBehaviour, IAction
{
[SerializeField] int acceptanceRadius;
[SerializeField] float timeBetweenGathers;
private ResourceGathering target;
private float timeSinceLastGather = Mathf.Infinity;
private SkillStore skillStore;
private Inventory playerInventory;
private SkillExperience skillExperience;
private void Awake() {
skillStore = GetComponent<SkillStore>();
playerInventory = GetComponent<Inventory>();
skillExperience = GetComponent<SkillExperience>();
}
private void Update() {
timeSinceLastGather += Time.deltaTime;
if (target == null) return;
if (!GetIsInRange(target.transform)) {
GetComponent<Mover>().MoveTo(target.transform.position, 1.0f);
}
else {
GetComponent<Mover>().Cancel();
GatherBehaviour();
}
}
private void TriggerGatheringAnimation() {
if (GetAssociatedSkill() == Skill.Woodcutting) {
playerInventory.GetComponent<Animator>().ResetTrigger("stopCuttingWood");
playerInventory.GetComponent<Animator>().SetTrigger("cutWood");
}
else if (GetAssociatedSkill() == Skill.Mining) {
playerInventory.GetComponent<Animator>().ResetTrigger("stopMiningRocks");
playerInventory.GetComponent<Animator>().SetTrigger("mineRocks");
}
// you get the drill for the rest...
}
public void Gather(ResourceGathering target) {
GetComponent<ActionSchedular>().StartAction(this);
this.target = target;
}
private bool GetIsInRange(Transform targetTransform) {
return Vector3.Distance(transform.position, targetTransform.position) < acceptanceRadius;
}
private void GatherBehaviour() {
// Using the 'LookAt()' we invented below, instead of 'transform.LookAt()',
// to avoid the character from sloping his y-axis values, the 'Michael Jackson' effect:
LookAt(target.transform.position);
/* if (timeSinceLastGather < timeBetweenGathers) return;
TriggerGatheringAnimation();
Gather();
timeSinceLastGather = 0f; */
if (timeSinceLastGather > timeBetweenGathers) {
TriggerGatheringAnimation();
Gather();
timeSinceLastGather = 0;
}
}
void LookAt(Vector3 position) {
position.y = transform.position.y;
transform.LookAt(position);
}
public void Gather() {
// Steps for the 'Gather()' function:
// 1. If your source is not out of resources yet, then start off by playing the 'gathering' animation
// 2. get the players' Skill level for the skill (Woodcutting, Mining, Farming, etc)
// 3. Get the probability of the player getting 'maxResourcesPerHit', each time he strikes the resource (the higher the level, the closer the player gets to 'maxResourcePerHit')
// 4. If the player gets a resource, determine (out of a max value of 'maxResourcesPerHit') how many resources he can get, based on his resource-gathering level
// 5. ensure that the player does NOT get more than the resources left in the source (so he can't get 6 logs if the tree only has 2 more left for example)
// 6. Give the player the resource, deduct it from the source, and then give him XP for it
// 7. If the source is out of resources, kill it and stop the 'gathering' animation
if (target.quantityLeft > 0) {
TriggerGatheringAnimation();
var playerSkillLevel = skillStore.GetSkillLevel(target.GetAssociatedSkill());
var playerDropChance = Mathf.Clamp01((playerSkillLevel - target.minSkillRequired)/(float)(target.maxSkillRequired - target.minSkillRequired));
if (Random.value <= playerDropChance) {
int amountToGive = DetermineAmountToGive(playerSkillLevel, target.GetMinSkillRequired(), target.GetMaxSkillRequired(), target.GetMaxResourcesPerHit());
amountToGive = Mathf.Min(amountToGive, target.GetQuantityLeft());
playerInventory.AddToFirstEmptySlot(target.ResourceToAccumulate(), amountToGive);
target.quantityLeft -= amountToGive;
AccumulateXP();
}
}
if (target.quantityLeft <= 0) {
target.DestroyResource();
StopGatheringAnimation();
}
}
private int DetermineAmountToGive(int playerSkillLevel, int minLevel, int maxLevel, int maxResourcesPerHit) {
// float randomValue = Random.value * (playerSkillLevel - minLevel);
float randomValue = Mathf.CeilToInt(Random.value * (playerSkillLevel - minLevel));
float fractionPerLevel = maxResourcesPerHit / (maxLevel - minLevel);
float amountToGive = 1f;
// for (int i = 0; i < (maxLevel - minLevel); i++) {
// changing 'randomValue -= i' to 'randomValue -= 1' below
// ensures that the "CHANCES" of us getting more than a log at a time at higher
// levels are properly calculated:
// randomValue -= 1;
// amountToGive += fractionPerLevel;
amountToGive += randomValue * fractionPerLevel;
// if (randomValue <= 0f) break;
// }
// return Mathf.FloorToInt(amountToGive);
return Mathf.FloorToInt(Mathf.Min(amountToGive, maxResourcesPerHit));
}
private void AccumulateXP() {
skillExperience.GainExperience(GetAssociatedSkill(), GetExperienceValue());
}
public Skill GetAssociatedSkill() {
return target.GetAssociatedSkill();
}
public int GetExperienceValue() {
return target.GetExperienceValue();
}
private void StopGatheringAnimation() {
if (GetAssociatedSkill() == Skill.Woodcutting) {
GetComponent<Animator>().ResetTrigger("cutWood");
GetComponent<Animator>().SetTrigger("stopCuttingWood");
}
else if (GetAssociatedSkill() == Skill.Mining) {
GetComponent<Animator>().ResetTrigger("mineRocks");
GetComponent<Animator>().SetTrigger("stopMiningRocks");
}
// you get the drill for the rest...
}
public void Cancel()
{
StopGatheringAnimation();
target = null;
GetComponent<Mover>().Cancel();
}
}
‘ResourceRespawner.cs’ (this one goes on the parent of the resource, to ensure it waits for some time and respawns, and assigns values accordingly. This one is a major headache with the NavMesh Agent, mainly because everytime I bake it, I have to deactivate and re-activate the children trees):
using System.Collections;
using UnityEngine;
namespace RPG.ResourceManager
{
public class ResourceRespawner : MonoBehaviour
{
[SerializeField] GameObject resourceToRespawn;
[SerializeField] int hideTime;
[SerializeField] int originalQuantity = 10;
private GameObject resourceObject;
private ResourceGathering resourceGathering;
private bool isRespawning = false;
void Start()
{
// Initialize the resourceGathering component
resourceGathering = GetComponent<ResourceGathering>();
// Spawn the initial tree
SpawnSource();
}
void Update()
{
// Check if the current tree is destroyed and respawn it
if (resourceObject == null && !isRespawning)
{
StartCoroutine(RespawnAfterDelay());
}
}
IEnumerator RespawnAfterDelay()
{
isRespawning = true;
yield return new WaitForSeconds(hideTime);
SpawnSource();
isRespawning = false;
}
private void SpawnSource()
{
// Instantiate a new tree ('Quaternion.identity' = no Rotation assigned - Success):
resourceObject = Instantiate(resourceToRespawn, transform.position, Quaternion.identity);
// Make the current tree a child of this GameObject (the Tree Respawner - Success):
resourceObject.transform.parent = transform;
// Assign a tag to the spawned resource source - NOT NECESSARY THOUGH (Success):
// resourceObject.gameObject.tag = "ResourceTree";
// Access the ResourceGathering script on the instantiated object and set its properties (Success):
ResourceGathering resourceGathering = resourceObject.GetComponent<ResourceGathering>();
// If you have a new source for a specific resource, reset the values for another round of resource gathering:
if (resourceGathering != null)
{
// Reset the quantity left for resource gathering
resourceGathering.quantityLeft = originalQuantity; // Reset the quantity for the next resource instance
resourceGathering.isDestroyed = false; // Reset the IsDestroyed flag
}
}
}
}
‘ResourceDataStructure.cs’, which only helps us create Resource Items in the game engine:
using UnityEngine;
using GameDevTV.Inventories;
[CreateAssetMenu(fileName = "Resource Data", menuName = "Resources", order = 0)]
public class ResourceDataStructure : InventoryItem
{
// Creating a Resource to gather as an Inventory Item, nothing more
}
and ‘ResourceFinder.cs’, which was supposed to be my first step to the new third person integration:
using System.Collections;
using System.Collections.Generic;
using RPG.Core;
using UnityEngine;
using System.Collections;
using System.Collections.Generic;
using RPG.Core;
using UnityEngine;
namespace RPG.ResourceManager {
public class ResourceFinder : RangeFinder<ResourceGathering> {}
}
(P.S: I am really sorry for the huge confusion)
lol I’m probably not having any of these anytime soon - but hey, enjoy your night
Edit: I think Brian is trying to break the ‘longest date night ever’ record…