home · contact · privacy
Uploading current state of work.
authorChristian Heller <c.heller@plomlompom.de>
Mon, 22 Apr 2013 02:11:51 +0000 (04:11 +0200)
committerChristian Heller <c.heller@plomlompom.de>
Mon, 22 Apr 2013 02:11:51 +0000 (04:11 +0200)
Makefile [new file with mode: 0644]
roguelike.c [new file with mode: 0644]
windows.c [new file with mode: 0644]
windows.h [new file with mode: 0644]

diff --git a/Makefile b/Makefile
new file mode 100644 (file)
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 (file)
index 0000000..c2e4510
--- /dev/null
@@ -0,0 +1,240 @@
+#include <ncurses.h>
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#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 = &map;
+
+  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 (file)
index 0000000..f879e6b
--- /dev/null
+++ b/windows.c
@@ -0,0 +1,241 @@
+#include <stdlib.h>
+#include <ncurses.h>
+#include <string.h>
+#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 (file)
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);