]> git.lizzy.rs Git - nothing.git/blob - src/game/level.c
(#814) TODOs
[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(
196             boxes_layer,
197             platforms_layer,
198             back_platforms_layer),
199         destroy_level_editor);
200     if (level->level_editor == NULL) {
201         RETURN_LT(lt, NULL);
202     }
203
204     destroy_line_stream(RELEASE_LT(lt, level_stream));
205
206     return level;
207 }
208
209 void destroy_level(Level *level)
210 {
211     trace_assert(level);
212     RETURN_LT0(level->lt);
213 }
214
215 // TODO: Level rendering obscures LevelEditor rendering
216
217 int level_render(const Level *level, Camera *camera)
218 {
219     trace_assert(level);
220
221     if (background_render(level->background, camera) < 0) {
222         return -1;
223     }
224
225     if (platforms_render(level->back_platforms, camera) < 0) {
226         return -1;
227     }
228
229     if (player_render(level->player, camera) < 0) {
230         return -1;
231     }
232
233     if (boxes_render(level->boxes, camera) < 0) {
234         return -1;
235     }
236
237     if (lava_render(level->lava, camera) < 0) {
238         return -1;
239     }
240
241     if (platforms_render(level->platforms, camera) < 0) {
242         return -1;
243     }
244
245     if (goals_render(level->goals, camera) < 0) {
246         return -1;
247     }
248
249     if (labels_render(level->labels, camera) < 0) {
250         return -1;
251     }
252
253     if (regions_render(level->regions, camera) < 0) {
254         return -1;
255     }
256
257     if (level->edit_mode) {
258         if (level_editor_render(level->level_editor, camera) < 0) {
259             return -1;
260         }
261     }
262
263     return 0;
264 }
265
266 int level_update(Level *level, float delta_time)
267 {
268     trace_assert(level);
269     trace_assert(delta_time > 0);
270
271     boxes_float_in_lava(level->boxes, level->lava);
272     rigid_bodies_apply_omniforce(level->rigid_bodies, vec(0.0f, LEVEL_GRAVITY));
273
274     boxes_update(level->boxes, delta_time);
275     player_update(level->player, delta_time);
276
277     rigid_bodies_collide(level->rigid_bodies, level->platforms);
278
279     player_hide_goals(level->player, level->goals);
280     player_die_from_lava(level->player, level->lava);
281     regions_player_enter(level->regions, level->player);
282     regions_player_leave(level->regions, level->player);
283
284     goals_update(level->goals, delta_time);
285     lava_update(level->lava, delta_time);
286     labels_update(level->labels, delta_time);
287
288     if (level->edit_mode) {
289         level_editor_update(level->level_editor, delta_time);
290     }
291
292     return 0;
293 }
294
295 int level_event(Level *level, const SDL_Event *event, const Camera *camera)
296 {
297     trace_assert(level);
298     trace_assert(event);
299
300     switch (event->type) {
301     case SDL_KEYDOWN:
302         switch (event->key.keysym.sym) {
303         case SDLK_SPACE: {
304             player_jump(level->player);
305         } break;
306
307         case SDLK_TAB: {
308             level->edit_mode = !level->edit_mode;
309             SDL_SetRelativeMouseMode(level->edit_mode);
310             if (!level->edit_mode) {
311                 level->boxes = RESET_LT(
312                     level->lt,
313                     level->boxes,
314                     create_boxes_from_layer(
315                         level_editor_boxes(level->level_editor),
316                         level->rigid_bodies));
317                 if (level->boxes == NULL) {
318                     return -1;
319                 }
320
321                 level->platforms = RESET_LT(
322                     level->lt,
323                     level->platforms,
324                     create_platforms_from_layer(
325                         level_editor_platforms(
326                             level->level_editor)));
327                 if (level->platforms == NULL) {
328                     return -1;
329                 }
330             }
331         };
332         }
333         break;
334
335     case SDL_JOYBUTTONDOWN:
336         if (event->jbutton.button == 1) {
337             player_jump(level->player);
338         }
339         break;
340     }
341
342     if (level->edit_mode) {
343         level_editor_event(level->level_editor, event, camera);
344     }
345
346     return 0;
347 }
348
349 int level_input(Level *level,
350                 const Uint8 *const keyboard_state,
351                 SDL_Joystick *the_stick_of_joy)
352 {
353     trace_assert(level);
354     trace_assert(keyboard_state);
355     (void) the_stick_of_joy;
356
357     if (keyboard_state[SDL_SCANCODE_A]) {
358         player_move_left(level->player);
359     } else if (keyboard_state[SDL_SCANCODE_D]) {
360         player_move_right(level->player);
361     } else if (the_stick_of_joy && SDL_JoystickGetAxis(the_stick_of_joy, 0) < 0) {
362         player_move_left(level->player);
363     } else if (the_stick_of_joy && SDL_JoystickGetAxis(the_stick_of_joy, 0) > 0) {
364         player_move_right(level->player);
365     } else {
366         player_stop(level->player);
367     }
368
369     return 0;
370 }
371
372 int level_sound(Level *level, Sound_samples *sound_samples)
373 {
374     if (goals_sound(level->goals, sound_samples) < 0) {
375         return -1;
376     }
377
378     if (player_sound(level->player, sound_samples) < 0) {
379         return -1;
380     }
381
382     return 0;
383 }
384
385 void level_toggle_debug_mode(Level *level)
386 {
387     background_toggle_debug_mode(level->background);
388 }
389
390 int level_enter_camera_event(Level *level, Camera *camera)
391 {
392     if (!level->edit_mode) {
393         player_focus_camera(level->player, camera);
394         camera_scale(camera, 1.0f);
395     } else {
396         level_editor_focus_camera(
397             level->level_editor,
398             camera);
399     }
400
401     goals_cue(level->goals, camera);
402     goals_checkpoint(level->goals, level->player);
403     labels_enter_camera_event(level->labels, camera);
404     return 0;
405 }
406
407 struct EvalResult level_send(Level *level, Gc *gc, struct Scope *scope, struct Expr path)
408 {
409     trace_assert(level);
410     trace_assert(gc);
411     trace_assert(scope);
412
413     const char *target = NULL;
414     struct Expr rest = void_expr();
415     struct EvalResult res = match_list(gc, "q*", path, &target, &rest);
416     if (res.is_error) {
417         return res;
418     }
419
420     if (strcmp(target, "goal") == 0) {
421         return goals_send(level->goals, gc, scope, rest);
422     } else if (strcmp(target, "label") == 0) {
423         return labels_send(level->labels, gc, scope, rest);
424     } else if (strcmp(target, "box") == 0) {
425         return boxes_send(level->boxes, gc, scope, rest);
426     } else if (strcmp(target, "body-push") == 0) {
427         long int id = 0, x = 0, y = 0;
428         res = match_list(gc, "ddd", rest, &id, &x, &y);
429         if (res.is_error) {
430             return res;
431         }
432
433         rigid_bodies_apply_force(level->rigid_bodies, (size_t) id, vec((float) x, (float) y));
434
435         return eval_success(NIL(gc));
436     } else if (strcmp(target, "edit") == 0) {
437         level->edit_mode = !level->edit_mode;
438         SDL_SetRelativeMouseMode(level->edit_mode);
439         return eval_success(NIL(gc));
440     }
441
442     return unknown_target(gc, "level", target);
443 }
444
445 bool level_edit_mode(const Level *level)
446 {
447     trace_assert(level);
448     return level->edit_mode;
449 }