Crendgrim's Lua Thread
Moderator: Forum Moderators
Crendgrim's Lua Thread
So I started to learn Lua, too. I'll use this thread to ask questions which will come up during my learning phase.
The first is already there:
I'm creating an (easy) inventory system, because I think that this will be useful for some future add-ons of mine while being not too difficult.
After some initial problems with translated strings (thanks to Anonymissimus for explaining me that issue) I successfully created two little WML tags called [cg_init_inventory] and [cg_inventory_size]. Actually, I'm going to remove the first, because the latter does everything now: Initialize an inventory by setting its size as well as modifying this size.
However, I'm stuck at [cg_item_add]. This tag is supposed to add an item to the unit's inventory. For now, it only takes a SUF to get all wanted units and a "name" attribute for the item's name.
Yesterday evening I some problem, so I decided to take a look again today. However, this didn't fix it.
So, getting to my actual problem, I'll post my code. The comments should explain what it's supposed to do.
The problem is that this doesn't save the data to the unit's variables, but to a WML variable "unit".
How is this done correctly?
Crend
The first is already there:
I'm creating an (easy) inventory system, because I think that this will be useful for some future add-ons of mine while being not too difficult.
After some initial problems with translated strings (thanks to Anonymissimus for explaining me that issue) I successfully created two little WML tags called [cg_init_inventory] and [cg_inventory_size]. Actually, I'm going to remove the first, because the latter does everything now: Initialize an inventory by setting its size as well as modifying this size.
However, I'm stuck at [cg_item_add]. This tag is supposed to add an item to the unit's inventory. For now, it only takes a SUF to get all wanted units and a "name" attribute for the item's name.
Yesterday evening I some problem, so I decided to take a look again today. However, this didn't fix it.
So, getting to my actual problem, I'll post my code. The comments should explain what it's supposed to do.
Code: Select all
--[[ [cg_item_add]
This tag adds an item.
Arguments:
- StandardUnitFilter
- name: the item's name
]]
function wml_actions.cg_item_add(cfg)
local filter = wesnoth.get_units(helper.get_child(cfg, "filter")) or helper.wml_error("[cg_item_add] missing required [filter] tag")
local name = cfg.name or helper.wml_error("Missing required name= attribute in [cg_item_add]")
local newitem = { name = name }
for index, unit in ipairs(filter) do
-- if cg_inventory_size isn't set or equals zero, the unit has no inventory, so we skip the following process
if unit.variables.cg_inventory_size and unit.variables.cg_inventory_size ~= 0 then
local items = {} -- creating an empty array
if unit.variables.cg_items then items = helper.get_variable_array("unit.variables.cg_items") end -- if possible, retrieve information from the unit's variables
if #items >= unit.variables.cg_inventory_size then -- do we still have some place for the item?
wesnoth.message(tostring(unit.name), "No place in inventory!")
else
table.insert(items, newitem) -- insert the new item
helper.set_variable_array("unit.variables.cg_items", items) -- saving it. See below.
end -- if (size)
end -- if (inventory)
end -- for
end -- function
How is this done correctly?
Crend
UMC Story Images — Story images for your campaign!
-
- Inactive Developer
- Posts: 2461
- Joined: August 15th, 2008, 8:46 pm
- Location: Germany
Re: Crendgrim's Lua Thread
You can try adding wesnoth.put_unit(wesnoth.get_variable("unit")) after the
helper.set_variable_array("unit.variables.cg_items", items)
line if your code works otherwise.
But note that it depends on the existance of a wml variable "unit", so won't work in many types of events, and is somewhat inefficient then. YOu can add wesnoth.set_variable("unit", unit.__cfg) above the
if unit.variables.cg_items then items = helper.get_variable_array("unit.variables.cg_items") end
line to remove the dependency on the variable existance.
The way to go is calling unit=unit.__cfg near the start of your loop for every unit to modify I'm pretty sure, but since I'm not silene I can't post some modified code of yours now which is likely enough to work untested.
trying anyway (completely untested)
helper.set_variable_array("unit.variables.cg_items", items)
line if your code works otherwise.
But note that it depends on the existance of a wml variable "unit", so won't work in many types of events, and is somewhat inefficient then. YOu can add wesnoth.set_variable("unit", unit.__cfg) above the
if unit.variables.cg_items then items = helper.get_variable_array("unit.variables.cg_items") end
line to remove the dependency on the variable existance.
The way to go is calling unit=unit.__cfg near the start of your loop for every unit to modify I'm pretty sure, but since I'm not silene I can't post some modified code of yours now which is likely enough to work untested.
trying anyway (completely untested)
Code: Select all
--[[ [cg_item_add]
This tag adds an item.
Arguments:
- StandardUnitFilter
- name: the item's name
]]
function wml_actions.cg_item_add(cfg)
local units = wesnoth.get_units(helper.get_child(cfg, "filter")) or helper.wml_error("[cg_item_add] missing required [filter] tag")
local name = cfg.name or helper.wml_error("Missing required name= attribute in [cg_item_add]")
local newitem = { name = name }
for index, unit in ipairs(units) do
-- if cg_inventory_size isn't set or equals zero, the unit has no inventory, so we skip the following process
local i_size = unit.variables.cg_inventory_size
if i_size and i_size ~= 0 then
unit = unit.__cfg --since for proxy unit.variables "only toplevel named fields are proxied", convert to a wml table
local variables = helper.get_child(unit, "variables")
if #variables >= i_size then -- do we still have some place for the item?
--comparison works only if you have no other unnamed fields in the variable table (no other arrays under the [variables] tag)
wesnoth.message(tostring(unit.name), "No place in inventory!")
else
table.insert(variables, {"cg_items", newitem }) -- insert the new item
wesnoth.put_unit(unit)
end -- if (size)
end -- if (inventory)
end -- for
end -- function
projects (BfW 1.12):
A Simple Campaign: campaign draft for wml starters • Plan Your Advancements: mp mod
The Earth's Gut: sp campaign • Settlers of Wesnoth: mp scenario • Wesnoth Lua Pack: lua tags and utils
updated to 1.8 and handed over: A Gryphon's Tale: sp campaign
A Simple Campaign: campaign draft for wml starters • Plan Your Advancements: mp mod
The Earth's Gut: sp campaign • Settlers of Wesnoth: mp scenario • Wesnoth Lua Pack: lua tags and utils
updated to 1.8 and handed over: A Gryphon's Tale: sp campaign
Re: Crendgrim's Lua Thread
Thanks a lot
Your code worked without any modifications, so it looks as if you were becoming a second silene
However, I changed it a bit to create a slightly different structure (unit.variables.cg_items.item for each item), so counting items in the inventory is easier now.
This way it works completely as it's supposed to be:
Now I'm only curious about that
Crend
Your code worked without any modifications, so it looks as if you were becoming a second silene
However, I changed it a bit to create a slightly different structure (unit.variables.cg_items.item for each item), so counting items in the inventory is easier now.
This way it works completely as it's supposed to be:
Code: Select all
--[[ [cg_item_add]
This tag adds an item.
Arguments:
- StandardUnitFilter
- name: the item's name
]]
function wml_actions.cg_item_add(cfg)
local filter = wesnoth.get_units(helper.get_child(cfg, "filter")) or helper.wml_error("[cg_item_add] missing required [filter] tag")
local name = cfg.name or helper.wml_error("Missing required name= attribute in [cg_item_add]")
local newitem = { name = name }
for index, unit in ipairs(filter) do
-- if cg_inventory_size isn't set or equals zero, the unit has no inventory, so we skip the following process
local i_size = unit.variables.cg_inventory_size
if i_size and i_size ~= 0 then
unit = unit.__cfg --since for proxy unit.variables "only toplevel named fields are proxied", convert to a wml table
local variables = helper.get_child(unit, "variables")
local insert = false
local items = helper.get_child(variables, "cg_items")
if not items then -- isn't there an inventory yet? Let us create one...
items = {}
insert = true
end
if #items >= i_size then -- do we still have some place for the item?
wesnoth.message(tostring(unit.name), "No place in inventory!")
else
table.insert(items, {"item", newitem}) -- insert the new item
if insert then table.insert(variables, {"cg_items", items}) end -- insert [cg_items] tag if needed
wesnoth.put_unit(unit) -- store information
end
end
end
end
unit = unit.__cfg
part, because it looks as if that was the key solution. What exactly does that do?Crend
UMC Story Images — Story images for your campaign!
-
- Inactive Developer
- Posts: 2461
- Joined: August 15th, 2008, 8:46 pm
- Location: Germany
Re: Crendgrim's Lua Thread
It transforms a lua proxy unit variable, which is an accessor to an instance of the C++ class "unit" from unit.hpp into a wml table describing a unit. (in this case - other types of proxy variables can have a __cfg field too) The process is similar like storing an on-map unit into a wml variable with [store_unit]. In fact, it's the same, since [store_unit] does just call the __cfg field as you can see in its implementation in data/lua/wml-tags.lua.Crendgrim wrote:Now I'm only curious about thatunit = unit.__cfg
part, because it looks as if that was the key solution. What exactly does that do?
projects (BfW 1.12):
A Simple Campaign: campaign draft for wml starters • Plan Your Advancements: mp mod
The Earth's Gut: sp campaign • Settlers of Wesnoth: mp scenario • Wesnoth Lua Pack: lua tags and utils
updated to 1.8 and handed over: A Gryphon's Tale: sp campaign
A Simple Campaign: campaign draft for wml starters • Plan Your Advancements: mp mod
The Earth's Gut: sp campaign • Settlers of Wesnoth: mp scenario • Wesnoth Lua Pack: lua tags and utils
updated to 1.8 and handed over: A Gryphon's Tale: sp campaign
Re: Crendgrim's Lua Thread
Okay, thanks. I think I got that.
However, the next problem is appearing. This time I'm not sure whether it's better doable in WML or in Lua, but I ask it here nevertheless for I already started here.
So, I created another tag
My plan is to make this function accessible via a menu_item in the menu. I'm currently doing it this way:
The problem is that this way also units without an inventory (without the variable "cg_inventory_size") get the menu item.
Is there any way to test via WML if a certain variable exists? Or, alternatively, how would this menu be implemented in Lua?
Crend
[cg_item_add]
and [cg_item_remove]
are working now also for multiple units. However, the next problem is appearing. This time I'm not sure whether it's better doable in WML or in Lua, but I ask it here nevertheless for I already started here.
So, I created another tag
[cg_show_inventory]
which outputs a message with all items available. Later I plan to implement there some functionality to use certain items as weapons etc.My plan is to make this function accessible via a menu_item in the menu. I'm currently doing it this way:
Code: Select all
[event]
name=prestart
[set_menu_item]
id=cg_show_inventory_item
description= _ "Show Inventory"
[show_if]
[have_unit]
side=$side_number
x=$x1
y=$y1
[not]
[filter_wml]
[variables]
cg_inventory_size=0
[/variables]
[/filter_wml]
[/not]
[/have_unit]
[/show_if]
[command]
[cg_show_inventory]
x=$x1
y=$y1
[/cg_show_inventory]
[/command]
[/set_menu_item]
[/event]
Is there any way to test via WML if a certain variable exists? Or, alternatively, how would this menu be implemented in Lua?
Crend
UMC Story Images — Story images for your campaign!
Re: Crendgrim's Lua Thread
Try greater than 0 instead of not 0.
-
- Inactive Developer
- Posts: 2461
- Joined: August 15th, 2008, 8:46 pm
- Location: Germany
Re: Crendgrim's Lua Thread
(untested)
load a global function
and then use [have_unit]lua_function="cg_has_inventory"
load a global function
Code: Select all
function cg_has_inventory(unit)
return unit.variables.cg_inventory_size
end
projects (BfW 1.12):
A Simple Campaign: campaign draft for wml starters • Plan Your Advancements: mp mod
The Earth's Gut: sp campaign • Settlers of Wesnoth: mp scenario • Wesnoth Lua Pack: lua tags and utils
updated to 1.8 and handed over: A Gryphon's Tale: sp campaign
A Simple Campaign: campaign draft for wml starters • Plan Your Advancements: mp mod
The Earth's Gut: sp campaign • Settlers of Wesnoth: mp scenario • Wesnoth Lua Pack: lua tags and utils
updated to 1.8 and handed over: A Gryphon's Tale: sp campaign
Re: Crendgrim's Lua Thread
8680:
How can that be done in a [variables] tag?
Anonymissimus:
Great!
I didn't know that this worked.. I should read the Wiki more carefully.
Thanks a lot, this should fix it
Crend
How can that be done in a [variables] tag?
Anonymissimus:
Great!
I didn't know that this worked.. I should read the Wiki more carefully.
Thanks a lot, this should fix it
Crend
UMC Story Images — Story images for your campaign!
Re: Crendgrim's Lua Thread
I... oh. I guess I only skimmed your code; and I also guess it's been too long since I used WML. Apologies. Apologies. I'll go away now.Crendgrim wrote:8680: How can that be done in a [variables] tag?
Re: Crendgrim's Lua Thread
I just thought I had missed something
Okay, I finally managed to create a working inventory system. It's very WIP (since the items can't do anything right now), but it works .. somehow.
I'm pretty sure that it can be done nicer in many parts, but hey! at least it works
Features:
Actually, the very first function (
To use it, you have to do the following:
For now I'm just glad that it finally works
Future plans:
Crend
EDIT:
Archive attached as promised
Okay, I finally managed to create a working inventory system. It's very WIP (since the items can't do anything right now), but it works .. somehow.
I'm pretty sure that it can be done nicer in many parts, but hey! at least it works
Features:
- inventories for multiple units
- change inventory size
- place and remove items
- drop items from the inventory
- on stepping on an item the unit can take it up
- items can have names, images and descriptions
inventory.lua
[cg_init_inventory]
) is redundant, because its job is also done by [cg_inventory_size]
. I didn't change it yet only because I don't know whether [cg_init_inventory]
will do more once (for example, setting up pre-defined items, other variables or whatever).To use it, you have to do the following:
- include the lua file in a preload event
- put this code in your scenario:
Code: Select all
[event] name=prestart [set_menu_item] id=cg_show_inventory_item description= _ "Show Inventory" [show_if] [have_unit] side=$side_number x=$x1 y=$y1 lua_function=cg_has_inventory [/have_unit] [/show_if] [command] [cg_show_inventory] x=$x1 y=$y1 [/cg_show_inventory] [/command] [/set_menu_item] [/event]
- Use
[cg_init_inventory]
with a StandardUnitFilter and a size= argument for each of your units who should get an inventory - Place items using
[cg_place_item]
.
For now I'm just glad that it finally works
Future plans:
- add item properties; e.g. heal, bonus, strength
- allow certain items (with strength) to be equipped as weapons
- make certain items undroppable
- whatever comes to my mind
Crend
EDIT:
Archive attached as promised
- Attachments
-
- CGInventory.tar.gz
- First version.
- (4.01 KiB) Downloaded 525 times
UMC Story Images — Story images for your campaign!
Re: Crendgrim's Lua Thread
Okay, the last project I eventually abandoned, because nothing worked any more as expected.
But today I started another one, and I am again completely stuck. This time, I just want to have one inventory for the human player (inside a campaign), so I do not have to care about units and their respective items.
I know I'm doing something terribly wrong, but I have no idea what actually. Besides, I changed the above code so often that it's quite possible that it once was more correct.
I would be very grateful if someone could point me to my problem.
Crend
But today I started another one, and I am again completely stuck. This time, I just want to have one inventory for the human player (inside a campaign), so I do not have to care about units and their respective items.
Code: Select all
function wml_actions.cg_inv_add(cfg)
local id = cfg.id or helper.wml_error("Missing required id= attribute in [cg_inv_add]")
local name = cfg.name or helper.wml_error("Missing required name= attribute in [cg_inv_add]")
local image = cfg.image or helper.wml_error("Missing required image= attribute in [cg_inv_add]")
local description = cfg.description or helper.wml_error("Missing required description= attribute in [cg_inv_add]")
local item = wesnoth.create_unit { type = "CG Item", id = id, name = name, image = image, description = description }
-- yes, it's a unit type I want to store
local debug_utils = wesnoth.require("~add-ons/Wesnoth_Lua_Pack/debug_utils.lua")
local inventory = helper.get_variable_proxy_array "cg_inventory"
wesnoth.set_variable "qow_inventory"
table.insert(inventory, {"item", item.__cfg} )
debug_utils.dbms(inventory)
-- and here is the weird thing:
-- in the first tag call, dbms will verify that it's a WML table, but in the second call not.
-- however, in both cases there is an error with the following command
wesnoth.set_variable("cg_inventory", inventory)
end
I would be very grateful if someone could point me to my problem.
Crend
UMC Story Images — Story images for your campaign!
-
- Inactive Developer
- Posts: 2461
- Joined: August 15th, 2008, 8:46 pm
- Location: Germany
Re: Crendgrim's Lua Thread
I don't see a "second call" do dbms. Well, what did it say why it isn't a wml table ?
If you get the "wml table inconsistently predicted" message from dbms it means my algorithm detected the wml table status (yes/no) differently from the engine which you should report then, I countercheck this every time.
If you get the "wml table inconsistently predicted" message from dbms it means my algorithm detected the wml table status (yes/no) differently from the engine which you should report then, I countercheck this every time.
projects (BfW 1.12):
A Simple Campaign: campaign draft for wml starters • Plan Your Advancements: mp mod
The Earth's Gut: sp campaign • Settlers of Wesnoth: mp scenario • Wesnoth Lua Pack: lua tags and utils
updated to 1.8 and handed over: A Gryphon's Tale: sp campaign
A Simple Campaign: campaign draft for wml starters • Plan Your Advancements: mp mod
The Earth's Gut: sp campaign • Settlers of Wesnoth: mp scenario • Wesnoth Lua Pack: lua tags and utils
updated to 1.8 and handed over: A Gryphon's Tale: sp campaign
Re: Crendgrim's Lua Thread
I mean, if I use that tag two times it tells me "heey, you don't have a WML table, stupid" and then shows me some weird garbage of which I have no idea where it comes from...
Code: Select all
lua_var is of type table, value table: 0x7f2b26d0ffa0, length 2, but no WML table: table introducing subtag at [1] is not of the form {"tag_name", {}}:
{
[1] = {
__varname = "cg_inventory[0]"
},
[2] = {
[1] = "item",
[2] = {
[...]
UMC Story Images — Story images for your campaign!
-
- Inactive Developer
- Posts: 2461
- Joined: August 15th, 2008, 8:46 pm
- Location: Germany
Re: Crendgrim's Lua Thread
in the table at the unnamed field [1] you are in the "intermediate" level where named fields such as __varname= (a string) are not allowed (for a wml table). If an unnamed field appears in a wml table at the "main" level, it must hold a table, and that table must introduce a subtag by consisting of an unnamed field holding a string (the tagname) and another unnamed field holding a table. Within that table we're at "main" level again so named fields holding primitve types and unnamed ones holding tables (to introduce another subtag) are allowed. So that's what that error message means.
projects (BfW 1.12):
A Simple Campaign: campaign draft for wml starters • Plan Your Advancements: mp mod
The Earth's Gut: sp campaign • Settlers of Wesnoth: mp scenario • Wesnoth Lua Pack: lua tags and utils
updated to 1.8 and handed over: A Gryphon's Tale: sp campaign
A Simple Campaign: campaign draft for wml starters • Plan Your Advancements: mp mod
The Earth's Gut: sp campaign • Settlers of Wesnoth: mp scenario • Wesnoth Lua Pack: lua tags and utils
updated to 1.8 and handed over: A Gryphon's Tale: sp campaign
Re: Crendgrim's Lua Thread
My guess is that the proxy table needs special non-WML data in order to work, hence the incorrect element 1. Maybe you could use helper.get_variable_array instead of get_variable_proxy_array.
Ninja'd, but am I right?
Ninja'd, but am I right?
Last edited by Luther on August 17th, 2011, 4:29 pm, edited 1 time in total.