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