Horrible lua hack almost working

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

Moderator: Forum Moderators

Post Reply
User avatar
Spannerbag
Posts: 759
Joined: December 18th, 2016, 6:14 pm
Location: Yes

Horrible lua hack almost working

Post by Spannerbag »

Hi,
I so often need to extract the x,y of a unit that I decided to write a quick WML inline hack to do this.
It works as expected except for units on recall.

Here's the relevant code:

Code: Select all

# unit_xy template
#
# lua: takes a filter and returns first matching unit's x,y
#
# WML uses [get_unit_xy] tag to find (first) matching unit on map
#
#    [get_unit_xy]
#        ... filter (=cfg in lua) ...
#    [/get_unit_xy]
#
# Sets variables $gux and $guy
# These are 0,0 if unit not found, no error is issued

...

  [lua]
    code = <<
      function wesnoth.wml_actions.get_unit_xy(cfg)
      local guxy = wesnoth.units.find(cfg)
      if not guxy[1] then
        wml.variables.gux = 0
        wml.variables.guy = 0
      else
        wml.variables.gux = guxy[1].x
        wml.variables.guy = guxy[1].y
      end
    end
    >>
  [/lua]

# Our heroes
  [side]
    team_name=Goodies
    user_team_name= _ "Goodies"
    side=1
    x,y=1,1
    id=Goody
    name=_"Goody"
    type=Spearman
    controller=human
    canrecruit=yes
    recruit=Bowman,Spearman
    gold=100
  [/side]


# Enemy side
  [side]
    side=2
    x,y=44,33
    id=Baddy
    type=Goblin Rouser
    name= _ "Baddy"
    canrecruit=yes
    controller=ai
    team_name=Baddies
    user_team_name= _ "Baddies"
    random_traits=yes
    recruit=Goblin Spearman,Goblin Impaler
    gold=100
  [/side]


# --- Events ---

  [event]
    name=side 1 turn 1
    {UNIT 1 Mage recall recall (id=testmage
generate_name=yes)}
  [/event]

  [event]
    name=side 1 turn 2
{DEBUG_MSG (_"About to test [get_unit_xy]")}
    [get_unit_xy]
#      side=2
#      [filter_location]
#        terrain=*^V*
#      [/filter_location]
      id=testmage
    [/get_unit_xy]
{DEBUG_MSG (_"Done [get_unit_xy], x=$gux, y=$guy")}
  [/event]
This all works fine ($gux and $guy return values expected) except for recall.

Ideally I'd like to:
  • Allow the coder to specify a particular WML array variable or default to gu if no array specified.
    I tried setting $gu.x and $gu.y in lua but couldn't get the syntax quite right.
    Is wml.array_variables the right command to do this?
    Will keep experimenting when I next get time.
  • Return x,y=recall,recall for units on recall.
    E.g. if I recall the mage with id=testmage on turn 1, it's co-ordinates are correctly reported on turn 2.
    However if I leave it on recall I don't get x,y=recall,recall as expected.
    I thought wesnoth.units.find included recall but for me it doesn't seem to work so what am I doing wrong?
    (I'm guessing I need to use unstore_unit or similar to do this but I'm getting out of my depth.)
    I can't spend any more time on this now as I have other stuff to do hence this post, hopefully it's a trivial mistake I've made somewhere.
There are a few other bells and whistles I'd like to add but there's enough here for now. :)

[sanity_check]
Is this actually any quicker/more efficient than using [store_unit] to extract the x,y values?
[/sanity_check]

Any help, thoughts, suggestions etc. gratefully received!

Cheers!
-- Spannerbag
SP Campaigns: After EI (v1.14) Leafsea Burning (v1.18, v1.16)
I suspect the universe is simpler than we think and stranger than we can know.
Also, I fear that beyond a certain point more intelligence does not necessarily benefit a species...
User avatar
Ravana
Forum Moderator
Posts: 3313
Joined: January 29th, 2012, 12:49 am
Location: Estonia
Contact:

Re: Horrible lua hack almost working

Post by Ravana »

User avatar
Spannerbag
Posts: 759
Joined: December 18th, 2016, 6:14 pm
Location: Yes

Re: Horrible lua hack almost working

Post by Spannerbag »

Got an unexpected extra 30 mins and managed (by sheer trial and error) to get the array specification right so that now works. :D

Many thanks for your advice, will have a play with unit.valid when I next get chance, much appreciated.
Cheers!
-- Spannerbag
SP Campaigns: After EI (v1.14) Leafsea Burning (v1.18, v1.16)
I suspect the universe is simpler than we think and stranger than we can know.
Also, I fear that beyond a certain point more intelligence does not necessarily benefit a species...
User avatar
Spannerbag
Posts: 759
Joined: December 18th, 2016, 6:14 pm
Location: Yes

Re: Horrible lua hack almost working

Post by Spannerbag »

Hi again,
Making progress, thought I'd post my latest effort/progress for comment.

The logic can produce one of 3 outputs (if multiple matches, only first unit processed):
  1. Unit found on map.
  2. Unit found in recall.
  3. No match.
I encountered problems distinguishing between 2 and 3 above.

This was because if the unit was found on map it created a unit object/dataset and unit.valid existed.
However if the unit was found on recall an error was reported: basically my guess is that there was no .valid operation available which was the same (to my logic) as no unit being found.
Thus I could not distinguish between "no match found" and "unit(s) found in recall".

This puzzled me because wesnoth.units.find seemed to behave differently whether the unit was found on map or in recall.
I assume I'm doing something stupid hence this post.

I have a workaround but it's ugly and I'm sure there's a better, more elegant way of doing this.

Here's the relevant code:

_main.cfg

Code: Select all

# get_unit_xy
#
# Takes a filter and if:
# a) unit(s) found on map     returns gu.x,gu.y = first matching unit's x,y co-ordinates
# b) unit(s) found in recall  returns gu.x,gu.y = recall,recall
# c) no units match filter    returns gu.x,gu.y = 0,0
#
# WML usage:
#
#    [get_unit_xy]
#        ... filter ...		# Do not use a [filter] tag
#    [/get_unit_xy]
#
# Returns WML variable $gu with elements .x and .y
# This needs to be cleared
# These are 0,0 if unit not found, no error is issued

  [lua]
    code="wesnoth.require '~add-ons/LSB/lua/get_unit_xy.lua'"
  [/lua]
get_unit_xy.lua

Code: Select all

function wesnoth.wml_actions.get_unit_xy(cfg)
local guxy = wesnoth.units.find(cfg)
  if not guxy[1] then
    local guxy = wesnoth.units.find_on_recall(cfg)
  end
  if not guxy[1] then
    wml.array_variables['gu'] = { {x=0,y=0} }
  elseif guxy[1].valid == "map" then
    wml.array_variables['gu'] = { {x=guxy[1].x,y=guxy[1].y} }
  elseif guxy[1].valid == "recall" then
    wml.array_variables['gu'] = { {x="recall",y="recall"} }
  else
    wml.array_variables['gu'] = { {x=0,y=0} }	-- Should never happen
  end
end
Note that without an explicit test for recall my logic fails and units on recall are flagged as not found (x,y=0,0).
I.e. this code is required:

Code: Select all

  if not guxy[1] then
    local guxy = wesnoth.units.find_on_recall(cfg)
  end
Also, collapsing the logic fails so "or"-ing both find operations doesn't work (again units on recall retuirn x,y=0,0):

Code: Select all

  elseif guxy[1].valid == "map" or guxy[1].valid == "recall" then
    wml.array_variables['gu'] = { {x=guxy[1].x,y=guxy[1].y} }
Any thoughts/comments on how to properly test for the various outputs would be greatly apreciated as I'm sure my logic isn't the best!

Cheers!
-- Spannerbag
SP Campaigns: After EI (v1.14) Leafsea Burning (v1.18, v1.16)
I suspect the universe is simpler than we think and stranger than we can know.
Also, I fear that beyond a certain point more intelligence does not necessarily benefit a species...
gnombat
Posts: 892
Joined: June 10th, 2010, 8:49 pm

Re: Horrible lua hack almost working

Post by gnombat »

Spannerbag wrote: March 22nd, 2025, 9:58 am this code is required:

Code: Select all

  if not guxy[1] then
    local guxy = wesnoth.units.find_on_recall(cfg)
  end
I tried removing the above code and the function still seems to work without it.

This is what I tested it with:

Code: Select all

        [get_unit_xy]
            side=1
            type=Spearman
        [/get_unit_xy]
        [message]
            speaker=harry
            message="It looks like the spearman is at coordinates $gu.x $gu.y"
        [/message]
It seems to work fine even without find_on_recall

Wesnoth 1.18.4
Wesnoth 1.18.4
Also, collapsing the logic fails so "or"-ing both find operations doesn't work (again units on recall retuirn x,y=0,0):

Code: Select all

  elseif guxy[1].valid == "map" or guxy[1].valid == "recall" then
    wml.array_variables['gu'] = { {x=guxy[1].x,y=guxy[1].y} }
I don't see how that could work - according to the unit documentation "x and y values ... have no meaning", so if you want x and y to be set to recall you will have to do that in your own code.
User avatar
Spannerbag
Posts: 759
Joined: December 18th, 2016, 6:14 pm
Location: Yes

Re: Horrible lua hack almost working

Post by Spannerbag »

gnombat wrote: March 22nd, 2025, 2:32 pm
Spannerbag wrote: March 22nd, 2025, 9:58 am this code is required:

Code: Select all

  if not guxy[1] then
    local guxy = wesnoth.units.find_on_recall(cfg)
  end
I tried removing the above code and the function still seems to work without it.
Weird, just repeated tests myself and as you say that code isn't needed so no idea what I'd done wrong before.
Mind you I was rushing (as ever, am under time pressure now :roll: ).

Anyway, tested it with and without the recall test and both results seem identical so will remove it.
gnombat wrote: March 22nd, 2025, 2:32 pm
Also, collapsing the logic fails...
I don't see how that could work - according to the unit documentation "x and y values ... have no meaning", so if you want x and y to be set to recall you will have to do that in your own code.
Yeah, some time later when I had a bit of time to reflect I dimly realised something similar... :doh:
Thanks for sanity checking my code - it needed it! :augh:
Cheers!
-- Spannerbag
SP Campaigns: After EI (v1.14) Leafsea Burning (v1.18, v1.16)
I suspect the universe is simpler than we think and stranger than we can know.
Also, I fear that beyond a certain point more intelligence does not necessarily benefit a species...
User avatar
Spannerbag
Posts: 759
Joined: December 18th, 2016, 6:14 pm
Location: Yes

Re: Horrible lua hack working!

Post by Spannerbag »

Just thought I'd post the latest version as there's been several changes:
  1. Changed tag to [get_uxyid] - but if anyone has a better name I'd appreciate it.
    Returns x,y,id in the gu array.
  2. Returns an array of units found and length indicates number found (so length=0 means no matches).
  3. Decided not to bother adding a variable name specifier, I want this to be succinct and use minimal WML.
  4. Be neat to embed a scenario end event in the lua that clears the WML gu array.
    Then coder would not have to clear it manually - but that's probably overkill and confusing...
Yes, I know I've basically written [store_unit_lite]... but more importantly I've learned a bit more lua! :D

Any suggestions etc. much appreciated (especially 1. above).

Code: Select all

--- As above but returns all x,y,id values
--- If unit(s) found, assumes all are valid
function wesnoth.wml_actions.get_uxyid(cfg)
  local guxyid = wesnoth.units.find(cfg)
  local gu = {}
  wml.array_variables.gu = {}
  if guxyid then
    for index, unit in ipairs(guxyid) do
      if unit.valid == "map" then
        gu[index] = {x=unit.x,y=unit.y,id=unit.id}
      elseif unit.valid == "recall" then
        gu[index] = {x="recall",y="recall",id=unit.id}
      end
      wml.array_variables.gu = gu
    end
  end
end
WML used in testing:

Code: Select all

{DEBUG_MSG (_"About to test [get_uxyid] id=Baddy : unit on map")}
    [get_uxyid]
      id=Baddy
    [/get_uxyid]
{DEBUG_MSG (_"Done Baddy: units found = $gu.length, gu.x=$gu.x, gu.y=$gu.y, gu.id=$gu.id
About to test [get_unit_xyid] id=testmage : unit in recall")}
    [get_uxyid]
      id=testmage
    [/get_uxyid]
{DEBUG_MSG (_"Done testmage: units found = $gu.length, gu.x=$gu.x, gu.y=$gu.y, gu.id=$gu.id
About to test [get_unit_xyid] with nonexistent unit id=doesnotexist")}
    [get_uxyid]
      id=doesnotexist
    [/get_uxyid]
{DEBUG_MSG (_"Done doesnotexist: units found = $gu.length, gu.x=$gu.x, gu.y=$gu.y, gu.id=$gu.id
About to test [get_uxyid] side=1")}
    [get_uxyid]
      side=1
    [/get_uxyid]
{DEBUG_MSG (_"side=1: units found = $gu.length")}
  [foreach]
    array=gu
    [do]
{DEBUG_MSG(_"$i: x,y,id=$gu[$i].x,$gu[$i].y,$gu[$i].id")}
    [/do]
  [/foreach]
{DEBUG_MSG(_"About to test [get_uxyid] side=3 : no units match")}
    [get_uxyid]
      side=3
    [/get_uxyid]
{DEBUG_MSG (_"side=3: units found = $gu.length")}
  [foreach]
    array=gu
    [do]
{DEBUG_MSG(_"$i: x,y,id=$gu[$i].x,$gu[$i].y,$gu[$i].id")}
    [/do]
  [/foreach]
{DEBUG_MSG (_"Done side=3")}
Cheers!
-- Spannerbag
SP Campaigns: After EI (v1.14) Leafsea Burning (v1.18, v1.16)
I suspect the universe is simpler than we think and stranger than we can know.
Also, I fear that beyond a certain point more intelligence does not necessarily benefit a species...
Post Reply