]> git.lizzy.rs Git - nothing.git/blob - src/game/level.c
(#814) Remove soft-reloading feature
[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_sound(Level *level, Sound_samples *sound_samples)
350 {
351     if (goals_sound(level->goals, sound_samples) < 0) {
352         return -1;
353     }
354
355     if (player_sound(level->player, sound_samples) < 0) {
356         return -1;
357     }
358
359     return 0;
360 }
361
362 void level_toggle_debug_mode(Level *level)
363 {
364     background_toggle_debug_mode(level->background);
365 }
366
367 int level_enter_camera_event(Level *level, Camera *camera)
368 {
369     if (!level->edit_mode) {
370         player_focus_camera(level->player, camera);
371         camera_scale(camera, 1.0f);
372     } else {
373         level_editor_focus_camera(
374             level->level_editor,
375             camera);
376     }
377
378     goals_cue(level->goals, camera);
379     goals_checkpoint(level->goals, level->player);
380     labels_enter_camera_event(level->labels, camera);
381     return 0;
382 }
383
384 struct EvalResult level_send(Level *level, Gc *gc, struct Scope *scope, struct Expr path)
385 {
386     trace_assert(level);
387     trace_assert(gc);
388     trace_assert(scope);
389
390     const char *target = NULL;
391     struct Expr rest = void_expr();
392     struct EvalResult res = match_list(gc, "q*", path, &target, &rest);
393     if (res.is_error) {
394         return res;
395     }
396
397     if (strcmp(target, "goal") == 0) {
398         return goals_send(level->goals, gc, scope, rest);
399     } else if (strcmp(target, "label") == 0) {
400         return labels_send(level->labels, gc, scope, rest);
401     } else if (strcmp(target, "box") == 0) {
402         return boxes_send(level->boxes, gc, scope, rest);
403     } else if (strcmp(target, "body-push") == 0) {
404         long int id = 0, x = 0, y = 0;
405         res = match_list(gc, "ddd", rest, &id, &x, &y);
406         if (res.is_error) {
407             return res;
408         }
409
410         rigid_bodies_apply_force(level->rigid_bodies, (size_t) id, vec((float) x, (float) y));
411
412         return eval_success(NIL(gc));
413     } else if (strcmp(target, "edit") == 0) {
414         level->edit_mode = !level->edit_mode;
415         SDL_SetRelativeMouseMode(level->edit_mode);
416         return eval_success(NIL(gc));
417     }
418
419     return unknown_target(gc, "level", target);
420 }
421
422 bool level_edit_mode(const Level *level)
423 {
424     trace_assert(level);
425     return level->edit_mode;
426 }