[engine] road path wml tag

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

Moderator: Forum Moderators

Forum rules
Before posting a new idea, you must read the following:
Post Reply
vghetto
Posts: 755
Joined: November 2nd, 2019, 5:12 pm

[engine] road path wml tag

Post by vghetto »

Hi,

I propose having a new wml tag for creating roads.The end result would connect some source to some destination by doing a [terrain] change along a cost based path.

What I have in mind is something similar to how the pre-existing map generation roads do their thing.

It can be called [terrain_path] or [road_path]. An example usage would be something like the following:

Code: Select all

# Called from inside some event like prestart.
[road_path]
        # road start
        x,y=10,11
        
        # road destination
        to_x,to_y=20,21
        
        # should the border tile be considered for change
        include_borders=no
        
        # cost randomness factor
        road_windiness=3
        
        # costs fed to wesnoth.find_path function to determine the path.
        [road_cost]
                terrain=Ch
                cost=2
                convert_to=Ch
        [/road_cost]
        [road_cost]
                terrain=Rr
                cost=2
                convert_to=Rr
        [/road_cost]
        [road_cost]
                terrain=Gg
                cost=10
                convert_to=Rr
        [/road_cost]
        [road_cost]
                terrain=Hh
                cost=20
                convert_to=Rr
        [/road_cost]
        [road_cost]
                terrain=Ww
                cost=50
                convert_to_bridge=Ww^Bw|, Ww^Bw/, Ww^Bw\
                convert_to=Ch
        [/road_cost]
[/road_path]
Thanks.
vghetto
Posts: 755
Joined: November 2nd, 2019, 5:12 pm

Re: [engine] road path wml tag

Post by vghetto »

A working version of [road_path].
It needed to create a dummy unit for find_path to work. (why?)

Code mainly comes from the lua_mapgen_stuff branch on github. on_board was from wml-tags.lua.
I believe the code is by Celtic Minstrel and gfgtdf. There might have been other contributors.

Code: Select all

local helper = wesnoth.require "lua/helper.lua"
local LS = wesnoth.require "location_set"

local function on_board(x, y)
        if type(x) ~= "number" or type(y) ~= "number" then
                return false
        end
        local w, h = wesnoth.get_map_size()
        return x >= 1 and y >= 1 and x <= w and y <= h
end

local function insert_locs(x, y, locs_set)
	if locs_set:get(x,y) or not on_board(x, y) then
		return
	end
	locs_set:insert(x,y)
end

local function place_road(to_x, to_y, from_x, from_y, road_ops)
	if not on_board(to_x, to_y) then
		return
	end

	local tile_op = road_ops[wesnoth.get_terrain(to_x, to_y)]
	if tile_op then
		if tile_op.convert_to_bridge and from_x and from_y then
			local bridges = {}
			for elem in tile_op.convert_to_bridge:gmatch("[^%s,][^,]*") do
				table.insert(bridges, elem)
			end
			local dir = wesnoth.map.get_relative_dir(from_x, from_y, to_x, to_y)
			if dir == 'n' or dir == 's' then
				wesnoth.set_terrain(to_x, to_y, bridges[1])
			elseif dir == 'sw' or dir == 'ne' then
				wesnoth.set_terrain(to_x, to_y, bridges[2])
			elseif dir == 'se' or dir == 'nw' then
				wesnoth.set_terrain(to_x, to_y, bridges[3])
			end
		elseif tile_op.convert_to then
			local tile = helper.rand(tile_op.convert_to)
			wesnoth.set_terrain(to_x, to_y, tile)
		end
	end
end

function wesnoth.wml_actions.road_path(cfg)
	local from_x = tonumber(cfg.from_x) or helper.wml_error("[road_path] expects a from_x= attribute.")
	local from_y = tonumber(cfg.from_y) or helper.wml_error("[road_path] expects a from_y= attribute.")
	local to_x = tonumber(cfg.to_x) or helper.wml_error("[road_path] expects a to_x= attribute.")
	local to_y = tonumber(cfg.to_y) or helper.wml_error("[road_path] expects a to_y= attribute.")
        if not on_board(from_x, from_y) then
                return
        end

        if not on_board(to_x, to_y) then
                return
        end

	local windiness = tonumber(cfg.road_windiness) or 1

	local road_costs, road_ops = {}, {}
	for road in helper.child_range(cfg, "road_cost") do
		road_costs[road.terrain] = road.cost
		road_ops[road.terrain] = road
	end

	local path, cost

-- if wesnoth version >= 1.15.0
if wesnoth.compare_versions(wesnoth.game_config.version, ">=", "1.15.0") then

	-- viewing_side = 0 will crash
	-- max_cost = 1000, is ignored
	path, cost = wesnoth.find_path(from_x, from_y, to_x, to_y, {
		viewing_side = 1, ignore_units = true, ignore_teleport = true,
		calculate = function(x, y, current_cost)
			local res = 1.0
			local tile = wesnoth.get_terrain(x, y)
			res = road_costs[tile] or 1.0
			if windiness > 1 then
				res = res * wesnoth.random(windiness)
			end
			return res
		end })

-- else wesnoth version < 1.15.0
else

	path, cost = wesnoth.find_path(from_x, from_y, to_x, to_y,
		function(x, y, current_cost)
			local res = 1.0
			local tile = wesnoth.get_terrain(x, y)
			res = road_costs[tile] or 1.0
			if windiness > 1 then
				res = res * wesnoth.random(windiness)
			end
			return res
		end )


end
-- end wesnoth version

	local prev_x, prev_y
	for i, loc in ipairs(path) do
		local locs_set = LS.create()
		insert_locs(loc[1], loc[2], locs_set)
		for x,y in locs_set:stable_iter() do
			place_road(x, y, prev_x, prev_y, road_ops)
			prev_x, prev_y = x, y
		end
	end
end
Edit: fixed prev_x, prev_y for bridge building. Moved them outside the loop.
Edit2: update attachment to support 1.14 and 1.15.
Removed temp unit requirement.

Edit3: added check on the from_x,from_y and to_x,to_y
Expanded example to include scattered villages and then connect those villages to the nearest roads. Like WCII
Attachments
_main.cfg
(12.17 KiB) Downloaded 207 times
Post Reply