enclave's Lua thread

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

Moderator: Forum Moderators

Post Reply
enclave
Posts: 936
Joined: December 15th, 2007, 8:52 am

Re: enclave's Lua thread

Post by enclave »

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?? :(
gfgtdf
Developer
Posts: 1431
Joined: February 10th, 2013, 2:25 pm

Re: enclave's Lua thread

Post by gfgtdf »

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.
enclave
Posts: 936
Joined: December 15th, 2007, 8:52 am

Re: enclave's Lua thread

Post by enclave »

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: 936
Joined: December 15th, 2007, 8:52 am

Re: enclave's Lua thread

Post by enclave »

lua wesnoth.copy_unit only works for 1 single unit?? so I can not copy all leaders in 1 go into array?
User avatar
Pentarctagon
Project Manager
Posts: 5496
Joined: March 22nd, 2009, 10:50 pm
Location: Earth (occasionally)

Re: enclave's Lua thread

Post by Pentarctagon »

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
enclave
Posts: 936
Joined: December 15th, 2007, 8:52 am

Re: enclave's Lua thread

Post by enclave »

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..
gfgtdf
Developer
Posts: 1431
Joined: February 10th, 2013, 2:25 pm

Re: enclave's Lua thread

Post by gfgtdf »

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.
enclave
Posts: 936
Joined: December 15th, 2007, 8:52 am

Re: enclave's Lua thread

Post by enclave »

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? :(
User avatar
Ravana
Forum Moderator
Posts: 2934
Joined: January 29th, 2012, 12:49 am
Location: Estonia
Contact:

Re: enclave's Lua thread

Post by Ravana »

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].
enclave
Posts: 936
Joined: December 15th, 2007, 8:52 am

Re: enclave's Lua thread

Post by enclave »

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: 936
Joined: December 15th, 2007, 8:52 am

Re: enclave's Lua thread

Post by enclave »

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#w ... _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...
shrieker1
Posts: 21
Joined: August 7th, 2017, 5:53 pm

Re: enclave's Lua thread

Post by shrieker1 »

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

-->>
enclave
Posts: 936
Joined: December 15th, 2007, 8:52 am

Re: enclave's Lua thread

Post by enclave »

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

Re: enclave's Lua thread

Post by enclave »

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: 936
Joined: December 15th, 2007, 8:52 am

Re: enclave's Lua thread

Post by enclave »

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.
Post Reply