]> git.lizzy.rs Git - nothing.git/blob - src/game/level.c
0a485c473f4c7c601f16bb2b8f1ea5ded23073f2
[nothing.git] / src / game / level.c
1 #include <SDL.h>
2 #include "system/stacktrace.h"
3
4 #include "broadcast.h"
5 #include "color.h"
6 #include "ebisp/builtins.h"
7 #include "ebisp/interpreter.h"
8 #include "game/camera.h"
9 #include "game/level.h"
10 #include "game/level/background.h"
11 #include "game/level/boxes.h"
12 #include "game/level/goals.h"
13 #include "game/level/labels.h"
14 #include "game/level/lava.h"
15 #include "game/level/platforms.h"
16 #include "game/level/player.h"
17 #include "game/level/regions.h"
18 #include "game/level/rigid_bodies.h"
19 #include "game/level_metadata.h"
20 #include "game/level/level_editor/proto_rect.h"
21 #include "game/level/level_editor/rect_layer.h"
22 #include "game/level/level_editor/point_layer.h"
23 #include "game/level/level_editor/player_layer.h"
24 #include "game/level/level_editor/label_layer.h"
25 #include "system/line_stream.h"
26 #include "system/log.h"
27 #include "system/lt.h"
28 #include "system/nth_alloc.h"
29 #include "system/str.h"
30 #include "game/level/level_editor.h"
31 #include "game/level/script.h"
32
33 #define LEVEL_LINE_MAX_LENGTH 512
34 #define LEVEL_GRAVITY 1500.0f
35
36 struct Level
37 {
38     Lt *lt;
39
40     const char *file_name;
41     // TODO(#887): LevelEditor does not support chaning the metadata of the Level
42     LevelMetadata *metadata;
43     // TODO(#812): LevelEditor does not support Background
44     Background *background;
45     RigidBodies *rigid_bodies;
46     Player *player;
47     Platforms *platforms;
48     Goals *goals;
49     Lava *lava;
50     Platforms *back_platforms;
51     Boxes *boxes;
52     // TODO(#818): LevelEditor does not support Labels
53     Labels *labels;
54     Regions *regions;
55     Broadcast *broadcast;
56     Script *supa_script;
57
58     bool edit_mode;
59     LevelEditor *level_editor;
60 };
61
62 Level *create_level_from_file(const char *file_name, Broadcast *broadcast)
63 {
64     trace_assert(file_name);
65
66     Lt *lt = create_lt();
67
68     Level *const level = PUSH_LT(lt, nth_calloc(1, sizeof(Level)), free);
69     if (level == NULL) {
70         RETURN_LT(lt, NULL);
71     }
72     level->lt = lt;
73
74     level->file_name = PUSH_LT(lt, string_duplicate(file_name, NULL), free);
75     if (level->file_name == NULL) {
76         RETURN_LT(lt, NULL);
77     }
78
79     LineStream *level_stream = PUSH_LT(
80         lt,
81         create_line_stream(
82             file_name,
83             "r",
84             LEVEL_LINE_MAX_LENGTH),
85         destroy_line_stream);
86     if (level_stream == NULL) {
87         RETURN_LT(lt, NULL);
88     }
89
90     level->metadata = PUSH_LT(
91         lt,
92         create_level_metadata_from_line_stream(level_stream),
93         destroy_level_metadata);
94     if (level->metadata == NULL) {
95         RETURN_LT(lt, NULL);
96     }
97
98     level->background = PUSH_LT(
99         lt,
100         create_background_from_line_stream(level_stream),
101         destroy_background);
102     if (level->background == NULL) {
103         RETURN_LT(lt, NULL);
104     }
105
106     level->rigid_bodies = PUSH_LT(lt, create_rigid_bodies(1024), destroy_rigid_bodies);
107     if (level->rigid_bodies == NULL) {
108         RETURN_LT(lt, NULL);
109     }
110
111     PlayerLayer *player_layer = create_player_layer_from_line_stream(level_stream);
112     if (player_layer == NULL) {
113         RETURN_LT(lt, NULL);
114     }
115
116     level->player = PUSH_LT(
117         lt,
118         create_player_from_player_layer(player_layer, level->rigid_bodies, broadcast),
119         destroy_player);
120     if (level->player == NULL) {
121         RETURN_LT(lt, NULL);
122     }
123
124     RectLayer *platforms_layer = create_rect_layer_from_line_stream(level_stream);
125     if (platforms_layer == NULL) {
126         RETURN_LT(lt, NULL);
127     }
128
129     level->platforms = PUSH_LT(
130         lt,
131         create_platforms_from_rect_layer(platforms_layer),
132         destroy_platforms);
133     if (level->platforms == NULL) {
134         RETURN_LT(lt, NULL);
135     }
136
137     PointLayer *goals_layer = create_point_layer_from_line_stream(level_stream);
138     if (goals_layer == NULL) {
139         RETURN_LT(lt, NULL);
140     }
141
142     level->goals = PUSH_LT(
143         lt,
144         create_goals_from_point_layer(goals_layer),
145         destroy_goals);
146     if (level->goals == NULL) {
147         RETURN_LT(lt, NULL);
148     }
149
150     RectLayer *lava_layer = create_rect_layer_from_line_stream(level_stream);
151     if (lava_layer == NULL) {
152         RETURN_LT(lt, NULL);
153     }
154
155     level->lava = PUSH_LT(
156         lt,
157         create_lava_from_rect_layer(lava_layer),
158         destroy_lava);
159     if (level->lava == NULL) {
160         RETURN_LT(lt, NULL);
161     }
162
163     RectLayer *back_platforms_layer = create_rect_layer_from_line_stream(level_stream);
164     if (back_platforms_layer == NULL) {
165         RETURN_LT(lt, NULL);
166     }
167
168     level->back_platforms = PUSH_LT(
169         lt,
170         create_platforms_from_rect_layer(back_platforms_layer),
171         destroy_platforms);
172     if (level->back_platforms == NULL) {
173         RETURN_LT(lt, NULL);
174     }
175
176     RectLayer *boxes_layer = create_rect_layer_from_line_stream(level_stream);
177     if (boxes_layer == NULL) {
178         RETURN_LT(lt, NULL);
179     }
180
181     level->boxes = PUSH_LT(
182         lt,
183         create_boxes_from_rect_layer(boxes_layer, level->rigid_bodies),
184         destroy_boxes);
185     if (level->boxes == NULL) {
186         RETURN_LT(lt, NULL);
187     }
188
189     LabelLayer *label_layer = create_label_layer_from_line_stream(level_stream);
190     if (label_layer == NULL) {
191         RETURN_LT(lt, NULL);
192     }
193
194     level->labels = PUSH_LT(
195         lt,
196         create_labels_from_label_layer(label_layer),
197         destroy_labels);
198     if (level->labels == NULL) {
199         RETURN_LT(lt, NULL);
200     }
201
202     RectLayer *regions_layer =
203         create_rect_layer_from_line_stream(level_stream);
204     if (regions_layer == NULL) {
205         RETURN_LT(lt, NULL);
206     }
207
208     level->regions = PUSH_LT(
209         lt,
210         create_regions_from_rect_layer(regions_layer),
211         destroy_regions);
212     if (level->regions == NULL) {
213         RETURN_LT(lt, NULL);
214     }
215
216     level->broadcast = broadcast;
217     level->supa_script = PUSH_LT(
218         lt,
219         create_script_from_line_stream(
220             level_stream,
221             broadcast),
222         destroy_script);
223     if (level->supa_script == NULL) {
224         log_fail("Could not construct Supa Script for the level\n");
225         RETURN_LT(lt, NULL);
226     }
227
228     level->edit_mode = false;
229     level->level_editor = PUSH_LT(
230         lt,
231         create_level_editor(
232             boxes_layer,
233             platforms_layer,
234             back_platforms_layer,
235             goals_layer,
236             player_layer,
237             lava_layer,
238             regions_layer,
239             background_base_color(level->background),
240             label_layer),
241         destroy_level_editor);
242     if (level->level_editor == NULL) {
243         RETURN_LT(lt, NULL);
244     }
245
246     destroy_line_stream(RELEASE_LT(lt, level_stream));
247
248     return level;
249 }
250
251 void destroy_level(Level *level)
252 {
253     trace_assert(level);
254     RETURN_LT0(level->lt);
255 }
256
257
258 int level_render(const Level *level, Camera *camera)
259 {
260     trace_assert(level);
261
262     if (level->edit_mode) {
263         if (level_editor_render(level->level_editor, camera) < 0) {
264             return -1;
265         }
266
267         return 0;
268     }
269
270     if (background_render(level->background, camera) < 0) {
271         return -1;
272     }
273
274     if (platforms_render(level->back_platforms, camera) < 0) {
275         return -1;
276     }
277
278     if (player_render(level->player, camera) < 0) {
279         return -1;
280     }
281
282     if (boxes_render(level->boxes, camera) < 0) {
283         return -1;
284     }
285
286     if (lava_render(level->lava, camera) < 0) {
287         return -1;
288     }
289
290     if (platforms_render(level->platforms, camera) < 0) {
291         return -1;
292     }
293
294     if (goals_render(level->goals, camera) < 0) {
295         return -1;
296     }
297
298     if (labels_render(level->labels, camera) < 0) {
299         return -1;
300     }
301
302     if (regions_render(level->regions, camera) < 0) {
303         return -1;
304     }
305
306     return 0;
307 }
308
309 int level_update(Level *level, float delta_time)
310 {
311     trace_assert(level);
312     trace_assert(delta_time > 0);
313
314     boxes_float_in_lava(level->boxes, level->lava);
315     rigid_bodies_apply_omniforce(level->rigid_bodies, vec(0.0f, LEVEL_GRAVITY));
316
317     boxes_update(level->boxes, delta_time);
318     player_update(level->player, delta_time);
319
320     rigid_bodies_collide(level->rigid_bodies, level->platforms);
321
322     player_hide_goals(level->player, level->goals);
323     player_die_from_lava(level->player, level->lava);
324     regions_player_enter(level->regions, level->player, level->supa_script);
325     regions_player_leave(level->regions, level->player, level->supa_script);
326
327     goals_update(level->goals, delta_time);
328     lava_update(level->lava, delta_time);
329     labels_update(level->labels, delta_time);
330
331     return 0;
332 }
333
334 int level_event(Level *level, const SDL_Event *event, const Camera *camera)
335 {
336     trace_assert(level);
337     trace_assert(event);
338
339     switch (event->type) {
340     case SDL_KEYDOWN:
341         switch (event->key.keysym.sym) {
342         case SDLK_SPACE: {
343             player_jump(level->player, level->supa_script);
344         } break;
345
346         case SDLK_TAB: {
347             level->edit_mode = !level->edit_mode;
348             SDL_SetRelativeMouseMode(level->edit_mode);
349             if (!level->edit_mode) {
350                 level->boxes = RESET_LT(
351                     level->lt,
352                     level->boxes,
353                     create_boxes_from_rect_layer(
354                         level_editor_boxes(level->level_editor),
355                         level->rigid_bodies));
356                 if (level->boxes == NULL) {
357                     return -1;
358                 }
359
360                 level->platforms = RESET_LT(
361                     level->lt,
362                     level->platforms,
363                     create_platforms_from_rect_layer(
364                         level_editor_platforms(
365                             level->level_editor)));
366                 if (level->platforms == NULL) {
367                     return -1;
368                 }
369
370                 level->back_platforms = RESET_LT(
371                     level->lt,
372                     level->back_platforms,
373                     create_platforms_from_rect_layer(
374                         level_editor_back_platforms(
375                             level->level_editor)));
376                 if (level->back_platforms == NULL) {
377                     return -1;
378                 }
379
380                 level->goals = RESET_LT(
381                     level->lt,
382                     level->goals,
383                     create_goals_from_point_layer(
384                         level_editor_goals_layer(
385                             level->level_editor)));
386                 if (level->goals == NULL) {
387                     return -1;
388                 }
389
390                 level->player = RESET_LT(
391                     level->lt,
392                     level->player,
393                     create_player_from_player_layer(
394                         level_editor_player_layer(
395                             level->level_editor),
396                         level->rigid_bodies,
397                         level->broadcast));
398                 if (level->player == NULL) {
399                     return -1;
400                 }
401
402                 level->lava = RESET_LT(
403                     level->lt,
404                     level->lava,
405                     create_lava_from_rect_layer(
406                         level_editor_lava_layer(
407                             level->level_editor)));
408                 if (level->lava == NULL) {
409                     return -1;
410                 }
411
412                 level->regions = RESET_LT(
413                     level->lt,
414                     level->regions,
415                     create_regions_from_rect_layer(
416                         level_editor_regions_layer(
417                             level->level_editor)));
418                 if (level->regions == NULL) {
419                     return -1;
420                 }
421
422                 level->background = RESET_LT(
423                     level->lt,
424                     level->background,
425                     create_background(
426                         level_editor_background_color(
427                             level->level_editor)));
428                 if (level->background == NULL) {
429                     return -1;
430                 }
431
432                 level->labels = RESET_LT(
433                     level->lt,
434                     level->labels,
435                     create_labels_from_label_layer(
436                         level_editor_label_layer(
437                             level->level_editor)));
438                 if (level->labels == NULL) {
439                     return -1;
440                 }
441             }
442         };
443         }
444         break;
445
446     case SDL_JOYBUTTONDOWN:
447         if (event->jbutton.button == 1) {
448             player_jump(level->player, level->supa_script);
449         }
450         break;
451     }
452
453     if (level->edit_mode) {
454         level_editor_event(level->level_editor, event, camera);
455     }
456
457     return 0;
458 }
459
460 int level_input(Level *level,
461                 const Uint8 *const keyboard_state,
462                 SDL_Joystick *the_stick_of_joy)
463 {
464     trace_assert(level);
465     trace_assert(keyboard_state);
466     (void) the_stick_of_joy;
467
468     if (keyboard_state[SDL_SCANCODE_A]) {
469         player_move_left(level->player);
470     } else if (keyboard_state[SDL_SCANCODE_D]) {
471         player_move_right(level->player);
472     } else if (the_stick_of_joy && SDL_JoystickGetAxis(the_stick_of_joy, 0) < 0) {
473         player_move_left(level->player);
474     } else if (the_stick_of_joy && SDL_JoystickGetAxis(the_stick_of_joy, 0) > 0) {
475         player_move_right(level->player);
476     } else {
477         player_stop(level->player);
478     }
479
480     return 0;
481 }
482
483 int level_sound(Level *level, Sound_samples *sound_samples)
484 {
485     if (goals_sound(level->goals, sound_samples) < 0) {
486         return -1;
487     }
488
489     if (player_sound(level->player, sound_samples) < 0) {
490         return -1;
491     }
492
493     return 0;
494 }
495
496 void level_toggle_debug_mode(Level *level)
497 {
498     background_toggle_debug_mode(level->background);
499 }
500
501 int level_enter_camera_event(Level *level, Camera *camera)
502 {
503     if (!level->edit_mode) {
504         player_focus_camera(level->player, camera);
505         camera_scale(camera, 1.0f);
506     } else {
507         level_editor_focus_camera(
508             level->level_editor,
509             camera);
510     }
511
512     goals_cue(level->goals, camera);
513     goals_checkpoint(level->goals, level->player);
514     labels_enter_camera_event(level->labels, camera);
515     return 0;
516 }
517
518 struct EvalResult level_send(Level *level, Gc *gc, struct Scope *scope, struct Expr path)
519 {
520     trace_assert(level);
521     trace_assert(gc);
522     trace_assert(scope);
523
524     const char *target = NULL;
525     struct Expr rest = void_expr();
526     struct EvalResult res = match_list(gc, "q*", path, &target, &rest);
527     if (res.is_error) {
528         return res;
529     }
530
531     if (strcmp(target, "goal") == 0) {
532         return goals_send(level->goals, gc, scope, rest);
533     } else if (strcmp(target, "label") == 0) {
534         return labels_send(level->labels, gc, scope, rest);
535     } else if (strcmp(target, "box") == 0) {
536         return boxes_send(level->boxes, gc, scope, rest);
537     } else if (strcmp(target, "body-push") == 0) {
538         long int id = 0, x = 0, y = 0;
539         res = match_list(gc, "ddd", rest, &id, &x, &y);
540         if (res.is_error) {
541             return res;
542         }
543
544         rigid_bodies_apply_force(level->rigid_bodies, (size_t) id, vec((float) x, (float) y));
545
546         return eval_success(NIL(gc));
547     } else if (strcmp(target, "edit") == 0) {
548         level->edit_mode = !level->edit_mode;
549         SDL_SetRelativeMouseMode(level->edit_mode);
550         return eval_success(NIL(gc));
551     }
552
553     return unknown_target(gc, "level", target);
554 }
555
556 bool level_edit_mode(const Level *level)
557 {
558     trace_assert(level);
559     return level->edit_mode;
560 }