Created
September 29, 2013 21:49
-
-
Save gwx/6756909 to your computer and use it in GitHub Desktop.
random rooms
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// This is a library for array based integer lists. | |
// Each list is an array. | |
// The first element is the length. | |
// The rest of the elements are the contents. | |
// Because of this, indices are 1-based. | |
const int SIZE = 0; | |
void ArrayList_Clear(int list) { | |
list[0] = 0;} | |
void ArrayList_Append(int list, int value) { | |
list[SIZE]++; | |
list[list[SIZE]] = value; | |
//ArrayList_Print(list); | |
} | |
int ArrayList_IndexOf(int list, int value) { | |
int size = list[0]; | |
for (int index = 1; index <= size; index++) { | |
if (list[index] == value) { | |
return index;}} | |
return -1;} | |
// Returns removed value. | |
int ArrayList_Remove(int list, int index) { | |
int size = list[SIZE]; | |
if (index > size) {return -1;} | |
int value = list[index]; | |
for (; index < size; index++) { | |
list[index] = list[index + 1];} | |
list[0]--; | |
//ArrayList_Print(list); | |
return value;} | |
// Removes a random element. | |
int ArrayList_RemoveRandom(int list) { | |
return ArrayList_Remove(list, Rand(list[SIZE]) + 1);} | |
void ArrayList_Print(int list) { | |
int msg[256]; | |
int format[] = "List (%d):"; | |
sprintf(msg, format, list[SIZE]); | |
TraceS(msg); | |
for (int i = 1; i <= list[SIZE]; i++) { | |
int format[] = " %d"; | |
int msg[32]; | |
sprintf(msg, format, list[i]); | |
TraceS(msg);} | |
TraceNL();} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//////////////////////////////////////////////////////////////// | |
//// Constants | |
const int VARIANT_COUNT = 6; | |
//////////////// | |
//// Level | |
// The number of the level. | |
const int LEVEL_NUMBER = 0; | |
// The number of rooms in the level. | |
const int LEVEL_ROOM_COUNT = 1; | |
// The level's start room. | |
const int LEVEL_ROOM_START = 2; | |
// The level's end room. | |
const int LEVEL_ROOM_END = 3; | |
//////////////// | |
//// Rooms | |
// Mask for the direction for the room's source. | |
const int ROOM_SOURCE_MASK = 0x7; | |
// Set if the room is currently in use. | |
const int ROOM_IN_USE = 0x10000; | |
// Up-going stairs. | |
const int ROOM_UP = 0x4; | |
// Down-going stairs. | |
const int ROOM_DOWN = 0x8; | |
// Non-terminal room. | |
const int ROOM_MIDDLE = 0x10; | |
// The boss key room. | |
const int ROOM_BKEY = 0x20; | |
// A normal key room. | |
const int ROOM_KEY = 0x40; | |
// Mask for the room containing something already. | |
const int ROOM_FULL_MASK = 0x6C; | |
//////////////// | |
//// Doors | |
const int DOOR_WALL = 1; | |
const int DOOR_OPEN = 2; | |
const int DOOR_LOCK = 4; | |
const int DOOR_BOSS = 5; | |
const int DOOR_BOMB = 6; | |
//////////////// | |
//// Flags | |
// A new room was carved. | |
const int DGCDF_NEW_ROOM = 1; | |
//////////////////////////////////////////////////////////////// | |
//// Functions | |
//////////////// | |
//// Carves a level into an 8x8 array. | |
//// | |
//// levelInfo - The array of level info. Uses the LEVEL_ constants as | |
//// indices. Should be at least size 4. | |
//// | |
//// rooms - The array of rooms. Each level takes 64 rooms, so this | |
//// array must be at least length <level * 64>. | |
//// | |
//// doors - The array of doors (4 to a room). Each level has up to | |
//// 256 doors, so this array must be at least length <level * 256>. | |
//// | |
//// variants - The array of room variants. Should be the same size as | |
//// the rooms array. | |
//// | |
//// start - The index of the start room. From 0 to 63. | |
void DungeonGen_CarveLevel( | |
int levelInfo, int rooms, int doors, int variants, int start) { | |
int level = levelInfo[LEVEL_NUMBER]; | |
// All doors that have been created but not yet assigned. | |
int list_UnusedDoors[257]; | |
// Offsets based on the current level. | |
int roomOff = (level - 1) << 6; | |
int doorOff = (level - 1) << 8; | |
// Just pick the variants randomly. | |
for (int i = 0; i < 64; i++) { | |
variants[roomOff + i] = Rand(VARIANT_COUNT);} | |
//// Initialize with the start room. | |
// Mark the starting room with an up staircase. | |
rooms[roomOff + start] = ROOM_IN_USE + ROOM_UP; | |
levelInfo[LEVEL_ROOM_COUNT] = 1; | |
// Add each of the room's doors to the unused list. | |
for (int dir = 0; dir < 4; dir++) { | |
ArrayList_Append(list_UnusedDoors, doorOff + (start << 2) + dir);} | |
//// Loop through every unused door until they are all used. This | |
//// loop adds doors as they are created, as well. | |
while (list_UnusedDoors[SIZE] > 0) { | |
// Grab a random unused door. | |
int door = ArrayList_RemoveRandom(list_UnusedDoors); | |
int carve = DungeonGen_CarveDoor(levelInfo, rooms, doors, door); | |
// If we created a new room. | |
if (carve & DGCDF_NEW_ROOM) { | |
int room = DungeonGen_DoorTargetRoom(door); | |
// Mark the room as in use. | |
rooms[room] |= ROOM_IN_USE; | |
// Mark the new room's source direction. | |
rooms[room] |= (door & 3) ^ 1; | |
// Count a new created room. | |
levelInfo[LEVEL_ROOM_COUNT]++; | |
// Add the unused doors in the new room. | |
for (int i = 0; i < 4; i++) { | |
int door = room * 4 + i; | |
if (doors[door] == 0) { | |
ArrayList_Append(list_UnusedDoors, door);}} | |
// Mark the source room as being non-terminal. | |
room = DungeonGen_DoorSourceRoom(door); | |
rooms[room] |= ROOM_MIDDLE;}} | |
// Collect all the terminal rooms. | |
int list_TerminalRooms[65]; | |
DungeonGen_CollectTerminalRooms(level, rooms, list_TerminalRooms); | |
// Pick a random terminal room for the exit. | |
levelInfo[LEVEL_ROOM_END] = ArrayList_RemoveRandom(list_TerminalRooms); | |
rooms[levelInfo[LEVEL_ROOM_END]] |= ROOM_DOWN; | |
// Change the exit's source door to the boss key door. | |
int sourceDoor = DungeonGen_RoomSourceDoor(rooms, levelInfo[LEVEL_ROOM_END]); | |
doors[sourceDoor] = DOOR_BOSS; | |
doors[DungeonGen_DoorOpposite(sourceDoor)] = DOOR_BOSS; | |
// Pick another terminal room for the boss key. | |
if (list_TerminalRooms[SIZE] > 0) { | |
int bkeyRoom = ArrayList_RemoveRandom(list_TerminalRooms); | |
rooms[bkeyRoom] |= ROOM_BKEY;} | |
// Since there were no more terminal rooms, pick a room completely | |
// at random to hold the boss key. | |
else { | |
// We'll pick the nth empty room we come across. We've used 2 | |
// rooms so far - the start and end. | |
int count = Rand(levelInfo[LEVEL_ROOM_COUNT] - 2) + 1; | |
int room = roomOff - 1; | |
while (count > 0) { | |
room++; | |
if ((!(rooms[room] & ROOM_FULL_MASK)) && | |
(rooms[room] & ROOM_IN_USE)) { | |
count--;}} | |
rooms[room] |= ROOM_BKEY;} | |
// Lock Doors. | |
for (int i = 0; i < 64; i++) { | |
int room = Rand(64) + roomOff; | |
int sourceDoor = DungeonGen_RoomSourceDoor(rooms, room); | |
if (sourceDoor != -1) { | |
if (rooms[room] & ROOM_IN_USE && doors[sourceDoor] == DOOR_OPEN) { | |
// Pick a random empty source room. | |
int source = DungeonGen_RandomEmptySource(rooms, room); | |
if (source != -1) { | |
// Mark the doors as locked. | |
doors[sourceDoor] = DOOR_LOCK; | |
doors[DungeonGen_DoorOpposite(sourceDoor)] = DOOR_LOCK; | |
// Mark the chosen source room as a key room. | |
rooms[source] |= ROOM_KEY;}}}} | |
// | |
} | |
//////////////// | |
//// Carves a door in a level. See DungeonGen_CarveLevel for the first | |
//// 3 arguments, which are passed directly. | |
//// | |
//// door - The door that is being carved. | |
//// | |
//// roomCount - The number of rooms already created on this level. | |
//// | |
//// returns: See the DGCDF_ constants. | |
int DungeonGen_CarveDoor(int levelInfo, int rooms, int doors, int door) { | |
int level = levelInfo[LEVEL_NUMBER]; | |
int sourceRoom = DungeonGen_DoorSourceRoom(door); | |
int targetRoom = DungeonGen_DoorTargetRoom(door); | |
int opposite = DungeonGen_DoorOpposite(door); | |
// If the target room doesn't exist, make the door a wall. | |
if (targetRoom == -1) { | |
doors[door] = DOOR_WALL;} | |
// If the opposite door is already defined, make this door match it. | |
else if (doors[opposite] != 0) { | |
doors[door] = doors[opposite];} | |
// If the target room has already been visited, then randomly pick | |
// the door. | |
else if (rooms[targetRoom] & ROOM_IN_USE) { | |
doors[door] = Cond(Rand(2) == 0, DOOR_WALL, DOOR_OPEN);} | |
// The target room hasn't been visited, so base this on roomCount. | |
else { | |
int excessRooms = levelInfo[LEVEL_ROOM_COUNT] - (10 + level * 5); | |
// If we haven't met the target number of rooms, force an open door. | |
if (excessRooms <= 0) { | |
doors[door] = DOOR_OPEN;} | |
// If we have, then possibly make a bomb wall. | |
else if (Rand(excessRooms + level) < level) { | |
doors[door] = DOOR_BOMB;} | |
// Then possibly make an open door. | |
else if (Rand(excessRooms + level) < level) { | |
doors[door] = DOOR_OPEN;} | |
// Otherwise it's a wall. | |
else { | |
doors[door] = DOOR_WALL;} | |
// Inform that the room is new. | |
levelInfo[LEVEL_ROOM_COUNT]++; | |
return DGCDF_NEW_ROOM;} | |
// No special code. | |
return 0;} | |
//////////////// | |
//// Get the room in a given direction. Return -1 if there is no such | |
//// room (out of bounds). | |
int DungeonGen_AdjacentRoom(int room, int dir) { | |
if (dir == DIR_UP) { | |
if ((room & 0x38) == 0) {return -1;} | |
else {return room - 8;}} | |
else if (dir == DIR_DOWN) { | |
if ((room & 0x38) == 0x38) {return -1;} | |
else {return room + 8;}} | |
else if (dir == DIR_LEFT) { | |
if ((room & 7) == 0) {return -1;} | |
else {return room - 1;}} | |
else if (dir == DIR_RIGHT) { | |
if ((room & 7) == 7) {return -1;} | |
else {return room + 1;}} | |
else {return -1;}} | |
//////////////// | |
//// Get the source room of a door. | |
int DungeonGen_DoorSourceRoom(int door) { | |
return door >> 2;} | |
//////////////// | |
//// Get the target room of a door. | |
int DungeonGen_DoorTargetRoom(int door) { | |
return DungeonGen_AdjacentRoom(door >> 2, door & 3);} | |
//////////////// | |
//// Get the opposite door of a door. | |
int DungeonGen_DoorOpposite(int door) { | |
int room = DungeonGen_DoorTargetRoom(door); | |
if (room == -1) {return -1;} | |
else {return (room << 2) + ((door & 3) ^ 1);}} | |
//////////////// | |
//// Collect all the terminal rooms on a level into a list. The list | |
//// should be size 65. | |
int DungeonGen_CollectTerminalRooms(int level, int rooms, | |
int list_TerminalRooms) { | |
list_TerminalRooms[SIZE] = 0; | |
for (int i = (level - 1) << 6; i < level << 6; i++) { | |
if (!(rooms[i] & ROOM_MIDDLE)) { | |
ArrayList_Append(list_TerminalRooms, i);}}} | |
//////////////// | |
//// Get the source door of a given room, or -1 if it has none. | |
int DungeonGen_RoomSourceDoor(int rooms, int room) { | |
int dir = rooms[room] & ROOM_SOURCE_MASK; | |
if (dir >= 4) {return -1;} | |
else {return room << 2 + dir;}} | |
//////////////// | |
//// Get the source room of a given room, or -1 if it has none. | |
int DungeonGen_RoomSource(int rooms, int room) { | |
int info = rooms[room]; | |
int dir = info & ROOM_SOURCE_MASK; | |
if (dir >= 4) {return -1;} | |
else {return DungeonGen_AdjacentRoom(room, dir);}} | |
//////////////// | |
//// Count how many steps away from the level start. | |
int DungeonGen_SourceDistance(int rooms, int room) { | |
int count = -1; | |
while (room != -1) { | |
count++; | |
room = DungeonGen_RoomSource(rooms, room);} | |
return count;} | |
//////////////// | |
//// Get a random empty source of a room. | |
int DungeonGen_RandomEmptySource(int rooms, int room) { | |
// count of empty sources. | |
int count = 0; | |
int source = DungeonGen_RoomSource(rooms, room); | |
while (source != -1) { | |
if (!(rooms[source] & ROOM_FULL_MASK)) { | |
count++;} | |
source = DungeonGen_RoomSource(rooms, source);} | |
if (count == 0) {return -1;} | |
count = Rand(count) + 1; | |
source = room; | |
while (count > 0) { | |
source = DungeonGen_RoomSource(rooms, source); | |
if (!(rooms[source] & ROOM_FULL_MASK)) { | |
count--;}} | |
return source;} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import "std.zh" | |
import "string.zh" | |
import "random/array-list.zh" | |
import "random/dungeon-gen.zh" | |
const int LEVEL_WIDTH = 8; | |
const int LEVEL_HEIGHT = 8; | |
const int LEVEL_COUNT = 8; | |
const int SOURCE_MAP = 9; | |
// Script 1 flag - used for key / other items. | |
const int CF_ITEM = 98; | |
int Rooms[512]; | |
int Doors[2048]; | |
int Variants[512]; | |
int StartRoom = 0; | |
void GenerateLevels() { | |
int level1[4]; | |
int msg1[] = "Generating Level 1..."; | |
level1[LEVEL_NUMBER] = 1; | |
TraceS(msg1); | |
DungeonGen_CarveLevel(level1, Rooms, Doors, Variants, StartRoom); | |
int msg2[] = " Done."; | |
TraceS(msg2); TraceNL(); | |
} | |
// Load the levels into the game. | |
void LoadLevels() { | |
for (int level = 0; level < 8; level++) { | |
for (int y = 0; y < 8; y++) { | |
for (int x = 0; x < 8; x++) { | |
LoadRoom(level, x, y);}}}} | |
void CopyLocation(int sourceMap, int sourceScreen, | |
int destMap, int destScreen, int loc) { | |
int combo = Game->GetComboData(sourceMap, sourceScreen, loc); | |
int flag = Game->GetComboFlag(sourceMap, sourceScreen, loc); | |
int cset = Game->GetComboCSet(sourceMap, sourceScreen, loc); | |
Game->SetComboData(destMap, destScreen, loc, combo); | |
Game->SetComboFlag(destMap, destScreen, loc, flag); | |
Game->SetComboCSet(destMap, destScreen, loc, cset);} | |
void LoadRoom(int level, int x, int y) { | |
int map = level + 1; | |
int screen = y * 16 + x; | |
int index = level * 64 + y * 8 + x; | |
// Copy basic room. | |
for (int loc = 0; loc < 176; loc++) { | |
CopyLocation(SOURCE_MAP, 16 + Variants[index], map, screen, loc);} | |
// Copy the up gate (2 rows). | |
for (int loc = 0; loc < 32; loc++) { | |
int source = Doors[index * 4 + DIR_UP]; | |
CopyLocation(SOURCE_MAP, source, map, screen, loc);} | |
// Copy the down gate (2 rows). | |
for (int loc = 144; loc < 176; loc++) { | |
int source = Doors[index * 4 + DIR_DOWN]; | |
CopyLocation(SOURCE_MAP, source, map, screen, loc);} | |
// Copy the left gate (2 columns). | |
for (int i = 0; i < 22; i++) { | |
int source = Doors[index * 4 + DIR_LEFT]; | |
int loc = (i >> 1) * 16 + (i % 2); | |
CopyLocation(SOURCE_MAP, source, map, screen, loc);} | |
// Copy the right gate (2 columns). | |
for (int i = 0; i < 22; i++) { | |
int source = Doors[index * 4 + DIR_RIGHT]; | |
int loc = (i >> 1) * 16 + (i % 2) + 14; | |
CopyLocation(SOURCE_MAP, source, map, screen, loc);}} | |
// Find the first combo on the screen with the given flag. | |
int FindFlag(int flag) { | |
for (int loc = 0; loc < 176; loc++) { | |
if (ComboFI(loc, flag)) { | |
return loc;}} | |
return -1;} | |
// Called when you enter a room. | |
void EnterRoom() { | |
int map = Game->GetCurMap(); | |
int screen = Game->GetCurScreen(); | |
int level = map - 1; | |
int y = screen >> 4; | |
int x = screen & 7; | |
int index = level * 64 + y * 8 + x; | |
// Adjust gates. | |
for (int dir = 0; dir < 4; dir++) { | |
int type = Doors[index * 4 + dir]; | |
if (type == DOOR_WALL) {Screen->Door[dir] = D_WALL;} | |
else if (type == DOOR_OPEN) {Screen->Door[dir] = D_OPEN;} | |
else if (type == DOOR_LOCK) { | |
if (Screen->State[dir]) {Screen->Door[dir] = D_UNLOCKED;} | |
else {Screen->Door[dir] = D_LOCKED;}}} | |
// If this room has a key, add it in. | |
if ((Rooms[index] & ROOM_KEY) && | |
!Screen->State[ST_ITEM]) { | |
int loc = FindFlag(CF_ITEM); | |
if (loc != -1) { | |
item key = Screen->CreateItem(I_LEVELKEY); | |
key->X = ComboX(loc); | |
key->Y = ComboY(loc); | |
key->Pickup |= IP_ST_ITEM;}}} | |
global script Init { | |
void run() { | |
GenerateLevels(); | |
Link->Warp(0, (StartRoom >> 3) * 16 + (StartRoom % 8));}} | |
global script Active { | |
void run() { | |
LoadLevels(); | |
Waitframe(); | |
Link->X = 120; | |
Link->Y = 80; | |
//for (int i = 0; i < 20; i++) { | |
// Trace(Doors[i]);} | |
while (true) { | |
Waitdraw(); | |
ScreenChange_Update(); | |
if (ScreenChanged) { | |
EnterRoom();} | |
Waitframe();}}} | |
// Test for changes in screen. | |
// Last noticed DMap. | |
int LastDMap = -1; | |
// Last noticed DScreen. | |
int LastDScreen = -1; | |
// Last noticed map. | |
int LastMap = -1; | |
// Last noticed screen. | |
int LastScreen = -1; | |
// If the screen has changed. | |
bool ScreenChanged = false; | |
// If the dmap has changed. | |
bool DMapChanged = false; | |
void ScreenChange_Update() { | |
int dMap = Game->GetCurDMap(); | |
int dScreen = Game->GetCurDMapScreen(); | |
int map = Game->GetCurMap(); | |
int screen = Game->GetCurScreen(); | |
DMapChanged = dMap != LastDMap; | |
ScreenChanged = DMapChanged || dScreen != LastDScreen; | |
LastDMap = dMap; | |
LastDScreen = dScreen; | |
//if (LastMap != map || LastScreen != screen) { | |
// Game->SetScreenState(LastMap, LastScreen, ST_VISITED, true);} | |
LastMap = map; | |
LastScreen = screen;} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment