StendhalScripting/Lua: Difference between revisions
Jump to navigation
Jump to search
Content deleted Content added
imported>AntumDeluge →Setting Zone: add zone music |
imported>AntumDeluge add categories |
||
| (16 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 === |
|||
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. |
|||
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") |
|||
-- access the enum values like so |
|||
ConversationStates.IDLE |
|||
</pre> |
|||
Example of creating an object instance: |
|||
<pre> |
|||
-- 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> |
|||
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. |
|||
=== game === |
|||
The main object that handles setting zone & adding entities to game. |
|||
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>. |
|||
== Zones == |
== Zones == |
||
| Line 240: | Line 268: | ||
<pre> |
<pre> |
||
game:setZone("0_semos_city") |
game:setZone("0_semos_city") |
||
</pre> |
|||
The logger is exposed to Lua via the <code>logger</code> object: |
|||
<pre> |
|||
local zone = "0_semos_city" |
|||
if game:setZone(zone) then |
|||
-- do something |
|||
else |
|||
logger:error("Could not set zone: " .. zone) |
|||
end |
|||
</pre> |
</pre> |
||
| Line 261: | Line 278: | ||
=== Add Zone Music === |
=== Add Zone Music === |
||
Music can be added to zones with the <code> |
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. |
|||
* ''volume:'' (optional) Volume level. |
|||
* Valid keys: |
|||
* ''x:'' (optional) The horizontal point for the source of the music. |
|||
** <span style="color:darkblue; font-style:italic;">volume:</span> Volume level (default: 100). |
|||
* ''y:'' (optional) The vertical point for the source of the music. |
|||
** <span style="color:darkblue; font-style:italic;">x:</span> The horizontal point for the source of the music (default: 1). |
|||
* ''radius:'' (optional) The radial range at which the music can be heard. |
|||
** <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: |
Example: |
||
<pre> |
<pre> |
||
if game:setZone("0_semos_plains_n") then |
if game:setZone("0_semos_plains_n") then |
||
game:setMusic("pleasant_creek_loop", {volume=85, radius=100}) |
|||
end |
end |
||
</pre> |
</pre> |
||
| Line 279: | 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 285: | 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 299: | Line 318: | ||
=== NPCs === |
=== NPCs === |
||
Use the <code> |
Use the <code>entities:createSpeakerNPC</code> method to create an interactive NPC: |
||
<pre> |
<pre> |
||
| Line 305: | 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 318: | Line 337: | ||
} |
} |
||
npc:setPath(nodes) |
|||
-- Use helper object to create NPC path |
|||
npcHelper:setPath(npc, nodes) |
|||
-- Dialogue |
-- Dialogue |
||
| Line 336: | 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 345: | 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 359: | 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 379: | 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 395: | Line 419: | ||
<pre> |
<pre> |
||
local conditions = { |
local conditions = { |
||
conditions:create("PlayerHasItemWithHimCondition", {"money"}), |
|||
{ |
{ |
||
conditions:notCondition(conditions:create("NakedCondition")), |
|||
}, |
}, |
||
} |
} |
||
| Line 407: | 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 464: | 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]] |
|||