Added a sample lua map generator

this is more or less the code of the cavegen as used in HttT scenario 17
ported to lua.
This commit is contained in:
gfgtdf 2016-02-08 21:10:41 +01:00
parent 80a99f828c
commit 37e613b6fc
2 changed files with 352 additions and 0 deletions

View file

@ -0,0 +1,14 @@
#ifdef DEBUG_MODE
[multiplayer]
id="lua_cavegen"
name= _ "Cave Generator"
description= _ "A sample lua map generator that generates caves like in scenario 17 of the mainline campaign 'Heir to the Throne'"
map_generation="lua"
[generator]
id="lua_cavegen"
config_name="Lua Cave Generator"
create_map = << return wesnoth.dofile("multiplayer/scenarios/Random_Scenario_Lua_Cave.lua") >>
[/generator]
{DEFAULT_SCHEDULE}
[/multiplayer]
#endif

View file

@ -0,0 +1,338 @@
local helper = wesnoth.require "lua/helper.lua"
local T = helper.set_wml_tag_metatable {}
local random = wesnoth.random
local passage_chance = function(chance, dest, width, windiness, jagged)
return T.passage {
chance = chance,
destination = dest,
width = width,
windiness = windiness,
jagged = jagged,
}
end
local passage_normal = function(dest, width, windiness, jagged)
return passage_chance(nil, dest, width, windiness, jagged)
end
local param_chambers = {
{
id = "player",
x = "15-35",
y = 68,
size = 8,
jagged = 50,
side = 1,
},
{
id = "antechamber_1",
x = "10-25",
y = "50-60",
size = 7,
jagged = 5,
side = 2,
passage_normal("player", 2, 10, 10),
},
{
id="antechamber_2",
x="25-40",
y="50-60",
size=7,
jagged=2,
passage_normal("player", 2, 3, 1),
passage_chance(40, "antechamber_1", 1, 9, 9),
side=3,
},
{
id="center",
x="25-26",
y="35-36",
size=2,
jagged=1,
passage_normal("antechamber_1", 1, 20, 3),
passage_normal("antechamber_2", 1, 20, 3),
},
{
id="mini_1",
x="10-16",
y="36-40",
size=5,
jagged=2,
passage_normal("center", 1, 5, 2),
passage_normal("antechamber_1", 2, 5, 2),
},
{
id="mini_2",
x="8-20",
y="17-26",
size=5,
jagged=3,
passage_normal("center", 1, 5, 2),
passage_normal("mini_1", 1, 5, 2),
},
{
id="mini_3",
x="6-44",
y="14-30",
size=3,
jagged=4,
passage_normal("center", 1, 5, 2),
passage_normal("mini_2", 2, 5, 2),
},
{
id="mini_4",
x="30-42",
y="17-26",
size=5,
jagged=5,
passage_normal("center", 1, 5, 2),
passage_normal("mini_3", 2, 5, 2),
},
{
id="mini_5",
x="34-40",
y="36-40",
size=5,
jagged=5,
passage_normal("mini_4", 2, 5, 2),
passage_normal("center", 1, 5, 2),
passage_normal("antechamber_2", 2, 5, 2),
},
{
id="exit",
x=25,
y=1,
size=2,
jagged=1,
passage_normal("mini_2", 1, 5, 2),
passage_normal("mini_3", 1, 5, 2),
passage_normal("mini_4", 1, 5, 2),
},
{
id="enemy_1",
x="6-15",
y="45-55",
size=5,
jagged=3,
passage_normal("mini_1", 2, 5, 2),
passage_chance(60, "mini_2", 1, 5, 2),
passage_chance(40, "antechamber_2", 1, 5, 2),
side=4,
},
{
id="enemy_2",
x="6-15",
y="1-35",
size=5,
jagged=3,
passage_normal("mini_2", 2, 5, 2),
passage_normal("enemy_1", 2, 5, 5),
passage_chance(60, "mini_1", 1, 5, 2),
passage_chance(40, "mini_3", 1, 5, 2),
side=5,
},
{
id="enemy_3",
x="35-45",
y="1-35",
size=5,
jagged=3,
passage_normal("mini_4", 2, 5, 2),
passage_chance(60, "mini_5", 1, 5, 2),
passage_chance(40, "mini_3", 1, 5, 2),
side=6,
},
{
id="enemy_4",
x="35-45",
y="45-55",
size=5,
jagged=3,
passage_normal("mini_5", 2, 5, 2),
passage_normal("enemy_3", 2, 5, 5),
passage_chance(60, "mini_4", 1, 5, 2),
passage_chance(40, "antechamber_2", 1, 5, 2),
side=7,
}
}
local size_x = 50
local size_y = 70
local terrain_wall = "Xu"
local terrain_clear = "Re"
local terrain_keep = "Kud"
local terrain_castle = "Cud"
local terrain_village = "Uu^Vud"
local terrain_path_marker = "Gs"
local map = {}
local function loc_to_index(x,y)
return x + 1 + (y) * size_x
end
for i = 1, size_x * size_y do
table.insert(map, terrain_wall)
end
--positions are 0-based to match the ingame locations.
local function set_tile_index(index, val)
if index < 1 or index > size_x * size_y then
return
end
if map[index] == terrain_castle or map[index] == terrain_keep or map[index] == terrain_path_marker then
return
end
if random(100) < 5 then
map[index] = terrain_village
else
map[index] = val
end
end
local function set_tile(x, y, val)
set_tile_index(x + 1 + (y) * size_x, val)
end
local function get_tile(x, y, val)
local index = x + 1 + (y) * size_x
return map[index], index
end
local function on_board(x, y)
return x >= 0 and y >= 0 and x < size_x and y < size_y
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 = x + 1 + (y) * size_x
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 i,v in ipairs(param_chambers) do
local x = v.x
local y = v.y
local id = v.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)
table.insert(chambers, {
center_x = x,
center_y = y,
side_num = v.side,
locs_set = locs_set,
id = id,
items = helper.get_child(v, "items")
})
chambers_by_id[id] = chambers[#chambers]
for passage in helper.child_range(v, "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, terrain_clear)
table.insert(locs_list, v2)
end
if v.side_num then
starting_positions[v.side_num] = { v.center_x, v.center_y }
set_tile(v.center_x, v.center_y, terrain_keep)
for x, y in adjacent_tiles(v.center_x, v.center_y) do
set_tile(x, y, terrain_castle)
end
end
end
for i,v in ipairs(passages) do
if math.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) == terrain_wall then
res = laziness
end
if windiness > 1 then
res = res * (math.random(windiness) - 1)
end
return res
end
local path, cost = wesnoth.find_path(v.start_x, v.start_y, v.dest_x, v.dest_y, calc, size_x, size_y)
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, terrain_clear)
end
--set_tile(loc[1], loc[2], terrain_path_marker)
end
end
end
local starting_pos_by_tile = {}
for k,v in ipairs(starting_positions) do
starting_pos_by_tile[v[1] + 1 + v[2] * size_x] = k
end
local stringbuilder = {}
for y = 0, size_y - 1 do
local stringbuilder_row = {}
for x = 0, size_x - 1 do
local index = x + 1 + y * size_x
if starting_pos_by_tile[index] ~= nil then
table.insert(stringbuilder_row, tostring(starting_pos_by_tile[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")