]> git.lizzy.rs Git - nothing.git/blob - src/game/level/level_editor.c
(#903) Introduce test notice
[nothing.git] / src / game / level / level_editor.c
1 #include <stdbool.h>
2
3 #include "game/camera.h"
4 #include "game/level_metadata.h"
5 #include "game/level/boxes.h"
6 #include "game/level/level_editor/color_picker.h"
7 #include "game/level/level_editor/rect_layer.h"
8 #include "game/level/level_editor/point_layer.h"
9 #include "game/level/level_editor/player_layer.h"
10 #include "game/level/level_editor/label_layer.h"
11 #include "ui/edit_field.h"
12 #include "system/stacktrace.h"
13 #include "system/nth_alloc.h"
14 #include "system/lt.h"
15 #include "system/lt_adapters.h"
16 #include "system/log.h"
17 #include "system/str.h"
18
19 #include "level_editor.h"
20
21 #define LEVEL_LINE_MAX_LENGTH 512
22 #define LEVEL_EDITOR_EDIT_FIELD_SIZE vec(5.0f, 5.0f)
23 #define LEVEL_EDITOR_EDIT_FIELD_COLOR COLOR_BLACK
24
25 static int level_editor_dump(const LevelEditor *level_editor);
26
27 LevelEditor *create_level_editor(void)
28 {
29     Lt *lt = create_lt();
30     LevelEditor *level_editor = PUSH_LT(
31         lt,
32         nth_calloc(1, sizeof(LevelEditor)),
33         free);
34     if (level_editor == NULL) {
35         RETURN_LT(lt, NULL);
36     }
37     level_editor->lt = lt;
38
39     level_editor->edit_field_filename = PUSH_LT(
40         lt,
41         create_edit_field(
42             LEVEL_EDITOR_EDIT_FIELD_SIZE,
43             LEVEL_EDITOR_EDIT_FIELD_COLOR),
44         destroy_edit_field);
45     if (level_editor->edit_field_filename == NULL) {
46         RETURN_LT(lt, NULL);
47     }
48
49     level_editor->metadata = PUSH_LT(
50         lt,
51         create_level_metadata("New Level"),
52         destroy_level_metadata);
53     if (level_editor->metadata == NULL) {
54         RETURN_LT(lt, NULL);
55     }
56
57     level_editor->background_layer = create_color_picker_from_rgba(hexstr("fffda5"));
58
59     level_editor->player_layer =
60         create_player_layer(vec(0.0f, 0.0f), hexstr("ff8080"));
61
62     level_editor->platforms_layer = PUSH_LT(
63         lt,
64         create_rect_layer(),
65         destroy_rect_layer);
66     if (level_editor->platforms_layer == NULL) {
67         RETURN_LT(lt, NULL);
68     }
69
70     level_editor->goals_layer = PUSH_LT(
71         lt,
72         create_point_layer(),
73         destroy_point_layer);
74     if (level_editor->goals_layer == NULL) {
75         RETURN_LT(lt, NULL);
76     }
77
78     level_editor->lava_layer = PUSH_LT(
79         lt,
80         create_rect_layer(),
81         destroy_rect_layer);
82     if (level_editor->lava_layer == NULL) {
83         RETURN_LT(lt, NULL);
84     }
85
86     level_editor->back_platforms_layer = PUSH_LT(
87         lt,
88         create_rect_layer(),
89         destroy_rect_layer);
90     if (level_editor->back_platforms_layer == NULL) {
91         RETURN_LT(lt, NULL);
92     }
93
94     level_editor->boxes_layer = PUSH_LT(
95         lt,
96         create_rect_layer(),
97         destroy_rect_layer);
98     if (level_editor->boxes_layer == NULL) {
99         RETURN_LT(lt, NULL);
100     }
101
102     level_editor->label_layer = PUSH_LT(
103         lt,
104         create_label_layer(),
105         destroy_label_layer);
106     if (level_editor->label_layer == NULL) {
107         RETURN_LT(lt, NULL);
108     }
109
110     level_editor->regions_layer = PUSH_LT(
111         lt,
112         create_rect_layer(),
113         destroy_rect_layer);
114     if (level_editor->regions_layer == NULL) {
115         RETURN_LT(lt, NULL);
116     }
117
118     level_editor->supa_script_source =
119         PUSH_LT(lt, string_duplicate("", NULL), free);
120
121     level_editor->layers[LAYER_PICKER_BOXES] = rect_layer_as_layer(level_editor->boxes_layer);
122     level_editor->layers[LAYER_PICKER_PLATFORMS] = rect_layer_as_layer(level_editor->platforms_layer);
123     level_editor->layers[LAYER_PICKER_BACK_PLATFORMS] = rect_layer_as_layer(level_editor->back_platforms_layer);
124     level_editor->layers[LAYER_PICKER_GOALS] = point_layer_as_layer(level_editor->goals_layer);
125     level_editor->layers[LAYER_PICKER_PLAYER] = player_layer_as_layer(&level_editor->player_layer);
126     level_editor->layers[LAYER_PICKER_LAVA] = rect_layer_as_layer(level_editor->lava_layer);
127     level_editor->layers[LAYER_PICKER_REGIONS] = rect_layer_as_layer(level_editor->regions_layer);
128     level_editor->layers[LAYER_PICKER_BACKGROUND] = color_picker_as_layer(&level_editor->background_layer);
129     level_editor->layers[LAYER_PICKER_LABELS] = label_layer_as_layer(level_editor->label_layer);
130
131     level_editor->drag = false;
132
133     level_editor->notice = (FadingWigglyText) {
134         .wiggly_text = {
135             .text = "Test",
136             .color = COLOR_RED,
137             .scale = vec(10.0f, 10.0f)
138         },
139         .duration = 1.0f,
140     };
141
142     return level_editor;
143 }
144
145 LevelEditor *create_level_editor_from_file(const char *file_name)
146 {
147     trace_assert(file_name);
148
149     Lt *lt = create_lt();
150     LevelEditor *level_editor = PUSH_LT(
151         lt,
152         nth_calloc(1, sizeof(LevelEditor)),
153         free);
154     if (level_editor == NULL) {
155         RETURN_LT(lt, NULL);
156     }
157     level_editor->lt = lt;
158
159     level_editor->edit_field_filename = PUSH_LT(
160         lt,
161         create_edit_field(
162             LEVEL_EDITOR_EDIT_FIELD_SIZE,
163             LEVEL_EDITOR_EDIT_FIELD_COLOR),
164         destroy_edit_field);
165     if (level_editor->edit_field_filename == NULL) {
166         RETURN_LT(lt, NULL);
167     }
168
169     level_editor->file_name =
170         PUSH_LT(
171             lt,
172             string_duplicate(file_name, NULL),
173             free);
174
175     LineStream *level_stream = PUSH_LT(
176         lt,
177         create_line_stream(
178             file_name,
179             "r",
180             LEVEL_LINE_MAX_LENGTH),
181         destroy_line_stream);
182     if (level_stream == NULL) {
183         RETURN_LT(lt, NULL);
184     }
185
186     level_editor->metadata = PUSH_LT(
187         lt,
188         create_level_metadata_from_line_stream(level_stream),
189         destroy_level_metadata);
190     if (level_editor->metadata == NULL) {
191         RETURN_LT(lt, NULL);
192     }
193
194     if (color_picker_read_from_line_stream(
195             &level_editor->background_layer,
196             level_stream) < 0) {
197         RETURN_LT(lt, NULL);
198     }
199
200     level_editor->player_layer =
201         create_player_layer_from_line_stream(level_stream);
202
203     level_editor->platforms_layer =
204         PUSH_LT(
205             lt,
206             create_rect_layer_from_line_stream(level_stream),
207             destroy_rect_layer);
208     if (level_editor->platforms_layer == NULL) {
209         RETURN_LT(lt, NULL);
210     }
211
212     level_editor->goals_layer = PUSH_LT(
213         lt,
214         create_point_layer_from_line_stream(level_stream),
215         destroy_point_layer);
216     if (level_editor->goals_layer == NULL) {
217         RETURN_LT(lt, NULL);
218     }
219
220     level_editor->lava_layer =
221         PUSH_LT(
222             lt,
223             create_rect_layer_from_line_stream(level_stream),
224             destroy_rect_layer);
225     if (level_editor->lava_layer == NULL) {
226         RETURN_LT(lt, NULL);
227     }
228
229     level_editor->back_platforms_layer =
230         PUSH_LT(
231             lt,
232             create_rect_layer_from_line_stream(level_stream),
233             destroy_rect_layer);
234     if (level_editor->back_platforms_layer == NULL) {
235         RETURN_LT(lt, NULL);
236     }
237
238     level_editor->boxes_layer =
239         PUSH_LT(
240             lt,
241             create_rect_layer_from_line_stream(level_stream),
242             destroy_rect_layer);
243     if (level_editor->boxes_layer == NULL) {
244         RETURN_LT(lt, NULL);
245     }
246
247     level_editor->label_layer =
248         PUSH_LT(
249             lt,
250             create_label_layer_from_line_stream(level_stream),
251             destroy_label_layer);
252     if (level_editor->label_layer == NULL) {
253         RETURN_LT(lt, NULL);
254     }
255
256     level_editor->regions_layer =
257         PUSH_LT(
258             lt,
259             create_rect_layer_from_line_stream(level_stream),
260             destroy_rect_layer);
261     if (level_editor->regions_layer == NULL) {
262         RETURN_LT(lt, NULL);
263     }
264
265     level_editor->supa_script_source =
266         PUSH_LT(
267             lt,
268             line_stream_collect_until_end(level_stream),
269             free);
270     if (level_editor->supa_script_source == NULL) {
271         RETURN_LT(lt, NULL);
272     }
273
274     level_editor->layers[LAYER_PICKER_BOXES] = rect_layer_as_layer(level_editor->boxes_layer);
275     level_editor->layers[LAYER_PICKER_PLATFORMS] = rect_layer_as_layer(level_editor->platforms_layer);
276     level_editor->layers[LAYER_PICKER_BACK_PLATFORMS] = rect_layer_as_layer(level_editor->back_platforms_layer);
277     level_editor->layers[LAYER_PICKER_GOALS] = point_layer_as_layer(level_editor->goals_layer);
278     level_editor->layers[LAYER_PICKER_PLAYER] = player_layer_as_layer(&level_editor->player_layer);
279     level_editor->layers[LAYER_PICKER_LAVA] = rect_layer_as_layer(level_editor->lava_layer);
280     level_editor->layers[LAYER_PICKER_REGIONS] = rect_layer_as_layer(level_editor->regions_layer);
281     level_editor->layers[LAYER_PICKER_BACKGROUND] = color_picker_as_layer(&level_editor->background_layer);
282     level_editor->layers[LAYER_PICKER_LABELS] = label_layer_as_layer(level_editor->label_layer);
283
284     level_editor->drag = false;
285
286     level_editor->notice = (FadingWigglyText) {
287         .wiggly_text = {
288             .text = "Test",
289             .color = COLOR_RED,
290             .scale = vec(10.0f, 10.0f)
291         },
292         .duration = 1.0f,
293     };
294
295     return level_editor;
296 }
297
298 void destroy_level_editor(LevelEditor *level_editor)
299 {
300     trace_assert(level_editor);
301     RETURN_LT0(level_editor->lt);
302 }
303
304 int level_editor_render(const LevelEditor *level_editor,
305                         Camera *camera)
306 {
307     trace_assert(level_editor);
308     trace_assert(camera);
309
310     if (camera_clear_background(camera, color_picker_rgba(&level_editor->background_layer)) < 0) {
311         return -1;
312     }
313
314     for (size_t i = 0; i < LAYER_PICKER_N; ++i) {
315         if (layer_render(
316                 level_editor->layers[i],
317                 camera,
318                 i == level_editor->layer_picker) < 0) {
319             return -1;
320         }
321     }
322
323     if (layer_picker_render(&level_editor->layer_picker, camera) < 0) {
324         return -1;
325     }
326
327     if (level_editor->state == LEVEL_EDITOR_SAVEAS) {
328         /* CSS */
329         const Point size = LEVEL_EDITOR_EDIT_FIELD_SIZE;
330         const char *save_as_text = "Save as: ";
331         const Point position = vec(200.0f, 200.0f);
332         const float save_as_width =
333             (float) strlen(save_as_text) * FONT_CHAR_WIDTH * size.x;
334
335         /* HTML */
336         if (camera_render_text_screen(
337                 camera,
338                 save_as_text,
339                 LEVEL_EDITOR_EDIT_FIELD_SIZE,
340                 LEVEL_EDITOR_EDIT_FIELD_COLOR,
341                 position) < 0) {
342             return -1;
343         }
344
345         if (edit_field_render_screen(
346                 level_editor->edit_field_filename,
347                 camera,
348                 vec(position.x + save_as_width, position.y)) < 0) {
349             return -1;
350         }
351     }
352
353     fading_wiggly_text_render(&level_editor->notice, camera);
354
355     return 0;
356 }
357
358 static
359 int level_editor_saveas_event(LevelEditor *level_editor,
360                               const SDL_Event *event,
361                               const Camera *camera)
362 {
363     trace_assert(level_editor);
364     trace_assert(event);
365     trace_assert(camera);
366
367     switch (event->type) {
368     case SDL_KEYDOWN: {
369         if (event->key.keysym.sym == SDLK_RETURN) {
370             trace_assert(level_editor->file_name == NULL);
371             level_editor->file_name = PUSH_LT(
372                 level_editor->lt,
373                 string_duplicate(
374                     edit_field_as_text(
375                         level_editor->edit_field_filename),
376                     NULL),
377                 free);
378             level_editor_dump(level_editor);
379             SDL_StopTextInput();
380             level_editor->state = LEVEL_EDITOR_IDLE;
381             return 0;
382         }
383     } break;
384     }
385
386     return edit_field_event(level_editor->edit_field_filename, event);
387 }
388
389 static
390 int level_editor_idle_event(LevelEditor *level_editor,
391                             const SDL_Event *event,
392                             const Camera *camera)
393 {
394     trace_assert(level_editor);
395     trace_assert(event);
396     trace_assert(camera);
397
398     switch (event->type) {
399     case SDL_KEYDOWN: {
400         switch(event-> key.keysym.sym) {
401         case SDLK_s: {
402             /* TODO(#903): There is no indication that the level is saved when you press S in Level Editor */
403             if (level_editor->file_name) {
404                 level_editor_dump(level_editor);
405                 log_info("Saving level to `%s`\n", level_editor->file_name);
406             } else {
407                 SDL_StartTextInput();
408                 level_editor->state = LEVEL_EDITOR_SAVEAS;
409             }
410         } break;
411         }
412     } break;
413
414     case SDL_MOUSEWHEEL: {
415         // TODO(#679): zooming in edit mode is not smooth enough
416         if (event->wheel.y > 0) {
417             level_editor->camera_scale += 0.1f;
418         } else if (event->wheel.y < 0) {
419             level_editor->camera_scale = fmaxf(0.1f, level_editor->camera_scale - 0.1f);
420         }
421     } break;
422
423     case SDL_MOUSEBUTTONUP:
424     case SDL_MOUSEBUTTONDOWN: {
425         if (event->type == SDL_MOUSEBUTTONDOWN && event->button.button == SDL_BUTTON_MIDDLE) {
426             level_editor->drag = true;
427         }
428
429         if (event->type == SDL_MOUSEBUTTONUP && event->button.button == SDL_BUTTON_MIDDLE) {
430             level_editor->drag = false;
431         }
432     } break;
433
434     case SDL_MOUSEMOTION: {
435         if (level_editor->drag) {
436             const Vec next_position = camera_map_screen(camera, event->motion.x, event->motion.y);
437             const Vec prev_position = camera_map_screen(
438                 camera,
439                 event->motion.x + event->motion.xrel,
440                 event->motion.y + event->motion.yrel);
441
442             vec_add(&level_editor->camera_position,
443                     vec_sub(next_position, prev_position));
444         }
445
446     } break;
447     }
448
449     bool selected = false;
450     if (layer_picker_event(
451             &level_editor->layer_picker,
452             event,
453             camera,
454             &selected) < 0) {
455         return -1;
456     }
457
458     if (!selected) {
459         if (layer_event(
460                 level_editor->layers[level_editor->layer_picker],
461                 event,
462                 camera) < 0) {
463             return -1;
464         }
465     }
466
467
468     return 0;
469 }
470
471 int level_editor_event(LevelEditor *level_editor,
472                        const SDL_Event *event,
473                        const Camera *camera)
474 {
475     trace_assert(level_editor);
476     trace_assert(event);
477     trace_assert(camera);
478
479     switch (event->type) {
480     case SDL_MOUSEBUTTONDOWN: {
481         level_editor->notice.wiggly_text.color.a = 1.0f;
482     } break;
483     }
484
485     switch (level_editor->state) {
486     case LEVEL_EDITOR_IDLE:
487         return level_editor_idle_event(level_editor, event, camera);
488
489     case LEVEL_EDITOR_SAVEAS:
490         return level_editor_saveas_event(level_editor, event, camera);
491     }
492
493     return 0;
494 }
495
496 int level_editor_focus_camera(LevelEditor *level_editor,
497                               Camera *camera)
498 {
499     camera_center_at(camera, level_editor->camera_position);
500     camera_scale(camera, level_editor->camera_scale);
501     return 0;
502 }
503
504 /* TODO(#904): LevelEditor does not check that the saved level file is modified by external program */
505 static int level_editor_dump(const LevelEditor *level_editor)
506 {
507     trace_assert(level_editor);
508
509     FILE *filedump = PUSH_LT(
510         level_editor->lt,
511         fopen(level_editor->file_name, "w"),
512         fclose_lt);
513
514     if (fprintf(filedump, "%s\n", level_metadata_title(level_editor->metadata)) < 0) {
515         return -1;
516     }
517
518     for (size_t i = 0; i < LAYER_PICKER_N; ++i) {
519         if (layer_dump_stream(
520                 level_editor->layers[i],
521                 filedump) < 0) {
522             return -1;
523         }
524     }
525
526     fprintf(filedump, "%s", level_editor->supa_script_source);
527
528     fclose(RELEASE_LT(level_editor->lt, filedump));
529
530     return 0;
531 }
532
533 int level_editor_update(LevelEditor *level_editor, float delta_time)
534 {
535     return fading_wiggly_text_update(&level_editor->notice, delta_time);
536 }