]> git.lizzy.rs Git - nothing.git/blob - src/ui/console.c
(#366) Implement easing for console slide down animation
[nothing.git] / src / ui / console.c
1 #include <assert.h>
2
3 #include "game/level.h"
4 #include "game/level/player/rigid_rect.h"
5 #include "script/gc.h"
6 #include "script/interpreter.h"
7 #include "script/parser.h"
8 #include "script/scope.h"
9 #include "sdl/renderer.h"
10 #include "system/error.h"
11 #include "system/lt.h"
12 #include "ui/console.h"
13 #include "ui/edit_field.h"
14 #include "ui/log.h"
15
16 #define FONT_WIDTH_SCALE 3.0f
17 #define FONT_HEIGHT_SCALE 3.0f
18
19 #define LOG_CAPACITY 10
20 #define PROMPT_HEIGHT (FONT_HEIGHT_SCALE * FONT_CHAR_HEIGHT)
21 #define LOG_HEIGHT (FONT_HEIGHT_SCALE * FONT_CHAR_HEIGHT * LOG_CAPACITY)
22
23 #define CONSOLE_HEIGHT (LOG_HEIGHT + PROMPT_HEIGHT)
24
25 #define SLIDE_DOWN_TIME 0.4f
26
27 #define CONSOLE_BACKGROUND (color(0.20f, 0.20f, 0.20f, 1.0f))
28 #define CONSOLE_FOREGROUND (color(0.80f, 0.80f, 0.80f, 1.0f))
29 #define CONSOLE_ERROR (color(0.80f, 0.50f, 0.50f, 1.0f))
30
31 struct Console
32 {
33     Lt *lt;
34     Gc *gc;
35     struct Scope scope;
36     Edit_field *edit_field;
37     Log *log;
38     Level *level;
39     float a;
40 };
41
42 /* TODO(#354): Console does not allow to travel the history by pressing up and down */
43 /* TODO(#355): Console does not support Emacs keybindings */
44 /* TODO(#356): Console does not support autocompletion */
45 /* TODO(#357): Console does not show the state of the GC of the script */
46 /* TODO(#358): Console does not support copy, cut, paste operations */
47
48 static struct EvalResult rect_apply_force(void *param, Gc *gc, struct Scope *scope, struct Expr args)
49 {
50     assert(gc);
51     assert(scope);
52     assert(param);
53
54     Level *level = (Level*) param;
55     const char *rect_id = CAR(args).atom->str;
56     struct Expr vector_force_expr = CAR(CDR(args));
57     const float force_x = (float) CAR(vector_force_expr).atom->num;
58     const float force_y = (float) CDR(vector_force_expr).atom->num;
59
60     print_expr_as_sexpr(args); printf("\n");
61
62     Rigid_rect *rigid_rect = level_rigid_rect(level, rect_id);
63     if (rigid_rect != NULL) {
64         printf("Found rect `%s`\n", rect_id);
65         printf("Applying force (%f, %f)\n", force_x, force_y);
66         rigid_rect_apply_force(rigid_rect, vec(force_x, force_y));
67     } else {
68         fprintf(stderr, "Couldn't find rigid_rect `%s`", rect_id);
69     }
70
71     return eval_success(NIL(gc));
72 }
73
74 Console *create_console(Level *level,
75                         const Sprite_font *font)
76 {
77     Lt *lt = create_lt();
78
79     if (lt == NULL) {
80         return NULL;
81     }
82
83     Console *console = PUSH_LT(lt, malloc(sizeof(Console)), free);
84     if (console == NULL) {
85         throw_error(ERROR_TYPE_LIBC);
86         RETURN_LT(lt, NULL);
87     }
88     console->lt = lt;
89
90     console->gc = PUSH_LT(lt, create_gc(), destroy_gc);
91     if (console->gc == NULL) {
92         RETURN_LT(lt, NULL);
93     }
94
95     console->scope.expr = CONS(console->gc,
96                                NIL(console->gc),
97                                NIL(console->gc));
98     set_scope_value(
99         console->gc,
100         &console->scope,
101         SYMBOL(console->gc, "rect-apply-force"),
102         NATIVE(console->gc, rect_apply_force, level));
103
104     console->edit_field = PUSH_LT(
105         lt,
106         create_edit_field(
107             font,
108             vec(FONT_WIDTH_SCALE, FONT_HEIGHT_SCALE),
109             CONSOLE_FOREGROUND),
110         destroy_edit_field);
111     if (console->edit_field == NULL) {
112         RETURN_LT(lt, NULL);
113     }
114
115     console->log = PUSH_LT(
116         lt,
117         create_log(
118             font,
119             vec(FONT_WIDTH_SCALE, FONT_HEIGHT_SCALE),
120             LOG_CAPACITY),
121         destroy_log);
122
123     console->level = level;
124     console->a = 0;
125
126     return console;
127 }
128
129 void destroy_console(Console *console)
130 {
131     assert(console);
132     RETURN_LT0(console->lt);
133 }
134
135 int console_handle_event(Console *console,
136                          const SDL_Event *event)
137 {
138     switch(event->type) {
139     case SDL_KEYDOWN:
140         switch(event->key.keysym.sym) {
141         case SDLK_RETURN: {
142             const char *source_code = edit_field_as_text(console->edit_field);
143             struct ParseResult parse_result = read_expr_from_string(console->gc,
144                                                                     source_code);
145             if (parse_result.is_error) {
146                 if (log_push_line(console->log, source_code, CONSOLE_ERROR) < 0) {
147                     return -1;
148                 }
149
150                 if (log_push_line(console->log, parse_result.error_message, CONSOLE_ERROR)) {
151                     return -1;
152                 }
153
154                 edit_field_clean(console->edit_field);
155
156                 return 0;
157             }
158
159             struct EvalResult eval_result = eval(
160                 console->gc,
161                 &console->scope,
162                 parse_result.expr);
163
164             if (eval_result.is_error) {
165                 /* TODO(#360): Console doesn't report any eval errors visually */
166                 printf("Error:\t");
167                 print_expr_as_sexpr(eval_result.expr);
168                 printf("\n");
169             }
170
171             gc_collect(console->gc, console->scope.expr);
172
173             if (log_push_line(console->log,
174                               edit_field_as_text(console->edit_field),
175                               CONSOLE_FOREGROUND) < 0) {
176                 return -1;
177             }
178             edit_field_clean(console->edit_field);
179
180         } return 0;
181         }
182         break;
183     }
184
185     return edit_field_handle_event(console->edit_field, event);
186 }
187
188 int console_render(const Console *console,
189                    SDL_Renderer *renderer)
190 {
191     /* TODO(#364): console doesn't have any padding around the edit fields */
192     SDL_Rect view_port;
193     SDL_RenderGetViewport(renderer, &view_port);
194
195     const float e = console->a * (2 - console->a);
196     const float y = -(1.0f - e) * CONSOLE_HEIGHT;
197
198     if (fill_rect(renderer,
199                   rect(0.0f, y,
200                        (float) view_port.w,
201                        CONSOLE_HEIGHT),
202                   CONSOLE_BACKGROUND) < 0) {
203         return -1;
204     }
205
206     if (log_render(console->log,
207                    renderer,
208                    vec(0.0f, y)) < 0) {
209         return -1;
210     }
211
212     if (edit_field_render(console->edit_field,
213                           renderer,
214                           vec(0.0f, y + LOG_HEIGHT)) < 0) {
215         return -1;
216     }
217
218     return 0;
219 }
220
221 int console_update(Console *console, float delta_time)
222 {
223     assert(console);
224
225     /* TODO(#366): console slide down animation doesn't have any easing */
226
227     if (console->a < 1.0f) {
228         console->a += 1.0f / SLIDE_DOWN_TIME * delta_time;
229
230         if (console->a > 1.0f) {
231             console->a = 1.0f;
232         }
233     }
234
235     return 0;
236 }
237
238 void console_slide_down(Console *console)
239 {
240     assert(console);
241     console->a = 0.0f;
242 }