Anonymissimus' lua thread

Discussion of Lua and LuaWML support, development, and ideas.

Moderator: Forum Moderators

Post Reply
silene
Posts: 1109
Joined: August 28th, 2004, 10:02 pm

Re: some first steps in lua, questions and discussion

Post by silene »

Anonymissimus wrote:Yes, it's a workaround for the non-svn-version bugs.
Which one? The off-by-one coordinates? (Just to be sure I'm not missing a bug.)
Anonymissimus wrote:Is it generally advisable to avoid "wesnoth.fire("some_tag")" and to use "wesnoth.some_tag" instead (e.g. store_side - wesnoth.get_side) ? (interpretation of wml ?))
Not necessarily. Though if there is a Lua function that does the job, it will be faster and hopefully easier to use than the corresponding WML tag.
Anonymissimus wrote:
On the contrary, function get_units returns references to actual units; they are things that have a real existence and can be interacted with in real time. There are nothing similar in plain WML, so I hope what I'm saying makes some sense.
"references" - like pointers to the (content) variables representing the units (I suppose) in the C++ code ?
But isn't the variable type in the lua code the same, no matter whether returned by get_variable("unit") or get_units(...) ?
Yes, like C++ pointers. In fact, they would be C++ pointers if we didn't care for security. And no, they don't have the same Lua type. (Though this is hardly a good criteria, since there is only one structured datatype in Lua, and it may just have been the type of both of them.) In particular, all the fields are updated in real time, and some of them can even be used to modify units/sides on the fly.

Code: Select all

local target = wesnoth.get_units(filter)[1]
target.side = 2 - target.side
-- now the target unit has really switched sides
Anonymissimus
Inactive Developer
Posts: 2461
Joined: August 15th, 2008, 8:46 pm
Location: Germany

Re: some first steps in lua, questions and discussion

Post by Anonymissimus »

silene wrote:
Anonymissimus wrote:Yes, it's a workaround for the non-svn-version bugs.
Which one? The off-by-one coordinates? (Just to be sure I'm not missing a bug.)
Yes, and the unit_var.__cfg.x one. If I can track something down I'll report it. Though usually I don't know whether it's a bug or my mistake. There seem to be still lots of bugs around lua due to the very limited number of people using it. ;)
wesnoth: unit_map.cpp:54: unit_map::~unit_map(): Assertion `num_iters_ == 0 && "dangling iterators will attempt to dereference an invalid pointer in their destructor"' failed.
and crash :mrgreen:
projects (BfW 1.12):
A Simple Campaign: campaign draft for wml startersPlan Your Advancements: mp mod
The Earth's Gut: sp campaignSettlers of Wesnoth: mp scenarioWesnoth Lua Pack: lua tags and utils
updated to 1.8 and handed over: A Gryphon's Tale: sp campaign
silene
Posts: 1109
Joined: August 28th, 2004, 10:02 pm

Re: some first steps in lua, questions and discussion

Post by silene »

Anonymissimus wrote:Yes, and the unit_var.__cfg.x one.
In fact, this last one is not a bug, it was just me getting confused about what the engine puts into unit WML. As a strange as it may seem, coordinates are not part of unit WML from a C++ point of view. That's why they were missing from __cfg. But since it can be confusing (it did confuse me), I decided to add them manually to the __cfg table after you reported the issue.
Anonymissimus wrote:wesnoth: unit_map.cpp:54: unit_map::~unit_map(): Assertion `num_iters_ == 0 && "dangling iterators will attempt to dereference an invalid pointer in their destructor"' failed.
This crash is probably unrelated to Lua (though Lua may be helpful in triggering it), but this is nonetheless pretty bad. Please post a way to reproduce this issue, if you know of one.
Anonymissimus
Inactive Developer
Posts: 2461
Joined: August 15th, 2008, 8:46 pm
Location: Germany

Re: some first steps in lua, questions and discussion

Post by Anonymissimus »

silene wrote:This crash is probably unrelated to Lua (though Lua may be helpful in triggering it), but this is nonetheless pretty bad. Please post a way to reproduce this issue, if you know of one.
here ya go
1.7.10, Linux

Code: Select all

	[event]
		name=turn 2

		[lua]
			code=<<
			local Hamel =wesnoth.get_units({id=Hamel})[1]
			Hamel.facing=se
			>>
		[/lua]
	[/event]
1. execute that lua code: there's an error which is ok since it should be Hamel.facing="se"
2. click "back to turn 1" from the menu: crash
console output:
20100207 20:46:38 error scripting/lua: [string "..."]:3: bad argument #-1 to '?' (string expected, got nil)
stack traceback:
: ?
[string "..."]:3: in main chunk
wesnoth: unit_map.cpp:54: unit_map::~unit_map(): Assertion `num_iters_ == 0 && "dangling iterators will attempt to dereference an invalid pointer in their destructor"' failed.
[/quote]
I've tried to modify some of the attributes which are marked as writable in the get_units documentation on-the-fly, e.g. .moves, it doesn't seem to work. Only side worked as expected, without put_unit or unstore_unit like usually necessary.
projects (BfW 1.12):
A Simple Campaign: campaign draft for wml startersPlan Your Advancements: mp mod
The Earth's Gut: sp campaignSettlers of Wesnoth: mp scenarioWesnoth Lua Pack: lua tags and utils
updated to 1.8 and handed over: A Gryphon's Tale: sp campaign
silene
Posts: 1109
Joined: August 28th, 2004, 10:02 pm

Re: some first steps in lua, questions and discussion

Post by silene »

Anonymissimus wrote:

Code: Select all

	[event]
		name=turn 2

		[lua]
			code=<<
			local Hamel =wesnoth.get_units({id=Hamel})[1]
			Hamel.facing=se
			>>
		[/lua]
	[/event]
1. execute that lua code: there's an error which is ok since it should be Hamel.facing="se"
2. click "back to turn 1" from the menu: crash
Thanks, fixed.

By the way, you also forgot the quotes around the "Hamel" id, so it denotes a global variable, which probably does not exist, hence causing the id attribute to be nil/empty, so the filter will match all the units.
Anonymissimus wrote:I've tried to modify some of the attributes which are marked as writable in the get_units documentation on-the-fly, e.g. .moves, it doesn't seem to work. Only side worked as expected, without put_unit or unstore_unit like usually necessary.
Strange, I will try to reproduce it. Make sure that the display is up-to-date though (e.g. with the [redraw] tag), otherwise the interface may just be displaying the characteristics of the old unit.
Anonymissimus
Inactive Developer
Posts: 2461
Joined: August 15th, 2008, 8:46 pm
Location: Germany

Re: some first steps in lua, questions and discussion

Post by Anonymissimus »

silene wrote:Strange, I will try to reproduce it. Make sure that the display is up-to-date though (e.g. with the [redraw] tag), otherwise the interface may just be displaying the characteristics of the old unit.
Don't know what was wrong - all work; facing only after clicking on the unit, though...
projects (BfW 1.12):
A Simple Campaign: campaign draft for wml startersPlan Your Advancements: mp mod
The Earth's Gut: sp campaignSettlers of Wesnoth: mp scenarioWesnoth Lua Pack: lua tags and utils
updated to 1.8 and handed over: A Gryphon's Tale: sp campaign
Anonymissimus
Inactive Developer
Posts: 2461
Joined: August 15th, 2008, 8:46 pm
Location: Germany

Re: some first steps in lua, questions and discussion

Post by Anonymissimus »

*forgive the double post please*

-Is it possible to create a [have_unit_recall] tag, which behaves like [have_unit] except for that it also checks for units on the recall list ? (probably not, it's not an action tag...)
-Could there be a wesnoth.get_locations function be implemented, analogically to wesnoth.get_units and [store_locations] ? It should be clear what I expect that function to do...
projects (BfW 1.12):
A Simple Campaign: campaign draft for wml startersPlan Your Advancements: mp mod
The Earth's Gut: sp campaignSettlers of Wesnoth: mp scenarioWesnoth Lua Pack: lua tags and utils
updated to 1.8 and handed over: A Gryphon's Tale: sp campaign
silene
Posts: 1109
Joined: August 28th, 2004, 10:02 pm

Re: some first steps in lua, questions and discussion

Post by silene »

Anonymissimus wrote:Is it possible to create a [have_unit_recall] tag, which behaves like [have_unit] except for that it also checks for units on the recall list ? (probably not, it's not an action tag...)
Right, the part of the engine responsible for checking conditionals is not extensible at all. That being said, I could add a [lua_test] tag. It would work like the [lua] one, with a code= attribute and an [args] subtag, but it would be recognized in conditionals and expect the Lua code to return a boolean value. You would still not be able to define your [have_unit_recall] tag, but at least you would be able to define it as a macro.
Anonymissimus wrote:Could there be a wesnoth.get_locations function be implemented, analogically to wesnoth.get_units and [store_locations] ? It should be clear what I expect that function to do...
In fact, it isn't clear to me what the function would do. The point of the wesnoth.get_units function is to give direct access to the C++ objects behind units. There is no such things for locations, so I can't think of a C++ implementation that would bring something more than a straight Lua implementation.

Code: Select all

function get_locations(filter)
  local cfg = filter.__literal
  local var_name = "LUA_store_location"
  cfg.variable = var_name
  wesnoth.fire("store_locations", cfg)
  local t = helper.get_variable_array var_name
  wesnoth.clear_variable var_name
  return t
end
However, if you feel like the code above (untested) is sufficiently helpful, I can always add it to the helper library.
Anonymissimus
Inactive Developer
Posts: 2461
Joined: August 15th, 2008, 8:46 pm
Location: Germany

Re: some first steps in lua, questions and discussion

Post by Anonymissimus »

silene wrote:Right, the part of the engine responsible for checking conditionals is not extensible at all. That being said, I could add a [lua_test] tag. It would work like the [lua] one, with a code= attribute and an [args] subtag, but it would be recognized in conditionals and expect the Lua code to return a boolean value. You would still not be able to define your [have_unit_recall] tag, but at least you would be able to define it as a macro.
I already have a wml macro, so we postpone this idea, unless there are other/new use cases for such a [lua_test] tag. There is a subkey for have_unit adding this functionality suggested on the easy codings page, that would be the best solution.
silene wrote:In fact, it isn't clear to me what the function would do. The point of the wesnoth.get_units function is to give direct access to the C++ objects behind units. There is no such things for locations, so I can't think of a C++ implementation that would bring something more than a straight Lua implementation.
I was hoping there'd be a C++ array containing the coordinates and terrain strings or the like...Using wesnoth.fire("store_locations",...) is a direct call to the same C++ function that is called whenever this tag appears in a wml code ? (same for other wesnoth.fire(...) stuff ?)
I was wondering about the nearest_hex algorithm which I want to write as efficient as possible, since I really do use it frequently and it slows things down. There doesn't seem to be a suitable wesnoth.something function that is really useful to achieve the same functionality.

Also, I see there is no (not yet) a "various lua editors index"-thread. Emacs lua mode doesn't satisfy me (it lacks auto-completion and region-auto-indentation), what editor are you using for lua, silene ?
Ideally, I would want to have an IDE that lets me edit and compile wml, lua, java and C++ with such comfortable functions like "refactor" - e.g. updating all references to a function when I change the function's name somewhere in the code. I'll probably need different editors for each of the languages, though.^^
projects (BfW 1.12):
A Simple Campaign: campaign draft for wml startersPlan Your Advancements: mp mod
The Earth's Gut: sp campaignSettlers of Wesnoth: mp scenarioWesnoth Lua Pack: lua tags and utils
updated to 1.8 and handed over: A Gryphon's Tale: sp campaign
silene
Posts: 1109
Joined: August 28th, 2004, 10:02 pm

Re: some first steps in lua, questions and discussion

Post by silene »

Anonymissimus wrote:Using wesnoth.fire("store_locations",...) is a direct call to the same C++ function that is called whenever this tag appears in a wml code ? (same for other wesnoth.fire(...) stuff ?)
Yes.
Anonymissimus wrote:I was wondering about the nearest_hex algorithm which I want to write as efficient as possible, since I really do use it frequently and it slows things down. There doesn't seem to be a suitable wesnoth.something function that is really useful to achieve the same functionality.
I don't know which generic facility would be useful for your algorithm. In fact, I'm not even sure that you really need the nearest hex. That is, perhaps another hex would do just as well, as long as it satisfies some properties. (No idea if what I'm suggesting does even make sense.) For instance, if you could use a function that returned all the hexes reachable with a given cost, would that help you? I'm suggesting it because it is a variant of the find_path function that exists on the C++ side, and I may just as well export it to Lua if it can be helpful.
Anonymissimus wrote:Also, I see there is no (not yet) a "various lua editors index"-thread. Emacs lua mode doesn't satisfy me (it lacks auto-completion and region-auto-indentation), what editor are you using for lua, silene ?
Ideally, I would want to have an IDE that lets me edit and compile wml, lua, java and C++ with such comfortable functions like "refactor" - e.g. updating all references to a function when I change the function's name somewhere in the code.
I'm probably too old for these things, as I never really understood the need for a fancy text editor. I need something that works without prior knowledge (not vi), that starts instantly (not emacs nor eclipse nor qt-based editors since I'm usually running a gtk-based environment), that supports syntactic coloring, utf8, and mouse cursor. In other words, I'm using gedit and nano. (And in case some people wonder, nano does indeed support all these features, though they happen to be disabled by default.)
Anonymissimus
Inactive Developer
Posts: 2461
Joined: August 15th, 2008, 8:46 pm
Location: Germany

Re: some first steps in lua, questions and discussion

Post by Anonymissimus »

The algorithm that I'm at would be called like this

Code: Select all

	[nearest_hex]
		x_source={X}
		y_source={Y}
		max_distance={MAX_DISTANCE}
		{FILTER}
		variable={VARIABLE_NAME}
	[/nearest_hex]
where {FILTER} is a SLF that must be matched by the hexes which should have a minimum distance from x_source, y_source.
Basically, I want to call [store_locations] for all tiles matching {FILTER} and then run a single loop over the resulting array, calling find_path from x_source, y_source to each of the tiles, storing the resulting cost and the tile that belongs to it and replacing it if a new tile has lesser cost. That way the loop with repeated calls to the slow [store_location] function doesn't exist.
What I don't like about the find_path function is that I need to create and kill a unit at the source location in case that there is none.
The max_distance attribute will probably no longer be neccessary.
In melinath's code

Code: Select all

local function store_nearest_hex(args)
   local args=args.__literal
   local max_radius=args[1][2].radius or helper.wml_error("[store_nearest_hex] expects a radius= in the first subtag")
   local mode = args.mode or 'all'
   args.mode=nil
   local final_store = args.variable or "nearest_hex_store"
   wesnoth.set_variable(final_store)
   args.variable = "store_nearest_hex_tmp"

   for i=0,max_radius do
      args[1][2].radius=i
      wesnoth.fire("store_locations",args)
      local length=wesnoth.get_variable("store_nearest_hex_tmp.length")
      if length>0 then
	 wesnoth.set_variable(final_store..".radius",args.radius)
	 for i=0, length-1 do
	    wesnoth.set_variable(string.format("%s[%d]", final_store, i), wesnoth.get_variable("store_nearest_hex_tmp["..i..']'))
	    if mode=='first' then break end
	 end
	 break
      end
   end

   wesnoth.set_variable('store_nearest_hex_tmp')
end
I do not understand what the inner loop is good for: The outer loop has already acquired all hexes in the minimum distance; the inner loop makes just a copy of them in case that the user doesn't only want a single hex in the minimum distance.

While writing, I realize that the movement costs of the source unit are important...either I replace it with a unit that has costs=1 for all terrain or I don't use find_path at all, meaning to compute distance between hexes manually...
projects (BfW 1.12):
A Simple Campaign: campaign draft for wml startersPlan Your Advancements: mp mod
The Earth's Gut: sp campaignSettlers of Wesnoth: mp scenarioWesnoth Lua Pack: lua tags and utils
updated to 1.8 and handed over: A Gryphon's Tale: sp campaign
silene
Posts: 1109
Joined: August 28th, 2004, 10:02 pm

Re: some first steps in lua, questions and discussion

Post by silene »

Anonymissimus wrote:Basically, I want to call [store_locations] for all tiles matching {FILTER} and then run a single loop over the resulting array, calling find_path from x_source, y_source to each of the tiles, storing the resulting cost and the tile that belongs to it and replacing it if a new tile has lesser cost. That way the loop with repeated calls to the slow [store_location] function doesn't exist.
I don't see anything wrong with this approach.
Anonymissimus wrote:What I don't like about the find_path function is that I need to create and kill a unit at the source location in case that there is none.
The unit is needed for getting the movement costs. If all the costs are 1, there is not much point in using find_path, unless you are interested in the actual path, which you are not.

That being said, I notice that the code is overly restrictive. No unit should be required when the Lua script is using a custom cost function. I will relax this constraint for the next release.
Anonymissimus wrote:either I replace it with a unit that has costs=1 for all terrain or I don't use find_path at all, meaning to compute distance between hexes manually...
No need for a custom unit, a custom cost function is enough.

Code: Select all

local path, cost = wesnoth.find_path(x1, y1, x2, y2, function(x, y, sofar) return 1 end)
-- except that it currently fails if (x1,y1) happens to be an empty tile
But if all the costs are 1, executing the above code is overly expensive. Computing the distance between two tiles can be done faster:

Code: Select all

local function is_even(v) return v % 2 == 0 end
function distance_between(x1, y1, x2, y2)
  local hdist = math.abs(x1 - x2)
  local vdist = math.abs(y1 - y2)
  if (not is_even(x1) and is_even(x2) and y1 < y2) or
     (not is_even(x2) and is_even(x1) and y2 < y1)
  then vdist = vdist + 1 end
  return math.max(hdist, vdist + math.floor(hdist / 2))
end
I guess I should add this function to the helper library.
Anonymissimus
Inactive Developer
Posts: 2461
Joined: August 15th, 2008, 8:46 pm
Location: Germany

Re: some first steps in lua, questions and discussion

Post by Anonymissimus »

silene wrote:That being said, I notice that the code is overly restrictive. No unit should be required when the Lua script is using a custom cost function. I will relax this constraint for the next release.
Yes, please.
But if all the costs are 1, executing the above code is overly expensive. Computing the distance between two tiles can be done faster:

Code: Select all

local function is_even(v) return v % 2 == 0 end
function distance_between(x1, y1, x2, y2)
  local hdist = math.abs(x1 - x2)
  local vdist = math.abs(y1 - y2)
  if (not is_even(x1) and is_even(x2) and y1 < y2) or
     (not is_even(x2) and is_even(x1) and y2 < y1)
  then vdist = vdist + 1 end
  return math.max(hdist, vdist + math.floor(hdist / 2))
end
This seems to be exactly what I was looking for: Computing the distance between 2 hexes on a hexagonal grid with the coordinate system used by BfW. Please add it either to the helper library or make the C++ function that you've translated it from (I assume) available. (Where is it so I may have a look ?)
Glad to see some mathematician has figured it out. ;)
projects (BfW 1.12):
A Simple Campaign: campaign draft for wml startersPlan Your Advancements: mp mod
The Earth's Gut: sp campaignSettlers of Wesnoth: mp scenarioWesnoth Lua Pack: lua tags and utils
updated to 1.8 and handed over: A Gryphon's Tale: sp campaign
silene
Posts: 1109
Joined: August 28th, 2004, 10:02 pm

Re: some first steps in lua, questions and discussion

Post by silene »

Anonymissimus wrote:This seems to be exactly what I was looking for: Computing the distance between 2 hexes on a hexagonal grid with the coordinate system used by BfW. Please add it either to the helper library or make the C++ function that you've translated it from (I assume) available. (Where is it so I may have a look ?)
Such functions can be found in src/map_location.cpp. Be careful when reading the C++ code, as there are several coordinate systems in Wesnoth. In particular, the Lua function I posted is compatible with WML coordinates, while the original C++ one is not.
Anonymissimus
Inactive Developer
Posts: 2461
Joined: August 15th, 2008, 8:46 pm
Location: Germany

Re: some first steps in lua, questions and discussion

Post by Anonymissimus »

[nearest_hex] is finished:
in the scenario, output the nearest cave wall hex every time that "Testwhelp" moves:

Code: Select all

	{SETUP_LUA}
	[event]
		name=moveto
		first_time_only=no
		[filter]
			id=Testwhelp
		[/filter]
		[nearest_hex]
			x_source=$x1
			y_source=$y1
			terrain=Xu
			variable=loc_Hex
		[/nearest_hex]
		{DEBUG_MSG ($loc_Hex.x, $loc_Hex.y)}
	[/event]

Code: Select all

function functions.nearest_hex(args)
   local locations = functions.require("locations")
   locations.nearest_hex(args);
end

Code: Select all

--Compute the (a single one) nearest hex, which must match a given SLF, to a given source hex, and store it in a wml location variable.
function locations.nearest_hex(args)
   local args = args.__parsed

   local l_iX = tonumber(args.x_source) or helper.wml_error("Error in [nearest_hex] while reading source location."); args.x_source = nil
   local l_iY = tonumber(args.y_source) or helper.wml_error("Error in [nearest_hex] while reading source location."); args.y_source = nil
   local l_sVariable = tostring(args.variable) or helper.wml_error("Error in [nearest_hex] while reading the variable name.")

   wesnoth.fire("store_locations", args)
   local l_locPossibleNearestHexes = helper.get_variable_array(l_sVariable)

   local l_iNearestHex
   local l_iNearestHexDistance = MAX_INT
   local l_iPossibleNearestHexDistance = MAX_INT
   for l_iCurrentHex, l_locCurrentHex in ipairs(l_locPossibleNearestHexes) do
      l_iPossibleNearestHexDistance = locations.distance_between(l_iX, l_iY, l_locCurrentHex.x, l_locCurrentHex.y)

      if(l_iPossibleNearestHexDistance < l_iNearestHexDistance) then
	 l_iNearestHexDistance = l_iPossibleNearestHexDistance
	 l_iNearestHex = l_iCurrentHex

      end
   end
   wesnoth.set_variable(l_sVariable)
   wesnoth.set_variable(l_sVariable, l_locPossibleNearestHexes[l_iNearestHex])
end
(For the complete dependency tree to get a working example you'd need to download DK)

things like the following may be good to add to helper.lua (or similar):

Code: Select all

function functions.is_even(l_iValue)
   return ((l_iValue %2) == 0);
end
function functions.is_odd(l_iValue)
   return not ((l_iValue %2) == 0);
end

MAX_INT = 1000000000000

function dbms(arguments)
   local arguments=tostring(arguments)
   wesnoth.message(arguments)
   wesnoth.fire("message", {speaker="narrator", message=arguments} )
end
-What is MAX_INT in lua really, does it exist, and how to access ? (What is it in wml ? I've never needed it so far...) math.pi is there, nut not max_int.
-Is it good to export and import functionalities like this (to load them when needed)
local locations = functions.require("locations")
?
I need to make new files when the base file becomes too large.
-How can functions within a table
local functions = {}
function functions...
...
return functions
be "private" (while some others are "public" ?)
-Does wesnoth.find_vacant_tile check whether the tile currently being considered has suitable terrain for the unit ? This info should be added to the documentation - the fact that [unstore_unit]find_vacant=yes doesn't check for terrain is one of the main reasons why I need the nearest_hex functionality.
projects (BfW 1.12):
A Simple Campaign: campaign draft for wml startersPlan Your Advancements: mp mod
The Earth's Gut: sp campaignSettlers of Wesnoth: mp scenarioWesnoth Lua Pack: lua tags and utils
updated to 1.8 and handed over: A Gryphon's Tale: sp campaign
Post Reply