From: Christian Heller Date: Wed, 19 Jun 2013 03:22:21 +0000 (+0200) Subject: Restructured source tree. Code moves to src/, object files to build/. X-Git-Tag: tce~1212 X-Git-Url: https://plomlompom.com/repos/blog?a=commitdiff_plain;h=e9d8b1aca776341c9cdaa2ea6406336661d82a76;p=plomrogue Restructured source tree. Code moves to src/, object files to build/. --- diff --git a/Makefile b/Makefile index bedecc0..69eb0a9 100644 --- a/Makefile +++ b/Makefile @@ -1,15 +1,19 @@ CC=cc CFLAGS=-Wall -g TARGET=roguelike -SOURCES=$(shell find . -type f -name \*.c) -OBJECTS=$(SOURCES:.c=.o) +SRCDIR=src +BUILDDIR=build +SOURCES=$(shell find $(SRCDIR)/ -type f -name \*.c) +OBJECTS=$(patsubst $(SRCDIR)/%,$(BUILDDIR)/%,$(SOURCES:.c=.o)) roguelike: $(OBJECTS) $(CC) $(CFLAGS) -o roguelike $(OBJECTS) -lncurses -%.o: %.c - $(CC) $(CFLAGS) -c $< -o $@ +$(BUILDDIR)/%.o: $(SRCDIR)/%.c + mkdir -p $(BUILDDIR); $(CC) $(CFLAGS) -c $< -o $@ .PHONY: clean clean: - rm $(OBJECTS); rm roguelike + rm $(OBJECTS) + rmdir $(BUILDDIR) + rm $(TARGET) diff --git a/draw_wins.c b/draw_wins.c deleted file mode 100644 index 818037a..0000000 --- a/draw_wins.c +++ /dev/null @@ -1,138 +0,0 @@ -#include -#include -#include -#include -#include "windows.h" -#include "draw_wins.h" -#include "roguelike.h" -#include "keybindings.h" - -void draw_with_linebreaks (struct Win * win, char * text, uint16_t start_y) { -// Write text into window content space. Start on row start_y. Fill unused rows with whitespace. - uint16_t x, y; - char toggle; - char fin = 0; - int16_t z = -1; - for (y = start_y; y < win->frame.size.y; y++) { - if (0 == fin) - toggle = 0; - for (x = 0; x < win->frame.size.x; x++) { - if (0 == toggle) { - z++; - if ('\n' == text[z]) { - toggle = 1; - continue; } - else - mvwaddch(win->frame.curses_win, y, x, text[z]); - if ('\n' == text[z+1]) { - z++; - toggle = 1; } - else if (0 == text[z+1]) { - toggle = 1; - fin = 1; } } } } } - -void draw_text_from_bottom (struct Win * win, char * text) { -// Draw text from end/bottom to the top. - char toggle = 0; - uint16_t x, y, offset; - int16_t z = -1; - for (y = 0; 0 == toggle; y++) // Determine number of lines text would have in - for (x = 0; x < win->frame.size.x; x++) { // a window of available width, but infinite height. - z++; - if ('\n' == text[z]) // Treat \n and \0 as control characters for incrementing y and stopping - break; // the loop. Make sure they don't count as cell space themselves. - if ('\n' == text[z+1]) { - z++; - break; } - else if (0 == text[z+1]) { - toggle = 1; - break; } } - z = -1; - uint16_t start_y = 0; - if (y < win->frame.size.y) // Depending on what is bigger, determine start point in window or in text. - start_y = win->frame.size.y - y; - else if (y > win->frame.size.y) { - offset = y - win->frame.size.y; - for (y = 0; y < offset; y++) - for (x = 0; x < win->frame.size.x; x++) { - z++; - if ('\n' == text[z]) - break; - if ('\n' == text[z+1]) { - z++; - break; } } - text = text + (sizeof(char) * (z + 1)); } - draw_with_linebreaks(win, text, start_y); } - -void draw_log_win (struct Win * win) { -// Draw log text from world struct in win->data from bottom to top. - struct World * world = (struct World *) win->data; - draw_text_from_bottom(win, world->log); } - -void draw_map_win (struct Win * win) { -// Draw map determined by win->data Map struct into window. Respect offset. - struct World * world = (struct World *) win->data; - struct Map * map = world->map; - struct Player * player = world->player; - struct Monster * monster = world->monster; - char * cells = map->cells; - uint16_t width_map_av = map->width - map->offset_x; - uint16_t height_map_av = map->height - map->offset_y; - uint16_t x, y, z; - for (y = 0; y < win->frame.size.y; y++) { - z = map->offset_x + (map->offset_y + y) * (map->width); - for (x = 0; x < win->frame.size.x; x++) { - if (y < height_map_av && x < width_map_av) { - if (z == (map->width * player->y) + player->x) - mvwaddch(win->frame.curses_win, y, x, '@'); - else if (z == (map->width * monster->y) + monster->x) - mvwaddch(win->frame.curses_win, y, x, 'M'); - else - mvwaddch(win->frame.curses_win, y, x, cells[z]); - z++; } } } } - -void draw_info_win (struct Win * win) { -// Draw info window by appending win->data integer value to "Turn: " display. - struct World * world = (struct World *) win->data; - uint16_t count = world->turn; - char text[100]; - snprintf(text, 100, "Turn: %d", count); - draw_with_linebreaks(win, text, 0); } - -void draw_keys_win (struct Win * win) { -// Draw keybindings window. - struct World * world = (struct World *) win->data; - uint16_t offset = 0, y, x; - if (world->keyswindata->max >= win->frame.size.y) { - if (world->keyswindata->select > win->frame.size.y / 2) { - if (world->keyswindata->select < (world->keyswindata->max - (win->frame.size.y / 2))) - offset = world->keyswindata->select - (win->frame.size.y / 2); - else - offset = world->keyswindata->max - win->frame.size.y + 1; } } - uint8_t keydescwidth = 9 + 1; // max length assured by get_keyname() + \0 - char * keydesc = malloc(keydescwidth), * keyname; - attr_t attri; - for (y = 0; y <= world->keyswindata->max && y < win->frame.size.y; y++) { - if (0 == y && offset > 0) { - draw_scroll_hint(&win->frame, y, offset + 1, '^'); - continue; } - else if (win->frame.size.y == y + 1 && 0 < world->keyswindata->max - (win->frame.size.y + offset - 1)) { - draw_scroll_hint(&win->frame, y, world->keyswindata->max - (offset + win->frame.size.y) + 2, 'v'); - continue; } - attri = 0; - if (y == world->keyswindata->select - offset) { - attri = A_REVERSE; - if (1 == world->keyswindata->edit) - attri = attri | A_BLINK; } - keyname = get_keyname(world->keybindings[y + offset].key); - snprintf(keydesc, keydescwidth, "%-9s", keyname); - free(keyname); - for (x = 0; x < win->frame.size.x; x++) - if (x < strlen(keydesc)) - mvwaddch(win->frame.curses_win, y, x, keydesc[x] | attri); - else if (strlen(keydesc) < x && x < strlen(world->keybindings[y + offset].name) + strlen(keydesc) + 1) - mvwaddch(win->frame.curses_win, y, x, - world->keybindings[y + offset].name[x - strlen(keydesc) - 1] | attri); - else - mvwaddch(win->frame.curses_win, y, x, ' ' | attri); } - free(keydesc); } diff --git a/draw_wins.h b/draw_wins.h deleted file mode 100644 index 64f332c..0000000 --- a/draw_wins.h +++ /dev/null @@ -1,6 +0,0 @@ -void draw_with_linebreaks (struct Win *, char *, uint16_t); -void draw_text_from_bottom (struct Win *, char *); -void draw_log_win (struct Win *); -void draw_map_win (struct Win *); -void draw_info_win (struct Win *); -void draw_keys_win (struct Win *); diff --git a/keybindings.c b/keybindings.c deleted file mode 100644 index b6ff412..0000000 --- a/keybindings.c +++ /dev/null @@ -1,127 +0,0 @@ -#include -#include -#include -#include -#include "windows.h" -#include "roguelike.h" -#include "keybindings.h" - -void init_keybindings(struct World * world) { -// Initialize keybindings from file "keybindings". - FILE * file = fopen("keybindings", "r"); - uint16_t lines = 0; - int c = 0; - uint16_t linemax = 0; - uint16_t c_count = 0; - while (EOF != c) { - c_count++; - c = getc(file); - if ('\n' == c) { - if (c_count > linemax) - linemax = c_count + 1; - c_count = 0; - lines++; } } - struct KeyBinding * keybindings = malloc(lines * sizeof(struct KeyBinding)); - fseek(file, 0, SEEK_SET); - char * command = malloc(linemax); - uint16_t commcount = 0; - char * cmdptr; - while (fgets(command, linemax, file)) { - keybindings[commcount].key = atoi(command); - cmdptr = strchr(command, ' ') + 1; - keybindings[commcount].name = malloc(strlen(cmdptr)); - memcpy(keybindings[commcount].name, cmdptr, strlen(cmdptr) - 1); - keybindings[commcount].name[strlen(cmdptr) - 1] = '\0'; - commcount++; } - free(command); - fclose(file); - struct KeysWinData * keyswindata = malloc(sizeof(struct KeysWinData)); - keyswindata->max = lines - 1; - keyswindata->select = 0; - keyswindata->edit = 0; - world->keybindings = keybindings; - world->keyswindata = keyswindata; } - -void save_keybindings(struct World * world) { -// Write keybindings to keybindings file. - struct KeysWinData * keyswindata = (struct KeysWinData *) world->keyswindata; - struct KeyBinding * keybindings = world->keybindings; - FILE * file = fopen("keybindings", "w"); - uint16_t linemax = 0; - uint16_t i; - for (i = 0; i <= keyswindata->max; i++) - if (strlen(keybindings[i].name) > linemax) - linemax = strlen(keybindings[i].name); - linemax = linemax + 6; // + 6 = + 3 digits + whitespace + newline + null byte - char * line = malloc(linemax); - for (i = 0; i <= keyswindata->max; i++) { - snprintf(line, linemax, "%d %s\n", keybindings[i].key, keybindings[i].name); - fwrite(line, sizeof(char), strlen(line), file); } - free(line); - fclose(file); } - -uint16_t get_action_key (struct KeyBinding * keybindings, char * name) { -// Return key matching name in keybindings. - uint16_t i = 0; - while (strcmp(keybindings[i].name, name) ) - i++; - return keybindings[i].key; } - -char * get_keyname(uint16_t keycode) { -// Translate some keycodes to readable names of max 9 chars. - char * keyname; - keyname = malloc(15); - if (32 < keycode && keycode < 127) - sprintf(keyname, "%c", keycode); - else if (keycode == 9) - sprintf(keyname, "TAB"); - else if (keycode == 10) - sprintf(keyname, "RETURN"); - else if (keycode == 27) - sprintf(keyname, "ESCAPE"); - else if (keycode == 32) - sprintf(keyname, "SPACE"); - else if (keycode == KEY_UP) - sprintf(keyname, "UP"); - else if (keycode == KEY_DOWN) - sprintf(keyname, "DOWN"); - else if (keycode == KEY_LEFT) - sprintf(keyname, "LEFT"); - else if (keycode == KEY_RIGHT) - sprintf(keyname, "RIGHT"); - else if (keycode == KEY_HOME) - sprintf(keyname, "HOME"); - else if (keycode == KEY_BACKSPACE) - sprintf(keyname, "BACKSPACE"); - else if (keycode >= KEY_F0 && keycode <= KEY_F(63)) { - uint16_t f = keycode - KEY_F0; - sprintf(keyname, "F%d", f); } - else if (keycode == KEY_DC) - sprintf(keyname, "DELETE"); - else if (keycode == KEY_IC) - sprintf(keyname, "INSERT"); - else if (keycode == KEY_NPAGE) - sprintf(keyname, "NEXT PAGE"); - else if (keycode == KEY_PPAGE) - sprintf(keyname, "PREV PAGE"); - else if (keycode == KEY_END) - sprintf(keyname, "END"); - else - sprintf(keyname, "(unknown)"); - return keyname; } - -void keyswin_mod_key (struct World * world, struct WinMeta * win_meta) { -// In keybindings window, mark selection modifiable, modify key. Ensure max of three digits in key code field. - world->keyswindata->edit = 1; - draw_all_wins (win_meta); - int key = getch(); - if (key < 1000) - world->keybindings[world->keyswindata->select].key = key; - world->keyswindata->edit = 0; } - -void keyswin_move_selection (struct World * world, char dir) { -// In keybindings window, move selection upwards or downwards (if within limits of list length). - if ('u' == dir && world->keyswindata->select > 0) - world->keyswindata->select--; - else if ('d' == dir && world->keyswindata->select < world->keyswindata->max) - world->keyswindata->select++; } diff --git a/keybindings.h b/keybindings.h deleted file mode 100644 index b0ff912..0000000 --- a/keybindings.h +++ /dev/null @@ -1,15 +0,0 @@ -struct KeyBinding { - char * name; - uint16_t key; }; - -struct KeysWinData { - uint16_t max; - char edit; - uint16_t select; }; - -void init_keybindings(struct World *); -void save_keybindings(struct World *); -uint16_t get_action_key (struct KeyBinding *, char *); -char * get_keyname(uint16_t); -void keyswin_mod_key (struct World *, struct WinMeta *); -void keyswin_move_selection (struct World *, char); diff --git a/readwrite.c b/readwrite.c deleted file mode 100644 index ed5a3a3..0000000 --- a/readwrite.c +++ /dev/null @@ -1,40 +0,0 @@ -#include -#include -#include - -uint16_t read_uint16_bigendian(FILE * file) { -// Read uint16 from file in big-endian order. - const uint16_t nchar = UCHAR_MAX + 1; - unsigned char a = fgetc(file); - unsigned char b = fgetc(file); - return (a * nchar) + b; } - -void write_uint16_bigendian(uint16_t x, FILE * file) { -// Write uint16 to file in beg-endian order. - const uint16_t nchar = UCHAR_MAX + 1; - unsigned char a = x / nchar; - unsigned char b = x % nchar; - fputc(a, file); - fputc(b, file); } - -uint32_t read_uint32_bigendian(FILE * file) { -// Read uint32 from file in big-endian order. - const uint16_t nchar = UCHAR_MAX + 1; - unsigned char a = fgetc(file); - unsigned char b = fgetc(file); - unsigned char c = fgetc(file); - unsigned char d = fgetc(file); - return (a * nchar * nchar * nchar) + (b * nchar * nchar) + (c * nchar) + d; } - -void write_uint32_bigendian(uint32_t x, FILE * file) { -// Write uint32 to file in beg-endian order. - const uint16_t nchar = UCHAR_MAX + 1; - unsigned char a = x / (nchar * nchar * nchar); - unsigned char b = (x - (a * nchar * nchar * nchar)) / (nchar * nchar); - unsigned char c = (x - ((a * nchar * nchar * nchar) + (b * nchar * nchar))) / nchar; - unsigned char d = x % nchar; - fputc(a, file); - fputc(b, file); - fputc(c, file); - fputc(d, file); } - diff --git a/readwrite.h b/readwrite.h deleted file mode 100644 index 28bc3ab..0000000 --- a/readwrite.h +++ /dev/null @@ -1,5 +0,0 @@ -uint16_t read_uint16_bigendian(FILE * file); -void write_uint16_bigendian(uint16_t x, FILE * file); -uint32_t read_uint32_bigendian(FILE * file); -void write_uint32_bigendian(uint32_t x, FILE * file); - diff --git a/roguelike.c b/roguelike.c deleted file mode 100644 index f0057e2..0000000 --- a/roguelike.c +++ /dev/null @@ -1,383 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include "windows.h" -#include "draw_wins.h" -#include "roguelike.h" -#include "keybindings.h" -#include "readwrite.h" - -uint16_t rrand(char use_seed, uint32_t new_seed) { -// Pseudo-random number generator (LGC algorithm). Use instead of rand() to ensure portable predictability. - static uint32_t seed; - if (0 != use_seed) - seed = new_seed; - seed = ((seed * 1103515245) + 12345) % 2147483648; // Values as recommended by POSIX.1-2001 (see rand(3)). - return (seed / 65536); } // Ignore least significant 16 bits (they are less random). - -void save_game(struct World * world) { -// Save game data to game file. - FILE * file = fopen("savefile", "w"); - write_uint32_bigendian(world->seed, file); - write_uint32_bigendian(world->turn, file); - write_uint16_bigendian(world->player->y, file); - write_uint16_bigendian(world->player->x, file); - write_uint16_bigendian(world->monster->y, file); - write_uint16_bigendian(world->monster->x, file); - fclose(file); } - -void toggle_window (struct WinMeta * win_meta, struct Win * win) { -// Toggle display of window win. - if (0 != win->frame.curses_win) - suspend_win(win_meta, win); - else - append_win(win_meta, win); } - -void scroll_pad (struct WinMeta * win_meta, char dir) { -// Try to scroll pad left or right. - if ('+' == dir) - reset_pad_offset(win_meta, win_meta->pad_offset + 1); - else if ('-' == dir) - reset_pad_offset(win_meta, win_meta->pad_offset - 1); } - -void growshrink_active_window (struct WinMeta * win_meta, char change) { -// Grow or shrink active window horizontally or vertically by one cell size. - if (0 != win_meta->active) { - uint16_t height = win_meta->active->frame.size.y; - uint16_t width = win_meta->active->frame.size.x; - if (change == '-') - height--; - else if (change == '+') - height++; - else if (change == '_') - width--; - else if (change == '*') - width++; - resize_active_win (win_meta, height, width); } } - -struct Map init_map () { -// Initialize map with some experimental start values. - struct Map map; - map.width = 64; - map.height = 64; - map.offset_x = 0; - map.offset_y = 0; - uint32_t size = map.width * map.height; - map.cells = malloc(size); - uint16_t y, x; - for (y = 0; y < map.height; y++) - for (x = 0; x < map.width; x++) - map.cells[(y * map.width) + x] = '~'; - map.cells[size / 2 + (map.width / 2)] = '.'; - uint32_t repeats, root, curpos; - for (root = 0; root * root * root < size; root++); - for (repeats = 0; repeats < size * root; repeats++) { - y = rrand(0, 0) % map.height; - x = rrand(0, 0) % map.width; - curpos = y * map.width + x; - if ('~' == map.cells[curpos] && - ( (curpos >= map.width && '.' == map.cells[curpos - map.width]) - || (curpos < map.width * (map.height-1) && '.' == map.cells[curpos + map.width]) - || (curpos > 0 && curpos % map.width != 0 && '.' == map.cells[curpos-1]) - || (curpos < (map.width * map.height) && (curpos+1) % map.width != 0 && '.' == map.cells[curpos+1]))) - map.cells[y * map.width + x] = '.'; } - return map; } - -void map_scroll (struct Map * map, char dir) { -// Scroll map into direction dir if possible by changing the offset. - if ('n' == dir && map->offset_y > 0) - map->offset_y--; - else if ('s' == dir) - map->offset_y++; - else if ('w' == dir && map->offset_x > 0) - map->offset_x--; - else if ('e' == dir) - map->offset_x++; } - -void next_turn (struct World * world) { -// Increment turn and move enemy. - world->turn++; - rrand(1, world->seed * world->turn); - char d = rrand(0, 0) % 5; - uint16_t ty = world->monster->y; - uint16_t tx = world->monster->x; - if (1 == d) - ty++; - else if (2 == d) - ty--; - else if (3 == d) - tx++; - else if (4 == d) - tx--; - if (tx == world->player->x && ty == world->player->y) - update_log(world, "\nThe monster hits you."); - else if (is_passable(world->map, ty, tx)) { - world->monster->y = ty; - world->monster->x = tx; } } - -void update_log (struct World * world, char * text) { -// Update log with new text to be appended. - char * new_text; - uint16_t len_old = strlen(world->log); - uint16_t len_new = strlen(text); - uint16_t len_whole = len_old + len_new + 1; - new_text = calloc(len_whole, sizeof(char)); - memcpy(new_text, world->log, len_old); - memcpy(new_text + len_old, text, len_new); - free(world->log); - world->log = new_text; } - -char is_passable (struct Map * map, uint16_t y, uint16_t x) { -// Check if coordinate on (or beyond) map is accessible to movement. - char passable = 0; - if (0 <= x && x < map->width && 0 <= y && y < map->height) - if ('.' == map->cells[y * map->width + x]) - passable = 1; - return passable; } - -void record_action (char action) { -// Append action to game record file. - FILE * file = fopen("record", "a"); - fputc(action, file); - fclose(file); } - -void move_player (struct World * world, char d) { -// Move player in direction d, increment turn counter and update log. - static char prev = 0; - char success = 0; - char * dir; - uint16_t ty = world->player->y; - uint16_t tx = world->player->x; - if ('s' == d) { - dir = "south"; - ty++; } - if ('n' == d) { - dir = "north"; - ty--; } - if ('w' == d) { - dir = "west"; - tx--; } - if ('e' == d) { - dir = "east"; - tx++; } - if (ty == world->monster->y && tx == world->monster->x) - success = 2; - else if (is_passable(world->map, ty, tx)) { - success = 1; - world->player->y = ty; - world->player->x = tx; } - if (success * d == prev) - update_log (world, "."); - else { - if (2 == success) - update_log (world, "\nYou hit the monster."); - else { - char * msg = calloc(25, sizeof(char)); - char * msg_content = "You fail to move"; - if (1 == success) - msg_content = "You move"; - sprintf(msg, "\n%s %s.", msg_content, dir); - update_log (world, msg); - free(msg); } } - prev = success * d; - if (1 == world->interactive) - record_action(d); - next_turn (world); } - -void player_wait (struct World * world) { -// Make player wait one turn. - if (1 == world->interactive) - record_action(0); - next_turn (world); - update_log (world, "\nYou wait."); } - -unsigned char meta_keys(int key, struct World * world, struct WinMeta * win_meta, struct Win * win_keys, - struct Win * win_map, struct Win * win_info, struct Win * win_log) { -// Call some meta program / window management actions dependent on key. Return 1 to signal quitting. - if (key == get_action_key(world->keybindings, "quit")) - return 1; - else if (key == get_action_key(world->keybindings, "scroll pad right")) - scroll_pad (win_meta, '+'); - else if (key == get_action_key(world->keybindings, "scroll pad left")) - scroll_pad (win_meta, '-'); - else if (key == get_action_key(world->keybindings, "toggle keys window")) - toggle_window(win_meta, win_keys); - else if (key == get_action_key(world->keybindings, "toggle map window")) - toggle_window(win_meta, win_map); - else if (key == get_action_key(world->keybindings, "toggle info window")) - toggle_window(win_meta, win_info); - else if (key == get_action_key(world->keybindings, "toggle log window")) - toggle_window(win_meta, win_log); - else if (key == get_action_key(world->keybindings, "cycle forwards")) - cycle_active_win(win_meta, 'n'); - else if (key == get_action_key(world->keybindings, "cycle backwards")) - cycle_active_win(win_meta, 'p'); - else if (key == get_action_key(world->keybindings, "shift forwards")) - shift_active_win(win_meta, 'f'); - else if (key == get_action_key(world->keybindings, "shift backwards")) - shift_active_win(win_meta, 'b'); - else if (key == get_action_key(world->keybindings, "grow horizontally")) - growshrink_active_window(win_meta, '*'); - else if (key == get_action_key(world->keybindings, "shrink horizontally")) - growshrink_active_window(win_meta, '_'); - else if (key == get_action_key(world->keybindings, "grow vertically")) - growshrink_active_window(win_meta, '+'); - else if (key == get_action_key(world->keybindings, "shrink vertically")) - growshrink_active_window(win_meta, '-'); - else if (key == get_action_key(world->keybindings, "save keys")) - save_keybindings(world); - else if (key == get_action_key(world->keybindings, "keys nav up")) - keyswin_move_selection (world, 'u'); - else if (key == get_action_key(world->keybindings, "keys nav down")) - keyswin_move_selection (world, 'd'); - else if (key == get_action_key(world->keybindings, "keys mod")) - keyswin_mod_key (world, win_meta); - else if (key == get_action_key(world->keybindings, "map up")) - map_scroll (world->map, 'n'); - else if (key == get_action_key(world->keybindings, "map down")) - map_scroll (world->map, 's'); - else if (key == get_action_key(world->keybindings, "map right")) - map_scroll (world->map, 'e'); - else if (key == get_action_key(world->keybindings, "map left")) - map_scroll (world->map, 'w'); - return 0; } - -int main (int argc, char *argv[]) { - struct World world; - world.interactive = 1; - int opt; - uint32_t start_turn; - while ((opt = getopt(argc, argv, "s::")) != -1) { - switch (opt) { - case 's': - world.interactive = 0; - start_turn = 0; - if (optarg) - start_turn = atoi(optarg); - break; - default: - exit(EXIT_FAILURE); } } - - world.log = calloc(1, sizeof(char)); - update_log (&world, " "); - struct Player player; - world.player = &player; - struct Monster monster; - world.monster = &monster; - FILE * file; - if (1 == world.interactive && 0 == access("savefile", F_OK)) { - file = fopen("savefile", "r"); - world.seed = read_uint32_bigendian(file); - world.turn = read_uint32_bigendian(file); - player.y = read_uint16_bigendian(file); - player.x = read_uint16_bigendian(file); - monster.y = read_uint16_bigendian(file); - monster.x = read_uint16_bigendian(file); - fclose(file); } - else { - world.turn = 1; - if (0 == world.interactive) { - file = fopen("record", "r"); - world.seed = read_uint32_bigendian(file); } - else { - file = fopen("record", "w"); - world.seed = time(NULL); - write_uint32_bigendian(world.seed, file); - fclose(file); } } - rrand(1, world.seed); - struct Map map = init_map(); - world.map = ↦ - if (1 == world.turn) { - for (player.y = player.x = 0; 0 == is_passable(&map, player.y, player.x);) { - player.y = rrand(0, 0) % map.height; - player.x = rrand(0, 0) % map.width; } - for (monster.y = monster.x = 0; 0 == is_passable(&map, monster.y, monster.x);) { - monster.y = rrand(0, 0) % map.height; - monster.x = rrand(0, 0) % map.width; } } - - WINDOW * screen = initscr(); - noecho(); - curs_set(0); - keypad(screen, TRUE); - raw(); - init_keybindings(&world); - struct WinMeta win_meta = init_win_meta(screen); - struct Win win_keys = init_win(&win_meta, "Keys", &world, draw_keys_win); - struct Win win_map = init_win(&win_meta, "Map", &world, draw_map_win); - struct Win win_info = init_win(&win_meta, "Info", &world, draw_info_win); - struct Win win_log = init_win(&win_meta, "Log", &world, draw_log_win); - win_keys.frame.size.x = 29; - win_map.frame.size.x = win_meta.pad.size.x - win_keys.frame.size.x - win_log.frame.size.x - 2; - win_info.frame.size.y = 1; - win_log.frame.size.y = win_meta.pad.size.y - 3; - toggle_window(&win_meta, &win_keys); - toggle_window(&win_meta, &win_map); - toggle_window(&win_meta, &win_info); - toggle_window(&win_meta, &win_log); - - int key; - unsigned char quit_called = 0; - if (0 == world.interactive) { - unsigned char still_reading_file = 1; - int action; - while (1) { - if (start_turn == world.turn) - start_turn = 0; - if (0 == start_turn) { - draw_all_wins (&win_meta); - key = getch(); } - if (1 == still_reading_file && - (world.turn < start_turn || key == get_action_key(world.keybindings, "wait / next turn")) ) { - action = getc(file); - if (EOF == action) { - start_turn = 0; - still_reading_file = 0; } - else if (0 == action) - player_wait (&world); - else if ('s' == action) - move_player(&world, 's'); - else if ('n' == action) - move_player(&world, 'n'); - else if ('e' == action) - move_player(&world, 'e'); - else if ('w' == action) - move_player(&world, 'w'); } - else - quit_called = meta_keys(key, &world, &win_meta, &win_keys, &win_map, &win_info, &win_log); - if (1 == quit_called) - break; } } - else { - uint32_t last_turn = 0; - while (1) { - if (last_turn != world.turn) { - save_game(&world); - last_turn = world.turn; } - draw_all_wins (&win_meta); - key = getch(); - if (key == get_action_key(world.keybindings, "player down")) - move_player(&world, 's'); - else if (key == get_action_key(world.keybindings, "player up")) - move_player(&world, 'n'); - else if (key == get_action_key(world.keybindings, "player right")) - move_player(&world, 'e'); - else if (key == get_action_key(world.keybindings, "player left")) - move_player(&world, 'w'); - else if (key == get_action_key(world.keybindings, "wait / next turn")) - player_wait (&world); - else - quit_called = meta_keys(key, &world, &win_meta, &win_keys, &win_map, &win_info, &win_log); - if (1 == quit_called) - break; } } - - free(map.cells); - for (key = 0; key <= world.keyswindata->max; key++) - free(world.keybindings[key].name); - free(world.keybindings); - free(world.keyswindata); - free(world.log); - - endwin(); - return 0; } diff --git a/roguelike.h b/roguelike.h deleted file mode 100644 index d82313e..0000000 --- a/roguelike.h +++ /dev/null @@ -1,40 +0,0 @@ -struct World { - char interactive; - struct KeyBinding * keybindings; - struct KeysWinData * keyswindata; - uint32_t seed; - uint32_t turn; - char * log; - struct Map * map; - struct Monster * monster; - struct Player * player; }; - -struct Map { - uint16_t width; - uint16_t height; - uint16_t offset_x; - uint16_t offset_y; - char * cells; }; - -struct Player { - uint16_t y; - uint16_t x; }; - -struct Monster { - uint16_t y; - uint16_t x; }; - -uint16_t rrand(char, uint32_t); -void save_game(struct World *); -void toggle_window (struct WinMeta *, struct Win *); -void scroll_pad (struct WinMeta *, char); -void growshrink_active_window (struct WinMeta *, char); -struct Map init_map (); -void map_scroll (struct Map *, char); -void next_turn (struct World *); -void update_log (struct World *, char *); -char is_passable (struct Map *, uint16_t, uint16_t); -void record_action (char); -void move_player (struct World *, char); -void player_wait(struct World *); -unsigned char meta_keys(int, struct World *, struct WinMeta *, struct Win *, struct Win *, struct Win *, struct Win *); diff --git a/src/draw_wins.c b/src/draw_wins.c new file mode 100644 index 0000000..818037a --- /dev/null +++ b/src/draw_wins.c @@ -0,0 +1,138 @@ +#include +#include +#include +#include +#include "windows.h" +#include "draw_wins.h" +#include "roguelike.h" +#include "keybindings.h" + +void draw_with_linebreaks (struct Win * win, char * text, uint16_t start_y) { +// Write text into window content space. Start on row start_y. Fill unused rows with whitespace. + uint16_t x, y; + char toggle; + char fin = 0; + int16_t z = -1; + for (y = start_y; y < win->frame.size.y; y++) { + if (0 == fin) + toggle = 0; + for (x = 0; x < win->frame.size.x; x++) { + if (0 == toggle) { + z++; + if ('\n' == text[z]) { + toggle = 1; + continue; } + else + mvwaddch(win->frame.curses_win, y, x, text[z]); + if ('\n' == text[z+1]) { + z++; + toggle = 1; } + else if (0 == text[z+1]) { + toggle = 1; + fin = 1; } } } } } + +void draw_text_from_bottom (struct Win * win, char * text) { +// Draw text from end/bottom to the top. + char toggle = 0; + uint16_t x, y, offset; + int16_t z = -1; + for (y = 0; 0 == toggle; y++) // Determine number of lines text would have in + for (x = 0; x < win->frame.size.x; x++) { // a window of available width, but infinite height. + z++; + if ('\n' == text[z]) // Treat \n and \0 as control characters for incrementing y and stopping + break; // the loop. Make sure they don't count as cell space themselves. + if ('\n' == text[z+1]) { + z++; + break; } + else if (0 == text[z+1]) { + toggle = 1; + break; } } + z = -1; + uint16_t start_y = 0; + if (y < win->frame.size.y) // Depending on what is bigger, determine start point in window or in text. + start_y = win->frame.size.y - y; + else if (y > win->frame.size.y) { + offset = y - win->frame.size.y; + for (y = 0; y < offset; y++) + for (x = 0; x < win->frame.size.x; x++) { + z++; + if ('\n' == text[z]) + break; + if ('\n' == text[z+1]) { + z++; + break; } } + text = text + (sizeof(char) * (z + 1)); } + draw_with_linebreaks(win, text, start_y); } + +void draw_log_win (struct Win * win) { +// Draw log text from world struct in win->data from bottom to top. + struct World * world = (struct World *) win->data; + draw_text_from_bottom(win, world->log); } + +void draw_map_win (struct Win * win) { +// Draw map determined by win->data Map struct into window. Respect offset. + struct World * world = (struct World *) win->data; + struct Map * map = world->map; + struct Player * player = world->player; + struct Monster * monster = world->monster; + char * cells = map->cells; + uint16_t width_map_av = map->width - map->offset_x; + uint16_t height_map_av = map->height - map->offset_y; + uint16_t x, y, z; + for (y = 0; y < win->frame.size.y; y++) { + z = map->offset_x + (map->offset_y + y) * (map->width); + for (x = 0; x < win->frame.size.x; x++) { + if (y < height_map_av && x < width_map_av) { + if (z == (map->width * player->y) + player->x) + mvwaddch(win->frame.curses_win, y, x, '@'); + else if (z == (map->width * monster->y) + monster->x) + mvwaddch(win->frame.curses_win, y, x, 'M'); + else + mvwaddch(win->frame.curses_win, y, x, cells[z]); + z++; } } } } + +void draw_info_win (struct Win * win) { +// Draw info window by appending win->data integer value to "Turn: " display. + struct World * world = (struct World *) win->data; + uint16_t count = world->turn; + char text[100]; + snprintf(text, 100, "Turn: %d", count); + draw_with_linebreaks(win, text, 0); } + +void draw_keys_win (struct Win * win) { +// Draw keybindings window. + struct World * world = (struct World *) win->data; + uint16_t offset = 0, y, x; + if (world->keyswindata->max >= win->frame.size.y) { + if (world->keyswindata->select > win->frame.size.y / 2) { + if (world->keyswindata->select < (world->keyswindata->max - (win->frame.size.y / 2))) + offset = world->keyswindata->select - (win->frame.size.y / 2); + else + offset = world->keyswindata->max - win->frame.size.y + 1; } } + uint8_t keydescwidth = 9 + 1; // max length assured by get_keyname() + \0 + char * keydesc = malloc(keydescwidth), * keyname; + attr_t attri; + for (y = 0; y <= world->keyswindata->max && y < win->frame.size.y; y++) { + if (0 == y && offset > 0) { + draw_scroll_hint(&win->frame, y, offset + 1, '^'); + continue; } + else if (win->frame.size.y == y + 1 && 0 < world->keyswindata->max - (win->frame.size.y + offset - 1)) { + draw_scroll_hint(&win->frame, y, world->keyswindata->max - (offset + win->frame.size.y) + 2, 'v'); + continue; } + attri = 0; + if (y == world->keyswindata->select - offset) { + attri = A_REVERSE; + if (1 == world->keyswindata->edit) + attri = attri | A_BLINK; } + keyname = get_keyname(world->keybindings[y + offset].key); + snprintf(keydesc, keydescwidth, "%-9s", keyname); + free(keyname); + for (x = 0; x < win->frame.size.x; x++) + if (x < strlen(keydesc)) + mvwaddch(win->frame.curses_win, y, x, keydesc[x] | attri); + else if (strlen(keydesc) < x && x < strlen(world->keybindings[y + offset].name) + strlen(keydesc) + 1) + mvwaddch(win->frame.curses_win, y, x, + world->keybindings[y + offset].name[x - strlen(keydesc) - 1] | attri); + else + mvwaddch(win->frame.curses_win, y, x, ' ' | attri); } + free(keydesc); } diff --git a/src/draw_wins.h b/src/draw_wins.h new file mode 100644 index 0000000..64f332c --- /dev/null +++ b/src/draw_wins.h @@ -0,0 +1,6 @@ +void draw_with_linebreaks (struct Win *, char *, uint16_t); +void draw_text_from_bottom (struct Win *, char *); +void draw_log_win (struct Win *); +void draw_map_win (struct Win *); +void draw_info_win (struct Win *); +void draw_keys_win (struct Win *); diff --git a/src/keybindings.c b/src/keybindings.c new file mode 100644 index 0000000..b6ff412 --- /dev/null +++ b/src/keybindings.c @@ -0,0 +1,127 @@ +#include +#include +#include +#include +#include "windows.h" +#include "roguelike.h" +#include "keybindings.h" + +void init_keybindings(struct World * world) { +// Initialize keybindings from file "keybindings". + FILE * file = fopen("keybindings", "r"); + uint16_t lines = 0; + int c = 0; + uint16_t linemax = 0; + uint16_t c_count = 0; + while (EOF != c) { + c_count++; + c = getc(file); + if ('\n' == c) { + if (c_count > linemax) + linemax = c_count + 1; + c_count = 0; + lines++; } } + struct KeyBinding * keybindings = malloc(lines * sizeof(struct KeyBinding)); + fseek(file, 0, SEEK_SET); + char * command = malloc(linemax); + uint16_t commcount = 0; + char * cmdptr; + while (fgets(command, linemax, file)) { + keybindings[commcount].key = atoi(command); + cmdptr = strchr(command, ' ') + 1; + keybindings[commcount].name = malloc(strlen(cmdptr)); + memcpy(keybindings[commcount].name, cmdptr, strlen(cmdptr) - 1); + keybindings[commcount].name[strlen(cmdptr) - 1] = '\0'; + commcount++; } + free(command); + fclose(file); + struct KeysWinData * keyswindata = malloc(sizeof(struct KeysWinData)); + keyswindata->max = lines - 1; + keyswindata->select = 0; + keyswindata->edit = 0; + world->keybindings = keybindings; + world->keyswindata = keyswindata; } + +void save_keybindings(struct World * world) { +// Write keybindings to keybindings file. + struct KeysWinData * keyswindata = (struct KeysWinData *) world->keyswindata; + struct KeyBinding * keybindings = world->keybindings; + FILE * file = fopen("keybindings", "w"); + uint16_t linemax = 0; + uint16_t i; + for (i = 0; i <= keyswindata->max; i++) + if (strlen(keybindings[i].name) > linemax) + linemax = strlen(keybindings[i].name); + linemax = linemax + 6; // + 6 = + 3 digits + whitespace + newline + null byte + char * line = malloc(linemax); + for (i = 0; i <= keyswindata->max; i++) { + snprintf(line, linemax, "%d %s\n", keybindings[i].key, keybindings[i].name); + fwrite(line, sizeof(char), strlen(line), file); } + free(line); + fclose(file); } + +uint16_t get_action_key (struct KeyBinding * keybindings, char * name) { +// Return key matching name in keybindings. + uint16_t i = 0; + while (strcmp(keybindings[i].name, name) ) + i++; + return keybindings[i].key; } + +char * get_keyname(uint16_t keycode) { +// Translate some keycodes to readable names of max 9 chars. + char * keyname; + keyname = malloc(15); + if (32 < keycode && keycode < 127) + sprintf(keyname, "%c", keycode); + else if (keycode == 9) + sprintf(keyname, "TAB"); + else if (keycode == 10) + sprintf(keyname, "RETURN"); + else if (keycode == 27) + sprintf(keyname, "ESCAPE"); + else if (keycode == 32) + sprintf(keyname, "SPACE"); + else if (keycode == KEY_UP) + sprintf(keyname, "UP"); + else if (keycode == KEY_DOWN) + sprintf(keyname, "DOWN"); + else if (keycode == KEY_LEFT) + sprintf(keyname, "LEFT"); + else if (keycode == KEY_RIGHT) + sprintf(keyname, "RIGHT"); + else if (keycode == KEY_HOME) + sprintf(keyname, "HOME"); + else if (keycode == KEY_BACKSPACE) + sprintf(keyname, "BACKSPACE"); + else if (keycode >= KEY_F0 && keycode <= KEY_F(63)) { + uint16_t f = keycode - KEY_F0; + sprintf(keyname, "F%d", f); } + else if (keycode == KEY_DC) + sprintf(keyname, "DELETE"); + else if (keycode == KEY_IC) + sprintf(keyname, "INSERT"); + else if (keycode == KEY_NPAGE) + sprintf(keyname, "NEXT PAGE"); + else if (keycode == KEY_PPAGE) + sprintf(keyname, "PREV PAGE"); + else if (keycode == KEY_END) + sprintf(keyname, "END"); + else + sprintf(keyname, "(unknown)"); + return keyname; } + +void keyswin_mod_key (struct World * world, struct WinMeta * win_meta) { +// In keybindings window, mark selection modifiable, modify key. Ensure max of three digits in key code field. + world->keyswindata->edit = 1; + draw_all_wins (win_meta); + int key = getch(); + if (key < 1000) + world->keybindings[world->keyswindata->select].key = key; + world->keyswindata->edit = 0; } + +void keyswin_move_selection (struct World * world, char dir) { +// In keybindings window, move selection upwards or downwards (if within limits of list length). + if ('u' == dir && world->keyswindata->select > 0) + world->keyswindata->select--; + else if ('d' == dir && world->keyswindata->select < world->keyswindata->max) + world->keyswindata->select++; } diff --git a/src/keybindings.h b/src/keybindings.h new file mode 100644 index 0000000..b0ff912 --- /dev/null +++ b/src/keybindings.h @@ -0,0 +1,15 @@ +struct KeyBinding { + char * name; + uint16_t key; }; + +struct KeysWinData { + uint16_t max; + char edit; + uint16_t select; }; + +void init_keybindings(struct World *); +void save_keybindings(struct World *); +uint16_t get_action_key (struct KeyBinding *, char *); +char * get_keyname(uint16_t); +void keyswin_mod_key (struct World *, struct WinMeta *); +void keyswin_move_selection (struct World *, char); diff --git a/src/readwrite.c b/src/readwrite.c new file mode 100644 index 0000000..ed5a3a3 --- /dev/null +++ b/src/readwrite.c @@ -0,0 +1,40 @@ +#include +#include +#include + +uint16_t read_uint16_bigendian(FILE * file) { +// Read uint16 from file in big-endian order. + const uint16_t nchar = UCHAR_MAX + 1; + unsigned char a = fgetc(file); + unsigned char b = fgetc(file); + return (a * nchar) + b; } + +void write_uint16_bigendian(uint16_t x, FILE * file) { +// Write uint16 to file in beg-endian order. + const uint16_t nchar = UCHAR_MAX + 1; + unsigned char a = x / nchar; + unsigned char b = x % nchar; + fputc(a, file); + fputc(b, file); } + +uint32_t read_uint32_bigendian(FILE * file) { +// Read uint32 from file in big-endian order. + const uint16_t nchar = UCHAR_MAX + 1; + unsigned char a = fgetc(file); + unsigned char b = fgetc(file); + unsigned char c = fgetc(file); + unsigned char d = fgetc(file); + return (a * nchar * nchar * nchar) + (b * nchar * nchar) + (c * nchar) + d; } + +void write_uint32_bigendian(uint32_t x, FILE * file) { +// Write uint32 to file in beg-endian order. + const uint16_t nchar = UCHAR_MAX + 1; + unsigned char a = x / (nchar * nchar * nchar); + unsigned char b = (x - (a * nchar * nchar * nchar)) / (nchar * nchar); + unsigned char c = (x - ((a * nchar * nchar * nchar) + (b * nchar * nchar))) / nchar; + unsigned char d = x % nchar; + fputc(a, file); + fputc(b, file); + fputc(c, file); + fputc(d, file); } + diff --git a/src/readwrite.h b/src/readwrite.h new file mode 100644 index 0000000..28bc3ab --- /dev/null +++ b/src/readwrite.h @@ -0,0 +1,5 @@ +uint16_t read_uint16_bigendian(FILE * file); +void write_uint16_bigendian(uint16_t x, FILE * file); +uint32_t read_uint32_bigendian(FILE * file); +void write_uint32_bigendian(uint32_t x, FILE * file); + diff --git a/src/roguelike.c b/src/roguelike.c new file mode 100644 index 0000000..f0057e2 --- /dev/null +++ b/src/roguelike.c @@ -0,0 +1,383 @@ +#include +#include +#include +#include +#include +#include +#include "windows.h" +#include "draw_wins.h" +#include "roguelike.h" +#include "keybindings.h" +#include "readwrite.h" + +uint16_t rrand(char use_seed, uint32_t new_seed) { +// Pseudo-random number generator (LGC algorithm). Use instead of rand() to ensure portable predictability. + static uint32_t seed; + if (0 != use_seed) + seed = new_seed; + seed = ((seed * 1103515245) + 12345) % 2147483648; // Values as recommended by POSIX.1-2001 (see rand(3)). + return (seed / 65536); } // Ignore least significant 16 bits (they are less random). + +void save_game(struct World * world) { +// Save game data to game file. + FILE * file = fopen("savefile", "w"); + write_uint32_bigendian(world->seed, file); + write_uint32_bigendian(world->turn, file); + write_uint16_bigendian(world->player->y, file); + write_uint16_bigendian(world->player->x, file); + write_uint16_bigendian(world->monster->y, file); + write_uint16_bigendian(world->monster->x, file); + fclose(file); } + +void toggle_window (struct WinMeta * win_meta, struct Win * win) { +// Toggle display of window win. + if (0 != win->frame.curses_win) + suspend_win(win_meta, win); + else + append_win(win_meta, win); } + +void scroll_pad (struct WinMeta * win_meta, char dir) { +// Try to scroll pad left or right. + if ('+' == dir) + reset_pad_offset(win_meta, win_meta->pad_offset + 1); + else if ('-' == dir) + reset_pad_offset(win_meta, win_meta->pad_offset - 1); } + +void growshrink_active_window (struct WinMeta * win_meta, char change) { +// Grow or shrink active window horizontally or vertically by one cell size. + if (0 != win_meta->active) { + uint16_t height = win_meta->active->frame.size.y; + uint16_t width = win_meta->active->frame.size.x; + if (change == '-') + height--; + else if (change == '+') + height++; + else if (change == '_') + width--; + else if (change == '*') + width++; + resize_active_win (win_meta, height, width); } } + +struct Map init_map () { +// Initialize map with some experimental start values. + struct Map map; + map.width = 64; + map.height = 64; + map.offset_x = 0; + map.offset_y = 0; + uint32_t size = map.width * map.height; + map.cells = malloc(size); + uint16_t y, x; + for (y = 0; y < map.height; y++) + for (x = 0; x < map.width; x++) + map.cells[(y * map.width) + x] = '~'; + map.cells[size / 2 + (map.width / 2)] = '.'; + uint32_t repeats, root, curpos; + for (root = 0; root * root * root < size; root++); + for (repeats = 0; repeats < size * root; repeats++) { + y = rrand(0, 0) % map.height; + x = rrand(0, 0) % map.width; + curpos = y * map.width + x; + if ('~' == map.cells[curpos] && + ( (curpos >= map.width && '.' == map.cells[curpos - map.width]) + || (curpos < map.width * (map.height-1) && '.' == map.cells[curpos + map.width]) + || (curpos > 0 && curpos % map.width != 0 && '.' == map.cells[curpos-1]) + || (curpos < (map.width * map.height) && (curpos+1) % map.width != 0 && '.' == map.cells[curpos+1]))) + map.cells[y * map.width + x] = '.'; } + return map; } + +void map_scroll (struct Map * map, char dir) { +// Scroll map into direction dir if possible by changing the offset. + if ('n' == dir && map->offset_y > 0) + map->offset_y--; + else if ('s' == dir) + map->offset_y++; + else if ('w' == dir && map->offset_x > 0) + map->offset_x--; + else if ('e' == dir) + map->offset_x++; } + +void next_turn (struct World * world) { +// Increment turn and move enemy. + world->turn++; + rrand(1, world->seed * world->turn); + char d = rrand(0, 0) % 5; + uint16_t ty = world->monster->y; + uint16_t tx = world->monster->x; + if (1 == d) + ty++; + else if (2 == d) + ty--; + else if (3 == d) + tx++; + else if (4 == d) + tx--; + if (tx == world->player->x && ty == world->player->y) + update_log(world, "\nThe monster hits you."); + else if (is_passable(world->map, ty, tx)) { + world->monster->y = ty; + world->monster->x = tx; } } + +void update_log (struct World * world, char * text) { +// Update log with new text to be appended. + char * new_text; + uint16_t len_old = strlen(world->log); + uint16_t len_new = strlen(text); + uint16_t len_whole = len_old + len_new + 1; + new_text = calloc(len_whole, sizeof(char)); + memcpy(new_text, world->log, len_old); + memcpy(new_text + len_old, text, len_new); + free(world->log); + world->log = new_text; } + +char is_passable (struct Map * map, uint16_t y, uint16_t x) { +// Check if coordinate on (or beyond) map is accessible to movement. + char passable = 0; + if (0 <= x && x < map->width && 0 <= y && y < map->height) + if ('.' == map->cells[y * map->width + x]) + passable = 1; + return passable; } + +void record_action (char action) { +// Append action to game record file. + FILE * file = fopen("record", "a"); + fputc(action, file); + fclose(file); } + +void move_player (struct World * world, char d) { +// Move player in direction d, increment turn counter and update log. + static char prev = 0; + char success = 0; + char * dir; + uint16_t ty = world->player->y; + uint16_t tx = world->player->x; + if ('s' == d) { + dir = "south"; + ty++; } + if ('n' == d) { + dir = "north"; + ty--; } + if ('w' == d) { + dir = "west"; + tx--; } + if ('e' == d) { + dir = "east"; + tx++; } + if (ty == world->monster->y && tx == world->monster->x) + success = 2; + else if (is_passable(world->map, ty, tx)) { + success = 1; + world->player->y = ty; + world->player->x = tx; } + if (success * d == prev) + update_log (world, "."); + else { + if (2 == success) + update_log (world, "\nYou hit the monster."); + else { + char * msg = calloc(25, sizeof(char)); + char * msg_content = "You fail to move"; + if (1 == success) + msg_content = "You move"; + sprintf(msg, "\n%s %s.", msg_content, dir); + update_log (world, msg); + free(msg); } } + prev = success * d; + if (1 == world->interactive) + record_action(d); + next_turn (world); } + +void player_wait (struct World * world) { +// Make player wait one turn. + if (1 == world->interactive) + record_action(0); + next_turn (world); + update_log (world, "\nYou wait."); } + +unsigned char meta_keys(int key, struct World * world, struct WinMeta * win_meta, struct Win * win_keys, + struct Win * win_map, struct Win * win_info, struct Win * win_log) { +// Call some meta program / window management actions dependent on key. Return 1 to signal quitting. + if (key == get_action_key(world->keybindings, "quit")) + return 1; + else if (key == get_action_key(world->keybindings, "scroll pad right")) + scroll_pad (win_meta, '+'); + else if (key == get_action_key(world->keybindings, "scroll pad left")) + scroll_pad (win_meta, '-'); + else if (key == get_action_key(world->keybindings, "toggle keys window")) + toggle_window(win_meta, win_keys); + else if (key == get_action_key(world->keybindings, "toggle map window")) + toggle_window(win_meta, win_map); + else if (key == get_action_key(world->keybindings, "toggle info window")) + toggle_window(win_meta, win_info); + else if (key == get_action_key(world->keybindings, "toggle log window")) + toggle_window(win_meta, win_log); + else if (key == get_action_key(world->keybindings, "cycle forwards")) + cycle_active_win(win_meta, 'n'); + else if (key == get_action_key(world->keybindings, "cycle backwards")) + cycle_active_win(win_meta, 'p'); + else if (key == get_action_key(world->keybindings, "shift forwards")) + shift_active_win(win_meta, 'f'); + else if (key == get_action_key(world->keybindings, "shift backwards")) + shift_active_win(win_meta, 'b'); + else if (key == get_action_key(world->keybindings, "grow horizontally")) + growshrink_active_window(win_meta, '*'); + else if (key == get_action_key(world->keybindings, "shrink horizontally")) + growshrink_active_window(win_meta, '_'); + else if (key == get_action_key(world->keybindings, "grow vertically")) + growshrink_active_window(win_meta, '+'); + else if (key == get_action_key(world->keybindings, "shrink vertically")) + growshrink_active_window(win_meta, '-'); + else if (key == get_action_key(world->keybindings, "save keys")) + save_keybindings(world); + else if (key == get_action_key(world->keybindings, "keys nav up")) + keyswin_move_selection (world, 'u'); + else if (key == get_action_key(world->keybindings, "keys nav down")) + keyswin_move_selection (world, 'd'); + else if (key == get_action_key(world->keybindings, "keys mod")) + keyswin_mod_key (world, win_meta); + else if (key == get_action_key(world->keybindings, "map up")) + map_scroll (world->map, 'n'); + else if (key == get_action_key(world->keybindings, "map down")) + map_scroll (world->map, 's'); + else if (key == get_action_key(world->keybindings, "map right")) + map_scroll (world->map, 'e'); + else if (key == get_action_key(world->keybindings, "map left")) + map_scroll (world->map, 'w'); + return 0; } + +int main (int argc, char *argv[]) { + struct World world; + world.interactive = 1; + int opt; + uint32_t start_turn; + while ((opt = getopt(argc, argv, "s::")) != -1) { + switch (opt) { + case 's': + world.interactive = 0; + start_turn = 0; + if (optarg) + start_turn = atoi(optarg); + break; + default: + exit(EXIT_FAILURE); } } + + world.log = calloc(1, sizeof(char)); + update_log (&world, " "); + struct Player player; + world.player = &player; + struct Monster monster; + world.monster = &monster; + FILE * file; + if (1 == world.interactive && 0 == access("savefile", F_OK)) { + file = fopen("savefile", "r"); + world.seed = read_uint32_bigendian(file); + world.turn = read_uint32_bigendian(file); + player.y = read_uint16_bigendian(file); + player.x = read_uint16_bigendian(file); + monster.y = read_uint16_bigendian(file); + monster.x = read_uint16_bigendian(file); + fclose(file); } + else { + world.turn = 1; + if (0 == world.interactive) { + file = fopen("record", "r"); + world.seed = read_uint32_bigendian(file); } + else { + file = fopen("record", "w"); + world.seed = time(NULL); + write_uint32_bigendian(world.seed, file); + fclose(file); } } + rrand(1, world.seed); + struct Map map = init_map(); + world.map = ↦ + if (1 == world.turn) { + for (player.y = player.x = 0; 0 == is_passable(&map, player.y, player.x);) { + player.y = rrand(0, 0) % map.height; + player.x = rrand(0, 0) % map.width; } + for (monster.y = monster.x = 0; 0 == is_passable(&map, monster.y, monster.x);) { + monster.y = rrand(0, 0) % map.height; + monster.x = rrand(0, 0) % map.width; } } + + WINDOW * screen = initscr(); + noecho(); + curs_set(0); + keypad(screen, TRUE); + raw(); + init_keybindings(&world); + struct WinMeta win_meta = init_win_meta(screen); + struct Win win_keys = init_win(&win_meta, "Keys", &world, draw_keys_win); + struct Win win_map = init_win(&win_meta, "Map", &world, draw_map_win); + struct Win win_info = init_win(&win_meta, "Info", &world, draw_info_win); + struct Win win_log = init_win(&win_meta, "Log", &world, draw_log_win); + win_keys.frame.size.x = 29; + win_map.frame.size.x = win_meta.pad.size.x - win_keys.frame.size.x - win_log.frame.size.x - 2; + win_info.frame.size.y = 1; + win_log.frame.size.y = win_meta.pad.size.y - 3; + toggle_window(&win_meta, &win_keys); + toggle_window(&win_meta, &win_map); + toggle_window(&win_meta, &win_info); + toggle_window(&win_meta, &win_log); + + int key; + unsigned char quit_called = 0; + if (0 == world.interactive) { + unsigned char still_reading_file = 1; + int action; + while (1) { + if (start_turn == world.turn) + start_turn = 0; + if (0 == start_turn) { + draw_all_wins (&win_meta); + key = getch(); } + if (1 == still_reading_file && + (world.turn < start_turn || key == get_action_key(world.keybindings, "wait / next turn")) ) { + action = getc(file); + if (EOF == action) { + start_turn = 0; + still_reading_file = 0; } + else if (0 == action) + player_wait (&world); + else if ('s' == action) + move_player(&world, 's'); + else if ('n' == action) + move_player(&world, 'n'); + else if ('e' == action) + move_player(&world, 'e'); + else if ('w' == action) + move_player(&world, 'w'); } + else + quit_called = meta_keys(key, &world, &win_meta, &win_keys, &win_map, &win_info, &win_log); + if (1 == quit_called) + break; } } + else { + uint32_t last_turn = 0; + while (1) { + if (last_turn != world.turn) { + save_game(&world); + last_turn = world.turn; } + draw_all_wins (&win_meta); + key = getch(); + if (key == get_action_key(world.keybindings, "player down")) + move_player(&world, 's'); + else if (key == get_action_key(world.keybindings, "player up")) + move_player(&world, 'n'); + else if (key == get_action_key(world.keybindings, "player right")) + move_player(&world, 'e'); + else if (key == get_action_key(world.keybindings, "player left")) + move_player(&world, 'w'); + else if (key == get_action_key(world.keybindings, "wait / next turn")) + player_wait (&world); + else + quit_called = meta_keys(key, &world, &win_meta, &win_keys, &win_map, &win_info, &win_log); + if (1 == quit_called) + break; } } + + free(map.cells); + for (key = 0; key <= world.keyswindata->max; key++) + free(world.keybindings[key].name); + free(world.keybindings); + free(world.keyswindata); + free(world.log); + + endwin(); + return 0; } diff --git a/src/roguelike.h b/src/roguelike.h new file mode 100644 index 0000000..d82313e --- /dev/null +++ b/src/roguelike.h @@ -0,0 +1,40 @@ +struct World { + char interactive; + struct KeyBinding * keybindings; + struct KeysWinData * keyswindata; + uint32_t seed; + uint32_t turn; + char * log; + struct Map * map; + struct Monster * monster; + struct Player * player; }; + +struct Map { + uint16_t width; + uint16_t height; + uint16_t offset_x; + uint16_t offset_y; + char * cells; }; + +struct Player { + uint16_t y; + uint16_t x; }; + +struct Monster { + uint16_t y; + uint16_t x; }; + +uint16_t rrand(char, uint32_t); +void save_game(struct World *); +void toggle_window (struct WinMeta *, struct Win *); +void scroll_pad (struct WinMeta *, char); +void growshrink_active_window (struct WinMeta *, char); +struct Map init_map (); +void map_scroll (struct Map *, char); +void next_turn (struct World *); +void update_log (struct World *, char *); +char is_passable (struct Map *, uint16_t, uint16_t); +void record_action (char); +void move_player (struct World *, char); +void player_wait(struct World *); +unsigned char meta_keys(int, struct World *, struct WinMeta *, struct Win *, struct Win *, struct Win *, struct Win *); diff --git a/src/windows.c b/src/windows.c new file mode 100644 index 0000000..98c8cf8 --- /dev/null +++ b/src/windows.c @@ -0,0 +1,300 @@ +#include +#include +#include +#include +#include "windows.h" + +struct Corners { + struct yx_uint16 tl; + struct yx_uint16 tr; + struct yx_uint16 bl; + struct yx_uint16 br; }; + +static void refit_pad (struct WinMeta *); +static void place_win (struct WinMeta *, struct Win *); +static void update_wins (struct WinMeta *, struct Win *); +static void destroy_win (struct Win *); +static void draw_wins_borders (struct Win *, struct Win *, struct Corners *, uint16_t); +static void draw_win_borders (struct Win *, char); +static void draw_wins (struct Win *); + +extern struct WinMeta init_win_meta (WINDOW * screen) { +// Create and populate WinMeta struct with sane default values. + struct WinMeta wmeta; + wmeta.screen = screen; + wmeta.pad.size.y = getmaxy(screen); + wmeta.pad.size.x = getmaxx(screen); + wmeta.chain_start = 0; + wmeta.chain_end = 0; + wmeta.pad_offset = 0; + wmeta.pad.curses_win = newpad(wmeta.pad.size.y, 1); + wmeta.active = 0; + return wmeta; } + +extern struct Win init_win (struct WinMeta * wmeta, char * title, void * data, void * func) { +// Create and populate Win struct with sane default values. + struct Win w; + w.prev = 0; + w.next = 0; + w.frame.curses_win = 0; + w.title = title; + w.frame.size.x = 20; + w.frame.size.y = wmeta->pad.size.y - 1; + w.data = data; + w.draw = func; + return w; } + +extern void append_win (struct WinMeta * wmeta, struct Win * w) { +// Append win to window chain. Set active, if first window. Update geometry of windows from new window on. + if (0 != wmeta->chain_start) { + w->prev = wmeta->chain_end; + wmeta->chain_end->next = w; } + else { + wmeta->active = w; + wmeta->chain_start = w; } + wmeta->chain_end = w; + update_wins(wmeta, w); } + +static void refit_pad (struct WinMeta * wmeta) { +// Fit pad width to minimum width demanded by current windows' geometries. + uint16_t lastwincol = 0; + struct Win * w_p = wmeta->chain_start; + while (w_p != 0) { + if (w_p->start.x + w_p->frame.size.x > lastwincol + 1) + lastwincol = w_p->start.x + w_p->frame.size.x - 1; + w_p = w_p->next; } + if (getmaxx(wmeta->pad.curses_win) != lastwincol) + wresize(wmeta->pad.curses_win, getmaxy(wmeta->pad.curses_win), lastwincol + 2); } + +extern void suspend_win (struct WinMeta * wmeta, struct Win * w) { +// Destroy win, suspend from chain. Update geometry of following rows and pad, as well as activity selection. + destroy_win(w); + if (wmeta->chain_start != w) // Give win's position in the chain to element next to it in the chain. + w->prev->next = w->next; + else + wmeta->chain_start = w->next; + char pad_refitted = 0; + if (wmeta->chain_end != w) { // Let chain element next to win know its new predecessor. + w->next->prev = w->prev; + if (wmeta->active == w) // If win was active, shift active window pointer to + wmeta->active = w->next; // the next chain element, if that is a window ... + update_wins(wmeta, w->next); + pad_refitted = 1; } + else { + wmeta->chain_end = w->prev; + if (wmeta->active == w) // ... or else to the previous element. + wmeta->active = w->prev; } + w->prev = 0; + w->next = 0; + if (0 == pad_refitted) // Refit pad if necessary. + refit_pad(wmeta); } + +static void place_win (struct WinMeta * wmeta, struct Win * w) { +// Based on position and sizes of previous window, find fitting place for current window. + w->start.x = 0; // if window is first in chain, place it on top-left corner + w->start.y = 1; + if (0 != w->prev) { + struct Win * w_top = w->prev; + while (w_top->start.y != 1) + w_top = w_top->prev; // else, default to placing window in new top + w->start.x = w_top->start.x + w_top->frame.size.x + 1; // column to the right of the last one + uint16_t w_prev_maxy = w->prev->start.y + getmaxy(w->prev->frame.curses_win); + if (w->frame.size.x <= w->prev->frame.size.x && w->frame.size.y < wmeta->pad.size.y - w_prev_maxy) { + w->start.x = w->prev->start.x; // place window below previous window if it fits + w->start.y = w_prev_maxy + 1; } // vertically and is not wider than its predecessor + else { + struct Win * w_up = w->prev; + struct Win * w_upup = w_up; + uint16_t widthdiff; + while (w_up != w_top) { + w_upup = w_up->prev; + while (1) { + if (w_up->start.y != w_upup->start.y) + break; + w_upup = w_upup->prev; } + w_prev_maxy = w_upup->start.y + getmaxy(w_upup->frame.curses_win); + widthdiff = (w_upup->start.x + w_upup->frame.size.x) - (w_up->start.x + w_up->frame.size.x); + if (w->frame.size.y < wmeta->pad.size.y - w_prev_maxy && w->frame.size.x < widthdiff) { + w->start.x = w_up->start.x + w_up->frame.size.x + 1 ; // else try to open new sub column under + w->start.y = w_prev_maxy + 1; // last window below which enough space remains + break; } + w_up = w_upup; } } } } + +static void update_wins (struct WinMeta * wmeta, struct Win * w) { +// Update geometry of win and its next of kin. Destroy (if visible), (re-)build window. If need, resize pad. + if (0 != w->frame.curses_win) + destroy_win (w); + place_win(wmeta, w); + refit_pad(wmeta); + w->frame.curses_win=subpad(wmeta->pad.curses_win, w->frame.size.y, w->frame.size.x, w->start.y, w->start.x); + if (0 != w->next) + update_wins (wmeta, w->next); } + +static void destroy_win (struct Win * w) { +// Delete window. + delwin(w->frame.curses_win); + w->frame.curses_win = 0; } + +static void draw_win_borders (struct Win * w, char active) { +// Draw borders of window win, including title. Decorate in a special way if window is marked as active. + uint16_t y, x; + for (y = w->start.y; y <= w->start.y + w->frame.size.y; y++) { + mvwaddch(wgetparent(w->frame.curses_win), y, w->start.x - 1, '|'); + mvwaddch(wgetparent(w->frame.curses_win), y, w->start.x + w->frame.size.x, '|'); } + for (x = w->start.x; x <= w->start.x + w->frame.size.x; x++) { + mvwaddch(wgetparent(w->frame.curses_win), w->start.y - 1, x, '-'); + mvwaddch(wgetparent(w->frame.curses_win), w->start.y + w->frame.size.y, x, '-'); } + char min_title_length_visible = 3; // 1 char minimal, plus 2 chars for decoration left/right of title + if (w->frame.size.x >= min_title_length_visible) { + uint16_t title_offset = 0; + if (w->frame.size.x > strlen(w->title) + 2) + title_offset = (w->frame.size.x - (strlen(w->title) + 2)) / 2; // + 2 is for decoration + uint16_t length_visible = strnlen(w->title, w->frame.size.x - 2); + char title[length_visible + 3]; + char decoration = ' '; + if (1 == active) + decoration = '$'; + memcpy(title + 1, w->title, length_visible); + title[0] = title[length_visible + 1] = decoration; + title[length_visible + 2] = '\0'; + mvwaddstr(wgetparent(w->frame.curses_win), w->start.y - 1, w->start.x + title_offset, title); } } + +static void draw_wins_borders (struct Win * w, struct Win * w_active, struct Corners * corners, uint16_t i) { +// Call draw_win_borders() for all windows in chain from win on. Save current window's border corners. + char active = 0; + if (w == w_active) + active = 1; + draw_win_borders(w, active); + corners[i].tl.y = w->start.y - 1; + corners[i].tl.x = w->start.x - 1; + corners[i].tr.y = w->start.y - 1; + corners[i].tr.x = w->start.x + w->frame.size.x; + corners[i].bl.y = w->start.y + w->frame.size.y; + corners[i].bl.x = w->start.x - 1; + corners[i].br.y = w->start.y + w->frame.size.y; + corners[i].br.x = w->start.x + w->frame.size.x; + if (0 != w->next) { + draw_wins_borders (w->next, w_active, corners, i + 1); } } + +static void draw_wins (struct Win * w) { +// Draw contents of all windows in window chain from win on. + w->draw(w); + if (0 != w->next) { + draw_wins (w->next); } } + +extern void draw_scroll_hint (struct Frame * frame, uint16_t pos, uint32_t dist, char dir) { +// Draw scroll hint into frame at pos (row or col dependend on dir), mark distance of dist cells into dir. + char more[] = "more"; + char unit_cols[] = "columns"; + char unit_rows[] = "lines"; + uint16_t dsc_space = frame->size.x; + char * unit = unit_rows; + if ('<' == dir || '>' == dir) { + dsc_space = frame->size.y; + unit = unit_cols; } + char * scrolldsc = malloc((4 * sizeof(char)) + strlen(more) + strlen(unit) + 10); // 10 = uint32 max strlen + sprintf(scrolldsc, " %d %s %s ", dist, more, unit); + char offset = 1, q; + if (dsc_space > strlen(scrolldsc) + 1) + offset = (dsc_space - strlen(scrolldsc)) / 2; + chtype symbol; + for (q = 0; q < dsc_space; q++) { + if (q >= offset && q < strlen(scrolldsc) + offset) + symbol = scrolldsc[q - offset] | A_REVERSE; + else + symbol = dir | A_REVERSE; + if ('<' == dir || '>' == dir) + mvwaddch(frame->curses_win, q, pos, symbol); + else + mvwaddch(frame->curses_win, pos, q, symbol); } + free(scrolldsc); } + +extern void draw_all_wins (struct WinMeta * wmeta) { +// Draw pad with all windows and their borders, plus scrolling hints. + erase(); + wnoutrefresh(wmeta->screen); + werase(wmeta->pad.curses_win); + if (wmeta->chain_start) { + uint16_t n_wins = 1, i; + struct Win * win_p = wmeta->chain_start; + while (0 != win_p->next) { + win_p = win_p->next; + n_wins++; } + struct Corners * all_corners = malloc(sizeof(struct Corners) * n_wins); + draw_wins (wmeta->chain_start); + draw_wins_borders (wmeta->chain_start, wmeta->active, all_corners, 0); + for (i = 0; i < n_wins; i++) { + mvwaddch(wmeta->pad.curses_win, all_corners[i].tl.y, all_corners[i].tl.x, '+'); + mvwaddch(wmeta->pad.curses_win, all_corners[i].tr.y, all_corners[i].tr.x, '+'); + mvwaddch(wmeta->pad.curses_win, all_corners[i].bl.y, all_corners[i].bl.x, '+'); + mvwaddch(wmeta->pad.curses_win, all_corners[i].br.y, all_corners[i].br.x, '+'); } + free(all_corners); + if (wmeta->pad_offset > 0) + draw_scroll_hint(&wmeta->pad, wmeta->pad_offset, wmeta->pad_offset + 1, '<'); + if (wmeta->pad_offset + wmeta->pad.size.x < getmaxx(wmeta->pad.curses_win) - 1) + draw_scroll_hint(&wmeta->pad, wmeta->pad_offset + wmeta->pad.size.x - 1, + getmaxx(wmeta->pad.curses_win) - (wmeta->pad_offset + wmeta->pad.size.x), '>'); + pnoutrefresh(wmeta->pad.curses_win, 0, wmeta->pad_offset, 0, 0, wmeta->pad.size.y, wmeta->pad.size.x-1); } + doupdate(); } + +extern void resize_active_win (struct WinMeta * wmeta, uint16_t height, uint16_t width) { +// Grow or shrink currently active window. Correct its geometry and that of its followers. + if (0 != wmeta->active && width > 0 && height > 0 && height < wmeta->pad.size.y) { + wmeta->active->frame.size.y = height; + wmeta->active->frame.size.x = width; + update_wins(wmeta, wmeta->chain_start); } } + +extern void cycle_active_win (struct WinMeta * wmeta, char dir) { +// Cycle active window selection forwards (dir = 'n') or backwards. + if (0 != wmeta->active) { + if ('n' == dir) { + if (wmeta->active->next != 0) + wmeta->active = wmeta->active->next; + else + wmeta->active = wmeta->chain_start; } + else { + if (wmeta->active->prev != 0) + wmeta->active = wmeta->active->prev; + else + wmeta->active = wmeta->chain_end; } } } + +extern void shift_active_win (struct WinMeta * wmeta, char dir) { +// Move active window forward/backward in window chain. If jumping beyond start/end, move to other chain end. + if (0 != wmeta->active && wmeta->chain_start != wmeta->chain_end && (dir == 'f' || dir == 'b')) { + struct Win * w_shift = wmeta->active, * w_p, * w_p_next; + char wrap = 0; + if ((dir == 'f' && w_shift == wmeta->chain_end) || (dir == 'b' && w_shift == wmeta->chain_start)) + wrap = 1; + uint16_t i, i_max; + for (i_max = 1, w_p = wmeta->chain_start; w_p != wmeta->chain_end; i_max++) + w_p = w_p->next; + struct Win ** wins = malloc(i_max * sizeof(struct Win *)); + for (i = 0, w_p = wmeta->chain_start; i < i_max; i++) { + w_p_next = w_p->next; + suspend_win(wmeta, w_p); + wins[i] = w_p; + w_p = w_p_next; } + if (wrap) + if (dir == 'f') { + append_win(wmeta, w_shift); + for (i = 0; i < i_max - 1; i++) + append_win(wmeta, wins[i]); } + else { + for (i = 1; i < i_max; i++) + append_win(wmeta, wins[i]); + append_win(wmeta, w_shift); } + else + for (i = 0; i < i_max; i++) + if ((dir == 'f' && w_shift == wins[i]) || (dir == 'b' && w_shift == wins[i+1])) { + append_win(wmeta, wins[i+1]); + append_win(wmeta, wins[i]); + i++; } + else + append_win(wmeta, wins[i]); + free(wins); + wmeta->active = w_shift; } } + +extern void reset_pad_offset(struct WinMeta * wmeta, uint16_t new_offset) { +// Apply new_offset to windows pad, if it proves to be sane. + if (new_offset >= 0 && new_offset + wmeta->pad.size.x < getmaxx(wmeta->pad.curses_win)) + wmeta->pad_offset = new_offset; } diff --git a/src/windows.h b/src/windows.h new file mode 100644 index 0000000..c194907 --- /dev/null +++ b/src/windows.h @@ -0,0 +1,35 @@ +struct yx_uint16 { + uint16_t y; + uint16_t x; }; + +struct Frame { + WINDOW * curses_win; + struct yx_uint16 size; }; + +struct WinMeta { + WINDOW * screen; + uint16_t pad_offset; + struct Frame pad; + struct Win * chain_start; + struct Win * chain_end; + struct Win * active; }; + +struct Win { + struct Win * prev; + struct Win * next; + struct yx_uint16 start; + struct Frame frame; + char * title; + void (* draw) (struct Win *); + void * data; }; + +extern struct WinMeta init_win_meta (WINDOW *); +extern struct Win init_win (struct WinMeta *, char *, void *, void *); +extern void append_win (struct WinMeta *, struct Win *); +extern void suspend_win (struct WinMeta *, struct Win *); +extern void draw_scroll_hint (struct Frame *, uint16_t, uint32_t, char); +extern void draw_all_wins (struct WinMeta *); +extern void resize_active_win (struct WinMeta *, uint16_t, uint16_t); +extern void cycle_active_win (struct WinMeta *, char); +extern void shift_active_win (struct WinMeta *, char); +extern void reset_pad_offset (struct WinMeta *, uint16_t); diff --git a/windows.c b/windows.c deleted file mode 100644 index 98c8cf8..0000000 --- a/windows.c +++ /dev/null @@ -1,300 +0,0 @@ -#include -#include -#include -#include -#include "windows.h" - -struct Corners { - struct yx_uint16 tl; - struct yx_uint16 tr; - struct yx_uint16 bl; - struct yx_uint16 br; }; - -static void refit_pad (struct WinMeta *); -static void place_win (struct WinMeta *, struct Win *); -static void update_wins (struct WinMeta *, struct Win *); -static void destroy_win (struct Win *); -static void draw_wins_borders (struct Win *, struct Win *, struct Corners *, uint16_t); -static void draw_win_borders (struct Win *, char); -static void draw_wins (struct Win *); - -extern struct WinMeta init_win_meta (WINDOW * screen) { -// Create and populate WinMeta struct with sane default values. - struct WinMeta wmeta; - wmeta.screen = screen; - wmeta.pad.size.y = getmaxy(screen); - wmeta.pad.size.x = getmaxx(screen); - wmeta.chain_start = 0; - wmeta.chain_end = 0; - wmeta.pad_offset = 0; - wmeta.pad.curses_win = newpad(wmeta.pad.size.y, 1); - wmeta.active = 0; - return wmeta; } - -extern struct Win init_win (struct WinMeta * wmeta, char * title, void * data, void * func) { -// Create and populate Win struct with sane default values. - struct Win w; - w.prev = 0; - w.next = 0; - w.frame.curses_win = 0; - w.title = title; - w.frame.size.x = 20; - w.frame.size.y = wmeta->pad.size.y - 1; - w.data = data; - w.draw = func; - return w; } - -extern void append_win (struct WinMeta * wmeta, struct Win * w) { -// Append win to window chain. Set active, if first window. Update geometry of windows from new window on. - if (0 != wmeta->chain_start) { - w->prev = wmeta->chain_end; - wmeta->chain_end->next = w; } - else { - wmeta->active = w; - wmeta->chain_start = w; } - wmeta->chain_end = w; - update_wins(wmeta, w); } - -static void refit_pad (struct WinMeta * wmeta) { -// Fit pad width to minimum width demanded by current windows' geometries. - uint16_t lastwincol = 0; - struct Win * w_p = wmeta->chain_start; - while (w_p != 0) { - if (w_p->start.x + w_p->frame.size.x > lastwincol + 1) - lastwincol = w_p->start.x + w_p->frame.size.x - 1; - w_p = w_p->next; } - if (getmaxx(wmeta->pad.curses_win) != lastwincol) - wresize(wmeta->pad.curses_win, getmaxy(wmeta->pad.curses_win), lastwincol + 2); } - -extern void suspend_win (struct WinMeta * wmeta, struct Win * w) { -// Destroy win, suspend from chain. Update geometry of following rows and pad, as well as activity selection. - destroy_win(w); - if (wmeta->chain_start != w) // Give win's position in the chain to element next to it in the chain. - w->prev->next = w->next; - else - wmeta->chain_start = w->next; - char pad_refitted = 0; - if (wmeta->chain_end != w) { // Let chain element next to win know its new predecessor. - w->next->prev = w->prev; - if (wmeta->active == w) // If win was active, shift active window pointer to - wmeta->active = w->next; // the next chain element, if that is a window ... - update_wins(wmeta, w->next); - pad_refitted = 1; } - else { - wmeta->chain_end = w->prev; - if (wmeta->active == w) // ... or else to the previous element. - wmeta->active = w->prev; } - w->prev = 0; - w->next = 0; - if (0 == pad_refitted) // Refit pad if necessary. - refit_pad(wmeta); } - -static void place_win (struct WinMeta * wmeta, struct Win * w) { -// Based on position and sizes of previous window, find fitting place for current window. - w->start.x = 0; // if window is first in chain, place it on top-left corner - w->start.y = 1; - if (0 != w->prev) { - struct Win * w_top = w->prev; - while (w_top->start.y != 1) - w_top = w_top->prev; // else, default to placing window in new top - w->start.x = w_top->start.x + w_top->frame.size.x + 1; // column to the right of the last one - uint16_t w_prev_maxy = w->prev->start.y + getmaxy(w->prev->frame.curses_win); - if (w->frame.size.x <= w->prev->frame.size.x && w->frame.size.y < wmeta->pad.size.y - w_prev_maxy) { - w->start.x = w->prev->start.x; // place window below previous window if it fits - w->start.y = w_prev_maxy + 1; } // vertically and is not wider than its predecessor - else { - struct Win * w_up = w->prev; - struct Win * w_upup = w_up; - uint16_t widthdiff; - while (w_up != w_top) { - w_upup = w_up->prev; - while (1) { - if (w_up->start.y != w_upup->start.y) - break; - w_upup = w_upup->prev; } - w_prev_maxy = w_upup->start.y + getmaxy(w_upup->frame.curses_win); - widthdiff = (w_upup->start.x + w_upup->frame.size.x) - (w_up->start.x + w_up->frame.size.x); - if (w->frame.size.y < wmeta->pad.size.y - w_prev_maxy && w->frame.size.x < widthdiff) { - w->start.x = w_up->start.x + w_up->frame.size.x + 1 ; // else try to open new sub column under - w->start.y = w_prev_maxy + 1; // last window below which enough space remains - break; } - w_up = w_upup; } } } } - -static void update_wins (struct WinMeta * wmeta, struct Win * w) { -// Update geometry of win and its next of kin. Destroy (if visible), (re-)build window. If need, resize pad. - if (0 != w->frame.curses_win) - destroy_win (w); - place_win(wmeta, w); - refit_pad(wmeta); - w->frame.curses_win=subpad(wmeta->pad.curses_win, w->frame.size.y, w->frame.size.x, w->start.y, w->start.x); - if (0 != w->next) - update_wins (wmeta, w->next); } - -static void destroy_win (struct Win * w) { -// Delete window. - delwin(w->frame.curses_win); - w->frame.curses_win = 0; } - -static void draw_win_borders (struct Win * w, char active) { -// Draw borders of window win, including title. Decorate in a special way if window is marked as active. - uint16_t y, x; - for (y = w->start.y; y <= w->start.y + w->frame.size.y; y++) { - mvwaddch(wgetparent(w->frame.curses_win), y, w->start.x - 1, '|'); - mvwaddch(wgetparent(w->frame.curses_win), y, w->start.x + w->frame.size.x, '|'); } - for (x = w->start.x; x <= w->start.x + w->frame.size.x; x++) { - mvwaddch(wgetparent(w->frame.curses_win), w->start.y - 1, x, '-'); - mvwaddch(wgetparent(w->frame.curses_win), w->start.y + w->frame.size.y, x, '-'); } - char min_title_length_visible = 3; // 1 char minimal, plus 2 chars for decoration left/right of title - if (w->frame.size.x >= min_title_length_visible) { - uint16_t title_offset = 0; - if (w->frame.size.x > strlen(w->title) + 2) - title_offset = (w->frame.size.x - (strlen(w->title) + 2)) / 2; // + 2 is for decoration - uint16_t length_visible = strnlen(w->title, w->frame.size.x - 2); - char title[length_visible + 3]; - char decoration = ' '; - if (1 == active) - decoration = '$'; - memcpy(title + 1, w->title, length_visible); - title[0] = title[length_visible + 1] = decoration; - title[length_visible + 2] = '\0'; - mvwaddstr(wgetparent(w->frame.curses_win), w->start.y - 1, w->start.x + title_offset, title); } } - -static void draw_wins_borders (struct Win * w, struct Win * w_active, struct Corners * corners, uint16_t i) { -// Call draw_win_borders() for all windows in chain from win on. Save current window's border corners. - char active = 0; - if (w == w_active) - active = 1; - draw_win_borders(w, active); - corners[i].tl.y = w->start.y - 1; - corners[i].tl.x = w->start.x - 1; - corners[i].tr.y = w->start.y - 1; - corners[i].tr.x = w->start.x + w->frame.size.x; - corners[i].bl.y = w->start.y + w->frame.size.y; - corners[i].bl.x = w->start.x - 1; - corners[i].br.y = w->start.y + w->frame.size.y; - corners[i].br.x = w->start.x + w->frame.size.x; - if (0 != w->next) { - draw_wins_borders (w->next, w_active, corners, i + 1); } } - -static void draw_wins (struct Win * w) { -// Draw contents of all windows in window chain from win on. - w->draw(w); - if (0 != w->next) { - draw_wins (w->next); } } - -extern void draw_scroll_hint (struct Frame * frame, uint16_t pos, uint32_t dist, char dir) { -// Draw scroll hint into frame at pos (row or col dependend on dir), mark distance of dist cells into dir. - char more[] = "more"; - char unit_cols[] = "columns"; - char unit_rows[] = "lines"; - uint16_t dsc_space = frame->size.x; - char * unit = unit_rows; - if ('<' == dir || '>' == dir) { - dsc_space = frame->size.y; - unit = unit_cols; } - char * scrolldsc = malloc((4 * sizeof(char)) + strlen(more) + strlen(unit) + 10); // 10 = uint32 max strlen - sprintf(scrolldsc, " %d %s %s ", dist, more, unit); - char offset = 1, q; - if (dsc_space > strlen(scrolldsc) + 1) - offset = (dsc_space - strlen(scrolldsc)) / 2; - chtype symbol; - for (q = 0; q < dsc_space; q++) { - if (q >= offset && q < strlen(scrolldsc) + offset) - symbol = scrolldsc[q - offset] | A_REVERSE; - else - symbol = dir | A_REVERSE; - if ('<' == dir || '>' == dir) - mvwaddch(frame->curses_win, q, pos, symbol); - else - mvwaddch(frame->curses_win, pos, q, symbol); } - free(scrolldsc); } - -extern void draw_all_wins (struct WinMeta * wmeta) { -// Draw pad with all windows and their borders, plus scrolling hints. - erase(); - wnoutrefresh(wmeta->screen); - werase(wmeta->pad.curses_win); - if (wmeta->chain_start) { - uint16_t n_wins = 1, i; - struct Win * win_p = wmeta->chain_start; - while (0 != win_p->next) { - win_p = win_p->next; - n_wins++; } - struct Corners * all_corners = malloc(sizeof(struct Corners) * n_wins); - draw_wins (wmeta->chain_start); - draw_wins_borders (wmeta->chain_start, wmeta->active, all_corners, 0); - for (i = 0; i < n_wins; i++) { - mvwaddch(wmeta->pad.curses_win, all_corners[i].tl.y, all_corners[i].tl.x, '+'); - mvwaddch(wmeta->pad.curses_win, all_corners[i].tr.y, all_corners[i].tr.x, '+'); - mvwaddch(wmeta->pad.curses_win, all_corners[i].bl.y, all_corners[i].bl.x, '+'); - mvwaddch(wmeta->pad.curses_win, all_corners[i].br.y, all_corners[i].br.x, '+'); } - free(all_corners); - if (wmeta->pad_offset > 0) - draw_scroll_hint(&wmeta->pad, wmeta->pad_offset, wmeta->pad_offset + 1, '<'); - if (wmeta->pad_offset + wmeta->pad.size.x < getmaxx(wmeta->pad.curses_win) - 1) - draw_scroll_hint(&wmeta->pad, wmeta->pad_offset + wmeta->pad.size.x - 1, - getmaxx(wmeta->pad.curses_win) - (wmeta->pad_offset + wmeta->pad.size.x), '>'); - pnoutrefresh(wmeta->pad.curses_win, 0, wmeta->pad_offset, 0, 0, wmeta->pad.size.y, wmeta->pad.size.x-1); } - doupdate(); } - -extern void resize_active_win (struct WinMeta * wmeta, uint16_t height, uint16_t width) { -// Grow or shrink currently active window. Correct its geometry and that of its followers. - if (0 != wmeta->active && width > 0 && height > 0 && height < wmeta->pad.size.y) { - wmeta->active->frame.size.y = height; - wmeta->active->frame.size.x = width; - update_wins(wmeta, wmeta->chain_start); } } - -extern void cycle_active_win (struct WinMeta * wmeta, char dir) { -// Cycle active window selection forwards (dir = 'n') or backwards. - if (0 != wmeta->active) { - if ('n' == dir) { - if (wmeta->active->next != 0) - wmeta->active = wmeta->active->next; - else - wmeta->active = wmeta->chain_start; } - else { - if (wmeta->active->prev != 0) - wmeta->active = wmeta->active->prev; - else - wmeta->active = wmeta->chain_end; } } } - -extern void shift_active_win (struct WinMeta * wmeta, char dir) { -// Move active window forward/backward in window chain. If jumping beyond start/end, move to other chain end. - if (0 != wmeta->active && wmeta->chain_start != wmeta->chain_end && (dir == 'f' || dir == 'b')) { - struct Win * w_shift = wmeta->active, * w_p, * w_p_next; - char wrap = 0; - if ((dir == 'f' && w_shift == wmeta->chain_end) || (dir == 'b' && w_shift == wmeta->chain_start)) - wrap = 1; - uint16_t i, i_max; - for (i_max = 1, w_p = wmeta->chain_start; w_p != wmeta->chain_end; i_max++) - w_p = w_p->next; - struct Win ** wins = malloc(i_max * sizeof(struct Win *)); - for (i = 0, w_p = wmeta->chain_start; i < i_max; i++) { - w_p_next = w_p->next; - suspend_win(wmeta, w_p); - wins[i] = w_p; - w_p = w_p_next; } - if (wrap) - if (dir == 'f') { - append_win(wmeta, w_shift); - for (i = 0; i < i_max - 1; i++) - append_win(wmeta, wins[i]); } - else { - for (i = 1; i < i_max; i++) - append_win(wmeta, wins[i]); - append_win(wmeta, w_shift); } - else - for (i = 0; i < i_max; i++) - if ((dir == 'f' && w_shift == wins[i]) || (dir == 'b' && w_shift == wins[i+1])) { - append_win(wmeta, wins[i+1]); - append_win(wmeta, wins[i]); - i++; } - else - append_win(wmeta, wins[i]); - free(wins); - wmeta->active = w_shift; } } - -extern void reset_pad_offset(struct WinMeta * wmeta, uint16_t new_offset) { -// Apply new_offset to windows pad, if it proves to be sane. - if (new_offset >= 0 && new_offset + wmeta->pad.size.x < getmaxx(wmeta->pad.curses_win)) - wmeta->pad_offset = new_offset; } diff --git a/windows.h b/windows.h deleted file mode 100644 index c194907..0000000 --- a/windows.h +++ /dev/null @@ -1,35 +0,0 @@ -struct yx_uint16 { - uint16_t y; - uint16_t x; }; - -struct Frame { - WINDOW * curses_win; - struct yx_uint16 size; }; - -struct WinMeta { - WINDOW * screen; - uint16_t pad_offset; - struct Frame pad; - struct Win * chain_start; - struct Win * chain_end; - struct Win * active; }; - -struct Win { - struct Win * prev; - struct Win * next; - struct yx_uint16 start; - struct Frame frame; - char * title; - void (* draw) (struct Win *); - void * data; }; - -extern struct WinMeta init_win_meta (WINDOW *); -extern struct Win init_win (struct WinMeta *, char *, void *, void *); -extern void append_win (struct WinMeta *, struct Win *); -extern void suspend_win (struct WinMeta *, struct Win *); -extern void draw_scroll_hint (struct Frame *, uint16_t, uint32_t, char); -extern void draw_all_wins (struct WinMeta *); -extern void resize_active_win (struct WinMeta *, uint16_t, uint16_t); -extern void cycle_active_win (struct WinMeta *, char); -extern void shift_active_win (struct WinMeta *, char); -extern void reset_pad_offset (struct WinMeta *, uint16_t);