]> git.lizzy.rs Git - nothing.git/blob - src/ui/console.c
Replace Tokens with Strings
[nothing.git] / src / ui / console.c
1 #include <ctype.h>
2 #include <math.h>
3
4 #include "system/stacktrace.h"
5
6 #include "game.h"
7 #include "game/level.h"
8 #include "sdl/renderer.h"
9 #include "system/log.h"
10 #include "system/lt.h"
11 #include "system/nth_alloc.h"
12 #include "ui/console.h"
13 #include "ui/console_log.h"
14 #include "ui/edit_field.h"
15 #include "ui/history.h"
16 #include "math/extrema.h"
17
18 #define FONT_WIDTH_SCALE 3.0f
19 #define FONT_HEIGHT_SCALE 3.0f
20
21 #define CONSOLE_LOG_CAPACITY 10
22 #define HISTORY_CAPACITY 20
23 #define PROMPT_HEIGHT (FONT_HEIGHT_SCALE * FONT_CHAR_HEIGHT)
24 #define CONSOLE_LOG_HEIGHT (FONT_HEIGHT_SCALE * FONT_CHAR_HEIGHT * CONSOLE_LOG_CAPACITY)
25
26 #define CONSOLE_HEIGHT (CONSOLE_LOG_HEIGHT + PROMPT_HEIGHT)
27
28 #define SLIDE_DOWN_TIME 0.4f
29
30 #define CONSOLE_ALPHA (0.80f)
31 #define CONSOLE_BACKGROUND (rgba(0.20f, 0.20f, 0.20f, CONSOLE_ALPHA))
32 #define CONSOLE_FOREGROUND (rgba(0.80f, 0.80f, 0.80f, CONSOLE_ALPHA))
33 #define CONSOLE_ERROR (rgba(0.80f, 0.50f, 0.50f, CONSOLE_ALPHA))
34
35 struct Console
36 {
37     Lt *lt;
38     Edit_field *edit_field;
39     Console_Log *console_log;
40     History *history;
41     Game *game;
42     float a;
43 };
44
45 /* TODO(#356): Console does not support autocompletion */
46
47 Console *create_console(Game *game)
48 {
49     Lt *lt = create_lt();
50
51     Console *console = PUSH_LT(lt, nth_calloc(1, sizeof(Console)), free);
52     if (console == NULL) {
53         RETURN_LT(lt, NULL);
54     }
55     console->lt = lt;
56
57     console->edit_field = PUSH_LT(
58         lt,
59         create_edit_field(
60             vec(FONT_WIDTH_SCALE, FONT_HEIGHT_SCALE),
61             CONSOLE_FOREGROUND),
62         destroy_edit_field);
63     if (console->edit_field == NULL) {
64         RETURN_LT(lt, NULL);
65     }
66
67     console->console_log = PUSH_LT(
68         lt,
69         create_console_log(
70             vec(FONT_WIDTH_SCALE, FONT_HEIGHT_SCALE),
71             CONSOLE_LOG_CAPACITY),
72         destroy_console_log);
73
74     console->a = 0;
75
76     console->history = PUSH_LT(
77         lt,
78         create_history(HISTORY_CAPACITY),
79         destroy_history);
80     if (console->history == NULL) {
81         RETURN_LT(lt, NULL);
82     }
83
84     console->game = game;
85
86     return console;
87 }
88
89 void destroy_console(Console *console)
90 {
91     trace_assert(console);
92     RETURN_LT0(console->lt);
93 }
94
95 static int console_eval_input(Console *console)
96 {
97     const char *input_text = edit_field_as_text(console->edit_field);
98
99     String input = string_nt(input_text);
100     String command = chop_word(&input);
101
102     if (string_equal(command, STRING_LIT(""))) {
103         edit_field_clean(console->edit_field);
104         return 0;
105     }
106
107     if (history_push(console->history, input_text) < 0) {
108         return -1;
109     }
110
111     if (console_log_push_line(console->console_log, input_text, NULL, CONSOLE_FOREGROUND) < 0) {
112         return -1;
113     }
114
115     if (string_equal(command, STRING_LIT("load"))) {
116         String level = chop_word(&input);
117         console_log_push_line(console->console_log, "Loading level:", NULL, CONSOLE_FOREGROUND);
118         console_log_push_line(console->console_log, level.data, level.data + level.count, CONSOLE_FOREGROUND);
119         char level_name[256];
120         memset(level_name, 0, 256);
121         memcpy(level_name, level.data, min_size_t(level.count, 255));
122
123         if (game_load_level(console->game, level_name) < 0) {
124             console_log_push_line(console->console_log, "Could not load level", NULL, CONSOLE_ERROR);
125         }
126     } else if (string_equal(command, STRING_LIT("menu"))) {
127         console_log_push_line(console->console_log, "Loading menu", NULL, CONSOLE_FOREGROUND);
128         game_switch_state(console->game, GAME_STATE_LEVEL_PICKER);
129     } else {
130         console_log_push_line(console->console_log, "Unknown command", NULL, CONSOLE_ERROR);
131     }
132
133     edit_field_clean(console->edit_field);
134
135     return 0;
136 }
137
138 int console_handle_event(Console *console,
139                          const SDL_Event *event)
140 {
141     switch(event->type) {
142     case SDL_KEYDOWN: {
143         switch(event->key.keysym.sym) {
144         case SDLK_RETURN:
145             return console_eval_input(console);
146
147         case SDLK_UP:
148             edit_field_replace(
149                 console->edit_field,
150                 history_current(console->history));
151             history_prev(console->history);
152             return 0;
153
154         case SDLK_p: {
155             if (event->key.keysym.mod & KMOD_CTRL) {
156                 edit_field_replace(
157                     console->edit_field, history_current(console->history));
158                 history_prev(console->history);
159                 return 0;
160             }
161         } break;
162
163        case SDLK_l: {
164             if (event->key.keysym.mod & KMOD_CTRL) {
165                 console_log_clear(console->console_log);
166                 return 0;
167             }
168         } break;
169
170         case SDLK_DOWN:
171             edit_field_replace(
172                 console->edit_field,
173                 history_current(console->history));
174             history_next(console->history);
175             return 0;
176
177         case SDLK_n: {
178             if (event->key.keysym.mod & KMOD_CTRL) {
179                 edit_field_replace(
180                     console->edit_field, history_current(console->history));
181                 history_next(console->history);
182                 return 0;
183             }
184         } break;
185         }
186     } break;
187     }
188
189     return edit_field_event(console->edit_field, event);
190 }
191
192 int console_render(const Console *console,
193                    const Camera *camera)
194 {
195     /* TODO(#364): console doesn't have any padding around the edit fields */
196     SDL_Rect view_port;
197     SDL_RenderGetViewport(camera->renderer, &view_port);
198
199     const float e = console->a * (2 - console->a);
200     const float y = -(1.0f - e) * CONSOLE_HEIGHT;
201
202     if (camera_fill_rect_screen(
203             camera,
204             rect(0.0f, y,
205                  (float) view_port.w,
206                  CONSOLE_HEIGHT),
207             CONSOLE_BACKGROUND) < 0) {
208         return -1;
209     }
210
211     console_log_render(console->console_log,
212                        camera,
213                        vec(0.0f, y));
214
215     if (edit_field_render_screen(console->edit_field,
216                                  camera,
217                                  vec(0.0f, y + CONSOLE_LOG_HEIGHT)) < 0) {
218         return -1;
219     }
220
221     return 0;
222 }
223
224 int console_update(Console *console, float delta_time)
225 {
226     trace_assert(console);
227
228     if (console->a < 1.0f) {
229         console->a += 1.0f / SLIDE_DOWN_TIME * delta_time;
230
231         if (console->a > 1.0f) {
232             console->a = 1.0f;
233         }
234     }
235
236     return 0;
237 }
238
239 void console_slide_down(Console *console)
240 {
241     trace_assert(console);
242     console->a = 0.0f;
243 }