enclave's Lua thread

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

Moderators: Forum Moderators, Developers

Re: enclave's Lua thread

Postby enclave » September 1st, 2017, 6:44 pm

Hi, sorry, could somebody tell me what is wrong with the simple following code:

Code: Select all
      ns_all_kings_stored = wesnoth.get_units { canrecruit = true }
      for i=1,#ns_all_kings_stored,1 do
         wesnoth.put_unit(ns_all_kings_stored[i].x,ns_all_kings_stored[i].y,{type="Spearman", side=i})
      end
      for i=1,#ns_all_kings_stored,1 do
         wesnoth.put_unit(ns_all_kings_stored[i].x,ns_all_kings_stored[i].y,{type="Bowman", side=i})
      end


It does the first cycle no problem, but for the second one it gives error saying: "bad argument #1 to '__index' (unknown unit)"
I tried change i to j, Bowman to Mage.. same thing.. I feel stupid.. what is wrong?? :(
enclave
 
Posts: 515
Joined: December 15th, 2007, 8:52 am

Re: enclave's Lua thread

Postby gfgtdf » September 1st, 2017, 6:49 pm

after the first loop the units in ns_all_kings_stored don't exit anymore since they have been overwritten with newly created units, so using them results in an error.
Scenario with Robots SP scenario (1.11/1.12), allows you to build your units with components, PYR No preperation turn 1.12 mp-mod that allows you to select your units immideately after the game begins.
gfgtdf
Developer
 
Posts: 898
Joined: February 10th, 2013, 2:25 pm

Re: enclave's Lua thread

Postby enclave » September 1st, 2017, 7:00 pm

gfgtdf wrote:after the first loop the units in ns_all_kings_stored don't exit anymore since they have been overwritten with newly created units, so using them results in an error.


What would I need to use/do in lua to remove kings from map and then put them anywhere I like?
Is there any alternative for WML [store_unit][/unstore_unit]? I tried to do
Code: Select all
ns_all_kings_stored = wesnoth.get_units { canrecruit = true }
    ns_all_kings_stored2 = wesnoth.get_units { canrecruit = true }

but in second cycle it's all same when I used ns_all_kings_stored2

It's very important in my code to have all kings off the map because I then use radius or distance to other leaders to create new random leader positions based on minimal distance specified in settings... I could use wesnoth.fire("store_unit", {}) but I just don't believe that lua has no better alternative options...?

Ah sorry, now I found copy and extract.. I will try them, thanks for your help!
enclave
 
Posts: 515
Joined: December 15th, 2007, 8:52 am

Re: enclave's Lua thread

Postby enclave » September 1st, 2017, 7:13 pm

lua wesnoth.copy_unit only works for 1 single unit?? so I can not copy all leaders in 1 go into array?
enclave
 
Posts: 515
Joined: December 15th, 2007, 8:52 am

Re: enclave's Lua thread

Postby Pentarctagon » September 1st, 2017, 7:26 pm

enclave wrote:lua wesnoth.copy_unit only works for 1 single unit?? so I can not copy all leaders in 1 go into array?


The name is copy_unit, not copy_units, so that is what I would assume. The wiki documentation also supports that.
99 little bugs in the code, 99 little bugs
take one down, patch it around
-2,147,483,648 little bugs in the code
User avatar
Pentarctagon
Forum Administrator
 
Posts: 2897
Joined: March 22nd, 2009, 10:50 pm
Location: Earth (occasionally)

Re: enclave's Lua thread

Postby enclave » September 1st, 2017, 7:45 pm

This seems to have worked for me:

Code: Select all
ns_copy_kings = {}
    ns_count_kings = wesnoth.get_units { canrecruit = true }
   
   
      for i=1,#ns_count_kings,1 do
         ns_copy_kings[i] = wesnoth.copy_unit(wesnoth.get_units({ canrecruit=true})[i])
      end
      
      
      for i=1,#ns_copy_kings,1 do
         wesnoth.put_unit(ns_copy_kings[i].x,ns_copy_kings[i].y,{type="Spearman"})
      end
      
      for i=1,#ns_copy_kings,1 do
         wesnoth.put_unit(ns_copy_kings[i].x,ns_copy_kings[i].y,ns_copy_kings[i])
      end


Wesnoth creators could maybe create a function to make everyone's life easier? something like that? It probably has 1000 mistakes, but the idea is there..
Code: Select all
function wesnoth.store_unit(filter_to_apply,name_of_variable)
         name_of_variable = {}
    ns_count_filtered_units = wesnoth.get_units { filter_to_apply }
                for i=1,#ns_count_filtered_units,1 do
         name_of_variable[i] = wesnoth.copy_unit(wesnoth.get_units({ filter_to_apply})[i])
      end
           return name_of_variable
end

Thanks for attention and advices..
enclave
 
Posts: 515
Joined: December 15th, 2007, 8:52 am

Re: enclave's Lua thread

Postby gfgtdf » September 1st, 2017, 7:56 pm

enclave wrote:This seems to have worked for me:

Code: Select all
ns_copy_kings = {}
    ns_count_kings = wesnoth.get_units { canrecruit = true }
   
   
      for i=1,#ns_count_kings,1 do
         ns_copy_kings[i] = wesnoth.copy_unit(wesnoth.get_units({ canrecruit=true})[i])
      end
      
      
      for i=1,#ns_copy_kings,1 do
         wesnoth.put_unit(ns_copy_kings[i].x,ns_copy_kings[i].y,{type="Spearman"})
      end
      
      for i=1,#ns_copy_kings,1 do
         wesnoth.put_unit(ns_copy_kings[i].x,ns_copy_kings[i].y,ns_copy_kings[i])
      end


you cna probably just write wesnoth.extract_unit(ns_copy_kings[i]) instead of ns_copy_kings[i] = wesnoth.copy_unit(wesnoth.get_units({ canrecruit=true})[i]) in the loop, but then again i don't really see what's the point of temporatiyl replacing it with a spearman in the first place.
Scenario with Robots SP scenario (1.11/1.12), allows you to build your units with components, PYR No preperation turn 1.12 mp-mod that allows you to select your units immideately after the game begins.
gfgtdf
Developer
 
Posts: 898
Joined: February 10th, 2013, 2:25 pm

Re: enclave's Lua thread

Postby enclave » September 1st, 2017, 8:32 pm

gfgtdf wrote:you cna probably just write wesnoth.extract_unit(ns_copy_kings[i]) instead of ns_copy_kings[i] = wesnoth.copy_unit(wesnoth.get_units({ canrecruit=true})[i]) in the loop, but then again i don't really see what's the point of temporatiyl replacing it with a spearman in the first place.

It was just my step by step test of code.. Needed to see if it works.. spearman is no needed.. and leaders not needed..
today is exhausting.. I tried to convert my WML into lua to make it work faster but I meet obstacles on a flat surface whole day... Now seems like all my codes work separate.. or at least most of them.. but when I put them together even wesnoth.fire with message doesnt work after some point.. for no obvious reason.
This code:
Code: Select all
local ns_all_kings_stored = {}
   ns_count_kings = wesnoth.get_units { canrecruit = true }
      for i=1,#ns_count_kings,1 do
         ns_all_kings_stored[i] = wesnoth.copy_unit(wesnoth.get_units({ canrecruit=true })[i])
      end
      wesnoth.fire("message",{message="#ns_all_kings_stored "..#ns_all_kings_stored})
         
      for j=1,#ns_all_kings_stored,1 do
         wesnoth.put_unit(ns_all_kings_stored[j].x,ns_all_kings_stored[j].y)
      end
      wesnoth.fire("message",{message="whatever"})

It just doesn't show the message whatever... all the kings are removed from map and then nothing! no message... why? :(
enclave
 
Posts: 515
Joined: December 15th, 2007, 8:52 am

Re: enclave's Lua thread

Postby Ravana » September 1st, 2017, 9:07 pm

I have noticed that you need to wrap numbers in tostring() before adding them to string. Also, I would suggest wesnoth.message() for testing, rather than calling [message].
User avatar
Ravana
Moderator
 
Posts: 1441
Joined: January 29th, 2012, 12:49 am
Location: Estonia

Re: enclave's Lua thread

Postby enclave » September 1st, 2017, 9:17 pm

Ravana wrote:I have noticed that you need to wrap numbers in tostring() before adding them to string. Also, I would suggest wesnoth.message() for testing, rather than calling [message].


Thanks Ravana, at least it shows me the message now :) with wesnoth.fire.. thats a good start.. In my other code I think I was misusing the
local ns_test_locations = wesnoth.get_locations { {"not", { terrain = "W*,W*^*,Xv,_off^_usr" } }}
Where later I was trying to call for x,y coordinates via ns_test_locations[i].x, I guess it gives nil and requires me to use ipairs instead?
enclave
 
Posts: 515
Joined: December 15th, 2007, 8:52 am

Re: enclave's Lua thread

Postby enclave » September 1st, 2017, 9:53 pm

Hi again!
Could anyone show me any semi-working example of how you would extract x,y and terrain from table created via local my_table = wesnoth.get_locations { {"not", { terrain = "W*,W*^*,Xv,_off^_usr" } }}?
How would I read the x,y and terrain of 1st element of this table? Thanks!

Ok so, nobody replied so far.. and while I was waiting I was trying to make it work.. and I found the following stuff..
1) wesnoth.get_locations only saves x and y :D nothing else.. that's why it is in "pairs".. could specify it somewhere in https://wiki.wesnoth.org/LuaWML/Tiles#wesnoth.get_locations just.. to.. save.. people's time looking for something that doesn't exist.
2) You can use it like so:
Code: Select all
local table_of_pairs_of_x_and_y = wesnoth.get_locations { {"not", { terrain = "W*,W*^*,Xv,_off^_usr" } }}
 for i, your_new_array_of_2_elements in ipairs(table_of_pairs_of_x_and_y) do
      wesnoth.message("x="..your_new_array_of_2_elements[1]..",".."y="..your_new_array_of_2_elements[2])
    end

Where later if you need to know terrain, you could use wesnoth.get_terrain(x, y)
3) I have no idea how I could extract coordinates of this "table" at index 39.. I can only assume that it would be like so:
Code: Select all
local table_of_pairs_of_x_and_y = wesnoth.get_locations { {"not", { terrain = "W*,W*^*,Xv,_off^_usr" } }}
 for i, your_new_array_of_2_elements in ipairs(table_of_pairs_of_x_and_y) do
if i==39 then
      wesnoth.message("Coordinates of 39th element of a table: x="..your_new_array_of_2_elements[1]..",".."y="..your_new_array_of_2_elements[2])
end
    end

4) But I guess no matter what.. I MUST use for loop.. otherwise this thing just doesnt work...
5) I finally figured out how to make my code work and it works! But it was a proper nightmare...
enclave
 
Posts: 515
Joined: December 15th, 2007, 8:52 am

Re: enclave's Lua thread

Postby shrieker1 » September 11th, 2017, 7:38 pm

enclave wrote:Hi again!
Could anyone show me any semi-working example of how you would extract x,y and terrain from table created via local my_table = wesnoth.get_locations { {"not", { terrain = "W*,W*^*,Xv,_off^_usr" } }}?
How would I read the x,y and terrain of 1st element of this table? Thanks!



I'm bit late noticing this.

Depending on use case location_set could improve the code. You could use wesnoth.fire("store_locations",...) and then location_set:of_wml_var. But I don't have any code doing that.

Following code should be fairly simple example how to get same information as store_locations provides for wml.

Code: Select all
local location_list = wesnoth.get_locations { terrain = "!,W*,W*^*,Xv,_off^_usr" }
for i,loc in ipairs(location_list) do
  local location_info = {
    x=loc[1],
    y=loc[2],
    terrain=wesnoth.get_terrain(loc[1], loc[2]),
  } -- This would need to be inserted to table if processing requires more than single for loop
end
--Specific location can be read using array indexing like
local nth_location =  {x=location_list[n][1], y=location_list[n][2]}


A bit more practical example:
Code: Select all
local function highlight_ranged_enemies(unit)
   local fn = "highlight_ranged_enemies"
   clear_ranged_enemies()

   if not unit then
      ns.err(fn .. " nil unit passed")
      return false
   end

   -- Check if adjacent enemy blocks ranged attacks
   local adjenemies = wesnoth.match_location(unit.x, unit.y,
                    { ns.WT.filter {
                       ns.WT.filter_adjacent {
                          is_enemy=true,
                       }
                    }})

   if adjenemies then
      -- There is adjacent enemy
      ns.debug(fn .. " adjacent enemy blocks")
      return false
   end

   -- Filter impassable terrains
   local impassable_terrains = "!,Xv,*^Xo,*^Qov,*^_fme,_off^*,Xo*,Xu*,M*^Xm"
   -- Get enemies inside range
   local enemies = wesnoth.get_locations({
                     ns.WT['and'] {
                        x=unit.x, y=unit.y, radius=unit.variables.range,
                        ns.WT.filter_radius {
                           terrain=impassable_terrains,
                        },
                     },
                     ns.WT['and'] {
                        ns.WT.filter {
                           ns.WT.filter_side {
                              ns.WT.enemy_of {
                                 side=unit.side,
                              }
                           }
                        }
                     }
                  })

   if not enemies[1] then
      -- No enemies in range -> no highlight
      ns.debug(fn .. " No enemies in range")
      return false
   end

   wesnoth.add_tile_overlay(unit.x, unit.y, { image=selectoverlay })
   wesnoth.set_variable(selectedunit, {x=unit.x, y=unit.y})

   for i, e in ipairs(enemies) do
      wesnoth.add_tile_overlay(e[1], e[2], { image=enemyoverlay })
      -- Make wml variable array that works in find_in filter in set_menu_item filter_location
      wesnoth.set_variable(string.format("%s[%d]",enemylocation,i-1),
                 {x=e[1], y=e[2]})
   end
   ns.WA.redraw {}

   return true
end



enclave wrote:5) I finally figured out how to make my code work and it works! But it was a proper nightmare...


Inline lua code in wml files is nightmare to debug because wesnoth produces horrible debug messages for them. It even fails to report many parsing errors for inline lua code. That why I simple made a helper macro to load lua code with wesnoth.dofile when running wesnoth in debug mode (-d command line parameter)

Code: Select all
#define NS_LUA_DOFILE FILE
    [lua]
#ifdef DEBUG_MODE
        code = "wesnoth.dofile ""{FILE}"""
#else
        code = {{FILE}}
#endif
    [/lua]

#enddef

#define NS_LUA_MAIN
# Lua code loaded and run from era top level if wesnoth 1.13
# Older wesnoth requires era code to use preload

#ifver WESNOTH_VERSION < 1.13.0
[event]
    name=preload
    first_time_only=no
#endif
    {NS_LUA_DOFILE (~add-ons/New_Settlers/lua/lua/main.lua)}
#ifver WESNOTH_VERSION < 1.13.0
[/event]
#endif

#enddef


Then lua file just has to be started and ended with wesnoth parser disable and enable tags like

Code: Select all
--<<

Any lua code can be put here

-->>
shrieker1
 
Posts: 21
Joined: August 7th, 2017, 5:53 pm

Re: enclave's Lua thread

Postby enclave » September 13th, 2017, 9:23 pm

Thanks Shrieker1, all that you have wrote here is quite high above my level of understanding wesnoth coding languages, but thanks anyway!
enclave
 
Posts: 515
Joined: December 15th, 2007, 8:52 am

Re: enclave's Lua thread

Postby enclave » September 13th, 2017, 10:30 pm

Hello, could anyone tell me what is the best way to message users while in the loop?
I have a loop in my code and I tried to put wesnoth.message and wesnoth.fire("print"), but all the messages appear after the loop has been finished, which makes no sense to my idea of how it should be.
I'm generating random prestart things in lua and I would like users to be informed of progress (otherwise if it's long they may think that it's a big lag and just close wesnoth), but for some reason messages are executed last.. when loop is finished.. Any ideas?

My particular code example inside spoiler (if it makes any difference):
Code: Select all
for i, ns_pairs_locations in ipairs(ns_test_locations) do
         if ns_king_rand_start_counter<ns_all_kings_stored_length then
            local ns_test_inner_locations = wesnoth.match_location(ns_pairs_locations[1], ns_pairs_locations[2], { radius=ns_min_dist , { "filter", { canrecruit=true } } })
            if ns_test_inner_locations == false then
               local ns_test_inner_locations_water_radius1 = wesnoth.match_location(ns_pairs_locations[1],ns_pairs_locations[2], { { "filter_adjacent_location", {terrain = "W*,W*^*,Xv,_off^_usr"} } })
               if ns_test_inner_locations_water_radius1 == false then
                  local ns_store_inner_locations = wesnoth.get_locations { {"not", { terrain = "W*,W*^*,Xv,_off^_usr" }}, {"and", { x=ns_pairs_locations[1],y=ns_pairs_locations[2],radius=4 }}, {"and", { {"not", { x="0,"..tostring(w) }}}}, {"and", { {"not", { y="0,"..tostring(h) }}}}  }
                  if #ns_store_inner_locations>=ns_min_island then
                     local arg2 = wesnoth.get_variable("ns_all_kings_stored.length")-1
                     local ns_rand_leader = math.random(0,arg2)
                     ns_rand_leader = wesnoth.synchronize_choice(function() return { value = ns_rand_leader } end).value
                          wesnoth.message("Attempt to shuffle sides with minimal distance "..ns_min_dist..":"..ns_king_rand_start_counter)
                     wesnoth.fire("unstore_unit",{animate=no,x=ns_pairs_locations[1],y=ns_pairs_locations[2],variable="ns_all_kings_stored["..tostring(ns_rand_leader).."]"})
                  --   wesnoth.fire("unstore_unit",{animate=no,x=ns_pairs_locations[1],y=ns_pairs_locations[2],variable="ns_all_kings_stored["..ns_king_rand_start_counter.."]"})
                     wesnoth.fire("clear_variable",{name="ns_all_kings_stored["..tostring(ns_rand_leader).."]"})
                     ns_king_rand_start_counter = ns_king_rand_start_counter + 1
                  end
               end
            end
         end
      end

I'm thinking maybe I need to add another loop inside loop to make messages show.. or something..?
Last edited by Ravana on September 13th, 2017, 10:57 pm, edited 1 time in total.
Reason: [code] does not look nice in [spoiler]
enclave
 
Posts: 515
Joined: December 15th, 2007, 8:52 am

Re: enclave's Lua thread

Postby enclave » September 13th, 2017, 10:43 pm

Sorry I found answer to my question.. for some reason wesnoth.delay(500) after message has worked very well! I guess if i decrease it from 500 into 1 it will work same well.. Thanks everyone for attention.. However I'm not sure if this way it is the most efficient, so if anyone knows better way? Then please reply.
enclave
 
Posts: 515
Joined: December 15th, 2007, 8:52 am

PreviousNext

Return to Lua Labs

Who is online

Users browsing this forum: No registered users and 1 guest