Interfaces opcionales
Estas interfaces no son necesarias para el drag & drop básico. Úsalas cuando necesites añadir comportamiento encima de una transferencia normal.
Qué elegir
| Si necesitas... | Usa |
|---|---|
| Bloquear una transferencia por dinero, permisos, propietario del item o estado de la tienda | ITransferDomainHandler |
| Preguntar a un servidor u otro sistema externo antes de transferir | IAsyncTransferDomainHandler |
| Añadir comportamiento especial al soltar sobre un slot ocupado: insertar en contenedor, equipar, abrir un item | IPreRuleOccupiedSlotDropHandler o IPostRuleOccupiedSlotDropHandler |
| Definir distintos límites de stack para distintos items | IStackSizeLimitable |
| Mostrar una descripción del item en un tooltip u otra UI | IDescribable |
ITransferDomainHandler
Usa ITransferDomainHandler cuando la decisión depende de lógica del juego, no solo del slot y del item.
Ejemplos:
- si el jugador tiene suficiente oro para comprar
- si este item puede venderse
- si el item pertenece al jugador
- si la tienda está abierta
- si se pueden mover items entre estos dos contenedores
Normalmente la interface se implementa en un DataBinding:
public class ShopInventoryBinding
: ListInventoryDataBinding<ItemModel, ItemModelAdapter>, ITransferDomainHandler
{
public RuleResult CanStartTransfer(DragContext context, IInventory targetInventory)
{
return _shopIsOpen ? RuleResult.Success() : RuleResult.Failure("Shop is closed");
}
public RuleResult CanCommitTransfer(TransferDomainContext context)
{
return HasEnoughMoney(context)
? RuleResult.Success()
: RuleResult.Failure("Not enough money");
}
public void OnTransferSucceeded(TransferDomainContext context)
{
SpendMoney(context);
}
}
Métodos:
| Método | Cuándo se ejecuta | Para qué sirve |
|---|---|---|
CanStartTransfer |
una vez antes de la transferencia | rechazar toda la operación |
CanCommitTransfer |
antes de confirmar una colocación concreta | comprobar dinero, permisos, propietario y lógica similar |
OnTransferSucceeded |
después de una colocación exitosa | gastar dinero, enviar analytics, actualizar un sistema externo |
No pongas aquí una comprobación normal de "este tipo de item puede ir en este slot". Para eso encajan mejor rules, CanDrop o la configuración de fixed slots.
IAsyncTransferDomainHandler
IAsyncTransferDomainHandler sirve cuando la transferencia debe esperar una respuesta externa.
Por ejemplo:
- el servidor valida la transferencia
- se comprueban datos guardados en disco
- un sistema externo comprueba permisos
public async Task<RuleResult> CanStartTransferAsync(
DragContext context,
IInventory targetInventory,
CancellationToken cancellationToken)
{
return await _serverApi.ValidateTransferAsync(context, cancellationToken);
}
Reglas importantes:
- la comprobación se aplica a toda la transferencia
- se ejecuta antes de modificar inventarios
- si la comprobación falla, la transferencia no empieza
- si la comprobación es rápida y local, normalmente basta con
ITransferDomainHandler
IOccupiedSlotDropHandler
IOccupiedSlotDropHandler sirve cuando soltar sobre un slot ocupado debe significar una acción propia, no swap normal ni colocación alternativa.
Ejemplos:
- soltar un item sobre una bolsa para meterlo dentro
- soltar un item sobre un slot equipado para hacer un reemplazo especial
- soltar una llave sobre un contenedor para abrirlo
No implementes directamente IOccupiedSlotDropHandler. Implementa una de las interfaces de timing:
| Interface | Cuándo se ejecuta | Para qué sirve |
|---|---|---|
IPreRuleOccupiedSlotDropHandler |
antes de las drop rules del slot destino | el drop realmente va dirigido al objeto dentro del slot, por ejemplo un contenedor |
IPostRuleOccupiedSlotDropHandler |
después de las drop rules del slot destino | las reglas normales del destino deben permitir el drop primero |
Normalmente la interface se implementa en el DataBinding del inventario destino:
public class ContainerInventoryBinding
: SlotIndexedInventoryDataBinding<ItemModel, ItemModelAdapter>,
IPreRuleOccupiedSlotDropHandler
{
public bool CheckOccupiedSlotDrop(DragEntry entry, BaseSlot occupiedSlot)
{
return occupiedSlot.Stack?.PrimaryAdapter is ContainerAdapter;
}
public OccupiedSlotDropResult ExecuteOccupiedSlotDrop(DragEntry entry, BaseSlot occupiedSlot)
{
return TryPutIntoContainer(entry, occupiedSlot)
? OccupiedSlotDropResult.Handled
: OccupiedSlotDropResult.Rejected;
}
}
ExecuteOccupiedSlotDrop devuelve:
| Resultado | Qué hace el pipeline |
|---|---|
Handled |
el handler ejecutó la acción; no se ejecutan el drop normal, swap ni colocación alternativa |
Rejected |
el handler rechazó la acción; la transferencia se revierte |
Fallthrough |
el handler decide no interceptar; la transferencia continúa como un drop normal sobre un slot ocupado |
Si el handler devuelve Handled o Rejected, termina ese intento de transferencia. El sistema no ejecuta swap ni colocación alternativa después.
IStackSizeLimitable
IStackSizeLimitable permite que un item concreto defina su propio límite de stack:
Ejemplos:
- pociones con stack de 20
- flechas con stack de 999
- armas con stack de 1
- recursos y herramientas con límites distintos
Para que este límite se use, _allowItemStackOverride debe estar activado en UniversalInventory.
Si _allowItemStackOverride está desactivado, se usa el _maxStackSize general del inventario.
Si está activado y el item implementa IStackSizeLimitable, el límite del item sustituye al límite general.
Ejemplos:
| Configuración | Resultado |
|---|---|
inventario _maxStackSize = 20, override desactivado, item MaxStackSize = 99 |
límite 20 |
inventario _maxStackSize = 20, override activado, item MaxStackSize = 99 |
límite 99 |
inventario _maxStackSize = 20, override activado, item MaxStackSize = 5 |
límite 5 |
IDescribable
IDescribable permite que un adapter entregue una descripción del item para la UI.
public class ItemAdapter : IItemAdapter, IDescribable
{
public string Description => _item.Description;
}
El ejemplo estándar DefaultTooltipView muestra Description cuando el adapter implementa IDescribable.
También puedes usar esta interface en tu propia UI:
- tooltip
- panel de inspección
- hover card
- detalles en menú contextual
Interfaces pequeñas propias
Si IItemAdapter no basta, puedes añadir interfaces pequeñas propias para datos específicos del proyecto.
Por ejemplo:
IItemStatsProviderIRarityProviderIFlavorTextProvider
El core del inventario no depende de ellas. Solo deberían leerlas los sistemas de UI o gameplay que necesiten esos datos.