Skip to content
Snippets Groups Projects
Commit d206cf14 authored by DarkGod's avatar DarkGod
Browse files

MapScript generator

modularizing stuff and making Tilemap the basis for mapscripts
parent b4e022fd
No related branches found
No related tags found
1 merge request!509WIP: Orange tooltip edits
Pipeline #
......@@ -30,7 +30,7 @@ function _M:init(zone, map, level, data)
self.data = data
self.grid_list = zone.grid_list
self.spots = {}
self.mapsize = {self.map.w, self.map.h}
self.mapsize = {self.map.w, self.map.h, w=self.map.w, h=self.map.h}
RoomsLoader.init(self, data)
end
......@@ -55,7 +55,21 @@ function _M:regenerate()
end
function _M:custom(lev, old_lev)
if self.data.custom then return self.data.custom(self, lev, old_lev) end
if self.data.mapscript then
local file = self:getFile(self.data.mapscript..".lua", "mapscripts")
local f, err = loadfile(file)
if not f and err then error(err) end
setfenv(f, setmetatable(env or {
self = self,
lev = lev,
old_lev = old_lev,
Tilemap = require "engine.tilemaps.Tilemap",
}, {__index=_G}))
return f()
end
if self.data.custom then
return self.data.custom(self, lev, old_lev)
end
error("Generator FromCustom called without customization!")
end
......
......@@ -21,28 +21,41 @@ require "engine.class"
local lom = require "lxp.lom"
local mime = require "mime"
--- Generate map-like data from samples using the WaveFunctionCollapse algorithm (in C++)
-- @classmod engine.WaveFunctionCollapse
--- Base class to generate map-like
-- @classmod engine.tilemaps.Tilemap
module(..., package.seeall, class.make)
--- Run the algorithm
-- It will produce internal results which this class can then manipulate
-- If async is true in the parameters the generator will run in an asynchronous thread and you must call :waitCompute()
-- before using the results, this allows you to run multiple WFCs for later merging wihtout taking more time (if the user have enoguh CPUs obviously)
function _M:init(t)
assert(t.mode == "overlapping", "bad WaveFunctionCollapse mode")
assert(t.size, "WaveFunctionCollapse has no size")
self.data_w = t.size[1]
self.data_h = t.size[2]
if t.mode == "overlapping" then
if type(t.sample) == "string" then
t.sample = self:tmxLoad(t.sample)
function _M:init(size, fill_with)
if size then
self.data_w = size[1]
self.data_h = size[2]
if self.data_w and self.data_h then
self.data = {}
for y = 1, self.data_h do
self.data[y] = {}
for x = 1, self.data_w do
self.data[y][x] = fill_with or ' '
end
end
end
self:run(t)
end
end
--- Used internally to load a sample from a tmx file
--- Find all empty spaces (defaults to ' ') and fill them with a give char
function _M:fillAll(fill_with, empty_char)
if not self.data then return end
empty_char = empty_char or ' '
fill_with = fill_with or '#'
for y = 1, self.data_h do
for x = 1, self.data_w do
if self.data[y][x] == empty_char then
self.data[y][x] = fill_with
end
end
end
end
--- Used internally to load a tilemap from a tmx file
function _M:tmxLoad(file)
local f = fs.open(file, "r") local data = f:read(10485760) f:close()
local map = lom.parse(data)
......@@ -118,36 +131,14 @@ function _M:tmxLoad(file)
end
end
end
for y = 1, h do data[y] = table.concat(data[y]) end
return data
end
--- Used internally to parse the results
function _M:parseResult(data)
if not data then return end
self.data = {}
for y = 1, #data do
local x = 1
self.data[y] = {}
for c in data[y]:gmatch('.') do
self.data[y][x] = c
x = x + 1
end
end
return data, w, h
end
--- Called by the constructor to actaully start doing stuff
function _M:run(t)
print("[WaveFunctionCollapse] running with parameter table:")
table.print(t)
if not t.async then
local data = core.generator.wfc.overlapping(t.sample, t.size[1], t.size[2], t.n, t.symmetry, t.periodic_out, t.periodic_in, t.has_foundation)
self:parseResult(data)
return false
else
self.async_data = core.generator.wfc.asyncOverlapping(t.sample, t.size[1], t.size[2], t.n, t.symmetry, t.periodic_out, t.periodic_in, t.has_foundation)
return true
end
function _M:collapseToLineFormat(data)
if not data then return nil end
local ndata = {}
for y = 1, #data do ndata[y] = table.concat(data[y]) end
return ndata
end
--- Do we have results, or did we fail?
......@@ -155,31 +146,9 @@ function _M:hasResult()
return self.data and true or false
end
--- Wait for computation to finish if in async mode, if not it just returns immediately
function _M:waitCompute()
if not self.async_data then return end
local data = self.async_data:wait()
self.async_data = nil
self:parseResult(data)
return self:hasResult()
end
--- Wait for multiple WaveFunctionCollapse at once
-- Static
function _M:waitAll(...)
local all_have_data = true
for _, wfcasync in ipairs{...} do
wfcasync:waitCompute()
if not wfcasync:hasResult() then all_have_data = false end -- We cant break, we need to wait all the threads to not leave them dangling in the wind
end
return all_have_data
end
--- Return a list of groups of tiles representing each of the connected areas
function _M:findFloodfillGroups(wall)
if type(wall) == "table" then local first = wall[1] wall = table.reverse(wall) wall.__first = first
else wall = {[wall] = true, __first=wall} end
--- Return a list of groups of tiles that matches the given cond function
function _M:findGroups(cond)
if not self.data then return {} end
local fills = {}
local opens = {}
......@@ -187,7 +156,7 @@ function _M:findFloodfillGroups(wall)
for i = 1, self.data_w do
opens[i] = {}
for j = 1, self.data_h do
if not wall[self.data[j][i]] then
if cond(self.data[j][i]) then
opens[i][j] = #list+1
list[#list+1] = {x=i, y=j}
end
......@@ -226,28 +195,40 @@ function _M:findFloodfillGroups(wall)
local i, l = next(list)
local closed = floodFill(l.x, l.y)
groups[#groups+1] = {id=id, list=closed}
print("[WaveFunctionCollapse] Floodfill group", i, #closed)
print("[Tilemap] Floodfill group", i, #closed)
end
return groups, wall
return groups
end
--- Return a list of groups of tiles representing each of the connected areas
function _M:findGroupsNotOf(wall)
wall = table.reverse(wall)
return self:findGroups(function(c) return not wall[c] end)
end
--- Find groups by floodfill and apply a custom function over them
--- Return a list of groups of tiles representing each of the connected areas
function _M:findGroupsOf(floor)
floor = table.reverse(floor)
return self:findGroups(function(c) return floor[c] end)
end
--- Apply a custom method over the given groups, sorting them from bigger to smaller
-- It gives the groups in order of bigger to smaller
function _M:applyOnFloodfillGroups(wall, fct)
function _M:applyOnGroups(groups, fct)
if not self.data then return end
local groups = self:findFloodfillGroups(wall)
table.sort(groups, function(a,b) return #a.list > #b.list end)
for _, group in ipairs(groups) do
fct(self.data_w, self.data_h, self.data, group)
for id, group in ipairs(groups) do
fct(self.data_w, self.data_h, self.data, group, id)
end
end
--- Given a list of groups, eliminate them all
function _M:eliminateGroups(wall, groups)
print("[WaveFunctionCollapse] Eleminating groups", #groups)
if not self.data then return end
print("[Tilemap] Eleminating groups", #groups)
for i = 1, #groups do
print("[WaveFunctionCollapse] Eleminating group "..i.." of", #groups[i].list)
print("[Tilemap] Eleminating group "..i.." of", #groups[i].list)
for j = 1, #groups[i].list do
local jn = groups[i].list[j]
self.data[jn.y][jn.x] = wall
......@@ -256,12 +237,13 @@ function _M:eliminateGroups(wall, groups)
end
--- Simply destroy all connected groups except the biggest one
function _M:eliminateByFloodfill(wall)
local groups, wall = self:findFloodfillGroups(wall)
function _M:eliminateByFloodfill(walls)
if not self.data then return 0 end
local groups = self:findGroupsNotOf(walls)
-- If nothing exists, regen
if #groups == 0 then
print("[WaveFunctionCollapse] Floodfill found nothing")
print("[Tilemap] Floodfill found nothing")
return 0
end
......@@ -269,20 +251,64 @@ function _M:eliminateByFloodfill(wall)
table.sort(groups, function(a,b) return #a.list < #b.list end)
local g = table.remove(groups)
if g and #g.list > 0 then
print("[WaveFunctionCollapse] Ok floodfill with main group size", #g.list)
self:eliminateGroups(wall.__first, groups)
print("[Tilemap] Ok floodfill with main group size", #g.list)
self:eliminateGroups(walls[1], groups)
return #g.list
else
print("[WaveFunctionCollapse] Floodfill left nothing")
print("[Tilemap] Floodfill left nothing")
return 0
end
end
function _M:isInGroup(group, x, y)
if not group.reverse then
group.reverse = {}
for j = 1, #group.list do
local jn = group.list[j]
group.reverse[jn.x] = group.reverse[jn.x] or {}
group.reverse[jn.x][jn.y] = true
end
end
return group.reverse[x] and group.reverse[x][y]
end
--- Find the biggest rectangle that can fit fully in the given group
function _M:groupInnerRectangle(group)
end
--- Find the smallest rectangle that can fit around in the given group
function _M:groupOuterRectangle(group)
local n = group.list[1]
if not n then return end -- wtf?
local x1, x2 = n.x, n.x
local y1, y2 = n.y, n.y
for j = 1, #group.list do
local jn = group.list[j]
if jn.x < x1 then x1 = jn.x end
if jn.x > x2 then x2 = jn.x end
if jn.y < y1 then y1 = jn.y end
if jn.y > y2 then y2 = jn.y end
end
-- Debug
-- for i = x1, x2 do for j = y1, y2 do
-- if not self:isInGroup(group, i, j) then
-- if self.data[j][i] == '#' then
-- self.data[j][i] = 'T'
-- end
-- end
-- end end
return {x1=x1, y1=y1, x2=x2, y2=y2}
end
--- Get the results
-- @param is_array if true returns a table[][] of characters, if false a table[] of string lines
function _M:getResult(is_array)
if is_array then return self.data end
if not self.data then return nil end
if is_array then return self.data end
local data = {}
for y = 1, self.data_h do data[y] = table.concat(self.data[y]) end
return data
......@@ -290,8 +316,13 @@ end
--- Debug function to print the result to the log
function _M:printResult()
if not self.data then
print("-------------")
print("------------- Tilemap result")
return
end
print("-------------")
print("------------- WaveFunctionCollapse result")
print("------------- Tilemap result")
print("-----------[[")
for _, line in ipairs(self:getResult()) do
print(line)
......@@ -301,22 +332,28 @@ function _M:printResult()
print("-------------")
end
--- Merge and other WaveFunctionCollapse's data
function _M:merge(wfc, empty_char, char_order)
--- Merge and other Tilemap's data
function _M:merge(x, y, tm, char_order, empty_char)
if not self.data or not tm.data then return end
x = math.floor(x)
y = math.floor(y)
char_order = table.reverse(char_order or {})
empty_char = empty_char or ' '
if not wfc.data then return end
for i = 1, math.min(self.data_w, wfc.data_w) do
for j = 1, math.min(self.data_h, wfc.data_h) do
local c = wfc.data[j][i]
if c ~= empty_char then
local sc = self.data[j][i]
local sc_o = char_order[sc] or 0
local c_o = char_order[c] or 0
if c_o >= sc_o then
self.data[j][i] = wfc.data[j][i]
if not tm.data then return end
for i = 1, tm.data_w do
for j = 1, tm.data_h do
local si, sj = i + x - 1, j + y - 1
if si >= 1 and si <= self.data_w and sj >= 1 and sj <= self.data_h then
local c = tm.data[j][i]
if c ~= empty_char then
local sc = self.data[sj][si]
local sc_o = char_order[sc] or 0
local c_o = char_order[c] or 0
if c_o >= sc_o then
self.data[sj][si] = tm.data[j][i]
end
end
end
end
......
-- TE4 - T-Engine 4
-- Copyright (C) 2009 - 2017 Nicolas Casalini
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
--
-- Nicolas Casalini "DarkGod"
-- darkgod@te4.org
require "engine.class"
local lom = require "lxp.lom"
local mime = require "mime"
local Tilemap = require "engine.tilemaps.Tilemap"
--- Generate map-like data from samples using the WaveFunctionCollapse algorithm (in C++)
-- @classmod engine.tilemaps.WaveFunctionCollapse
module(..., package.seeall, class.inherit(Tilemap))
--- Run the algorithm
-- It will produce internal results which this class can then manipulate
-- If async is true in the parameters the generator will run in an asynchronous thread and you must call :waitCompute()
-- before using the results, this allows you to run multiple WFCs for later merging wihtout taking more time (if the user have enoguh CPUs obviously)
function _M:init(t)
assert(t.mode == "overlapping", "bad WaveFunctionCollapse mode")
assert(t.size, "WaveFunctionCollapse has no size")
self.data_w = t.size[1]
self.data_h = t.size[2]
if t.mode == "overlapping" then
if type(t.sample) == "string" then
t.sample = self:collapseToLineFormat(self:tmxLoad(t.sample))
end
self:run(t)
end
end
--- Used internally to parse the results
function _M:parseResult(data)
print("[WaveFunctionCollapse] compute done", data)
if not data then return end
self.data = {}
for y = 1, #data do
local x = 1
self.data[y] = {}
for c in data[y]:gmatch('.') do
self.data[y][x] = c
x = x + 1
end
end
end
--- Called by the constructor to actaully start doing stuff
function _M:run(t)
print("[WaveFunctionCollapse] running with parameter table:")
table.print(t)
if not t.async then
local data = core.generator.wfc.overlapping(t.sample, t.size[1], t.size[2], t.n, t.symmetry, t.periodic_out, t.periodic_in, t.has_foundation)
self:parseResult(data)
return false
else
self.async_data = core.generator.wfc.asyncOverlapping(t.sample, t.size[1], t.size[2], t.n, t.symmetry, t.periodic_out, t.periodic_in, t.has_foundation)
return true
end
end
--- Wait for computation to finish if in async mode, if not it just returns immediately
function _M:waitCompute()
if not self.async_data then return end
local data = self.async_data:wait()
self.async_data = nil
self:parseResult(data)
return self:hasResult()
end
--- Wait for multiple WaveFunctionCollapse at once
-- Static
function _M:waitAll(...)
local all_have_data = true
for _, wfcasync in ipairs{...} do
wfcasync:waitCompute()
if not wfcasync:hasResult() then all_have_data = false end -- We cant break, we need to wait all the threads to not leave them dangling in the wind
end
return all_have_data
end
......@@ -1845,7 +1845,11 @@ function _M:setupCommands()
print("===============")
end end,
[{"_g","ctrl"}] = function() if config.settings.cheat then
self:changeLevel(game.level.level + 1)
if self.zone.short_name ~= "test" then
self:changeLevel(1, "test")
else
self:changeLevel(game.level.level + 1)
end
do return end
local m = game.zone:makeEntity(game.level, "actor", {name="elven mage"}, nil, true)
local x, y = util.findFreeGrid(game.player.x, game.player.y, 20, true, {[Map.ACTOR]=true})
......
......@@ -20,3 +20,4 @@
load("/data/general/grids/basic.lua")
load("/data/general/grids/forest.lua")
load("/data/general/grids/water.lua")
load("/data/general/grids/jungle_hut.lua")
-- ToME - Tales of Maj'Eyal
-- Copyright (C) 2009 - 2017 Nicolas Casalini
--
-- This program is free software: you can redistribute it and/or modify
-- it under the terms of the GNU General Public License as published by
-- the Free Software Foundation, either version 3 of the License, or
-- (at your option) any later version.
--
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-- GNU General Public License for more details.
--
-- You should have received a copy of the GNU General Public License
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
--
-- Nicolas Casalini "DarkGod"
-- darkgod@te4.org
local Tilemap = require "engine.tilemaps.Tilemap"
local WaveFunctionCollapse = require "engine.tilemaps.WaveFunctionCollapse"
local merge_order = {'.', '_', 'r', '+', '#', 'O', '=', ';', 'T'}
-- Water & trees layer
local wfcwater = WaveFunctionCollapse.new{
mode="overlapping", async=true,
sample=self:getFile("!wfctest2.tmx", "samples"),
size={self.mapsize.w/3, self.mapsize.h},
n=3, symmetry=8, periodic_out=true, periodic_in=true, has_foundation=false
}
-- Outer buildings
local wfcouter = WaveFunctionCollapse.new{
mode="overlapping", async=true,
sample=self:getFile("!wfctest.tmx", "samples"),
size={self.mapsize.w/3, self.mapsize.h},
n=3, symmetry=8, periodic_out=true, periodic_in=true, has_foundation=false
}
-- Inner buildings
local wfcinner = WaveFunctionCollapse.new{
mode="overlapping", async=true,
sample=self:getFile("!wfctest4.tmx", "samples"),
size={self.mapsize.w*2/3, self.mapsize.h},
n=3, symmetry=8, periodic_out=false, periodic_in=false, has_foundation=false
}
-- Wait for all generators to finish
if not WaveFunctionCollapse:waitAll(wfcinner, wfcwater, wfcouter) then print("[inner_outer] a WFC failed") return self:regenerate() end
-- Merge them all
local tm = Tilemap.new(self.mapsize)
wfcouter:merge(1, 1, wfcwater, self.data.merge_order)
if wfcouter:eliminateByFloodfill{'#', 'T'} < 400 then print("[inner_outer] outer is too small") return self:regenerate() end
if wfcinner:eliminateByFloodfill{'#', 'T'} < 400 then print("[inner_outer] inner is too small") return self:regenerate() end
tm:merge(1, 1, wfcouter, self.data.merge_order)
tm:merge(self.mapsize.w - wfcinner.data_w, 1, wfcinner, self.data.merge_order)
-- Find rooms
local rooms = tm:findGroupsOf{'r'}
tm:applyOnGroups(rooms, function(w, h, data, room, idx)
print("ROOM", idx, "::" , unpack(tm:groupOuterRectangle(room)))
for j = 1, #room.list do
local jn = room.list[j]
-- data[jn.y][jn.x] = tostring(idx)
end
end)
tm:fillAll('.', 'r')
tm:fillAll()
-- Elimitate the rest
if tm:eliminateByFloodfill{'#', 'T'} < 400 then return self:regenerate() end
tm:printResult()
return tm:getResult(true)
......@@ -17,7 +17,7 @@
</tile>
<tile id="51">
<properties>
<property name="id" value="."/>
<property name="id" value="_"/>
</properties>
</tile>
<tile id="54">
......@@ -25,16 +25,26 @@
<property name="id" value="*"/>
</properties>
</tile>
<tile id="63">
<properties>
<property name="id" value="O"/>
</properties>
</tile>
</tileset>
<tileset firstgid="91" name="tome-terrains" tilewidth="32" tileheight="32" tilecount="144">
<image source="../../../../../../../tiled-maps/gfx/tome-terrains.png" width="256" height="576"/>
<tile id="22">
<properties>
<property name="id" value=";"/>
</properties>
</tile>
</tileset>
<tileset firstgid="235" name="numbers" tilewidth="32" tileheight="32" tilecount="36">
<image source="../../../../../../../tiled-maps/gfx/numbers.png" width="192" height="192"/>
</tileset>
<layer name="Terrain" width="16" height="16">
<data encoding="base64" compression="zlib">
eJxjZGBgYARiFiiNjLGJYRM3QcPYxHCJk6KfWDNZaGQ/LrWEwoucsCInLMjRPxTkSU1/g818fPK40iq94hcA4S4l9A==
eJxzYGBgcADiQiiNjLGJYRMvRMPYxHCJk6KfWDNJUUsN9xMKL3LCihz/kaN/KMhjC0N8YTrYzKck/mgdvwBZdmTy
</data>
</layer>
</map>
......@@ -49,7 +49,7 @@
</tileset>
<layer name="Terrain" width="16" height="16">
<data encoding="base64" compression="zlib">
eJxjYBgFwwEUomFy9NZDMTb9+MxE1otPPzb3odtdj0dNPxQjy2PTj+4GZL3IZuByPyn6kf1Eqf5CBvz+JxSG+NIALnEAH5QrWQ==
eJxjYKAPKKSCfmRMjt5+KCbVDGS9yGYMJv34wgddDl0NjF8Pxdjchi/skfXi0o8LoNtdz0Ba/GDTT44byNWPHC6U6oeZQU76xBe/+AAAWvQrWQ==
</data>
</layer>
</map>
<?xml version="1.0" encoding="UTF-8"?>
<map version="1.0" orientation="orthogonal" renderorder="right-down" width="16" height="16" tilewidth="32" tileheight="32" nextobjectid="31">
<map version="1.0" orientation="orthogonal" renderorder="right-down" width="20" height="20" tilewidth="32" tileheight="32" nextobjectid="31">
<properties>
<property name="status_all" value="{no_teleport=true}"/>
</properties>
......@@ -22,7 +22,12 @@
</tile>
<tile id="54">
<properties>
<property name="id" value="*"/>
<property name="id" value="_"/>
</properties>
</tile>
<tile id="58">
<properties>
<property name="id" value="r"/>
</properties>
</tile>
</tileset>
......@@ -32,9 +37,9 @@
<tileset firstgid="235" name="numbers" tilewidth="32" tileheight="32" tilecount="36">
<image source="../../../../../../../tiled-maps/gfx/numbers.png" width="192" height="192"/>
</tileset>
<layer name="Terrain" width="16" height="16">
<layer name="Terrain" width="20" height="20">
<data encoding="base64" compression="zlib">
eJxjZGBgYMSBTfDIoatDxoTEidVPrFvw6UeXI0YNvfXj8yc5+onBtLafmHRDaboj1e/DXT85GABHnxWG
eJytkzEOwDAIAxOJJ3Xj///qVCmywLiEwVPQQbDZa62d6CFvTH4Ieac6PGR2ZmU8pdcNr8OseCoz2tME75MRnhHfWS6q+bAeGX+zptTaIC/7Wzfn2b4iXuU3Y3lRF3lyw3NgKfNZ0TfKmnoTyr1M87pZmMw+6gVVviXl
</data>
</layer>
</map>
......@@ -24,8 +24,8 @@ return {
max_level = 4,
decay = {300, 800},
actor_adjust_level = function(zone, level, e) return zone.base_level + e:getRankLevelAdjust() + level.level-1 + rng.range(-1,2) end,
width = 50, height = 50,
-- all_remembered = true,
width = 80, height = 50,
all_remembered = true,
all_lited = true,
no_level_connectivity = true,
......@@ -35,37 +35,13 @@ return {
generator = {
map = {
-- [[
class = "engine.generator.map.FromCustom",
class = "engine.generator.map.MapScript",
['<'] = "UP", ['>'] = "DOWN",
['.'] = "FLOOR", ['+'] = "DOOR", ['#'] = "WALL",
['_'] = "OLD_FLOOR", ['O'] = "OLD_WALL",
[';'] = "GRASS", ['T'] = "TREE",
['='] = "DEEP_WATER",
custom = function(self, lev, old_lev)
local WaveFunctionCollapse = require "engine.WaveFunctionCollapse"
-- Water & trees layer
local wfcwater = WaveFunctionCollapse.new{
mode="overlapping", async=true,
sample=self:getFile("!wfctest2.tmx", "samples"),
size=self.mapsize,
n=3, symmetry=8, periodic_out=true, periodic_in=true, has_foundation=false
}
-- Base buildings
local wfc = WaveFunctionCollapse.new{
mode="overlapping", async=true,
sample=self:getFile("!wfctest4.tmx", "samples"),
size=self.mapsize,
n=3, symmetry=8, periodic_out=false, periodic_in=false, has_foundation=false
}
-- Wait for all generators to finish
if not WaveFunctionCollapse:waitAll(wfc, wfcwater) then return self:regenerate() end
-- Merge water in
wfc:merge(wfcwater, ' ', {'.', '+', '#', '=', ';', 'T'})
-- Elimitate the rest
if wfc:eliminateByFloodfill{'#', 'T'} < 800 then return self:regenerate() end
wfc:printResult()
return wfc:getResult(true)
end,
mapscript = "!inner_outer",
--]]
--[[
class = "engine.generator.map.Hexacle",
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment