3 #include "game/camera.h"
4 #include "game/level_metadata.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 "ui/edit_field.h"
12 #include "system/stacktrace.h"
13 #include "system/nth_alloc.h"
14 #include "system/lt.h"
15 #include "system/lt_adapters.h"
16 #include "system/log.h"
17 #include "system/str.h"
19 #include "level_editor.h"
21 #define LEVEL_LINE_MAX_LENGTH 512
22 #define LEVEL_EDITOR_EDIT_FIELD_SIZE vec(5.0f, 5.0f)
23 #define LEVEL_EDITOR_EDIT_FIELD_COLOR COLOR_BLACK
25 static int level_editor_dump(const LevelEditor *level_editor);
27 LevelEditor *create_level_editor(void)
30 LevelEditor *level_editor = PUSH_LT(
32 nth_calloc(1, sizeof(LevelEditor)),
34 if (level_editor == NULL) {
37 level_editor->lt = lt;
39 level_editor->edit_field_filename = PUSH_LT(
42 LEVEL_EDITOR_EDIT_FIELD_SIZE,
43 LEVEL_EDITOR_EDIT_FIELD_COLOR),
45 if (level_editor->edit_field_filename == NULL) {
49 level_editor->metadata = PUSH_LT(
51 create_level_metadata("New Level"),
52 destroy_level_metadata);
53 if (level_editor->metadata == NULL) {
57 level_editor->background_layer = create_color_picker_from_rgba(hexstr("fffda5"));
59 level_editor->player_layer =
60 create_player_layer(vec(0.0f, 0.0f), hexstr("ff8080"));
62 level_editor->platforms_layer = PUSH_LT(
66 if (level_editor->platforms_layer == NULL) {
70 level_editor->goals_layer = PUSH_LT(
74 if (level_editor->goals_layer == NULL) {
78 level_editor->lava_layer = PUSH_LT(
82 if (level_editor->lava_layer == NULL) {
86 level_editor->back_platforms_layer = PUSH_LT(
90 if (level_editor->back_platforms_layer == NULL) {
94 level_editor->boxes_layer = PUSH_LT(
98 if (level_editor->boxes_layer == NULL) {
102 level_editor->label_layer = PUSH_LT(
104 create_label_layer(),
105 destroy_label_layer);
106 if (level_editor->label_layer == NULL) {
110 level_editor->regions_layer = PUSH_LT(
114 if (level_editor->regions_layer == NULL) {
118 level_editor->supa_script_source =
119 PUSH_LT(lt, string_duplicate("", NULL), free);
121 level_editor->layers[LAYER_PICKER_BOXES] = rect_layer_as_layer(level_editor->boxes_layer);
122 level_editor->layers[LAYER_PICKER_PLATFORMS] = rect_layer_as_layer(level_editor->platforms_layer);
123 level_editor->layers[LAYER_PICKER_BACK_PLATFORMS] = rect_layer_as_layer(level_editor->back_platforms_layer);
124 level_editor->layers[LAYER_PICKER_GOALS] = point_layer_as_layer(level_editor->goals_layer);
125 level_editor->layers[LAYER_PICKER_PLAYER] = player_layer_as_layer(&level_editor->player_layer);
126 level_editor->layers[LAYER_PICKER_LAVA] = rect_layer_as_layer(level_editor->lava_layer);
127 level_editor->layers[LAYER_PICKER_REGIONS] = rect_layer_as_layer(level_editor->regions_layer);
128 level_editor->layers[LAYER_PICKER_BACKGROUND] = color_picker_as_layer(&level_editor->background_layer);
129 level_editor->layers[LAYER_PICKER_LABELS] = label_layer_as_layer(level_editor->label_layer);
131 level_editor->drag = false;
133 level_editor->notice = (FadingWigglyText) {
137 .scale = vec(10.0f, 10.0f)
145 LevelEditor *create_level_editor_from_file(const char *file_name)
147 trace_assert(file_name);
149 Lt *lt = create_lt();
150 LevelEditor *level_editor = PUSH_LT(
152 nth_calloc(1, sizeof(LevelEditor)),
154 if (level_editor == NULL) {
157 level_editor->lt = lt;
159 level_editor->edit_field_filename = PUSH_LT(
162 LEVEL_EDITOR_EDIT_FIELD_SIZE,
163 LEVEL_EDITOR_EDIT_FIELD_COLOR),
165 if (level_editor->edit_field_filename == NULL) {
169 level_editor->file_name =
172 string_duplicate(file_name, NULL),
175 LineStream *level_stream = PUSH_LT(
180 LEVEL_LINE_MAX_LENGTH),
181 destroy_line_stream);
182 if (level_stream == NULL) {
186 level_editor->metadata = PUSH_LT(
188 create_level_metadata_from_line_stream(level_stream),
189 destroy_level_metadata);
190 if (level_editor->metadata == NULL) {
194 if (color_picker_read_from_line_stream(
195 &level_editor->background_layer,
200 level_editor->player_layer =
201 create_player_layer_from_line_stream(level_stream);
203 level_editor->platforms_layer =
206 create_rect_layer_from_line_stream(level_stream),
208 if (level_editor->platforms_layer == NULL) {
212 level_editor->goals_layer = PUSH_LT(
214 create_point_layer_from_line_stream(level_stream),
215 destroy_point_layer);
216 if (level_editor->goals_layer == NULL) {
220 level_editor->lava_layer =
223 create_rect_layer_from_line_stream(level_stream),
225 if (level_editor->lava_layer == NULL) {
229 level_editor->back_platforms_layer =
232 create_rect_layer_from_line_stream(level_stream),
234 if (level_editor->back_platforms_layer == NULL) {
238 level_editor->boxes_layer =
241 create_rect_layer_from_line_stream(level_stream),
243 if (level_editor->boxes_layer == NULL) {
247 level_editor->label_layer =
250 create_label_layer_from_line_stream(level_stream),
251 destroy_label_layer);
252 if (level_editor->label_layer == NULL) {
256 level_editor->regions_layer =
259 create_rect_layer_from_line_stream(level_stream),
261 if (level_editor->regions_layer == NULL) {
265 level_editor->supa_script_source =
268 line_stream_collect_until_end(level_stream),
270 if (level_editor->supa_script_source == NULL) {
274 level_editor->layers[LAYER_PICKER_BOXES] = rect_layer_as_layer(level_editor->boxes_layer);
275 level_editor->layers[LAYER_PICKER_PLATFORMS] = rect_layer_as_layer(level_editor->platforms_layer);
276 level_editor->layers[LAYER_PICKER_BACK_PLATFORMS] = rect_layer_as_layer(level_editor->back_platforms_layer);
277 level_editor->layers[LAYER_PICKER_GOALS] = point_layer_as_layer(level_editor->goals_layer);
278 level_editor->layers[LAYER_PICKER_PLAYER] = player_layer_as_layer(&level_editor->player_layer);
279 level_editor->layers[LAYER_PICKER_LAVA] = rect_layer_as_layer(level_editor->lava_layer);
280 level_editor->layers[LAYER_PICKER_REGIONS] = rect_layer_as_layer(level_editor->regions_layer);
281 level_editor->layers[LAYER_PICKER_BACKGROUND] = color_picker_as_layer(&level_editor->background_layer);
282 level_editor->layers[LAYER_PICKER_LABELS] = label_layer_as_layer(level_editor->label_layer);
284 level_editor->drag = false;
286 level_editor->notice = (FadingWigglyText) {
290 .scale = vec(10.0f, 10.0f)
298 void destroy_level_editor(LevelEditor *level_editor)
300 trace_assert(level_editor);
301 RETURN_LT0(level_editor->lt);
304 int level_editor_render(const LevelEditor *level_editor,
307 trace_assert(level_editor);
308 trace_assert(camera);
310 if (camera_clear_background(camera, color_picker_rgba(&level_editor->background_layer)) < 0) {
314 for (size_t i = 0; i < LAYER_PICKER_N; ++i) {
316 level_editor->layers[i],
318 i == level_editor->layer_picker) < 0) {
323 if (layer_picker_render(&level_editor->layer_picker, camera) < 0) {
327 if (level_editor->state == LEVEL_EDITOR_SAVEAS) {
329 const Point size = LEVEL_EDITOR_EDIT_FIELD_SIZE;
330 const char *save_as_text = "Save as: ";
331 const Point position = vec(200.0f, 200.0f);
332 const float save_as_width =
333 (float) strlen(save_as_text) * FONT_CHAR_WIDTH * size.x;
336 if (camera_render_text_screen(
339 LEVEL_EDITOR_EDIT_FIELD_SIZE,
340 LEVEL_EDITOR_EDIT_FIELD_COLOR,
345 if (edit_field_render_screen(
346 level_editor->edit_field_filename,
348 vec(position.x + save_as_width, position.y)) < 0) {
353 fading_wiggly_text_render(&level_editor->notice, camera);
359 int level_editor_saveas_event(LevelEditor *level_editor,
360 const SDL_Event *event,
361 const Camera *camera)
363 trace_assert(level_editor);
365 trace_assert(camera);
367 switch (event->type) {
369 if (event->key.keysym.sym == SDLK_RETURN) {
370 trace_assert(level_editor->file_name == NULL);
371 level_editor->file_name = PUSH_LT(
375 level_editor->edit_field_filename),
378 level_editor_dump(level_editor);
380 level_editor->state = LEVEL_EDITOR_IDLE;
386 return edit_field_event(level_editor->edit_field_filename, event);
390 int level_editor_idle_event(LevelEditor *level_editor,
391 const SDL_Event *event,
392 const Camera *camera)
394 trace_assert(level_editor);
396 trace_assert(camera);
398 switch (event->type) {
400 switch(event-> key.keysym.sym) {
402 /* TODO(#903): There is no indication that the level is saved when you press S in Level Editor */
403 if (level_editor->file_name) {
404 level_editor_dump(level_editor);
405 log_info("Saving level to `%s`\n", level_editor->file_name);
407 SDL_StartTextInput();
408 level_editor->state = LEVEL_EDITOR_SAVEAS;
414 case SDL_MOUSEWHEEL: {
415 // TODO(#679): zooming in edit mode is not smooth enough
416 if (event->wheel.y > 0) {
417 level_editor->camera_scale += 0.1f;
418 } else if (event->wheel.y < 0) {
419 level_editor->camera_scale = fmaxf(0.1f, level_editor->camera_scale - 0.1f);
423 case SDL_MOUSEBUTTONUP:
424 case SDL_MOUSEBUTTONDOWN: {
425 if (event->type == SDL_MOUSEBUTTONDOWN && event->button.button == SDL_BUTTON_MIDDLE) {
426 level_editor->drag = true;
429 if (event->type == SDL_MOUSEBUTTONUP && event->button.button == SDL_BUTTON_MIDDLE) {
430 level_editor->drag = false;
434 case SDL_MOUSEMOTION: {
435 if (level_editor->drag) {
436 const Vec next_position = camera_map_screen(camera, event->motion.x, event->motion.y);
437 const Vec prev_position = camera_map_screen(
439 event->motion.x + event->motion.xrel,
440 event->motion.y + event->motion.yrel);
442 vec_add(&level_editor->camera_position,
443 vec_sub(next_position, prev_position));
449 bool selected = false;
450 if (layer_picker_event(
451 &level_editor->layer_picker,
460 level_editor->layers[level_editor->layer_picker],
471 int level_editor_event(LevelEditor *level_editor,
472 const SDL_Event *event,
473 const Camera *camera)
475 trace_assert(level_editor);
477 trace_assert(camera);
479 switch (event->type) {
480 case SDL_MOUSEBUTTONDOWN: {
481 level_editor->notice.wiggly_text.color.a = 1.0f;
485 switch (level_editor->state) {
486 case LEVEL_EDITOR_IDLE:
487 return level_editor_idle_event(level_editor, event, camera);
489 case LEVEL_EDITOR_SAVEAS:
490 return level_editor_saveas_event(level_editor, event, camera);
496 int level_editor_focus_camera(LevelEditor *level_editor,
499 camera_center_at(camera, level_editor->camera_position);
500 camera_scale(camera, level_editor->camera_scale);
504 /* TODO(#904): LevelEditor does not check that the saved level file is modified by external program */
505 static int level_editor_dump(const LevelEditor *level_editor)
507 trace_assert(level_editor);
509 FILE *filedump = PUSH_LT(
511 fopen(level_editor->file_name, "w"),
514 if (fprintf(filedump, "%s\n", level_metadata_title(level_editor->metadata)) < 0) {
518 for (size_t i = 0; i < LAYER_PICKER_N; ++i) {
519 if (layer_dump_stream(
520 level_editor->layers[i],
526 fprintf(filedump, "%s", level_editor->supa_script_source);
528 fclose(RELEASE_LT(level_editor->lt, filedump));
533 int level_editor_update(LevelEditor *level_editor, float delta_time)
535 return fading_wiggly_text_update(&level_editor->notice, delta_time);