]> git.lizzy.rs Git - nothing.git/blob - src/game/level/level_editor.c
Wire up pp to level loop
[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     rect_layer_load(level_editor->pp_layer, memory, &input);
117     undo_history_clean(level_editor->undo_history);
118
119     return level_editor;
120 }
121
122 int level_editor_render(const LevelEditor *level_editor,
123                         const Camera *camera)
124 {
125     trace_assert(level_editor);
126     trace_assert(camera);
127
128     if (camera_clear_background(camera, color_picker_rgba(&level_editor->background_layer.color_picker)) < 0) {
129         return -1;
130     }
131
132     const Rect world_viewport = camera_view_port(camera);
133
134     if (PLAYER_DEATH_LEVEL < world_viewport.y + world_viewport.h) {
135         if (camera_fill_rect(
136                 camera,
137                 rect(
138                     world_viewport.x, PLAYER_DEATH_LEVEL,
139                     world_viewport.w, world_viewport.h + fmaxf(0.0f, world_viewport.y - PLAYER_DEATH_LEVEL)),
140                 LEVEL_EDITOR_DETH_LEVEL_COLOR) < 0) {
141             return -1;
142         }
143     }
144
145     for (size_t i = 0; i < LAYER_PICKER_N; ++i) {
146         if (layer_render(
147                 level_editor->layers[i],
148                 camera,
149                 i == level_editor->layer_picker) < 0) {
150             return -1;
151         }
152     }
153
154     if (layer_picker_render(&level_editor->layer_picker, camera) < 0) {
155         return -1;
156     }
157
158     if (level_editor->state == LEVEL_EDITOR_SAVEAS) {
159         /* CSS */
160         const Vec2f size = LEVEL_EDITOR_EDIT_FIELD_SIZE;
161         const char *save_as_text = "Save as: ";
162         const Vec2f position = vec(200.0f, 200.0f);
163         const float save_as_width =
164             (float) strlen(save_as_text) * FONT_CHAR_WIDTH * size.x;
165
166         /* HTML */
167         camera_render_text_screen(
168             camera,
169             save_as_text,
170             LEVEL_EDITOR_EDIT_FIELD_SIZE,
171             LEVEL_EDITOR_EDIT_FIELD_COLOR,
172             position);
173
174         if (edit_field_render_screen(
175                 &level_editor->edit_field_filename,
176                 camera,
177                 vec(position.x + save_as_width, position.y)) < 0) {
178             return -1;
179         }
180     }
181
182     const Rect screen_viewport = camera_view_port_screen(camera);
183     const Vec2f text_size = fading_wiggly_text_size(&level_editor->notice);
184
185     fading_wiggly_text_render(
186         &level_editor->notice, camera,
187         vec(screen_viewport.w * 0.5f - text_size.x * 0.5f,
188             LEVEL_EDITOR_NOTICE_PADDING_TOP));
189
190     return 0;
191 }
192
193 static
194 int level_editor_saveas_event(LevelEditor *level_editor,
195                               const SDL_Event *event,
196                               const Camera *camera,
197                               Memory *memory)
198 {
199     trace_assert(level_editor);
200     trace_assert(event);
201     trace_assert(camera);
202
203     switch (event->type) {
204     case SDL_KEYDOWN: {
205         if (event->key.keysym.sym == SDLK_RETURN) {
206             trace_assert(level_editor->file_name == NULL);
207             char path[LEVEL_FOLDER_MAX_LENGTH];
208             snprintf(
209                 path,
210                 LEVEL_FOLDER_MAX_LENGTH,
211                 "./assets/levels/%s.txt",
212                 edit_field_as_text(&level_editor->edit_field_filename));
213             level_editor->file_name = strdup_to_memory(memory, path);
214             level_editor_dump(level_editor);
215             SDL_StopTextInput();
216             level_editor->state = LEVEL_EDITOR_IDLE;
217             return 0;
218         }
219     } break;
220     }
221
222     return edit_field_event(&level_editor->edit_field_filename, event);
223 }
224
225 static
226 int level_editor_idle_event(LevelEditor *level_editor,
227                             const SDL_Event *event,
228                             Camera *camera)
229 {
230     trace_assert(level_editor);
231     trace_assert(event);
232     trace_assert(camera);
233
234     switch (event->type) {
235     case SDL_KEYDOWN: {
236         switch(event->key.keysym.sym) {
237         case SDLK_s: {
238             if (!SDL_IsTextInputActive()) {
239                 if (level_editor->file_name) {
240                     level_editor_dump(level_editor);
241                     log_info("Saving level to `%s`\n", level_editor->file_name);
242                 } else {
243                     SDL_StartTextInput();
244                     level_editor->state = LEVEL_EDITOR_SAVEAS;
245                 }
246             }
247         } break;
248
249         case SDLK_z: {
250             if (event->key.keysym.mod & KMOD_CTRL) {
251                 if (undo_history_empty(level_editor->undo_history)) {
252                     level_editor->bell = 1;
253                 }
254                 undo_history_pop(level_editor->undo_history);
255             }
256         } break;
257         }
258     } break;
259
260     case SDL_MOUSEWHEEL: {
261         int x, y;
262         SDL_GetMouseState(&x, &y);
263
264         Vec2f position = camera_map_screen(camera, x, y);
265         if (event->wheel.y > 0) {
266             level_editor->camera_scale += 0.1f;
267         } else if (event->wheel.y < 0) {
268             level_editor->camera_scale = fmaxf(0.1f, level_editor->camera_scale - 0.1f);
269         }
270         camera_scale(camera, level_editor->camera_scale);
271         Vec2f zoomed_position = camera_map_screen(camera, x, y);
272
273         level_editor->camera_position =
274             vec_sum(
275                 level_editor->camera_position,
276                 vec_sub(position, zoomed_position));
277         camera_center_at(camera, level_editor->camera_position);
278     } break;
279
280     case SDL_MOUSEBUTTONUP:
281     case SDL_MOUSEBUTTONDOWN: {
282         if (event->type == SDL_MOUSEBUTTONDOWN && event->button.button == SDL_BUTTON_MIDDLE) {
283             level_editor->drag = true;
284         }
285
286         if (event->type == SDL_MOUSEBUTTONUP && event->button.button == SDL_BUTTON_MIDDLE) {
287             level_editor->drag = false;
288         }
289     } break;
290
291     case SDL_MOUSEMOTION: {
292         if (level_editor->drag) {
293             const Vec2f next_position = camera_map_screen(camera, event->motion.x, event->motion.y);
294             const Vec2f prev_position = camera_map_screen(
295                 camera,
296                 event->motion.x + event->motion.xrel,
297                 event->motion.y + event->motion.yrel);
298
299             vec_add(&level_editor->camera_position,
300                     vec_sub(next_position, prev_position));
301             camera_center_at(camera, level_editor->camera_position);
302         }
303
304     } break;
305     }
306
307     bool selected = false;
308     if (layer_picker_event(
309             &level_editor->layer_picker,
310             event,
311             camera,
312             &selected) < 0) {
313         return -1;
314     }
315
316     if (!selected) {
317         if (layer_event(
318                 level_editor->layers[level_editor->layer_picker],
319                 event,
320                 camera,
321                 level_editor->undo_history) < 0) {
322             return -1;
323         }
324     } else {
325         level_editor->click = 1;
326     }
327
328
329     return 0;
330 }
331
332 int level_editor_event(LevelEditor *level_editor,
333                        const SDL_Event *event,
334                        Camera *camera,
335                        Memory *memory)
336 {
337     trace_assert(level_editor);
338     trace_assert(event);
339     trace_assert(camera);
340
341     switch (level_editor->state) {
342     case LEVEL_EDITOR_IDLE:
343         return level_editor_idle_event(level_editor, event, camera);
344
345     case LEVEL_EDITOR_SAVEAS:
346         return level_editor_saveas_event(level_editor, event, camera, memory);
347     }
348
349     return 0;
350 }
351
352 int level_editor_focus_camera(LevelEditor *level_editor,
353                               Camera *camera)
354 {
355     camera_center_at(camera, level_editor->camera_position);
356     camera_scale(camera, level_editor->camera_scale);
357     return 0;
358 }
359
360 static LayerPicker level_format_layer_order[LAYER_PICKER_N] = {
361     LAYER_PICKER_BACKGROUND,
362     LAYER_PICKER_PLAYER,
363     LAYER_PICKER_PLATFORMS,
364     LAYER_PICKER_GOALS,
365     LAYER_PICKER_LAVA,
366     LAYER_PICKER_BACK_PLATFORMS,
367     LAYER_PICKER_BOXES,
368     LAYER_PICKER_LABELS,
369     LAYER_PICKER_REGIONS,
370     LAYER_PICKER_PP
371 };
372
373 /* TODO(#904): LevelEditor does not check that the saved level file is modified by external program */
374 static int level_editor_dump(LevelEditor *level_editor)
375 {
376     trace_assert(level_editor);
377
378     FILE *filedump = fopen(level_editor->file_name, "w");
379     trace_assert(filedump);
380
381     if (fprintf(filedump, "%s\n", VERSION) < 0) {
382         return -1;
383     }
384
385     for (size_t i = 0; i < LAYER_PICKER_N; ++i) {
386         if (layer_dump_stream(
387                 level_editor->layers[level_format_layer_order[i]],
388                 filedump) < 0) {
389             return -1;
390         }
391     }
392
393     fclose(filedump);
394
395     fading_wiggly_text_reset(&level_editor->notice);
396     level_editor->save = 1;
397
398     return 0;
399 }
400
401 int level_editor_update(LevelEditor *level_editor, float delta_time)
402 {
403     return fading_wiggly_text_update(&level_editor->notice, delta_time);
404 }
405
406 void level_editor_sound(LevelEditor *level_editor, Sound_samples *sound_samples)
407 {
408     trace_assert(sound_samples);
409
410     if (level_editor) {
411         if (level_editor->bell) {
412             level_editor->bell = 0;
413             sound_samples_play_sound(sound_samples, 2);
414         }
415
416         if (level_editor->click) {
417             level_editor->click = 0;
418             sound_samples_play_sound(sound_samples, 3);
419         }
420
421         if (level_editor->save) {
422             level_editor->save = 0;
423             sound_samples_play_sound(sound_samples, 4);
424         }
425     }
426 }