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