ModernUO/assets/js/40e6ee33.f752822f.js

1 line
No EOL
25 KiB
JavaScript

"use strict";(globalThis.webpackChunkwebsite=globalThis.webpackChunkwebsite||[]).push([[958],{1466(e,t,n){n.r(t),n.d(t,{assets:()=>a,contentTitle:()=>d,default:()=>h,frontMatter:()=>r,metadata:()=>l,toc:()=>c});const l=JSON.parse('{"id":"development/items-and-mobiles","title":"Items & Mobiles","description":"This guide covers the most common content creation tasks: building items and creatures for your shard.","source":"@site/content/development/items-and-mobiles.mdx","sourceDirName":"development","slug":"/development/items-and-mobiles","permalink":"/docs/development/items-and-mobiles","draft":false,"unlisted":false,"editUrl":"https://github.com/modernuo/ModernUO/tree/main/website/content/development/items-and-mobiles.mdx","tags":[],"version":"current","sidebarPosition":1,"frontMatter":{"sidebar_position":1,"title":"Items & Mobiles"},"sidebar":"docsSidebar","previous":{"title":"Configuration","permalink":"/docs/getting-started/configuration"},"next":{"title":"Serialization","permalink":"/docs/development/serialization"}}');var s=n(4848),i=n(8453);const r={sidebar_position:1,title:"Items & Mobiles"},d="Items & Mobiles",a={},c=[{value:"Creating an Item",id:"creating-an-item",level:2},{value:"Minimal Item",id:"minimal-item",level:3},{value:"Full Item Example",id:"full-item-example",level:3},{value:"Common Base Classes",id:"common-base-classes",level:2},{value:"Key Item Properties",id:"key-item-properties",level:2},{value:"Creating a Creature",id:"creating-a-creature",level:2},{value:"Basic Creature",id:"basic-creature",level:3},{value:"Optional Creature Overrides",id:"optional-creature-overrides",level:3},{value:"AI Types",id:"ai-types",level:2},{value:"Fight Modes",id:"fight-modes",level:2},{value:"Creature Stats Guide",id:"creature-stats-guide",level:2},{value:"Loot System",id:"loot-system",level:2},{value:"Predefined Loot Packs",id:"predefined-loot-packs",level:3},{value:"Specific Items",id:"specific-items",level:3},{value:"Property Lists (Tooltips)",id:"property-lists-tooltips",level:2},{value:"Entity Lifecycle",id:"entity-lifecycle",level:2},{value:"File Organization",id:"file-organization",level:2}];function o(e){const t={admonition:"admonition",code:"code",h1:"h1",h2:"h2",h3:"h3",header:"header",hr:"hr",li:"li",p:"p",pre:"pre",strong:"strong",table:"table",tbody:"tbody",td:"td",th:"th",thead:"thead",tr:"tr",ul:"ul",...(0,i.R)(),...e.components};return(0,s.jsxs)(s.Fragment,{children:[(0,s.jsx)(t.header,{children:(0,s.jsx)(t.h1,{id:"items--mobiles",children:"Items & Mobiles"})}),"\n",(0,s.jsx)(t.p,{children:"This guide covers the most common content creation tasks: building items and creatures for your shard."}),"\n",(0,s.jsx)(t.hr,{}),"\n",(0,s.jsx)(t.h2,{id:"creating-an-item",children:"Creating an Item"}),"\n",(0,s.jsx)(t.h3,{id:"minimal-item",children:"Minimal Item"}),"\n",(0,s.jsxs)(t.p,{children:["Every item needs ",(0,s.jsx)(t.code,{children:"[SerializationGenerator]"}),", a ",(0,s.jsx)(t.code,{children:"partial"})," class, and a ",(0,s.jsx)(t.code,{children:"[Constructible]"})," constructor:"]}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-csharp",children:'using ModernUO.Serialization;\n\nnamespace Server.Items;\n\n[SerializationGenerator(0)]\npublic partial class SimpleItem : Item\n{\n [Constructible]\n public SimpleItem() : base(0x1234)\n {\n Weight = 1.0;\n }\n\n public override string DefaultName => "a simple item";\n}\n'})}),"\n",(0,s.jsxs)(t.ul,{children:["\n",(0,s.jsxs)(t.li,{children:[(0,s.jsx)(t.code,{children:"0x1234"})," is the item graphic ID from UO art files."]}),"\n",(0,s.jsxs)(t.li,{children:[(0,s.jsx)(t.code,{children:"DefaultName"})," sets the tooltip name. Use ",(0,s.jsx)(t.code,{children:"LabelNumber"})," for cliloc-based names instead."]}),"\n"]}),"\n",(0,s.jsx)(t.h3,{id:"full-item-example",children:"Full Item Example"}),"\n",(0,s.jsx)(t.p,{children:"A complete item with serialized fields, a timer, property list, and double-click behavior:"}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-csharp",children:'using ModernUO.Serialization;\n\nnamespace Server.Items;\n\n[SerializationGenerator(0)]\npublic partial class MagicLantern : Item\n{\n [SerializableField(0)]\n [InvalidateProperties]\n [SerializedCommandProperty(AccessLevel.GameMaster)]\n private int _charges;\n\n [SerializableField(1)]\n [SerializedCommandProperty(AccessLevel.GameMaster)]\n private Mobile _owner;\n\n private TimerExecutionToken _glowTimer;\n\n [Constructible]\n public MagicLantern() : base(0xA25)\n {\n _charges = Utility.RandomMinMax(5, 15);\n Weight = 2.0;\n Light = LightType.Circle300;\n StartGlow();\n }\n\n public override string DefaultName => "a magic lantern";\n\n private void StartGlow()\n {\n Timer.StartTimer(TimeSpan.FromSeconds(3), TimeSpan.FromSeconds(3), Glow, out _glowTimer);\n }\n\n [AfterDeserialization]\n private void AfterDeserialization() => StartGlow();\n\n public override void OnAfterDelete()\n {\n _glowTimer.Cancel();\n base.OnAfterDelete();\n }\n\n private void Glow()\n {\n if (_charges > 0)\n {\n Effects.SendLocationParticles(this, 0x376A, 9, 10, 5042);\n }\n }\n\n public override void OnDoubleClick(Mobile from)\n {\n if (!IsChildOf(from.Backpack))\n {\n from.SendLocalizedMessage(1042001); // Must be in your backpack\n return;\n }\n\n if (_charges <= 0)\n {\n from.SendMessage("The lantern is depleted.");\n return;\n }\n\n Charges--;\n from.SendMessage("The lantern flares brightly!");\n from.FixedParticles(0x376A, 9, 32, 5042, EffectLayer.Waist);\n }\n\n public override void GetProperties(IPropertyList list)\n {\n base.GetProperties(list);\n list.Add(1060741, $"{_charges}"); // "charges: ~1_val~"\n }\n}\n'})}),"\n",(0,s.jsx)(t.p,{children:"Key patterns:"}),"\n",(0,s.jsxs)(t.ul,{children:["\n",(0,s.jsxs)(t.li,{children:[(0,s.jsx)(t.strong,{children:(0,s.jsx)(t.code,{children:"TimerExecutionToken"})})," is never serialized -- restart it in ",(0,s.jsx)(t.code,{children:"[AfterDeserialization]"}),"."]}),"\n",(0,s.jsxs)(t.li,{children:[(0,s.jsx)(t.strong,{children:(0,s.jsx)(t.code,{children:"[InvalidateProperties]"})})," auto-refreshes the tooltip when ",(0,s.jsx)(t.code,{children:"Charges"})," changes."]}),"\n",(0,s.jsxs)(t.li,{children:[(0,s.jsx)(t.strong,{children:(0,s.jsx)(t.code,{children:"[SerializedCommandProperty]"})})," exposes the field to the ",(0,s.jsx)(t.code,{children:"[Props"})," gump for GMs."]}),"\n",(0,s.jsxs)(t.li,{children:[(0,s.jsx)(t.strong,{children:(0,s.jsx)(t.code,{children:"OnAfterDelete"})})," cancels the timer to prevent it firing on a deleted entity."]}),"\n"]}),"\n",(0,s.jsx)(t.hr,{}),"\n",(0,s.jsx)(t.h2,{id:"common-base-classes",children:"Common Base Classes"}),"\n",(0,s.jsxs)(t.table,{children:[(0,s.jsx)(t.thead,{children:(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.th,{style:{textAlign:"left"},children:"Base Class"}),(0,s.jsx)(t.th,{style:{textAlign:"left"},children:"Use For"})]})}),(0,s.jsxs)(t.tbody,{children:[(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"Item"})}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"Generic items"})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"BaseWeapon"})}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"Melee weapons"})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"BaseRanged"})}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"Ranged weapons (bows, crossbows)"})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"BaseArmor"})}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"Armor pieces"})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"BaseShield"})}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"Shields"})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"BaseClothing"})}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"Wearable clothing"})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"BaseJewel"})}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"Rings, bracelets, necklaces"})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"BaseContainer"})}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"Containers (bags, boxes, chests)"})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"BasePotion"})}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"Potions"})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"Food"})}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"Edible items"})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"SpellScroll"})}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"Spell scrolls"})]})]})]}),"\n",(0,s.jsx)(t.hr,{}),"\n",(0,s.jsx)(t.h2,{id:"key-item-properties",children:"Key Item Properties"}),"\n",(0,s.jsx)(t.p,{children:"Set these in the constructor:"}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-csharp",children:"Weight = 1.0; // Weight in stones\nStackable = true; // Can stack with same type\nAmount = 1; // Stack amount\nMovable = true; // Can be picked up\nHue = 0; // Color (0 = default)\nLootType = LootType.Regular; // Regular, Newbied, Blessed, Cursed\nLayer = Layer.OneHanded; // Equipment layer\nLight = LightType.Circle300; // Light emission\n"})}),"\n",(0,s.jsx)(t.hr,{}),"\n",(0,s.jsx)(t.h2,{id:"creating-a-creature",children:"Creating a Creature"}),"\n",(0,s.jsx)(t.h3,{id:"basic-creature",children:"Basic Creature"}),"\n",(0,s.jsxs)(t.p,{children:["Creatures extend ",(0,s.jsx)(t.code,{children:"BaseCreature"})," and define stats, resistances, skills, and loot:"]}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-csharp",children:'using ModernUO.Serialization;\nusing Server.Items;\n\nnamespace Server.Mobiles;\n\n[SerializationGenerator(0)]\npublic partial class ForestWolf : BaseCreature\n{\n [Constructible]\n public ForestWolf() : base(AIType.AI_Melee, FightMode.Closest)\n {\n Body = 225;\n BaseSoundID = 0xE5;\n\n SetStr(80, 120);\n SetDex(90, 110);\n SetInt(20, 40);\n\n SetHits(60, 80);\n SetMana(0);\n\n SetDamage(8, 14);\n\n SetDamageType(ResistanceType.Physical, 100);\n\n SetResistance(ResistanceType.Physical, 25, 35);\n SetResistance(ResistanceType.Fire, 5, 10);\n SetResistance(ResistanceType.Cold, 15, 25);\n SetResistance(ResistanceType.Poison, 10, 15);\n SetResistance(ResistanceType.Energy, 5, 10);\n\n SetSkill(SkillName.MagicResist, 30.0, 50.0);\n SetSkill(SkillName.Tactics, 50.0, 70.0);\n SetSkill(SkillName.Wrestling, 50.0, 70.0);\n\n Fame = 600;\n Karma = 0;\n\n VirtualArmor = 28;\n\n Tamable = true;\n ControlSlots = 1;\n MinTameSkill = 50.1;\n }\n\n public override string CorpseName => "a wolf corpse";\n public override string DefaultName => "a forest wolf";\n public override int Meat => 1;\n public override int Hides => 6;\n public override HideType HideType => HideType.Regular;\n public override FoodType FavoriteFood => FoodType.Meat;\n public override PackInstinct PackInstinct => PackInstinct.Canine;\n\n public override void GenerateLoot()\n {\n AddLoot(LootPack.Meager);\n }\n}\n'})}),"\n",(0,s.jsx)(t.h3,{id:"optional-creature-overrides",children:"Optional Creature Overrides"}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-csharp",children:"public override Poison PoisonImmune => Poison.Regular;\npublic override Poison HitPoison => Poison.Lesser;\npublic override double HitPoisonChance => 0.2;\npublic override bool CanRummageCorpses => true;\npublic override bool BardImmune => true;\npublic override bool Unprovokable => true;\npublic override bool CanFly => true;\npublic override int TreasureMapLevel => 3;\npublic override double WeaponAbilityChance => 0.4;\n"})}),"\n",(0,s.jsx)(t.hr,{}),"\n",(0,s.jsx)(t.h2,{id:"ai-types",children:"AI Types"}),"\n",(0,s.jsxs)(t.table,{children:[(0,s.jsx)(t.thead,{children:(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.th,{style:{textAlign:"left"},children:"AIType"}),(0,s.jsx)(t.th,{style:{textAlign:"left"},children:"Use For"})]})}),(0,s.jsxs)(t.tbody,{children:[(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"AI_Melee"})}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"Warriors, melee fighters"})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"AI_Mage"})}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"Spellcasters"})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"AI_Archer"})}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"Ranged attackers"})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"AI_Animal"})}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"Passive animals (flee when hurt)"})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"AI_Predator"})}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"Hunting animals"})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"AI_Healer"})}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"Healing NPCs"})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"AI_Vendor"})}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"Shop NPCs"})]})]})]}),"\n",(0,s.jsx)(t.hr,{}),"\n",(0,s.jsx)(t.h2,{id:"fight-modes",children:"Fight Modes"}),"\n",(0,s.jsxs)(t.table,{children:[(0,s.jsx)(t.thead,{children:(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.th,{style:{textAlign:"left"},children:"FightMode"}),(0,s.jsx)(t.th,{style:{textAlign:"left"},children:"Behavior"})]})}),(0,s.jsxs)(t.tbody,{children:[(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"None"})}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"Never attacks"})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"Aggressor"})}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"Only retaliates when attacked"})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"Strongest"})}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"Targets highest-stat enemy"})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"Weakest"})}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"Targets lowest-stat enemy"})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"Closest"})}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"Targets nearest enemy"})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"Evil"})}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"Attacks aggressors or evil-karma targets"})]})]})]}),"\n",(0,s.jsx)(t.hr,{}),"\n",(0,s.jsx)(t.h2,{id:"creature-stats-guide",children:"Creature Stats Guide"}),"\n",(0,s.jsx)(t.p,{children:"Use these ranges as a baseline when creating creatures:"}),"\n",(0,s.jsxs)(t.table,{children:[(0,s.jsx)(t.thead,{children:(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.th,{style:{textAlign:"left"},children:"Level"}),(0,s.jsx)(t.th,{style:{textAlign:"left"},children:"Str"}),(0,s.jsx)(t.th,{style:{textAlign:"left"},children:"Dex"}),(0,s.jsx)(t.th,{style:{textAlign:"left"},children:"Int"}),(0,s.jsx)(t.th,{style:{textAlign:"left"},children:"Hits"}),(0,s.jsx)(t.th,{style:{textAlign:"left"},children:"Damage"}),(0,s.jsx)(t.th,{style:{textAlign:"left"},children:"Fame"})]})}),(0,s.jsxs)(t.tbody,{children:[(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"Weak"}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"30--60"}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"30--50"}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"10--20"}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"20--40"}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"2--6"}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"100--300"})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"Average"}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"80--120"}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"60--90"}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"20--40"}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"60--100"}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"6--14"}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"500--1,500"})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"Strong"}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"150--250"}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"80--120"}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"50--100"}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"120--200"}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"12--22"}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"2,000--5,000"})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"Elite"}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"300--500"}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"100--150"}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"100--200"}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"250--500"}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"18--30"}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"5,000--15,000"})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"Boss"}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"500--1,000"}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"150--250"}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"200--400"}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"500--2,000"}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"25--40"}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"15,000+"})]})]})]}),"\n",(0,s.jsx)(t.hr,{}),"\n",(0,s.jsx)(t.h2,{id:"loot-system",children:"Loot System"}),"\n",(0,s.jsx)(t.h3,{id:"predefined-loot-packs",children:"Predefined Loot Packs"}),"\n",(0,s.jsxs)(t.p,{children:["Use ",(0,s.jsx)(t.code,{children:"AddLoot"})," in ",(0,s.jsx)(t.code,{children:"GenerateLoot()"})," to assign standard loot tiers:"]}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-csharp",children:"public override void GenerateLoot()\n{\n AddLoot(LootPack.Poor); // ~50 gold equivalent\n AddLoot(LootPack.Meager); // ~100 gold equivalent\n AddLoot(LootPack.Average); // ~250 gold equivalent\n AddLoot(LootPack.Rich); // ~500 gold equivalent\n AddLoot(LootPack.FilthyRich); // ~1,000 gold equivalent\n AddLoot(LootPack.UltraRich); // ~2,000 gold equivalent\n AddLoot(LootPack.SuperBoss); // Boss-level loot\n\n // Auxiliary packs\n AddLoot(LootPack.Gems, 2); // 2 random gems\n AddLoot(LootPack.Potions); // Random potion\n AddLoot(LootPack.LowScrolls); // Circle 1--4 scroll\n AddLoot(LootPack.MedScrolls); // Circle 5--6 scroll\n AddLoot(LootPack.HighScrolls); // Circle 7--8 scroll\n}\n"})}),"\n",(0,s.jsx)(t.p,{children:"Packs automatically select era-appropriate loot based on the server expansion."}),"\n",(0,s.jsx)(t.h3,{id:"specific-items",children:"Specific Items"}),"\n",(0,s.jsx)(t.p,{children:"For items that always drop, add them directly:"}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-csharp",children:"PackItem(new Arrow(Utility.RandomMinMax(20, 40)));\nPackGold(100, 200);\nPackItem(new Bandage(Utility.RandomMinMax(5, 10)));\n"})}),"\n",(0,s.jsx)(t.hr,{}),"\n",(0,s.jsx)(t.h2,{id:"property-lists-tooltips",children:"Property Lists (Tooltips)"}),"\n",(0,s.jsxs)(t.p,{children:["Override ",(0,s.jsx)(t.code,{children:"GetProperties"})," to customize what players see when hovering over your item:"]}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-csharp",children:'public override void GetProperties(IPropertyList list)\n{\n base.GetProperties(list); // Always call base first\n\n // Cliloc with a value argument\n list.Add(1060741, $"{_charges}"); // "charges: ~1_val~"\n\n // Key-value pair (string constants must be holes)\n list.Add(1060658, $"{"Quality"}\\t{_quality}"); // "~1_val~: ~2_val~"\n\n // Raw string line\n list.Add($"{"Crafted with care"}");\n}\n'})}),"\n",(0,s.jsx)(t.admonition,{type:"warning",children:(0,s.jsxs)(t.p,{children:["String literals in interpolated property list arguments ",(0,s.jsx)(t.strong,{children:"must"})," be wrapped as holes: ",(0,s.jsx)(t.code,{children:'$"{"Map"}\\t{value}"'})," not ",(0,s.jsx)(t.code,{children:'$"Map\\t{value}"'}),". The handler treats bare text as delimiters and ",(0,s.jsx)(t.code,{children:"{}"})," holes as arguments. Only ",(0,s.jsx)(t.code,{children:"\\t"})," should be a bare literal."]})}),"\n",(0,s.jsxs)(t.p,{children:["Use ",(0,s.jsx)(t.code,{children:"[InvalidateProperties]"})," on serialized fields to auto-refresh tooltips when values change."]}),"\n",(0,s.jsx)(t.hr,{}),"\n",(0,s.jsx)(t.h2,{id:"entity-lifecycle",children:"Entity Lifecycle"}),"\n",(0,s.jsx)(t.p,{children:"Entities go through a two-phase deletion process:"}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{className:"language-csharp",children:"// Phase 1: Pre-removal -- cancel timers, unregister from systems\npublic override void OnDelete()\n{\n _timerToken.Cancel();\n base.OnDelete();\n}\n\n// Phase 2: Post-removal -- null out references\npublic override void OnAfterDelete()\n{\n _timer?.Stop();\n _timer = null;\n _owner = null;\n base.OnAfterDelete();\n}\n"})}),"\n",(0,s.jsxs)(t.table,{children:[(0,s.jsx)(t.thead,{children:(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.th,{style:{textAlign:"left"},children:"Phase"}),(0,s.jsx)(t.th,{style:{textAlign:"left"},children:"Method"}),(0,s.jsx)(t.th,{style:{textAlign:"left"},children:"What to Do"})]})}),(0,s.jsxs)(t.tbody,{children:[(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"Pre-removal"}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"OnDelete()"})}),(0,s.jsxs)(t.td,{style:{textAlign:"left"},children:["Cancel ",(0,s.jsx)(t.code,{children:"TimerExecutionToken"}),", unregister from tracking systems"]})]}),(0,s.jsxs)(t.tr,{children:[(0,s.jsx)(t.td,{style:{textAlign:"left"},children:"Post-removal"}),(0,s.jsx)(t.td,{style:{textAlign:"left"},children:(0,s.jsx)(t.code,{children:"OnAfterDelete()"})}),(0,s.jsxs)(t.td,{style:{textAlign:"left"},children:["Stop and null ",(0,s.jsx)(t.code,{children:"Timer"})," references, null ",(0,s.jsx)(t.code,{children:"Item"}),"/",(0,s.jsx)(t.code,{children:"Mobile"})," references"]})]})]})]}),"\n",(0,s.jsx)(t.hr,{}),"\n",(0,s.jsx)(t.h2,{id:"file-organization",children:"File Organization"}),"\n",(0,s.jsxs)(t.p,{children:["Place new content files under ",(0,s.jsx)(t.code,{children:"Projects/UOContent/"})," following this structure:"]}),"\n",(0,s.jsx)(t.pre,{children:(0,s.jsx)(t.code,{children:"Projects/UOContent/\n Items/\n Weapons/Swords/ # Swords\n Weapons/Maces/ # Maces\n Weapons/Ranged/ # Bows, crossbows\n Armor/Plate/ # Plate armor\n Armor/Leather/ # Leather armor\n Clothing/ # Wearable clothing\n Containers/ # Bags, boxes, chests\n Misc/ # General items\n Special/ # Unique or quest items\n Resources/ # Crafting materials\n Mobiles/\n Animals/Bears/ # Bears\n Animals/Birds/ # Birds\n Monsters/AOS/ # AOS-era monsters\n Monsters/SE/ # SE-era monsters\n Monsters/ML/ # ML-era monsters\n Special/ # Champions, bosses\n Vendors/ # NPC vendors\n Townfolk/ # NPCs\n"})}),"\n",(0,s.jsx)(t.p,{children:(0,s.jsx)(t.strong,{children:"Naming rules:"})}),"\n",(0,s.jsxs)(t.ul,{children:["\n",(0,s.jsx)(t.li,{children:"File name matches the primary class name."}),"\n",(0,s.jsx)(t.li,{children:"One primary class per file."}),"\n",(0,s.jsx)(t.li,{children:"Group related items in subdirectories."}),"\n",(0,s.jsx)(t.li,{children:"Era-specific content goes in era-named subdirectories."}),"\n"]})]})}function h(e={}){const{wrapper:t}={...(0,i.R)(),...e.components};return t?(0,s.jsx)(t,{...e,children:(0,s.jsx)(o,{...e})}):o(e)}}}]);