]> git.lizzy.rs Git - nothing.git/blob - src/game/level/level_editor.c
Delete LineStream from the existance
[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 #include "system/file.h"
23
24 #include "level_editor.h"
25
26 #define LEVEL_FOLDER_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 #define LEVEL_EDITOR_TMPMEM_CAPACITY (640 * KILO)
34
35 static int level_editor_dump(LevelEditor *level_editor);
36
37 // TODO(#994): too much duplicate code between create_level_editor and create_level_editor_from_file
38
39 LevelEditor *create_level_editor(Cursor *cursor)
40 {
41     Lt *lt = create_lt();
42     LevelEditor *level_editor = PUSH_LT(
43         lt,
44         nth_calloc(1, sizeof(LevelEditor)),
45         free);
46     if (level_editor == NULL) {
47         RETURN_LT(lt, NULL);
48     }
49     level_editor->lt = lt;
50
51     level_editor->edit_field_filename = PUSH_LT(
52         lt,
53         create_edit_field(
54             LEVEL_EDITOR_EDIT_FIELD_SIZE,
55             LEVEL_EDITOR_EDIT_FIELD_COLOR),
56         destroy_edit_field);
57     if (level_editor->edit_field_filename == NULL) {
58         RETURN_LT(lt, NULL);
59     }
60
61     level_editor->background_layer = create_background_layer(hexstr("fffda5"));
62
63     level_editor->player_layer =
64         create_player_layer(vec(0.0f, 0.0f), hexstr("ff8080"));
65
66     level_editor->platforms_layer = PUSH_LT(
67         lt,
68         create_rect_layer("platform", cursor),
69         destroy_rect_layer);
70     if (level_editor->platforms_layer == NULL) {
71         RETURN_LT(lt, NULL);
72     }
73
74     level_editor->goals_layer = PUSH_LT(
75         lt,
76         create_point_layer("goal"),
77         destroy_point_layer);
78     if (level_editor->goals_layer == NULL) {
79         RETURN_LT(lt, NULL);
80     }
81
82     level_editor->lava_layer = PUSH_LT(
83         lt,
84         create_rect_layer("lava", cursor),
85         destroy_rect_layer);
86     if (level_editor->lava_layer == NULL) {
87         RETURN_LT(lt, NULL);
88     }
89
90     level_editor->back_platforms_layer = PUSH_LT(
91         lt,
92         create_rect_layer("back_platform", cursor),
93         destroy_rect_layer);
94     if (level_editor->back_platforms_layer == NULL) {
95         RETURN_LT(lt, NULL);
96     }
97
98     level_editor->boxes_layer = PUSH_LT(
99         lt,
100         create_rect_layer("box", cursor),
101         destroy_rect_layer);
102     if (level_editor->boxes_layer == NULL) {
103         RETURN_LT(lt, NULL);
104     }
105
106     level_editor->label_layer = PUSH_LT(
107         lt,
108         create_label_layer("label"),
109         destroy_label_layer);
110     if (level_editor->label_layer == NULL) {
111         RETURN_LT(lt, NULL);
112     }
113
114     level_editor->regions_layer = PUSH_LT(
115         lt,
116         create_rect_layer("region", cursor),
117         destroy_rect_layer);
118     if (level_editor->regions_layer == NULL) {
119         RETURN_LT(lt, NULL);
120     }
121
122     level_editor->layers[LAYER_PICKER_BOXES] = rect_layer_as_layer(level_editor->boxes_layer);
123     level_editor->layers[LAYER_PICKER_PLATFORMS] = rect_layer_as_layer(level_editor->platforms_layer);
124     level_editor->layers[LAYER_PICKER_BACK_PLATFORMS] = rect_layer_as_layer(level_editor->back_platforms_layer);
125     level_editor->layers[LAYER_PICKER_GOALS] = point_layer_as_layer(level_editor->goals_layer);
126     level_editor->layers[LAYER_PICKER_PLAYER] = player_layer_as_layer(&level_editor->player_layer);
127     level_editor->layers[LAYER_PICKER_LAVA] = rect_layer_as_layer(level_editor->lava_layer);
128     level_editor->layers[LAYER_PICKER_REGIONS] = rect_layer_as_layer(level_editor->regions_layer);
129     level_editor->layers[LAYER_PICKER_BACKGROUND] = background_layer_as_layer(&level_editor->background_layer);
130     level_editor->layers[LAYER_PICKER_LABELS] = label_layer_as_layer(level_editor->label_layer);
131
132     level_editor->notice = (FadingWigglyText) {
133         .wiggly_text = {
134             .text = "Level saved",
135             .color = rgba(0.0f, 0.0f, 0.0f, 0.0f),
136             .scale = LEVEL_EDITOR_NOTICE_SCALE
137         },
138         .duration = LEVEL_EDITOR_NOTICE_DURATION,
139     };
140
141     level_editor->camera_scale = 1.0f;
142
143     return level_editor;
144 }
145
146 LevelEditor *create_level_editor_from_file(const char *file_name, Cursor *cursor)
147 {
148     trace_assert(file_name);
149
150     Lt *lt = create_lt();
151     LevelEditor *level_editor = PUSH_LT(
152         lt,
153         nth_calloc(1, sizeof(LevelEditor)),
154         free);
155     if (level_editor == NULL) {
156         RETURN_LT(lt, NULL);
157     }
158     level_editor->lt = lt;
159
160     level_editor->edit_field_filename = PUSH_LT(
161         lt,
162         create_edit_field(
163             LEVEL_EDITOR_EDIT_FIELD_SIZE,
164             LEVEL_EDITOR_EDIT_FIELD_COLOR),
165         destroy_edit_field);
166     if (level_editor->edit_field_filename == NULL) {
167         RETURN_LT(lt, NULL);
168     }
169
170     level_editor->file_name =
171         PUSH_LT(
172             lt,
173             string_duplicate(file_name, NULL),
174             free);
175
176     Memory tmpmem = {
177         .capacity = LEVEL_EDITOR_TMPMEM_CAPACITY,
178         .buffer = malloc(LEVEL_EDITOR_TMPMEM_CAPACITY),
179     };
180     trace_assert(tmpmem.buffer);
181
182     String input = read_whole_file(&tmpmem, file_name);
183     trace_assert(input.data);
184
185     String version = trim(chop_by_delim(&input, '\n'));
186
187     if (string_equal(version, STRING_LIT("1"))) {
188         chop_by_delim(&input, '\n');
189     } else if (string_equal(version, STRING_LIT("2"))) {
190         // Nothing
191     } else {
192         log_fail("Version `%s` is not supported. Expected version `%s`.\n",
193                  string_to_cstr(&tmpmem, version),
194                  VERSION);
195         RETURN_LT(lt, NULL);
196     }
197
198     level_editor->background_layer = chop_background_layer(&input);
199     level_editor->player_layer = chop_player_layer(&tmpmem, &input);
200     level_editor->platforms_layer = chop_rect_layer(&tmpmem, &input, "platform", cursor);
201     level_editor->goals_layer = chop_point_layer(&tmpmem, &input, "goal");
202     level_editor->lava_layer = chop_rect_layer(&tmpmem, &input, "lava", cursor);
203     level_editor->back_platforms_layer = chop_rect_layer(&tmpmem, &input, "back_platform", cursor);
204     level_editor->boxes_layer = chop_rect_layer(&tmpmem, &input, "box", cursor);
205     level_editor->label_layer = chop_label_layer(&tmpmem, &input, "label");
206     level_editor->regions_layer = chop_rect_layer(&tmpmem, &input, "region", cursor),
207
208     level_editor->layers[LAYER_PICKER_BOXES] = rect_layer_as_layer(level_editor->boxes_layer);
209     level_editor->layers[LAYER_PICKER_PLATFORMS] = rect_layer_as_layer(level_editor->platforms_layer);
210     level_editor->layers[LAYER_PICKER_BACK_PLATFORMS] = rect_layer_as_layer(level_editor->back_platforms_layer);
211     level_editor->layers[LAYER_PICKER_GOALS] = point_layer_as_layer(level_editor->goals_layer);
212     level_editor->layers[LAYER_PICKER_PLAYER] = player_layer_as_layer(&level_editor->player_layer);
213     level_editor->layers[LAYER_PICKER_LAVA] = rect_layer_as_layer(level_editor->lava_layer);
214     level_editor->layers[LAYER_PICKER_REGIONS] = rect_layer_as_layer(level_editor->regions_layer);
215     level_editor->layers[LAYER_PICKER_BACKGROUND] = background_layer_as_layer(&level_editor->background_layer);
216     level_editor->layers[LAYER_PICKER_LABELS] = label_layer_as_layer(level_editor->label_layer);
217
218     level_editor->drag = false;
219
220     level_editor->notice = (FadingWigglyText) {
221         .wiggly_text = {
222             .text = "Level saved",
223             .color = rgba(0.0f, 0.0f, 0.0f, 0.0f),
224             .scale = LEVEL_EDITOR_NOTICE_SCALE
225         },
226         .duration = LEVEL_EDITOR_NOTICE_DURATION,
227     };
228
229     level_editor->camera_scale = 1.0f;
230
231     log_info("%ld bytes of tmp memory consumed during parsing the level\n", tmpmem.size);
232
233     free(tmpmem.buffer);
234
235     return level_editor;
236 }
237
238 void destroy_level_editor(LevelEditor *level_editor)
239 {
240     trace_assert(level_editor);
241     destroy_undo_history(level_editor->undo_history);
242     RETURN_LT0(level_editor->lt);
243 }
244
245 int level_editor_render(const LevelEditor *level_editor,
246                         const Camera *camera)
247 {
248     trace_assert(level_editor);
249     trace_assert(camera);
250
251     if (camera_clear_background(camera, color_picker_rgba(&level_editor->background_layer.color_picker)) < 0) {
252         return -1;
253     }
254
255     const Rect world_viewport = camera_view_port(camera);
256
257     if (PLAYER_DEATH_LEVEL < world_viewport.y + world_viewport.h) {
258         if (camera_fill_rect(
259                 camera,
260                 rect(
261                     world_viewport.x, PLAYER_DEATH_LEVEL,
262                     world_viewport.w, world_viewport.h + fmaxf(0.0f, world_viewport.y - PLAYER_DEATH_LEVEL)),
263                 LEVEL_EDITOR_DETH_LEVEL_COLOR) < 0) {
264             return -1;
265         }
266     }
267
268     for (size_t i = 0; i < LAYER_PICKER_N; ++i) {
269         if (layer_render(
270                 level_editor->layers[i],
271                 camera,
272                 i == level_editor->layer_picker) < 0) {
273             return -1;
274         }
275     }
276
277     if (layer_picker_render(&level_editor->layer_picker, camera) < 0) {
278         return -1;
279     }
280
281     if (level_editor->state == LEVEL_EDITOR_SAVEAS) {
282         /* CSS */
283         const Vec2f size = LEVEL_EDITOR_EDIT_FIELD_SIZE;
284         const char *save_as_text = "Save as: ";
285         const Vec2f position = vec(200.0f, 200.0f);
286         const float save_as_width =
287             (float) strlen(save_as_text) * FONT_CHAR_WIDTH * size.x;
288
289         /* HTML */
290         camera_render_text_screen(
291             camera,
292             save_as_text,
293             LEVEL_EDITOR_EDIT_FIELD_SIZE,
294             LEVEL_EDITOR_EDIT_FIELD_COLOR,
295             position);
296
297         if (edit_field_render_screen(
298                 level_editor->edit_field_filename,
299                 camera,
300                 vec(position.x + save_as_width, position.y)) < 0) {
301             return -1;
302         }
303     }
304
305     const Rect screen_viewport = camera_view_port_screen(camera);
306     const Vec2f text_size = fading_wiggly_text_size(&level_editor->notice);
307
308     fading_wiggly_text_render(
309         &level_editor->notice, camera,
310         vec(screen_viewport.w * 0.5f - text_size.x * 0.5f,
311             LEVEL_EDITOR_NOTICE_PADDING_TOP));
312
313     return 0;
314 }
315
316 static
317 int level_editor_saveas_event(LevelEditor *level_editor,
318                               const SDL_Event *event,
319                               const Camera *camera)
320 {
321     trace_assert(level_editor);
322     trace_assert(event);
323     trace_assert(camera);
324
325     switch (event->type) {
326     case SDL_KEYDOWN: {
327         if (event->key.keysym.sym == SDLK_RETURN) {
328             trace_assert(level_editor->file_name == NULL);
329             char path[LEVEL_FOLDER_MAX_LENGTH];
330             snprintf(
331                 path,
332                 LEVEL_FOLDER_MAX_LENGTH,
333                 "./assets/levels/%s.txt",
334                 edit_field_as_text(level_editor->edit_field_filename));
335             level_editor->file_name = PUSH_LT(
336                 level_editor->lt,
337                 string_duplicate(path, NULL),
338                 free);
339             level_editor_dump(level_editor);
340             SDL_StopTextInput();
341             level_editor->state = LEVEL_EDITOR_IDLE;
342             return 0;
343         }
344     } break;
345     }
346
347     return edit_field_event(level_editor->edit_field_filename, event);
348 }
349
350 static
351 int level_editor_idle_event(LevelEditor *level_editor,
352                             const SDL_Event *event,
353                             Camera *camera)
354 {
355     trace_assert(level_editor);
356     trace_assert(event);
357     trace_assert(camera);
358
359     switch (event->type) {
360     case SDL_KEYDOWN: {
361         switch(event->key.keysym.sym) {
362         case SDLK_s: {
363             if (!SDL_IsTextInputActive()) {
364                 if (level_editor->file_name) {
365                     level_editor_dump(level_editor);
366                     log_info("Saving level to `%s`\n", level_editor->file_name);
367                 } else {
368                     SDL_StartTextInput();
369                     level_editor->state = LEVEL_EDITOR_SAVEAS;
370                 }
371             }
372         } break;
373
374         case SDLK_z: {
375             if (event->key.keysym.mod & KMOD_CTRL) {
376                 if (undo_history_empty(&level_editor->undo_history)) {
377                     level_editor->bell = 1;
378                 }
379                 undo_history_pop(&level_editor->undo_history);
380             }
381         } break;
382         }
383     } break;
384
385     case SDL_MOUSEWHEEL: {
386         int x, y;
387         SDL_GetMouseState(&x, &y);
388
389         Vec2f position = camera_map_screen(camera, x, y);
390         if (event->wheel.y > 0) {
391             level_editor->camera_scale += 0.1f;
392         } else if (event->wheel.y < 0) {
393             level_editor->camera_scale = fmaxf(0.1f, level_editor->camera_scale - 0.1f);
394         }
395         camera_scale(camera, level_editor->camera_scale);
396         Vec2f zoomed_position = camera_map_screen(camera, x, y);
397
398         level_editor->camera_position =
399             vec_sum(
400                 level_editor->camera_position,
401                 vec_sub(position, zoomed_position));
402         camera_center_at(camera, level_editor->camera_position);
403     } break;
404
405     case SDL_MOUSEBUTTONUP:
406     case SDL_MOUSEBUTTONDOWN: {
407         if (event->type == SDL_MOUSEBUTTONDOWN && event->button.button == SDL_BUTTON_MIDDLE) {
408             level_editor->drag = true;
409         }
410
411         if (event->type == SDL_MOUSEBUTTONUP && event->button.button == SDL_BUTTON_MIDDLE) {
412             level_editor->drag = false;
413         }
414     } break;
415
416     case SDL_MOUSEMOTION: {
417         if (level_editor->drag) {
418             const Vec2f next_position = camera_map_screen(camera, event->motion.x, event->motion.y);
419             const Vec2f prev_position = camera_map_screen(
420                 camera,
421                 event->motion.x + event->motion.xrel,
422                 event->motion.y + event->motion.yrel);
423
424             vec_add(&level_editor->camera_position,
425                     vec_sub(next_position, prev_position));
426             camera_center_at(camera, level_editor->camera_position);
427         }
428
429     } break;
430     }
431
432     bool selected = false;
433     if (layer_picker_event(
434             &level_editor->layer_picker,
435             event,
436             camera,
437             &selected) < 0) {
438         return -1;
439     }
440
441     if (!selected) {
442         if (layer_event(
443                 level_editor->layers[level_editor->layer_picker],
444                 event,
445                 camera,
446                 &level_editor->undo_history) < 0) {
447             return -1;
448         }
449     } else {
450         level_editor->click = 1;
451     }
452
453
454     return 0;
455 }
456
457 int level_editor_event(LevelEditor *level_editor,
458                        const SDL_Event *event,
459                        Camera *camera)
460 {
461     trace_assert(level_editor);
462     trace_assert(event);
463     trace_assert(camera);
464
465     switch (level_editor->state) {
466     case LEVEL_EDITOR_IDLE:
467         return level_editor_idle_event(level_editor, event, camera);
468
469     case LEVEL_EDITOR_SAVEAS:
470         return level_editor_saveas_event(level_editor, event, camera);
471     }
472
473     return 0;
474 }
475
476 int level_editor_focus_camera(LevelEditor *level_editor,
477                               Camera *camera)
478 {
479     camera_center_at(camera, level_editor->camera_position);
480     camera_scale(camera, level_editor->camera_scale);
481     return 0;
482 }
483
484 static LayerPicker level_format_layer_order[LAYER_PICKER_N] = {
485     LAYER_PICKER_BACKGROUND,
486     LAYER_PICKER_PLAYER,
487     LAYER_PICKER_PLATFORMS,
488     LAYER_PICKER_GOALS,
489     LAYER_PICKER_LAVA,
490     LAYER_PICKER_BACK_PLATFORMS,
491     LAYER_PICKER_BOXES,
492     LAYER_PICKER_LABELS,
493     LAYER_PICKER_REGIONS
494 };
495
496 /* TODO(#904): LevelEditor does not check that the saved level file is modified by external program */
497 static int level_editor_dump(LevelEditor *level_editor)
498 {
499     trace_assert(level_editor);
500
501     FILE *filedump = PUSH_LT(
502         level_editor->lt,
503         fopen(level_editor->file_name, "w"),
504         fclose_lt);
505
506     if (fprintf(filedump, "%s\n", VERSION) < 0) {
507         return -1;
508     }
509
510     for (size_t i = 0; i < LAYER_PICKER_N; ++i) {
511         if (layer_dump_stream(
512                 level_editor->layers[level_format_layer_order[i]],
513                 filedump) < 0) {
514             return -1;
515         }
516     }
517
518     fclose(RELEASE_LT(level_editor->lt, filedump));
519
520     fading_wiggly_text_reset(&level_editor->notice);
521     level_editor->save = 1;
522
523     return 0;
524 }
525
526 int level_editor_update(LevelEditor *level_editor, float delta_time)
527 {
528     return fading_wiggly_text_update(&level_editor->notice, delta_time);
529 }
530
531 void level_editor_sound(LevelEditor *level_editor, Sound_samples *sound_samples)
532 {
533     trace_assert(sound_samples);
534
535     if (level_editor) {
536         if (level_editor->bell) {
537             level_editor->bell = 0;
538             sound_samples_play_sound(sound_samples, 2);
539         }
540
541         if (level_editor->click) {
542             level_editor->click = 0;
543             sound_samples_play_sound(sound_samples, 3);
544         }
545
546         if (level_editor->save) {
547             level_editor->save = 0;
548             sound_samples_play_sound(sound_samples, 4);
549         }
550     }
551 }