/* Minetest Copyright (C) 2010-2013 celeron55, Perttu Ahola This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #include "dungeongen.h" #include "mapgen.h" #include "voxel.h" #include "noise.h" #include "mapblock.h" #include "mapnode.h" #include "map.h" #include "nodedef.h" #include "profiler.h" #include "settings.h" //#define DGEN_USE_TORCHES NoiseParams nparams_dungeon_density(0.9, 0.5, v3f(500.0, 500.0, 500.0), 0, 2, 0.8, 2.0); NoiseParams nparams_dungeon_alt_wall(-0.4, 1.0, v3f(40.0, 40.0, 40.0), 32474, 6, 1.1, 2.0); /////////////////////////////////////////////////////////////////////////////// DungeonGen::DungeonGen(INodeDefManager *ndef, GenerateNotifier *gennotify, DungeonParams *dparams) { assert(ndef); this->ndef = ndef; this->gennotify = gennotify; #ifdef DGEN_USE_TORCHES c_torch = ndef->getId("default:torch"); #endif if (dparams) { memcpy(&dp, dparams, sizeof(dp)); } else { // Default dungeon parameters dp.seed = 0; dp.c_water = ndef->getId("mapgen_water_source"); dp.c_river_water = ndef->getId("mapgen_river_water_source"); dp.c_wall = ndef->getId("mapgen_cobble"); dp.c_alt_wall = ndef->getId("mapgen_mossycobble"); dp.c_stair = ndef->getId("mapgen_stair_cobble"); if (dp.c_river_water == CONTENT_IGNORE) dp.c_river_water = ndef->getId("mapgen_water_source"); dp.diagonal_dirs = false; dp.only_in_ground = true; dp.holesize = v3s16(1, 2, 1); dp.corridor_len_min = 1; dp.corridor_len_max = 13; dp.room_size_min = v3s16(4, 4, 4); dp.room_size_max = v3s16(8, 6, 8); dp.room_size_large_min = v3s16(8, 8, 8); dp.room_size_large_max = v3s16(16, 16, 16); dp.rooms_min = 2; dp.rooms_max = 16; dp.y_min = -MAX_MAP_GENERATION_LIMIT; dp.y_max = MAX_MAP_GENERATION_LIMIT; dp.notifytype = GENNOTIFY_DUNGEON; dp.np_density = nparams_dungeon_density; dp.np_alt_wall = nparams_dungeon_alt_wall; } } void DungeonGen::generate(MMVManip *vm, u32 bseed, v3s16 nmin, v3s16 nmax) { assert(vm); //TimeTaker t("gen dungeons"); if (nmin.Y < dp.y_min || nmax.Y > dp.y_max) return; float nval_density = NoisePerlin3D(&dp.np_density, nmin.X, nmin.Y, nmin.Z, dp.seed); if (nval_density < 1.0f) return; this->vm = vm; this->blockseed = bseed; random.seed(bseed + 2); // Dungeon generator doesn't modify places which have this set vm->clearFlag(VMANIP_FLAG_DUNGEON_INSIDE | VMANIP_FLAG_DUNGEON_PRESERVE); if (dp.only_in_ground) { // Set all air and water to be untouchable // to make dungeons open to caves and open air for (s16 z = nmin.Z; z <= nmax.Z; z++) { for (s16 y = nmin.Y; y <= nmax.Y; y++) { u32 i = vm->m_area.index(nmin.X, y, z); for (s16 x = nmin.X; x <= nmax.X; x++) { content_t c = vm->m_data[i].getContent(); if (c == CONTENT_AIR || c == dp.c_water || c == dp.c_river_water) vm->m_flags[i] |= VMANIP_FLAG_DUNGEON_PRESERVE; i++; } } } } // Add them for (u32 i = 0; i < floor(nval_density); i++) makeDungeon(v3s16(1, 1, 1) * MAP_BLOCKSIZE); // Optionally convert some structure to alternative structure if (dp.c_alt_wall == CONTENT_IGNORE) return; for (s16 z = nmin.Z; z <= nmax.Z; z++) for (s16 y = nmin.Y; y <= nmax.Y; y++) { u32 i = vm->m_area.index(nmin.X, y, z); for (s16 x = nmin.X; x <= nmax.X; x++) { if (vm->m_data[i].getContent() == dp.c_wall) { if (NoisePerlin3D(&dp.np_alt_wall, x, y, z, blockseed) > 0.0f) vm->m_data[i].setContent(dp.c_alt_wall); } i++; } } //printf("== gen dungeons: %dms\n", t.stop()); } void DungeonGen::makeDungeon(v3s16 start_padding) { v3s16 areasize = vm->m_area.getExtent(); v3s16 roomsize; v3s16 roomplace; /* Find place for first room. There is a 1 in 4 chance of the first room being 'large', all other rooms are not 'large'. */ bool fits = false; for (u32 i = 0; i < 100 && !fits; i++) { bool is_large_room = ((random.next() & 3) == 1); if (is_large_room) { roomsize.Z = random.range( dp.room_size_large_min.Z, dp.room_size_large_max.Z); roomsize.Y = random.range( dp.room_size_large_min.Y, dp.room_size_large_max.Y); roomsize.X = random.range( dp.room_size_large_min.X, dp.room_size_large_max.X); } else { roomsize.Z = random.range(dp.room_size_min.Z, dp.room_size_max.Z); roomsize.Y = random.range(dp.room_size_min.Y, dp.room_size_max.Y); roomsize.X = random.range(dp.room_size_min.X, dp.room_size_max.X); } // start_padding is used to disallow starting the generation of // a dungeon in a neighboring generation chunk roomplace = vm->m_area.MinEdge + start_padding; roomplace.Z += random.range(0, areasize.Z - roomsize.Z - start_padding.Z); roomplace.Y += random.range(0, areasize.Y - roomsize.Y - start_padding.Y); roomplace.X += random.range(0, areasize.X - roomsize.X - start_padding.X); /* Check that we're not putting the room to an unknown place, otherwise it might end up floating in the air */ fits = true; for (s16 z = 0; z < roomsize.Z; z++) for (s16 y = 0; y < roomsize.Y; y++) for (s16 x = 0; x < roomsize.X; x++) { v3s16 p = roomplace + v3s16(x, y, z); u32 vi = vm->m_area.index(p); if ((vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) || vm->m_data[vi].getContent() == CONTENT_IGNORE) { fits = false; break; } } } // No place found if (fits == false) return; /* Stores the center position of the last room made, so that a new corridor can be started from the last room instead of the new room, if chosen so. */ v3s16 last_room_center = roomplace + v3s16(roomsize.X / 2, 1, roomsize.Z / 2); u32 room_count = random.range(dp.rooms_min, dp.rooms_max); for (u32 i = 0; i < room_count; i++) { // Make a room to the determined place makeRoom(roomsize, roomplace); v3s16 room_center = roomplace + v3s16(roomsize.X / 2, 1, roomsize.Z / 2); if (gennotify) gennotify->addEvent(dp.notifytype, room_center); #ifdef DGEN_USE_TORCHES // Place torch at room center (for testing) vm->m_data[vm->m_area.index(room_center)] = MapNode(c_torch); #endif // Quit if last room if (i == room_count - 1) break; // Determine walker start position bool start_in_last_room = (random.range(0, 2) != 0); v3s16 walker_start_place; if (start_in_last_room) { walker_start_place = last_room_center; } else { walker_start_place = room_center; // Store center of current room as the last one last_room_center = room_center; } // Create walker and find a place for a door v3s16 doorplace; v3s16 doordir; m_pos = walker_start_place; if (!findPlaceForDoor(doorplace, doordir)) return; if (random.range(0, 1) == 0) // Make the door makeDoor(doorplace, doordir); else // Don't actually make a door doorplace -= doordir; // Make a random corridor starting from the door v3s16 corridor_end; v3s16 corridor_end_dir; makeCorridor(doorplace, doordir, corridor_end, corridor_end_dir); // Find a place for a random sized room roomsize.Z = random.range(dp.room_size_min.Z, dp.room_size_max.Z); roomsize.Y = random.range(dp.room_size_min.Y, dp.room_size_max.Y); roomsize.X = random.range(dp.room_size_min.X, dp.room_size_max.X); m_pos = corridor_end; m_dir = corridor_end_dir; if (!findPlaceForRoomDoor(roomsize, doorplace, doordir, roomplace)) return; if (random.range(0, 1) == 0) // Make the door makeDoor(doorplace, doordir); else // Don't actually make a door roomplace -= doordir; } } void DungeonGen::makeRoom(v3s16 roomsize, v3s16 roomplace) { MapNode n_wall(dp.c_wall); MapNode n_air(CONTENT_AIR); // Make +-X walls for (s16 z = 0; z < roomsize.Z; z++) for (s16 y = 0; y < roomsize.Y; y++) { { v3s16 p = roomplace + v3s16(0, y, z); if (!vm->m_area.contains(p)) continue; u32 vi = vm->m_area.index(p); if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) continue; vm->m_data[vi] = n_wall; } { v3s16 p = roomplace + v3s16(roomsize.X - 1, y, z); if (!vm->m_area.contains(p)) continue; u32 vi = vm->m_area.index(p); if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) continue; vm->m_data[vi] = n_wall; } } // Make +-Z walls for (s16 x = 0; x < roomsize.X; x++) for (s16 y = 0; y < roomsize.Y; y++) { { v3s16 p = roomplace + v3s16(x, y, 0); if (!vm->m_area.contains(p)) continue; u32 vi = vm->m_area.index(p); if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) continue; vm->m_data[vi] = n_wall; } { v3s16 p = roomplace + v3s16(x, y, roomsize.Z - 1); if (!vm->m_area.contains(p)) continue; u32 vi = vm->m_area.index(p); if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) continue; vm->m_data[vi] = n_wall; } } // Make +-Y walls (floor and ceiling) for (s16 z = 0; z < roomsize.Z; z++) for (s16 x = 0; x < roomsize.X; x++) { { v3s16 p = roomplace + v3s16(x, 0, z); if (!vm->m_area.contains(p)) continue; u32 vi = vm->m_area.index(p); if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) continue; vm->m_data[vi] = n_wall; } { v3s16 p = roomplace + v3s16(x,roomsize. Y - 1, z); if (!vm->m_area.contains(p)) continue; u32 vi = vm->m_area.index(p); if (vm->m_flags[vi] & VMANIP_FLAG_DUNGEON_UNTOUCHABLE) continue; vm->m_data[vi] = n_wall; } } // Fill with air for (s16 z = 1; z < roomsize.Z - 1; z++) for (s16 y = 1; y < roomsize.Y - 1; y++) for (s16 x = 1; x < roomsize.X - 1; x++) { v3s16 p = roomplace + v3s16(x, y, z); if (!vm->m_area.contains(p)) continue; u32 vi = vm->m_area.index(p); vm->m_flags[vi] |= VMANIP_FLAG_DUNGEON_UNTOUCHABLE; vm->m_data[vi] = n_air; } } void DungeonGen::makeFill(v3s16 place, v3s16 size, u8 avoid_flags, MapNode n, u8 or_flags) { for (s16 z = 0; z < size.Z; z++) for (s16 y = 0; y < size.Y; y++) for (s16 x = 0; x < size.X; x++) { v3s16 p = place + v3s16(x, y, z); if (!vm->m_area.contains(p)) continue; u32 vi = vm->m_area.index(p); if (vm->m_flags[vi] & avoid_flags) continue; vm->m_flags[vi] |= or_flags; vm->m_data[vi] = n; } } void /* Minetest Copyright (C) 2010-2013 celeron55, Perttu Ahola <celeron55@gmail.com> This program is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 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 Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ #ifndef SERVEROBJECT_HEADER #define SERVEROBJECT_HEADER #include "irrlichttypes_bloated.h" #include "activeobject.h" #include "inventorymanager.h" #include "itemgroup.h" #include "util/container.h" /* Some planning ------------- * Server environment adds an active object, which gets the id 1 * The active object list is scanned for each client once in a while, and it finds out what objects have been added that are not known by the client yet. This scan is initiated by the Server class and the result ends up directly to the server. * A network packet is created with the info and sent to the client. * Environment converts objects to static data and static data to objects, based on how close players are to them. */ class ServerEnvironment; struct ItemStack; class Player; struct ToolCapabilities; struct ObjectProperties; class ServerActiveObject : public ActiveObject { public: /* NOTE: m_env can be NULL, but step() isn't called if it is. Prototypes are used that way. */ ServerActiveObject(ServerEnvironment *env, v3f pos); virtual ~ServerActiveObject(); virtual u8 getSendType() const { return getType(); } // Called after id has been set and has been inserted in environment virtual void addedToEnvironment(u32 dtime_s){}; // Called before removing from environment virtual void removingFromEnvironment(){}; // Returns true if object's deletion is the job of the // environment virtual bool environmentDeletes() const { return true; } virtual bool unlimitedTransferDistance() const { return false; } // Create a certain type of ServerActiveObject static ServerActiveObject* create(u8 type, ServerEnvironment *env, u16 id, v3f pos, const std::string &data); /* Some simple getters/setters */ v3f getBasePosition(){ return m_base_position; } void setBasePosition(v3f pos){ m_base_position = pos; } ServerEnvironment* getEnv(){ return m_env; } /* Some more dynamic interface */ virtual void setPos