]> git.lizzy.rs Git - nothing.git/blob - src/game/level.c
Add TODO(#809)
[nothing.git] / src / game / level.c
1 #include <SDL2/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 "system/line_stream.h"
22 #include "system/log.h"
23 #include "system/lt.h"
24 #include "system/lt/lt_adapters.h"
25 #include "system/nth_alloc.h"
26 #include "system/str.h"
27 #include "game/level/level_editor.h"
28
29 #define LEVEL_LINE_MAX_LENGTH 512
30 #define LEVEL_GRAVITY 1500.0f
31
32 struct Level
33 {
34     Lt *lt;
35
36     const char *file_name;
37     LevelMetadata *metadata;
38     Background *background;
39     RigidBodies *rigid_bodies;
40     Player *player;
41     Platforms *platforms;
42     Goals *goals;
43     Lava *lava;
44     Platforms *back_platforms;
45     Boxes *boxes;
46     Labels *labels;
47     Regions *regions;
48
49     bool edit_mode;
50     // TODO(#809): LevelEditor doesn't capture the initial state of the level loaded from a file
51     LevelEditor *level_editor;
52 };
53
54 Level *create_level_from_file(const char *file_name, Broadcast *broadcast)
55 {
56     trace_assert(file_name);
57
58     Lt *const lt = create_lt();
59     if (lt == NULL) {
60         return NULL;
61     }
62
63     Level *const level = PUSH_LT(lt, nth_alloc(sizeof(Level)), free);
64     if (level == NULL) {
65         RETURN_LT(lt, NULL);
66     }
67     level->lt = lt;
68
69     level->file_name = PUSH_LT(lt, string_duplicate(file_name, NULL), free);
70     if (level->file_name == NULL) {
71         RETURN_LT(lt, NULL);
72     }
73
74     LineStream *level_stream = PUSH_LT(
75         lt,
76         create_line_stream(
77             file_name,
78             "r",
79             LEVEL_LINE_MAX_LENGTH),
80         destroy_line_stream);
81     if (level_stream == NULL) {
82         RETURN_LT(lt, NULL);
83     }
84
85     level->metadata = PUSH_LT(
86         lt,
87         create_level_metadata_from_line_stream(level_stream),
88         destroy_level_metadata);
89     if (level->metadata == NULL) {
90         RETURN_LT(lt, NULL);
91     }
92
93     level->background = PUSH_LT(
94         lt,
95         create_background_from_line_stream(level_stream),
96         destroy_background);
97     if (level->background == NULL) {
98         RETURN_LT(lt, NULL);
99     }
100
101     level->rigid_bodies = PUSH_LT(lt, create_rigid_bodies(1024), destroy_rigid_bodies);
102     if (level->rigid_bodies == NULL) {
103         RETURN_LT(lt, NULL);
104     }
105
106     level->player = PUSH_LT(
107         lt,
108         create_player_from_line_stream(level_stream, level->rigid_bodies, broadcast),
109         destroy_player);
110     if (level->player == NULL) {
111         RETURN_LT(lt, NULL);
112     }
113
114     level->platforms = PUSH_LT(
115         lt,
116         create_platforms_from_line_stream(level_stream),
117         destroy_platforms);
118     if (level->platforms == NULL) {
119         RETURN_LT(lt, NULL);
120     }
121
122     level->goals = PUSH_LT(
123         lt,
124         create_goals_from_line_stream(level_stream),
125         destroy_goals);
126     if (level->goals == NULL) {
127         RETURN_LT(lt, NULL);
128     }
129
130     level->lava = PUSH_LT(
131         lt,
132         create_lava_from_line_stream(level_stream),
133         destroy_lava);
134     if (level->lava == NULL) {
135         RETURN_LT(lt, NULL);
136     }
137
138     level->back_platforms = PUSH_LT(
139         lt,
140         create_platforms_from_line_stream(level_stream),
141         destroy_platforms);
142     if (level->back_platforms == NULL) {
143         RETURN_LT(lt, NULL);
144     }
145
146     level->boxes = PUSH_LT(
147         lt,
148         create_boxes_from_line_stream(level_stream, level->rigid_bodies),
149         destroy_boxes);
150     if (level->boxes == NULL) {
151         RETURN_LT(lt, NULL);
152     }
153
154     level->labels = PUSH_LT(
155         lt,
156         create_labels_from_line_stream(level_stream),
157         destroy_labels);
158     if (level->labels == NULL) {
159         RETURN_LT(lt, NULL);
160     }
161
162     level->regions = PUSH_LT(
163         lt,
164         create_regions_from_line_stream(level_stream, broadcast),
165         destroy_regions);
166     if (level->regions == NULL) {
167         RETURN_LT(lt, NULL);
168     }
169
170     level->edit_mode = false;
171     level->level_editor = PUSH_LT(
172         lt,
173         create_level_editor(),
174         destroy_level_editor);
175     if (level->level_editor == NULL) {
176         RETURN_LT(lt, NULL);
177     }
178
179     destroy_line_stream(RELEASE_LT(lt, level_stream));
180
181     return level;
182 }
183
184 void destroy_level(Level *level)
185 {
186     trace_assert(level);
187     RETURN_LT0(level->lt);
188 }
189
190 int level_render(const Level *level, Camera *camera)
191 {
192     trace_assert(level);
193
194     if (background_render(level->background, camera) < 0) {
195         return -1;
196     }
197
198     if (platforms_render(level->back_platforms, camera) < 0) {
199         return -1;
200     }
201
202     if (player_render(level->player, camera) < 0) {
203         return -1;
204     }
205
206     if (boxes_render(level->boxes, camera) < 0) {
207         return -1;
208     }
209
210     if (lava_render(level->lava, camera) < 0) {
211         return -1;
212     }
213
214     if (platforms_render(level->platforms, camera) < 0) {
215         return -1;
216     }
217
218     if (goals_render(level->goals, camera) < 0) {
219         return -1;
220     }
221
222     if (labels_render(level->labels, camera) < 0) {
223         return -1;
224     }
225
226     if (regions_render(level->regions, camera) < 0) {
227         return -1;
228     }
229
230     if (level->edit_mode) {
231         if (level_editor_render(level->level_editor, camera) < 0) {
232             return -1;
233         }
234     }
235
236     return 0;
237 }
238
239 int level_update(Level *level, float delta_time)
240 {
241     trace_assert(level);
242     trace_assert(delta_time > 0);
243
244     boxes_float_in_lava(level->boxes, level->lava);
245     rigid_bodies_apply_omniforce(level->rigid_bodies, vec(0.0f, LEVEL_GRAVITY));
246
247     boxes_update(level->boxes, delta_time);
248     player_update(level->player, delta_time);
249
250     rigid_bodies_collide(level->rigid_bodies, level->platforms);
251
252     player_hide_goals(level->player, level->goals);
253     player_die_from_lava(level->player, level->lava);
254     regions_player_enter(level->regions, level->player);
255     regions_player_leave(level->regions, level->player);
256
257     goals_update(level->goals, delta_time);
258     lava_update(level->lava, delta_time);
259     labels_update(level->labels, delta_time);
260
261     if (level->edit_mode) {
262         level_editor_update(level->level_editor, delta_time);
263     }
264
265     return 0;
266 }
267
268 int level_event(Level *level, const SDL_Event *event, const Camera *camera)
269 {
270     trace_assert(level);
271     trace_assert(event);
272
273     switch (event->type) {
274     case SDL_KEYDOWN:
275         switch (event->key.keysym.sym) {
276         case SDLK_SPACE: {
277             player_jump(level->player);
278         } break;
279
280         case SDLK_TAB: {
281             level->edit_mode = !level->edit_mode;
282             SDL_SetRelativeMouseMode(level->edit_mode);
283             if (!level->edit_mode) {
284                 level->boxes = RESET_LT(
285                     level->lt,
286                     level->boxes,
287                     create_boxes_from_layer(
288                         level_editor_boxes(level->level_editor),
289                         level->rigid_bodies));
290                 if (level->boxes == NULL) {
291                     return -1;
292                 }
293             }
294         };
295         }
296         break;
297
298     case SDL_JOYBUTTONDOWN:
299         if (event->jbutton.button == 1) {
300             player_jump(level->player);
301         }
302         break;
303     }
304
305     if (level->edit_mode) {
306         level_editor_event(level->level_editor, event, camera);
307     }
308
309     return 0;
310 }
311
312 int level_input(Level *level,
313                 const Uint8 *const keyboard_state,
314                 SDL_Joystick *the_stick_of_joy)
315 {
316     trace_assert(level);
317     trace_assert(keyboard_state);
318     (void) the_stick_of_joy;
319
320     if (keyboard_state[SDL_SCANCODE_A]) {
321         player_move_left(level->player);
322     } else if (keyboard_state[SDL_SCANCODE_D]) {
323         player_move_right(level->player);
324     } else if (the_stick_of_joy && SDL_JoystickGetAxis(the_stick_of_joy, 0) < 0) {
325         player_move_left(level->player);
326     } else if (the_stick_of_joy && SDL_JoystickGetAxis(the_stick_of_joy, 0) > 0) {
327         player_move_right(level->player);
328     } else {
329         player_stop(level->player);
330     }
331
332     return 0;
333 }
334
335 int level_reload_preserve_player(Level *level, Broadcast *broadcast)
336 {
337     Lt * const lt = create_lt();
338     if (lt == NULL) {
339         return -1;
340     }
341
342     log_info("Soft-reloading the level from '%s'...\n", level->file_name);
343
344     /* TODO(#104): duplicate code in create_level_from_file and level_reload_preserve_player */
345
346     LineStream * const level_stream = PUSH_LT(
347         lt,
348         create_line_stream(
349             level->file_name,
350             "r",
351             LEVEL_LINE_MAX_LENGTH),
352         destroy_line_stream);
353     if (level_stream == NULL) {
354         RETURN_LT(lt, -1);
355     }
356
357     LevelMetadata *const metadata = create_level_metadata_from_line_stream(level_stream);
358     if (metadata == NULL) {
359         RETURN_LT(lt, -1);
360     }
361     level->metadata = RESET_LT(level->lt, level->metadata, metadata);
362
363     Background * const background = create_background_from_line_stream(level_stream);
364     if (background == NULL) {
365         RETURN_LT(lt, -1);
366     }
367     level->background = RESET_LT(level->lt, level->background, background);
368
369     Player * const skipped_player = create_player_from_line_stream(level_stream, level->rigid_bodies, broadcast);
370     if (skipped_player == NULL) {
371         RETURN_LT(lt, -1);
372     }
373     destroy_player(skipped_player);
374
375     Platforms * const platforms = create_platforms_from_line_stream(level_stream);
376     if (platforms == NULL) {
377         RETURN_LT(lt, -1);
378     }
379     level->platforms = RESET_LT(level->lt, level->platforms, platforms);
380
381     Goals * const goals = create_goals_from_line_stream(level_stream);
382     if (goals == NULL) {
383         RETURN_LT(lt, -1);
384     }
385     level->goals = RESET_LT(level->lt, level->goals, goals);
386
387     Lava * const lava = create_lava_from_line_stream(level_stream);
388     if (lava == NULL) {
389         RETURN_LT(lt, -1);
390     }
391     level->lava = RESET_LT(level->lt, level->lava, lava);
392
393     Platforms * const back_platforms = create_platforms_from_line_stream(level_stream);
394     if (back_platforms == NULL) {
395         RETURN_LT(lt, -1);
396     }
397     level->back_platforms = RESET_LT(level->lt, level->back_platforms, back_platforms);
398
399     Boxes * const boxes = create_boxes_from_line_stream(level_stream, level->rigid_bodies);
400     if (boxes == NULL) {
401         RETURN_LT(lt, -1);
402     }
403     level->boxes = RESET_LT(level->lt, level->boxes, boxes);
404
405     Labels * const labels = create_labels_from_line_stream(level_stream);
406     if (labels == NULL) {
407         RETURN_LT(lt, -1);
408     }
409     level->labels = RESET_LT(level->lt, level->labels, labels);
410
411     Regions * const regions = create_regions_from_line_stream(level_stream, broadcast);
412     if (regions == NULL) {
413         RETURN_LT(lt, -1);
414     }
415     level->regions = RESET_LT(level->lt, level->regions, regions);
416
417     RETURN_LT(lt, 0);
418 }
419
420 int level_sound(Level *level, Sound_samples *sound_samples)
421 {
422     if (goals_sound(level->goals, sound_samples) < 0) {
423         return -1;
424     }
425
426     if (player_sound(level->player, sound_samples) < 0) {
427         return -1;
428     }
429
430     return 0;
431 }
432
433 void level_toggle_debug_mode(Level *level)
434 {
435     background_toggle_debug_mode(level->background);
436 }
437
438 int level_enter_camera_event(Level *level, Camera *camera)
439 {
440     if (!level->edit_mode) {
441         player_focus_camera(level->player, camera);
442         camera_scale(camera, 1.0f);
443     } else {
444         level_editor_focus_camera(
445             level->level_editor,
446             camera);
447     }
448
449     goals_cue(level->goals, camera);
450     goals_checkpoint(level->goals, level->player);
451     labels_enter_camera_event(level->labels, camera);
452     return 0;
453 }
454
455 struct EvalResult level_send(Level *level, Gc *gc, struct Scope *scope, struct Expr path)
456 {
457     trace_assert(level);
458     trace_assert(gc);
459     trace_assert(scope);
460
461     const char *target = NULL;
462     struct Expr rest = void_expr();
463     struct EvalResult res = match_list(gc, "q*", path, &target, &rest);
464     if (res.is_error) {
465         return res;
466     }
467
468     if (strcmp(target, "goal") == 0) {
469         return goals_send(level->goals, gc, scope, rest);
470     } else if (strcmp(target, "label") == 0) {
471         return labels_send(level->labels, gc, scope, rest);
472     } else if (strcmp(target, "box") == 0) {
473         return boxes_send(level->boxes, gc, scope, rest);
474     } else if (strcmp(target, "body-push") == 0) {
475         long int id = 0, x = 0, y = 0;
476         res = match_list(gc, "ddd", rest, &id, &x, &y);
477         if (res.is_error) {
478             return res;
479         }
480
481         rigid_bodies_apply_force(level->rigid_bodies, (size_t) id, vec((float) x, (float) y));
482
483         return eval_success(NIL(gc));
484     } else if (strcmp(target, "edit") == 0) {
485         level->edit_mode = !level->edit_mode;
486         SDL_SetRelativeMouseMode(level->edit_mode);
487         return eval_success(NIL(gc));
488     }
489
490     return unknown_target(gc, "level", target);
491 }
492
493 bool level_edit_mode(const Level *level)
494 {
495     trace_assert(level);
496     return level->edit_mode;
497 }