1 #include <stdio.h>
2 #include <string.h>
3 #include <stdint.h>
4 #include <signal.h>
5 #include <sys/time.h>
6 #include "platform.h"
7 #include "term.h"
8 
gameLoop()9 static void gameLoop() {
10     signal(SIGINT, SIG_DFL); // keep SDL from overriding the default ^C handler when it's linked
11 
12     if (!Term.start()) {
13         return;
14     }
15     Term.title("Brogue");
16     Term.resize(COLS, ROWS);
17 
18     rogueMain();
19 
20     Term.end();
21 }
22 
glyphToAscii(enum displayGlyph glyph)23 static char glyphToAscii(enum displayGlyph glyph) {
24     unsigned int ch;
25 
26     switch (glyph) {
27         case G_UP_ARROW: return '^';
28         case G_DOWN_ARROW: return 'v';
29         case G_FLOOR: return '.';
30         case G_CHASM: return ':';
31         case G_TRAP: return '%';
32         case G_FIRE: return '^';
33         case G_FOLIAGE: return '&';
34         case G_AMULET: return ',';
35         case G_SCROLL: return '?';
36         case G_RING: return '=';
37         case G_WEAPON: return '(';
38         case G_GEM: return '+';
39         case G_TOTEM: return '0'; // zero
40         case G_GOOD_MAGIC: return '$';
41         case G_BAD_MAGIC: return '+';
42         case G_DOORWAY: return '<';
43         case G_CHARM: return '7';
44         case G_GUARDIAN: return '5';
45         case G_WINGED_GUARDIAN: return '5';
46         case G_EGG: return 'o';
47         case G_BLOODWORT_STALK: return '&';
48         case G_FLOOR_ALT: return '.';
49         case G_UNICORN: return 'U';
50         case G_TURRET: return '*';
51         case G_CARPET: return '.';
52         case G_STATUE: return '5';
53         case G_CRACKED_STATUE: return '5';
54         case G_MAGIC_GLYPH: return ':';
55         case G_ELECTRIC_CRYSTAL: return '$';
56 
57         default:
58             ch = glyphToUnicode(glyph);
59             brogueAssert(ch < 0x80); // assert ascii
60             return ch;
61     }
62 }
63 
curses_plotChar(enum displayGlyph ch,short xLoc,short yLoc,short foreRed,short foreGreen,short foreBlue,short backRed,short backGreen,short backBlue)64 static void curses_plotChar(enum displayGlyph ch,
65               short xLoc, short yLoc,
66               short foreRed, short foreGreen, short foreBlue,
67               short backRed, short backGreen, short backBlue) {
68 
69     fcolor fore;
70     fcolor back;
71 
72     fore.r = (float) foreRed / 100;
73     fore.g = (float) foreGreen / 100;
74     fore.b = (float) foreBlue / 100;
75     back.r = (float) backRed / 100;
76     back.g = (float) backGreen / 100;
77     back.b = (float) backBlue / 100;
78 
79     ch = glyphToAscii(ch);
80 
81     if (ch < ' ' || ch > 127) ch = ' ';
82     Term.put(xLoc, yLoc, ch, &fore, &back);
83 }
84 
85 
86 struct mapsymbol {
87     int in_c, out_c;
88     struct mapsymbol *next;
89 };
90 
91 static struct mapsymbol *keymap = NULL;
92 
rewriteKey(int key,boolean text)93 static int rewriteKey(int key, boolean text) {
94     if (text) return key;
95 
96     struct mapsymbol *s = keymap;
97     while (s != NULL) {
98         if (s->in_c == key) {
99             return s->out_c;
100         }
101 
102         s = s->next;
103     }
104     return key;
105 }
106 
107 
108 #define PAUSE_BETWEEN_EVENT_POLLING     34//17
109 
getTime()110 static uint64_t getTime() {
111     struct timeval tv;
112     gettimeofday(&tv, NULL);
113     return (uint64_t)tv.tv_sec * 1000 + tv.tv_usec / 1000;
114 }
115 
curses_pauseForMilliseconds(short milliseconds)116 static boolean curses_pauseForMilliseconds(short milliseconds) {
117     Term.refresh();
118     Term.wait(milliseconds);
119 
120     // hasKey returns true if we have a mouse event, too.
121     return Term.hasKey();
122 }
123 
curses_nextKeyOrMouseEvent(rogueEvent * returnEvent,boolean textInput,boolean colorsDance)124 static void curses_nextKeyOrMouseEvent(rogueEvent *returnEvent, boolean textInput, boolean colorsDance) {
125     int key;
126     // TCOD_mouse_t mouse;
127     uint64_t theTime, waitTime;
128     // short x, y;
129 
130     Term.refresh();
131 
132     for (;;) {
133         theTime = getTime(); //TCOD_sys_elapsed_milli();
134 
135         /*if (TCOD_console_is_window_closed()) {
136             rogue.gameHasEnded = true; // causes the game loop to terminate quickly
137             returnEvent->eventType = KEYSTROKE;
138             returnEvent->param1 = ACKNOWLEDGE_KEY;
139             return;
140         }*/
141 
142         if (colorsDance) {
143             shuffleTerrainColors(3, true);
144             commitDraws();
145         }
146 
147 
148         key = Term.getkey();
149         if (key == TERM_MOUSE) {
150             if (Term.mouse.x > 0 && Term.mouse.y > 0 && Term.mouse.x < COLS && Term.mouse.y < ROWS) {
151                 returnEvent->param1 = Term.mouse.x;
152                 returnEvent->param2 = Term.mouse.y;
153                 returnEvent->eventType = KEYSTROKE;
154                 if (Term.mouse.justReleased) returnEvent->eventType = MOUSE_UP;
155                 if (Term.mouse.justPressed) returnEvent->eventType = MOUSE_DOWN;
156                 if (Term.mouse.justMoved) returnEvent->eventType = MOUSE_ENTERED_CELL;
157                 returnEvent->controlKey = Term.mouse.control;
158                 returnEvent->shiftKey = Term.mouse.shift;
159                 if (returnEvent->eventType != KEYSTROKE) return;
160             }
161         } else if (key != TERM_NONE) {
162             key = rewriteKey(key, textInput);
163 
164             returnEvent->eventType = KEYSTROKE;
165             returnEvent->controlKey = 0; //(key.rctrl || key.lctrl);
166             returnEvent->shiftKey = 0; //key.shift;
167             returnEvent->param1 = key;
168 
169             if (key == Term.keys.backspace || key == Term.keys.del) returnEvent->param1 = DELETE_KEY;
170             else if (key == Term.keys.up) returnEvent->param1 = UP_ARROW;
171             else if (key == Term.keys.down) returnEvent->param1 = DOWN_ARROW;
172             else if (key == Term.keys.left) returnEvent->param1 = LEFT_ARROW;
173             else if (key == Term.keys.right) returnEvent->param1 = RIGHT_ARROW;
174             else if (key == Term.keys.quit) {
175                 rogue.gameHasEnded = true;
176                 rogue.nextGame = NG_QUIT; // causes the menu to drop out immediately
177             }
178             else if ((key >= 'A' && key <= 'Z')) {
179                 returnEvent->shiftKey = 1;
180                 // returnEvent->param1 += 'a' - 'A';
181             }
182             // we could try to catch control keys, where possible, but we'll catch keys we mustn't
183             /* else if ((key >= 'A'-'@' && key <= 'Z'-'@')) {
184                 returnEvent->controlKey = 1;
185                 returnEvent->param1 += 'a' - ('A'-'@');
186             } */
187 
188             return;
189         }
190 
191         waitTime = PAUSE_BETWEEN_EVENT_POLLING + theTime - getTime();
192 
193         if (waitTime > 0 && waitTime <= PAUSE_BETWEEN_EVENT_POLLING) {
194             curses_pauseForMilliseconds(waitTime);
195         }
196     }
197 }
198 
curses_remap(const char * input_name,const char * output_name)199 static void curses_remap(const char *input_name, const char *output_name) {
200     struct mapsymbol *sym = malloc(sizeof(*sym));
201 
202     if (sym == NULL) return; // out of memory?  seriously?
203 
204     sym->in_c = Term.keycodeByName(input_name);
205     sym->out_c = Term.keycodeByName(output_name);
206 
207     sym->next = keymap;
208     keymap = sym;
209 }
210 
modifier_held(int modifier)211 static boolean modifier_held(int modifier) {
212     return 0;
213 }
214 
215 struct brogueConsole cursesConsole = {
216     gameLoop,
217     curses_pauseForMilliseconds,
218     curses_nextKeyOrMouseEvent,
219     curses_plotChar,
220     curses_remap,
221     modifier_held,
222     NULL,
223     NULL,
224     NULL
225 };
226