The Mana server uses the scripting language LUA for scripting. This is a list of the script commands currently implemented in addition to the standard lua statements and functions.
Also, if you want to look at actual working samples, you can have a look at the example/serverdata/scripts folder in the Manaserv's source which has always the latest working set.
create_npc(string name, int spriteID, int gender, int x, int y, function talkfunct, function updatefunct)
Return value: A handle to the created NPC.
Creates a new NPC with the name name at the coordinates x:y which appears to the players with the appearence listed in their npcs.xml under spriteID and the gender gender. Every game tick the function updatefunct is called with the handle of the NPC. When a character talks to the NPC the function talkfunct is called with the NPC handle and the character handle.
For setting the gender you can use the constants defined in the libmana-constants.lua:
| 0 | GENDER_MALE |
| 1 | GENDER_FEMALE |
| 2 | GENDER_UNSPECIFIED |
mana.monster_create(int monsterID, int x, int y)
mana.monster_create(string monstername, int x, int y)
Return value: A handle to the created monster.
Spawns a new monster of type monsterID or monstername on the current map on the pixel coordinates x:y.
mana.monster_remove(handle monster)
Return value: True if removing the monster suceeded.
Remove the monster monster from the current map.
mana.trigger_create(int x, int y, int width, int height, string functionname, int arg, bool once)
Creates a new trigger area with the given height and width in pixels at the map position x:y in pixels. When a being steps into this area the function with the name functionname is called with the being handle and arg as arguments. When once is false the function is called every game tick the being is inside the area. When once is true it is only called again when the being leaves and reenters the area.
mana.effect_create(int id, int x, int y) mana.effect_create(int id, being b)
Triggers the effect id from the clients effects.xml (particle and/or sound) at map location x:y or on being b. This has no effect on gameplay.
Warning: Remember that clients might switch off particle effects for performance reasons. Thus you should not use this for important visual input.
mana.item_drop(int x, int y, int id[, int number])
mana.item_drop(int x, int y, string itemname[, int number])
Drops a stack of number items at the location x:y on the current map. If no number is given, one item will be dropped.
Return value: True if the drop on map succeeded, false otherwise.
do_message(handle npc, handle character, string message)
Warning: May only be called from an NPC talk function.
Shows an NPC dialog box on the screen of character ch displaying the string msg.
Idles the current thread until the user click “OK”.
do_choice(handle npc, handle character, item1, item2, ... itemN)
Return value: Number of the option the player selected (starting with 1).
Warning: May only be called from an NPC talk function.
Shows an NPC dialog box on the users screen with a number of dialog options to choose from. Idles the current thread until the user selects one or aborts the current thread when the user clicks “cancel”.
Items are either strings or tables of strings (indices are ignored, but presumed to be taken in order). So, do_choice(npc, ch, “A”, {“B”, “C”, “D”}, “E”) is the same as do_choice(npc, ch, “A”, “B”, “C”, “D”, “E”).
do_ask_integer(handle npc, handle character, min_num, max_num, [default_num])
Return value: The number the player entered into the field.
Warning: May only be called from an NPC talk function.
Shows a dialog box to the user which allows him to choose a number between min_num and max_num. If default_num is set this number will be uses as default. Otherwise min_num will be the default.
do_ask_string(handle npc, handle character)
Return value: The string the player entered.
Warning: May only be called from an NPC talk function.
Shows a dialog box to a user which allows him to enter a text.
mana.being_say(handle being, string message)
Makes being, which can be a character, monster or NPC, speak the string message as if it was entered by a player in the chat bar.
mana.chat_message(handle character, string message)
Outputs the string message in the chatlog of character which will appear as a private message from “Server”.
mana.announce(string message [, string sender])
Sends a global announce with the given message and sender. If no sender is passed “Server” will be used as sender.
mana.npc_trade(handle npc, handle character, bool mode, {int item1id, int item1amount, int item1cost}, ..., {int itemNid, int itemNamount, int itemNcost})
mana.npc_trade(handle npc, handle character, bool mode, {string item1name, int item1amount, int item1cost}, ..., {string itemNname, int itemNamount, int itemNcost})
Opens a trade window for character while talking with npc. mode is true for selling and false for buying. You have to set each items the NPC is buying/selling, the cost and the maximum amount in {}.
Note: If the fourth parameters (table type) is omitted or invalid, and the mode set to sell (true), the whole player inventory is then sellable.
N.B.: Be sure to put a value (item cost) parameter in your items.xml to permit the player to sell it when using this option.
Return values:
Examples:
-- "A buy sample." local buycase = mana.npc_trade(npc, ch, false, { {"Sword", 10, 20}, {"Bow", 10, 30}, {"Dagger", 10, 50} }) if buycase == 0 then do_message(npc, ch, "What do you want to buy?") elseif buycase == 1 then do_message(npc, ch, "I've got no items to sell.") else do_message(npc, ch, "Hmm, something went wrong... Ask a scripter to fix the buying mode!") end -- ... -- "Example: Let the player sell only pre-determined items." local sellcase = mana.npc_trade(npc, ch, true, { {"Sword", 10, 20}, {"Bow", 10, 30}, {"Dagger", 10, 200}, {"Knife", 10, 300}, {"Arrow", 10, 500}, {"Cactus Drink", 10, 25} }) if sellcase == 0 then do_message(npc, ch, "Here we go:") elseif sellcase == 1 then do_message(npc, ch, "I'm not interested by your items.") else do_message(npc, ch, "Hmm, something went wrong... Ask a scripter to fix me!") end -- ... -- "Example: Let the player sell every item with a 'value' parameter in the server's items.xml file local sellcase = mana.npc_trade(npc, ch, true) if sellcase == 0 then do_message(npc, ch, "Ok, what do you want to sell:") elseif sellcase == 1 then do_message(npc, ch, "I'm not interested by any of your items.") else do_message(npc, ch, "Hmm, something went wrong... Ask a scripter to fix this!") end
mana.chr_inv_count(handle character, bool inInventory, bool inEquipment, int id1, ..., int idN)
mana.chr_inv_count(handle character, bool inInventory, bool inEquipment, string name1, ..., string nameN)
The boolean values inInventory and inEquipment make possible to select whether equipped or carried items must be counted.
Return values: A number of integers with the amount of items id or name carried or equipped by the character.
mana.chr_inv_change(handle character, int id1, int number1, ..., int idN, numberN)
mana.chr_inv_change(handle character, string name1, int number1, ..., string nameN, numberN)
Return value: Boolean true on success, boolean false on failure.
Changes the number of items with the item ID id or name owned by character by number. You can change any number of items with this function by passing multiple id or name and number pairs. A failure can be caused by trying to take items the character doesn't possess.
Warning: When one of the operations fails the following operations are ignored but these before are executed. For that reason you should always check if the character possesses items you are taking away using mana.chr_inv_count.
mana.chr_get_inventory(character): table[]{slot, item id, name, amount}
used to get a full view of a character's inventory. This is not the preferred way to know whether an item is in the character's inventory: Use mana.chr_inv_count for simple cases.
Return value: A table containing all the info about the character's inventory. Empty slots are not listed.
Example of use:
local inventory_table = mana.chr_get_inventory(ch) for i = 1, #inventory_table do item_message = item_message.."\n"..inventory_table[i].slot..", " ..inventory_table[i].id..", "..inventory_table[i].name..", " ..inventory_table[i].amount end
mana.chr_get_equipment(character): table[](slot, item id, name)}
Used to get a full view of a character's equipment. This is not the preferred way to know whether an item is equipped: Use mana.chr_inv_count for simple cases.
Return value: A table containing all the info about the character's equipment. Empty slots are not listed.
Example of use:
local equipment_table = mana.chr_get_equipment(ch) for i = 1, #equipment_table do item_message = item_message.."\n"..equipment_table[i].slot..", " ..equipment_table[i].id..", "..equipment_table[i].name end
mana.chr_money(handle character)
Return value: The money currently owned by character
mana.chr_money_change(handle character, int amount)
Changes the money currently owned by character by amount.
Warning: Before reducing the money make sure to check if the character owns enough money using mana.chr_money.
get_quest_var(handle character, string name)
Return value: The quest variable named name for the given character.
Warning: Calling this function from a function different from a NPC talk function might result in bugs.
mana.chr_set_quest(handle character, string name, string value)
Sets the quest variable named name for the given character to the value value.
mana.being_walk(handle character, int pixelX, int pixelY, int walkSpeed)
Set the desired destination in pixels for the 'character'.
The 'WalkSpeed' is to be given in tiles per second. The average speed is 6.0 tiles per second.
mana.being_get_speed(handle character)
Get the current walk speed for the given 'character'.
mana.being_get_speed(handle character, float speed)
Set the walk speed for the given 'character'.
The 'speed' is to be given in tiles per second. The average speed is 6.0 tiles per second.
mana.being_damage(handle being, int damage, int delta, int accuracy, int type, int element)
Inflicts damage to being. The severity of the attack is between damage and (damage + delta) and is calculated using the normal damage calculation rules. The being has a chance to dodge the attack with its agility attribute. The accuracy decides how hard this is.
type affects which kind of armor and character attributes reduce the damage. It can be one of the following values:
| 0 | DAMAGE_PHYSICAL |
| 1 | DAMAGE_MAGICAL |
| 2 | DAMAGE_OTHER |
element decides how the element system changes the damage. The following values are possible:
| 0 | ELEMENT_NEUTRAL |
| 1 | ELEMENT_FIRE |
| 2 | ELEMENT_WATER |
| 3 | ELEMENT_EARTH |
| 4 | ELEMENT_AIR |
| 5 | ELEMENT_LIGHTNING |
| 6 | ELEMENT_METAL |
| 7 | ELEMENT_WOOD |
| 8 | ELEMENT_ICE |
Return Value: Actual HP reduction resulting from the attack.
mana.being_heal(handle being[, int value])
Restores value lost hit points to being. Value can be omitted to restore the being to full hit points.
While you can (ab)use this function to hurt a being by using a negative value you should rather use mana.being_damage for this purpose.
mana.being_get_name(handle being)
Return value: Name of the being.
mana.being_type(handle being)
Return value: Type of the given being. These type constants are defined in libmana-constants.lua:
| 0 | TYPE_ITEM |
| 1 | TYPE_ACTOR |
| 2 | TYPE_NPC |
| 3 | TYPE_MONSTER |
| 4 | TYPE_CHARACTER |
| 5 | TYPE_EFFECT |
| 6 | TYPE_OTHER |
mana.being_get_action(handle being)
Return value: Current action of the given being. These action constants are defined in libmana-constants.lua:
| 0 | ACTION_STAND |
| 1 | ACTION_WALK |
| 2 | ACTION_ATTACK |
| 3 | ACTION_SIT |
| 4 | ACTION_DEAD |
| 5 | ACTION_HURT |
mana.being_set_action(handle being, int action)
mana.being_get_direction(handle being)
Return value: Current direction of the given being. These direction constants are defined in libmana-constants.lua:
| 0 | DIRECTION_DEFAULT |
| 1 | DIRECTION_UP |
| 2 | DIRECTION_DOWN |
| 3 | DIRECTION_LEFT |
| 4 | DIRECTION_RIGHT |
| 5 | DIRECTION_INVALID |
mana.being_set_direction(handle being, int direction)
Sets the current direction of the given being. Directions are same as in mana.being_get_direction.
mana.chr_warp(handle character, int mapID, int posX, int posY)
mana.chr_warp(handle character, string mapName, int posX, int posY)
Teleports the character to the position posX:posY on the map with the ID number mapID or name mapName. The mapID can be substituted by nil to warp the character to a new position on the current map.
mana.posX(handle being)
Return value: The horizontal position of the being in pixels measured from the left border of the map it is currently on.
mana.posY(handle being)
Return value: The vertical position of the being in pixels measured from the upper border of the map it is currently on.
mana.being_get_base_attribute(handle being, int attribute_id)
Set the value of the being's base attribute to the 'new_value' parameter given. (It can be negative).
Return value: Returns nothing.
mana.being_set_base_attribute(handle being, int attribute_id, double new_value)
Return value: Returns the double value of the being's base attribute.
mana.being_get_modified_attribute(handle being, int attribute_id)
Return value: Returns the double value of the being's modified attribute.
The modified attribute is equal to the base attribute + currently applied modifiers.
To get to know how to configure and create modifiers, you can have a look at the attributes.xml file
and at the mana.being_apply_attribute_modifier() and mana.being_remove_attribute_modifier() lua functions.
Note also that items, equipment, and monsters attacks can cause attribute modifiers.
: This functions about applying and removing modifiers are still WIP, because some simplifications and renaming could occur.
mana.being_apply_attribute_modifier(handle being, int attribute_id, double value, unsigned int layer, [unsigned short duration, [unsigned int effect_id]])
Parameters description:
: Check this.)Return value: Returns nothing.
mana.being_remove_attribute_modifier(handle being, int attribute_id, double value, unsigned int layer)
Permits to remove an attribute modifier by giving its value and its layer.
Return value: Returns nothing.
mana.being_get_gender(handle being)
Return value: The gender of the being. These gender constants are defined in libmana-constants.lua:
| 0 | GENDER_MALE |
| 1 | GENDER_FEMALE |
| 2 | GENDER_UNSPECIFIED |
mana.being_set_gender(handle being, int gender)
Sets the gender of a being.
The gender constants are defined in libmana-constants.lua:
| 0 | GENDER_MALE |
| 1 | GENDER_FEMALE |
| 2 | GENDER_UNSPECIFIED |
mana.chr_get_level(handle character)
Return value: Returns the level of the character.
mana.chr_get_exp(handle character, int attribute)
Return value: The total experience collected by character in skill attribute.
mana.chr_give_exp(handle character, int attribute, int amount [, int optimalLevel])
Gives character amount experience in skill attribute. When an optimal level is set (over 0), the experience is reduced when the characters skill level is beyond this.
mana.exp_for_level(int level)
Return value: Returns the total experience necessary (counted from level 0) for reaching level in any skill.
mana.chr_get_hair_color(handle character)
Return value: The hair color ID of character
mana.chr_set_hair_color(handle character, int color)
Sets the hair color ID of character to color
mana.chr_get_hair_style(handle character)
Return value: The hair style ID of character
mana.chr_set_hair_style(handle character, int style)
Sets the hair style ID of character to style
mana.chr_get_kill_count(handle character, int monsterType)
Return value: The total number of monsters of type monsterType the character has killed during its career.
mana.chr_get_rights(handle character)
Return value: The access level of the account of character.
mana.monster_change_anger(handle monster, handle being, int anger)
Makes the monster more angry about the being by adding anger to the being.
mana.being_apply_status(handle Being, int status_id, int time)
Gives a being a status effect status_id, status effects don't work on NPCs. time is in game ticks.
mana.being_remove_status(handle Being, int status_id)
Removes a given status effect from a being.
mana.being_has_status(handle Being, int status_id)
Return value: Bool if the being has a given status effect.
mana.being_get_status_time(handle Being, int status_id)
Return Value: Number of ticks remaining on a status effect.
mana.being_set_status_time(handle Being, int status_id, int time)
Sets the time on a status effect a target being already has.
atinit(function() [function body] end)
Adds a function which is executed when the gameserver loads the map this script belongs to. Usually used for placing NPCs or trigger areas and for setting up cronjobs with schedule_every. Any number of functions can be added this way.
schedule_in(seconds, function() [function body] end)
Executes the function body in seconds seconds.
schedule_every(seconds, function() [function body] end)
Executes the function body every seconds seconds from now on.
schedule_per_date(year, month, day, hour, minute, function() [function body] end)
Executes the function body at the given date and time.
on_death(handle being, function() [function body] end)
Executes the function body when being is killed. Note that this doesn't happen anymore after the being left the map.
on_remove(handle being, function() [function body] end)
Executes the function body when being is no longer on the map for some reason (leaves the map voluntarily, is warped away, logs out, cleaned up after getting killed or whatever).
mana.monster_get_name(int id)
Return value: The name of the monster with the ID id
mana.item_get_name(int id)
Return value: The name of the item with the ID id
mana.get_map_id()
Return value: The ID number of the map the script runs on.
mana.get_map_property(string key)
Return value: The value of the property key of the current map. The string is empty if the property key does not exists.
mana.is_walkable(int x, int y)
Return value: True if x:y is a walkable pixel on the current map.
mana.map_get_objects()
mana.map_get_objects(string type)
Return value: A table of all objects or a table of all object of the given type.
mana.map_get_object_property(handle object, string key)
Return value: The value of the property key of the object.
mana.map_get_object_bounds(handle object)
Return value: x, y position and height, width of the object.
Example use:
local x, y, width, height = mana.map_get_object_bounds(my_object)
mana.get_object_name(handle object)
Return value: Name as set in the mapeditor of the object.
mana.get_object_type(handle object)
Return value: Type as set in the mapeditor of the object.
Be aware that due to a design flaw, all map-bound and global variables share the same namespace. You can also currently interact with map variables on other maps by referring to them as global variables. So you'd better not try to exploit this! You have been warned! A workaround is to prefix all map-bound variable names with a different prefix on each map and all global variables with yet a different prefix.
Details about the bug on http://bugs.manasource.org/view.php?id=310
mana.setvar_map(string key, string value)
Sets a persistent variable. The scope of the variable is the map the script runs on. It can later be retrieved using mana.getvar_map from any script running on the same map. The value is stored in the database and thus will survive a server reboot.
mana.getvar_map(string key)
Return value: Value of a map-bound variable.
mana.setvar_world(string key, string value)
Sets a persistent variable. The scope of the variable is the whole game world. It can later be retrieved using mana.getvar_world from any script running on any map. The value is stored in the database and thus will survive a server reboot.
When you are using this function, be aware of race conditions: It is impossible to prevent that another map changes the value of a variable between you requesting to old value and setting a new value.
mana.getvar_world(string key)
Return value: Value of a world-bound variable.
mana.log(int log_level, string log_entry)
Log something at the specified log level. The available log levels are:
| 0 | LOG_FATAL |
| 1 | LOG_ERROR |
| 2 | LOG_WARNING |
| 3 | LOG_INFO |
| 4 | LOG_DEBUG |
In order to easily use area of effects in your items or in your scripts, the following functions are available:
mana.get_beings_in_circle(int x, int y, int radius)
Return value: This function returns a lua table of all beings in a circle of radius radius centered at the pixel at (x, y).
mana.get_beings_in_rectangle(int x, int y, int width, int height)
Return value: An array of being objects.
mana.get_distance(handle being1, handle being2)
mana.get_distance(int x1, int y1, int x2, int y2)
Return value: The distance between the two beings or the two points in pixels.
With the introduction of the scripted Special System some new script bindings are needed which makes it easier for server administrators to define damage inflicting specials.
Possible missing area of effect functions:
Most servers will likely want a lot of special attacks which inflict damage based on the attributes of the attacker in the same way as a normal attack, just that they are stronger. To make this easier we need a function which makes being A attack being B with the normal combat rules, except that the damage and accuracy can be made stronger or weaker and that the element can be changed.
* perform_modified_attack(handle attacker, handle defender, float damage_factor, float damage_delta_factor, float accuracy_factor, int type, int element)
The float factors will be multipliers to the damage values created by a normal attack. Element can be nil to use the element of the currently equipped weapon.
IMO, we should have a separate article for each function, with code samples and all.
I Agree. Scripting really lacks some useful snippets. (If the per function article is too much, maybe per-section pages, then?)
The current state of the page (at the time of writing) is quite fine to me. The 'example' folder is also a good point of information and samples collection. Hence, I'd let it as is.