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