static int FindWallRun( const Map *map, const Vec2i mid, const Vec2i d, const int len) { int run = 0; int next = 0; bool plus = false; // Find the wall run by starting from a midpoint and expanding outwards in // both directions, in a series 0, 1, -1, 2, -2... for (int i = 0; i < len; i++, run++) { // Check if this is a wall so we can add a door here // Also check if the two tiles aside are not walls // Note: we must look for runs if (plus) { next += i; } else { next -= i; } const Vec2i v = Vec2iAdd(mid, Vec2iScale(d, next)); plus = !plus; if (IMapGet(map, v) != MAP_WALL || IMapGet(map, Vec2iNew(v.x + d.y, v.y + d.x)) == MAP_WALL || IMapGet(map, Vec2iNew(v.x - d.y, v.y - d.x)) == MAP_WALL) { break; } } return run; }
void MissionConvertToType(Mission *m, Map *map, MapType type) { memset(&m->u, 0, sizeof m->u); switch (type) { case MAPTYPE_CLASSIC: // Setup default parameters m->u.Classic.Walls = 10; m->u.Classic.WallLength = 5; m->u.Classic.CorridorWidth = 2; m->u.Classic.Rooms.Count = 10; m->u.Classic.Rooms.Min = 5; m->u.Classic.Rooms.Max = 8;; m->u.Classic.Rooms.Edge = true; m->u.Classic.Rooms.Overlap = true; m->u.Classic.Rooms.Walls = 1; m->u.Classic.Rooms.WallLength = 1; m->u.Classic.Rooms.WallPad = 1; m->u.Classic.Squares = 1; m->u.Classic.Doors.Enabled = true; m->u.Classic.Doors.Min = 1; m->u.Classic.Doors.Max = 2; m->u.Classic.Pillars.Count = 1; m->u.Classic.Pillars.Min = 2; m->u.Classic.Pillars.Max = 3; break; case MAPTYPE_STATIC: { Vec2i v; // Take all the tiles from the current map // and save them in the static map CArrayInit(&m->u.Static.Tiles, sizeof(unsigned short)); for (v.y = 0; v.y < m->Size.y; v.y++) { for (v.x = 0; v.x < m->Size.x; v.x++) { unsigned short tile = IMapGet(map, v); CArrayPushBack(&m->u.Static.Tiles, &tile); } } CArrayInit(&m->u.Static.Items, sizeof(MapObjectPositions)); CArrayInit(&m->u.Static.Characters, sizeof(CharacterPositions)); CArrayInit(&m->u.Static.Objectives, sizeof(ObjectivePositions)); CArrayInit(&m->u.Static.Keys, sizeof(KeyPositions)); } break; case MAPTYPE_CAVE: // Setup default parameters m->u.Cave.FillPercent = 40; m->u.Cave.Repeat = 4; m->u.Cave.R1 = 5; m->u.Cave.R2 = 2; m->u.Cave.CorridorWidth = 2; break; default: CASSERT(false, "unknown map type"); break; } m->Type = type; }
int MapIsAreaClearOrRoom(Map *map, Vec2i pos, Vec2i size) { Vec2i v; if (pos.x < 0 || pos.y < 0 || pos.x + size.x >= map->Size.x || pos.y + size.y >= map->Size.y) { return 0; } for (v.y = pos.y; v.y < pos.y + size.y; v.y++) { for (v.x = pos.x; v.x < pos.x + size.x; v.x++) { unsigned short tile = IMapGet(map, v) & MAP_MASKACCESS; switch (tile) { case MAP_FLOOR: // fallthrough case MAP_ROOM: break; case MAP_WALL: // Check if this wall is part of a room if (!MapTileIsPartOfRoom(map, v)) { return 0; } break; default: return 0; } } } return 1; }
int MapIsAreaClearOrWall(Map *map, Vec2i pos, Vec2i size) { Vec2i v; if (pos.x < 0 || pos.y < 0 || pos.x + size.x >= map->Size.x || pos.y + size.y >= map->Size.y) { return 0; } for (v.y = pos.y; v.y < pos.y + size.y; v.y++) { for (v.x = pos.x; v.x < pos.x + size.x; v.x++) { switch (IMapGet(map, v) & MAP_MASKACCESS) { case MAP_FLOOR: break; case MAP_WALL: // need to check if this is not a room wall if (MapTileIsPartOfRoom(map, v)) { return 0; } break; default: return 0; } } } return 1; }
static int MapFindWallRun(Map *map, Vec2i start, Vec2i d, int len) { int wallRun = 0; Vec2i v; int i; for (i = 0, v = start; i < len; i++, v = Vec2iAdd(v, d)) { // Check if this is a wall so we can add a door here // Also check if the two tiles aside are not walls if (IMapGet(map, v) == MAP_WALL && IMapGet(map, Vec2iNew(v.x + d.y, v.y + d.x)) != MAP_WALL && IMapGet(map, Vec2iNew(v.x - d.y, v.y - d.x)) != MAP_WALL) { wallRun++; } } return wallRun; }
void MapMakeRoom(Map *map, int xOrigin, int yOrigin, int width, int height) { int x, y; // Set the perimeter walls and interior // If the tile is a room interior already, do not turn it into a wall // This is due to overlapping rooms for (y = yOrigin; y < yOrigin + height; y++) { for (x = xOrigin; x < xOrigin + width; x++) { if (y == yOrigin || y == yOrigin + height - 1 || x == xOrigin || x == xOrigin + width - 1) { if (IMapGet(map, Vec2iNew(x, y)) == MAP_FLOOR) { IMapSet(map, Vec2iNew(x, y), MAP_WALL); } } else { IMapSet(map, Vec2iNew(x, y), MAP_ROOM); } } } // Check perimeter again; if there are walls where both sides contain // rooms, remove the wall as the rooms have merged for (y = yOrigin; y < yOrigin + height; y++) { for (x = xOrigin; x < xOrigin + width; x++) { if (y == yOrigin || y == yOrigin + height - 1 || x == xOrigin || x == xOrigin + width - 1) { if (((IMapGet(map, Vec2iNew(x + 1, y)) & MAP_MASKACCESS) == MAP_ROOM && (IMapGet(map, Vec2iNew(x - 1, y)) & MAP_MASKACCESS) == MAP_ROOM) || ((IMapGet(map, Vec2iNew(x, y + 1)) & MAP_MASKACCESS) == MAP_ROOM && (IMapGet(map, Vec2iNew(x, y - 1)) & MAP_MASKACCESS) == MAP_ROOM)) { IMapSet(map, Vec2iNew(x, y), MAP_ROOM); } } } } }
// Set tile properties for a map tile, such as picture to use static void MapSetupTile(Map *map, const Vec2i pos, const Mission *m) { const int floor = m->FloorStyle % FLOOR_STYLE_COUNT; const int wall = m->WallStyle % WALL_STYLE_COUNT; const int room = m->RoomStyle % ROOM_STYLE_COUNT; Tile *tAbove = MapGetTile(map, Vec2iNew(pos.x, pos.y - 1)); bool canSeeTileAbove = !(tAbove != NULL && !TileCanSee(tAbove)); Tile *t = MapGetTile(map, pos); if (!t) { return; } switch (IMapGet(map, pos) & MAP_MASKACCESS) { case MAP_FLOOR: case MAP_SQUARE: t->pic = PicManagerGetMaskedStylePic( &gPicManager, "floor", floor, canSeeTileAbove ? FLOOR_NORMAL : FLOOR_SHADOW, m->FloorMask, m->AltMask); if (canSeeTileAbove) { // Normal floor tiles can be replaced randomly with // special floor tiles such as drainage t->flags |= MAPTILE_IS_NORMAL_FLOOR; } break; case MAP_ROOM: case MAP_DOOR: t->pic = PicManagerGetMaskedStylePic( &gPicManager, "room", room, canSeeTileAbove ? ROOMFLOOR_NORMAL : ROOMFLOOR_SHADOW, m->RoomMask, m->AltMask); break; case MAP_WALL: t->pic = PicManagerGetMaskedStylePic( &gPicManager, "wall", wall, MapGetWallPic(map, pos), m->WallMask, m->AltMask); t->flags = MAPTILE_NO_WALK | MAPTILE_NO_SHOOT | MAPTILE_NO_SEE | MAPTILE_IS_WALL; break; case MAP_NOTHING: t->pic = NULL; t->flags = MAPTILE_NO_WALK | MAPTILE_IS_NOTHING; break; } }
static int MapTileIsPartOfRoom(Map *map, Vec2i pos) { Vec2i v2; int isRoom = 0; int isFloor = 0; // Find whether a wall tile is part of a room perimeter // The surrounding tiles must have normal floor and room tiles // to be a perimeter for (v2.y = pos.y - 1; v2.y <= pos.y + 1; v2.y++) { for (v2.x = pos.x - 1; v2.x <= pos.x + 1; v2.x++) { if ((IMapGet(map, v2) & MAP_MASKACCESS) == MAP_ROOM) { isRoom = 1; } else if ((IMapGet(map, v2) & MAP_MASKACCESS) == MAP_FLOOR) { isFloor = 1; } } } return isRoom && isFloor; }
int MapIsValidStartForWall( Map *map, int x, int y, unsigned short tileType, int pad) { Vec2i d; if (x == 0 || y == 0 || x == map->Size.x - 1 || y == map->Size.y - 1) { return 0; } for (d.x = x - pad; d.x <= x + pad; d.x++) { for (d.y = y - pad; d.y <= y + pad; d.y++) { if (IMapGet(map, d) != tileType) { return 0; } } } return 1; }
int MapIsAreaClear(Map *map, Vec2i pos, Vec2i size) { Vec2i v; if (pos.x < 0 || pos.y < 0 || pos.x + size.x >= map->Size.x || pos.y + size.y >= map->Size.y) { return 0; } for (v.y = pos.y; v.y < pos.y + size.y; v.y++) { for (v.x = pos.x; v.x < pos.x + size.x; v.x++) { if (IMapGet(map, v) != MAP_FLOOR) { return 0; } } } return 1; }
// Paint all the edge tiles as a wall, unless they are room tiles already; // then paint the interior as room tiles static void EditorBrushPaintRoom(EditorBrush *b, Mission *m) { Vec2i v; for (v.y = 0; v.y < b->BrushSize; v.y++) { for (v.x = 0; v.x < b->BrushSize; v.x++) { unsigned short tile = MAP_ROOM; if (v.x == 0 || v.x == b->BrushSize - 1 || v.y == 0 || v.y == b->BrushSize - 1) { tile = MAP_WALL; } const Vec2i pos = Vec2iAdd(b->Pos, v); const unsigned short tileExisting = IMapGet(&gMap, pos); if (tileExisting != MAP_ROOM) { SetTile(m, pos, tile); } } } b->IsPainting = true; b->LastPos = b->Pos; }
static void MapGrowWall( Map *map, int x, int y, unsigned short tileType, int pad, int d, int length) { int l; Vec2i v; if (length <= 0) return; switch (d) { case 0: if (y < 2 + pad) { return; } // Check tiles above // xxxxx // xxx // o for (v.y = y - 2; v.y > y - 2 - pad; v.y--) { int level = v.y - (y - 2); for (v.x = x - 1 - level; v.x <= x + 1 + level; v.x++) { if (IMapGet(map, v) != tileType) { return; } } } y--; break; case 1: // Check tiles to the right // x // xx // oxx // xx // x for (v.x = x + 2; v.x < x + 2 + pad; v.x++) { int level = v.x - (x + 2); for (v.y = y - 1 - level; v.y <= y + 1 + level; v.y++) { if (IMapGet(map, v) != tileType) { return; } } } x++; break; case 2: // Check tiles below // o // xxx // xxxxx for (v.y = y + 2; v.y < y + 2 + pad; v.y++) { int level = v.y - (y + 2); for (v.x = x - 1 - level; v.x <= x + 1 + level; v.x++) { if (IMapGet(map, v) != tileType) { return; } } } y++; break; case 4: if (x < 2 + pad) { return; } // Check tiles to the left // x // xx // xxo // xx // x for (v.x = x - 2; v.x > x - 2 - pad; v.x--) { int level = v.x - (x - 2); for (v.y = y - 1 - level; v.y <= y + 1 + level; v.y++) { if (IMapGet(map, v) != tileType) { return; } } } x--; break; } MapMakeWall(map, Vec2iNew(x, y)); length--; if (length > 0 && (rand() & 3) == 0) { // Randomly try to grow the wall in a different direction l = rand() % length; MapGrowWall(map, x, y, tileType, pad, rand() & 3, l); length -= l; } // Keep growing wall in same direction MapGrowWall(map, x, y, tileType, pad, d, length); }
// Check that this area does not overlap two or more "walls" int MapIsLessThanTwoWallOverlaps(Map *map, Vec2i pos, Vec2i size) { Vec2i v; int numOverlaps = 0; Vec2i overlapMin = Vec2iZero(); Vec2i overlapMax = Vec2iZero(); if (pos.x < 0 || pos.y < 0 || pos.x + size.x >= map->Size.x || pos.y + size.y >= map->Size.y) { return 0; } for (v.y = pos.y; v.y < pos.y + size.y; v.y++) { for (v.x = pos.x; v.x < pos.x + size.x; v.x++) { // only check perimeter if (v.x != pos.x && v.x != pos.x + size.x - 1 && v.y != pos.y && v.y != pos.y + size.y - 1) { continue; } switch (IMapGet(map, v)) { case MAP_WALL: // Check if this wall is part of a room if (!MapTileIsPartOfRoom(map, v)) { if (numOverlaps == 0) { overlapMin = overlapMax = v; } else { overlapMin = Vec2iMin(overlapMin, v); overlapMax = Vec2iMax(overlapMax, v); } numOverlaps++; } break; default: break; } } } if (numOverlaps < 2) { return 1; } // Now check that all tiles between the first and last tiles are // pillar tiles for (v.y = overlapMin.y; v.y <= overlapMax.y; v.y++) { for (v.x = overlapMin.x; v.x <= overlapMax.x; v.x++) { switch (IMapGet(map, v) & MAP_MASKACCESS) { case MAP_WALL: // Check if this wall is not part of a room if (MapTileIsPartOfRoom(map, v)) { return 0; } break; default: // invalid tile type return 0; } } } return 1; }
// Find the size of the passage created by the overlap of two rooms // To find whether an overlap is valid, // collect the perimeter walls that overlap // Two possible cases where there is a valid overlap: // - if there are exactly two overlapping perimeter walls // i.e. // X // room 2 XXXXXX // X X <-- all tiles between either belong to room 1 or room 2 // XXXXXX // X room 1 // // - if the collection of overlapping tiles are contiguous // i.e. // X room 1 X // XXXXXXXXXXXXXXX // X room 2 X // // In both cases, the overlap is valid if all tiles in between are room or // perimeter tiles. The size of the passage is given by the largest difference // in the x or y coordinates between the first and last intersection tiles, // minus 1 int MapGetRoomOverlapSize( Map *map, Vec2i pos, Vec2i size, unsigned short *overlapAccess) { Vec2i v; int numOverlaps = 0; Vec2i overlapMin = Vec2iZero(); Vec2i overlapMax = Vec2iZero(); if (pos.x < 0 || pos.y < 0 || pos.x + size.x >= map->Size.x || pos.y + size.y >= map->Size.y) { return 0; } // Find perimeter tiles that overlap for (v.y = pos.y; v.y < pos.y + size.y; v.y++) { for (v.x = pos.x; v.x < pos.x + size.x; v.x++) { // only check perimeter if (v.x != pos.x && v.x != pos.x + size.x - 1 && v.y != pos.y && v.y != pos.y + size.y - 1) { continue; } switch (IMapGet(map, v)) { case MAP_WALL: // Check if this wall is part of a room if (MapTileIsPartOfRoom(map, v)) { // Get the access level of the room Vec2i v2; for (v2.y = v.y - 1; v2.y <= v.y + 1; v2.y++) { for (v2.x = v.x - 1; v2.x <= v.x + 1; v2.x++) { if ((IMapGet(map, v2) & MAP_MASKACCESS) == MAP_ROOM) { *overlapAccess |= IMapGet(map, v2) & MAP_ACCESSBITS; } } } if (numOverlaps == 0) { overlapMin = overlapMax = v; } else { overlapMin = Vec2iMin(overlapMin, v); overlapMax = Vec2iMax(overlapMax, v); } numOverlaps++; } break; default: break; } } } if (numOverlaps < 2) { return 0; } // Now check that all tiles between the first and last tiles are room or // perimeter tiles for (v.y = overlapMin.y; v.y <= overlapMax.y; v.y++) { for (v.x = overlapMin.x; v.x <= overlapMax.x; v.x++) { switch (IMapGet(map, v) & MAP_MASKACCESS) { case MAP_ROOM: break; case MAP_WALL: // Check if this wall is part of a room if (!MapTileIsPartOfRoom(map, v)) { return 0; } break; default: // invalid tile type return 0; } } } return MAX(overlapMax.x - overlapMin.x, overlapMax.y - overlapMin.y) - 1; }
void MapPlaceDoors( Map *map, Vec2i pos, Vec2i size, int hasDoors, int doors[4], int doorMin, int doorMax, unsigned short accessMask) { int x, y; int i; unsigned short doorTile = hasDoors ? MAP_DOOR : MAP_ROOM; Vec2i v; // Set access mask for (y = pos.y + 1; y < pos.y + size.y - 1; y++) { for (x = pos.x + 1; x < pos.x + size.x - 1; x++) { if ((IMapGet(map, Vec2iNew(x, y)) & MAP_MASKACCESS) == MAP_ROOM) { IMapSet(map, Vec2iNew(x, y), MAP_ROOM | accessMask); } } } // Set the doors if (doors[0]) { int doorSize = MIN( (doorMax > doorMin ? (rand() % (doorMax - doorMin + 1)) : 0) + doorMin, size.y - 4); for (i = -doorSize / 2; i < (doorSize + 1) / 2; i++) { v = Vec2iNew(pos.x, pos.y + size.y / 2 + i); if (IMapGet(map, Vec2iNew(v.x + 1, v.y)) != MAP_WALL && IMapGet(map, Vec2iNew(v.x - 1, v.y)) != MAP_WALL) { IMapSet(map, v, doorTile); } } } if (doors[1]) { int doorSize = MIN( (doorMax > doorMin ? (rand() % (doorMax - doorMin + 1)) : 0) + doorMin, size.y - 4); for (i = -doorSize / 2; i < (doorSize + 1) / 2; i++) { v = Vec2iNew(pos.x + size.x - 1, pos.y + size.y / 2 + i); if (IMapGet(map, Vec2iNew(v.x + 1, v.y)) != MAP_WALL && IMapGet(map, Vec2iNew(v.x - 1, v.y)) != MAP_WALL) { IMapSet(map, v, doorTile); } } } if (doors[2]) { int doorSize = MIN( (doorMax > doorMin ? (rand() % (doorMax - doorMin + 1)) : 0) + doorMin, size.x - 4); for (i = -doorSize / 2; i < (doorSize + 1) / 2; i++) { v = Vec2iNew(pos.x + size.x / 2 + i, pos.y); if (IMapGet(map, Vec2iNew(v.x, v.y + 1)) != MAP_WALL && IMapGet(map, Vec2iNew(v.x, v.y - 1)) != MAP_WALL) { IMapSet(map, v, doorTile); } } } if (doors[3]) { int doorSize = MIN( (doorMax > doorMin ? (rand() % (doorMax - doorMin + 1)) : 0) + doorMin, size.x - 4); for (i = -doorSize / 2; i < (doorSize + 1) / 2; i++) { v = Vec2iNew(pos.x + size.x / 2 + i, pos.y + size.y - 1); if (IMapGet(map, Vec2iNew(v.x, v.y + 1)) != MAP_WALL && IMapGet(map, Vec2iNew(v.x, v.y - 1)) != MAP_WALL) { IMapSet(map, v, doorTile); } } } }
static int W(Map *map, int x, int y) { return IMapGet(map, Vec2iNew(x, y)) == MAP_WALL; }