diff --git a/game/engine/Generator.lua b/game/engine/Generator.lua index 5a79fcd6319c01a59da0f5831b497d49747be87d..f8fe51028ecbc778e0e0b6cc498bc3ce82e834ae 100644 --- a/game/engine/Generator.lua +++ b/game/engine/Generator.lua @@ -20,10 +20,11 @@ require "engine.class" module(..., package.seeall, class.make) -function _M:init(zone, map, level) +function _M:init(zone, map, level, spots) self.zone = zone self.map = map self.level = level + self.spots = spots -- Setup the map's room-map if not map.room_map then diff --git a/game/engine/Zone.lua b/game/engine/Zone.lua index 6c0ac30947c6e921637a7a34fc669132f86ad566..779433551295036dc8874f138653b8ac5ee78dbd 100644 --- a/game/engine/Zone.lua +++ b/game/engine/Zone.lua @@ -20,6 +20,7 @@ require "engine.class" local Savefile = require "engine.Savefile" local Map = require "engine.Map" +local Astar = require "engine.Astar" --- Defines a zone: a set of levels, with depth, nps, objects, level generator, ... module(..., package.seeall, class.make) @@ -450,7 +451,7 @@ function _M:newLevel(level_data, lev, old_lev, game) level.ups = {{x=ux, y=uy}} level.downs = {{x=dx, y=dy}} - level.spots = spots + level.spots = spots or {} -- Generate objects if level_data.generator.object then @@ -482,5 +483,29 @@ function _M:newLevel(level_data, lev, old_lev, game) -- Delete the room_map, now useless map.room_map = nil + -- Check for connectivity from entrance to exit + local a = Astar.new(map, game:getPlayer()) + if ux and uy and dx and dy and not a:calc(ux, uy, dx, dy) then + print("Level unconnected, no way from entrance to exit") + level:removed() + return self:newLevel(level_data, lev, old_lev, game) + end + for i = 1, #spots do + local spot = spots[i] + if spot.check_connectivity then + local cx, cy + if type(spot.check_connectivity) == "string" and spot.check_connectivity == "entrance" then cx, cy = ux, uy + elseif type(spot.check_connectivity) == "string" and spot.check_connectivity == "exit" then cx, cy = dx, dy + else cx, cy = spot.check_connectivity.x, spot.check_connectivity.y + end + + if not a:calc(spot.x, spot.y, cx, cy) then + print("Level unconnected, no way from", spot.x, spot.y, "to", cx, cy) + level:removed() + return self:newLevel(level_data, lev, old_lev, game) + end + end + end + return level end diff --git a/game/engine/generator/actor/Random.lua b/game/engine/generator/actor/Random.lua index 6caa279d259ec829a6315936f15d003cd05ca9e3..b31e1e177c02330f18f227639681b429ae5a2a1f 100644 --- a/game/engine/generator/actor/Random.lua +++ b/game/engine/generator/actor/Random.lua @@ -22,8 +22,8 @@ local Map = require "engine.Map" require "engine.Generator" module(..., package.seeall, class.inherit(engine.Generator)) -function _M:init(zone, map, level) - engine.Generator.init(self, zone, map, level) +function _M:init(zone, map, level, spots) + engine.Generator.init(self, zone, map, level, spots) local data = level.data.generator.actor if data.adjust_level then @@ -52,6 +52,7 @@ function _M:generate() tries = tries + 1 end if tries < 100 then + self.spots[#self.spots+1] = {x=x, y=y, gardian=true, check_connectivity="entrance"} self.zone:addEntity(self.level, m, "actor", x, y) print("Guardian allocated: ", self.guardian, m.uid, m.name) end diff --git a/game/engine/generator/map/Forest.lua b/game/engine/generator/map/Forest.lua new file mode 100644 index 0000000000000000000000000000000000000000..619bb6df75b207430adf13056fea237354260c96 --- /dev/null +++ b/game/engine/generator/map/Forest.lua @@ -0,0 +1,143 @@ +-- TE4 - T-Engine 4 +-- Copyright (C) 2009, 2010 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 Map = require "engine.Map" +require "engine.Generator" +module(..., package.seeall, class.inherit(engine.Generator)) + +function _M:init(zone, map, level, data) + engine.Generator.init(self, zone, map, level) + self.data = data + self.grid_list = self.zone.grid_list + self.noise = data.noise or "fbm_perlin" + self.zoom = data.zoom or 5 + self.max_percent = data.max_percent or 80 + self.sqrt_percent = data.sqrt_percent or 30 + self.hurst = data.hurst or nil + self.lacunarity = data.lacunarity or nil + self.octave = data.octave or 4 +end + +function _M:resolve(c) + local res = self.data[c] + if type(res) == "function" then + return res() + elseif type(res) == "table" then + return res[rng.range(1, #res)] + else + return res + end +end + +function _M:generate(lev, old_lev) + for i = 0, self.map.w - 1 do for j = 0, self.map.h - 1 do + self.map(i, j, Map.TERRAIN, self.grid_list[self.floor]) + end end + + -- make the noise + local noise = core.noise.new(2, self.hurst, self.lacunarity) + for i = 1, self.map.w do + for j = 1, self.map.h do + local v = math.floor((noise[self.noise](noise, self.zoom * i / self.map.w, self.zoom * j / self.map.h, self.octave) / 2 + 0.5) * self.max_percent) + if (v >= self.sqrt_percent and rng.percent(v)) or (v < self.sqrt_percent and rng.percent(math.sqrt(v))) then + self.map(i-1, j-1, Map.TERRAIN, self.grid_list[self:resolve("wall")]) + else + self.map(i-1, j-1, Map.TERRAIN, self.grid_list[self:resolve("floor")]) + end + end + end + + local ux, uy, dx, dy, spots + if self.data.edge_entrances then + ux, uy, dx, dy, spots = self:makeStairsSides(lev, old_lev, self.data.edge_entrances, spots) + else + ux, uy, dx, dy, spots = self:makeStairsInside(lev, old_lev, spots) + end + + return ux, uy, dx, dy, spots +end + +--- Create the stairs inside the level +function _M:makeStairsInside(lev, old_lev, spots) + -- Put down stairs + local dx, dy + if lev < self.zone.max_level or self.data.force_last_stair then + while true do + dx, dy = rng.range(1, self.map.w - 1), rng.range(1, self.map.h - 1) + if not self.map:checkEntity(dx, dy, Map.TERRAIN, "block_move") and not self.map.room_map[dx][dy].special then + self.map(dx, dy, Map.TERRAIN, self.grid_list[self:resolve("down")]) + self.map.room_map[dx][dy].special = "exit" + break + end + end + end + + -- Put up stairs + local ux, uy + while true do + ux, uy = rng.range(1, self.map.w - 1), rng.range(1, self.map.h - 1) + if not self.map:checkEntity(ux, uy, Map.TERRAIN, "block_move") and not self.map.room_map[ux][uy].special then + self.map(ux, uy, Map.TERRAIN, self.grid_list[self:resolve("up")]) + self.map.room_map[ux][uy].special = "exit" + break + end + end + + return ux, uy, dx, dy, spots +end + +--- Create the stairs on the sides +function _M:makeStairsSides(lev, old_lev, sides, spots) + -- Put down stairs + local dx, dy + if lev < self.zone.max_level or self.data.force_last_stair then + while true do + if sides[2] == 4 then dx, dy = 0, rng.range(0, self.map.h - 1) + elseif sides[2] == 6 then dx, dy = self.map.w - 1, rng.range(0, self.map.h - 1) + elseif sides[2] == 8 then dx, dy = rng.range(0, self.map.w - 1), 0 + elseif sides[2] == 2 then dx, dy = rng.range(0, self.map.w - 1), self.map.h - 1 + end + + if not self.map.room_map[dx][dy].special then + self.map(dx, dy, Map.TERRAIN, self.grid_list[self:resolve("down")]) + self.map.room_map[dx][dy].special = "exit" + break + end + end + end + + -- Put up stairs + local ux, uy + while true do + if sides[1] == 4 then ux, uy = 0, rng.range(0, self.map.h - 1) + elseif sides[1] == 6 then ux, uy = self.map.w - 1, rng.range(0, self.map.h - 1) + elseif sides[1] == 8 then ux, uy = rng.range(0, self.map.w - 1), 0 + elseif sides[1] == 2 then ux, uy = rng.range(0, self.map.w - 1), self.map.h - 1 + end + + if not self.map.room_map[ux][uy].special then + self.map(ux, uy, Map.TERRAIN, self.grid_list[self:resolve("up")]) + self.map.room_map[ux][uy].special = "exit" + break + end + end + + return ux, uy, dx, dy, spots +end diff --git a/game/modules/tome/class/Game.lua b/game/modules/tome/class/Game.lua index 8c1601c3622c75b51d85cf844582d99ab8578b6e..89417a78aab6cc186ad02ed9c0f1d5e460524c75 100644 --- a/game/modules/tome/class/Game.lua +++ b/game/modules/tome/class/Game.lua @@ -503,8 +503,7 @@ function _M:setupCommands() self.key:addCommands{ [{"_d","ctrl"}] = function() - if config.settings.tome.cheat then self:changeLevel(3, "moria") end --- if config.settings.tome.cheat then self:changeLevel(1, "wilderness-arda-fareast") end + if config.settings.tome.cheat then self:changeLevel(1, "test") end end, } self.key:addBinds diff --git a/game/modules/tome/data/zones/test/grids.lua b/game/modules/tome/data/zones/test/grids.lua new file mode 100644 index 0000000000000000000000000000000000000000..649dff9ed66f0d608b12a4bb999101dcdd42386a --- /dev/null +++ b/game/modules/tome/data/zones/test/grids.lua @@ -0,0 +1,21 @@ +-- ToME - Tales of Middle-Earth +-- Copyright (C) 2009, 2010 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 + +load("/data/general/grids/basic.lua") +load("/data/general/grids/forest.lua") diff --git a/game/modules/tome/data/zones/test/npcs.lua b/game/modules/tome/data/zones/test/npcs.lua new file mode 100644 index 0000000000000000000000000000000000000000..66f585828f4fa1d3c0f48ffcdacc530ef0ff4f46 --- /dev/null +++ b/game/modules/tome/data/zones/test/npcs.lua @@ -0,0 +1,81 @@ +-- ToME - Tales of Middle-Earth +-- Copyright (C) 2009, 2010 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 + +load("/data/general/npcs/orc.lua") +load("/data/general/npcs/troll.lua") + +local Talents = require("engine.interface.ActorTalents") + +-- The boss of Moria, no "rarity" field means it will not be randomly generated +newEntity{ define_as = "GOLBUG", + type = "humanoid", subtype = "orc", unique = true, + name = "Golbug the Destroyer", + display = "o", color=colors.VIOLET, + desc = [[A huge and muscular orc of unknown breed. He looks both menacing and cunning...]], + level_range = {28, 45}, exp_worth = 2, + max_life = 350, life_rating = 16, fixed_rating = true, + max_stamina = 245, + rank = 4, + size_category = 3, + stats = { str=22, dex=19, cun=34, mag=10, con=16 }, + + body = { INVEN = 10, MAINHAND=1, OFFHAND=1, BODY=1, NECK=1, HEAD=1, }, + equipment = resolvers.equip{ + {type="weapon", subtype="mace", ego_chance=100, autoreq=true}, + {type="armor", subtype="shield", ego_chance=100, autoreq=true}, + {type="armor", subtype="head", autoreq=true}, + {type="armor", subtype="massive", ego_chance=50, autoreq=true}, + }, + resolvers.drops{chance=100, nb=5, {ego_chance=100} }, + resolvers.drops{chance=100, nb=1, {type="jewelry", subtype="orb", defined="ORB_MANY_WAYS"} }, + + stun_immune = 1, + see_invisible = 5, + + resolvers.talents{ + [Talents.T_HEAVY_ARMOUR_TRAINING]=1, + [Talents.T_MASSIVE_ARMOUR_TRAINING]=3, + [Talents.T_WEAPON_COMBAT]=6, + [Talents.T_MACE_MASTERY]=6, + [Talents.T_SHIELD_PUMMEL]=4, + [Talents.T_RUSH]=4, + [Talents.T_RIPOSTE]=4, + [Talents.T_BLINDING_SPEED]=4, + [Talents.T_OVERPOWER]=3, + [Talents.T_ASSAULT]=3, + [Talents.T_SHIELD_WALL]=3, + [Talents.T_SHIELD_EXPERTISE]=2, + + [Talents.T_BELLOWING_ROAR]=3, + [Talents.T_WING_BUFFET]=2, + [Talents.T_FIRE_BREATH]=4, + + [Talents.T_ICE_CLAW]=3, + [Talents.T_ICY_SKIN]=4, + [Talents.T_ICE_BREATH]=4, + }, + + autolevel = "warrior", + ai = "dumb_talented_simple", ai_state = { talent_in=2, }, + + on_die = function(self, who) +-- who:resolveSource():grantQuest("tol-falas") +-- who:resolveSource():setQuestStatus("tol-falas", engine.Quest.DONE) + end, +} diff --git a/game/modules/tome/data/zones/test/objects.lua b/game/modules/tome/data/zones/test/objects.lua new file mode 100644 index 0000000000000000000000000000000000000000..b5facd63f5b34e95c1aab209db0f312a8aef3e91 --- /dev/null +++ b/game/modules/tome/data/zones/test/objects.lua @@ -0,0 +1,20 @@ +-- ToME - Tales of Middle-Earth +-- Copyright (C) 2009, 2010 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 + +load("/data/general/objects/objects.lua") diff --git a/game/modules/tome/data/zones/test/traps.lua b/game/modules/tome/data/zones/test/traps.lua new file mode 100644 index 0000000000000000000000000000000000000000..e5201481de6fe45b57f3ad3c6dab9523fa929074 --- /dev/null +++ b/game/modules/tome/data/zones/test/traps.lua @@ -0,0 +1,21 @@ +-- ToME - Tales of Middle-Earth +-- Copyright (C) 2009, 2010 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 + +load("/data/general/traps/elemental.lua") +load("/data/general/traps/alarm.lua") diff --git a/game/modules/tome/data/zones/test/zone.lua b/game/modules/tome/data/zones/test/zone.lua new file mode 100644 index 0000000000000000000000000000000000000000..4d8f0862585fe274e6d89daa8a5103f24c071913 --- /dev/null +++ b/game/modules/tome/data/zones/test/zone.lua @@ -0,0 +1,63 @@ +-- ToME - Tales of Middle-Earth +-- Copyright (C) 2009, 2010 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 + +return { + name = "Mines of Moria", + level_range = {20, 30}, + level_scheme = "player", + 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, + all_lited = true, +-- persistant = "zone", + generator = { + map = { + class = "engine.generator.map.Forest", + edge_entrances = {4,6}, + floor = "GRASS", + wall = "TREE", + up = "UP", + down = "DOWN", + }, +--[[ + actor = { + class = "engine.generator.actor.Random", + nb_npc = {40, 50}, + }, + object = { + class = "engine.generator.object.Random", + nb_object = {12, 16}, + }, + trap = { + class = "engine.generator.trap.Random", + nb_trap = {20, 30}, + }, +]] + }, + levels = + { + [1] = { + generator = { map = { + up = "UP_WILDERNESS", + }, }, + }, + }, +} diff --git a/game/modules/tome/data/zones/trollshaws/zone.lua b/game/modules/tome/data/zones/trollshaws/zone.lua index 2fe579ba9dd938218e823a6ea2330f2b7b8f6282..93d440940984a80d38e6413d3db64258f4c4c8a0 100644 --- a/game/modules/tome/data/zones/trollshaws/zone.lua +++ b/game/modules/tome/data/zones/trollshaws/zone.lua @@ -31,15 +31,14 @@ return { ambiant_music = "elven_town.ogg", generator = { map = { - class = "engine.generator.map.Roomer", - nb_rooms = 10, + class = "engine.generator.map.Forest", edge_entrances = {4,6}, - rooms = {"forest_clearing"}, - ['.'] = function() if rng.chance(20) then return "FLOWER" else return "GRASS" end end, - ['#'] = "TREE", + zoom = 5, + noise = "fbm_perlin", + floor = function() if rng.chance(20) then return "FLOWER" else return "GRASS" end end,, + wall = "TREE", up = "UP", down = "DOWN", - door = "GRASS", }, actor = { class = "engine.generator.actor.Random", diff --git a/src/noise.c b/src/noise.c index fbaa719f0ede78f30ffb951ebc8fc32ac540d9d0..ec58dc3865698809fb56ed3c385428fde8c5359b 100644 --- a/src/noise.c +++ b/src/noise.c @@ -73,10 +73,10 @@ static int noise_simplex(lua_State *L) static int noise_fbm_simplex(lua_State *L) { noise_t *n = (noise_t*)auxiliar_checkclass(L, "noise{core}", 1); - float octave = luaL_checknumber(L, 2); float p[4]; int i; - for (i = 0; i < n->ndim; i++) p[i] = luaL_checknumber(L, 3 + i); + for (i = 0; i < n->ndim; i++) p[i] = luaL_checknumber(L, 2 + i); + float octave = luaL_checknumber(L, 2 + i); lua_pushnumber(L, TCOD_noise_fbm_simplex(n->noise, p, octave)); return 1; @@ -85,10 +85,10 @@ static int noise_fbm_simplex(lua_State *L) static int noise_turbulence_simplex(lua_State *L) { noise_t *n = (noise_t*)auxiliar_checkclass(L, "noise{core}", 1); - float octave = luaL_checknumber(L, 2); float p[4]; int i; - for (i = 0; i < n->ndim; i++) p[i] = luaL_checknumber(L, 3 + i); + for (i = 0; i < n->ndim; i++) p[i] = luaL_checknumber(L, 2 + i); + float octave = luaL_checknumber(L, 2 + i); lua_pushnumber(L, TCOD_noise_turbulence_simplex(n->noise, p, octave)); return 1; @@ -108,10 +108,10 @@ static int noise_perlin(lua_State *L) static int noise_fbm_perlin(lua_State *L) { noise_t *n = (noise_t*)auxiliar_checkclass(L, "noise{core}", 1); - float octave = luaL_checknumber(L, 2); float p[4]; int i; - for (i = 0; i < n->ndim; i++) p[i] = luaL_checknumber(L, 3 + i); + for (i = 0; i < n->ndim; i++) p[i] = luaL_checknumber(L, 2 + i); + float octave = luaL_checknumber(L, 2 + i); lua_pushnumber(L, TCOD_noise_fbm_perlin(n->noise, p, octave)); return 1; @@ -120,10 +120,10 @@ static int noise_fbm_perlin(lua_State *L) static int noise_turbulence_perlin(lua_State *L) { noise_t *n = (noise_t*)auxiliar_checkclass(L, "noise{core}", 1); - float octave = luaL_checknumber(L, 2); float p[4]; int i; - for (i = 0; i < n->ndim; i++) p[i] = luaL_checknumber(L, 3 + i); + for (i = 0; i < n->ndim; i++) p[i] = luaL_checknumber(L, 2 + i); + float octave = luaL_checknumber(L, 2 + i); lua_pushnumber(L, TCOD_noise_turbulence_perlin(n->noise, p, octave)); return 1; @@ -143,10 +143,10 @@ static int noise_wavelet(lua_State *L) static int noise_fbm_wavelet(lua_State *L) { noise_t *n = (noise_t*)auxiliar_checkclass(L, "noise{core}", 1); - float octave = luaL_checknumber(L, 2); float p[4]; int i; - for (i = 0; i < n->ndim; i++) p[i] = luaL_checknumber(L, 3 + i); + for (i = 0; i < n->ndim; i++) p[i] = luaL_checknumber(L, 2 + i); + float octave = luaL_checknumber(L, 2 + i); lua_pushnumber(L, TCOD_noise_fbm_wavelet(n->noise, p, octave)); return 1; @@ -155,10 +155,10 @@ static int noise_fbm_wavelet(lua_State *L) static int noise_turbulence_wavelet(lua_State *L) { noise_t *n = (noise_t*)auxiliar_checkclass(L, "noise{core}", 1); - float octave = luaL_checknumber(L, 2); float p[4]; int i; - for (i = 0; i < n->ndim; i++) p[i] = luaL_checknumber(L, 3 + i); + for (i = 0; i < n->ndim; i++) p[i] = luaL_checknumber(L, 2 + i); + float octave = luaL_checknumber(L, 2 + i); lua_pushnumber(L, TCOD_noise_turbulence_wavelet(n->noise, p, octave)); return 1;