[engine] Design of [query_location]

Brainstorm ideas of possible additions to the game. Read this before posting!

Moderators: Forum Moderators, Developers

Forum rules
Before posting a new idea, you must read the following:
User avatar
Celtic_Minstrel
Developer
Posts: 1208
Joined: August 3rd, 2012, 11:26 pm
Contact:

Re: [engine] Design of [query_location]

Post by Celtic_Minstrel » February 9th, 2018, 1:19 am

Yeah, that's why I think something other than a click would be better for finishing early. Alternatively, you could maybe try a different mouse button? (ie right-click to finish early, all left-clicks either select or deselect)
Author of The Black Cross of Aleron campaign and Default++ era.
Maintainer of Steelhive.

User avatar
Sapient
Inactive Developer
Posts: 4449
Joined: November 26th, 2005, 7:41 am
Contact:

Re: [engine] Design of [query_location]

Post by Sapient » February 9th, 2018, 2:38 am

Ravana wrote: I guess normal use cases would add at least some filter, so might be worth logging notice that filter is expected.. like [kill] should warn if [filter] encountered.
I don't think so. There are many conceivable use cases where any map hex is valid. Therefore, there's no good reason to force the author to write that empty tag out.
http://www.wesnoth.org/wiki/User:Sapient... "Looks like your skills saved us again. Uh, well at least, they saved Soarin's apple pie."

User avatar
Ravana
Moderator
Posts: 1832
Joined: January 29th, 2012, 12:49 am
Location: Estonia
Contact:

Re: [engine] Design of [query_location]

Post by Ravana » February 9th, 2018, 1:02 pm

Is there way to catch rightclick event with Lua?

User avatar
Ravana
Moderator
Posts: 1832
Joined: January 29th, 2012, 12:49 am
Location: Estonia
Contact:

Re: [engine] Design of [query_location]

Post by Ravana » February 9th, 2018, 6:24 pm

Seems that all below only applies when there is another query_location in current execution queue. Still, without way to detect control changes I dont think I can avoid such problem.
Problems then
I find that when leaving game during selection and having side assigned to player/ai, game gets stuck, unable to do anything other than leaving.

Investigating shows that wesnoth.game_events.on_mouse_action is nil.

2p game: p1 calls query_location. p1 leaves game. p2 sets 1 as local side.
Result is that for p2 the tag function is called twice - first when p1 call is synced upon his leaving, and then after taking control of 1. That second call is what breaks game.
Current code with some more debug messages about control change issue

Code: Select all

--<<

-- Parameters
-- [filter_location] location filter - error if matches no location, if missing matches all locations
-- max_count - optional default 1, how many locations player may select, not allowed to be 0, not allowed to be less than min_count
-- min_count - optional default 1, how many locations player needs to select
-- allow_fog - optional default false
-- allow_shroud - optional default false. If you allow both shroud and fog here, you can include custom vision location filter
-- variable - WML variable for result - optional default location
-- confirm_message - optional, if used then there is dialog to finish selection, with min_count=0 cancel, reseting selection
-- overlay_selected, overlay_selectable, overlay_unselectable - custom overlay - optional, use "" to not display
-- overlay_message - optional printed while selecting
function wesnoth.wml_actions.query_location(cfg)
	local is_choosing_side = wesnoth.sides[wesnoth.current.side].is_local
	wesnoth.message("Im local: "..tostring(is_choosing_side))
	local _ = wesnoth.textdomain "wesnoth"
	local variable = cfg.variable or "location"
	local helper = wesnoth.require "lua/helper.lua"
	-- local utils = wesnoth.require "lua/wml-utils.lua"
	local T = helper.set_wml_tag_metatable {}
	local location_set = wesnoth.require "lua/location_set.lua"
	-- local overlay_choosable = cfg.overlay_selectable or "terrain/alphamask.png~O(0.2)~CS(255,255,0)"
	local overlay_choosable = cfg.overlay_selectable or "misc/hover-hex.png"
	-- local overlay_chosen = cfg.overlay_selected or "terrain/alphamask.png~O(0.2)~CS(0,255,0)"
	local overlay_chosen = cfg.overlay_selected or "misc/blank-hex.png~BLIT(misc/hover-hex-enemy-bottom.png)~BLIT(misc/hover-hex-enemy-top.png)"
	local overlay_unselectable = cfg.overlay_unselectable or "terrain/alphamask.png~O(0.4)~CS(127,127,127)"
	local allow_fog = cfg.allow_fog or false
	local allow_shroud = cfg.allow_shroud or false
	local max_count = cfg.max_count or 1
	local min_count = cfg.min_count or 1
	local confirm_message = cfg.confirm_message
	local overlay_message = cfg.overlay_message or _"Click on location to choose."
	local filter = helper.literal(helper.get_child(cfg, "filter_location")) or {}
	filter.include_borders = false -- prevent getting stuck when only way to get required amount of locations would be with borders, which are not selectable
	
	-- TODO testing shows that wesnoth.game_events.on_mouse_action is nil, find out what it is supposed to be
	local old_callback = wesnoth.game_events.on_mouse_action
	
	local res = wesnoth.synchronize_choice(_"selecting location", function()
		if not is_choosing_side then
			wesnoth.message("synchronize_choice function fired for nonlocal side")
			wesnoth.game_events.on_mouse_action = old_callback
			return {}
		end
		if not is_choosing_side then
			wesnoth.message("synchronize_choice function fired again despite return, for nonlocal side")
			wesnoth.game_events.on_mouse_action = old_callback
			return {}
		end
		
		if allow_fog and not allow_shroud then
			table.insert(filter, T.filter_vision{side=wesnoth.current.side,respect_fog=false})
		elseif not allow_fog and not allow_shroud then
			table.insert(filter, T.filter_vision{side=wesnoth.current.side})
		end
		
		local allowed_locations = wesnoth.get_locations(filter)
		if #allowed_locations < min_count then helper.wml_error("query_location matches less locations than min_count "..#allowed_locations.."<"..min_count) end
		if min_count > max_count then helper.wml_error("query_location min_count is higher than max_count "..min_count..">"..max_count) end
		if max_count == 0 then helper.wml_error("query_location called with max_count=0") end
		if #allowed_locations == 0 and min_count == 0 then return end
		
		local disabled_locations = location_set.create()
		if overlay_unselectable then
			for i, loc in ipairs(wesnoth.get_locations({{[1]="not",[2]=filter}})) do
				x = loc[1]
				y = loc[2]
				disabled_locations:insert(x, y)
				if is_choosing_side then
					wesnoth.add_tile_overlay(x, y, { image = overlay_unselectable })
				end
			end
		end
		
		wesnoth.wml_actions.disallow_end_turn()
	
		
		local finished = false
		local adding = true -- not used for now
		
		local candidates = location_set.create()
		local chosen_locations = location_set.create()
		for i, loc in ipairs(allowed_locations) do
			x = loc[1]
			y = loc[2]
			candidates:insert(x, y)
			wesnoth.add_tile_overlay(x, y, { image = overlay_choosable })
		end
		wesnoth.redraw{}
		
		function wesnoth.game_events.on_mouse_action(x,y)			
			if adding and candidates:get(x,y) then
				if chosen_locations:size() < max_count then
					chosen_locations:insert(x,y)
					candidates:remove(x,y)
					wesnoth.remove_tile_overlay(x, y, { image = overlay_choosable })
					wesnoth.add_tile_overlay(x, y, { image = overlay_chosen })
				end
			elseif adding and chosen_locations:get(x,y) then
				if chosen_locations:size() >= min_count then
					finished = true
				end
			elseif not adding and candidates:get(x,y) then
				finished = true
			elseif not adding and chosen_locations:get(x,y) then
				if chosen_locations:size() > min_count then
					candidates:insert(x,y)
					chosen_locations:remove(x,y)
					wesnoth.remove_tile_overlay(x, y, { image = overlay_chosen })
					wesnoth.add_tile_overlay(x, y, { image = overlay_choosable })
				else
					finished=true
				end
			elseif not candidates:get(x,y) and not chosen_locations:get(x,y) then
				if chosen_locations:size() >= min_count then
					finished = true
				end
			end
			
			if chosen_locations:size() == max_count then
				finished = true
			end
		end
		
		local function print_current_count()
			wesnoth.print{text=overlay_message .. _" Selected "..chosen_locations:size().."/ ("..min_count..".."..max_count..")",size=24}
			wesnoth.print{text="Im local: "..tostring(is_choosing_side), size=24}
		end
		
		local function clear_selection()
			chosen_locations:iter(function(x,y,data) 
				wesnoth.remove_tile_overlay(x, y, { image = overlay_chosen })
			end)
			chosen_locations:clear()
		end
		
		local function reset_selection()
			chosen_locations:iter(function(x,y,data) 
				wesnoth.remove_tile_overlay(x, y, { image = overlay_chosen })
				wesnoth.add_tile_overlay(x, y, { image = overlay_choosable })
				candidates:insert(x,y)
			end)
			chosen_locations:clear()
			finished = false
		end

		while not finished do
			while not finished do
				print_current_count()
				wesnoth.delay(10)
				if not is_choosing_side then
					finished = true
					wesnoth.message("I am not choosing side!")
					wesnoth.game_events.on_mouse_action = old_callback
				end
				wesnoth.redraw{}
			end
			
			print_current_count()
			
			if confirm_message then
				local options = {_"Finish", _"Choose again", _"Quit"}
				if min_count ~= 0 then
					table.remove(options, 3)
				end
				local result = helper.get_user_choice({ speaker = "narrator", message = confirm_message }, options)
				if result == 1 then
				elseif result == 2 then
					reset_selection()
				elseif result == 3 then
					clear_selection()
				end
			end
			
			if not is_choosing_side then
				finished = true
				wesnoth.game_events.on_mouse_action = old_callback
			end
		end
		
		wesnoth.game_events.on_mouse_action = old_callback
		
		candidates:iter(function(x,y,data) 
			wesnoth.remove_tile_overlay(x, y, { image = overlay_choosable })
		end)
		chosen_locations:iter(function(x,y,data) 
			wesnoth.remove_tile_overlay(x, y, { image = overlay_chosen })
		end)
		
		disabled_locations:iter(function(x,y,data) 
			wesnoth.remove_tile_overlay(x, y, { image = overlay_unselectable })
		end)
		disabled_locations:clear()
		
		local wml_object = {}
		for i,v in ipairs(chosen_locations:to_pairs()) do
			wml_object[i] = {"value",{x=v[1],y=v[2]}}
		end
		wesnoth.redraw{}
		
		return wml_object
	end, function() 
		return helper.wml_error("query_location used with AI side")
	end)

	local normal_object = {}
	for i=1,#res do 
		normal_object[i] = res[i][2]
	end
	wesnoth.wml_actions.allow_end_turn()
	helper.set_variable_array(variable, normal_object)
	return normal_object
end

function ql()
	return wesnoth.wml_actions.query_location({T.filter_location{terrain="Cud"}})
end
function qi()
	return wesnoth.wml_actions.query_location({T.filter_location{terrain="Cud"}, overlay_selectable=""})
end
function qo()
	return wesnoth.wml_actions.query_location({T.filter_location{T["not"]{terrain="Cud"}}})
end

function qk()
	return wesnoth.wml_actions.query_location({T.filter_location{terrain="Cud"}, confirm_message="qk"})
end

function qj()
	return wesnoth.wml_actions.query_location({T.filter_location{terrain="Cud"}, max_count=3})
end

function qh()
	return wesnoth.wml_actions.query_location({T.filter_location{terrain="Cud"}, max_count=3,allow_fog=true})
end

function qh()
	return wesnoth.wml_actions.query_location({T.filter_location{terrain="Cud"}, max_count=3,allow_fog=true})
end

-->>

User avatar
Ravana
Moderator
Posts: 1832
Joined: January 29th, 2012, 12:49 am
Location: Estonia
Contact:

Re: [engine] Design of [query_location]

Post by Ravana » February 9th, 2018, 8:25 pm

I found that chat is not synced while selecting, which takes away purpose of this implementation. https://i.imgur.com/a8yB5mK.jpg

It should still be useful in singleplayer content, but as I focus only on mp, I leave this issue.

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

Re: [engine] Design of [query_location]

Post by Celtic_Minstrel » February 10th, 2018, 3:10 am

Ravana wrote:Is there way to catch rightclick event with Lua?
I'm not sure. My instinct would've been that on_mouse_action tells you the button clicked, but I guess that's not the case?

What about my other idea of using [set_menu_item] to install a hotkey for finishing selection?
Author of The Black Cross of Aleron campaign and Default++ era.
Maintainer of Steelhive.

User avatar
Ravana
Moderator
Posts: 1832
Joined: January 29th, 2012, 12:49 am
Location: Estonia
Contact:

Re: [engine] Design of [query_location]

Post by Ravana » February 10th, 2018, 11:49 am

[set_menu_item] is wml-only, so Lua variables responsible for query_location state are not be visible to it.

https://github.com/wesnoth/wesnoth/comm ... d4f42a29e8 shows there is also on_mouse_move.

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

Re: [engine] Design of [query_location]

Post by Celtic_Minstrel » February 10th, 2018, 5:13 pm

Hmm. I don't think the underlying Lua call for [set_menu_item] supports Lua callbacks at the moment... so you'd need to set a WML variable if you wanted to use [set_menu_item]. It could simply call into Lua though, I think?
Author of The Black Cross of Aleron campaign and Default++ era.
Maintainer of Steelhive.

gfgtdf
Developer
Posts: 1067
Joined: February 10th, 2013, 2:25 pm

Re: [engine] Design of [query_location]

Post by gfgtdf » February 10th, 2018, 7:20 pm

you said that i somwhow doesnt work in multiplayer, but i don't realy see what could go wrong, maybe you coudl explain what your problems were?


ok i just saw you said chat doesn't work, that's basicially https://github.com/wesnoth/wesnoth/issues/1856 and nothing you can fix in lua. Was there something else.
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.

User avatar
Ravana
Moderator
Posts: 1832
Joined: January 29th, 2012, 12:49 am
Location: Estonia
Contact:

Re: [engine] Design of [query_location]

Post by Ravana » February 10th, 2018, 7:28 pm

That part too
Seems that all below only applies when there is another query_location in current execution queue. Still, without way to detect control changes I dont think I can avoid such problem.

I find that when leaving game during selection and having side assigned to player/ai, game gets stuck, unable to do anything other than leaving.

Investigating shows that wesnoth.game_events.on_mouse_action is nil.

2p game: p1 calls query_location. p1 leaves game. p2 sets 1 as local side.
Result is that for p2 the tag function is called twice - first when p1 call is synced upon his leaving, and then after taking control of 1. That second call is what breaks game.
From discord
situation is that wesnoth.game_events.on_mouse_action is assigned to location selecting for one player. That player leaves game. New host takes control of leavers side. Active location selecting is canceled, but next one in queue starts immediately - so that display of side assignment is still visible
then player can only interact with game by pressing esc (and leaving), and using rightclick to enter help menu
immediately after replacing with local player: https://i.imgur.com/Q5JG92W.jpg

rightclick on map after last screen: https://i.imgur.com/JuNGFPy.jpg

gfgtdf
Developer
Posts: 1067
Joined: February 10th, 2013, 2:25 pm

Re: [engine] Design of [query_location]

Post by gfgtdf » February 10th, 2018, 7:52 pm

ok i noticed that i looked at an outdated version from page 2 before, why do you ahve all this is_local_side checks in the new code? they actually looks like they will break more than they'll fix, sync_choice shodul do all this stuff for you an call the passed function only for the currently active side.
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.

User avatar
Ravana
Moderator
Posts: 1832
Joined: January 29th, 2012, 12:49 am
Location: Estonia
Contact:

Re: [engine] Design of [query_location]

Post by Ravana » February 10th, 2018, 8:22 pm

Page 2 is more correct indeed, new code is what I got to when trying to work around that issue. Though that version has some UI code that should be in sync_choice outside it still.

gfgtdf
Developer
Posts: 1067
Joined: February 10th, 2013, 2:25 pm

Re: [engine] Design of [query_location]

Post by gfgtdf » February 10th, 2018, 8:38 pm

i i am now testing a modified version fo the page2 code that has the if overlay_unselectable then blöock moved into sync_choice, what exactly does not work? i tried leaving on that side that is currently selecting locations ('2p game: p1 calls query_location. p1 leaves game. p2 sets 1 as local side.') and it worked as expected (the new side now had to choose a location)
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.

User avatar
Ravana
Moderator
Posts: 1832
Joined: January 29th, 2012, 12:49 am
Location: Estonia
Contact:

Re: [engine] Design of [query_location]

Post by Ravana » February 10th, 2018, 8:47 pm

When you have multiple query_location in execution queue - like you call

Code: Select all

[query_location]
[/query_location]
[query_location]
[/query_location]
and leave during first choosing.

gfgtdf
Developer
Posts: 1067
Joined: February 10th, 2013, 2:25 pm

Re: [engine] Design of [query_location]

Post by gfgtdf » February 10th, 2018, 8:55 pm

hmm just tested that withotu any problems (the overlay has some problems with fog but that is a different issue, or maybe that one is already fixed in a later wesnoth version).
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.

Post Reply