// 02 uncover map // hide everything on the map and uncover it as the player roams // makes 3x3 rooms with 1 gold pile and 1 monster each // lit rooms light up when player walks in // can discover traps using System; using System.Collections.Generic; class Program { static List items_on_floor; static List monsters; static THING player = new THING(); static bool found_amulet = false; static Random random = new Random(); static void Main(string[] args) { Console.WindowWidth = Console.BufferWidth = 80; Console.WindowHeight = Console.BufferHeight = 25; Console.Title = "steelgolem rogue - 02 uncover map"; CONSOLE.clear(); dungeon_level = 1; new_level(); update_status(); while (true) { // put cursor under player to highlight CONSOLE.move_cursor(player.x, player.y); char key_ch = CONSOLE.read_key(); switch (key_ch) { case '?': show_help(); break; case 'h': move_player(-1, 0); break; case 'j': move_player(0, +1); break; case 'k': move_player(0, -1); break; case 'l': move_player(+1, 0); break; case 'y': move_player(-1, -1); break; case 'u': move_player(+1, -1); break; case 'b': move_player(-1, +1); break; case 'n': move_player(+1, +1); break; case '>': if (dungeon_map[player.x, player.y] == '%') { if (found_amulet && dungeon_level == 1) win_game(); if (found_amulet) dungeon_level--; else dungeon_level++; new_level(); update_status(); } else { if (found_amulet) set_message("there is no way up"); else set_message("there is no way down"); } break; case (char)18: // ^R set_message(last_message); break; case 'Q': Environment.Exit(0); break; } } } #region dungeon level static int dungeon_level; static char[,] dungeon_map = new char[80, 25]; static ROOM[,] rooms; const int room_max_w = 80 / 3; const int room_max_h = 23 / 3; static List traps; static void new_level() { items_on_floor = new List(); monsters = new List(); traps = new List(); for (int y = 0; y < 25; y++) for (int x = 0; x < 80; x++) dungeon_map[x, y] = ' '; CONSOLE.clear(); // add 3x3 rooms rooms = new ROOM[3, 3]; for (int ry = 0; ry < 3; ry++) { for (int rx = 0; rx < 3; rx++) { ROOM room = new ROOM(); room.w = 4 + random.Next(room_max_w - 4); room.h = 4 + random.Next(room_max_h - 4); room.x = rx * room_max_w + random.Next(room_max_w - room.w); room.y = 1 + ry * room_max_h + random.Next(room_max_h - room.h); if (random.Next(100) < 50) room.is_lit = true; rooms[rx, ry] = room; room.draw_on_map(); // one door at the bottom dungeon_map[room.x + room.w / 2, room.y + room.h - 1] = '+'; } } // add some random level bits for (int c = 0; c < 80; c++) { int x = random.Next(80); int y = 1 + random.Next(23); char ch = "-|.+#"[random.Next(5)]; dungeon_map[x, y] = ch; } // add some traps for (int t = 0; t < 9; t++) { TRAP trap = new TRAP(); do { ROOM room = get_random_room(); room.set_random_pos(trap); } while (dungeon_map[trap.x, trap.y] != '.'); trap.ch = ">{$}~`"[random.Next(6)]; dungeon_map[trap.x, trap.y] = trap.ch; traps.Add(trap); } // add some gold for (int ry = 0; ry < 3; ry++) { for (int rx = 0; rx < 3; rx++) { ROOM room = rooms[rx, ry]; ITEM item = new ITEM(); do room.set_random_pos(item); while (dungeon_map[item.x, item.y] != '.'); item.ch = '*'; items_on_floor.Add(item); } } // add some items for (int c = 0; c < 9; c++) { ITEM item = new ITEM(); do { ROOM room = get_random_room(); room.set_random_pos(item); } while (dungeon_map[item.x, item.y] != '.'); item.ch = ":!?)]=/"[random.Next(7)]; items_on_floor.Add(item); } // add some monsters for (int ry = 0; ry < 3; ry++) { for (int rx = 0; rx < 3; rx++) { ROOM room = rooms[rx, ry]; THING monster = new THING(); do room.set_random_pos(monster); while (dungeon_map[monster.x, monster.y] != '.'); monster.ch = "ABCDEFGHIJKLMNOPQRSTUVWXYZ"[random.Next(26)]; monsters.Add(monster); } } // add the amulet if (dungeon_level >= 26) { ITEM item = new ITEM(); do { ROOM room = get_random_room(); room.set_random_pos(item); } while (dungeon_map[item.x, item.y] != '.'); item.ch = ','; items_on_floor.Add(item); } // add the stairs int stairs_x = -1, stairs_y = -1; do { ROOM room = get_random_room(); room.set_random_pos(ref stairs_x, ref stairs_y); } while (dungeon_map[stairs_x, stairs_y] != '.'); dungeon_map[stairs_x, stairs_y] = '%'; // place the player do { ROOM room = get_random_room(); room.set_random_pos(player); } while (dungeon_map[player.x, player.y] != '.'); draw_what_player_can_see(); } static ROOM get_random_room() { return rooms[random.Next(3), random.Next(3)]; } class ROOM { public int x, y, w, h; public bool is_lit; public void draw_on_map() { for (int y = 0; y < this.h; y++) for (int x = 0; x < this.w; x++) dungeon_map[this.x + x, this.y + y] = '.'; for (int y = 0; y < this.h; y++) { dungeon_map[this.x, this.y + y] = '|'; dungeon_map[this.x + this.w - 1, this.y + y] = '|'; } for (int x = 0; x < this.w; x++) { dungeon_map[this.x + x, this.y] = '-'; dungeon_map[this.x + x, this.y + this.h - 1] = '-'; } } public void set_random_pos(ref int x, ref int y) { x = this.x + 1 + random.Next(this.w - 2); y = this.y + 1 + random.Next(this.h - 2); } public void set_random_pos(ITEM item) { set_random_pos(ref item.x, ref item.y); } public void set_random_pos(THING thing) { set_random_pos(ref thing.x, ref thing.y); } public void set_random_pos(TRAP trap) { set_random_pos(ref trap.x, ref trap.y); } } class TRAP { public int x, y; public char ch; public bool found; } #endregion // dungeon level #region commands static void show_help() { string[] help_text = new string[] { "? show help", "h move left", "j move down", "k move up", "l move right", "y move up & left", "u move up & right", "b move down & left", "n move down & right", "> use stairs", "^R repeat last message", "Q quit game", }; CONSOLE.back_up_buffer(); CONSOLE.clear(); for (int h = 0; h < help_text.Length; h++) CONSOLE.write_string(0, h, help_text[h]); CONSOLE.write_string(0, 24, "[press any key]"); CONSOLE.read_key(); CONSOLE.restore_buffer(); } static void move_player(int dx, int dy) { clear_message(); int dest_x = player.x + dx; int dest_y = player.y + dy; char map_ch = dungeon_map[dest_x, dest_y]; // walls stop movement if (is_wall(map_ch)) return; // fight monster for (int m = monsters.Count - 1; m >= 0; m--) { THING monster = monsters[m]; if (dest_x == monster.x && dest_y == monster.y) { monsters.Remove(monster); draw_what_player_can_see(); set_message("defeated " + get_name(monster.ch)); return; } } erase_what_player_cant_see(); player.x = dest_x; player.y = dest_y; draw_what_player_can_see(); // pick up item for (int i = items_on_floor.Count - 1; i >= 0; i--) { ITEM item = items_on_floor[i]; if (dest_x == item.x && dest_y == item.y) { if (player.items.Count == 23) { set_message("no room in inventory"); return; } set_message("picked up " + get_name(item.ch)); items_on_floor.Remove(item); player.items.Add(item); if (item.ch == ',') found_amulet = true; } } // get trapped for (int t = 0; t < traps.Count; t++) { TRAP trap = traps[t]; if (dest_x == trap.x && dest_y == trap.y) { trap.found = true; string message = "found "; switch (trap.ch) { case '>': message += "trap door"; break; case '{': message += "arrow trap"; break; case '$': message += "sleep trap"; break; case '}': message += "bear trap"; break; case '~': message += "teleport trap"; break; case '`': message += "dart trap"; break; } set_message(message); } } } static void draw_what_player_can_see() { ROOM room = null; // without this line, we redraw the room and // everything in it every time we move // (is this actually perfectly fine?) //if (dungeon_map[player.x, player.y] == '+') room = get_room_at(player.x, player.y); if (room != null && room.is_lit) { for (int ry = 0; ry < room.h; ry++) { for (int rx = 0; rx < room.w; rx++) { int x = room.x + rx; int y = room.y + ry; if (">{$}~`".Contains(dungeon_map[x, y].ToString())) { TRAP trap = get_trap_at(x, y); if (trap.found) CONSOLE.put_char(x, y, '^'); else CONSOLE.put_char(x, y, '.'); } else CONSOLE.put_char(x, y, dungeon_map[x, y]); } } for (int i = 0; i < items_on_floor.Count; i++) { ITEM item = items_on_floor[i]; if (get_room_at(item.x, item.y) == room) CONSOLE.put_char(item.x, item.y, item.ch); } for (int m = 0; m < monsters.Count; m++) { THING monster = monsters[m]; if (get_room_at(monster.x, monster.y) == room) CONSOLE.put_char(monster.x, monster.y, monster.ch); } CONSOLE.put_char(player.x, player.y, '@'); } // 3x3 area around player for (int dy = -1; dy <= +1; dy++) { for (int dx = -1; dx <= +1; dx++) { int x = player.x + dx; int y = player.y + dy; if (x < 0 || x >= 80 || y < 0 || y >= 25) continue; CONSOLE.put_char(x, y, dungeon_map[x, y]); if (">{$}~`".Contains(dungeon_map[x, y].ToString())) { TRAP trap = get_trap_at(x, y); if (trap.found) CONSOLE.put_char(x, y, '^'); else CONSOLE.put_char(x, y, '.'); } for (int i = 0; i < items_on_floor.Count; i++) if (x == items_on_floor[i].x && y == items_on_floor[i].y) CONSOLE.put_char(x, y, items_on_floor[i].ch); for (int m = 0; m < monsters.Count; m++) if (x == monsters[m].x && y == monsters[m].y) CONSOLE.put_char(x, y, monsters[m].ch); } } CONSOLE.put_char(player.x, player.y, '@'); } static void erase_what_player_cant_see() { ROOM room = get_room_at(player.x, player.y); if (room != null && room.is_lit) return; // 3x3 area around player for (int dy = -1; dy <= +1; dy++) { for (int dx = -1; dx <= +1; dx++) { int x = player.x + dx; int y = player.y + dy; if (x < 0 || x >= 80 || y < 0 || y >= 25) continue; char ch = CONSOLE.get_char(x, y); if (!"-|+#%^".Contains(ch.ToString())) CONSOLE.put_char(x, y, ' '); } } } static ROOM get_room_at(int x, int y) { for (int ry = 0; ry < 3; ry++) { for (int rx = 0; rx < 3; rx++) { ROOM room = rooms[rx, ry]; if (x >= room.x && x < room.x + room.w && y >= room.y && y < room.y + room.h) return rooms[rx, ry]; } } return null; } static TRAP get_trap_at(int x, int y) { for (int t = 0; t < traps.Count; t++) if (x == traps[t].x && y == traps[t].y) return traps[t]; return null; } #endregion // commands #region helper functions static bool is_wall(char ch) { return "-|".Contains(ch.ToString()); } static string get_name(char dest_ch) { switch (dest_ch) { case 'A': return "giant ant"; case 'B': return "bat"; case 'C': return "centaur"; case 'D': return "dragon"; case 'E': return "floating eye"; case 'F': return "violet fungi"; case 'G': return "gnome"; case 'H': return "hobgoblin"; case 'I': return "invisible stalker"; case 'J': return "jackal"; case 'K': return "kobold"; case 'L': return "leprechaun"; case 'M': return "mimic"; case 'N': return "nymph"; case 'O': return "orc"; case 'P': return "purple worm"; case 'Q': return "quasit"; case 'R': return "rust monster"; case 'S': return "snake"; case 'T': return "troll"; case 'U': return "umber hulk"; case 'V': return "vampire"; case 'W': return "wraith"; case 'X': return "xorn"; case 'Y': return "yeti"; case 'Z': return "zombie"; case '*': return "gold"; case ':': return "food"; case '!': return "potion"; case '?': return "scroll"; case ')': return "weapon"; case ']': return "armor"; case '=': return "ring"; case '/': return "stick"; case ',': return "the amulet of yendor"; default: return null; } } static void update_status() { clear_status(); CONSOLE.write_string(0, 24, "level: " + dungeon_level); } #endregion // helper functions #region messages static string last_message = ""; static void set_message(string text) { CONSOLE.write_string(0, 0, text); last_message = text; } static void clear_message() { CONSOLE.cursor_off(); CONSOLE.write_string(0, 0, "".PadRight(80)); CONSOLE.cursor_on(); } static void clear_status() { CONSOLE.write_string(0, 24, "".PadRight(79)); } #endregion // messages #region game over static void win_game() { CONSOLE.clear(); CONSOLE.write_string("you win"); CONSOLE.read_key(); Environment.Exit(0); } #endregion // game over // THING must be in Program for access to dungeon_map[] class THING { public int x, y; public char ch; public List items = new List(); } } class ITEM { public int x; public int y; public char ch; } class CONSOLE { static char[,] buffer = new char[80, 25]; static int cursor_x, cursor_y; public static char get_char(int x, int y) { return buffer[x, y]; } public static void put_char(char ch) { buffer[cursor_x, cursor_y] = ch; cursor_x++; if (cursor_x == 80) { cursor_x = 0; cursor_y++; } Console.Write(ch); } public static void put_char(int x, int y, char ch) { move_cursor(x, y); put_char(ch); } public static void move_cursor(int x, int y) { cursor_x = x; cursor_y = y; Console.SetCursorPosition(x, y); } public static char read_key() { return Console.ReadKey(true).KeyChar; } public static void write_string(string text) { for (int c = 0; c < text.Length; c++) put_char(text[c]); } public static void write_string(int x, int y, string text) { move_cursor(x, y); write_string(text); } public static void cursor_on() { Console.CursorVisible = true; } public static void cursor_off() { Console.CursorVisible = false; } public static void clear() { for (int y = 0; y < 25; y++) for (int x = 0; x < 80; x++) buffer[x, y] = ' '; cursor_x = cursor_y = 0; Console.Clear(); } static char[,] old_buffer = new char[80, 25]; public static void back_up_buffer() { Array.Copy(CONSOLE.buffer, old_buffer, 80 * 25); } public static void restore_buffer() { Array.Copy(old_buffer, CONSOLE.buffer, 80 * 25); for (int y = 0; y < 25; y++) for (int x = 0; x < 80; x++) if (!(x == 79 && y == 24)) CONSOLE.put_char(x, y, CONSOLE.buffer[x, y]); } }