162 lines
5.7 KiB
C#
162 lines
5.7 KiB
C#
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using Sirenix.OdinInspector;
|
|
using UnityEngine;
|
|
using UnityEngine.AI;
|
|
using VOID.Generic.Objects.Abstract;
|
|
using VOID.Generic.Objects.Abstract.Events;
|
|
using VOID.Generic.Objects.Container;
|
|
using VOID.Generic.Objects.Item;
|
|
using VOID.Generic.Objects.Item.Events;
|
|
using VOID.Generic.Objects.Unit;
|
|
using VOID.Generic.Util;
|
|
|
|
namespace VOID.Generic.DropTable
|
|
{
|
|
public class DropTableComponent : MonoBehaviour
|
|
{
|
|
// Serialized properties
|
|
[InfoBox("<u><b>OnSpawn</b></u> gives items instantly to target, but works only on Unit and Container\n" +
|
|
"<u><b>OnDeath</b></u> gives or spawns when target dies, works on any object")]
|
|
[SerializeField, EnumToggleButtons] private DropTableTrigger _trigger;
|
|
[SerializeField, HideLabel, Title("Drop Table")] private MultiSourceWrapper<DropTable> _dropTable;
|
|
|
|
// Public accessors
|
|
public DropTableTrigger trigger => _trigger;
|
|
public DropTable dropTable => _dropTable.value;
|
|
|
|
// Cached object attached to
|
|
private AbstractObject _obj;
|
|
|
|
/// <summary>
|
|
/// Start listening for attached object events.
|
|
/// If object already spawned and OnSpawn selected then run immediately
|
|
/// </summary>
|
|
private void Start()
|
|
{
|
|
_obj = GetComponent<AbstractObject>();
|
|
if (_trigger is DropTableTrigger.OnSpawn)
|
|
{
|
|
if (_obj.didStart)
|
|
{
|
|
OnSpawn();
|
|
}
|
|
else
|
|
{
|
|
_obj.basicEvents.onSpawn.AddListener(OnSpawn);
|
|
}
|
|
}
|
|
|
|
if (_trigger is DropTableTrigger.OnDeath)
|
|
{
|
|
_obj.basicEvents.onDeathAfter.AddListener(OnDeath);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// Return randomized items from given DropTable
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private List<ItemObject> GetRandomItemsFromDropTable()
|
|
{
|
|
var itemList = new List<ItemObject>();
|
|
|
|
var groups = _dropTable.value.groups.ToList();
|
|
var groupCounter = new Dictionary<DropTableGroup, int>();
|
|
|
|
for (var i = 0; i < _dropTable.value.count; i++)
|
|
{
|
|
var randomGroup = RandomUtil.RandomElementByWeight(groups, group => group.weight);
|
|
itemList.AddRange(randomGroup.items);
|
|
|
|
if (!randomGroup.isLimited) continue;
|
|
groupCounter.TryAdd(randomGroup, 0);
|
|
groupCounter[randomGroup]++;
|
|
if (groupCounter[randomGroup] < randomGroup.limit) continue;
|
|
groups.Remove(randomGroup);
|
|
}
|
|
|
|
return itemList;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Run when attached object spawn
|
|
/// </summary>
|
|
private void OnSpawn()
|
|
{
|
|
_obj.basicEvents.onSpawn.RemoveListener(OnSpawn);
|
|
|
|
if (_obj is UnitObject unitObject)
|
|
{
|
|
GetRandomItemsFromDropTable().ForEach(item => GiveItemToUnit(item, unitObject));
|
|
return;
|
|
}
|
|
|
|
if (_obj is ContainerObject containerObject)
|
|
{
|
|
GetRandomItemsFromDropTable().ForEach(item => GiveItemToContainer(item, containerObject));
|
|
return;
|
|
}
|
|
|
|
Debug.LogError($"{_obj.GetType()} can't contain items! Selected trigger won't work!");
|
|
}
|
|
|
|
/// <summary>
|
|
/// Run when attached object die
|
|
/// </summary>
|
|
private void OnDeath(DeathEvent deathEvent)
|
|
{
|
|
_obj.basicEvents.onDeathAfter.RemoveListener(OnDeath);
|
|
|
|
if (_obj is UnitObject unitObject)
|
|
{
|
|
GetRandomItemsFromDropTable().ForEach(item => GiveItemToUnit(item, unitObject));
|
|
return;
|
|
}
|
|
|
|
GetRandomItemsFromDropTable().ForEach(item => DropItemFromObject(item, _obj));
|
|
}
|
|
|
|
/// <summary>
|
|
/// Instantiate given item prefab and put directly to attached unit's backpack
|
|
/// </summary>
|
|
private void GiveItemToUnit(ItemObject item, UnitObject unit)
|
|
{
|
|
var newItemGameObject = Instantiate(item.gameObject);
|
|
var newItem = newItemGameObject.GetComponent<ItemObject>();
|
|
|
|
var pickEvent = new PickEvent
|
|
{
|
|
item = newItem,
|
|
unit = unit
|
|
};
|
|
|
|
newItem.itemEvents.onPickedBefore.Invoke(pickEvent);
|
|
newItem.itemEvents.onPickedAfter.Invoke(pickEvent);
|
|
|
|
unit.unitBackpack.Take(newItem);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Instantiate given item prefab and put directly to attached container's content
|
|
/// </summary>
|
|
private void GiveItemToContainer(ItemObject item, ContainerObject container)
|
|
{
|
|
var newItemGameObject = Instantiate(item.gameObject);
|
|
var newItem = newItemGameObject.GetComponent<ItemObject>();
|
|
container.containerContent.MoveIn(newItem);
|
|
}
|
|
|
|
/// <summary>
|
|
/// Instantiate given item prefab and "pop" it randomly near attached object
|
|
/// </summary>
|
|
private void DropItemFromObject(ItemObject item, AbstractObject obj)
|
|
{
|
|
const float dropDistance = 1f;
|
|
var newItemGameObject = Instantiate(item, obj.transform.position, obj.transform.rotation);
|
|
var newItem = newItemGameObject.GetComponent<ItemObject>();
|
|
NavMesh.SamplePosition(obj.transform.position + Random.insideUnitSphere, out var hit, dropDistance, NavAreaManager.Walkable);
|
|
if (hit.hit) newItem.Move(hit.position);
|
|
}
|
|
}
|
|
} |