]> git.lizzy.rs Git - nothing.git/blob - src/game/level/level_editor.c
(#1108) handle user input
[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     action_picker_render(
378         &level_editor->action_picker,
379         camera);
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             level_editor->file_name = PUSH_LT(
398                 level_editor->lt,
399                 string_duplicate(
400                     edit_field_as_text(
401                         level_editor->edit_field_filename),
402                     NULL),
403                 free);
404             level_editor_dump(level_editor);
405             SDL_StopTextInput();
406             level_editor->state = LEVEL_EDITOR_IDLE;
407             return 0;
408         }
409     } break;
410     }
411
412     return edit_field_event(level_editor->edit_field_filename, event);
413 }
414
415 static
416 int level_editor_idle_event(LevelEditor *level_editor,
417                             const SDL_Event *event,
418                             Camera *camera)
419 {
420     trace_assert(level_editor);
421     trace_assert(event);
422     trace_assert(camera);
423
424     switch (event->type) {
425     case SDL_KEYDOWN: {
426         switch(event->key.keysym.sym) {
427         case SDLK_s: {
428             if (!SDL_IsTextInputActive()) {
429                 if (level_editor->file_name) {
430                     level_editor_dump(level_editor);
431                     log_info("Saving level to `%s`\n", level_editor->file_name);
432                 } else {
433                     SDL_StartTextInput();
434                     level_editor->state = LEVEL_EDITOR_SAVEAS;
435                 }
436             }
437         } break;
438
439         case SDLK_z: {
440             if (event->key.keysym.mod & KMOD_CTRL) {
441                 if (undo_history_empty(&level_editor->undo_history)) {
442                     level_editor->bell = 1;
443                 }
444                 undo_history_pop(&level_editor->undo_history);
445             }
446         } break;
447         }
448     } break;
449
450     case SDL_MOUSEWHEEL: {
451         int x, y;
452         SDL_GetMouseState(&x, &y);
453
454         Vec2f position = camera_map_screen(camera, x, y);
455         if (event->wheel.y > 0) {
456             level_editor->camera_scale += 0.1f;
457         } else if (event->wheel.y < 0) {
458             level_editor->camera_scale = fmaxf(0.1f, level_editor->camera_scale - 0.1f);
459         }
460         camera_scale(camera, level_editor->camera_scale);
461         Vec2f zoomed_position = camera_map_screen(camera, x, y);
462
463         level_editor->camera_position =
464             vec_sum(
465                 level_editor->camera_position,
466                 vec_sub(position, zoomed_position));
467         camera_center_at(camera, level_editor->camera_position);
468     } break;
469
470     case SDL_MOUSEBUTTONUP:
471     case SDL_MOUSEBUTTONDOWN: {
472         if (event->type == SDL_MOUSEBUTTONDOWN && event->button.button == SDL_BUTTON_MIDDLE) {
473             level_editor->drag = true;
474         }
475
476         if (event->type == SDL_MOUSEBUTTONUP && event->button.button == SDL_BUTTON_MIDDLE) {
477             level_editor->drag = false;
478         }
479     } break;
480
481     case SDL_MOUSEMOTION: {
482         if (level_editor->drag) {
483             const Vec2f next_position = camera_map_screen(camera, event->motion.x, event->motion.y);
484             const Vec2f prev_position = camera_map_screen(
485                 camera,
486                 event->motion.x + event->motion.xrel,
487                 event->motion.y + event->motion.yrel);
488
489             vec_add(&level_editor->camera_position,
490                     vec_sub(next_position, prev_position));
491             camera_center_at(camera, level_editor->camera_position);
492         }
493
494     } break;
495     }
496
497     bool selected = false;
498     if (layer_picker_event(
499             &level_editor->layer_picker,
500             event,
501             camera,
502             &selected) < 0) {
503         return -1;
504     }
505
506     if (!selected) {
507         if (layer_event(
508                 level_editor->layers[level_editor->layer_picker],
509                 event,
510                 camera,
511                 &level_editor->undo_history) < 0) {
512             return -1;
513         }
514     } else {
515         level_editor->click = 1;
516     }
517
518
519     return 0;
520 }
521
522 int level_editor_event(LevelEditor *level_editor,
523                        const SDL_Event *event,
524                        Camera *camera)
525 {
526     trace_assert(level_editor);
527     trace_assert(event);
528     trace_assert(camera);
529
530     level_editor->action_picker.position = vec(400.0f, 200.0f);
531     action_picker_event(&level_editor->action_picker, event);
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", level_metadata_title(level_editor->metadata)) < 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 }