diff --git a/game/engines/default/engine/Zone.lua b/game/engines/default/engine/Zone.lua index 7d9141c34bca1b8b0eea6110148b828c4a2b3160..f2ff872bf200bd26a007c497bbae16b90e9795f0 100644 --- a/game/engines/default/engine/Zone.lua +++ b/game/engines/default/engine/Zone.lua @@ -1074,6 +1074,7 @@ function _M:newLevel(level_data, lev, old_lev, game) if level_data.post_process_map then level_data.post_process_map(level, self) if level.force_recreate then + forceprint("[Zone:newLevel] post_process_map "..generator.__CLASSNAME.." forced recreation: ",level.force_recreate) level:removed() return self:newLevel(level_data, lev, old_lev, game) end @@ -1125,6 +1126,7 @@ function _M:newLevel(level_data, lev, old_lev, game) if level_data.post_process then level_data.post_process(level, self) if level.force_recreate then + forceprint("[Zone:newLevel] post_process "..generator.__CLASSNAME.." forced recreation: ",level.force_recreate) level:removed() return self:newLevel(level_data, lev, old_lev, game) end @@ -1135,7 +1137,7 @@ function _M:newLevel(level_data, lev, old_lev, game) if not level_data.no_level_connectivity then print("[LEVEL GENERATION] checking entrance to exit A*", ux, uy, "to", dx, dy) if ux and uy and dx and dy and (ux ~= dx or uy ~= dy) and not a:calc(ux, uy, dx, dy) then - forceprint("Level unconnected, no way from entrance", ux, uy, "to exit", dx, dy) + forceprint("[Zone:newLevel] Level unconnected, no way from entrance", ux, uy, "to exit", dx, dy) level:removed() return self:newLevel(level_data, lev, old_lev, game) end @@ -1152,7 +1154,7 @@ function _M:newLevel(level_data, lev, old_lev, game) print("[LEVEL GENERATION] checking A*", spot.x, spot.y, "to", cx, cy) if spot.x and spot.y and cx and cy and (spot.x ~= cx or spot.y ~= cy) and not a:calc(spot.x, spot.y, cx, cy) then - forceprint("Level unconnected, no way from spot", spot.type, spot.subtyp, "at", spot.x, spot.y, "to", cx, cy, spot.check_connectivity) + forceprint("[Zone:newLevel] Level unconnected, no way from spot", spot.type, spot.subtyp, "at", spot.x, spot.y, "to", cx, cy, spot.check_connectivity) level:removed() return self:newLevel(level_data, lev, old_lev, game) end diff --git a/game/engines/default/engine/generator/map/MapScript.lua b/game/engines/default/engine/generator/map/MapScript.lua index 40fb301100009b0ccbdd7d2229261414897ad4c0..b2f6e98355d1353b5df7e7d0128a7e41db6c24ed 100644 --- a/game/engines/default/engine/generator/map/MapScript.lua +++ b/game/engines/default/engine/generator/map/MapScript.lua @@ -19,15 +19,14 @@ require "engine.class" local Map = require "engine.Map" -local BaseGenerator = require "engine.Generator" local RoomsLoader = require "engine.generator.map.RoomsLoader" -require "engine.Generator" +local Generator = require "engine.Generator" --- @classmod engine.generator.map.MapScript -module(..., package.seeall, class.inherit(engine.Generator, RoomsLoader)) +module(..., package.seeall, class.inherit(Generator, RoomsLoader)) function _M:init(zone, map, level, data) - engine.Generator.init(self, zone, map, level) + Generator.init(self, zone, map, level) self.data = data self.grid_list = zone.grid_list self.spots = {} @@ -35,10 +34,19 @@ function _M:init(zone, map, level, data) self.post_gen = {} self.maps_positions = {} self.maps_registers = {} + self.self_tiles = {} RoomsLoader.init(self, data) end +function _M:resolve(c, list, force) + if force then return Generator.resolve(self, c, list, force) end + if self.self_tiles[c] then + return Generator.resolve(self, self.self_tiles[c].grid or '.', list, true) + else + return Generator.resolve(self, c, list, force) + end +end --- Resolve a filename, from /data/ /data-addon/ or subfodler of zonedir function _M:getFile(file, folder) @@ -71,24 +79,27 @@ function _M:loadFile(mapscript, lev, old_lev, args) lev = lev, old_lev = old_lev, loadMapScript = function(name, args) return self:loadFile(name, lev, old_lev, args) end, + merge_order = {'.', '_', 'r', '+', '#', 'O', ';', '=', 'T'}, } for f in fs.iterate("/engine/tilemaps/", function(f) return f:find("%.lua$") end) do local n = f:sub(1, -5) local nf = "engine.tilemaps."..n nenv[n] = require(nf) end + nenv.tm = nenv.Tilemap.new(self.mapsize) setfenv(f, setmetatable(nenv, {__index=_G})) return f() end function _M:custom(lev, old_lev) - for f in fs.iterate("/engine/tilemaps/", function(f) return f:find("%.lua$") end) do - local n = f:sub(1, -5) - local nf = "engine.tilemaps."..n - package.loaded[nf] = nil + if config.settings.cheat then + for f in fs.iterate("/engine/tilemaps/", function(f) return f:find("%.lua$") end) do + local n = f:sub(1, -5) + local nf = "engine.tilemaps."..n + package.loaded[nf] = nil + end end - local ret = nil if self.data.mapscript then local mapscript = self.data.mapscript @@ -127,19 +138,96 @@ function _M:generate(lev, old_lev) if not self.exit_pos then self.exit_pos = data:point(1, 1) end data = data:getResult(true) - for i = 0, self.map.w - 1 do - for j = 0, self.map.h - 1 do - if data[j+1][i+1] ~= "â“" and data[j+1][i+1] ~= "⎕" then - self.map(i, j, Map.TERRAIN, self:resolve(data[j+1][i+1] or '#')) + + -- First do the map itself + for i = 0, self.map.w - 1 do for j = 0, self.map.h - 1 do + if data[j+1][i+1] ~= "â“" and data[j+1][i+1] ~= "⎕" then + self.map(i, j, Map.TERRAIN, self:resolve(data[j+1][i+1] or '#')) + end + + if self.status_all then + local s = table.clone(self.status_all) + if s.lite then self.level.map.lites(i, j, true) s.lite = nil end + if s.remember then self.level.map.remembers(i, j, true) s.remember = nil end + if s.special then self.map.room_map[i][j].special = s.special s.special = nil end + if s.room_map then for k, v in pairs(s.room_map) do self.map.room_map[i][j][k] = v end s.room_map = nil end + if pairs(s) then for k, v in pairs(s) do self.level.map.attrs(i, j, k, v) end end + end + end end + + -- Now add the various entities + for i = 0, self.map.w - 1 do for j = 0, self.map.h - 1 do local def = self.self_tiles[data[j+1][i+1]] if def then + local actor = def.actor + local trap = def.trap + local object = def.object + local trigger = def.trigger + local status = def.status + local define_spot = def.define_spot + + if trigger then + local t, mod + if type(trigger) == "string" then t = self.zone:makeEntityByName(self.level, "trigger", trigger) + elseif type(trigger) == "table" and trigger.random_filter then mod = trigger.entity_mod t = self.zone:makeEntity(self.level, "terrain", trigger.random_filter, nil, true) + else t = self.zone:finishEntity(self.level, "terrain", trigger) end + if t then if mod then t = mod(t) end self:roomMapAddEntity(i, j, "trigger", t) end + end + + if object then + local o, mod + if type(object) == "string" then o = self.zone:makeEntityByName(self.level, "object", object) + elseif type(object) == "table" and object.random_filter then mod = object.entity_mod o = self.zone:makeEntity(self.level, "object", object.random_filter, nil, true) + else o = self.zone:finishEntity(self.level, "object", object) + end + if o then if mod then o = mod(o) end self:roomMapAddEntity(i, j, "object", o, elists) end --takes care of uniques + end + + if trap then + local t, mod + if type(trap) == "string" then t = self.zone:makeEntityByName(self.level, "trap", trap) + elseif type(trap) == "table" and trap.random_filter then mod = trap.entity_mod t = self.zone:makeEntity(self.level, "trap", trap.random_filter, nil, true) + else t = self.zone:finishEntity(self.level, "trap", trap) + end + if t then if mod then t = mod(t) end self:roomMapAddEntity(i, j, "trap", t, elists) end end - end + if actor then + local m, mod + if type(actor) == "string" then m = self.zone:makeEntityByName(self.level, "actor", actor) + elseif type(actor) == "table" and actor.random_filter then mod = actor.entity_mod m = self.zone:makeEntity(self.level, "actor", actor.random_filter, nil, true) + else m = self.zone:finishEntity(self.level, "actor", actor) + end + if m then + if mod then m = mod(m) end + self:roomMapAddEntity(i, j, "actor", m, elists) + end + end + + if status then + local s = table.clone(status) + if s.lite then self.level.map.lites(i, j, true) s.lite = nil end + if s.remember then self.level.map.remembers(i, j, true) s.remember = nil end + if s.special then self.map.room_map[i][j].special = s.special s.special = nil end + if s.room_map then for k, v in pairs(s.room_map) do self.map.room_map[i][j][k] = v end s.room_map = nil end + if pairs(s) then for k, v in pairs(s) do self.level.map.attrs(i, j, k, v) end end + end + + if define_spot then + define_spot = table.clone(define_spot, true) + assert(define_spot.type, "defineTile auto spot without type field") + assert(define_spot.subtype, "defineTile auto spot without subtype field") + define_spot.x = i + define_spot.y = j + self.spots[#self.spots+1] = define_spot + end + end end end + + -- Any psot gen stuff for _, post in pairs(self.post_gen) do post(self, lev, old_lev) end - return self.entrance_pos.x - 1, self.entrance_pos.y - 1, self.exit_pos.x - 1, self.exit_pos.y - 1 + return self.entrance_pos.x - 1, self.entrance_pos.y - 1, self.exit_pos.x - 1, self.exit_pos.y - 1, self.spots end function _M:setEntrance(pos) @@ -149,16 +237,44 @@ function _M:setExit(pos) self.exit_pos = pos end -function _M:addSpot(x, y, type, subtype, data) +function _M:setStatusAll(s) + self.status_all = s +end + +function _M:addSpot(x, y, _type, subtype, data) + if type(x) == "table" then x, y, _type, subtype, data = x.x, x.y, y, _type, subtype end data = data or {} -- Tilemap uses 1 based indexes data.x = math.floor(x) - 1 data.y = math.floor(y) - 1 - data.type = type + data.type = _type data.subtype = subtype self.spots[#self.spots+1] = data end +function _M:addZone(p1, p2, type, subtype, additional) + local zone = {x1=p1.x-1, y1=p1.y-1, x2=p2.x-1, y2=p2.y-1, type=type or "static", subtype=subtype or "static"} + table.update(zone, additional or {}) + self.level.custom_zones = self.level.custom_zones or {} + self.level.custom_zones[#self.level.custom_zones+1] = zone +end + +function _M:checkConnectivity(dst, src, type, subtype) + local data = {} + if type(src) == "string" then data.check_connectivity = src + else data.check_connectivity = {x=src.x-1, y=src.y-1} end + self:addSpot(dst, type or "static", subtype or "static", data) +end + +function _M:defineTile(char, grid, obj, actor, trap, status, spot) + self.self_tiles[char] = {grid=grid, object=obj, actor=actor, trap=trap, status=status, define_spot=spot} +end + +function _M:copyTile(dchar, schar, alter) + self.self_tiles[dchar] = table.clone(self.self_tiles[schar], true) + if alter then alter(self.self_tiles[dchar]) end +end + function _M:postGen(fct) self.post_gen[#self.post_gen+1] = fct end @@ -174,7 +290,7 @@ function _M:makeTemporaryMap(map_w, map_h, fct) local new_data = table.clone(self.data, true) -- Fake a generator call to init tmp_map.room_map - local ngen = BaseGenerator.new({}, tmp_map, {}, {}) + local ngen = Generator.new({}, tmp_map, {}, {}) fct(tmp_map, new_data) diff --git a/game/engines/default/engine/tilemaps/Static.lua b/game/engines/default/engine/tilemaps/Static.lua index 1e81f8a0e4ec1fc6103fce6276391b1869265b60..d7d234f838688a7a61de384889131557bda715fb 100644 --- a/game/engines/default/engine/tilemaps/Static.lua +++ b/game/engines/default/engine/tilemaps/Static.lua @@ -27,7 +27,8 @@ module(..., package.seeall, class.inherit(Tilemap)) function _M:init(file) Tilemap.init(self) - self.data = self:tmxLoad(file) + if file:find("%.tmx$") then self.data = self:tmxLoad(file) + else self.data = self:mapLoad(file) end self.data_h = #self.data self.data_w = self.data[1] and #self.data[1] or 0 self.data_size = self:point(self.data_w, self.data_h) diff --git a/game/engines/default/engine/tilemaps/Tilemap.lua b/game/engines/default/engine/tilemaps/Tilemap.lua index 6741f4f634cc2e02c31912769f7eca28925102c6..b3df902d6bf295dbf2f885eb71b3c5a936066b1b 100644 --- a/game/engines/default/engine/tilemaps/Tilemap.lua +++ b/game/engines/default/engine/tilemaps/Tilemap.lua @@ -242,6 +242,29 @@ function _M:scale(sx, sy) return self end +--- Used internally to load a tilemap from a lua map file +function _M:mapLoad(file) + local f, err = loadfile(file) + if not f then error(err) end + setfenv(f, {}) + local ok, raw = pcall(f) + if not ok then error(raw) end + + raw = raw:split('\n') + local h = #raw + local w = #raw[1] + + local data = {} + for y = 1, h do + data[y] = {} + local row = raw[y] + for x = 1, w do + data[y][x] = row:sub(x, x) or ' ' + end + end + return data, w, h +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() @@ -511,6 +534,20 @@ function _M:eliminateByFloodfill(walls) end end +function _M:getBorderGroup(group) + local border = {list={}} + for _, d in ipairs(group.list) do + for i = -1, 1 do for j = -1, 1 do if (i ~= 0 or j ~= 0) and self.data[d.y+j] and self.data[d.y+j][d.x+i] then + if not self:isInGroup(group, d.x+i, d.y+j) then + if not self:isInGroup(border, d.x+i, d.y+j, true) then + border.list[#border.list+1] = self:point{x=d.x+i, y=d.y+j} + end + end + end end end + end + return border +end + function _M:fillGroup(group, char) -- print("[Tilemap] Filling group of", #group.list, "with", char) for j = 1, #group.list do @@ -519,17 +556,102 @@ function _M:fillGroup(group, char) 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 +function _M:computeGroupReverse(group) + 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 + +function _M:isInGroup(group, x, y, force) + if not group.reverse or force then + self:computeGroupReverse(group) end return group.reverse[x] and group.reverse[x][y] end + +function _M:pickGroupSpot(group, mode, what) + self:computeGroupReverse(group) + + local function check(x, y) + return group.reverse[x] and group.reverse[x][y] + end + + local list = table.clone(group.list) + while #list > 0 do + local jn = rng.tableRemove(list) + if mode == "any" or not mode then + return self:point{x=jn.x, y=jn.y} + elseif mode == "inside-wall" then + local g8 = check(jn.x+0, jn.y-1) + local g2 = check(jn.x+0, jn.y+1) + local g4 = check(jn.x-1, jn.y+0) + local g6 = check(jn.x+1, jn.y+0) + local g7 = check(jn.x-1, jn.y-1) + local g3 = check(jn.x+1, jn.y+1) + local g1 = check(jn.x-1, jn.y+1) + local g9 = check(jn.x+1, jn.y-1) + if not what or what == "any" or what == "straight" then + print("==check on ", jn.x, jn.y, "::", g8,g2,g4,g6) + if g8 and g2 and not g4 and not g6 then + return self:point{x=jn.x, y=jn.y} + elseif g4 and g6 and not g2 and not g8 then + return self:point{x=jn.x, y=jn.y} + end + end + if what == "any" or what == "diagonal" then + if not g8 and not g2 and not g4 and not g6 then + return self:point{x=jn.x, y=jn.y} + elseif not g4 and not g6 and not g2 and not g8 then + return self:point{x=jn.x, y=jn.y} + end + end + elseif mode == "corder" then + local g8 = check(jn.x+0, jn.y-1) + local g2 = check(jn.x+0, jn.y+1) + local g4 = check(jn.x-1, jn.y+0) + local g6 = check(jn.x+1, jn.y+0) + local g7 = check(jn.x-1, jn.y-1) + local g3 = check(jn.x+1, jn.y+1) + local g1 = check(jn.x-1, jn.y+1) + local g9 = check(jn.x+1, jn.y-1) + if not what or what == "any" or what == "straight" then + if not g8 and g2 and not g4 and g6 and not g7 and not g3 and not g1 and not g9 then + return self:point{x=jn.x, y=jn.y} + elseif g4 and not g6 and g2 and not g8 and not g7 and not g3 and not g1 and not g9 then + return self:point{x=jn.x, y=jn.y} + elseif g4 and not g6 and not g2 and g8 and not g7 and not g3 and not g1 and not g9 then + return self:point{x=jn.x, y=jn.y} + elseif not g4 and g6 and not g2 and g8 and not g7 and not g3 and not g1 and not g9 then + return self:point{x=jn.x, y=jn.y} + end + end + if what == "any" or what == "diagonal" then + if not g4 and not g6 and not g2 and not g8 and g7 and not g3 and g1 and not g7 then + return self:point{x=jn.x, y=jn.y} + elseif not g4 and not g6 and not g2 and not g8 and g7 and not g3 and not g1 and g9 then + return self:point{x=jn.x, y=jn.y} + elseif not g4 and not g6 and not g2 and not g8 and not g7 and g3 and not g1 and g9 then + return self:point{x=jn.x, y=jn.y} + elseif not g4 and not g6 and not g2 and not g8 and not g7 and g3 and g1 and not g9 then + return self:point{x=jn.x, y=jn.y} + end + end + elseif mode == "has-neighbours" then + local nb = 0 + for i = -1, 1 do for j = -1, 1 do if (i ~= 0 or j ~= 0) and check(jn.x+i, jn.y+j) then + nb = nb + 1 + end end end + if nb == what then + return self:point{x=jn.x, y=jn.y} + end + end + end + return nil +end + --[=[ --- Find the biggest rectangle that can fit fully in the given group function _M:groupInnerRectangle(group) diff --git a/game/modules/tome/class/Game.lua b/game/modules/tome/class/Game.lua index 23faf39c2bb71e7ccab7712d7cba9de45ab3fda9..052a86852b3cb16e54191553e29a73246c1f4c46 100644 --- a/game/modules/tome/class/Game.lua +++ b/game/modules/tome/class/Game.lua @@ -2013,7 +2013,9 @@ function _M:setupCommands() print("===============") end end, [{"_g","ctrl"}] = function() if config.settings.cheat then - local f, err = loadfile("/data-cults/general/events/tentacle-tree.lua") + self:changeLevel(game.level.level + 1) +do return end + local f, err = loadfile("/data/general/events/rat-lich.lua") print(f, err) setfenv(f, setmetatable({level=self.level, zone=self.zone}, {__index=_G})) print(pcall(f)) @@ -2028,9 +2030,6 @@ do return end else self:changeLevel(game.level.level + 1) end -do return end - package.loaded["mod.dialogs.Donation"] = nil - self:registerDialog(require("mod.dialogs.Donation").new()) 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}) diff --git a/game/modules/tome/class/Player.lua b/game/modules/tome/class/Player.lua index feb8c8cd1be6d2ff059b058e7ca44a9d01a615d0..8476cee5a817904557a88590524343552da8e76d 100644 --- a/game/modules/tome/class/Player.lua +++ b/game/modules/tome/class/Player.lua @@ -215,7 +215,7 @@ function _M:onEnterLevelEnd(zone, level) if level._player_enter_scatter then return end level._player_enter_scatter = true - if level.data.generator and level.data.generator.map and level.data.generator.map.class == "engine.generator.map.Static" and not level.data.static_force_scatter then return end + if level.data.generator and level.data.generator.map and (level.data.generator.map.class == "engine.generator.map.MapScript" or level.data.generator.map.class == "engine.generator.map.Static") and not level.data.static_force_scatter then return end self:project({type="ball", radius=5}, self.x, self.y, function(px, py) local a = level.map(px, py, Map.ACTOR) diff --git a/game/modules/tome/class/generator/map/VaultLevel.lua b/game/modules/tome/class/generator/map/VaultLevel.lua index 68abc4683b65643958b3e3b5f23044c1b73b7e8d..bbfee62d29c0ef5e2896dfa530d1bc449108d9e3 100644 --- a/game/modules/tome/class/generator/map/VaultLevel.lua +++ b/game/modules/tome/class/generator/map/VaultLevel.lua @@ -65,7 +65,8 @@ function _M:generate(lev, old_lev) end end end -- search for ways into the vault, always including the vault connection point - local possible_entrances = {{x=ret.cx, y=ret.cy}} + local possible_entrances_backup = {{x=ret.cx, y=ret.cy}} + local possible_entrances = {} for i = rx, rx + room.w -1 do local y1, y2 = ry, ry + room.h - 1 if self.map:checkEntity(i, y1, Map.TERRAIN, "is_door") then possible_entrances[#possible_entrances+1] = {x=i, y=y1, sx=i, sy=util.bound(y1-entry_path_length, 0, self.map.h-1)} end @@ -74,28 +75,30 @@ function _M:generate(lev, old_lev) for j = ry, ry + room.h - 1 do local x1, x2 = rx, rx + room.w - 1 if self.map:checkEntity(x1, j, Map.TERRAIN, "is_door") then possible_entrances[#possible_entrances+1] = {x=x1, y=j, sx=util.bound(x1-entry_path_length, 0, self.map.w-1), sy=j} end - if self.map:checkEntity(x2, j, Map.TERRAIN, "is_door") then possible_entrances[#possible_entrances+1] = {x=x2, y=j, sx=util.bound(x1+entry_path_length, 0, self.map.w-1), sy=j} end + if self.map:checkEntity(x2, j, Map.TERRAIN, "is_door") then possible_entrances[#possible_entrances+1] = {x=x2, y=j, sx=util.bound(x2+entry_path_length, 0, self.map.w-1), sy=j} end end if #possible_entrances > 0 then local e = rng.table(possible_entrances) sx, sy = e.sx, e.sy ex, ey = e.x, e.y - if not (sx and sy) then -- vault connection point: connect to a (nearest) non-vault grid - print("[VaultLevel] generating level start point from vault connection at", ex, ey) - -- start at center of room and pick a random direction - sx, sy = math.floor(rx+(room.w-1)/2), math.floor(ry+(room.h-1)/2) - local dir, xd, yd = util.getDir(ex, ey, sx+rng.normal(0, 1), sy+rng.normal(0, 1)) - if dir == 5 then dir = rng.table(util.primaryDirs()) end - local rm - local steps = math.max(room.w, room.h) + 2*(room.border or 0) + 1 - repeat - steps = steps - 1 - rm = self.map.room_map[sx][sy] - sx, sy = util.coordAddDir(sx, sy, dir) - if not (rm.room or rm.border) then entry_path_length = entry_path_length - 1 end - until steps <= 0 or entry_path_length <= 0 or not self.map:isBound(sx, sy) - sx, sy = util.bound(sx, 0, self.map.w-1), util.bound(sy, 0, self.map.h-1) - end + elseif #possible_entrances_backup > 0 then + local e = rng.table(possible_entrances_backup) + ex, ey = e.x, e.y + -- vault connection point: connect to a (nearest) non-vault grid + print("[VaultLevel] generating level start point from vault connection at", ex, ey) + -- start at center of room and pick a random direction + sx, sy = math.floor(rx+(room.w-1)/2), math.floor(ry+(room.h-1)/2) + local dir, xd, yd = util.getDir(ex, ey, sx+rng.normal(0, 1), sy+rng.normal(0, 1)) + if dir == 5 then dir = rng.table(util.primaryDirs()) end + local rm + local steps = math.max(room.w, room.h) + 2*(room.border or 0) + 1 + repeat + steps = steps - 1 + rm = self.map.room_map[sx][sy] + sx, sy = util.coordAddDir(sx, sy, dir) + if not (rm.room or rm.border) then entry_path_length = entry_path_length - 1 end + until steps <= 0 or entry_path_length <= 0 or not self.map:isBound(sx, sy) + sx, sy = util.bound(sx, 0, self.map.w-1), util.bound(sy, 0, self.map.h-1) else ex, ey = ret.cx, ret.cy end diff --git a/game/modules/tome/data/general/events/sub-vault.lua b/game/modules/tome/data/general/events/sub-vault.lua index 0c1569c5ce78c21023bbe28e71858b33894bca54..0c76a53ddf53ab3abac11b61fe69ccfb3cf83194 100644 --- a/game/modules/tome/data/general/events/sub-vault.lua +++ b/game/modules/tome/data/general/events/sub-vault.lua @@ -18,7 +18,10 @@ -- darkgod@te4.org -- Find a random spot -local x, y = game.state:findEventGrid(level) +local x, y +local spot = level:pickSpotRemove{type="event-spot", subtype="subvault-place"} +if spot then x, y = spot.x, spot.y +else x, y = game.state:findEventGrid(level) end if not x then return false end local id = "sub-vault"..game.turn.."-"..rng.range(1,9999) print("[EVENT] Placing event", id, "at", x, y) diff --git a/game/modules/tome/data/maps/zones/prides.lua b/game/modules/tome/data/maps/zones/prides.lua index 487976fc4a3c6371a5ce19cedc393f5d35f96325..fb816fa9bbedcfc5bb9d337257777e4981b6929b 100644 --- a/game/modules/tome/data/maps/zones/prides.lua +++ b/game/modules/tome/data/maps/zones/prides.lua @@ -23,7 +23,7 @@ endx = 0 endy = 32 subGenerator{ - x = 8, y = 8, w = 48, h = 48, + x = 7, y = 7, w = 48-2*8+2, h = 48-2*8+2, generator = data.sublevel.class, data = table.clone(data.sublevel), } @@ -55,67 +55,51 @@ defineTile('*', generic_leveler_door, nil, nil, nil, {lever_action=2, lever_acti -- ASCII map section return [[ -################################################################ -################################################################ -########################..g...################################## -########################&#g......############################### -########################..g...##.############################### -################################.############################### -################################.############################### -#######..................................................####### -#######..................................................####### -#######..................................................####### -#######..................................................####### -#######..................................................####### -#######..................................................####### -#######..................................................####### -#######..................................................####### -#######..................................................####### -#######..................................................####### -#######..................................................####### -#######..................................................####### -#######..................................................####### -#######..................................................####### -#######..................................................####### -#######..................................................####### -#######..................................................####### -#######..................................................####### -#######..................................................####### -###;;;#..................................................#ooo### -##;;;##..................................................##...## -##;;;##..................................................##...## -;;;;;##..................................................##..... -;;;;;#....................................................#..... -; ;;;#....................................................#..... -> ;;;*....................................................+O...< -; ;;;#....................................................#..... -;;;;;#....................................................#..... -;;;;;##..................................................##..... -##;;;##..................................................##...## -##;;;##..................................................##...## -###;;;#..................................................#ooo### -#######..................................................####### -#######..................................................####### -#######..................................................####### -#######..................................................####### -#######..................................................####### -#######..................................................####### -#######..................................................####### -#######..................................................####### -#######..................................................####### -#######..................................................####### -#######..................................................####### -#######..................................................####### -#######..................................................####### -#######..................................................####### -#######..................................................####### -#######..................................................####### -#######..................................................####### -#######..................................................####### -################################.############################### -################################.############################### -########################..g...##.############################### -########################&#g......############################### -########################..g...################################## -################################################################ -################################################################]] \ No newline at end of file +################################################ +################################################ +################..g...########################## +################&#g......####################### +################..g...##.####################### +########################.####################### +########################.####################### +#######..................................####### +#######..................................####### +#######..................................####### +#######..................................####### +#######..................................####### +#######..................................####### +#######..................................####### +#######..................................####### +#######..................................####### +#######..................................####### +#######..................................####### +###;;;#..................................#ooo### +##;;;##..................................##...## +##;;;##..................................##...## +;;;;;##..................................##..... +;;;;;#....................................#..... +; ;;;#....................................#..... +> ;;;*....................................+O...< +; ;;;#....................................#..... +;;;;;#....................................#..... +;;;;;##..................................##..... +##;;;##..................................##...## +##;;;##..................................##...## +###;;;#..................................#ooo### +#######..................................####### +#######..................................####### +#######..................................####### +#######..................................####### +#######..................................####### +#######..................................####### +#######..................................####### +#######..................................####### +#######..................................####### +#######..................................####### +########################.####################### +########################.####################### +################..g...##.####################### +################&#g......####################### +################..g...########################## +################################################ +################################################]] \ No newline at end of file diff --git a/game/modules/tome/data/mapscripts/maps/pride-outer.lua b/game/modules/tome/data/mapscripts/maps/pride-outer.lua new file mode 100644 index 0000000000000000000000000000000000000000..d287989b296fa9558558052511960c27bd02cadb --- /dev/null +++ b/game/modules/tome/data/mapscripts/maps/pride-outer.lua @@ -0,0 +1,49 @@ +return [[ +################################################ +################################################ +################..o...########################## +################&#o......####################### +################..o...##.####################### +########################.####################### +########################.####################### +####### ####### +####### ####### +####### ####### +####### ####### +####### ####### +####### ####### +####### ####### +####### ####### +####### ####### +####### ####### +####### ####### +###---# #ooo### +##---## ##...## +##---## ##...## +-----## ##..... +-----#; ;#..... +-X---#; ;#..... +>X---*; ;+O...< +-X---#; ;#..... +-----#; ;#..... +-----## ##..... +##---## ##...## +##---## ##...## +###---# #ooo### +####### ####### +####### ####### +####### ####### +####### ####### +####### ####### +####### ####### +####### ####### +####### ####### +####### ####### +####### ####### +########################.####################### +########################.####################### +################..o...##.####################### +################&#o......####################### +################..o...########################## +################################################ +################################################]] \ No newline at end of file diff --git a/game/modules/tome/data/zones/rak-shor-pride/zone.lua b/game/modules/tome/data/zones/rak-shor-pride/zone.lua index e626ad70163f694b8a6f70d698bf09ba5844400f..3dcb8da51a2ab896a06c7934efd11f328872f14a 100644 --- a/game/modules/tome/data/zones/rak-shor-pride/zone.lua +++ b/game/modules/tome/data/zones/rak-shor-pride/zone.lua @@ -27,6 +27,7 @@ return { width = 50, height = 50, persistent = "zone", -- all_remembered = true, + no_level_connectivity = true, all_lited = true, day_night = true, ambient_music = {"March.ogg", "weather/desert_base.ogg"}, diff --git a/game/modules/tome/data/zones/vor-pride/events.lua b/game/modules/tome/data/zones/vor-pride/events.lua index a7020c3f7e073ca8650b8566b835fb66e7226674..1c3247fa3e12198f0b2848f387cb70c7a60f23b0 100644 --- a/game/modules/tome/data/zones/vor-pride/events.lua +++ b/game/modules/tome/data/zones/vor-pride/events.lua @@ -22,4 +22,5 @@ return { one_per_level=true, {name="fell-aura", minor=true, percent=100, max_repeat=3}, {name="spellblaze-scar", minor=true, percent=50}, {name="glowing-chest", minor=true, percent=30}, + {name="sub-vault", minor=true, percent=100, max_repeat=3}, } diff --git a/game/modules/tome/data/zones/vor-pride/grids.lua b/game/modules/tome/data/zones/vor-pride/grids.lua index b27e55fcdf9db827dbb4c2a134c2bc8eaa8f951f..ed4ce8fbb6df62f5fc9361e3ebfd035130bb5b3d 100644 --- a/game/modules/tome/data/zones/vor-pride/grids.lua +++ b/game/modules/tome/data/zones/vor-pride/grids.lua @@ -17,6 +17,11 @@ -- Nicolas Casalini "DarkGod" -- darkgod@te4.org -load("/data/general/grids/basic.lua") +load("/data/general/grids/basic.lua", function(e) + if e.define_as ~= "FLOOR" and e.image == "terrain/marble_floor.png" then + e.image = "terrain/grass_burnt1.png" + end +end) load("/data/general/grids/forest.lua") load("/data/general/grids/water.lua") +load("/data/general/grids/burntland.lua") diff --git a/game/modules/tome/data/zones/vor-pride/mapscripts/main.lua b/game/modules/tome/data/zones/vor-pride/mapscripts/main.lua new file mode 100644 index 0000000000000000000000000000000000000000..f07948668b750b1e3cfb419dfb602cbde0044fd8 --- /dev/null +++ b/game/modules/tome/data/zones/vor-pride/mapscripts/main.lua @@ -0,0 +1,57 @@ +-- ToME - Tales of Maj'Eyal +-- Copyright (C) 2009 - 2019 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 + +self:defineTile("-", "BURNT_GROUND", nil, nil, nil, {no_teleport=true}) +self:defineTile("o", "FLOOR", nil, {entity_mod=function(e) e.make_escort = nil return e end, random_filter={type='humanoid', subtype='orc', special=function(e) return e.pride == mapdata.pride end}}) +if level.level == zone.max_level then self:defineTile(">", 'BURNT_GROUND') else self:defineTile(">", "FLAT_DOWN4", nil, nil, nil, {no_teleport=true}) end +self:defineTile("O", "FLOOR", nil, {random_filter={type='humanoid', subtype='orc', special=function(e) return e.pride == mapdata.pride end, random_boss={nb_classes=1, loot_quality="store", loot_quantity=3, ai_move="move_complex", rank=4,}}}) +self:defineTile("X", "BURNT_GROUND", nil, {entity_mod=function(e) e.make_escort = nil return e end, random_filter={type='humanoid', subtype='orc', special=function(e) return e.pride == mapdata.pride end, random_boss={nb_classes=1, loot_quality="store", loot_quantity=1, no_loot_randart=true, ai_move="move_complex", rank=3}}}, nil, {no_teleport=true}) +self:defineTile('&', "GENERIC_LEVER", nil, nil, nil, {lever=1, lever_kind="pride-doors", lever_spot={type="lever", subtype="door", check_connectivity="entrance"}}, {type="lever", subtype="lever", check_connectivity="entrance"}) +self:defineTile('*', "GENERIC_LEVER_DOOR", nil, nil, nil, {lever_action=2, lever_action_value=0, lever_action_kind="pride-doors"}, {type="lever", subtype="door", check_connectivity="entrance"}) + +local wfc = WaveFunctionCollapse.new{ + mode="overlapping", + sample=self:getFile("!buildings.tmx", "samples"), + size={34, 34}, + n=3, symmetry=8, periodic_out=false, periodic_in=false, has_foundation=false +} +local outer = Static.new(self:getFile("pride-outer.lua", "mapscripts/maps")) +tm:merge(1, 1, outer, merge_order) +tm:merge(8, 8, wfc, merge_order) + +-- Find rooms +local rooms = tm:findGroupsOf{'r'} +tm:applyOnGroups(rooms, function(room, idx) + tm:fillGroup(room, '.') + local border = tm:getBorderGroup(room) + local door = tm:pickGroupSpot(border, "inside-wall") + if door then tm:put(door, '+') end + + local event = tm:pickGroupSpot(room, "any") + if event then self:addSpot(event, "event-spot", "subvault-place") end +end) + +-- Complete the map by putting wall in all the remaining blank spaces +tm:fillAll() + +-- if tm:eliminateByFloodfill{'#', 'T'} < 400 then return self:regenerate() end + +tm:printResult() + +return tm diff --git a/game/modules/tome/data/zones/vor-pride/samples/buildings.tmx b/game/modules/tome/data/zones/vor-pride/samples/buildings.tmx new file mode 100644 index 0000000000000000000000000000000000000000..792fc5754b56ff8783efdbd02afaaa1e2b5bb3ab --- /dev/null +++ b/game/modules/tome/data/zones/vor-pride/samples/buildings.tmx @@ -0,0 +1,59 @@ +<?xml version="1.0" encoding="UTF-8"?> +<map version="1.0" tiledversion="1.0.3" orientation="orthogonal" renderorder="right-down" width="22" height="22" tilewidth="32" tileheight="32" nextobjectid="31"> + <properties> + <property name="status_all" value="{no_teleport=true}"/> + </properties> + <tileset firstgid="1" name="dg_dungeon32" tilewidth="32" tileheight="32" tilecount="90" columns="9"> + <image source="../../../../../../../tiled-maps/gfx/dg_dungeon32.gif" width="288" height="320"/> + <tile id="0"> + <properties> + <property name="id" value="#"/> + </properties> + </tile> + <tile id="3"> + <properties> + <property name="id" value="+"/> + </properties> + </tile> + <tile id="51"> + <properties> + <property name="id" value="."/> + </properties> + </tile> + <tile id="54"> + <properties> + <property name="id" value="_"/> + </properties> + </tile> + <tile id="58"> + <properties> + <property name="id" value="r"/> + </properties> + </tile> + </tileset> + <tileset firstgid="91" name="tome-terrains" tilewidth="32" tileheight="32" tilecount="144" columns="8"> + <image source="../../../../../../../tiled-maps/gfx/tome-terrains.png" width="256" height="576"/> + <tile id="24"> + <objectgroup draworder="index"> + <properties> + <property name="id" value=";"/> + </properties> + </objectgroup> + </tile> + <tile id="100"> + <objectgroup draworder="index"> + <properties> + <property name="id" value="T"/> + </properties> + </objectgroup> + </tile> + </tileset> + <tileset firstgid="235" name="numbers" tilewidth="32" tileheight="32" tilecount="36" columns="6"> + <image source="../../../../../../../tiled-maps/gfx/numbers.png" width="192" height="192"/> + </tileset> + <layer name="Terrain" width="22" height="22"> + <data encoding="base64" compression="zlib"> + eJytlFEOwCAIQ9m5PJYH5GrLEj9I0w4wfPChka7WN7aZ7VMP1B6qT2uditoezrjoU3pMd9ov5oD54F3c/r0rv7i/fnQr+TrZ7+QU+xkPmM8UL1E346HLy4TH7B0UJ8qrYqn7H6kz7M0ZT9jDcs78Kt3O/VhO1cwn2Kt8l91nkv0FuirDKj9KVzFSnRmZp1vdSka3Oag1mztTb9qdOy+yNJzT + </data> + </layer> +</map> diff --git a/game/modules/tome/data/zones/vor-pride/zone.lua b/game/modules/tome/data/zones/vor-pride/zone.lua index 7e84cc1cf1b3cdfa4a82f18eabeccbd50c5ca666..d4a4f4a0bb632ec3d4f0af1a57b01fa112d07c5b 100644 --- a/game/modules/tome/data/zones/vor-pride/zone.lua +++ b/game/modules/tome/data/zones/vor-pride/zone.lua @@ -24,44 +24,46 @@ return { max_level = 3, 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 = 64, height = 64, + width = 48, height = 48, persistent = "zone", --- all_remembered = true, + -- all_remembered = true, all_lited = true, day_night = true, ambient_music = "Breaking the siege.ogg", min_material_level = 4, max_material_level = 5, - no_level_connectivity = true, effects = {"EFF_ZONE_AURA_VOR"}, generator = { map = { - class = "engine.generator.map.Static", - map = "zones/prides", - up = "FLAT_UP6", - down = "FLAT_DOWN4", - sublevel = { - class = "engine.generator.map.Town", - pride = "vor", - building_chance = 70, - max_building_w = 8, max_building_h = 8, - edge_entrances = {6,4}, - floor = "FLOOR", - external_floor = "FLOOR", - wall = "WALL", - door = "DOOR", - up = "FLOOR", - down = "FLOOR", + class = "engine.generator.map.MapScript", + ['<'] = "FLAT_UP6", ['>'] = "FLAT_DOWN4", + ['.'] = "FLOOR", ['+'] = "DOOR", ['#'] = "WALL", + ['_'] = "FLOOR", ['O'] = "WALL", + [';'] = "BURNT_GROUND", ['T'] = "BURNT_TREE", + ['='] = "DEEP_WATER", + door = "DOOR", + mapscript = "!main", + pride = "vor", - nb_rooms = {1,1,2,3}, - rooms = {"lesser_vault", "greater_vault"}, - lesser_vaults_list = {"orc-armoury", "double-t", "circle", "hostel", "orc-necromancer", "horror-chamber"}, - lite_room_chance = 100, - }, + -- class = "engine.generator.map.Town", + -- building_chance = 70, + -- max_building_w = 6, max_building_h = 6, + -- edge_entrances = {6,4}, + -- floor = "FLOOR", + -- external_floor = "FLOOR", + -- wall = "WALL", + -- door = "DOOR", + -- up = "FLOOR", + -- down = "FLOOR", + + -- nb_rooms = {1,1,2,3}, + -- rooms = {"lesser_vault", "greater_vault"}, + -- lesser_vaults_list = {"orc-armoury", "double-t", "circle", "hostel", "orc-necromancer", "horror-chamber"}, + -- lite_room_chance = 100, }, actor = { class = "mod.class.generator.actor.Random", - nb_npc = {50, 60}, + nb_npc = {35, 40}, guardian = "VOR", }, object = { @@ -79,12 +81,7 @@ return { { [1] = { generator = { map = { - up = "FLAT_UP_WILDERNESS", - }, }, - }, - [3] = { - generator = { map = { - down = "FLAT_UP_WILDERNESS", + ['<'] = "FLAT_UP_WILDERNESS", }, }, }, },