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