Skip to main content

Items & Mobiles

This guide covers the most common content creation tasks: building items and creatures for your shard.


Creating an Item

Minimal Item

Every item needs [SerializationGenerator], a partial class, and a [Constructible] constructor:

using ModernUO.Serialization;

namespace Server.Items;

[SerializationGenerator(0)]
public partial class SimpleItem : Item
{
[Constructible]
public SimpleItem() : base(0x1234)
{
Weight = 1.0;
}

public override string DefaultName => "a simple item";
}
  • 0x1234 is the item graphic ID from UO art files.
  • DefaultName sets the tooltip name. Use LabelNumber for cliloc-based names instead.

Full Item Example

A complete item with serialized fields, a timer, property list, and double-click behavior:

using ModernUO.Serialization;

namespace Server.Items;

[SerializationGenerator(0)]
public partial class MagicLantern : Item
{
[SerializableField(0)]
[InvalidateProperties]
[SerializedCommandProperty(AccessLevel.GameMaster)]
private int _charges;

[SerializableField(1)]
[SerializedCommandProperty(AccessLevel.GameMaster)]
private Mobile _owner;

private TimerExecutionToken _glowTimer;

[Constructible]
public MagicLantern() : base(0xA25)
{
_charges = Utility.RandomMinMax(5, 15);
Weight = 2.0;
Light = LightType.Circle300;
StartGlow();
}

public override string DefaultName => "a magic lantern";

private void StartGlow()
{
Timer.StartTimer(TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(3), Glow, out _glowTimer);
}

[AfterDeserialization]
private void AfterDeserialization() => StartGlow();

public override void OnAfterDelete()
{
_glowTimer.Cancel();
base.OnAfterDelete();
}

private void Glow()
{
if (_charges > 0)
{
Effects.SendLocationParticles(this, 0x376A, 9, 10, 5042);
}
}

public override void OnDoubleClick(Mobile from)
{
if (!IsChildOf(from.Backpack))
{
from.SendLocalizedMessage(1042001); // Must be in your backpack
return;
}

if (_charges <= 0)
{
from.SendMessage("The lantern is depleted.");
return;
}

Charges--;
from.SendMessage("The lantern flares brightly!");
from.FixedParticles(0x376A, 9, 32, 5042, EffectLayer.Waist);
}

public override void GetProperties(IPropertyList list)
{
base.GetProperties(list);
list.Add(1060741, $"{_charges}"); // "charges: ~1_val~"
}
}

Key patterns:

  • TimerExecutionToken is never serialized -- restart it in [AfterDeserialization].
  • [InvalidateProperties] auto-refreshes the tooltip when Charges changes.
  • [SerializedCommandProperty] exposes the field to the [Props gump for GMs.
  • OnAfterDelete cancels the timer to prevent it firing on a deleted entity.

Common Base Classes

Base ClassUse For
ItemGeneric items
BaseWeaponMelee weapons
BaseRangedRanged weapons (bows, crossbows)
BaseArmorArmor pieces
BaseShieldShields
BaseClothingWearable clothing
BaseJewelRings, bracelets, necklaces
BaseContainerContainers (bags, boxes, chests)
BasePotionPotions
FoodEdible items
SpellScrollSpell scrolls

Key Item Properties

Set these in the constructor:

Weight = 1.0;                    // Weight in stones
Stackable = true; // Can stack with same type
Amount = 1; // Stack amount
Movable = true; // Can be picked up
Hue = 0; // Color (0 = default)
LootType = LootType.Regular; // Regular, Newbied, Blessed, Cursed
Layer = Layer.OneHanded; // Equipment layer
Light = LightType.Circle300; // Light emission

Creating a Creature

Basic Creature

Creatures extend BaseCreature and define stats, resistances, skills, and loot:

using ModernUO.Serialization;
using Server.Items;

namespace Server.Mobiles;

[SerializationGenerator(0)]
public partial class ForestWolf : BaseCreature
{
[Constructible]
public ForestWolf() : base(AIType.AI_Melee, FightMode.Closest)
{
Body = 225;
BaseSoundID = 0xE5;

SetStr(80, 120);
SetDex(90, 110);
SetInt(20, 40);

SetHits(60, 80);
SetMana(0);

SetDamage(8, 14);

SetDamageType(ResistanceType.Physical, 100);

SetResistance(ResistanceType.Physical, 25, 35);
SetResistance(ResistanceType.Fire, 5, 10);
SetResistance(ResistanceType.Cold, 15, 25);
SetResistance(ResistanceType.Poison, 10, 15);
SetResistance(ResistanceType.Energy, 5, 10);

SetSkill(SkillName.MagicResist, 30.0, 50.0);
SetSkill(SkillName.Tactics, 50.0, 70.0);
SetSkill(SkillName.Wrestling, 50.0, 70.0);

Fame = 600;
Karma = 0;

VirtualArmor = 28;

Tamable = true;
ControlSlots = 1;
MinTameSkill = 50.1;
}

public override string CorpseName => "a wolf corpse";
public override string DefaultName => "a forest wolf";
public override int Meat => 1;
public override int Hides => 6;
public override HideType HideType => HideType.Regular;
public override FoodType FavoriteFood => FoodType.Meat;
public override PackInstinct PackInstinct => PackInstinct.Canine;

public override void GenerateLoot()
{
AddLoot(LootPack.Meager);
}
}

Optional Creature Overrides

public override Poison PoisonImmune => Poison.Regular;
public override Poison HitPoison => Poison.Lesser;
public override double HitPoisonChance => 0.2;
public override bool CanRummageCorpses => true;
public override bool BardImmune => true;
public override bool Unprovokable => true;
public override bool CanFly => true;
public override int TreasureMapLevel => 3;
public override double WeaponAbilityChance => 0.4;

AI Types

AITypeUse For
AI_MeleeWarriors, melee fighters
AI_MageSpellcasters
AI_ArcherRanged attackers
AI_AnimalPassive animals (flee when hurt)
AI_PredatorHunting animals
AI_HealerHealing NPCs
AI_VendorShop NPCs

Fight Modes

FightModeBehavior
NoneNever attacks
AggressorOnly retaliates when attacked
StrongestTargets highest-stat enemy
WeakestTargets lowest-stat enemy
ClosestTargets nearest enemy
EvilAttacks aggressors or evil-karma targets

Creature Stats Guide

Use these ranges as a baseline when creating creatures:

LevelStrDexIntHitsDamageFame
Weak30--6030--5010--2020--402--6100--300
Average80--12060--9020--4060--1006--14500--1,500
Strong150--25080--12050--100120--20012--222,000--5,000
Elite300--500100--150100--200250--50018--305,000--15,000
Boss500--1,000150--250200--400500--2,00025--4015,000+

Loot System

Predefined Loot Packs

Use AddLoot in GenerateLoot() to assign standard loot tiers:

public override void GenerateLoot()
{
AddLoot(LootPack.Poor); // ~50 gold equivalent
AddLoot(LootPack.Meager); // ~100 gold equivalent
AddLoot(LootPack.Average); // ~250 gold equivalent
AddLoot(LootPack.Rich); // ~500 gold equivalent
AddLoot(LootPack.FilthyRich); // ~1,000 gold equivalent
AddLoot(LootPack.UltraRich); // ~2,000 gold equivalent
AddLoot(LootPack.SuperBoss); // Boss-level loot

// Auxiliary packs
AddLoot(LootPack.Gems, 2); // 2 random gems
AddLoot(LootPack.Potions); // Random potion
AddLoot(LootPack.LowScrolls); // Circle 1--4 scroll
AddLoot(LootPack.MedScrolls); // Circle 5--6 scroll
AddLoot(LootPack.HighScrolls); // Circle 7--8 scroll
}

Packs automatically select era-appropriate loot based on the server expansion.

Specific Items

For items that always drop, add them directly:

PackItem(new Arrow(Utility.RandomMinMax(20, 40)));
PackGold(100, 200);
PackItem(new Bandage(Utility.RandomMinMax(5, 10)));

Property Lists (Tooltips)

Override GetProperties to customize what players see when hovering over your item:

public override void GetProperties(IPropertyList list)
{
base.GetProperties(list); // Always call base first

// Cliloc with a value argument
list.Add(1060741, $"{_charges}"); // "charges: ~1_val~"

// Key-value pair (string constants must be holes)
list.Add(1060658, $"{"Quality"}\t{_quality}"); // "~1_val~: ~2_val~"

// Raw string line
list.Add($"{"Crafted with care"}");
}
warning

String literals in interpolated property list arguments must be wrapped as holes: $"{"Map"}\t{value}" not $"Map\t{value}". The handler treats bare text as delimiters and {} holes as arguments. Only \t should be a bare literal.

Use [InvalidateProperties] on serialized fields to auto-refresh tooltips when values change.


Entity Lifecycle

Entities go through a two-phase deletion process:

// Phase 1: Pre-removal -- cancel timers, unregister from systems
public override void OnDelete()
{
_timerToken.Cancel();
base.OnDelete();
}

// Phase 2: Post-removal -- null out references
public override void OnAfterDelete()
{
_timer?.Stop();
_timer = null;
_owner = null;
base.OnAfterDelete();
}
PhaseMethodWhat to Do
Pre-removalOnDelete()Cancel TimerExecutionToken, unregister from tracking systems
Post-removalOnAfterDelete()Stop and null Timer references, null Item/Mobile references

File Organization

Place new content files under Projects/UOContent/ following this structure:

Projects/UOContent/
Items/
Weapons/Swords/ # Swords
Weapons/Maces/ # Maces
Weapons/Ranged/ # Bows, crossbows
Armor/Plate/ # Plate armor
Armor/Leather/ # Leather armor
Clothing/ # Wearable clothing
Containers/ # Bags, boxes, chests
Misc/ # General items
Special/ # Unique or quest items
Resources/ # Crafting materials
Mobiles/
Animals/Bears/ # Bears
Animals/Birds/ # Birds
Monsters/AOS/ # AOS-era monsters
Monsters/SE/ # SE-era monsters
Monsters/ML/ # ML-era monsters
Special/ # Champions, bosses
Vendors/ # NPC vendors
Townfolk/ # NPCs

Naming rules:

  • File name matches the primary class name.
  • One primary class per file.
  • Group related items in subdirectories.
  • Era-specific content goes in era-named subdirectories.