4 #include "system/stacktrace.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"
18 #define FONT_WIDTH_SCALE 3.0f
19 #define FONT_HEIGHT_SCALE 3.0f
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)
26 #define CONSOLE_HEIGHT (CONSOLE_LOG_HEIGHT + PROMPT_HEIGHT)
28 #define SLIDE_DOWN_TIME 0.4f
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))
42 Token token(const char *begin, const char *end)
44 Token t = {begin, end};
49 int token_equals_str(Token t, const char *s)
51 trace_assert(t.begin <= t.end);
52 size_t n1 = (size_t) (t.end - t.begin);
53 size_t n2 = strlen(s);
54 if (n1 != n2) return 0;
55 return memcmp(t.begin, s, n1) == 0;
59 Token token_nt(const char *s)
61 return token(s, s + strlen(s));
67 while (t->begin < t->end && isspace(*t->begin)) {
73 Token chop_word(Token *t)
76 const char *end = t->begin;
77 while (end < t->end && !isspace(*end)) {
80 Token result = token(t->begin, end);
88 Edit_field *edit_field;
89 Console_Log *console_log;
95 /* TODO(#356): Console does not support autocompletion */
96 /* TODO(#358): Console does not support copy, cut, paste operations */
98 Console *create_console(Game *game)
100 Lt *lt = create_lt();
102 Console *console = PUSH_LT(lt, nth_calloc(1, sizeof(Console)), free);
103 if (console == NULL) {
108 console->edit_field = PUSH_LT(
111 vec(FONT_WIDTH_SCALE, FONT_HEIGHT_SCALE),
114 if (console->edit_field == NULL) {
118 console->console_log = PUSH_LT(
121 vec(FONT_WIDTH_SCALE, FONT_HEIGHT_SCALE),
122 CONSOLE_LOG_CAPACITY),
123 destroy_console_log);
127 console->history = PUSH_LT(
129 create_history(HISTORY_CAPACITY),
131 if (console->history == NULL) {
135 console->game = game;
140 void destroy_console(Console *console)
142 trace_assert(console);
143 RETURN_LT0(console->lt);
146 static int console_eval_input(Console *console)
148 const char *source_code = edit_field_as_text(console->edit_field);
150 /* TODO(#387): console pushes empty strings to the history */
151 if (history_push(console->history, source_code) < 0) {
155 if (console_log_push_line(console->console_log, source_code, NULL, CONSOLE_FOREGROUND) < 0) {
159 Token input = token_nt(source_code);
161 Token command = chop_word(&input);
162 if (token_equals_str(command, "load")) {
163 Token level = chop_word(&input);
164 console_log_push_line(console->console_log, "Loading level:", NULL, CONSOLE_FOREGROUND);
165 console_log_push_line(console->console_log, level.begin, level.end, CONSOLE_FOREGROUND);
166 char level_name[256];
167 memset(level_name, 0, 256);
168 memcpy(level_name, level.begin, min_size_t((size_t)(level.end - level.begin), 255));
170 if (game_load_level(console->game, level_name) < 0) {
171 console_log_push_line(console->console_log, "Could not load level", NULL, CONSOLE_ERROR);
173 } else if (token_equals_str(command, "menu")) {
174 console_log_push_line(console->console_log, "Loading menu", NULL, CONSOLE_FOREGROUND);
175 level_picker_clean_selection(console->game->level_picker);
176 game_switch_state(console->game, GAME_STATE_LEVEL_PICKER);
178 console_log_push_line(console->console_log, "Unknown command", NULL, CONSOLE_ERROR);
181 edit_field_clean(console->edit_field);
186 int console_handle_event(Console *console,
187 const SDL_Event *event)
189 switch(event->type) {
191 switch(event->key.keysym.sym) {
193 return console_eval_input(console);
198 history_current(console->history));
199 history_prev(console->history);
203 if (event->key.keysym.mod & KMOD_CTRL) {
205 console->edit_field, history_current(console->history));
206 history_prev(console->history);
214 history_current(console->history));
215 history_next(console->history);
219 if (event->key.keysym.mod & KMOD_CTRL) {
221 console->edit_field, history_current(console->history));
222 history_next(console->history);
230 return edit_field_event(console->edit_field, event);
233 int console_render(const Console *console,
234 const Camera *camera)
236 /* TODO(#364): console doesn't have any padding around the edit fields */
238 SDL_RenderGetViewport(camera->renderer, &view_port);
240 const float e = console->a * (2 - console->a);
241 const float y = -(1.0f - e) * CONSOLE_HEIGHT;
243 if (camera_fill_rect_screen(
248 CONSOLE_BACKGROUND) < 0) {
252 if (console_log_render(console->console_log,
258 if (edit_field_render_screen(console->edit_field,
260 vec(0.0f, y + CONSOLE_LOG_HEIGHT)) < 0) {
267 int console_update(Console *console, float delta_time)
269 trace_assert(console);
271 if (console->a < 1.0f) {
272 console->a += 1.0f / SLIDE_DOWN_TIME * delta_time;
274 if (console->a > 1.0f) {
282 void console_slide_down(Console *console)
284 trace_assert(console);