From a3c0f0cc2892e7efdaa4c85c844aa4b1ad1e077f Mon Sep 17 00:00:00 2001 From: Christian Heller Date: Mon, 22 Apr 2013 04:11:51 +0200 Subject: [PATCH] Uploading current state of work. --- Makefile | 7 ++ roguelike.c | 240 +++++++++++++++++++++++++++++++++++++++++++++++++++ windows.c | 241 ++++++++++++++++++++++++++++++++++++++++++++++++++++ windows.h | 34 ++++++++ 4 files changed, 522 insertions(+) create mode 100644 Makefile create mode 100644 roguelike.c create mode 100644 windows.c create mode 100644 windows.h diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..7a60ae0 --- /dev/null +++ b/Makefile @@ -0,0 +1,7 @@ +roguelike: + cc -g -o windows.o -c windows.c + cc -g -o roguelike.o -c roguelike.c + cc -g -o roguelike windows.o roguelike.o -lncurses + +clean: + rm *.o; rm roguelike diff --git a/roguelike.c b/roguelike.c new file mode 100644 index 0000000..c2e4510 --- /dev/null +++ b/roguelike.c @@ -0,0 +1,240 @@ +#include +#include +#include +#include +#include "windows.h" + +struct Map { + int width; + int height; + int offset_x; + int offset_y; + int player_x; + int player_y; + char * cells; }; + +void draw_with_linebreaks (struct Win * win, char * text, int start_y) { +// Write text into window content space. Start on row start_y. Fill unused rows with whitespace. + int x, y; + char toggle; + int height_av = win->height - 1; + int width_av = win->width - 1; + char fin = 0; + int z = -1; + for (y = start_y; y < height_av; y++) { + if (0 == fin) + toggle = 0; + for (x = 0; x < width_av; x++) { + if (0 == toggle) { + z++; + if ('\n' == text[z]) { + mvwaddch(win->curses_win, y+1, x+win->border_left, ' '); + toggle = 1; + continue; } + else + mvwaddch(win->curses_win, y+1, x+win->border_left, text[z]); + if ('\n' == text[z+1]) { + z++; + toggle = 1; } + else if (0 == text[z+1]) { + toggle = 1; + fin = 1; } } + else + mvwaddch(win->curses_win, y+1, x+win->border_left, ' '); } } } + +void draw_text_from_bottom (struct Win * win) { +// Draw text in win->data from end/bottom to the top. + char * text = (char *) win->data; + int width_av = win->width - 1; + int height_av = win->height - 1; + char toggle = 0; + int x, y, offset; + int z = -1; + for (y = 0; 0 == toggle; y++) // Determine number of lines text would have in a window of available + for (x = 0; x < width_av; x++) { // width, but infinite height. + z++; + if ('\n' == text[z]) // Treat \n and \0 as control characters for incrementing y and + break; // stopping the loop. Make sure they don't count as cell space + if ('\n' == text[z+1]) { // themselves. + z++; + break; } + else if (0 == text[z+1]) { + toggle = 1; + break; } } + z = -1; + int start_y = 0; + if (y < height_av) { // Depending on what is bigger, determine start point in window or in text. + start_y = height_av - y; + for (y = 0; y < start_y; y++) + for (x = 0; x < width_av; x++) + mvwaddch(win->curses_win, y+1, x+win->border_left, ' '); } + else if (y > height_av) { + offset = y - height_av; + for (y = 0; y < offset; y++) + for (x = 0; x < width_av; 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_map (struct Win * win) { +// Draw map determined by win->data Map struct into window. Respect offset. + int height_av = win->height - 1; + int width_av = win->width - 1; + struct Map map = * (struct Map *) win->data; + char * cells = map.cells; + int width_map_av = map.width - map.offset_x; + int height_map_av = map.height - map.offset_y; + int x, y, z; + for (y = 0; y < height_av; y++) { + z = map.offset_x + (map.offset_y + y) * (map.width); + for (x = 0; x < width_av; x++) { + if (y < height_map_av && x < width_map_av) { + if (z == (map.width * map.player_y) + map.player_x) + mvwaddch(win->curses_win, y+1, x+win->border_left, '@'); + else + mvwaddch(win->curses_win, y+1, x+win->border_left, cells[z]); + z++; } + else + mvwaddch(win->curses_win, y+1, x+win->border_left, ' '); } } } + +void draw_info (struct Win * win) { +// Draw info window by appending win->data integer value to "Turn: " display. + int count = * (int *) win->data; + char text[100]; + snprintf(text, 100, "Turn: %d", count); + draw_with_linebreaks(win, text, 0); } + +void toggle_window (struct WinMeta * win_meta, struct Win * win) { +// Toggle display of window win. + if (0 != win->curses_win) + suspend_window(win_meta, win); + else + append_window(win_meta, win); } + +struct Map init_map () { +// Initialize map with some experimental start values. + struct Map map; + map.width = 128; + map.height = 128; + map.offset_x = 0; + map.offset_y = 0; + map.player_x = 2; + map.player_y = 2; + map.cells = malloc(map.width * map.height); + int x, y; + for (y = 0; y < map.height; y++) + for (x = 0; x < map.width; x++) + map.cells[(y * map.width) + x] = '.'; + map.cells[(5 * map.width) + 5] = 'X'; + map.cells[(3 * map.width) + 8] = 'X'; + map.cells[(8 * map.width) + 3] = 'X'; + return map; } + +void update_info (struct Win * win) { +// Update info data by incrementing turn value. + * (int *) win->data = * (int *) win->data + 1; } + +void update_log (struct Win * win, char * text) { +// Update log with new text to be appended. + char * new_text; + int len_old = strlen(win->data); + int len_new = strlen(text); + int len_whole = len_old + len_new + 1; + new_text = calloc(len_whole, sizeof(char)); + memcpy(new_text, win->data, len_old); + memcpy(new_text + len_old, text, len_new); + free(win->data); + win->data = new_text; } + +int main () { + WINDOW * screen = initscr(); + noecho(); + curs_set(0); + struct WinMeta win_meta = init_win_meta(screen); + + struct Win win_map = init_window(&win_meta, "Map"); + win_map.draw = draw_map; + struct Map map = init_map(); + win_map.data = ↦ + + struct Win win_info = init_window(&win_meta, "Info"); + win_info.draw = draw_info; + win_info.data = malloc(sizeof(int)); + * (int *) win_info.data = 0; + + struct Win win_log = init_window(&win_meta, "Log"); + win_log.draw = draw_text_from_bottom; + win_log.data = calloc(1, sizeof(char)); + update_log (&win_log, "Start!"); + + char key; + while (1) { + key = getch(); + if (key == 'Q') // quit + break; + else if (key == '1') // toggle map window + toggle_window(&win_meta, &win_map); + else if (key == '2') // toggle info window + toggle_window(&win_meta, &win_info); + else if (key == '3') // toggle log window + toggle_window(&win_meta, &win_log); + else if (key == 'x') { // scroll map down + map.offset_y++; + draw_all_windows (&win_meta); } + else if (key == 'w' && map.offset_y > 0) { // scroll map up + map.offset_y--; + draw_all_windows (&win_meta); } + else if (key == 'd') { // scroll map right + map.offset_x++; + draw_all_windows (&win_meta); } + else if (key == 'a' && map.offset_x > 0) { // scroll map left + map.offset_x--; + draw_all_windows (&win_meta); } + else if (key == 'b' && map.player_y < map.height - 1) { // move player south + update_info (&win_info); + update_log (&win_log, "\nYou move south."); + map.player_y++; + draw_all_windows (&win_meta); } + else if (key == 't' && map.player_y > 0) { // move player north + update_info (&win_info); + update_log (&win_log, "\nYou move north."); + map.player_y--; + draw_all_windows (&win_meta); } + else if (key == 'h' && map.player_x < map.width - 1) { // move player east + update_info (&win_info); + update_log (&win_log, "\nYou move east."); + map.player_x++; + draw_all_windows (&win_meta); } + else if (key == 'f' && map.player_x > 0) { // move player west + update_info (&win_info); + update_log (&win_log, "\nYou move west."); + map.player_x--; + draw_all_windows (&win_meta); } + else if (key == '.') { // wait + update_info (&win_info); + update_log (&win_log, "\nYou wait."); + draw_all_windows(&win_meta); } + else if (key == '>' && win_meta.active != 0) // cycle forwards + cycle_active_window(&win_meta, 'n'); + else if (key == '<' && win_meta.active != 0) // cycle backwards + cycle_active_window(&win_meta, 'p'); + else if (key == 'y' && win_meta.active != 0) // shift window forwards + shift_window(&win_meta, 'f'); + else if (key == 'Y' && win_meta.active != 0) // shift window backwards + shift_window(&win_meta, 'b'); + else if (key == '*' && win_meta.active != 0) // grow window horizontally + resize_window(&win_meta, '*'); + else if (key == '_' && win_meta.active != 0) // shrink window horizontally + resize_window(&win_meta, '_'); + else if (key == '+' && win_meta.active != 0) // grow window vertically + resize_window(&win_meta, '+'); + else if (key == '-' && win_meta.active != 0) // shrink window vertically + resize_window(&win_meta, '-'); } + + endwin(); + return 0; } diff --git a/windows.c b/windows.c new file mode 100644 index 0000000..f879e6b --- /dev/null +++ b/windows.c @@ -0,0 +1,241 @@ +#include +#include +#include +#include "windows.h" + +struct WinMeta init_win_meta (WINDOW * screen) { +// Create and populate WinMeta struct with sane default values. + struct WinMeta win_meta; + win_meta.height = screen->_maxy + 1; + win_meta.chain_start = 0; + win_meta.chain_end = 0; + return win_meta; } + +struct Win init_window (struct WinMeta * win_meta, char * title) { +// Create and populate Win struct with sane default values. + struct Win win; + win.prev = 0; + win.next = 0; + win.curses_win = 0; + win.title = title; + win.width = 20; + win.height = win_meta->height; + return win; } + +void append_window (struct WinMeta * win_meta, struct Win * win) { +// Append win to window chain. Set active, if first window. Update geometry of windows from new window on. + if (0 != win_meta->chain_start) { + win->prev = win_meta->chain_end; + win_meta->chain_end->next = win; } + else { + win_meta->active = win; + win_meta->chain_start = win; } + win_meta->chain_end = win; + update_windows(win_meta, win); + draw_all_windows(win_meta); } + +void suspend_window (struct WinMeta * win_meta, struct Win * win) { +// Destroy win, suspend from window chain. Update geometry of following rows, as well as activity selection. + destroy_window(win); + if (win_meta->chain_start != win) // Give win's position in the chain to element next to it in the chain. + win->prev->next = win->next; + else + win_meta->chain_start = win->next; + if (win_meta->chain_end != win) { // Let chain element next to win know its new predecessor. + win->next->prev = win->prev; + if (win_meta->active == win) // If win was active, shift active window pointer to ... + win_meta->active = win->next; // ... the next chain element, if that is a window ... + update_windows(win_meta, win->next); } + else { + win_meta->chain_end = win->prev; + if (win_meta->active == win) // ... or else to the previous element. + win_meta->active = win->prev; } + win->prev = 0; + win->next = 0; + if (0 != win_meta->chain_start) + draw_all_windows(win_meta); } + +void place_window (struct WinMeta * win_meta, struct Win * win) { +// Based on position and sizes of previous window, find fitting place for current window. + win->start_x = 0; // if window is first in chain, place it on top-left corner + win->start_y = 0; + if (0 != win->prev) { + win->start_x = win->prev->start_x + win->prev->width; // next best default: open new window column with it + if (win->prev->height < win_meta->height) { // ... unless the previous window does not fill a whole column + struct Win * last_ceiling; + last_ceiling = win->prev; + while (last_ceiling->start_y != 0 // determine last window serving as a + && (last_ceiling->prev->start_y == last_ceiling->start_y // ceiling to other windows or filling + || last_ceiling->prev->width > last_ceiling->width)) // the whole last column's width + last_ceiling = last_ceiling->prev; + if (win->prev == last_ceiling) { + if (win->width <= win->prev->width + && win->prev->start_y + win->prev->height + win->height <= win_meta->height) { + win->start_x = win->prev->start_x; // if prev window is last ceiling, try to + win->start_y = win->prev->start_y + win->prev->height; } } // fit window below it; else: use default + else { + int remaining_width = last_ceiling->width; // calculate free width remaining in last row of last + struct Win * win_p = last_ceiling->next; // window column + while (win != win_p) { + remaining_width = remaining_width - win_p->width; + win_p = win_p->next; } + if (win->width <= remaining_width && win->height <= win->prev->height) { // if enough space left in + win->start_y = win->prev->start_y; // last column, place window + win->start_x = win->prev->start_x + win->prev->width; } // here + else if (win->width <= last_ceiling->width + && win->height + win->prev->height + win->prev->start_y <= win_meta->height ) { + win->start_y = last_ceiling->next->start_y + last_ceiling->next->height; // else, try to put it + win->start_x = last_ceiling->start_x; } // below + else // else, put it next to max + win->start_x = last_ceiling->width + last_ceiling->start_x; } } } } // width of the last last column + +void update_windows (struct WinMeta * win_meta, struct Win * win) { +// Update geometry of win and its next of kin. Before, destroy window, if visible. After, (re-)build it. + if (0 != win->curses_win) + destroy_window (win); + place_window(win_meta, win); + if (win->start_y + win->height < win_meta->height) // dependent on window position, + win->border_down = 1; // append space for borders to be drawn + else + win->border_down = 0; + if (win->start_x > 0) + win->border_left = 1; + else + win->border_left = 0; + win->curses_win = newwin(win->height + win->border_down, win->width + win->border_left, win->start_y, win->start_x - win->border_left); + if (0 != win->next) + update_windows (win_meta, win->next); } + +void destroy_window (struct Win * win) { +// Undraw and delete window. + undraw_window (win->curses_win); + delwin(win->curses_win); + win->curses_win = 0; } + +void draw_windows (struct WinMeta * win_meta, struct Win * win) { +// Draw all windows from the current one on. + draw_window(win_meta, win); + if (0 != win->next) + draw_windows (win_meta, win->next); } + +void draw_all_windows (struct WinMeta * win_meta) { +// Draw all windows from the chain start on. + draw_windows (win_meta, win_meta->chain_start); } + +void draw_window(struct WinMeta * win_meta, struct Win * win) { +// Draw win's content, including border and title (the latter dependent on space available for it). + char ls = '|'; + char rs = '|'; + char ts = '-'; + char bs = '-'; + char tl = '-'; + char tr = '+'; + char bl = '|'; + char br = '|'; + if (1 == win->border_down) { + bl = '+'; + br = '+'; } + if (1 == win->border_left) + tl = '+'; + wborder(win->curses_win, ls, rs, ts, bs, tl, tr, bl, br); + char min_title_length_visible = 3; // 1 char minimal, plus 2 chars for decoration left/right of title + if (win->width > min_title_length_visible) { + int title_length = strlen(win->title); + int title_offset = (((win->width) - (title_length + 2)) / 2) + win->border_left; // + 2 is for decoration + if (title_offset < win->border_left) + title_offset = win->border_left; + int length_visible = strnlen(win->title, win->width - min_title_length_visible); + char title[length_visible + 3]; + char decoration = ' '; + if (win_meta->active == win) + decoration = '$'; + memcpy(title + 1, win->title, length_visible); + title[0] = title[length_visible + 1] = decoration; + title[length_visible + 2] = '\0'; + mvwaddstr(win->curses_win, 0, title_offset, title); } + if (win->height > 1 && win->width > 1) ; + win->draw(win); + wrefresh(win->curses_win); } + +void undraw_window (WINDOW * win) { +// Fill entire window with whitespace. + int y, x; + for (y = 0; y <= win->_maxy; y++) + for (x = 0; x <= win->_maxx; x++) + mvwaddch(win, y, x, ' '); + wrefresh(win); } + +void resize_window (struct WinMeta * win_meta, char change) { +// Grow or shrink currently active window. Correct its geometry and that of its followers. + if (change == '-' && win_meta->active->height > 2) + win_meta->active->height--; + else if (change == '+' && win_meta->active->height < win_meta->height) + win_meta->active->height++; + else if (change == '_' && win_meta->active->width > 2) + win_meta->active->width--; + else if (change == '*') + win_meta->active->width++; + update_windows(win_meta, win_meta->chain_start); + draw_all_windows(win_meta); } + +void cycle_active_window (struct WinMeta * win_meta, char dir) { +// Cycle active window selection forwards (dir = 'n') or backwards. + if ('n' == dir) { + if (win_meta->active->next != 0) + win_meta->active = win_meta->active->next; + else + win_meta->active = win_meta->chain_start; } + else { + if (win_meta->active->prev != 0) + win_meta->active = win_meta->active->prev; + else + win_meta->active = win_meta->chain_end; } + draw_all_windows(win_meta); } + +void shift_window (struct WinMeta * win_meta, char dir) { +// Move active window forward/backward in window chain. If jumping beyond start/end, move to other chain end. + if (win_meta->active != win_meta->chain_start || win_meta->active != win_meta->chain_end) { + if ('f' == dir) { + if (win_meta->active == win_meta->chain_end) { // move forward beyond chain end + win_meta->active->prev->next = 0; + win_meta->chain_end = win_meta->active->prev; + win_meta->active->prev = 0; + win_meta->active->next = win_meta->chain_start; + win_meta->chain_start->prev = win_meta->active; + win_meta->chain_start = win_meta->active; } + else { // move forward before chain end + if (win_meta->chain_start != win_meta->active) + win_meta->active->prev->next = win_meta->active->next; + else + win_meta->chain_start = win_meta->active->next; + win_meta->active->next->prev = win_meta->active->prev; + win_meta->active->prev = win_meta->active->next; + win_meta->active->next = win_meta->active->next->next; + win_meta->active->prev->next = win_meta->active; + if (0 != win_meta->active->next) + win_meta->active->next->prev = win_meta->active; + else + win_meta->chain_end = win_meta->active; } } + else { // mirror of above, backwards + if (win_meta->active == win_meta->chain_start) { + win_meta->active->next->prev = 0; + win_meta->chain_start = win_meta->active->next; + win_meta->active->next = 0; + win_meta->active->prev = win_meta->chain_end; + win_meta->chain_end->next = win_meta->active; + win_meta->chain_end = win_meta->active; } + else { + if (win_meta->chain_end != win_meta->active) + win_meta->active->next->prev = win_meta->active->prev; + else + win_meta->chain_end = win_meta->active->prev; + win_meta->active->prev->next = win_meta->active->next; + win_meta->active->next = win_meta->active->prev; + win_meta->active->prev = win_meta->active->prev->prev; + win_meta->active->next->prev = win_meta->active; + if (0 != win_meta->active->prev) + win_meta->active->prev->next = win_meta->active; + else + win_meta->chain_start = win_meta->active; } } + update_windows(win_meta, win_meta->chain_start); + draw_all_windows(win_meta); } } diff --git a/windows.h b/windows.h new file mode 100644 index 0000000..c8b6841 --- /dev/null +++ b/windows.h @@ -0,0 +1,34 @@ +struct WinMeta { + struct Win * chain_start; + struct Win * chain_end; + struct Win * active; + int height; }; + +struct Win { + struct Win * prev; + struct Win * next; + int width; + int height; + int start_x; + int start_y; + WINDOW * curses_win; + char border_left; + char border_down; + char * title; + void (* draw) (struct Win *); + void * data; }; + +struct WinMeta init_win_meta (WINDOW *); +struct Win init_window (struct WinMeta *, char *); +void append_window (struct WinMeta *, struct Win *); +void suspend_window (struct WinMeta *, struct Win *); +void place_window (struct WinMeta *, struct Win *); +void update_windows (struct WinMeta *, struct Win *); +void destroy_window (struct Win *); +void draw_windows (struct WinMeta *, struct Win *); +void draw_all_windows (struct WinMeta *); +void draw_window(struct WinMeta *, struct Win *); +void undraw_window (WINDOW *); +void resize_window (struct WinMeta *, char); +void cycle_active_window (struct WinMeta *, char); +void shift_window (struct WinMeta *, char); -- 2.30.2