]> git.lizzy.rs Git - nothing.git/blob - src/game/level/level_editor.c
0d25850301171dbcefb6f3614aba4b3f87ec8a12
[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 "game/level/level_editor/background_layer.h"
12 #include "ui/edit_field.h"
13 #include "system/stacktrace.h"
14 #include "system/nth_alloc.h"
15 #include "system/lt.h"
16 #include "system/lt_adapters.h"
17 #include "system/log.h"
18 #include "system/str.h"
19
20 #include "level_editor.h"
21
22 #define LEVEL_LINE_MAX_LENGTH 512
23 #define LEVEL_EDITOR_EDIT_FIELD_SIZE vec(5.0f, 5.0f)
24 #define LEVEL_EDITOR_EDIT_FIELD_COLOR COLOR_BLACK
25
26 #define LEVEL_EDITOR_NOTICE_SCALE vec(10.0f, 10.0f)
27 #define LEVEL_EDITOR_NOTICE_DURATION 1.0f
28 #define LEVEL_EDITOR_NOTICE_PADDING_TOP 100.0f
29
30 static int level_editor_dump(LevelEditor *level_editor);
31
32 // TODO(#994): too much duplicate code between create_level_editor and create_level_editor_from_file
33
34 LevelEditor *create_level_editor(void)
35 {
36     Lt *lt = create_lt();
37     LevelEditor *level_editor = PUSH_LT(
38         lt,
39         nth_calloc(1, sizeof(LevelEditor)),
40         free);
41     if (level_editor == NULL) {
42         RETURN_LT(lt, NULL);
43     }
44     level_editor->lt = lt;
45
46     level_editor->edit_field_filename = PUSH_LT(
47         lt,
48         create_edit_field(
49             LEVEL_EDITOR_EDIT_FIELD_SIZE,
50             LEVEL_EDITOR_EDIT_FIELD_COLOR),
51         destroy_edit_field);
52     if (level_editor->edit_field_filename == NULL) {
53         RETURN_LT(lt, NULL);
54     }
55
56     level_editor->metadata = PUSH_LT(
57         lt,
58         create_level_metadata("New Level"),
59         destroy_level_metadata);
60     if (level_editor->metadata == NULL) {
61         RETURN_LT(lt, NULL);
62     }
63
64     level_editor->background_layer = create_background_layer(hexstr("fffda5"));
65
66     level_editor->player_layer =
67         create_player_layer(vec(0.0f, 0.0f), hexstr("ff8080"));
68
69     level_editor->platforms_layer = PUSH_LT(
70         lt,
71         create_rect_layer("platform"),
72         destroy_rect_layer);
73     if (level_editor->platforms_layer == NULL) {
74         RETURN_LT(lt, NULL);
75     }
76
77     level_editor->goals_layer = PUSH_LT(
78         lt,
79         create_point_layer("goal"),
80         destroy_point_layer);
81     if (level_editor->goals_layer == NULL) {
82         RETURN_LT(lt, NULL);
83     }
84
85     level_editor->lava_layer = PUSH_LT(
86         lt,
87         create_rect_layer("lava"),
88         destroy_rect_layer);
89     if (level_editor->lava_layer == NULL) {
90         RETURN_LT(lt, NULL);
91     }
92
93     level_editor->back_platforms_layer = PUSH_LT(
94         lt,
95         create_rect_layer("back_platform"),
96         destroy_rect_layer);
97     if (level_editor->back_platforms_layer == NULL) {
98         RETURN_LT(lt, NULL);
99     }
100
101     level_editor->boxes_layer = PUSH_LT(
102         lt,
103         create_rect_layer("box"),
104         destroy_rect_layer);
105     if (level_editor->boxes_layer == NULL) {
106         RETURN_LT(lt, NULL);
107     }
108
109     level_editor->label_layer = PUSH_LT(
110         lt,
111         create_label_layer("label"),
112         destroy_label_layer);
113     if (level_editor->label_layer == NULL) {
114         RETURN_LT(lt, NULL);
115     }
116
117     level_editor->regions_layer = PUSH_LT(
118         lt,
119         create_rect_layer("region"),
120         destroy_rect_layer);
121     if (level_editor->regions_layer == NULL) {
122         RETURN_LT(lt, NULL);
123     }
124
125     level_editor->supa_script_source =
126         PUSH_LT(lt, string_duplicate("", NULL), free);
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->supa_script_source =
273         PUSH_LT(
274             lt,
275             line_stream_collect_until_end(level_stream),
276             free);
277     if (level_editor->supa_script_source == NULL) {
278         RETURN_LT(lt, NULL);
279     }
280
281     level_editor->layers[LAYER_PICKER_BOXES] = rect_layer_as_layer(level_editor->boxes_layer);
282     level_editor->layers[LAYER_PICKER_PLATFORMS] = rect_layer_as_layer(level_editor->platforms_layer);
283     level_editor->layers[LAYER_PICKER_BACK_PLATFORMS] = rect_layer_as_layer(level_editor->back_platforms_layer);
284     level_editor->layers[LAYER_PICKER_GOALS] = point_layer_as_layer(level_editor->goals_layer);
285     level_editor->layers[LAYER_PICKER_PLAYER] = player_layer_as_layer(&level_editor->player_layer);
286     level_editor->layers[LAYER_PICKER_LAVA] = rect_layer_as_layer(level_editor->lava_layer);
287     level_editor->layers[LAYER_PICKER_REGIONS] = rect_layer_as_layer(level_editor->regions_layer);
288     level_editor->layers[LAYER_PICKER_BACKGROUND] = background_layer_as_layer(&level_editor->background_layer);
289     level_editor->layers[LAYER_PICKER_LABELS] = label_layer_as_layer(level_editor->label_layer);
290
291     level_editor->drag = false;
292
293     level_editor->notice = (FadingWigglyText) {
294         .wiggly_text = {
295             .text = "Level saved",
296             .color = rgba(0.0f, 0.0f, 0.0f, 0.0f),
297             .scale = LEVEL_EDITOR_NOTICE_SCALE
298         },
299         .duration = LEVEL_EDITOR_NOTICE_DURATION,
300     };
301
302     level_editor->camera_scale = 1.0f;
303
304     return level_editor;
305 }
306
307 void destroy_level_editor(LevelEditor *level_editor)
308 {
309     trace_assert(level_editor);
310     destroy_undo_history(level_editor->undo_history);
311     RETURN_LT0(level_editor->lt);
312 }
313
314 int level_editor_render(const LevelEditor *level_editor,
315                         const Camera *camera)
316 {
317     trace_assert(level_editor);
318     trace_assert(camera);
319
320     if (camera_clear_background(camera, color_picker_rgba(&level_editor->background_layer.color_picker)) < 0) {
321         return -1;
322     }
323
324     for (size_t i = 0; i < LAYER_PICKER_N; ++i) {
325         if (layer_render(
326                 level_editor->layers[i],
327                 camera,
328                 i == level_editor->layer_picker) < 0) {
329             return -1;
330         }
331     }
332
333     if (layer_picker_render(&level_editor->layer_picker, camera) < 0) {
334         return -1;
335     }
336
337     if (level_editor->state == LEVEL_EDITOR_SAVEAS) {
338         /* CSS */
339         const Point size = LEVEL_EDITOR_EDIT_FIELD_SIZE;
340         const char *save_as_text = "Save as: ";
341         const Point position = vec(200.0f, 200.0f);
342         const float save_as_width =
343             (float) strlen(save_as_text) * FONT_CHAR_WIDTH * size.x;
344
345         /* HTML */
346         if (camera_render_text_screen(
347                 camera,
348                 save_as_text,
349                 LEVEL_EDITOR_EDIT_FIELD_SIZE,
350                 LEVEL_EDITOR_EDIT_FIELD_COLOR,
351                 position) < 0) {
352             return -1;
353         }
354
355         if (edit_field_render_screen(
356                 level_editor->edit_field_filename,
357                 camera,
358                 vec(position.x + save_as_width, position.y)) < 0) {
359             return -1;
360         }
361     }
362
363     const Rect viewport = camera_view_port_screen(camera);
364     const Vec text_size = fading_wiggly_text_size(
365         &level_editor->notice,
366         camera);
367
368     fading_wiggly_text_render(
369         &level_editor->notice, camera,
370         vec(viewport.w * 0.5f - text_size.x * 0.5f,
371             LEVEL_EDITOR_NOTICE_PADDING_TOP));
372
373     return 0;
374 }
375
376 static
377 int level_editor_saveas_event(LevelEditor *level_editor,
378                               const SDL_Event *event,
379                               const Camera *camera)
380 {
381     trace_assert(level_editor);
382     trace_assert(event);
383     trace_assert(camera);
384
385     switch (event->type) {
386     case SDL_KEYDOWN: {
387         if (event->key.keysym.sym == SDLK_RETURN) {
388             trace_assert(level_editor->file_name == NULL);
389             level_editor->file_name = PUSH_LT(
390                 level_editor->lt,
391                 string_duplicate(
392                     edit_field_as_text(
393                         level_editor->edit_field_filename),
394                     NULL),
395                 free);
396             level_editor_dump(level_editor);
397             SDL_StopTextInput();
398             level_editor->state = LEVEL_EDITOR_IDLE;
399             return 0;
400         }
401     } break;
402     }
403
404     return edit_field_event(level_editor->edit_field_filename, event);
405 }
406
407 static
408 int level_editor_idle_event(LevelEditor *level_editor,
409                             const SDL_Event *event,
410                             Camera *camera)
411 {
412     trace_assert(level_editor);
413     trace_assert(event);
414     trace_assert(camera);
415
416     switch (event->type) {
417     case SDL_KEYDOWN: {
418         switch(event->key.keysym.sym) {
419         case SDLK_s: {
420             if (!SDL_IsTextInputActive()) {
421                 if (level_editor->file_name) {
422                     level_editor_dump(level_editor);
423                     log_info("Saving level to `%s`\n", level_editor->file_name);
424                 } else {
425                     SDL_StartTextInput();
426                     level_editor->state = LEVEL_EDITOR_SAVEAS;
427                 }
428             }
429         } break;
430
431         case SDLK_z: {
432             if (event->key.keysym.mod & KMOD_CTRL) {
433                 log_info("Undo\n");
434                 undo_history_pop(&level_editor->undo_history);
435             }
436         } break;
437         }
438     } break;
439
440     case SDL_MOUSEWHEEL: {
441         int x, y;
442         SDL_GetMouseState(&x, &y);
443
444         Vec position = camera_map_screen(camera, x, y);
445         if (event->wheel.y > 0) {
446             level_editor->camera_scale += 0.1f;
447         } else if (event->wheel.y < 0) {
448             level_editor->camera_scale = fmaxf(0.1f, level_editor->camera_scale - 0.1f);
449         }
450         camera_scale(camera, level_editor->camera_scale);
451         Vec zoomed_position = camera_map_screen(camera, x, y);
452
453         level_editor->camera_position =
454             vec_sum(
455                 level_editor->camera_position,
456                 vec_sub(position, zoomed_position));
457         camera_center_at(camera, level_editor->camera_position);
458     } break;
459
460     case SDL_MOUSEBUTTONUP:
461     case SDL_MOUSEBUTTONDOWN: {
462         if (event->type == SDL_MOUSEBUTTONDOWN && event->button.button == SDL_BUTTON_MIDDLE) {
463             level_editor->drag = true;
464         }
465
466         if (event->type == SDL_MOUSEBUTTONUP && event->button.button == SDL_BUTTON_MIDDLE) {
467             level_editor->drag = false;
468         }
469     } break;
470
471     case SDL_MOUSEMOTION: {
472         if (level_editor->drag) {
473             const Vec next_position = camera_map_screen(camera, event->motion.x, event->motion.y);
474             const Vec prev_position = camera_map_screen(
475                 camera,
476                 event->motion.x + event->motion.xrel,
477                 event->motion.y + event->motion.yrel);
478
479             vec_add(&level_editor->camera_position,
480                     vec_sub(next_position, prev_position));
481             camera_center_at(camera, level_editor->camera_position);
482         }
483
484     } break;
485     }
486
487     bool selected = false;
488     if (layer_picker_event(
489             &level_editor->layer_picker,
490             event,
491             camera,
492             &selected) < 0) {
493         return -1;
494     }
495
496     if (!selected) {
497         if (layer_event(
498                 level_editor->layers[level_editor->layer_picker],
499                 event,
500                 camera,
501                 &level_editor->undo_history) < 0) {
502             return -1;
503         }
504     }
505
506
507     return 0;
508 }
509
510 int level_editor_event(LevelEditor *level_editor,
511                        const SDL_Event *event,
512                        Camera *camera)
513 {
514     trace_assert(level_editor);
515     trace_assert(event);
516     trace_assert(camera);
517
518     switch (level_editor->state) {
519     case LEVEL_EDITOR_IDLE:
520         return level_editor_idle_event(level_editor, event, camera);
521
522     case LEVEL_EDITOR_SAVEAS:
523         return level_editor_saveas_event(level_editor, event, camera);
524     }
525
526     return 0;
527 }
528
529 int level_editor_focus_camera(LevelEditor *level_editor,
530                               Camera *camera)
531 {
532     camera_center_at(camera, level_editor->camera_position);
533     camera_scale(camera, level_editor->camera_scale);
534     return 0;
535 }
536
537 /* TODO(#904): LevelEditor does not check that the saved level file is modified by external program */
538 static int level_editor_dump(LevelEditor *level_editor)
539 {
540     trace_assert(level_editor);
541
542     FILE *filedump = PUSH_LT(
543         level_editor->lt,
544         fopen(level_editor->file_name, "w"),
545         fclose_lt);
546
547     if (fprintf(filedump, "%s\n", level_metadata_title(level_editor->metadata)) < 0) {
548         return -1;
549     }
550
551     for (size_t i = 0; i < LAYER_PICKER_N; ++i) {
552         if (layer_dump_stream(
553                 level_editor->layers[i],
554                 filedump) < 0) {
555             return -1;
556         }
557     }
558
559     fprintf(filedump, "%s", level_editor->supa_script_source);
560
561     fclose(RELEASE_LT(level_editor->lt, filedump));
562
563     fading_wiggly_text_reset(&level_editor->notice);
564
565     return 0;
566 }
567
568 int level_editor_update(LevelEditor *level_editor, float delta_time)
569 {
570     return fading_wiggly_text_update(&level_editor->notice, delta_time);
571 }