]> git.lizzy.rs Git - nothing.git/blob - src/ui/console.c
Remove Lt from Edit_field
[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.font_size = vec(FONT_WIDTH_SCALE, FONT_HEIGHT_SCALE);
58     console->edit_field.font_color = CONSOLE_FOREGROUND;
59
60     console->console_log = PUSH_LT(
61         lt,
62         create_console_log(
63             vec(FONT_WIDTH_SCALE, FONT_HEIGHT_SCALE),
64             CONSOLE_LOG_CAPACITY),
65         destroy_console_log);
66
67     console->a = 0;
68
69     console->history = PUSH_LT(
70         lt,
71         create_history(HISTORY_CAPACITY),
72         destroy_history);
73     if (console->history == NULL) {
74         RETURN_LT(lt, NULL);
75     }
76
77     console->game = game;
78
79     return console;
80 }
81
82 void destroy_console(Console *console)
83 {
84     trace_assert(console);
85     RETURN_LT0(console->lt);
86 }
87
88 static int console_eval_input(Console *console)
89 {
90     const char *input_text = edit_field_as_text(&console->edit_field);
91
92     String input = string_nt(input_text);
93     String command = chop_word(&input);
94
95     if (string_equal(command, STRING_LIT(""))) {
96         edit_field_clean(&console->edit_field);
97         return 0;
98     }
99
100     if (history_push(console->history, input_text) < 0) {
101         return -1;
102     }
103
104     if (console_log_push_line(console->console_log, input_text, NULL, CONSOLE_FOREGROUND) < 0) {
105         return -1;
106     }
107
108     if (string_equal(command, STRING_LIT("load"))) {
109         String level = chop_word(&input);
110         console_log_push_line(console->console_log, "Loading level:", NULL, CONSOLE_FOREGROUND);
111         console_log_push_line(console->console_log, level.data, level.data + level.count, CONSOLE_FOREGROUND);
112         char level_name[256];
113         memset(level_name, 0, 256);
114         memcpy(level_name, level.data, min_size_t(level.count, 255));
115
116         if (game_load_level(console->game, level_name) < 0) {
117             console_log_push_line(console->console_log, "Could not load level", NULL, CONSOLE_ERROR);
118         }
119     } else if (string_equal(command, STRING_LIT("menu"))) {
120         console_log_push_line(console->console_log, "Loading menu", NULL, CONSOLE_FOREGROUND);
121         game_switch_state(console->game, GAME_STATE_LEVEL_PICKER);
122     } else {
123         console_log_push_line(console->console_log, "Unknown command", NULL, CONSOLE_ERROR);
124     }
125
126     edit_field_clean(&console->edit_field);
127
128     return 0;
129 }
130
131 int console_handle_event(Console *console,
132                          const SDL_Event *event)
133 {
134     switch(event->type) {
135     case SDL_KEYDOWN: {
136         switch(event->key.keysym.sym) {
137         case SDLK_RETURN:
138             return console_eval_input(console);
139
140         case SDLK_UP:
141             edit_field_replace(
142                 &console->edit_field,
143                 history_current(console->history));
144             history_prev(console->history);
145             return 0;
146
147         case SDLK_p: {
148             if (event->key.keysym.mod & KMOD_CTRL) {
149                 edit_field_replace(
150                     &console->edit_field,
151                     history_current(console->history));
152                 history_prev(console->history);
153                 return 0;
154             }
155         } break;
156
157        case SDLK_l: {
158             if (event->key.keysym.mod & KMOD_CTRL) {
159                 console_log_clear(console->console_log);
160                 return 0;
161             }
162         } break;
163
164         case SDLK_DOWN:
165             edit_field_replace(
166                 &console->edit_field,
167                 history_current(console->history));
168             history_next(console->history);
169             return 0;
170
171         case SDLK_n: {
172             if (event->key.keysym.mod & KMOD_CTRL) {
173                 edit_field_replace(
174                     &console->edit_field, history_current(console->history));
175                 history_next(console->history);
176                 return 0;
177             }
178         } break;
179         }
180     } break;
181     }
182
183     return edit_field_event(&console->edit_field, event);
184 }
185
186 int console_render(const Console *console,
187                    const Camera *camera)
188 {
189     /* TODO(#364): console doesn't have any padding around the edit fields */
190     SDL_Rect view_port;
191     SDL_RenderGetViewport(camera->renderer, &view_port);
192
193     const float e = console->a * (2 - console->a);
194     const float y = -(1.0f - e) * CONSOLE_HEIGHT;
195
196     if (camera_fill_rect_screen(
197             camera,
198             rect(0.0f, y,
199                  (float) view_port.w,
200                  CONSOLE_HEIGHT),
201             CONSOLE_BACKGROUND) < 0) {
202         return -1;
203     }
204
205     console_log_render(console->console_log,
206                        camera,
207                        vec(0.0f, y));
208
209     if (edit_field_render_screen(&console->edit_field,
210                                  camera,
211                                  vec(0.0f, y + CONSOLE_LOG_HEIGHT)) < 0) {
212         return -1;
213     }
214
215     return 0;
216 }
217
218 int console_update(Console *console, float delta_time)
219 {
220     trace_assert(console);
221
222     if (console->a < 1.0f) {
223         console->a += 1.0f / SLIDE_DOWN_TIME * delta_time;
224
225         if (console->a > 1.0f) {
226             console->a = 1.0f;
227         }
228     }
229
230     return 0;
231 }
232
233 void console_slide_down(Console *console)
234 {
235     trace_assert(console);
236     console->a = 0.0f;
237 }