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