Skip to content

Items System

The items system in RPGJS allows you to manage player inventory, equipment, and item usage. You can add items to players in three different ways: using string IDs, item classes, or item objects.

Adding Items

Method 1: String ID (Pre-registered Items)

Items must be registered in the database property of provideServerModules before they can be used with string IDs.

ts
import { createServer, provideServerModules, RpgPlayer } from '@rpgjs/server'
import { Item } from '@rpgjs/database'

@Item({
  name: 'Potion',
  description: 'Restores 100 HP',
  price: 200,
  hpValue: 100,
  consumable: true
})
export class Potion {
  onAdd(player: RpgPlayer) {
    console.log('Potion added to inventory');
  }
  
  onUse(player: RpgPlayer) {
    player.hp += 100;
  }
}

export default createServer({
  providers: [
    provideServerModules([
      {
        database: {
          Potion  // Register the item in the database
        },
        player: {
          // ... your player hooks
        }
      }
    ])
  ]
});

Then you can use it:

ts
// In your server hooks or events
player.addItem('Potion', 5);  // Add 5 potions

Note: The @RpgModule decorator is available for backward compatibility with v4, but the modern approach is to use provideServerModules with the database property directly.

Method 2: Item Class (Auto-registered)

When using an item class directly, it will be automatically added to the map's database if not already present.

ts
import Potion from './database/Potion'

// The class is automatically registered in the map database
player.addItem(Potion, 3);

Method 3: Item Object (Dynamic Items)

You can create items dynamically using objects. These are automatically added to the map's database.

ts
// Create a dynamic item
player.addItem({
  id: 'custom-potion',
  name: 'Custom Potion',
  description: 'A special potion',
  price: 150,
  hpValue: 50,
  consumable: true,
  onAdd(player) {
    console.log('Custom potion added!');
  },
  onUse(player) {
    player.hp += 50;
    player.showText('You feel better!');
  }
}, 2);

If you don't provide an id, one will be auto-generated:

ts
// Auto-generated ID
player.addItem({
  name: 'Dynamic Item',
  price: 100,
  onUse(player) {
    console.log('Dynamic item used!');
  }
});

Dynamic Item Creation in Events

When creating items dynamically (especially in events), you should first add them to the map's database, then add them to the player.

Example: Event Giving an Item

ts
import { RpgPlayer, RpgMap } from '@rpgjs/server'

export function ChestEvent() {
  return {
    name: "CHEST-1",
    onInit() {
      this.setGraphic("chest");
    },
    async onAction(player: RpgPlayer) {
      const map = player.getCurrentMap();
      
      // Step 1: Add the item to the map database
      const itemData = {
        id: 'treasure-coin',
        name: 'Treasure Coin',
        description: 'A rare coin found in a chest',
        price: 500,
        onAdd(player) {
          player.showText('You found a rare coin!');
        },
        onUse(player) {
          player.gold += 500;
          player.showText('You sold the coin for 500 gold!');
        }
      };
      
      // Add to database first
      map.addInDatabase('treasure-coin', itemData);
      
      // Step 2: Add the item to the player
      player.addItem('treasure-coin', 1);
      
      // Or add directly with the object (it will auto-register)
      // player.addItem(itemData, 1);
    }
  };
}

Using String ID After Dynamic Registration

If you want to use a string ID after dynamically adding an item:

ts
// In an event
async onAction(player: RpgPlayer) {
  const map = player.getCurrentMap();
  
  // Create and register the item
  const specialItem = {
    id: 'quest-item-1',
    name: 'Ancient Artifact',
    description: 'A mysterious artifact',
    price: 0,  // Cannot be sold
    consumable: false
  };
  
  // Register in database
  map.addInDatabase('quest-item-1', specialItem);
  
  // Now you can use string ID
  player.addItem('quest-item-1', 1);
  
  // Later, you can also use the string ID
  if (player.hasItem('quest-item-1')) {
    console.log('Player has the quest item!');
  }
}

Item Hooks

Items can implement lifecycle hooks that are called at specific moments:

  • onAdd(player): Called when the item is added to inventory
  • onUse(player): Called when the item is successfully used
  • onUseFailed(player): Called when item usage fails (e.g., chance roll)
  • onRemove(player): Called when the item is removed from inventory
  • onEquip(player, equip): Called when the item is equipped/unequipped

Using Classes

ts
import { Item } from '@rpgjs/database'
import { createServer, provideServerModules, RpgPlayer } from '@rpgjs/server'

@Item({
  name: 'Magic Sword',
  price: 1000
})
export class MagicSword {
  onAdd(player: RpgPlayer) {
    player.showText('You obtained the Magic Sword!');
  }
  
  onEquip(player: RpgPlayer, equip: boolean) {
    if (equip) {
      player.showText('Magic Sword equipped!');
      // Add special effects
    } else {
      player.showText('Magic Sword unequipped!');
    }
  }
  
  onUse(player: RpgPlayer) {
    // This won't be called if consumable is false
    player.showText('You cannot use this item!');
  }
}

// Register in provideServerModules
export default createServer({
  providers: [
    provideServerModules([
      {
        database: {
          MagicSword
        }
      }
    ])
  ]
});

Using Objects

ts
import { createServer, provideServerModules, RpgPlayer } from '@rpgjs/server'

const magicSword = {
  id: 'magic-sword',
  name: 'Magic Sword',
  price: 1000,
  onAdd(player: RpgPlayer) {
    player.showText('You obtained the Magic Sword!');
  },
  onEquip(player: RpgPlayer, equip: boolean) {
    if (equip) {
      player.showText('Magic Sword equipped!');
    } else {
      player.showText('Magic Sword unequipped!');
    }
  }
};

// Register in provideServerModules
export default createServer({
  providers: [
    provideServerModules([
      {
        database: {
          'magic-sword': magicSword  // Use string key for object items
        }
      }
    ])
  ]
});

Managing Database

Adding Items to Database

ts
const map = player.getCurrentMap();

// Add item class
map.addInDatabase('Potion', PotionClass);

// Add item object
map.addInDatabase('custom-item', {
  name: 'Custom Item',
  price: 100
});

// Force overwrite existing item
map.addInDatabase('Potion', UpdatedPotionClass, { force: true });

Removing Items from Database

ts
const map = player.getCurrentMap();

// Remove item
const removed = map.removeInDatabase('Potion');
if (removed) {
  console.log('Item removed successfully');
}

Complete Example: Shop Event

ts
import { createServer, provideServerModules, RpgPlayer, RpgMap } from '@rpgjs/server'
import { Item } from '@rpgjs/database'

// Define shop items as classes (optional, can also be objects)
@Item({
  name: 'Shop Potion',
  description: 'A potion sold in shops',
  price: 200,
  hpValue: 100,
  consumable: true
})
export class ShopPotion {
  onUse(player: RpgPlayer) {
    player.hp += 100;
    player.showText('HP restored!');
  }
}

@Item({
  name: 'Iron Sword',
  description: 'A basic sword',
  price: 500
})
export class IronSword {
  onEquip(player: RpgPlayer, equip: boolean) {
    if (equip) {
      player.showText('Iron Sword equipped!');
    }
  }
}

export function ShopEvent() {
  return {
    name: "SHOP-1",
    onInit() {
      this.setGraphic("shopkeeper");
    },
    async onAction(player: RpgPlayer) {
      const map = player.getCurrentMap();
      
      // Option 1: Use pre-registered items from database
      await player.showText('Welcome to my shop!');
      try {
        player.buyItem('ShopPotion', 1);
        player.showText('Thank you for your purchase!');
      } catch (err) {
        player.showText('Not enough gold!');
      }
      
      // Option 2: Create dynamic items on the fly
      const dynamicItem = {
        id: 'limited-edition-potion',
        name: 'Limited Edition Potion',
        description: 'A rare potion',
        price: 500,
        hpValue: 200,
        consumable: true,
        onUse(player) {
          player.hp += 200;
          player.showText('Super HP restored!');
        }
      };
      
      // Register in database first
      map.addInDatabase('limited-edition-potion', dynamicItem);
      
      // Then add to player
      player.addItem('limited-edition-potion', 1);
    }
  };
}

export default createServer({
  providers: [
    provideServerModules([
      {
        database: {
          ShopPotion,  // Pre-registered items
          IronSword
        },
        player: {
          // ... your player hooks
        },
        maps: [
          {
            id: "town",
            events: [{ event: ShopEvent() }]
          }
        ]
      }
    ])
  ]
});