290 lines
9.5 KiB
C
290 lines
9.5 KiB
C
#include <unistd.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <termios.h>
|
|
|
|
#include <sys/ioctl.h>
|
|
#include <sys/types.h>
|
|
#include <sys/stat.h>
|
|
|
|
#define level_width_limit (128)
|
|
#define level_height_limit ( 64)
|
|
#define members ( 6)
|
|
#define items ( 36)
|
|
|
|
#define random(from, to) (rand () % (to - from + 1) + from)
|
|
#define minimum(a, b) ((a < b) ? a : b)
|
|
#define maximum(a, b) ((a > b) ? a : b)
|
|
#define clamp(x, a, b) ((x < a) ? a : (x > b) ? b : x)
|
|
|
|
enum { normal = '0', bold = '1', italic = '2', blink = '5', reverse = '7' };
|
|
|
|
enum { grey = '0', red = '1', green = '2', yellow = '3', blue = '4', pink = '5', cyan = '6', white = '7' };
|
|
|
|
enum { cut = 1, blunt = 2, pierce = 4, range = 8, magic = 16 };
|
|
|
|
enum { health, mana, stamina, experience, points };
|
|
|
|
enum { strength, dexterity, agility, intelligence, attributes };
|
|
|
|
enum { fighting, blocking, shooting, spellcasting, evoking, trading, healing, lockpicking, skills };
|
|
|
|
enum { warrior, mage, thief, priest, archer, alchemist, warlock, merchant, classes };
|
|
|
|
static struct {
|
|
char clip, style, colour, symbol;
|
|
} const tiles [] = {
|
|
{ 1, normal, white, ' ' },
|
|
{ 0, bold, grey, '.' },
|
|
{ 1, bold, grey, '#' }
|
|
};
|
|
|
|
static struct {
|
|
char * name, damage, condition, value, spell, type, style, colour, symbol;
|
|
} const weapons [] = {
|
|
{ "Bone Dagger", 1, 11, 5, 0, cut, bold, yellow, '!' },
|
|
{ "Iron Dagger", 1, 19, 7, 0, cut, bold, yellow, '!' },
|
|
{ "Steel Dagger", 1, 23, 11, 0, cut, bold, yellow, '!' },
|
|
{ "Silver Dagger", 2, 29, 13, 0, cut, bold, yellow, '!' },
|
|
{ "Bone Sword", 3, 23, 11, 0, cut, bold, yellow, '!' },
|
|
{ "Iron Sword", 5, 31, 29, 0, cut, bold, yellow, '!' },
|
|
{ "Steel Sword", 5, 37, 31, 0, cut, bold, yellow, '!' },
|
|
{ "Silver Sword", 7, 43, 37, 0, cut, bold, yellow, '!' },
|
|
{ "Bone Greatsword", 7, 41, 23, 0, blunt | cut, bold, yellow, '!' },
|
|
{ "Iron Greatsword", 11, 47, 71, 0, blunt | cut, bold, yellow, '!' },
|
|
{ "Steel Greatsword", 13, 53, 73, 0, blunt | cut, bold, yellow, '!' },
|
|
{ "Silver Greatsword", 17, 59, 79, 0, blunt | cut, bold, yellow, '!' }
|
|
};
|
|
|
|
static struct {
|
|
char * name, resistance, condition, value, spell, type, style, colour, symbol;
|
|
} const armours [] = {
|
|
{ "Wanderer Robe", 0, 11, 11, 0, cut, bold, green, ']' },
|
|
{ "Priest Robe", 0, 13, 7, 0, cut, bold, green, ']' },
|
|
{ "Commoner Clothes", 0, 5, 5, 0, blunt, bold, green, ']' },
|
|
{ "Nobleman Clothes", 0, 7, 31, 0, magic, bold, green, ']' },
|
|
{ "Assassin Clothes", 1, 11, 13, 0, blunt, bold, green, ']' },
|
|
{ "Adventurer Clothes", 1, 13, 17, 0, cut, bold, green, ']' },
|
|
{ "Thief Clothes", 1, 11, 11, 0, blunt, bold, green, ']' },
|
|
{ "Merchant Clothes", 1, 11, 13, 0, pierce, bold, green, ']' },
|
|
{ "Fur Armour", 2, 19, 23, 0, blunt | pierce, bold, green, ']' },
|
|
{ "Leather Armour", 2, 23, 31, 0, blunt, bold, green, ']' },
|
|
{ "Chainmail Armour", 3, 31, 41, 0, blunt | cut, bold, green, ']' },
|
|
{ "Mithril Armour", 7, 83, 97, 0, blunt | cut | pierce, bold, green, ']' },
|
|
{ "Bone Armour", 3, 29, 37, 0, blunt | pierce, bold, green, ']' },
|
|
{ "Iron Armour", 5, 53, 61, 0, blunt | cut, bold, green, ']' },
|
|
{ "Steel Armour", 5, 67, 71, 0, blunt | cut, bold, green, ']' },
|
|
{ "Silver Armour", 5, 71, 89, 0, blunt | cut | pierce, bold, green, ']' }
|
|
};
|
|
|
|
static struct {
|
|
char x, y, coins, inventory [items], weapon [members], armour [members], point [members] [points], attribute [members] [attributes], skill [members], class [members];
|
|
} player;
|
|
|
|
static struct termios old_terminal;
|
|
static struct termios new_terminal;
|
|
|
|
static int signal;
|
|
static int screen_width;
|
|
static int screen_height;
|
|
static char * screen;
|
|
static int level_width;
|
|
static int level_height;
|
|
static char * level;
|
|
|
|
static void screen_put (char style, char colour, char symbol, int x, int y) {
|
|
char format [] = "\033[ ;3 m \033[0m";
|
|
|
|
format [2] = style;
|
|
format [5] = colour;
|
|
format [7] = symbol;
|
|
|
|
strncpy (& screen [(y * screen_width + x) * 12 + 3], format, sizeof (format) - 1);
|
|
}
|
|
|
|
static void screen_print (char style, char colour, char * text, int x, int y, int limit) {
|
|
int i;
|
|
|
|
for (i = 0; (text [i] != '\0') && (i < limit); ++i) {
|
|
screen_put (style, colour, text [i], x + i, y);
|
|
}
|
|
}
|
|
|
|
static void initialize_screen (void) {
|
|
struct winsize screen_dimension;
|
|
|
|
ioctl (STDOUT_FILENO, TIOCGWINSZ, & screen_dimension);
|
|
|
|
screen_width = (int) screen_dimension.ws_col;
|
|
screen_height = (int) screen_dimension.ws_row;
|
|
|
|
tcgetattr (STDIN_FILENO, & old_terminal);
|
|
|
|
new_terminal = old_terminal;
|
|
|
|
new_terminal.c_cc [VMIN] = (unsigned char) 0;
|
|
new_terminal.c_cc [VTIME] = (unsigned char) 1;
|
|
|
|
new_terminal.c_iflag &= (unsigned int) ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
|
|
new_terminal.c_oflag &= (unsigned int) ~(OPOST);
|
|
new_terminal.c_cflag |= (unsigned int) (CS8);
|
|
new_terminal.c_lflag &= (unsigned int) ~(ECHO | ICANON | IEXTEN | ISIG);
|
|
|
|
tcsetattr (STDIN_FILENO, TCSAFLUSH, & new_terminal);
|
|
|
|
screen = calloc ((unsigned long int) (12 * screen_width * screen_height + 4), sizeof (* screen));
|
|
|
|
strcpy (screen, "\033[H");
|
|
}
|
|
|
|
static void deinitialize_screen (void) {
|
|
free (screen);
|
|
|
|
write (STDOUT_FILENO, "\033[2J\033[H", 7);
|
|
|
|
tcsetattr (STDIN_FILENO, TCSAFLUSH, & old_terminal);
|
|
}
|
|
|
|
static void synchronize_screen (void) {
|
|
int new_screen_width, new_screen_height;
|
|
|
|
struct winsize screen_dimension;
|
|
|
|
ioctl (STDOUT_FILENO, TIOCGWINSZ, & screen_dimension);
|
|
|
|
new_screen_width = (int) screen_dimension.ws_col;
|
|
new_screen_height = (int) screen_dimension.ws_row;
|
|
|
|
if ((screen_width != new_screen_width) || (screen_height != new_screen_height)) {
|
|
screen_width = new_screen_width;
|
|
screen_height = new_screen_height;
|
|
free (screen);
|
|
screen = calloc ((unsigned long int) (12 * screen_width * screen_height + 4), sizeof (* screen));
|
|
}
|
|
|
|
write (STDOUT_FILENO, screen, (unsigned long int) (12 * screen_width * screen_height + 4));
|
|
|
|
signal = 0;
|
|
|
|
read (STDIN_FILENO, & signal, sizeof (signal));
|
|
|
|
switch (signal) {
|
|
case 'k': { player.y -= 1; } break;
|
|
case 'j': { player.y += 1; } break;
|
|
case 'h': { player.x -= 1; } break;
|
|
case 'l': { player.x += 1; } break;
|
|
case 'y': { player.x -= 1; player.y -= 1; } break;
|
|
case 'u': { player.x += 1; player.y -= 1; } break;
|
|
case 'b': { player.x -= 1; player.y += 1; } break;
|
|
case 'n': { player.x += 1; player.y += 1; } break;
|
|
default: break;
|
|
}
|
|
|
|
player.x = clamp (player.x, 0, (char) level_width);
|
|
player.y = clamp (player.y, 0, (char) level_height);
|
|
}
|
|
|
|
static void generate_level (void) {
|
|
int room, i, j, x, y, width, height;
|
|
|
|
level_width = random (level_width_limit / 2, level_width_limit);
|
|
level_height = random (level_height_limit / 2, level_height_limit);
|
|
|
|
level = calloc ((unsigned long int) (level_width * level_height), sizeof (* level));
|
|
|
|
for (room = random (6, 18); room > 1; --room) {
|
|
width = random (5, level_width / 2);
|
|
height = random (5, level_height / 2);
|
|
x = random (1, level_width - width - 1);
|
|
y = random (1, level_height - height - 1);
|
|
for (i = 1; i < height - 1; ++i) {
|
|
for (j = 1; j < width - 1; ++j) {
|
|
level [(i + y) * level_width + (j + x)] = 1;
|
|
}
|
|
}
|
|
for (i = 0; i < height; ++i) {
|
|
level [(i + y) * level_width + x] = 2;
|
|
level [(i + y) * level_width + x + width - 1] = 2;
|
|
}
|
|
for (j = 0; j < width; ++j) {
|
|
level [y * level_width + j + x] = 2;
|
|
level [(y + height - 1) * level_width + j + x] = 2;
|
|
}
|
|
}
|
|
|
|
for (i = 1; i < level_height - 1; ++i) {
|
|
for (j = 1; j < level_width - 1; ++j) {
|
|
if (((level [i * level_width + (j - 1)] == 1) && (level [i * level_width + j] == 2) && (level [i * level_width + (j + 1)] == 1)) ||
|
|
((level [(i - 1) * level_width + j] == 1) && (level [i * level_width + j] == 2) && (level [(i + 1) * level_width + j] == 1))) {
|
|
level [i * level_width + j] = 1;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void generate_party (void) {
|
|
int i, j;
|
|
|
|
player.coins = 0;
|
|
|
|
for (i = 0; i < items; ++i) {
|
|
player.inventory [i] = 0;
|
|
}
|
|
|
|
for (i = 0; i < members; ++i) {
|
|
player.weapon [i] = random (0, 3);
|
|
player.armour [i] = random (0, 3);
|
|
for (j = 0; j < points; ++j) {
|
|
player.point [i] [j] = random (12, 24);
|
|
}
|
|
for (j = 0; j < attributes; ++j) {
|
|
player.attribute [i] [j] = random (1, 4);
|
|
}
|
|
player.skill [i] = rand () % skills;
|
|
player.class [i] = rand () % classes;
|
|
}
|
|
|
|
do {
|
|
player.x = (char) (rand () % level_width);
|
|
player.y = (char) (rand () % level_height);
|
|
} while (level [player.y * level_width + player.x] != 1);
|
|
}
|
|
|
|
int main (void) {
|
|
int i, x, y;
|
|
|
|
srand (time (0));
|
|
|
|
initialize_screen ();
|
|
|
|
generate_level ();
|
|
generate_party ();
|
|
|
|
while (signal != 'Q') {
|
|
for (y = 0; y < screen_height; ++y) {
|
|
for (x = 0; x < screen_width; ++x) {
|
|
screen_put (tiles [0].style, tiles [0].colour, tiles [0].symbol, x, y);
|
|
}
|
|
}
|
|
|
|
for (y = 0; (y < screen_height) && (y + player.y < level_height - 1); ++y) {
|
|
for (x = 0; (x < screen_width) && (x + player.x < level_width - 1); ++x) {
|
|
i = (int) level [(y + player.y) * level_width + (x + player.x)];
|
|
screen_put (tiles [i].style, tiles [i].colour, tiles [i].symbol, x, y);
|
|
}
|
|
}
|
|
|
|
screen_put (bold, cyan, '@', screen_width / 2, screen_height / 2);
|
|
|
|
screen_print (bold, grey, "Warrior", screen_width - 24, 0, 24);
|
|
|
|
synchronize_screen ();
|
|
}
|
|
|
|
free (level);
|
|
|
|
deinitialize_screen ();
|
|
|
|
return (0);
|
|
}
|