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