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
:
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 dialogs are just that - a dialog with no inputs, simply displaying text (elegantly named DisplayText) to a user.
Text Dialogs
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
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).