wesnoth.wml_actions.store_unit_defense and wesnoth.get_units

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

Moderators: Forum Moderators, Developers

Post Reply
Choicerer
Posts: 238
Joined: April 29th, 2017, 11:37 pm

wesnoth.wml_actions.store_unit_defense and wesnoth.get_units

Post by Choicerer » March 20th, 2018, 9:02 am

See this -

viewtopic.php?f=21&t=47694

So I've implemented this function in LUA now. Using that very code from later versions. I had to remove the
helper.wml_error "[store_unit_defense]'s filter didn't match any unit"
part, because it would give me an error (dunno why).

This works. Except it fails to store defence for a unit if it's a local game unit played by an Anonymous Local Player. It then throws an error:
Attempt to index local 'unit' (a nil value)
So it can't index the unit if it belongs to the said player. I figured it must be some string stuff or something (yeah) so I tried modifying the function.
Here is what it looked like -

Code: Select all

	function wesnoth.wml_actions.store_unit_defense(cfg)
				local unit = wesnoth.get_units(cfg)[1] 
				local terrain = cfg.terrain
				local defense
				
				if terrain then
					defense = wesnoth.unit_defense(unit, terrain)
				elseif cfg.loc_x and cfg.loc_y then
					defense = wesnoth.unit_defense(unit, wesnoth.get_terrain(cfg.loc_x, cfg.loc_y))
				else
					defense = wesnoth.unit_defense(unit, wesnoth.get_terrain(unit.x, unit.y))
				end
				wesnoth.set_variable(cfg.variable or "terrain_defense", defense)
		end
So this brings us to the second part of the issue.
I tried changing the
local unit = wesnoth.get_units(cfg)[1]
part to
local unit = wesnoth.get_units{ id = cfg.id }
as documented here - https://wiki.wesnoth.org/LuaWML/Units#wesnoth.get_units
but this throws an error-
bad argument #1 to 'get_terrain' (number expected, got nil)
indicating that it's not actually getting anything from get_units.
Well, I thought this maybe had to do something with the fact that cfg is a proxy table, but wesnoth.get_units in the wiki talks about WML conditions.
But, guess what, actually putting anything else there, like side =2, canrecruit = true from the example produces the same error.
Another thing I don't get is why the example says local leaders_on_side_two = get_units { side = 2, canrecruit = true }
while obviously there's no such function and it should be
local leaders_on_side_two = wesnoth.get_units { side = 2, canrecruit = true }
But as I said, even then it gives the same weird error indicating that the filter didn't work. So the only thing that actually works is passing a table to that function rather than a filter in square brackets and this then produces the aforementioned bug that I'm trying to get rid of. Yes, it can be avoided by assigning control to the non-anonymous player rather than local player in the local setup screen, but telling people to do that every time they use my mods is a drag and not everyone will notice the warning. Please help.

User avatar
Celtic_Minstrel
Developer
Posts: 1245
Joined: August 3rd, 2012, 11:26 pm
Contact:

Re: wesnoth.wml_actions.store_unit_defense and wesnoth.get_units

Post by Celtic_Minstrel » March 21st, 2018, 4:09 am

Choicerer wrote:
March 20th, 2018, 9:02 am
So I've implemented this function in LUA now. Using that very code from later versions. I had to remove the
helper.wml_error "[store_unit_defense]'s filter didn't match any unit"
part, because it would give me an error (dunno why).
The most obvious reason why this would give you an error is that you did not include local helper = wesnoth.require "lua/helper.lua" at the top of yor Lua file.
Choicerer wrote:
March 20th, 2018, 9:02 am
This works. Except it fails to store defence for a unit if it's a local game unit played by an Anonymous Local Player. It then throws an error:
Attempt to index local 'unit' (a nil value)
This error indicates that it was unable to find a unit that matched the filter. There's probably an issue with the filter rather than the function.
Choicerer wrote:
March 20th, 2018, 9:02 am
So this brings us to the second part of the issue.
I tried changing the
local unit = wesnoth.get_units(cfg)[1]
part to
local unit = wesnoth.get_units{ id = cfg.id }
as documented here - https://wiki.wesnoth.org/LuaWML/Units#wesnoth.get_units
but this throws an error-
bad argument #1 to 'get_terrain' (number expected, got nil)
indicating that it's not actually getting anything from get_units.
Well, I thought this maybe had to do something with the fact that cfg is a proxy table, but wesnoth.get_units in the wiki talks about WML conditions.
But, guess what, actually putting anything else there, like side =2, canrecruit = true from the example produces the same error.
Another thing I don't get is why the example says local leaders_on_side_two = get_units { side = 2, canrecruit = true }
while obviously there's no such function and it should be
local leaders_on_side_two = wesnoth.get_units { side = 2, canrecruit = true }
But as I said, even then it gives the same weird error indicating that the filter didn't work. So the only thing that actually works is passing a table to that function rather than a filter in square brackets and this then produces the aforementioned bug that I'm trying to get rid of. Yes, it can be avoided by assigning control to the non-anonymous player rather than local player in the local setup screen, but telling people to do that every time they use my mods is a drag and not everyone will notice the warning. Please help.
These errors aren't an indication that the filter isn't working, they're an artifact of you not properly understanding the code. That [1] in the original code was actually very important. The get_units function, as suggested by its name, returns not just one unit but a list of all units matching the filter; the [1] tells the engine to just take the first one. (Though unfortunately it does still find them all and then just drop the rest.) Even if there's only one matching unit, you get a list of length one rather than just that unit. The list of units doesn't have x or y keys, so attempting to fetch them just results in nil.
Author of The Black Cross of Aleron campaign and Default++ era.
Maintainer of Steelhive.

Choicerer
Posts: 238
Joined: April 29th, 2017, 11:37 pm

Re: wesnoth.wml_actions.store_unit_defense and wesnoth.get_units

Post by Choicerer » March 22nd, 2018, 9:13 am

Thank you for explanations 1 and 3! As regards this:
Celtic Minstrel wrote:
Choicerer wrote:
March 20th, 2018, 9:02 am
This works. Except it fails to store defence for a unit if it's a local game unit played by an Anonymous Local Player. It then throws an error:
Attempt to index local 'unit' (a nil value)
This error indicates that it was unable to find a unit that matched the filter. There's probably an issue with the filter rather than the function.
I'm afraid you are wrong. This filter works in every case EXCEPT when the defending/attacking unit belongs to an Anonymous Local Player. AI is fine. Players with names in Multiplayer or Local Games are fine. Again, maybe this has something to do with the fact that I stole this function from 1.3 and implemented it in 1.2.

Oh, by the way, could you answer this question please:
Choicerer wrote: Another thing I don't get is why the example says local leaders_on_side_two = get_units { side = 2, canrecruit = true }
while obviously there's no such function and it should be
local leaders_on_side_two = wesnoth.get_units { side = 2, canrecruit = true }
EDIT:

I can confirm your suggestions worked. So as to issue 2, once again, doing:
local unit = wesnoth.get_units{id=cfg.id}[1]
instead of
local unit = wesnoth.get_units(cfg)[1]
removes the error that is thrown when the function involves an Anonymous Local Player.

User avatar
Celtic_Minstrel
Developer
Posts: 1245
Joined: August 3rd, 2012, 11:26 pm
Contact:

Re: wesnoth.wml_actions.store_unit_defense and wesnoth.get_units

Post by Celtic_Minstrel » March 23rd, 2018, 12:28 am

I'm not wrong about the cause of the error; and the change that you describe as fixing it does suggest that there's a problem with the filter, as it ends up actually ignoring the filter with that change. Maybe it would help if you actually post the WML you're using, too.
Author of The Black Cross of Aleron campaign and Default++ era.
Maintainer of Steelhive.

Choicerer
Posts: 238
Joined: April 29th, 2017, 11:37 pm

Re: wesnoth.wml_actions.store_unit_defense and wesnoth.get_units

Post by Choicerer » March 23rd, 2018, 5:43 am

Celtic_Minstrel wrote:
March 23rd, 2018, 12:28 am
I'm not wrong about the cause of the error; and the change that you describe as fixing it does suggest that there's a problem with the filter, as it ends up actually ignoring the filter with that change. Maybe it would help if you actually post the WML you're using, too.
You almost certainly are wrong. How does it end up ignoring the filter? How does the player name have anything to do with how the filter works (that is the change. Are you sure you haven't confused this topic with another one)? Have you read the entirety of the code posted in this topic? If it ended up ignoring the filter, the mod wouldn't be working. It is, it is correctly storing the defence for a given unit and changing the attack/defend odds, it has been extensively tested... Have you entered the link in the first post? It further describes the problem. There is enough code posted to arrive at this assumption. But anyway, since I've managed to fix the problem now by slightly modifying the 1.3 function, I couldn't care less.

User avatar
Celtic_Minstrel
Developer
Posts: 1245
Joined: August 3rd, 2012, 11:26 pm
Contact:

Re: wesnoth.wml_actions.store_unit_defense and wesnoth.get_units

Post by Celtic_Minstrel » March 24th, 2018, 12:16 am

I don't know what the player name has to do with this. What I do know is these two things:

1. Given the code in the first post, the error Attempt to index local 'unit' (a nil value) indicates that the filter returned no units. In other words, no units matched the filter.

2. Your change to local unit = wesnoth.get_units{id=cfg.id}[1] means that the filter is being ignored; instead of using the full filter, it uses only the id from the filter.

Taken together, these two facts suggest there is something wrong with the filter being passed to the tag. That's why I suggested you post the WML you're using, so I can get a clearer picture of what's going on here.
Author of The Black Cross of Aleron campaign and Default++ era.
Maintainer of Steelhive.

Post Reply