Documentation Index
Fetch the complete documentation index at: https://mintlify.com/Bill3621/CustomItems/llms.txt
Use this file to discover all available pages before exploring further.
Overview
The Pocket Mirror is a custom item that allows players to teleport to and from the Pocket Dimension. It demonstrates how to:
- React to coin flip events
- Check player location and status
- Teleport players between zones
- Use delayed actions for item mechanics
- Validate item possession before executing effects
Features
- Teleports players into the Pocket Dimension when flipped
- Teleports players out of the Pocket Dimension without penalty
- 1.5 second delay before teleportation occurs
- Based on the Coin item type
- Item is consumed after use when in Pocket Dimension
- Safety checks prevent unintended behavior
Implementation
Full Source Code
using CustomPlayerEffects;
using LabApi.Events.Arguments.PlayerEvents;
using LabApi.Events.Handlers;
using LabApi.Features.Wrappers;
using MapGeneration;
using MEC;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
namespace CustomItems.API.Example;
public class PocketMirror : CustomItem
{
public override string Name => "Pocket Mirror";
public override string Description => "When used, teleport to the Pocket Dimension. If already in the pocket dimension, get out without any cost.";
public override ItemType Type => ItemType.Coin;
public override void OnRegistered()
{
PlayerEvents.FlippedCoin += OnFlippedCoin;
}
public override void OnUnregistered()
{
PlayerEvents.FlippedCoin -= OnFlippedCoin;
}
private void OnFlippedCoin(PlayerFlippedCoinEventArgs ev)
{
if (PocketDimension.IsPlayerInside(ev.Player))
{
Timing.CallDelayed(1.5f, () =>
{
if (!ev.Player.IsAlive) return;
if (!PocketDimension.IsPlayerInside(ev.Player)) return;
if (!ev.Player.Items.Any(item => item is not null && item.Serial == ev.CoinItem.Serial)) return;
PocketDimension.ForceExit(ev.Player);
ev.Player.RemoveItem(ev.CoinItem);
});
return;
}
Timing.CallDelayed(1.5f, () =>
{
if (!ev.Player.IsAlive) return;
if (PocketDimension.IsPlayerInside(ev.Player)) return;
if (!ev.Player.Items.Any(item => item is not null && item.Serial == ev.CoinItem.Serial)) return;
PocketDimension.ForceInside(ev.Player);
});
}
}
How It Works
Event Registration
The item registers for the FlippedCoin event:
public override void OnRegistered()
{
PlayerEvents.FlippedCoin += OnFlippedCoin;
}
The Pocket Mirror uses coin flip events because the base item type is ItemType.Coin. This leverages the built-in coin flip animation.
Location-Based Behavior
The handler checks if the player is already in the Pocket Dimension:
private void OnFlippedCoin(PlayerFlippedCoinEventArgs ev)
{
if (PocketDimension.IsPlayerInside(ev.Player))
{
// Escape logic
}
else
{
// Enter logic
}
}
Escaping the Pocket Dimension
When used inside the Pocket Dimension:
if (PocketDimension.IsPlayerInside(ev.Player))
{
Timing.CallDelayed(1.5f, () =>
{
if (!ev.Player.IsAlive) return;
if (!PocketDimension.IsPlayerInside(ev.Player)) return;
if (!ev.Player.Items.Any(item => item is not null && item.Serial == ev.CoinItem.Serial)) return;
PocketDimension.ForceExit(ev.Player);
ev.Player.RemoveItem(ev.CoinItem);
});
return;
}
Safety checks:
- IsAlive: Prevents errors if player died
- IsPlayerInside: Ensures player is still in Pocket Dimension
- Item possession: Verifies player still has the specific coin
The serial number check prevents issues if the player dropped or swapped the coin during the delay.
Entering the Pocket Dimension
When used outside the Pocket Dimension:
Timing.CallDelayed(1.5f, () =>
{
if (!ev.Player.IsAlive) return;
if (PocketDimension.IsPlayerInside(ev.Player)) return;
if (!ev.Player.Items.Any(item => item is not null && item.Serial == ev.CoinItem.Serial)) return;
PocketDimension.ForceInside(ev.Player);
});
Key differences:
- Uses
ForceInside() instead of ForceExit()
- Does NOT remove the item (can be used again to escape)
- Same safety checks apply
The item is only consumed when escaping the Pocket Dimension, making it a one-way ticket in but providing a safe exit.
Key Patterns
Conditional Item Behavior
The Pocket Mirror changes behavior based on player location:
if (PocketDimension.IsPlayerInside(ev.Player))
{
// Exit behavior + consume item
}
else
{
// Enter behavior + keep item
}
This pattern is useful for:
- Context-sensitive items
- Toggle mechanics
- Location-based utilities
Delayed Action Safety
All delayed actions include safety checks:
Timing.CallDelayed(1.5f, () =>
{
if (!ev.Player.IsAlive) return; // Player died
if (wrongLocation) return; // Player moved
if (!hasItem) return; // Item was dropped/removed
// Safe to execute effect
});
Always validate conditions in delayed callbacks. Game state can change significantly in 1.5 seconds.
Serial Number Validation
Verifying the exact item instance:
if (!ev.Player.Items.Any(item => item is not null && item.Serial == ev.CoinItem.Serial)) return;
This prevents issues when:
- Player picks up multiple coins
- Player drops and picks up different coins
- Item is removed/replaced during delay
Usage Example
var pocketMirror = new PocketMirror();
pocketMirror.Register();
// Give to a player
pocketMirror.Give(player);
// Scenario 1: Outside Pocket Dimension
// - Player flips coin
// - After 1.5 seconds, teleported into Pocket Dimension
// - Item remains in inventory
// Scenario 2: Inside Pocket Dimension
// - Player flips coin
// - After 1.5 seconds, teleported out safely
// - Item is removed from inventory
Advanced Patterns
Location Checks
The Pocket Dimension utility provides useful methods:
PocketDimension.IsPlayerInside(player); // Check location
PocketDimension.ForceInside(player); // Teleport in
PocketDimension.ForceExit(player); // Teleport out (safe exit)
Asymmetric Item Consumption
The Pocket Mirror demonstrates asymmetric consumption:
- Entering: Item is preserved
- Exiting: Item is consumed
This creates interesting gameplay:
- Players can safely explore the Pocket Dimension
- Escape has a cost (the item)
- Encourages strategic use
Customization Ideas
Cooldown Between Uses
private Dictionary<int, DateTime> lastUsed = new();
private void OnFlippedCoin(PlayerFlippedCoinEventArgs ev)
{
if (lastUsed.TryGetValue(ev.Player.Id, out DateTime last))
{
if ((DateTime.Now - last).TotalSeconds < 30)
{
ev.Player.ShowHint("Mirror is recharging...");
return;
}
}
lastUsed[ev.Player.Id] = DateTime.Now;
// ... rest of logic
}
Add Visual Effects
Timing.CallDelayed(1.5f, () =>
{
// Add hint before teleportation
ev.Player.ShowHint("The mirror shows another dimension...", 3);
PocketDimension.ForceInside(ev.Player);
});
Consume on Both Uses
// Remove item after entering too
PocketDimension.ForceInside(ev.Player);
ev.Player.RemoveItem(ev.CoinItem);