StendhalScripting/Lua: Difference between revisions
imported>AntumDeluge →Adding Transitions: nested tables |
imported>AntumDeluge add categories |
||
| (17 intermediate revisions by the same user not shown) | |||
| Line 24: | Line 24: | ||
== Variables == |
== Variables == |
||
By default, Lua variables are set in [https://en.wikipedia.org/wiki/Global_variable '''global''' scope] (meaning it is exposed to the entire Lua engine). To create a variable in [https://en.wikipedia.org/wiki/Local_variable '''local''' scope], the <code>local</code> keyword must be used: |
|||
<pre> |
<pre> |
||
-- a global variable |
-- a global variable |
||
| Line 31: | Line 31: | ||
-- a local variable |
-- a local variable |
||
local var2 = "Hello world!" |
local var2 = "Hello world!" |
||
</pre> |
|||
== Data Types == |
|||
Some common data types in Lua are ''string'', ''integer'', ''boolean'', & ''table''. Type names do not need to be declared when setting variables. |
|||
Examples: |
|||
<pre> |
|||
-- string variable |
|||
local var1 = "Hello world!" |
|||
-- integer variable |
|||
local var2 = 11 |
|||
-- boolean variable |
|||
local var3 = true |
|||
-- table variable |
|||
local var4 = {} |
|||
</pre> |
|||
=== Strings === |
|||
==== String Concatenation ==== |
|||
String concatenation is simple, much like Java uses a plus operator (<code>+</code>) to join strings, Lua uses two periods (<code>..</code>). |
|||
Example: |
|||
<pre> |
|||
-- create a string variable |
|||
local var = "Hello" |
|||
-- append another string |
|||
var = var .. " world!" |
|||
print(var) -- prints "Hello world!" |
|||
</pre> |
</pre> |
||
=== Tables === |
=== Tables === |
||
A Lua table is a data type similar to a list. Tables can be indexed or use key=value pairs. |
A Lua table is a data type similar to a Java list or map. Tables can be indexed or use key=value pairs. |
||
''(<span style="color:red;">IMPORTANT NOTE: Lua table indexes begin at 1, not 0</span>)'' |
''(<span style="color:red;">IMPORTANT NOTE: Lua table indexes begin at 1, not 0</span>)'' |
||
==== Creating Tables ==== |
|||
An empty table is initialized with a pair of curly braces (<code>{}</code>): |
An empty table is initialized with a pair of curly braces (<code>{}</code>): |
||
| Line 47: | Line 85: | ||
<pre> |
<pre> |
||
-- create a table with values |
-- create a table with values |
||
local mytable = { |
local mytable = {"foo"} |
||
"foo" |
|||
} |
|||
-- add value |
-- add value |
||
| Line 57: | Line 93: | ||
To create a key=value table, any of the following methods can be used to add values: |
To create a key=value table, any of the following methods can be used to add values: |
||
<pre> |
<pre> |
||
-- all of these do the same thing, that is, assigning "bar" to mytable.foo |
|||
local mytable { |
local mytable { |
||
foo = "bar", |
foo = "bar", |
||
["foo"] = "bar", |
["foo"] = "bar", |
||
} |
} |
||
mytable.foo = "bar" |
mytable.foo = "bar" |
||
mytable["foo"] = "bar" |
mytable["foo"] = "bar" |
||
</pre> |
|||
==== Accessing Table Values ==== |
|||
Square brackets (<code>[]</code>) enclosing an index number are used to access values in indexed tables (''remember that Lua table indexes start at "1" not "0"''): |
|||
<pre> |
|||
local mytable = {"foo", "bar"} |
|||
print(mytable[1]) -- prints "foo" |
|||
print(mytable[2]) -- prints "bar" |
|||
</pre> |
|||
In a key=value table, values can be accessed by either enclosing the key string in square brackets or concatenating the key member using a <code>.</code>: |
|||
<pre> |
|||
local mytable = {foo="bar"} |
|||
-- using square brackets |
|||
print(mytable["foo"]) -- prints "bar" |
|||
-- using concatenated member |
|||
print(mytable.foo) -- prints "bar" |
|||
</pre> |
</pre> |
||
==== Iterating Tables ==== |
==== Iterating Tables ==== |
||
Tables can be iterated in a |
Tables can be iterated in a <code>for</code> loop using the <code>pairs</code> or <code>ipairs</code> iterators. Loops are terminated with the <code>end</code> keyword: |
||
<pre> |
<pre> |
||
local mytable = { |
local mytable = {"foo", "bar"} |
||
"foo", |
|||
"bar", |
|||
} |
|||
print("indexes:") |
print("indexes:") |
||
| Line 132: | Line 186: | ||
Like normal variables, functions can be declared as '''global''' or '''local''' & must be terminated with the <code>end</code> keyword. |
Like normal variables, functions can be declared as '''global''' or '''local''' & must be terminated with the <code>end</code> keyword. |
||
There are two ways to |
There are two ways to define functions with the <code>function</code> keyword: |
||
<pre> |
<pre> |
||
local function myFunction() |
local function myFunction() |
||
| Line 146: | Line 200: | ||
</pre> |
</pre> |
||
Functions can also be |
Functions can also be members of a table: |
||
<pre> |
<pre> |
||
local myTable = {} |
local myTable = {} |
||
| Line 174: | Line 228: | ||
</pre> |
</pre> |
||
== Comparison Operators == |
|||
= Stendhal Application = |
|||
{| class="wikitable" |
|||
== Objects and Functions == |
|||
|+ Logical Operators |
|||
! Operator !! Description !! Java Equivalent |
|||
|- |
|||
| and || logical ''and'' || && |
|||
|- |
|||
| or || logical ''or'' || <nowiki>||</nowiki> |
|||
|- |
|||
| not || logical ''opposite'' || ! |
|||
|} |
|||
{| class="wikitable" |
|||
The following objects & functions are exposed to the Lua engine: |
|||
|+ Relational Operators |
|||
! Operator !! Description !! Java Equivalent |
|||
|- |
|||
| < || less than || < |
|||
|- |
|||
| > || greater than || > |
|||
|- |
|||
| <= || less than or equal to || <= |
|||
|- |
|||
| >= || greater than or equal to || >= |
|||
|- |
|||
| == || equal to || == |
|||
|- |
|||
| ~= || not equal to || != |
|||
|} |
|||
= Stendhal Application = |
|||
=== luajava === |
|||
== Zones == |
|||
This is an object of the LuajavaLib library. It can be used to coerce Java static objects to Lua or create new Java object instances. |
|||
=== Setting Zone === |
|||
Example of exposing a static object & enums to Lua: |
|||
<pre> |
|||
-- store a Java enum in a Lua global variable |
|||
ConversationStates = luajava.bindClass("games.stendhal.server.entity.npc.ConversationStates") |
|||
To set the zone to work with, use the <code>game</code> object: |
|||
-- access the enum values like so |
|||
ConversationStates.IDLE |
|||
</pre> |
|||
Example of creating an object instance: |
|||
<pre> |
<pre> |
||
game:setZone("0_semos_city") |
|||
-- store instance in local variable |
|||
local dog = luajava.newInstance("games.stendhal.server.entity.npc.SilentNPC") |
|||
-- access object methods like so |
|||
dog:setEntityClass("animal/puppy") |
|||
dog:setPosition(2, 5) |
|||
-- class with constructor using parameters |
|||
local speaker = luajava.newInstance("games.stendhal.server.entity.npc.SpeakerNPC", "Frank") |
|||
speaker:setOutfit("body=0,head=0,eyes=0,hair=5,dress=5") |
|||
speaker:setPosition(2, 6) |
|||
</pre> |
</pre> |
||
=== Create New Zone === |
|||
To make scripting easier, Stendhal employs a {{StendhalFile|master|src/games/stendhal/server/core/scripting/lua/init.lua|master script}} & some helper objects & methods to handle the functionality mentioned above. An explanation of these objects & methods follows. |
|||
It is recommended to create new zones in the XML configurations in {{StendhalFile|master|data/conf/zones|data/conf/zones}}. |
|||
=== game === |
|||
Currently creating new zones via Lua is not supported. |
|||
The main object that handles setting zone & adding entities to game. |
|||
=== Add Zone Music === |
|||
Methods: |
|||
* <code>game:add(object)</code> - Adds an object to the current zone. |
|||
* <code>game:setZone(name)</code> - Sets the current zone. |
|||
* <code>game:createSign(visible)</code> - Creates a new {{StendhalFile|master|src/games/stendhal/server/entity/mapstuff/sign/Sign.java|Sign}} instance. |
|||
* <code>game:createShopSign(name, title, caption, seller)</code> - Creates a new {{StendhalFile|master|src/games/stendhal/server/entity/mapstuff/sign/ShopSign.java|ShopSign}} instance. |
|||
=== npcHelper === |
|||
This object helps to create instances of {{StendhalFile|master|src/games/stendhal/server/entity/npc/SpeakerNPC.java|SpeakerNPC}} & {{StendhalFile|master|src/games/stendhal/server/entity/npc/SilentNPC.java|SilentNPC}} classes. |
|||
Methods: |
|||
* <code>npcHelper:createSpeakerNPC(name)</code> - Creates a new SpeakerNPC. |
|||
* <code>npcHelper:createSilentNPC()</code> - Creates a new SilentNPC. |
|||
* <code>npcHelper:setPath(npc, path, loop)</code> - Sets the path for the specified NPC. |
|||
* <code>npcHelper:setPathAndPosition(npc, path, loop)</code> - Sets the path & starting position of the specified NPC. |
|||
* <code>npcHelper:addMerchant(merchantType, npc, items, offer)</code> - Adds merchant behavior to <code>npc</code> of either a buyer or seller defined by <code>merchantType</code>. |
|||
* <code>npcHelper:addSeller(npc, items, offer)</code> - Adds seller merchant behavior to <code>npc</code>. |
|||
* <code>npcHelper:addBuyer(npc, items, offer)</code> - Adds buyer merchant behavior to <code>npc</code>. |
|||
== Setting Zone == |
|||
To set the zone to work with, use the <code>game</code> object: |
|||
<pre> |
|||
game:setZone("0_semos_city") |
|||
</pre> |
|||
Music can be added to zones with the <code>game:setMusic</code> function. It supports the following arguments: |
|||
* <span style="color:darkgreen; font-style:italic;>filename:</span> Basename of the OGG audio file to use stored in {{StendhalFile|master|data/music|data/music}}. |
|||
* <span style="color:darkgreen; font-style:italic;>args:</span> A table of key=value integers. |
|||
* Valid keys: |
|||
** <span style="color:darkblue; font-style:italic;">volume:</span> Volume level (default: 100). |
|||
** <span style="color:darkblue; font-style:italic;">x:</span> The horizontal point for the source of the music (default: 1). |
|||
** <span style="color:darkblue; font-style:italic;">y:</span> The vertical point for the source of the music (default: 1). |
|||
** <span style="color:darkblue; font-style:italic;">radius:</span> The radial range at which the music can be heard (default: 10000). |
|||
Example: |
|||
<pre> |
<pre> |
||
if game:setZone("0_semos_plains_n") then |
|||
local zone = "0_semos_city" |
|||
game:setMusic("pleasant_creek_loop", {volume=85, radius=100}) |
|||
if game:setZone(zone) then |
|||
-- do something |
|||
else |
|||
logger:error("Could not set zone: " .. zone) |
|||
end |
end |
||
</pre> |
</pre> |
||
| Line 255: | Line 298: | ||
=== Signs === |
=== Signs === |
||
Signs can be created with <code> |
Signs can be created with <code>entities:createSign</code> and <code>entities:createShopSign</code>: |
||
<pre> |
<pre> |
||
| Line 261: | Line 304: | ||
if game:setZone(zone) then |
if game:setZone(zone) then |
||
-- create the sign instance |
-- create the sign instance |
||
local sign = |
local sign = entities:createSign() |
||
sign:setEntityClass("signpost") |
sign:setEntityClass("signpost") |
||
sign:setPosition(12, 55) |
sign:setPosition(12, 55) |
||
| Line 275: | Line 318: | ||
=== NPCs === |
=== NPCs === |
||
Use the <code> |
Use the <code>entities:createSpeakerNPC</code> method to create an interactive NPC: |
||
<pre> |
<pre> |
||
| Line 281: | Line 324: | ||
if game:setZone(zone) then |
if game:setZone(zone) then |
||
-- Use helper object to create a new NPC |
-- Use helper object to create a new NPC |
||
local npc = |
local npc = entities:createSpeakerNPC("Lua") |
||
npc:setEntityClass("littlegirlnpc") |
npc:setEntityClass("littlegirlnpc") |
||
npc:setPosition(10, 55) |
npc:setPosition(10, 55) |
||
| Line 294: | Line 337: | ||
} |
} |
||
npc:setPath(nodes) |
|||
-- Use helper object to create NPC path |
|||
npcHelper:setPath(npc, nodes) |
|||
-- Dialogue |
-- Dialogue |
||
| Line 312: | Line 354: | ||
A simple example of adding a chat transition can be done without any special functionality: |
A simple example of adding a chat transition can be done without any special functionality: |
||
<pre> |
<pre> |
||
local frank = |
local frank = entities:createSpeakerNPC("Frank") |
||
frank:add(ConversationStates.IDLE, |
frank:add(ConversationStates.IDLE, |
||
ConversationPhrases.GREETING_MESSAGES, |
ConversationPhrases.GREETING_MESSAGES, |
||
| Line 321: | Line 363: | ||
</pre> |
</pre> |
||
This simply adds a response to saying "hello" & sets the NPC to attend to the player. |
This simply adds a response to saying "hello" & sets the NPC to attend to the player (equivalent of <code>frank:addGreeting("Hello")</code>). |
||
For more complicated behavior, we need to use some helper methods. If we want to check a condition we use the <code> |
For more complicated behavior, we need to use some helper methods. If we want to check a condition we use the <code>conditions:create</code> method. The first parameter is the string name of the ChatCondition we want to instantiate. The second parameter is a table that contains the values that should be passed to the ChatCondition constructor. |
||
Example: |
|||
<pre> |
<pre> |
||
frank:add(ConversationStates.IDLE, |
frank:add(ConversationStates.IDLE, |
||
ConversationPhrases.GREETING_MESSAGES, |
ConversationPhrases.GREETING_MESSAGES, |
||
conditions:create("PlayerHasItemWithHimCondition", {"money"}), |
|||
ConversationStates.ATTENDING, |
ConversationStates.ATTENDING, |
||
"Hello.", |
"Hello.", |
||
| Line 335: | Line 379: | ||
In this scenario, the NPC will only respond if the player is carrying <item>money</item>. |
In this scenario, the NPC will only respond if the player is carrying <item>money</item>. |
||
A NotCondition instance can be created with the <code> |
A NotCondition instance can be created with the <code>actions:notCondition</code> method: |
||
Example usage: |
|||
<pre> |
<pre> |
||
local condition = conditions.notCondition(conditions:create("PlayerHasItemWithHimCondition", {"money"}) |
|||
</pre> |
</pre> |
||
To add a ChatAction, we use the <code> |
To add a ChatAction, we use the <code>actions:create</code> method. Its usage is identical to <code>conditions:create</code>. |
||
Example: |
|||
<pre> |
<pre> |
||
frank:add(ConversationStates.IDLE, |
frank:add(ConversationStates.IDLE, |
||
ConversationPhrases.GREETING_MESSAGES, |
ConversationPhrases.GREETING_MESSAGES, |
||
conditions:create("PlayerHasItemWithHimCondition", {"money"}), |
|||
ConversationStates.ATTENDING, |
ConversationStates.ATTENDING, |
||
"Hello.", |
"Hello.", |
||
actions:create("NPCEmoteAction", {"looks greedily at your pouch of money.", false})) |
|||
</pre> |
</pre> |
||
| Line 355: | Line 403: | ||
ConversationPhrases.GREETING_MESSAGES, |
ConversationPhrases.GREETING_MESSAGES, |
||
{ |
{ |
||
conditions:create("PlayerHasItemWithHimCondition", {"money"}), |
|||
conditions:notCondition(conditions:create("NakedCondition")), |
|||
}, |
}, |
||
ConversationStates.ATTENDING, |
ConversationStates.ATTENDING, |
||
nil, |
nil, |
||
{ |
{ |
||
actions:create("SayTextAction", {"Hello."}), |
|||
actions:create("NPCEmoteAction", {"looks greedily at your pouch of money.", false}), |
|||
}) |
}) |
||
</pre> |
</pre> |
||
| Line 371: | Line 419: | ||
<pre> |
<pre> |
||
local conditions = { |
local conditions = { |
||
conditions:create("PlayerHasItemWithHimCondition", {"money"}), |
|||
{ |
{ |
||
conditions:notCondition(conditions:create("NakedCondition")), |
|||
}, |
}, |
||
} |
} |
||
| Line 383: | Line 431: | ||
nil, |
nil, |
||
{ |
{ |
||
actions:create("SayTextAction", {"Hello."}), |
|||
actions:create("NPCEmoteAction", {"looks greedily at your pouch of money.", false}), |
|||
}) |
}) |
||
</pre> |
</pre> |
||
==== Adding Merchant Behavior ==== |
==== Adding Merchant Behavior ==== |
||
The <code>merchants</code> object is used for adding merchant behavior (buying/selling) to an NPC. |
|||
Merchant behavior ''(buying/selling)'' can be set with one of the following helper functions: |
|||
* ''npcHelper:addMerchant(merchantType, npc, prices, addOffer)'' |
|||
* ''npcHelper:addBuyer(npc, prices, addOffer)'' |
|||
* ''npcHelper:addSeller(npc, prices, addOffer)'' |
|||
** Arguments: |
|||
*** ''merchantType:'' (string) If set to "buyer", will add buyer behavior, otherwise will be "seller" (may change type to boolean in future). |
|||
*** ''npc:'' (SpeakerNPC) The NPC to add the behavior to. |
|||
*** ''prices:'' (Map<String, Integer> or LuaTable) List of items & their prices. |
|||
*** ''addOffer:'' (boolean) If <code>true</code>, will add default replies for "offer". |
|||
Example of adding seller behavior to an NPC: |
Example of adding seller behavior to an NPC: |
||
<pre> |
<pre> |
||
if game:setZone("0_semos_city") then |
if game:setZone("0_semos_city") then |
||
local frank = |
local frank = entities.createSpeakerNPC("Frank") |
||
merchants:addSeller(frank, merchants.shops:get("shopname"), true) |
|||
game:add(frank) |
game:add(frank) |
||
| Line 440: | Line 479: | ||
Then add the seller behavior using the custom list: |
Then add the seller behavior using the custom list: |
||
<pre> |
<pre> |
||
merchants:addSeller(frank, priceList, true) |
|||
</pre> |
</pre> |
||
== System Properties == |
|||
Java's system properties are exposed to Lua with the <code>properties</code> object. |
|||
Examples: |
|||
<pre> |
|||
-- property state |
|||
if properties:enabled("stendhal.testserver") then |
|||
print("Test server enabled") |
|||
if properties:equals("stendhal.testserver", "junk") then |
|||
print("Junk enabled") |
|||
else |
|||
print("Junk disabled") |
|||
end |
|||
else |
|||
print("Test server disabled") |
|||
end |
|||
-- property value |
|||
local prop = properties:getValue("stendhal.testserver") |
|||
if prop ~= nil then |
|||
print("Test server enabled") |
|||
if prop == "junk" then |
|||
print("Junk enabled") |
|||
else |
|||
print("Junk disabled") |
|||
end |
|||
else |
|||
print("Test server disabled") |
|||
end |
|||
</pre> |
|||
== Misc == |
|||
=== Typecasting === |
|||
Lua does not support typecasting (as far as I know), but if the class you want to cast to has a copy constructor, achieving the same functionality is quite simple. |
|||
<pre> |
|||
-- "entities:getItem" returns an instance of Item |
|||
local bestiary = entities:getItem("bestiary") |
|||
-- in order to use the bestiary's "setOwner" method, we must convert it to an "OwnedItem" instance by calling its copy constructor |
|||
bestiary = luajava.newInstance("games.stendhal.server.entity.item.OwnedItem", bestiary) |
|||
bestiary:setOwner("Ted") |
|||
</pre> |
|||
= See Also = |
|||
* [[StendhalScripting/LuaAPI|Lua API]] |
|||
[[Category:Stendhal]] |
|||
[[Category:Documentation]] |
|||
[[Category:API]] |
|||
[[Category:Scripting]] |
|||
[[Category:Lua]] |
|||
Latest revision as of 21:56, 18 January 2022
this page is a work-in progress
Stendhal supports Lua scripting via the LuaJ library.
Lua scripts end in the .lua extension & are stored in the data/script directory.
Lua Basics
For more detailed information, see the Lua reference manual.
Comments
Lua uses double dashes (--) for single line comments & double dashes followed by double square brackets ([[) & closed with double square brackets (]]) for multi-line comments:
-- a single line comment --[[ a multi-line comment ]]
Variables
By default, Lua variables are set in global scope (meaning it is exposed to the entire Lua engine). To create a variable in local scope, the local keyword must be used:
-- a global variable var1 = "Hello world!" -- a local variable local var2 = "Hello world!"
Data Types
Some common data types in Lua are string, integer, boolean, & table. Type names do not need to be declared when setting variables.
Examples:
-- string variable
local var1 = "Hello world!"
-- integer variable
local var2 = 11
-- boolean variable
local var3 = true
-- table variable
local var4 = {}
Strings
String Concatenation
String concatenation is simple, much like Java uses a plus operator (+) to join strings, Lua uses two periods (..).
Example:
-- create a string variable local var = "Hello" -- append another string var = var .. " world!" print(var) -- prints "Hello world!"
Tables
A Lua table is a data type similar to a Java list or map. Tables can be indexed or use key=value pairs.
(IMPORTANT NOTE: Lua table indexes begin at 1, not 0)
Creating Tables
An empty table is initialized with a pair of curly braces ({}):
local mytable = {}
You can add values to indexed tables at initialization or with the table.insert method:
-- create a table with values
local mytable = {"foo"}
-- add value
table.insert(mytable, "bar")
To create a key=value table, any of the following methods can be used to add values:
-- all of these do the same thing, that is, assigning "bar" to mytable.foo
local mytable {
foo = "bar",
["foo"] = "bar",
}
mytable.foo = "bar"
mytable["foo"] = "bar"
Accessing Table Values
Square brackets ([]) enclosing an index number are used to access values in indexed tables (remember that Lua table indexes start at "1" not "0"):
local mytable = {"foo", "bar"}
print(mytable[1]) -- prints "foo"
print(mytable[2]) -- prints "bar"
In a key=value table, values can be accessed by either enclosing the key string in square brackets or concatenating the key member using a .:
local mytable = {foo="bar"}
-- using square brackets
print(mytable["foo"]) -- prints "bar"
-- using concatenated member
print(mytable.foo) -- prints "bar"
Iterating Tables
Tables can be iterated in a for loop using the pairs or ipairs iterators. Loops are terminated with the end keyword:
local mytable = {"foo", "bar"}
print("indexes:")
for idx in pairs(mytable) do
print(idx)
end
print("\nvalues:")
for idx, value in pairs(mytable) do
print(value)
end
Output:
indexes: 1 2 values: foo bar
Using a key=value table:
local mytable = {
["foo"] = "hello",
["bar"] = " world!",
}
print("keys:")
for key in pairs(mytable) do
print(key)
end
print("\nvalues:")
for key, value in pairs(mytable) do
print(value)
end
Output:
keys: foo bar values: hello world!
See also: Lua Tables Tutorial
Functions
Like normal variables, functions can be declared as global or local & must be terminated with the end keyword.
There are two ways to define functions with the function keyword:
local function myFunction()
print("Hello world!")
end
or
local myFunction = function()
print("Hello world!")
end
Functions can also be members of a table:
local myTable = {}
function myTable.myFunction()
print("Hello world!")
end
or
local myTable = {}
myTable.myFunction = function()
print("Hello world!")
end
or
local myTable = {
myFunction = function()
print("Hello world!")
end,
}
-- execute with
myTable.myFunction()
Comparison Operators
| Operator | Description | Java Equivalent |
|---|---|---|
| and | logical and | && |
| or | logical or | || |
| not | logical opposite | ! |
| Operator | Description | Java Equivalent |
|---|---|---|
| < | less than | < |
| > | greater than | > |
| <= | less than or equal to | <= |
| >= | greater than or equal to | >= |
| == | equal to | == |
| ~= | not equal to | != |
Stendhal Application
Zones
Setting Zone
To set the zone to work with, use the game object:
game:setZone("0_semos_city")
Create New Zone
It is recommended to create new zones in the XML configurations in data/conf/zones.
Currently creating new zones via Lua is not supported.
Add Zone Music
Music can be added to zones with the game:setMusic function. It supports the following arguments:
- filename: Basename of the OGG audio file to use stored in data/music.
- args: A table of key=value integers.
- Valid keys:
- volume: Volume level (default: 100).
- x: The horizontal point for the source of the music (default: 1).
- y: The vertical point for the source of the music (default: 1).
- radius: The radial range at which the music can be heard (default: 10000).
Example:
if game:setZone("0_semos_plains_n") then
game:setMusic("pleasant_creek_loop", {volume=85, radius=100})
end
Adding Entities
Signs
Signs can be created with entities:createSign and entities:createShopSign:
local zone = "0_semos_city"
if game:setZone(zone) then
-- create the sign instance
local sign = entities:createSign()
sign:setEntityClass("signpost")
sign:setPosition(12, 55)
sign:setText("Meet Lua!")
-- Add it to the world
game:add(sign)
else
logger:error("Could not set zone: " .. zone)
end
NPCs
Use the entities:createSpeakerNPC method to create an interactive NPC:
local zone = "0_semos_city"
if game:setZone(zone) then
-- Use helper object to create a new NPC
local npc = entities:createSpeakerNPC("Lua")
npc:setEntityClass("littlegirlnpc")
npc:setPosition(10, 55)
npc:setBaseSpeed(0.1)
npc:setCollisionAction(CollisionAction.STOP)
local nodes = {
{10, 55},
{11, 55},
{11, 56},
{10, 56},
}
npc:setPath(nodes)
-- Dialogue
npc:addJob("Actually, I am jobless.")
npc:addGoodbye();
-- Add to the world
game:add(npc)
else
logger:error("Could not set zone: " .. zone)
end
Adding Transitions
A simple example of adding a chat transition can be done without any special functionality:
local frank = entities:createSpeakerNPC("Frank")
frank:add(ConversationStates.IDLE,
ConversationPhrases.GREETING_MESSAGES,
nil,
ConversationStates.ATTENDING,
"Hello.",
nil)
This simply adds a response to saying "hello" & sets the NPC to attend to the player (equivalent of frank:addGreeting("Hello")).
For more complicated behavior, we need to use some helper methods. If we want to check a condition we use the conditions:create method. The first parameter is the string name of the ChatCondition we want to instantiate. The second parameter is a table that contains the values that should be passed to the ChatCondition constructor.
Example:
frank:add(ConversationStates.IDLE,
ConversationPhrases.GREETING_MESSAGES,
conditions:create("PlayerHasItemWithHimCondition", {"money"}),
ConversationStates.ATTENDING,
"Hello.",
nil)
In this scenario, the NPC will only respond if the player is carrying <item>money</item>.
A NotCondition instance can be created with the actions:notCondition method:
Example usage:
local condition = conditions.notCondition(conditions:create("PlayerHasItemWithHimCondition", {"money"})
To add a ChatAction, we use the actions:create method. Its usage is identical to conditions:create.
Example:
frank:add(ConversationStates.IDLE,
ConversationPhrases.GREETING_MESSAGES,
conditions:create("PlayerHasItemWithHimCondition", {"money"}),
ConversationStates.ATTENDING,
"Hello.",
actions:create("NPCEmoteAction", {"looks greedily at your pouch of money.", false}))
Lua tables can be used to add multiple conditions or actions:
frank:add(ConversationStates.IDLE,
ConversationPhrases.GREETING_MESSAGES,
{
conditions:create("PlayerHasItemWithHimCondition", {"money"}),
conditions:notCondition(conditions:create("NakedCondition")),
},
ConversationStates.ATTENDING,
nil,
{
actions:create("SayTextAction", {"Hello."}),
actions:create("NPCEmoteAction", {"looks greedily at your pouch of money.", false}),
})
In this scenario, the NPC will respond if the player has money & is not naked.
Nested tables are supported as well:
local conditions = {
conditions:create("PlayerHasItemWithHimCondition", {"money"}),
{
conditions:notCondition(conditions:create("NakedCondition")),
},
}
frank:add(ConversationStates.IDLE,
ConversationPhrases.GREETING_MESSAGES,
conditions,
ConversationStates.ATTENDING,
nil,
{
actions:create("SayTextAction", {"Hello."}),
actions:create("NPCEmoteAction", {"looks greedily at your pouch of money.", false}),
})
Adding Merchant Behavior
The merchants object is used for adding merchant behavior (buying/selling) to an NPC.
Example of adding seller behavior to an NPC:
if game:setZone("0_semos_city") then
local frank = entities.createSpeakerNPC("Frank")
merchants:addSeller(frank, merchants.shops:get("shopname"), true)
game:add(frank)
end
To create a custom shop list, you can use a Lua table (there are multiple ways to add elements to a Lua table):
Method 1:
local priceList = {
meat = 50,
["ham"] = 70,
}
Method 2:
local priceList = {}
priceList.meat = 50
priceList["ham"] = 70
The helper methods have special handling for underscore characters as well (the following are all the same):
local priceList = {
smoked_ham = 100,
["smoked ham"] = 100,
}
priceList.smoked_ham = 100
priceList["smoked ham"] = 100
Then add the seller behavior using the custom list:
merchants:addSeller(frank, priceList, true)
System Properties
Java's system properties are exposed to Lua with the properties object.
Examples:
-- property state
if properties:enabled("stendhal.testserver") then
print("Test server enabled")
if properties:equals("stendhal.testserver", "junk") then
print("Junk enabled")
else
print("Junk disabled")
end
else
print("Test server disabled")
end
-- property value
local prop = properties:getValue("stendhal.testserver")
if prop ~= nil then
print("Test server enabled")
if prop == "junk" then
print("Junk enabled")
else
print("Junk disabled")
end
else
print("Test server disabled")
end
Misc
Typecasting
Lua does not support typecasting (as far as I know), but if the class you want to cast to has a copy constructor, achieving the same functionality is quite simple.
-- "entities:getItem" returns an instance of Item
local bestiary = entities:getItem("bestiary")
-- in order to use the bestiary's "setOwner" method, we must convert it to an "OwnedItem" instance by calling its copy constructor
bestiary = luajava.newInstance("games.stendhal.server.entity.item.OwnedItem", bestiary)
bestiary:setOwner("Ted")