// 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<ITEM> items_on_floor;
static List<THING> 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<TRAP> traps;
static void new_level()
{
items_on_floor = new List<ITEM>();
monsters = new List<THING>();
traps = new List<TRAP>();
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<ITEM> items = new List<ITEM>();
}
}
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]);
}
}
Comments