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