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