Files
2026-07-09 21:33:33 +02:00

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);
}
}
}