wesnoth/data/lua/cave_map_generator.lua
gfgtdf 60d114b606 finish moving cavegen (cave_map_generator.cpp) to lua.
This map generator is used in HttT 17 and SoF 4. which needed to be updated aswell.

With this we finally have a lua generator that is used in mainline and that
can be used an an example by wml authors who want to write other map generators.

Unfortunateley there are still umc add ons in 1.12 that use
scenario_generation=cave so i didn't remove the cpp implementation yet.
2016-09-17 20:52:49 +02:00

201 lines
5.0 KiB
Lua

local helper = wesnoth.require "lua/helper.lua"
local T = helper.set_wml_tag_metatable {}
local random = wesnoth.random
local params = _G.params
local map = {}
local function loc_to_index(x,y)
return x + 1 + y * params.map_width
end
local function index_to_loc(index)
return (index -1) % params.map_width, math.floor((index -1) / params.map_width)
end
for i = 1, params.map_width * params.map_height do
table.insert(map, params.terrain_wall)
end
--positions are 0-based to match the ingame locations.
local function set_tile_index(index, val)
if index < 1 or index > params.map_width * params.map_height then
return
end
if map[index] == params.terrain_castle or map[index] == params.terrain_keep then
return
end
if val == params.terrain_clear then
local r = random(1000)
if r <= params.village_density then
map[index] = params.terrain_village
else
map[index] = params.terrain_clear
end
else
map[index] = val
end
end
local function set_tile(x, y, val)
set_tile_index(loc_to_index(x, y), val)
end
local function get_tile(x, y, val)
return map[loc_to_index(x,y)]
end
local function on_board(x, y)
return x >= 0 and y >= 0 and x < params.map_width and y < params.map_height
end
local function on_inner_board(x, y)
return x >= 1 and y >= 1 and x < params.map_width - 1 and y < params.map_height - 1
end
local adjacent_offset = {
{ {0,-1}, {1,-1}, {1,0}, {0,1}, {-1,0}, {-1,-1} },
{ {0,-1}, {1,0}, {1,1}, {0,1}, {-1,1}, {-1,0} }
}
local function adjacent_tiles(x, y)
local offset = adjacent_offset[2 - (x % 2)]
local i = 0
return function()
i = i + 1
if i <= 6 then
return offset[i][1] + x, offset[i][2] + y
else
return nil
end
end
end
local function build_chamber(x, y, locs_set, size, jagged)
local index = loc_to_index(x, y)
if locs_set[index] or not on_board(x, y) or size == 0 then
return
end
locs_set[index] = {x, y}
for xn, yn in adjacent_tiles(x, y) do
if random(100) <= 100 - jagged then
build_chamber(xn, yn, locs_set, size - 1, jagged)
end
end
end
local chambers = {}
local chambers_by_id = {}
local passages = {}
local starting_positions = {}
for chamber in helper.child_range(params, "chamber") do
local x = chamber.x
local y = chamber.y
local id = chamber.id
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 = {}
build_chamber(x, y, locs_set, chambers.size or 3, chambers.size or 0)
local items = {}
for item in helper.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,
--items = helper.get_child(v, "items")
})
chambers_by_id[id] = chambers[#chambers]
for passage in helper.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
end
for i,v in ipairs(chambers) do
local locs_list = {}
for k2,v2 in pairs(v.locs_set) do
set_tile_index(k2, params.terrain_clear)
if on_inner_board (v2[1], v2[2]) then
table.insert(locs_list, k2)
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 = index_to_loc(loc)
starting_positions[loc] = item.id
if item.place_castle then
set_tile_index(loc, params.terrain_keep)
for x2, y2 in adjacent_tiles(x, y) do
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, current_cost)
local res = 1.0
if 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, cost = wesnoth.find_path(v.start_x, v.start_y, v.dest_x, v.dest_y, calc, params.map_width, params.map_height)
for i, loc in ipairs(path) do
local locs_set = {}
build_chamber(loc[1], loc[2], locs_set, width, jagged)
for k,v in pairs(locs_set) do
set_tile_index(k, params.terrain_clear)
end
end
end
end
local stringbuilder = {}
for y = 0, params.map_height - 1 do
local stringbuilder_row = {}
for x = 0, params.map_width - 1 do
local index = x + 1 + y * params.map_width
if starting_positions[index] ~= nil then
table.insert(stringbuilder_row, tostring(starting_positions[index]) .. " " .. map[index])
else
table.insert(stringbuilder_row, map[index])
end
end
table.insert(stringbuilder, table.concat(stringbuilder_row, ","))
end
return table.concat(stringbuilder, "\n")