Script API Reference
Every class, method and property server scripts can call - ScriptPlayer, ScriptWorld, ScriptNpc, skills, loot, stores, data tables, modules, events, enums.
25 min read
How to read this page#
Vastopia server scripts are C# files compiled by the game server at startup. They talk to the game through a small set of script API classes that wrap the engine safely. This page lists that surface. The signatures and descriptions below are taken from the server source and from the same generated API database the Script Editor uses for autocomplete (script_api.json), so what you see here is what the compiler sees.
Three places give you the same information while you work:
- Script Editor, API panel - the bottom-right card lists every root object (
player,npc,obj,ctx,ScriptWorld,GameData, ...) with its members. Type in Search API... to filter, select a member to read its signature, double-click to insert it at the caret. - Script Editor, API Files menu - opens any API type as a read-only, syntax-highlighted reference tab (
ScriptPlayer (API)and so on). - Autocomplete - typing
player.in a script offers the members below with their one-line summaries.
Two concepts are used everywhere, so read them first.
Roots: the objects your script receives#
| Identifier | Type | Where it comes from |
|---|---|---|
player | ScriptPlayer | Passed to nearly every hook: commands, NPC/object/item options, interface actions, tasks, locations |
npc | NPC (in hooks) / ScriptNpc (from ScriptWorld lookups) | NPC script hooks receive the engine NPC; ScriptWorld.FindNpc returns the wrapper ScriptNpc |
obj | WorldObject | Object script hooks and ctx.Object |
target | ScriptNpc | Alias used in some combat-flavoured examples; same type as npc lookups |
ctx | TaskContext | Passed to task scripts; carries the object, NPC, item or command that started the task |
ScriptWorld | static class | World-wide actions: spawn, find NPCs, broadcast, world stores |
ScriptInstance | static class | Create and destroy instanced copies of a region |
ScriptLoot | static class | Roll loot tables authored in the Loot Tables tool |
GameData | static class | Read-only item/NPC definitions, data tables, random numbers, vector helpers (its members are also usable without the GameData. prefix) |
GameEnums | static class | Melee, Ranged, Magic combat styles |
Units, positions and ticks#
- Positions are
System.Numerics.Vector3with the server convention(X, Y, Z)=(east-west world units, north-south world units, height layer). The second horizontal axis is stored inPosition.Y, andPosition.Zis the height-layer index (0 = terrain, 1-4 = stacked height layers, see Height layers). Methods that take coordinates name the plane axesxandz(Teleport(region, x, z, heightLayer)) orx,y,z(ScriptWorld.SpawnNpc(npcId, region, x, y, z)); in both cases the last value is the height layer. - One tile is 0.5 world units.
world = tile * 0.5. Spawn coordinates should be multiples of 0.5 so footprints sit on whole tiles. See Coordinate system. - Time is measured in game ticks. 4 ticks is approximately 1 second. Every delay, respawn time and scheduler interval below is in ticks.
- Object positions are the south-west corner of the footprint; the footprint extends east (+X) and north.
Usings you never write#
The server prepends a fixed block of using directives to every script and module before compiling it, so ScriptPlayer, ModuleLoader, TaskScript, Vector3, List<T> and Task resolve without any using of your own. The full list is the one written to server/_VastopiaGlobalUsings.g.cs by generate_script_project (see IDE setup). One side effect worth knowing: using static GameData brings Random(int, int) into scope, so write System.Random.Shared when you want .NET's random class.
Script base classes#
A script is a class that extends one of these bases or implements one of these interfaces. Override only the hooks you need; the default for every click-handling bool hook is false ("not handled"), for the task hooks OnStart / OnTick it is true ("keep going"), and for every stat override it is -1 ("use the next fallback"). Which folder a file lives in decides which registry picks it up. Entity-bound scripts bind to IDs either through their NpcIds / ObjectIds / ItemIds array or through server/data/script_bindings.json (sections npcs, npc_combat, objects, items, equipment, each mapping an entity id to a script path), which the editor writes when you pick a script on an NPC, object or item. See NPC, object and interaction scripts for the workflow.
| Base | Folder | Bind by | Hooks |
|---|---|---|---|
ICommandScript | scripts/commands/ | Name (typed as ::name) | string Name, string[] Aliases (default none), string Description, string Usage (default Name), int RequiredRank (default 0), Task Execute(ScriptPlayer player, string[] args) |
NpcScript | scripts/npcs/ | int[] NpcIds or bindings | OnSpawn(NPC), OnDeath(NPC, Entity? killer), Process(NPC) every tick while alive, bool OnClick(ScriptPlayer, NPC, string option) (dispatches to OnOption by default), bool OnOption(...), int GetMaxHit(NPC), int GetAttackSpeed(NPC), float GetAccuracy(NPC), OnDamageTaken(NPC, Entity attacker, int damage), int GetRespawnTicks() (default 25), List<(int itemId, int amount)>? GetDrops(NPC, Entity? killer) (null = use authored drops) |
NpcCombatScript | scripts/combat/ | int[] NpcIds, bindings, or the NPC's combat_script_path | GetMaxHit, GetAttackSpeed, GetAccuracy, float GetAttackDistance(NPC), CombatStyle? GetCombatStyle(NPC), OnAttack(NPC, Entity target, CombatController), OnHit(NPC, Entity, int damage), OnMiss(NPC, Entity), OnDamageTaken(NPC, Entity, int), OnDeath(NPC, Entity?), OnKill(NPC, Entity victim) |
ObjectScript | scripts/objects/ | int[] ObjectIds or bindings | OnSpawn(WorldObject) (declared but never invoked by the current server), Process(WorldObject) every tick, bool OnClick(ScriptPlayer, WorldObject, string option), bool OnOption(...) |
ItemScript | scripts/items/ | int[] ItemIds or bindings | bool OnOption(ScriptPlayer, int itemId, int slot, string option), bool OnUseOnItem(ScriptPlayer, int itemId, int itemSlot, int targetItemId, int targetSlot), bool OnUseOnNpc(ScriptPlayer, int itemId, int itemSlot, NPC npc), bool OnUseOnObject(ScriptPlayer, int itemId, int itemSlot, WorldObject obj), EquipmentStats GetStats(int itemId) |
EquipmentScript | scripts/equipment/ | int[] ItemIds or bindings | OnEquip(ScriptPlayer, int itemId, int slot), OnUnequip(...), Process(ScriptPlayer, int itemId, int slot) every tick while worn, OnHit(ScriptPlayer, int itemId, Entity target, int damage), OnDamageTaken(ScriptPlayer, int itemId, Entity attacker, int damage) |
LocationScript | scripts/locations/ | string LocationId = the location's name in snake_case | OnEnter(ScriptPlayer), OnExit(ScriptPlayer), OnTick(ScriptPlayer); properties Location (LocationData) and MapName are set before each call |
IInterfaceScript | scripts/interfaces/ | string[] InterfaceNames (names from interface_database.json, case-insensitive) | Task OnOpen(ScriptPlayer, int interfaceId), Task OnAction(ScriptPlayer, int interfaceId, string actionId, string actionData), Task OnClose(ScriptPlayer, int interfaceId), Task OnLogin(ScriptPlayer, int interfaceId) (tab interfaces only) |
TaskScript<TEntity> | scripts/tasks/ (subfolders allowed) | string[] TaskIds or tasks_data.json | bool OnStart(TEntity, TaskContext), bool OnTick(TEntity, int tick, TaskContext), OnStop(TEntity, TaskContext); TEntity is ScriptPlayer, NPC or WorldObject and the task auto-cancels when that entity leaves the world |
TaskScript<TEntity, TState> | scripts/tasks/ | same | Same hooks with a TState state parameter; a fresh TState (any class with a parameterless constructor) is allocated per run and never shared between concurrent runs |
WorldTaskScript | scripts/tasks/ | same | bool OnStart(TaskContext), bool OnTick(int tick, TaskContext), OnStop(TaskContext); no player, use ScriptWorld |
IBuffDefinition | scripts/buffs/ | string BuffId | string Name, BuffType Type, bool Stackable (default false), int Priority (default 100), OnApply(ScriptPlayer, BuffState), bool OnTick(ScriptPlayer, BuffState) (return false to remove), OnRemove(ScriptPlayer, BuffState). BuffState carries BuffId, Duration (ticks, -1 = infinite), Power, TickCount, Data dictionary |
ISpecialAttack | scripts/combat/ | int[] WeaponIds | int EnergyCost (0-100), string Name, string Description, int Priority (default 100), bool Execute(ScriptPlayer player, Entity target, int weaponId) |
EquipmentStats is a record with nine integer bonuses: AccuracyMelee, AccuracyRanged, AccuracyMagic, StrengthMelee, StrengthRanged, StrengthMagic, DefenceMelee, DefenceRanged, DefenceMagic; EquipmentStats.Empty is all zeros.
NpcScript, NpcCombatScript and ObjectScript also give you per-instance variables that live as long as that NPC or object is in the world: SetVar(entity, key, value), T GetVar<T>(entity, key, defaultValue), bool HasVar(entity, key). They are keyed by the entity's server Index, and cleaned up when it despawns. They are not saved; for anything that must survive a restart use player.SetVar or a WorldStore (below).
ScriptPlayer#
ScriptPlayer is the player-facing API and is passed directly to all script methods.
Identity#
| Member | Description |
|---|---|
string Username | The player's stable identity - the save key, and what you store when you need to remember a player. On a platform server this is the account id, so do not show it to players. |
string DisplayName | The human-readable name (set with ::setname), falling back to Username. Use this in any message a player reads. |
static string ResolvePlayerName(string nameOrUsername) | Resolves a typed display name or raw username to the stable username, or null if no such player exists. |
static string DisplayNameFor(string username) | The display name for any username, online or not. |
int Level | The player's level. |
int Rank | Permission rank: 0 player, 1 moderator, 2 admin, 3 owner. Persisted with the character. |
bool HasRank(int rank) | True if the player's rank is at least rank. |
void SetRank(int rank) | Sets the rank and saves immediately. |
void Log(string message) | Writes to the server console, tagged with this player's username. |
Position and movement#
| Member | Description |
|---|---|
Vector3 Position | Current position (see units above). |
string Region | Current region (map) name. |
bool IsRunning, bool IsMoving | Movement state. |
void SetRunning(bool running), void ToggleRun() | Run/walk mode. |
void ClearMovement() | Clears the movement queue. |
Task Teleport(string region, float x, float z, float heightLayer) | Teleports to a region using X/Z for the plane and a height layer. |
Task Teleport(float x, float z, float heightLayer) | Same, within the current region. |
Task Teleport(string region, float x, float z, float heightLayer, string instance) | Teleports into a named instance ("" = shared world). A missing instance is created empty. |
float DistanceTo(float x, float y, float z) | Distance to a position. |
float DistanceTo2D(float x, float z) | Distance measured across the position's X and Z components. Note that Z is the height layer, so this is not a ground-plane distance - compare Position.X and Position.Y yourself for that. |
Instances and parties#
| Member | Description |
|---|---|
string InstanceId | Current instance key; "" is the shared default world. |
bool InInstance | True when inside an instance. |
Task LeaveInstance() | Back to the shared world at the current spot. |
Task JoinInstanceOf(ScriptPlayer leader) | Teleports into the same instance as another player, at their position. |
Task TeleportToInstance(string region, float x, float z, float heightLayer, string instanceId) | Teleports this player's whole party into an instance (creating it if needed); just this player when not in a party. |
ScriptParty? Party | The player's party, or null. |
ScriptParty CreateParty() | Creates a party led by this player, or returns the existing one. |
bool InviteToParty(ScriptPlayer other) / bool InviteToParty(string username) | Invites another player; false if not allowed. |
bool AcceptPartyInvite() | Accepts a pending invite; true if a party was joined. |
void LeaveParty() | Leaves the party (leadership is reassigned if needed). |
Health#
| Member | Description |
|---|---|
float Health, float MaxHealth | Current and maximum health. |
void SetHealth(float health) | Sets health, clamped to 0..MaxHealth. |
void Heal(float amount), void Damage(float amount) | Adjust health. |
Inventory#
| Member | Description |
|---|---|
bool GiveItem(int itemId, int amount = 1) | Gives an item. |
bool HasItem(int itemId, int amount = 1) | Checks the inventory. |
bool RemoveItem(int itemId, int amount = 1) | Removes from the inventory. |
List<(int itemId, int amount)> GiveLoot(string tableId) | Rolls a named Misc loot table and gives the result; anything that does not fit drops at the player's feet. Returns what was granted. |
bool InventoryIsFull | Whether the inventory is full. |
ItemContainer InventoryContainer | Direct access to the inventory container. |
Item GetEquippedItem(int slot) | The item equipped in a slot, or null. |
ItemContainer (used for inventory and bank) exposes Capacity, UsedSlots, FreeSlots, IsFull, IsEmpty, Item? GetItem(int slot), bool AddItem(Item item), Item? RemoveItem(int slot), bool RemoveItem(int itemId, int amount), int GetItemAmount(int itemId), bool HasItem(int itemId, int amount = 1), List<Item> GetItems(), int FindEmptySlot(), bool SwapItems(int slot1, int slot2) and void Clear().
Bank#
| Member | Description |
|---|---|
ItemContainer BankContainer | Direct access to the bank container. |
bool BankDeposit(int itemId, int amount = 1) | Inventory to bank. |
bool BankWithdraw(int itemId, int amount = 1) | Bank to inventory. |
bool BankHasItem(int itemId, int amount = 1) | Checks the bank. |
int BankGetItemAmount(int itemId) | Amount of an item in the bank. |
int BankFreeSlots, int BankUsedSlots, int BankCapacity | Bank space. |
int BankTabCount | Unlocked bank tabs (1 = only "All", max 10). |
int[] BankSlotTabs | Tab assignment per slot (0 = main, 1-9 = custom tab). |
int BankSelectedTab | Selected tab (0 = All). |
int BankSpawnedSlots | Slot nodes spawned in the current bank session. |
Animation and effects#
| Member | Description |
|---|---|
void PlayAnimation(int animationId, int delay = 0) | Plays an animation on the player. |
Task PlayGraphic(int graphicId) | Plays a VFX at the player's position. |
Task PlayGraphicAt(int graphicId, float x, float y, float z) | Plays a VFX at a position. |
void SetOverheadIcon(string spriteName) | Sets or clears the icon above the player's head. |
void ToggleGender(), void SetGender(bool isMale) | Appearance. |
void TransformToNpc(int npcId), void ClearTransform() | Visually turn the player into an NPC and back. |
Interfaces#
Interfaces are the screens built in the GUI editor. Every call below sends a packet to this player's client; nothing runs on the client itself.
| Member | Description |
|---|---|
Task OpenInterface(int interfaceId) / Task OpenInterface(string name) | Opens an interface by id or by name. |
Task OpenInterface(int interfaceId, byte mode) / Task OpenInterface(string name, byte mode) | Opens with a mode; mode 3 = walkable (stays open while the player moves). |
Task CloseInterface(int interfaceId) / Task CloseInterface(string name) | Closes it, calling the script's OnClose. |
Task SetInterfaceText(int interfaceId, string nodeName, string text) | Sets a node's text. |
Task SetInterfaceVisible(int interfaceId, string nodeName, bool visible) | Shows or hides a node. |
Task SetInterfaceTransform(int interfaceId, string nodeName, float x, float y) | Moves a node. |
Task SetInterfaceItem(int interfaceId, string nodeName, int itemId, int itemAmount) | Shows an item in a slot node. |
Task SetInterfaceItems(int interfaceId, string containerName, (int slot, int itemId, int amount)[] items) | Fills many slots in one packet; slots are the container's item-slot descendants in tree order (0-based); itemId/amount <= 0 clears a slot. |
Task SpawnInterfaceNode(int interfaceId, string nodeName, int amount) | Spawns copies of a template node. |
Task ClearSpawnedNodes(int interfaceId, string spawnerName) | Removes all spawned clones and resets the counter, so a list can be rebuilt without duplicates. |
Task SetInterfaceTexture(int interfaceId, string nodeName, string spriteName) | Sets a TextureRect or button icon by sprite name (looked up in AssetLibrary.json). |
Task SetInterfaceValue(int interfaceId, string nodeName, double value) | ProgressBar/Slider/SpinBox value, CheckBox/CheckButton pressed (non-zero = on), OptionButton selected index. |
Task SetInterfaceEnabled(int interfaceId, string nodeName, bool enabled) | Button disabled, LineEdit/TextEdit editable. |
Task SetInterfaceModel(int interfaceId, string nodeName, string kind, int refId, string animation = "") | Shows a 3D model in a Model Viewer widget; kind is "npc", "npc_head" or "item". |
Task SetAllowedTab(int tabIndex) | Locks the tab bar to one tab; -1 unlocks. |
Task SetContextMenuOverride(string options) | Overrides inventory context-menu options (comma-separated); null/empty restores defaults. |
Dictionary<string, string> FormValues | Snapshot of form values (node name to value) taken before OnAction runs. |
string GetFormValue(string nodeName, string defaultValue = "") | Reads one form value. |
Chat and dialogue#
| Member | Description |
|---|---|
Task SendGameMessage(string message) | A chatbox message only this player sees. |
void Say(string message) | Overhead chat bubble from the player. |
void ForceSay(string message) | Forced overhead message. |
void Narrate(string text, Action<ScriptPlayer> onContinue) | Narration line in the chatbox panel; onContinue runs when the player advances. |
void AskChoice(string prompt, params (string Label, Action<ScriptPlayer> OnSelect)[] options) | Clickable choice; each callback may chain further calls. |
void AskInput(string prompt, Action<ScriptPlayer, string> onSubmit) | Text-input panel. |
void SetDialogueActor(ScriptNpc npc) / (WorldObject obj) / (ScriptPlayer other) | Sets the entity that Say(text, onContinue) lines anchor a bubble above. |
void Say(string text, Action<ScriptPlayer> onContinue) | Speaks a line as the dialogue actor. |
void ClearDialogueActor() | Later Say lines fall back to narration. |
For branching conversations authored without code, see the Dialogue editor.
Combat#
| Member | Description |
|---|---|
AttackStyle CurrentAttackStyle | Get or set the attack style. |
int CombatLevel | Get or set the combat level. |
bool AutoRetaliate | Get or set auto-retaliate. |
Persistent variables#
Per-player key/value storage that survives logout and server restarts; it is saved with the character alongside inventory and skills. Use it for quest flags, unlocks, cooldown stamps and personal progress. Keys are case-insensitive; namespace them by convention ("quest.dragonslayer.stage"). For state shared by many players, use a WorldStore instead.
| Member | Description |
|---|---|
void SetVar(string key, string value) (+ int, long, float, bool overloads) | Sets a persistent variable. |
string GetVar(string key, string defaultValue = "") | Reads it, or the default when unset. |
int GetVarInt(string key, int defaultValue = 0), long GetVarLong(...), float GetVarFloat(...), bool GetVarBool(...) | Typed reads; the default is returned when unset or unparseable. |
bool HasVar(string key) | True if ever set. |
void RemoveVar(string key) | Removes it. |
long IncrementVar(string key, long amount = 1) | Atomically adds to a numeric variable and returns the new value (unset counts as 0). |
Scheduling#
| Member | Description |
|---|---|
ActionTask After(int ticks, Action action) | Runs once after a delay. |
ActionTask Every(int ticks, Action action) | Repeats every N ticks. |
ActionTask EveryStartingNow(int ticks, Action action) | Runs immediately, then every N ticks. |
void CancelAllTasks() | Cancels every scheduled task owned by this player. |
long CurrentTick | The server tick counter. |
The returned ActionTask has Stop(), ResetCountdown(), GetCountdown(), IsRunning and ExecutionCount.
ScriptSkills#
Reached through player.Skills. Skill names are the ones defined in Skills (skills_config.json); the server also generates a SkillId class with one constant per skill for the integer overloads.
| Member | Description |
|---|---|
int GetLevel(string skillName) / int GetLevel(int skillId) | Current level. |
double GetXp(string skillName) / double GetXp(int skillId) | Current XP. |
double GetXpToNext(string skillName) | XP needed for the next level. |
double GetProgress(string skillName) | Progress to the next level, 0.0 to 1.0. |
int TotalLevel, double TotalXp | Totals across all skills. |
bool AddXp(string skillName, double amount) / bool AddXp(int skillId, double amount) | Adds XP; returns true on level-up. |
bool HasLevel(string skillName, int required) / bool HasLevel(int skillId, int required) | Requirement check. |
void SetLevel(string skillName, int level), void SetXp(string skillName, double xp) | Direct set (admin/testing). |
Task SyncToClient() | Pushes all skill data to the client. |
Dictionary<int, SkillData> GetAllData() | All skill info for display. |
ScriptParty#
Obtained from player.Party or player.CreateParty().
| Member | Description |
|---|---|
string Leader | Leader's username. |
IReadOnlyList<string> Members | All member usernames, leader included. |
int Size | Member count. |
bool IsLeader(string username), bool HasMember(string username) | Membership checks. |
Task TeleportToInstance(string region, float x, float z, float heightLayer, string instanceId) | Teleports every online member into the same instance, creating it if needed. |
ScriptNpc#
The read-mostly wrapper returned by ScriptWorld lookups.
| Member | Description |
|---|---|
int Index | Server index of this NPC instance. |
int Id | Definition id. |
Vector3 Position, string Region | Where it is. |
float Health, float MaxHealth, bool IsAlive | Health state. |
void ForceSay(string message) | Overhead message. |
ScriptWorld#
All members are static - no player reference needed.
| Member | Description |
|---|---|
Task SpawnNpc(int npcId, string region, float x, float y, float z, string instance = "") | Spawns an NPC. The definition's name and footprint size are applied. |
void SpawnObject(int objectId, string region, float x, float y, float z, int size = 1, string instance = "") | Spawns a world object; width/depth default to the definition's tile footprint. |
ScriptNpc? FindNpcByIndex(int npcIndex) | By server index. |
ScriptNpc? FindNpc(int npcId) | First NPC with that definition id. |
List<ScriptNpc> FindNpcs(int npcId) | All NPCs with that definition id. |
List<ScriptNpc> GetNpcsInRegion(string region, string instance = "") | All NPCs in a region and instance. |
void SetObjectState(WorldObject obj, int newObjectId) | Swaps an object to another definition (tree to stump). |
void SetObjectState(WorldObject obj, int newObjectId, int respawnTicks, int respawnToObjectId) | Swaps, then swaps back after N ticks. |
void SendMessage(string message) | Game message to everyone online. |
void OpenInterfaceForAll(int interfaceId, byte mode = 0) / void CloseInterfaceForAll(int interfaceId) | Opens or closes an interface for every player, through each player's own open-tracking. |
WorldStore GetStore(string name) | A named persistent store for world state (below). |
int PlayerCount, int NpcCount | Online players and NPCs in the world. |
ScriptInstance#
Instances are isolated copies of a region for raids, minigames and private boss fights. They auto-destroy (despawning their NPCs, objects and ground items) when the last player leaves.
| Member | Description |
|---|---|
Task Create(string region, string instanceId, bool copyDefaults = false) | Creates an instance; with copyDefaults it is populated with the region's normal NPC/object spawns, otherwise it starts empty. Idempotent. |
Task Destroy(string instanceId) | Destroys it and everything in it. |
bool Exists(string instanceId) | Whether the key exists right now. |
Task SpawnNpc(string instanceId, int npcId, string region, float x, float y, float z) | Spawns an NPC into the instance. |
void SpawnObject(string instanceId, int objectId, string region, float x, float y, float z, int size = 1) | Spawns an object into the instance. |
await ScriptInstance.Create("boss", raidId, copyDefaults: false);
await ScriptInstance.SpawnNpc(raidId, BOSS_ID, "boss", 20, 20, 0);
await player.Teleport("boss", 20, 25, 0, raidId);
ScriptLoot#
Rolls the drop tables authored in the Loot Tables tool (server/schemas/Drops/*). Entity-bound tables (npc_drops, npc_table_drops, npc_wheel_drops, keyed by NPC ids) are rolled on death by the drop module; standalone Misc tables (misc_drops, misc_table_drops, misc_wheel_drops, keyed by a table_id string) are what you roll from code for chests, minigame rewards and quest prizes. Which file a table lives in decides how it rolls: independent tables roll every entry's 0-100 chance separately, named sub-tables roll once each and pick rolls items, and wheel tables spin rolls times over weighted groups.
| Member | Description |
|---|---|
List<(int itemId, int amount)> Roll(string tableId) | Rolls a Misc table and returns the items without granting them. |
List<(int itemId, int amount)> RollNpc(int npcId) | Rolls every authored table bound to an NPC definition. |
bool TableExists(string tableId) | Whether a Misc table exists. |
List<(int itemId, int amount)> GiveToPlayer(Player player, string tableId) | Rolls and puts the loot in the inventory, ground fallback at the player's feet. Takes the engine Player; from a script use player.GiveLoot(tableId) instead. |
List<(int itemId, int amount)> DropAt(string tableId, Vector3 position, string region, string instance = "", string owner = "") | Rolls and drops on the ground. Pass a username as owner to reserve the drop for the loot-ownership window; omit it for a free-for-all drop. |
void DropNpcLoot(NPC npc, List<(int itemId, int amount)> loot, string owner = "") | Drops an already-rolled list at the NPC's position (its instance). owner should be the killer. |
WorldStore#
A named, persistent key/value store for world state - minigame scores, leaderboards, world events, anything not owned by a single player. Get one with ScriptWorld.GetStore("pest_control"); names are normalised to [a-z0-9_-], so use one store per system. Values are strings with typed reads; keys are case-insensitive. Writes mark the store dirty; dirty stores are flushed every few seconds and everything is flushed on shutdown or crash. Each flush writes data/saves/world/{name}.json and, on platform servers with a database configured, the game's world_data collection.
| Member | Description |
|---|---|
string Name | The store's name. |
void Set(string key, string value) (+ int, long, float, bool) | Sets a value; persisted automatically. |
string Get(string key, string defaultValue) | Reads a value or the default. |
int GetInt(...), long GetLong(...), float GetFloat(...), bool GetBool(...) | Typed reads with a default. |
long Increment(string key, long amount) | Atomic add; returns the new total. |
bool Has(string key), void Remove(string key) | Presence and removal. |
List<string> Keys, int Count | Snapshot of keys and entry count. |
void Clear() | Removes every entry. |
var store = ScriptWorld.GetStore("pest_control");
store.Increment("points." + player.Username, 5);
int points = store.GetInt("points." + player.Username, 0);
GameData and GameEnums#
GameData is a static helper; because the prelude includes using static GameData, you may call GetItem(12) or Vec3(1, 2, 0) with or without the GameData. prefix.
| Member | Description |
|---|---|
ItemDefinition? GetItem(int itemId), string GetItemName(int itemId), bool ItemExists(int itemId) | Read-only item definitions from items.json. |
NpcDefinition? GetNpc(int npcId), string GetNpcName(int npcId), bool NpcExists(int npcId) | Read-only NPC definitions. |
DataTable? GetData(string name), DataSchema? GetSchema(string name), bool DataExists(string name) | Data tables from server/schemas/ (below). |
Vector3 Vec3(float x, float y, float z), Vector3 Vec3(float x, float z) (Y = 0), Vector2 Vec2(float x, float y) | Vector shorthands. |
Vector3 Zero, Up, Forward, Right | Unit vectors. |
int Random(int min, int max) (max exclusive), int Random(int max), float RandomFloat(), float RandomFloat(float min, float max) | Random numbers. |
float Clamp(float, float, float), int Clamp(int, int, int), float Lerp(float a, float b, float t) | Math helpers. |
float Distance(Vector3 a, Vector3 b), float Distance2D(Vector3 a, Vector3 b) | Distances. Distance2D drops the Y component and measures across X and Z, so with the position convention above it keeps the height layer and drops a plane axis - use Distance for a true 3D distance. |
GameEnums exposes three CombatStyle values: GameEnums.Melee, GameEnums.Ranged, GameEnums.Magic.
Data tables#
Every schema created in the Script Editor (server/schemas/<Category>/<name>/<name>.json plus <name>_data.json) or by a tool is readable at runtime:
| Type | Members |
|---|---|
DataTable | DataSchema Schema, List<DataRow> Rows, int Count, DataRow? FindById(int id), DataRow? FindByField(string field, string value), List<DataRow> Where(Func<DataRow, bool> predicate) |
DataRow | int GetInt(string field, int defaultValue = 0), float GetFloat(...), string GetString(...), bool GetBool(...), T? Get<T>(string field), bool Has(string field), IEnumerable<string> Fields |
DataSchema | Name, Description, Table, List<SchemaField> Fields, string? PrimaryKey |
var prices = GameData.GetData("shop_prices");
var row = prices?.FindById(itemId);
int cost = row?.GetInt("price", 100) ?? 100;
TaskContext#
Carries the entity references from the trigger that started a task, and is the same object across OnStart, OnTick and OnStop. Triggers are defined per task in the Tasks editor; the trigger types the server parses are object_click, npc_click, item_click, command, location_enter, item_on_object, item_on_npc, item_on_item, server_startup, player_join, npc_spawn and object_spawn - of these, location_enter is read from the task data but never fired.
| Member | Description |
|---|---|
WorldObject? Object | The clicked or targeted object (object_click, item_on_object). |
NPC? Npc | The clicked or targeted NPC (npc_click, item_on_npc). |
int ItemId, int ItemSlot | The item used (item_on_*, item_click); slot is -1 when not applicable. |
string Option | The click option or command name. |
string[] Args | Command arguments (command triggers only). |
WorldObject? SecondaryObject, NPC? SecondaryNpc, int SecondaryItemId, int SecondaryItemSlot | The second party in item-on-X interactions. |
Entity types#
Hooks hand you engine entities rather than wrappers. The members you will use:
WorldObject#
| Member | Description |
|---|---|
int Index, int Id | Instance index and definition id. |
Vector3 Position, string Region, string InstanceId | Where it is; Position is the south-west corner. |
int Width, int Depth | Footprint in tiles (east-west, north-south). |
float RotationY | Rotation in degrees: 0 north, 90 east, 180 south, 270 west. |
int EffectiveWidth, int EffectiveDepth | Footprint after rotation (90/270 swap width and depth; the anchor stays). |
int Size | Legacy: the larger of width and depth. |
int HeightLayerId | 0 = terrain, 1-4 = height layers; blocking only applies to entities on the same layer. |
int InteractDistance, int DefaultInteractDistance | Interaction range in tiles (Chebyshev), measured from the nearest footprint tile. |
ObjectCollisionType CollisionType, bool BlocksMovement, bool BlocksLineOfSight | Collision behaviour from the object definition's collision_type. |
void ForceSay(string message) | Message to nearby players from this object. |
NPC#
| Member | Description |
|---|---|
int Id, int Size | Definition id and footprint side length in tiles. |
Vector3 SpawnPosition, float WanderRadius, int WanderIntensity, bool Walkable | Spawn and wander settings. |
CombatController Combat | The combat pipeline; ScheduleDamage(Entity target, int damage, CombatStyle style, int delayTicks = 2) is how a combat script applies a hit, and Attack(Entity target) starts an attack. |
List<Hit> Hits, Entity? InteractingEntity, Vector3? FacePosition | Combat and facing state. |
string ForcedChat, string OverheadIcon, int TransformId, Animation? CurrentAnimation, Graphic? CurrentGraphic, ChatMessage? CurrentChat | Visual state the client is updated with. |
void ForceSay(string message) | Overhead message. |
void Transform(int newId) | Morphs the NPC into another definition in place: its Id changes (so combat scripts, drops and stats resolve to the new form) and the client swaps the model; health and per-instance script state are kept. |
Modules#
Modules are long-lived systems in server/modules/ (one class per file, compiled separately, loaded in dependency order). They are the place for shared logic, because each script file compiles alone and cannot see helpers declared in another script file. See Events and systems.
| Member | Description |
|---|---|
ModuleBase | abstract string Id, abstract string Name, virtual string Version ("1.0.0"), Description, Author, string[] Dependencies; lifecycle Task OnLoad(), OnEnable(), OnDisable(), OnUnload(); helpers Events (the EventBus), Modules (the ModuleLoader), Log(string), LogWarning(string), LogError(string). |
ModuleBase<TSelf> | Same, plus a typed singleton static TSelf? Instance that the loader binds when the module is constructed and clears on unload. |
ModuleLoader.Module<T>() | Static shorthand for reaching a module from a script: ModuleLoader.Module<FishingModule>()?.RollCatch(...). Returns null when the module is not loaded (it failed to compile, or was removed), so null-check and degrade. |
ModuleLoader.Instance.GetModule<T>() / GetModule(string moduleId) / GetModuleInfo(string) / GetAllModules() | Instance lookups. |
public class FishingModule : ModuleBase<FishingModule>
{
public override string Id => "fishing";
public override string Name => "Fishing";
public int RollCatch(int level) => GameData.Random(1, level + 2);
}
// From any script:
var fishing = ModuleLoader.Module<FishingModule>();
int fish = fishing?.RollCatch(player.Skills.GetLevel("Fishing")) ?? 0;
// or, because the module declares ModuleBase<FishingModule>:
FishingModule.Instance?.RollCatch(5);
A game module whose Id matches a pre-compiled platform module replaces it; two game modules with the same Id are a conflict and the second is skipped. A module listed as disabled in server/data/systems.json is never loaded, even if its file is present.
Events#
Modules (and scripts) subscribe to the event bus: EventBus.Instance.Subscribe<T>(Action<T> handler, EventPriority priority = EventPriority.NORMAL, bool ignoreCancelled = true) (an async Func<T, Task> overload exists), Unsubscribe, Publish<T>(T evt) and PublishAsync<T>. Every event derives from GameEvent with IsCancelled, Timestamp, Cancel() and Uncancel(); events marked IUncancellable cannot be cancelled. Priorities run LOWEST (0), LOW, NORMAL, HIGH, HIGHEST, MONITOR (5).
| Group | Events |
|---|---|
| Player | PlayerLoginEvent, PlayerLogoutEvent, PlayerDamageEvent, PlayerDeathEvent, PlayerRespawnEvent, PlayerChatEvent, PlayerCommandEvent, PlayerMoveEvent, PlayerExperienceGainEvent, PlayerLevelUpEvent, PlayerItemAcquiredEvent, PlayerItemDroppedEvent, PlayerTradedEvent, PlayerPurchaseEvent |
| Combat | CombatStartEvent, CombatEndEvent, AttackHitEvent, DamageAppliedEvent, EntityPreDeathEvent, EntityDeathEvent, EntityRespawnEvent |
| Movement | EntityMoveRequestEvent, EntityMovedEvent, EntityArrivedEvent, EntityFollowStartEvent, EntityFollowStopEvent, EntityTeleportEvent |
| World | GameTickEvent, ServerStartEvent, ServerShutdownEvent, ObjectInteractEvent, ItemOnObjectEvent, ItemUseEvent, ItemPickupEvent, ItemDropEvent, ItemEquipEvent, ItemUnequipEvent, NpcSpawnEvent, NpcDeathEvent, PlayerLocationEnterEvent, PlayerLocationExitEvent, PlayerInteractEvent, NpcInteractEvent, DialogueAdvanceEvent, DialogueCloseEvent, DialogueInputEvent |
Field-by-field detail for each event is in Events and systems. Player commands should be ICommandScript classes, not PlayerChatEvent parsers - see Commands.
Enums#
| Enum | Values |
|---|---|
AttackStyle | Accurate (0), Aggressive (1), Defensive (2), Controlled (3) |
CombatStyle | Melee, Ranged, Magic |
ObjectCollisionType | Obstacle (blocks movement and line of sight), Wall (same), GroundDeco (fully passable), Door (blocks when closed), LowFence (blocks movement, projectiles pass over), Transparent (blocks movement, line of sight passes through) |
BuffType | Buff, Debuff, Neutral |
EventPriority | LOWEST, LOW, NORMAL, HIGH, HIGHEST, MONITOR |
With AI (MCP)#
get_script_api returns this same surface to an assistant: call it with no argument for the index of API classes and script base classes, or with a class name (ScriptPlayer, NpcScript) for the full member list with signatures, summaries and which members are overridable hooks. read_server_script, write_server_script, move_server_script, delete_server_script and list_server_scripts work on the files themselves, and generate_script_project writes the IDE companion described in IDE setup. The full tool list is in the MCP tools reference.
