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