StendhalScripting/Lua: Difference between revisions

Jump to navigation Jump to search
Content deleted Content added
imported>AntumDeluge
Stendhal Application: checking system properties
imported>AntumDeluge
add categories
 
(11 intermediate revisions by the same user not shown)
Line 50: Line 50:
-- table variable
-- table variable
local var4 = {}
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>


Line 57: Line 74:


''(<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 66: 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 76: 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 ''for'' loop using the ''pairs'' or ''ipairs'' iterators. Loops are terminated with the <code>end</code> keyword:
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 151: 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 declare functions:
There are two ways to define functions with the <code>function</code> keyword:
<pre>
<pre>
local function myFunction()
local function myFunction()
Line 165: Line 200:
</pre>
</pre>


Functions can also be values in a table:
Functions can also be members of a table:
<pre>
<pre>
local myTable = {}
local myTable = {}
Line 224: Line 259:


= Stendhal Application =
= Stendhal Application =

== Objects and Functions ==

The following objects & functions are exposed to the Lua engine:

=== 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 289: 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 310: Line 278:
=== Add Zone Music ===
=== Add Zone Music ===


Music can be added to zones with the <code>setZoneMusic</code> global function. It supports the following arguments:
Music can be added to zones with the <code>game:setMusic</code> function. It supports the following arguments:
* ''filename:'' Basename of the OGG audio file to use stored in {{StendhalFile|master|data/music|data/music}}.
* <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
setZoneMusic("pleasant_creek_loop", 85)
game:setMusic("pleasant_creek_loop", {volume=85, radius=100})
end
end
</pre>
</pre>
Line 328: Line 298:
=== Signs ===
=== Signs ===


Signs can be created with <code>game:createSign</code> and <code>game:createShopSign</code>:
Signs can be created with <code>entities:createSign</code> and <code>entities:createShopSign</code>:


<pre>
<pre>
Line 334: Line 304:
if game:setZone(zone) then
if game:setZone(zone) then
-- create the sign instance
-- create the sign instance
local sign = game:createSign()
local sign = entities:createSign()
sign:setEntityClass("signpost")
sign:setEntityClass("signpost")
sign:setPosition(12, 55)
sign:setPosition(12, 55)
Line 348: Line 318:
=== NPCs ===
=== NPCs ===


Use the <code>game:createSpeakerNPC</code> method to create an interactive NPC:
Use the <code>entities:createSpeakerNPC</code> method to create an interactive NPC:


<pre>
<pre>
Line 354: 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 = npcHelper:createSpeakerNPC("Lua")
local npc = entities:createSpeakerNPC("Lua")
npc:setEntityClass("littlegirlnpc")
npc:setEntityClass("littlegirlnpc")
npc:setPosition(10, 55)
npc:setPosition(10, 55)
Line 367: Line 337:
}
}


npc:setPath(nodes)
-- Use helper object to create NPC path
npcHelper:setPath(npc, nodes)


-- Dialogue
-- Dialogue
Line 385: 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 = npcHelper:createSpeakerNPC("Frank")
local frank = entities:createSpeakerNPC("Frank")
frank:add(ConversationStates.IDLE,
frank:add(ConversationStates.IDLE,
ConversationPhrases.GREETING_MESSAGES,
ConversationPhrases.GREETING_MESSAGES,
Line 394: 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>newCondition</code> global function:
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,
newCondition("PlayerHasItemWithHimCondition", "money"),
conditions:create("PlayerHasItemWithHimCondition", {"money"}),
ConversationStates.ATTENDING,
ConversationStates.ATTENDING,
"Hello.",
"Hello.",
Line 408: 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>newNotCondition</code> global function:
A NotCondition instance can be created with the <code>actions:notCondition</code> method:

Example usage:
<pre>
<pre>
newNotCondition(newCondition("PlayerHasItemWithHimCondition", "money"))
local condition = conditions.notCondition(conditions:create("PlayerHasItemWithHimCondition", {"money"})
</pre>
</pre>


To add a ChatAction, we use the <code>newAction</code> global function:
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,
newCondition("PlayerHasItemWithHimCondition", "money"),
conditions:create("PlayerHasItemWithHimCondition", {"money"}),
ConversationStates.ATTENDING,
ConversationStates.ATTENDING,
"Hello.",
"Hello.",
newAction("NPCEmoteAction", "looks greedily at your pouch of money.", false))
actions:create("NPCEmoteAction", {"looks greedily at your pouch of money.", false}))
</pre>
</pre>


Line 428: Line 403:
ConversationPhrases.GREETING_MESSAGES,
ConversationPhrases.GREETING_MESSAGES,
{
{
newCondition("PlayerHasItemWithHimCondition", "money"),
conditions:create("PlayerHasItemWithHimCondition", {"money"}),
newNotCondition(newCondition("NakedCondition")),
conditions:notCondition(conditions:create("NakedCondition")),
},
},
ConversationStates.ATTENDING,
ConversationStates.ATTENDING,
nil,
nil,
{
{
newAction("SayTextAction", "Hello."),
actions:create("SayTextAction", {"Hello."}),
newAction("NPCEmoteAction", "looks greedily at your pouch of money.", false),
actions:create("NPCEmoteAction", {"looks greedily at your pouch of money.", false}),
})
})
</pre>
</pre>
Line 444: Line 419:
<pre>
<pre>
local conditions = {
local conditions = {
newCondition("PlayerHasItemWithHimCondition", "money"),
conditions:create("PlayerHasItemWithHimCondition", {"money"}),
{
{
newNotCondition(newCondition("NakedCondition")),
conditions:notCondition(conditions:create("NakedCondition")),
},
},
}
}
Line 456: Line 431:
nil,
nil,
{
{
newAction("SayTextAction", "Hello."),
actions:create("SayTextAction", {"Hello."}),
newAction("NPCEmoteAction", "looks greedily at your pouch of money.", false),
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 = npcHelper.createSpeakerNPC("Frank")
local frank = entities.createSpeakerNPC("Frank")
npcHelper:addSeller(frank, shops:get("shopname"), true)
merchants:addSeller(frank, merchants.shops:get("shopname"), true)


game:add(frank)
game:add(frank)
Line 513: Line 479:
Then add the seller behavior using the custom list:
Then add the seller behavior using the custom list:
<pre>
<pre>
npcHelper:addSeller(frank, priceList, true)
merchants:addSeller(frank, priceList, true)
</pre>
</pre>


== System Properties ==
== System Properties ==


Java's system properties are exposed to Lua with the <code>game:propertyEnabled</code> and <code>game:propertyEquals</code> methods.
Java's system properties are exposed to Lua with the <code>properties</code> object.


Examples:
Examples:
<pre>
<pre>
-- property state
-- property state
if game:propertyEnabled("stendhal.testserver") then
if properties:enabled("stendhal.testserver") then
print("Test server enabled")
print("Test server enabled")
if game:propertyEquals("stendhal.testserver", "junk") then
if properties:equals("stendhal.testserver", "junk") then
print("Junk enabled")
print("Junk enabled")
else
else
Line 535: Line 501:


-- property value
-- property value
local prop = game:getProperty("stendhal.testserver")
local prop = properties:getValue("stendhal.testserver")
if prop ~= nil then
if prop ~= nil then
print("Test server enabled")
print("Test server enabled")
Line 547: Line 513:
end
end
</pre>
</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]]