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