API:World
From WCell Wiki
You can use both, NPCs as well as GameObjects to change the looks of your world.
It is recommended to fall back on those two articles as reference while working with NPCs and GOs.
Contents |
Maps, Regions and Zones
- Every map (such as continents, instances and battlegrounds) are of type Region
- Everything that happens within one Region is thread-safe
- Every Region has at least one Zone
- Zones can have child-zones (or areas) which are also of type Zone
- You can access child and parent zones through the Children and ParentZone properties respectively
- Regions can be spawned (once data has been loaded, using the load command) and cleared ingame at any time without risk
- The command #rgn spawn (or short: #rgn s) will spawn all default objects on a map
- The command #rgn clear will remove all objects that are not owned by any player
- Static default data of Regions is defined in RegionInfo
- Static default data of Zones is defined in ZoneInfo
Ingame-Object Class Hierarchy
- ObjectBase (represents any entity that exists in the world)
- Item (anything that can be put in the backpack, such as weapons, ore etc)
- Container (anything that can hold hold other items, such as bags etc)
- WorldObject (anything that exists on the map)
- GameObjects (any static -often interactable- object, such as MailBox, Chairs, collectable flowers (and Transports) etc)
- Unit (any living character or creature)
- DynamicObject (static animations, such as Blizzard, Flamestrike etc - usually created temporarily when casting spells)
- Corpse (a Player's Corpse - NPC's corpses are actual NPC objects)
- Item (anything that can be put in the backpack, such as weapons, ore etc)
WorldObject
- The WorldObject is very important because it represents anything that can be put onto the face of the ingame world
- It is the base class for playable Characters, NPCs and GameObjects
Events
- Events can be used to execute code when something interesting happens
- In order to execute code you have to attach a delegate (or simply a method) to it, using the += operator
- The signature of the method to be executed is defined by the delegate in the event-signature
- The code can be attached in the form of:
- An anonymous method: MyEvent += delegate(ArgType1 arg1, ArgType2) { ... }
- A lambda expression: MyEvent += (arg1, arg2) => returnValue or: (arg1, arg2) => { CodeToBeExecuted(); return expectedValue; }
- The name of a well-named method: MyEvent += MyMethod
- You can remove these event-handlers from the event by using the -= operator
- All RealmServer-events can be found in the Events/ folder
- WCell defines a whole range of events - for example:
- static event CharacterLoginHandler Character.LoggedIn is triggered when a new Character is logged in
- event Action<Item> ItemTemplate.Equipped is triggered when an Item of that ItemTemplate is equipped
Event Examples
Character.LoggedIn
- The event and its delegate are defined like this:
/// <param name="character"></param> /// <param name="firstLogin">Indicates whether the Character starts a new session or if /// the client re-connected to a Character that was already logged in.</param> public delegate void CharacterLoginHandler(Character chr, bool firstLogin); // ... /// <summary> /// Is called when the Player logs in or reconnects to a Character that was logged in before and not logged out yet (due to logout delay). /// </summary> public static event CharacterLoginHandler LoggedIn;
- The delegate of the event is CharacterLoginHandler
- Which means, the event requires a method with return-type void (don't return anything) and 2 arguments: One Character and one bool
- The event is static, meaning delegates have to be attached through the containing class and not its objects (Character.LoggedIn rather than myChr.LoggedIn)
- You can use it like this:
[Initialization]
public static void InitCharEvents()
{
Character.LoggedIn += (chr, firstLogin) => chr.Say("Hello, I just logged in!"); // let a new Character say something when logging in
}
- Or a little more advanced:
[Initialization]
public static void InitCharEvents()
{
Character.LoggedIn += (chr, firstLogin) => {
// determine whether the Player logs in with a new Character, or simply reconnects to a logged in Character
string welcome;
if (!firstLogin) {
welcome = "Welcome back";
}
else {
welcome = "Welcome";
}
if (chr.Role.IsStaff) {
// tell staff members about the ticket-situation
chr.SendSystemMessage("{0} sir - There are {1} Tickets in the queue!", welcome, TicketMgr.TicketCount);
}
else {
// Charge players everytime they login, if they have anything
if (chr.Money >= 10)
{
chr.Money -= 10;
}
chr.SendSystemMessage("{0} - You have been charged {1} copper!", welcome, 10);
}
}
}
ItemTemplate.Equipped and Unequipped
- ItemTemplate-events can be used to hook to things that happen to Items
- These two ItemTemplate-events are defined like this:
/// <summary> /// Called whenever an Item of this ItemTemplate is equipped /// </summary> public event Action<Item> Equipped; /// <summary> /// Called whenever an Item of this ItemTemplate is unequipped /// </summary> public event Action<Item> Unequipped;
- The delegate is Action<Item> which is of return-type void and has one argument of type Item
- The events belong to ItemTemplate and are not static, meaning they need to be accessed through ItemTemplate-objects, rather than the ItemTemplate class
- You can use these events to add a specialty other than StatModifiers (which can be easily done by using ItemTemplate.AddMod) to specific items
- As described in the Items API, any item-related initializer method is dependent on the ItemMgr:
- You can easily make the wearer of any item invulnerable to any kind of damage, using this code:
[Initialization]
[DependentInitialization(typeof(ItemMgr))]
public static void InitItemEvents()
{
var templ = ItemMgr.GetTemplate(ItemId.ChestguardOfTheVanquishedChampion);
// let this item make the wearer invulnerable
templ.Equipped += chestGuard => chestGuard.OwningCharacter.Invul++;
templ.Unequipped += chestGuard => chestGuard.OwningCharacter.Invul--;
}
Add content to the world
Ingame
- Make sure that NPC and/or GOs are loaded, using the commands: #load all, #load npcs and/or #load gos
- Use the commands:
- #go add <goid> (create a default GameObject of the given id)
- #npc add <npcid> (create a homeless (without SpawnPoint) NPC of the given id at your current location)
- #npc spawn -c [<npcid>] (trigger spawning of the closest NPC to your location - optionally specify what kind of NPC you want - teleports you to the location)
Code
GameObjects
- Add a new GOTemplate:
[Initialization]
[DependentInitialization(typeof(GOMgr))]
public static void InitMyGOs
{
var lightEntry = GOMgr.GetEntry(GOEntryId.CrusaderDargathsLight);
lightEntry.AddTemplate(MapId.Kalimdor, new Vector3(1, 2, 3));
}
- IMPORTANT: If you load GOs using the #load command, the GO won't be added automatically before it is (re-)spawned the next time (using #rgn clear and #rgn spawn)
- If you want to spawn a GO at a specific time, rather than having them spawned automatically when starting the region, you need to:
- Deactivate auto-spawning and
- Call the Spawn method yourself:
static GOTemplate lightTemplate;
[Initialization]
[DependentInitialization(typeof(GOMgr))]
public static void InitMyGOs
{
var lightEntry = GOMgr.GetEntry(GOEntryId.CrusaderDargathsLight);
lightTemplate = lightEntry.AddTemplate(MapId.Kalimdor, new Vector3(1, 2, 3), false); // the last parameter decides whether to auto-spawn (true by default)
}
// ...
public static void SpawnLight()
{
lightTemplate.Spawn();
}
- You can also create GameObjects at any point on the map without defining a GOTemplate:
static GOEntry lightEntry;
[Initialization]
[DependentInitialization(typeof(GOMgr))]
public static void InitMyGOs
{
// remember the light-entry as soon as it gets initialized
lightEntry = GOMgr.GetEntry(GOEntryId.CrusaderDargathsLight);
// hook up an event to spawn the light when someone dies at night in EK:
World.EasternKingdom.RegionInfo.PlayerDied += action => {
var chr = action.Victim as Character;
if (RealmServer.IngameTime.Hour < 6) { // only spawn before 6 am
var light = lightEntry.Spawn(chr); // spawn at the victim's location
light.CallDelayed(60000, obj => obj.Delete); // delete after 60 seconds
}
};
}
NPCs
This is a stub
static NPCEntry arthasImageEntry;
[Initialization]
[DependentInitialization(typeof(NPCMgr))]
public static void InitMyNPCs
{
// remember the entry as soon as it gets initialized
arthasImageEntry = NPCMgr.GetEntry(NPCId.ImageOfArthas);
// hook up an event to let arthas scold everyone who dies
World.EasternKingdom.RegionInfo.PlayerDied += action => {
var chr = action.Victim as Character;
var arthas = arthasImageEntry.Create();
chr.Region.AddObject(arthas, chr); // spawn Arthas ontop of the victim
arthas.Brain.DefaultState = BrainState.Idle; // make Arthas idle
arthas.Invul++; // make Arthas invulnerable
arthas.Face(chr); // face the character
arthas.CallDelayed(1000, // wait a second before saying something
obj => arthas.Say("You naughty little {0}!", chr.Gender == GenderId.Male ? "boy" : "girl"));
arthas.CallDelayed(6000, obj => obj.Delete); // delete after a total of 6 seconds (5 seconds after saying something)
};
}
