Hybrasyl Scripting Guide - hybrasyl/server GitHub Wiki

Hybrasyl Scripting - An Overview

This guide will help you to understand scripting in Hybrasyl. It is a living document and is being updated regularly as scripting components are implemented and/or changed.

Scripting Environment / Terminology

Scripting in Hybrasyl now uses Moonsharp. Each script has its own Lua state which lives for the entire lifetime of its associate. An associate is, simply put, the game object to which the script is attached (an item, an NPC, a reactor, etc). For instance, if we had a NPC named Foobar, foobar.lua would be the script associated with it, and associate in that script would mean our NPC.

Each script also has access to a world object for the purposes of using the exposed World API, which can be used for a variety of functions, but mainly, is used for creating dialogs and dialog sequences.

Scripts also have access to a variety of other predefined variables, depending on their context. See below.

We expose several .NET namespaces, as well, for advanced operations:

Hybrasyl.Enums - used for a variety of purposes (such as Enums.LegendColor, Enums.LegendIcon, Enums.Class etc.)

Hybrasyl.Scripting - The entire Scripting namespace itself is directly accessible to scripts.

Please note that after script instantiation the import function is disabled, for safety. :)

Lastly, the scripting log is now kept in scripting.log in the same directory as hybrasyl.log (generally speaking, Documents\Hybrasyl\scripting.log). Scripting errors will be displayed in this log only. You can also write directly to this log using WriteLog. When errors occur, they will be written to this logfile.

Script Handlers & Available Global Variables

The global object invoker and associate is generally always available for all of these functions, except OnSpawn (where it would make no sense).

Handler Objects Uses & Global Variables Available
OnSpawn All Called by every object when it is inserted into the world. Used to register dialogs, and do other "setup" work.
OnUse Items Called when a scripted item is used in the world. invoker is the person using the item; item will be set to the item object running the script.
OnDamage Monsters Called when a scripted monster is damaged. damage will have the integer amount of damage done to the monster; source will be an object representing the attacker.
OnHeal Monsters Called when a scripted monster is healed. heal will have the integer amount of healing done to the monster; source will be an object representing the healer.
OnEntry Reactor Called when a creature enters a reactor (a scripted tile). invoker will be the creature entering the reactor; source will be the reactor itself.
OnLeave Reactor Called when a creature leave a reactor (a scripted tile). invoker will be the creature entering the reactor; source will be the reactor itself.
OnTake Reactor Called when an item is picked up off a reactor (a scripted tile). invoker will be the creature picking up the item; source will be the reactor itself; item will be the item being picked up.
OnDrop Reactor Called when an item is dropped onto a reactor (a scripted tile). invoker will be the creature picking up the item; source will be the reactor itself; item will be the item being dropped.
OnHear All visible objects Called when speech (say or shout) occurs nearby an object. text is what was said; invoker is the object that said it; shout is a boolean representing whether it was shouted or not.

World Object

The world object is a wrapped HybrasylWorld object which has an exposed API to scripts. It is currently used to create dialog sequences and dialogs. All scripts in Hybrasyl have global access to this item.

An associate, as mentioned above, is the in-game object with which the running script is associated. This need not be an NPC, but in most cases, it will be. In general, this will be a HybrasylWorldObject with the following functionality:

void RegisterSequence(DialogSequence sequence, string name)

Registers a dialog sequence with an associate with a given name. This allows the sequence to be used later by StartSequence et al.

void RegisterGlobalSequence(HybrasylDialogSequence globalSequence, string name)

It is possible to register dialog sequences globally (e.g. they can be reused by multiple NPCs) with a given name.

void SetGreetingText(string text)

Set the greeting text (displayed at the main menu) for an associate. Note: This will override the XML setting.

Notes: Not yet implemented.

void AddPursuit(DialogSequence sequence)

Registers a dialog sequence with an associate and adds it to the list of DialogSequences that can be evaluated when the main menu is generated. By default, adding a DialogSequence with no check function (see below) will simply ensure it shows up on the main menu.

void AddPursuit(string globalSequenceName)

Add a previously registered global sequence to an associate's main menu.

void DisplayPursuits(HybrasylWorldObject invoker)

Display a main menu. This can be useful if you've overrided the OnClick functionality and still want to display a menu to a user.

void Destroy()

Destroys an object, removing it from the game. Currently, this only works with items and gold, for what should be obvious reasons.

void Say(string message)

Say the passed message out loud.

void Shout(string message)

Shout (yellow text) the passed message.

Invoker Object

The invoker object is generally a HybrasylUser or HybrasylWorldObject.

HybrasylUser (Player) Object

Properties

Hp - Current HP of a player, which can be modified (set to a discrete value) directly. Example: invoker.Hp = 1

Mp - Current MP of a player, can also can be modified directly.

Name - Player's name.

Functions

List<HybrasylWorldObject> GetViewportObjects()

Returns a list of everything in a player's viewport (e.g. what is on screen for them).

List<HybrasylUser> GetViewportPlayers()

Same as GetViewportObjects, but only return players.

void Resurrect()

Resurrect a user.

HybrasylUser GetFacingUser()

Get a user directly facing another user, if it exists. This is effectively the player "in front of" another player.

List<HybrasylWorldObject> GetFacingObjects()

Get a list of objects directly in front of a user.

EndComa()

End a player's coma (skulling).

LegendMark GetLegendMark(string prefix)

Return a given legend mark with the provided prefix. A prefix is effectively a key that is invisible to the player..

Legend GetLegend()

Return a player's legend.

bool AddLegendMark(Enums.LegendIcon icon, Enums.LegendColor color, string text, [string prefix, bool isPublic, int quantity])

Add a legend mark to a player's legend, with the icon icon, color color and text text. If a prefix is specified, add it to the legend (this text is invisible to the player). isPublic determines whether or not a legend mark can be seen by other players (private marks follow the convention of beginning with "- " when displayed to the player). A quantity can also be specified, so that the legend mark will read Foo Bar (8). The Default is no quantity, which means that simply text will be displayed. The date of the mark defaults to the current date and time.

Returns a boolean indicating success.

bool AddLegendMark(Enums.LegendIcon icon, Enums.LegendColor color, string text, DateTime created,[string prefix, bool isPublic, int quantity])

Same as above, except you can optionally set a DateTime to award a mark in the past. This functionality has not yet been tested since the move to Lua.

Returns a boolean indicating success.

bool RemoveLegendMark(string prefix)

Remove a legend mark with a given prefix from a player's legend.

Returns a boolean indicating success.

bool ModifyLegendMark(string prefix, int quantity, bool isPublic)

Modify a legend mark with a specified prefix, either updating its quantity to quantity or setting it public/private.

void SetSessionFlag(string flag, dynamic value)

Set a session flag with a name of flag to an arbitrary value. Flags can be used to control specific script behaviors (for instance: has a person done something before? Has a person killed a specific creature? Etc). Session flags are temporary, and expire on logoff.

void SetFlag(string flag, dynamic value)

Set a flag with a name of flag to an arbitrary value. Flags can be used to control specific script behaviors (for instance: has a person done something before? Has a person killed a specific creature? Etc). While session flags are temporary, flags are permanent.

string GetSessionFlag(string flag)

Return a given session flag named flag as a string vaue. If the flag does not exist, an empty string is returned.

string GetFlag(string flag)

Return a given flag named flag as a string value. If the flag does not exist, an empty string is returned.

void DisplayEffect(ushort effect, [short speed = 100, bool global = true])

Display a given effect (id effect) to a player, with client speed set to speed. If global is set to false, only the player will be able to see the effect, no one else.

void DisplayEffectAtCoords(short x, short y, ushort effect, [short speed = 100, bool global = true])

Display a given effect (id effect) at coordinates (x,y) on the player's map, with client speed set to speed. If global is set to false, only the player will be able to see the effect, no one else.

void Teleport(string location, short x, short y)

Teleport a given player to a map named location, at coordinates (x,y).

void SoundEffect(byte sound)

Play a sound for a player.

void HealToFull()

Heal a player to maximum HP.

void Heal(int heal)

Heal a player for heal HP.

void Damage(int damage, [Enums.Element element = Enums.Element.None, Enums.DamageType damageType = Enums.DamageType.Direct])

Damage a player for damage damage, optionally specifing an element (Fire, Earth, etc) and damage type (Direct, magical, etc).

bool GiveItem(string name)

Give a player the item named name. Returns true or false based on whether or not the item was successfully given to the player.

Notes: This function needs to support quantity, which will be added in the future

bool TakeItem(string name)

Take the given item named name from the user.

Notes: To be implemented, also needs to support quantity

void GiveExperience(int exp)

Award the specified amount of experience points exp to a player.

void TakeExperience(int exp)

Remove the specified amount of experience points exp from a player.

void SystemMessage(string message)

Display a system (orange) message to a user.

void Whisper(string name, string message)

Send a whisper (blue) message to a given user named name, from this user.

void Mail(string name, string message)

Send a letter containing message to a given user named name from this user.

Notes: Not yet implemented.

void StartDialogSequence(string sequenceName, HybrasylWorldObject associate)

Start a dialog sequence named sequenceName for a specific associate associate.

void StartSequence(string sequenceName, [HybrasylWorldObject associateOverride = null])

Start a dialog sequence named sequenceName. Optionally, specify an associate to override.

NPC Interactions

Hybrasyl provides a variety of mechanisms for creating and engaging players with dialogs.

Dialogs are placed (in order) in DialogSequences, which can then either be registered with an associate (NPC, reactor, other game item) using RegisterSequence. You can also use AddPursuit to add the sequence to the main menu. Any scripted object in the game can potentially have a main menu, but this functionality is obviously most useful for NPCs.

An NPC's main menu is the composite of a number of potential menu options. NPCs can have functionality defined in XML (such as training skills, buying and selling items, etc) as well as script dialogs added by AddPursuit:

Main menu example

Dialog Sequences

Dialog sequences simply contain Dialogs in a specific order. They can be assigned a name which can be used to reference a sequence later.

Most of the time, you will simply interact with sequences at construction time, e.g. world:NewDialogSequence("My Cool Quest", dialog1, dialog2, dialog3).

Properties

Name - Returns the name of the given dialog sequence.

Functions

HybrasylDialogSequence(sequenceName, [bool closeOnEnd=false])

Constructor for dialog sequences. If closeOnEnd is false, at the end of the dialog sequence, if a user clicks Next, they will be returned to the main menu. Otherwise, their conversation with the NPC or object will end.

void AddDialog(HybrasylDialog dialog)

Add a given dialog to a dialog sequence.

void AddCheck(string expression)

Add a check (expression, a Lua expression that will return true or false) that can be used to determine whether or not this sequence will be displayed on a main menu. Note that this callback is only useful if the sequence has been added to the main menu using AddPursuit.

Dialogs

There are five different types of Dialogs supported in Hybrasyl: simple (SimpleDialog), input (InputDialog), options (OptionsDialog),text (TextDialog), and function (FunctionDialog). Currently, three are exposed for Hybrasyl scripting: simple, text, and option.

Simple Dialogs

Simple dialog example

Simple dialogs are just that - a dialog with no inputs, simply displaying text (elegantly named DisplayText) to a user.

Text Dialogs

Text dialog example

Text dialogs allow the user to input a response. They contain display text and top and bottom captions. Their input is generally handled by a Lua callback.

Option Dialogs

Simple dialog example

Option dialogs display a list of options to the user, along with display text.

Dialog Callbacks

Dialogs and dialog sequences can have a variety of callbacks. These callbacks allow fine-grained control over individual interactions, and also allow a dialog sequence to, say, display effects, cast spells, or generate more complex teractions. All callbacks are effectively string expressions that will be evaluated as Lua in the context of the script in which they are attached.

AddPreDisplayCallback(string expression)

Run expression before a dialog or dialog sequence is displayed / started.

AddPostDisplayCallback(string expression)

Run expression after a dialog or dialog sequence is displayed / started.

AddCheckCallback(string expression)

Run expression before a dialog or dialog sequence is displayed / started. This expression must evaluate to a boolean. If it does not, the scripting engine assumes the result is false.

If the expression evaluates to false, the dialog is not displayed, and if the dialog sequence contains a "next" dialog, that will be shown instead (it is effectively skipped). For dialog sequences, a CheckCallback can be used to programmatically determine whether or not a DialogSequence should be shown to a user OR whether or not it should be added to an NPC's main menu (AddPursuits will consider this as well). In the future, this functionality may be broken up into two separate callbacks if a use case presents itself.

Handling Dialog Responses

Generally, a handler can be defined to handle text and option dialogs. A handler can use the special template string %RESPONSE% which will be replaced at evaluation time with the response of the user. For a text dialog, this is the text input by the user; for an options dialog, it is the index of the dialog they selected.

A way to use this handler might be:

quantityLegend = world:NewDialogSequence('riona-legend-qty',
  world:NewTextDialog("Quantity", "Please enter a quantity", "and I will use that for the test.", 5, "AddLegendMarkWithQuantity(%RESPONSE%)"))

In this example, when a user enters in a quantity into this dialog, the function AddLegendMarkWithQuantity (a user defined function, here) will be called with the value entered into the text field.

Note that Hybrasyl will perform basic escaping and sanitization; it's still your responsiblity to ensure the value makes sense / is a number (for instance, tonumber in Lua can be used for this purpose).