]> git.lizzy.rs Git - nothing.git/blob - src/game/level/level_editor.c
11af440e5e463a541e20980e8c13c23282f21593
[nothing.git] / src / game / level / level_editor.c
1 #include <stdbool.h>
2
3 #include "game/camera.h"
4 #include "game/sound_samples.h"
5 #include "game/level/boxes.h"
6 #include "game/level/level_editor/action_picker.h"
7 #include "game/level/level_editor/color_picker.h"
8 #include "game/level/level_editor/rect_layer.h"
9 #include "game/level/level_editor/point_layer.h"
10 #include "game/level/level_editor/player_layer.h"
11 #include "game/level/level_editor/label_layer.h"
12 #include "game/level/level_editor/background_layer.h"
13 #include "ui/edit_field.h"
14 #include "system/stacktrace.h"
15 #include "system/nth_alloc.h"
16 #include "system/lt.h"
17 #include "system/lt_adapters.h"
18 #include "system/log.h"
19 #include "system/str.h"
20 #include "config.h"
21 #include "math/extrema.h"
22
23 #include "level_editor.h"
24
25 #define LEVEL_FOLDER_MAX_LENGTH 512
26 #define LEVEL_LINE_MAX_LENGTH 512
27 #define LEVEL_EDITOR_EDIT_FIELD_SIZE vec(5.0f, 5.0f)
28 #define LEVEL_EDITOR_EDIT_FIELD_COLOR COLOR_BLACK
29
30 #define LEVEL_EDITOR_NOTICE_SCALE vec(10.0f, 10.0f)
31 #define LEVEL_EDITOR_NOTICE_DURATION 1.0f
32 #define LEVEL_EDITOR_NOTICE_PADDING_TOP 100.0f
33
34 static int level_editor_dump(LevelEditor *level_editor);
35
36 // TODO(#994): too much duplicate code between create_level_editor and create_level_editor_from_file
37
38 LevelEditor *create_level_editor(Cursor *cursor)
39 {
40     Lt *lt = create_lt();
41     LevelEditor *level_editor = PUSH_LT(
42         lt,
43         nth_calloc(1, sizeof(LevelEditor)),
44         free);
45     if (level_editor == NULL) {
46         RETURN_LT(lt, NULL);
47     }
48     level_editor->lt = lt;
49
50     level_editor->edit_field_filename = PUSH_LT(
51         lt,
52         create_edit_field(
53             LEVEL_EDITOR_EDIT_FIELD_SIZE,
54             LEVEL_EDITOR_EDIT_FIELD_COLOR),
55         destroy_edit_field);
56     if (level_editor->edit_field_filename == NULL) {
57         RETURN_LT(lt, NULL);
58     }
59
60     level_editor->background_layer = create_background_layer(hexstr("fffda5"));
61
62     level_editor->player_layer =
63         create_player_layer(vec(0.0f, 0.0f), hexstr("ff8080"));
64
65     level_editor->platforms_layer = PUSH_LT(
66         lt,
67         create_rect_layer("platform", cursor),
68         destroy_rect_layer);
69     if (level_editor->platforms_layer == NULL) {
70         RETURN_LT(lt, NULL);
71     }
72
73     level_editor->goals_layer = PUSH_LT(
74         lt,
75         create_point_layer("goal"),
76         destroy_point_layer);
77     if (level_editor->goals_layer == NULL) {
78         RETURN_LT(lt, NULL);
79     }
80
81     level_editor->lava_layer = PUSH_LT(
82         lt,
83         create_rect_layer("lava", cursor),
84         destroy_rect_layer);
85     if (level_editor->lava_layer == NULL) {
86         RETURN_LT(lt, NULL);
87     }
88
89     level_editor->back_platforms_layer = PUSH_LT(
90         lt,
91         create_rect_layer("back_platform", cursor),
92         destroy_rect_layer);
93     if (level_editor->back_platforms_layer == NULL) {
94         RETURN_LT(lt, NULL);
95     }
96
97     level_editor->boxes_layer = PUSH_LT(
98         lt,
99         create_rect_layer("box", cursor),
100         destroy_rect_layer);
101     if (level_editor->boxes_layer == NULL) {
102         RETURN_LT(lt, NULL);
103     }
104
105     level_editor->label_layer = PUSH_LT(
106         lt,
107         create_label_layer("label"),
108         destroy_label_layer);
109     if (level_editor->label_layer == NULL) {
110         RETURN_LT(lt, NULL);
111     }
112
113     level_editor->regions_layer = PUSH_LT(
114         lt,
115         create_rect_layer("region", cursor),
116         destroy_rect_layer);
117     if (level_editor->regions_layer == NULL) {
118         RETURN_LT(lt, NULL);
119     }
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] = background_layer_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->notice = (FadingWigglyText) {
132         .wiggly_text = {
133             .text = "Level saved",
134             .color = rgba(0.0f, 0.0f, 0.0f, 0.0f),
135             .scale = LEVEL_EDITOR_NOTICE_SCALE
136         },
137         .duration = LEVEL_EDITOR_NOTICE_DURATION,
138     };
139
140     level_editor->camera_scale = 1.0f;
141
142     return level_editor;
143 }
144
145 LevelEditor *create_level_editor_from_file(const char *file_name, Cursor *cursor)
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     const char *line = line_stream_next(level_stream);
187     if (line == NULL) {
188         RETURN_LT(lt, NULL);
189     }
190
191     char version[METADATA_VERSION_MAX_SIZE] = {0};
192     memcpy(version, line,
193            MIN(size_t,
194                strlen(line),
195                METADATA_VERSION_MAX_SIZE - 1));
196     trim_endline(version);
197
198     if (strcmp(version, "1") == 0) {
199         if (line_stream_next(level_stream) == NULL)
200             RETURN_LT(lt, NULL);
201     } else if (strcmp(version, "2") == 0) {
202         // Nothing
203     } else {
204         log_fail("Version `%s` is not supported. Expected version `%s`.\n",
205                  version, VERSION);
206         RETURN_LT(lt, NULL);
207     }
208
209     if (background_layer_read_from_line_stream(
210             &level_editor->background_layer,
211             level_stream) < 0) {
212         RETURN_LT(lt, NULL);
213     }
214
215     level_editor->player_layer =
216         create_player_layer_from_line_stream(level_stream);
217
218     level_editor->platforms_layer =
219         PUSH_LT(
220             lt,
221             create_rect_layer_from_line_stream(level_stream, "platform", cursor),
222             destroy_rect_layer);
223     if (level_editor->platforms_layer == NULL) {
224         RETURN_LT(lt, NULL);
225     }
226
227     level_editor->goals_layer = PUSH_LT(
228         lt,
229         create_point_layer_from_line_stream(level_stream, "goal"),
230         destroy_point_layer);
231     if (level_editor->goals_layer == NULL) {
232         RETURN_LT(lt, NULL);
233     }
234
235     level_editor->lava_layer =
236         PUSH_LT(
237             lt,
238             create_rect_layer_from_line_stream(level_stream, "lava", cursor),
239             destroy_rect_layer);
240     if (level_editor->lava_layer == NULL) {
241         RETURN_LT(lt, NULL);
242     }
243
244     level_editor->back_platforms_layer =
245         PUSH_LT(
246             lt,
247             create_rect_layer_from_line_stream(level_stream, "back_platform", cursor),
248             destroy_rect_layer);
249     if (level_editor->back_platforms_layer == NULL) {
250         RETURN_LT(lt, NULL);
251     }
252
253     level_editor->boxes_layer =
254         PUSH_LT(
255             lt,
256             create_rect_layer_from_line_stream(level_stream, "box", cursor),
257             destroy_rect_layer);
258     if (level_editor->boxes_layer == NULL) {
259         RETURN_LT(lt, NULL);
260     }
261
262     level_editor->label_layer =
263         PUSH_LT(
264             lt,
265             create_label_layer_from_line_stream(level_stream, "label"),
266             destroy_label_layer);
267     if (level_editor->label_layer == NULL) {
268         RETURN_LT(lt, NULL);
269     }
270
271     level_editor->regions_layer =
272         PUSH_LT(
273             lt,
274             create_rect_layer_from_line_stream(level_stream, "region", cursor),
275             destroy_rect_layer);
276     if (level_editor->regions_layer == NULL) {
277         RETURN_LT(lt, NULL);
278     }
279
280     level_editor->layers[LAYER_PICKER_BOXES] = rect_layer_as_layer(level_editor->boxes_layer);
281     level_editor->layers[LAYER_PICKER_PLATFORMS] = rect_layer_as_layer(level_editor->platforms_layer);
282     level_editor->layers[LAYER_PICKER_BACK_PLATFORMS] = rect_layer_as_layer(level_editor->back_platforms_layer);
283     level_editor->layers[LAYER_PICKER_GOALS] = point_layer_as_layer(level_editor->goals_layer);
284     level_editor->layers[LAYER_PICKER_PLAYER] = player_layer_as_layer(&level_editor->player_layer);
285     level_editor->layers[LAYER_PICKER_LAVA] = rect_layer_as_layer(level_editor->lava_layer);
286     level_editor->layers[LAYER_PICKER_REGIONS] = rect_layer_as_layer(level_editor->regions_layer);
287     level_editor->layers[LAYER_PICKER_BACKGROUND] = background_layer_as_layer(&level_editor->background_layer);
288     level_editor->layers[LAYER_PICKER_LABELS] = label_layer_as_layer(level_editor->label_layer);
289
290     level_editor->drag = false;
291
292     level_editor->notice = (FadingWigglyText) {
293         .wiggly_text = {
294             .text = "Level saved",
295             .color = rgba(0.0f, 0.0f, 0.0f, 0.0f),
296             .scale = LEVEL_EDITOR_NOTICE_SCALE
297         },
298         .duration = LEVEL_EDITOR_NOTICE_DURATION,
299     };
300
301     level_editor->camera_scale = 1.0f;
302
303     return level_editor;
304 }
305
306 void destroy_level_editor(LevelEditor *level_editor)
307 {
308     trace_assert(level_editor);
309     destroy_undo_history(level_editor->undo_history);
310     RETURN_LT0(level_editor->lt);
311 }
312
313 int level_editor_render(const LevelEditor *level_editor,
314                         const Camera *camera)
315 {
316     trace_assert(level_editor);
317     trace_assert(camera);
318
319     if (camera_clear_background(camera, color_picker_rgba(&level_editor->background_layer.color_picker)) < 0) {
320         return -1;
321     }
322
323     const Rect world_viewport = camera_view_port(camera);
324
325     if (PLAYER_DEATH_LEVEL < world_viewport.y + world_viewport.h) {
326         if (camera_fill_rect(
327                 camera,
328                 rect(
329                     world_viewport.x, PLAYER_DEATH_LEVEL,
330                     world_viewport.w, world_viewport.h + fmaxf(0.0f, world_viewport.y - PLAYER_DEATH_LEVEL)),
331                 LEVEL_EDITOR_DETH_LEVEL_COLOR) < 0) {
332             return -1;
333         }
334     }
335
336     for (size_t i = 0; i < LAYER_PICKER_N; ++i) {
337         if (layer_render(
338                 level_editor->layers[i],
339                 camera,
340                 i == level_editor->layer_picker) < 0) {
341             return -1;
342         }
343     }
344
345     if (layer_picker_render(&level_editor->layer_picker, camera) < 0) {
346         return -1;
347     }
348
349     if (level_editor->state == LEVEL_EDITOR_SAVEAS) {
350         /* CSS */
351         const Vec2f size = LEVEL_EDITOR_EDIT_FIELD_SIZE;
352         const char *save_as_text = "Save as: ";
353         const Vec2f position = vec(200.0f, 200.0f);
354         const float save_as_width =
355             (float) strlen(save_as_text) * FONT_CHAR_WIDTH * size.x;
356
357         /* HTML */
358         camera_render_text_screen(
359             camera,
360             save_as_text,
361             LEVEL_EDITOR_EDIT_FIELD_SIZE,
362             LEVEL_EDITOR_EDIT_FIELD_COLOR,
363             position);
364
365         if (edit_field_render_screen(
366                 level_editor->edit_field_filename,
367                 camera,
368                 vec(position.x + save_as_width, position.y)) < 0) {
369             return -1;
370         }
371     }
372
373     const Rect screen_viewport = camera_view_port_screen(camera);
374     const Vec2f text_size = fading_wiggly_text_size(&level_editor->notice);
375
376     fading_wiggly_text_render(
377         &level_editor->notice, camera,
378         vec(screen_viewport.w * 0.5f - text_size.x * 0.5f,
379             LEVEL_EDITOR_NOTICE_PADDING_TOP));
380
381     return 0;
382 }
383
384 static
385 int level_editor_saveas_event(LevelEditor *level_editor,
386                               const SDL_Event *event,
387                               const Camera *camera)
388 {
389     trace_assert(level_editor);
390     trace_assert(event);
391     trace_assert(camera);
392
393     switch (event->type) {
394     case SDL_KEYDOWN: {
395         if (event->key.keysym.sym == SDLK_RETURN) {
396             trace_assert(level_editor->file_name == NULL);
397             char path[LEVEL_FOLDER_MAX_LENGTH];
398             snprintf(
399                 path,
400                 LEVEL_FOLDER_MAX_LENGTH,
401                 "./assets/levels/%s.txt",
402                 edit_field_as_text(level_editor->edit_field_filename));
403             level_editor->file_name = PUSH_LT(
404                 level_editor->lt,
405                 string_duplicate(path, NULL),
406                 free);
407             level_editor_dump(level_editor);
408             SDL_StopTextInput();
409             level_editor->state = LEVEL_EDITOR_IDLE;
410             return 0;
411         }
412     } break;
413     }
414
415     return edit_field_event(level_editor->edit_field_filename, event);
416 }
417
418 static
419 int level_editor_idle_event(LevelEditor *level_editor,
420                             const SDL_Event *event,
421                             Camera *camera)
422 {
423     trace_assert(level_editor);
424     trace_assert(event);
425     trace_assert(camera);
426
427     switch (event->type) {
428     case SDL_KEYDOWN: {
429         switch(event->key.keysym.sym) {
430         case SDLK_s: {
431             if (!SDL_IsTextInputActive()) {
432                 if (level_editor->file_name) {
433                     level_editor_dump(level_editor);
434                     log_info("Saving level to `%s`\n", level_editor->file_name);
435                 } else {
436                     SDL_StartTextInput();
437                     level_editor->state = LEVEL_EDITOR_SAVEAS;
438                 }
439             }
440         } break;
441
442         case SDLK_z: {
443             if (event->key.keysym.mod & KMOD_CTRL) {
444                 if (undo_history_empty(&level_editor->undo_history)) {
445                     level_editor->bell = 1;
446                 }
447                 undo_history_pop(&level_editor->undo_history);
448             }
449         } break;
450         }
451     } break;
452
453     case SDL_MOUSEWHEEL: {
454         int x, y;
455         SDL_GetMouseState(&x, &y);
456
457         Vec2f position = camera_map_screen(camera, x, y);
458         if (event->wheel.y > 0) {
459             level_editor->camera_scale += 0.1f;
460         } else if (event->wheel.y < 0) {
461             level_editor->camera_scale = fmaxf(0.1f, level_editor->camera_scale - 0.1f);
462         }
463         camera_scale(camera, level_editor->camera_scale);
464         Vec2f zoomed_position = camera_map_screen(camera, x, y);
465
466         level_editor->camera_position =
467             vec_sum(
468                 level_editor->camera_position,
469                 vec_sub(position, zoomed_position));
470         camera_center_at(camera, level_editor->camera_position);
471     } break;
472
473     case SDL_MOUSEBUTTONUP:
474     case SDL_MOUSEBUTTONDOWN: {
475         if (event->type == SDL_MOUSEBUTTONDOWN && event->button.button == SDL_BUTTON_MIDDLE) {
476             level_editor->drag = true;
477         }
478
479         if (event->type == SDL_MOUSEBUTTONUP && event->button.button == SDL_BUTTON_MIDDLE) {
480             level_editor->drag = false;
481         }
482     } break;
483
484     case SDL_MOUSEMOTION: {
485         if (level_editor->drag) {
486             const Vec2f next_position = camera_map_screen(camera, event->motion.x, event->motion.y);
487             const Vec2f prev_position = camera_map_screen(
488                 camera,
489                 event->motion.x + event->motion.xrel,
490                 event->motion.y + event->motion.yrel);
491
492             vec_add(&level_editor->camera_position,
493                     vec_sub(next_position, prev_position));
494             camera_center_at(camera, level_editor->camera_position);
495         }
496
497     } break;
498     }
499
500     bool selected = false;
501     if (layer_picker_event(
502             &level_editor->layer_picker,
503             event,
504             camera,
505             &selected) < 0) {
506         return -1;
507     }
508
509     if (!selected) {
510         if (layer_event(
511                 level_editor->layers[level_editor->layer_picker],
512                 event,
513                 camera,
514                 &level_editor->undo_history) < 0) {
515             return -1;
516         }
517     } else {
518         level_editor->click = 1;
519     }
520
521
522     return 0;
523 }
524
525 int level_editor_event(LevelEditor *level_editor,
526                        const SDL_Event *event,
527                        Camera *camera)
528 {
529     trace_assert(level_editor);
530     trace_assert(event);
531     trace_assert(camera);
532
533     switch (level_editor->state) {
534     case LEVEL_EDITOR_IDLE:
535         return level_editor_idle_event(level_editor, event, camera);
536
537     case LEVEL_EDITOR_SAVEAS:
538         return level_editor_saveas_event(level_editor, event, camera);
539     }
540
541     return 0;
542 }
543
544 int level_editor_focus_camera(LevelEditor *level_editor,
545                               Camera *camera)
546 {
547     camera_center_at(camera, level_editor->camera_position);
548     camera_scale(camera, level_editor->camera_scale);
549     return 0;
550 }
551
552 static LayerPicker level_format_layer_order[LAYER_PICKER_N] = {
553     LAYER_PICKER_BACKGROUND,
554     LAYER_PICKER_PLAYER,
555     LAYER_PICKER_PLATFORMS,
556     LAYER_PICKER_GOALS,
557     LAYER_PICKER_LAVA,
558     LAYER_PICKER_BACK_PLATFORMS,
559     LAYER_PICKER_BOXES,
560     LAYER_PICKER_LABELS,
561     LAYER_PICKER_REGIONS
562 };
563
564 /* TODO(#904): LevelEditor does not check that the saved level file is modified by external program */
565 static int level_editor_dump(LevelEditor *level_editor)
566 {
567     trace_assert(level_editor);
568
569     FILE *filedump = PUSH_LT(
570         level_editor->lt,
571         fopen(level_editor->file_name, "w"),
572         fclose_lt);
573
574     if (fprintf(filedump, "%s\n", VERSION) < 0) {
575         return -1;
576     }
577
578     for (size_t i = 0; i < LAYER_PICKER_N; ++i) {
579         if (layer_dump_stream(
580                 level_editor->layers[level_format_layer_order[i]],
581                 filedump) < 0) {
582             return -1;
583         }
584     }
585
586     fclose(RELEASE_LT(level_editor->lt, filedump));
587
588     fading_wiggly_text_reset(&level_editor->notice);
589     level_editor->save = 1;
590
591     return 0;
592 }
593
594 int level_editor_update(LevelEditor *level_editor, float delta_time)
595 {
596     return fading_wiggly_text_update(&level_editor->notice, delta_time);
597 }
598
599 void level_editor_sound(LevelEditor *level_editor, Sound_samples *sound_samples)
600 {
601     trace_assert(sound_samples);
602
603     if (level_editor) {
604         if (level_editor->bell) {
605             level_editor->bell = 0;
606             sound_samples_play_sound(sound_samples, 2);
607         }
608
609         if (level_editor->click) {
610             level_editor->click = 0;
611             sound_samples_play_sound(sound_samples, 3);
612         }
613
614         if (level_editor->save) {
615             level_editor->save = 0;
616             sound_samples_play_sound(sound_samples, 4);
617         }
618     }
619 }