mirror of
https://github.com/wesnoth/wesnoth
synced 2025-04-24 20:27:40 +00:00

Note the `exclude_files` in .luacheckrc should be deleted once the eventual Ubuntu 22.04 base image has an updated luacheck that supports lua 5.4.
186 lines
4.9 KiB
Lua
186 lines
4.9 KiB
Lua
local MG = wesnoth.require "mapgen_helper"
|
|
local LS = wesnoth.require "location_set"
|
|
local random = mathx.random
|
|
|
|
local callbacks = {}
|
|
|
|
function callbacks.generate_map(params)
|
|
local map = MG.create_map(params.map_width, params.map_height, params.terrain_wall)
|
|
|
|
local function build_chamber(x, y, locs_set, size, jagged)
|
|
if locs_set:get(x,y) or not map:on_board(x, y) or size == 0 then
|
|
return
|
|
end
|
|
locs_set:insert(x,y)
|
|
for xn, yn in MG.adjacent_tiles(x, y) do
|
|
if random(100) <= 100 - jagged then
|
|
build_chamber(xn, yn, locs_set, size - 1, jagged)
|
|
end
|
|
end
|
|
end
|
|
|
|
local function clear_tile(x, y)
|
|
if not map:on_board(x,y) then
|
|
return
|
|
end
|
|
if map:get_tile(x,y) == params.terrain_castle or map:get_tile(x,y) == params.terrain_keep then
|
|
return
|
|
end
|
|
local r = random(1000)
|
|
if r <= params.village_density then
|
|
map:set_tile(x, y, params.terrain_village)
|
|
else
|
|
map:set_tile(x, y, params.terrain_clear)
|
|
end
|
|
end
|
|
|
|
local chambers = {}
|
|
local chambers_by_id = {}
|
|
local passages = {}
|
|
|
|
for chamber in wml.child_range(params, "chamber") do
|
|
local chance = tonumber(chamber.chance) or 100
|
|
local x = chamber.x
|
|
local y = chamber.y
|
|
local id = chamber.id
|
|
if chance == 0 or random(100) > chance then
|
|
-- Set chance to 0 so that the scenario generator can tell which chambers were used
|
|
params.chance = 0
|
|
goto continue
|
|
end
|
|
-- Ditto, set it to 100
|
|
params.chance = 100
|
|
if type(x) == "string" then
|
|
local x_min, x_max = x:match("(%d+)-(%d+)")
|
|
x = random(tonumber(x_min), tonumber(x_max))
|
|
end
|
|
if type(y) == "string" then
|
|
local y_min, y_max = y:match("(%d+)-(%d+)")
|
|
y = random(tonumber(y_min), tonumber(y_max))
|
|
end
|
|
local locs_set = LS.create()
|
|
build_chamber(x, y, locs_set, chamber.size or 3, chamber.jagged or 0)
|
|
local items = {}
|
|
for item in wml.child_range(chamber, "item_location") do
|
|
table.insert(items, item)
|
|
end
|
|
table.insert(chambers, {
|
|
center_x = x,
|
|
center_y = y,
|
|
side_num = chamber.side,
|
|
locs_set = locs_set,
|
|
id = id,
|
|
items = items,
|
|
})
|
|
chambers_by_id[id] = chambers[#chambers]
|
|
for passage in wml.child_range(chamber, "passage") do
|
|
local dst = chambers_by_id[passage.destination]
|
|
if dst ~= nil then
|
|
table.insert(passages, {
|
|
start_x = x,
|
|
start_y = y,
|
|
dest_x = dst.center_x,
|
|
dest_y = dst.center_y,
|
|
data = passage,
|
|
})
|
|
end
|
|
end
|
|
::continue::
|
|
end
|
|
|
|
for i,v in ipairs(chambers) do
|
|
local locs_list = {}
|
|
for x, y in v.locs_set:stable_iter() do
|
|
clear_tile(x, y)
|
|
if map:on_inner_board(x, y) then
|
|
table.insert(locs_list, {x,y})
|
|
end
|
|
end
|
|
for i1, item in ipairs(v.items or {}) do
|
|
local index = random(#locs_list)
|
|
local loc = locs_list[index]
|
|
table.remove(locs_list, index)
|
|
local x, y = table.unpack(loc)
|
|
|
|
if item.id then
|
|
map:add_location(x, y, item.id)
|
|
end
|
|
|
|
if item.place_castle then
|
|
map:set_tile(x, y, params.terrain_keep)
|
|
for x2, y2 in MG.adjacent_tiles(x, y) do
|
|
map:set_tile(x2, y2, params.terrain_castle)
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
for i,v in ipairs(passages) do
|
|
if random(100) <= (v.data.chance or 100) then
|
|
local windiness = v.data.windiness or 0
|
|
local laziness = math.max(v.data.laziness or 1, 1)
|
|
local width = math.max(v.data.width or 1, 1)
|
|
local jagged = v.data.jagged or 0
|
|
local calc = function(x, y)
|
|
if x == 0 or x == params.map_width - 1 or y == 0 or y == params.map_height - 1 then
|
|
-- Map borders are impassable
|
|
return math.huge
|
|
end
|
|
local res = 1.0
|
|
if map:get_tile(x, y) == params.terrain_wall then
|
|
res = laziness
|
|
end
|
|
if windiness > 1 then
|
|
res = res * random(windiness)
|
|
end
|
|
return res
|
|
end
|
|
local path = wesnoth.paths.find_path(
|
|
v.start_x, v.start_y, v.dest_x, v.dest_y, calc, params.map_width, params.map_height)
|
|
for j, loc in ipairs(path) do
|
|
local locs_set = LS.create()
|
|
build_chamber(loc[1], loc[2], locs_set, width, jagged)
|
|
for x,y in locs_set:stable_iter() do
|
|
clear_tile(x, y)
|
|
end
|
|
end
|
|
end
|
|
|
|
end
|
|
|
|
if type(params.transform) == "string" then
|
|
local chance = params.transform_chance or 100
|
|
if random(100) <= chance then
|
|
local transforms = {}
|
|
for t in params.transform:gmatch("[^%s,][^,]*") do
|
|
if MG.is_valid_transform(t) then
|
|
table.insert(transforms, t)
|
|
else
|
|
wml.error("Unknown transformation '" .. t .. "'")
|
|
end
|
|
end
|
|
map[transforms[random(#transforms)]](map)
|
|
end
|
|
end
|
|
|
|
return tostring(map)
|
|
end
|
|
|
|
function callbacks.generate_scenario(params)
|
|
-- This is more or less backwards compatible with the cave generator syntax
|
|
local scenario = wml.get_child(params, "scenario")
|
|
scenario.map_data = callbacks.generate_map(params)
|
|
for chamber in wml.child_range(params, "chamber") do
|
|
local chamber_items = wml.get_child(chamber, "items")
|
|
if chamber.chance == 100 and chamber_items then
|
|
-- TODO: Should we support [event]same_location_as_previous=yes?
|
|
for i,tag in ipairs(chamber_items) do
|
|
table.insert(scenario, tag)
|
|
end
|
|
end
|
|
end
|
|
return scenario
|
|
end
|
|
|
|
return callbacks
|