]> git.lizzy.rs Git - nothing.git/blob - src/game/level.c
Merge pull request #622 from tsoding/616
[nothing.git] / src / game / level.c
1 #include <SDL2/SDL.h>
2 #include "system/stacktrace.h"
3
4 #include "color.h"
5 #include "game/camera.h"
6 #include "game/level.h"
7 #include "game/level/background.h"
8 #include "game/level/boxes.h"
9 #include "game/level/goals.h"
10 #include "game/level/labels.h"
11 #include "game/level/lava.h"
12 #include "game/level/physical_world.h"
13 #include "game/level/platforms.h"
14 #include "game/level/player.h"
15 #include "game/level/regions.h"
16 #include "system/line_stream.h"
17 #include "system/lt.h"
18 #include "system/lt/lt_adapters.h"
19 #include "system/nth_alloc.h"
20 #include "ebisp/interpreter.h"
21 #include "ebisp/builtins.h"
22 #include "broadcast.h"
23
24 #define LEVEL_LINE_MAX_LENGTH 512
25
26 struct Level
27 {
28     Lt *lt;
29
30     Physical_world *physical_world;
31     Player *player;
32     Platforms *platforms;
33     Goals *goals;
34     Lava *lava;
35     Platforms *back_platforms;
36     Background *background;
37     Boxes *boxes;
38     Labels *labels;
39     Regions *regions;
40 };
41
42 Level *create_level_from_file(const char *file_name, Broadcast *broadcast)
43 {
44     trace_assert(file_name);
45
46     Lt *const lt = create_lt();
47     if (lt == NULL) {
48         return NULL;
49     }
50
51     Level *const level = PUSH_LT(lt, nth_alloc(sizeof(Level)), free);
52     if (level == NULL) {
53         RETURN_LT(lt, NULL);
54     }
55
56     LineStream *level_stream = PUSH_LT(
57         lt,
58         create_line_stream(
59             file_name,
60             "r",
61             LEVEL_LINE_MAX_LENGTH),
62         destroy_line_stream);
63     if (level_stream == NULL) {
64         RETURN_LT(lt, NULL);
65     }
66     level->background = PUSH_LT(
67         lt,
68         create_background_from_line_stream(level_stream),
69         destroy_background);
70     if (level->background == NULL) {
71         RETURN_LT(lt, NULL);
72     }
73
74     level->player = PUSH_LT(
75         lt,
76         create_player_from_line_stream(level_stream, broadcast),
77         destroy_player);
78     if (level->player == NULL) {
79         RETURN_LT(lt, NULL);
80     }
81
82     level->platforms = PUSH_LT(
83         lt,
84         create_platforms_from_line_stream(level_stream),
85         destroy_platforms);
86     if (level->platforms == NULL) {
87         RETURN_LT(lt, NULL);
88     }
89
90     level->goals = PUSH_LT(
91         lt,
92         create_goals_from_line_stream(level_stream),
93         destroy_goals);
94     if (level->goals == NULL) {
95         RETURN_LT(lt, NULL);
96     }
97
98     level->lava = PUSH_LT(
99         lt,
100         create_lava_from_line_stream(level_stream),
101         destroy_lava);
102     if (level->lava == NULL) {
103         RETURN_LT(lt, NULL);
104     }
105
106     level->back_platforms = PUSH_LT(
107         lt,
108         create_platforms_from_line_stream(level_stream),
109         destroy_platforms);
110     if (level->back_platforms == NULL) {
111         RETURN_LT(lt, NULL);
112     }
113
114     level->boxes = PUSH_LT(
115         lt,
116         create_boxes_from_line_stream(level_stream),
117         destroy_boxes);
118     if (level->boxes == NULL) {
119         RETURN_LT(lt, NULL);
120     }
121
122     level->labels = PUSH_LT(
123         lt,
124         create_labels_from_line_stream(level_stream),
125         destroy_labels);
126     if (level->labels == NULL) {
127         RETURN_LT(lt, NULL);
128     }
129
130     level->regions = PUSH_LT(
131         lt,
132         create_regions_from_line_stream(level_stream, broadcast),
133         destroy_regions);
134     if (level->regions == NULL) {
135         RETURN_LT(lt, NULL);
136     }
137
138     level->physical_world = PUSH_LT(lt, create_physical_world(), destroy_physical_world);
139     if (level->physical_world == NULL) {
140         RETURN_LT(lt, NULL);
141     }
142     if (physical_world_add_solid(
143             level->physical_world,
144             player_as_solid(level->player)) < 0) { RETURN_LT(lt, NULL); }
145     if (boxes_add_to_physical_world(
146             level->boxes,
147             level->physical_world) < 0) { RETURN_LT(lt, NULL); }
148
149     level->lt = lt;
150
151     destroy_line_stream(RELEASE_LT(lt, level_stream));
152
153     return level;
154 }
155
156 void destroy_level(Level *level)
157 {
158     trace_assert(level);
159     RETURN_LT0(level->lt);
160 }
161
162 int level_render(const Level *level, Camera *camera)
163 {
164     trace_assert(level);
165
166     if (background_render(level->background, camera) < 0) {
167         return -1;
168     }
169
170     if (platforms_render(level->back_platforms, camera) < 0) {
171         return -1;
172     }
173
174     if (player_render(level->player, camera) < 0) {
175         return -1;
176     }
177
178     if (boxes_render(level->boxes, camera) < 0) {
179         return -1;
180     }
181
182     if (lava_render(level->lava, camera) < 0) {
183         return -1;
184     }
185
186     if (platforms_render(level->platforms, camera) < 0) {
187         return -1;
188     }
189
190     if (goals_render(level->goals, camera) < 0) {
191         return -1;
192     }
193
194     if (labels_render(level->labels, camera) < 0) {
195         return -1;
196     }
197
198     if (regions_render(level->regions, camera) < 0) {
199         return -1;
200     }
201
202     return 0;
203 }
204
205 int level_update(Level *level, float delta_time)
206 {
207     trace_assert(level);
208     trace_assert(delta_time > 0);
209
210     physical_world_apply_gravity(level->physical_world);
211     boxes_float_in_lava(level->boxes, level->lava);
212
213     boxes_update(level->boxes, delta_time);
214     player_update(level->player, delta_time);
215
216     physical_world_collide_solids(level->physical_world, level->platforms);
217
218     player_hide_goals(level->player, level->goals);
219     player_die_from_lava(level->player, level->lava);
220     regions_player_enter(level->regions, level->player);
221     regions_player_leave(level->regions, level->player);
222
223     goals_update(level->goals, delta_time);
224     lava_update(level->lava, delta_time);
225     labels_update(level->labels, delta_time);
226
227     return 0;
228 }
229
230 int level_event(Level *level, const SDL_Event *event)
231 {
232     trace_assert(level);
233     trace_assert(event);
234
235     switch (event->type) {
236     case SDL_KEYDOWN:
237         switch (event->key.keysym.sym) {
238         case SDLK_SPACE:
239             player_jump(level->player);
240             break;
241         }
242         break;
243
244     case SDL_JOYBUTTONDOWN:
245         if (event->jbutton.button == 1) {
246             player_jump(level->player);
247         }
248         break;
249     }
250
251     return 0;
252 }
253
254 int level_input(Level *level,
255                 const Uint8 *const keyboard_state,
256                 SDL_Joystick *the_stick_of_joy)
257 {
258     trace_assert(level);
259     trace_assert(keyboard_state);
260     (void) the_stick_of_joy;
261
262     if (keyboard_state[SDL_SCANCODE_A]) {
263         player_move_left(level->player);
264     } else if (keyboard_state[SDL_SCANCODE_D]) {
265         player_move_right(level->player);
266     } else if (the_stick_of_joy && SDL_JoystickGetAxis(the_stick_of_joy, 0) < 0) {
267         player_move_left(level->player);
268     } else if (the_stick_of_joy && SDL_JoystickGetAxis(the_stick_of_joy, 0) > 0) {
269         player_move_right(level->player);
270     } else {
271         player_stop(level->player);
272     }
273
274     return 0;
275 }
276
277 int level_reload_preserve_player(Level *level, const char *file_name, Broadcast *broadcast)
278 {
279     Lt * const lt = create_lt();
280     if (lt == NULL) {
281         return -1;
282     }
283
284     /* TODO(#104): duplicate code in create_level_from_file and level_reload_preserve_player */
285
286     LineStream * const level_stream = PUSH_LT(
287         lt,
288         create_line_stream(
289             file_name,
290             "r",
291             LEVEL_LINE_MAX_LENGTH),
292         destroy_line_stream);
293     if (level_stream == NULL) {
294         RETURN_LT(lt, -1);
295     }
296
297     Background * const background = create_background_from_line_stream(level_stream);
298     if (background == NULL) {
299         RETURN_LT(lt, -1);
300     }
301     level->background = RESET_LT(level->lt, level->background, background);
302
303     Player * const skipped_player = create_player_from_line_stream(level_stream, broadcast);
304     if (skipped_player == NULL) {
305         RETURN_LT(lt, -1);
306     }
307     destroy_player(skipped_player);
308
309     Platforms * const platforms = create_platforms_from_line_stream(level_stream);
310     if (platforms == NULL) {
311         RETURN_LT(lt, -1);
312     }
313     level->platforms = RESET_LT(level->lt, level->platforms, platforms);
314
315     Goals * const goals = create_goals_from_line_stream(level_stream);
316     if (goals == NULL) {
317         RETURN_LT(lt, -1);
318     }
319     level->goals = RESET_LT(level->lt, level->goals, goals);
320
321     Lava * const lava = create_lava_from_line_stream(level_stream);
322     if (lava == NULL) {
323         RETURN_LT(lt, -1);
324     }
325     level->lava = RESET_LT(level->lt, level->lava, lava);
326
327     Platforms * const back_platforms = create_platforms_from_line_stream(level_stream);
328     if (back_platforms == NULL) {
329         RETURN_LT(lt, -1);
330     }
331     level->back_platforms = RESET_LT(level->lt, level->back_platforms, back_platforms);
332
333     Boxes * const boxes = create_boxes_from_line_stream(level_stream);
334     if (boxes == NULL) {
335         RETURN_LT(lt, -1);
336     }
337     level->boxes = RESET_LT(level->lt, level->boxes, boxes);
338
339     Labels * const labels = create_labels_from_line_stream(level_stream);
340     if (labels == NULL) {
341         RETURN_LT(lt, -1);
342     }
343     level->labels = RESET_LT(level->lt, level->labels, labels);
344
345     Regions * const regions = create_regions_from_line_stream(level_stream, broadcast);
346     if (regions == NULL) {
347         RETURN_LT(lt, -1);
348     }
349     level->regions = RESET_LT(level->lt, level->regions, regions);
350
351     physical_world_clean(level->physical_world);
352     if (physical_world_add_solid(
353             level->physical_world,
354             player_as_solid(level->player)) < 0) { RETURN_LT(lt, -1); }
355     if (boxes_add_to_physical_world(
356             level->boxes,
357             level->physical_world) < 0) { RETURN_LT(lt, -1); }
358
359     RETURN_LT(lt, 0);
360 }
361
362 int level_sound(Level *level, Sound_samples *sound_samples)
363 {
364     if (goals_sound(level->goals, sound_samples) < 0) {
365         return -1;
366     }
367
368     if (player_sound(level->player, sound_samples) < 0) {
369         return -1;
370     }
371
372     return 0;
373 }
374
375 void level_toggle_debug_mode(Level *level)
376 {
377     background_toggle_debug_mode(level->background);
378 }
379
380 int level_enter_camera_event(Level *level, Camera *camera)
381 {
382     player_focus_camera(level->player, camera);
383     goals_cue(level->goals, camera);
384     goals_checkpoint(level->goals, level->player);
385     labels_enter_camera_event(level->labels, camera);
386     return 0;
387 }
388
389 Rigid_rect *level_rigid_rect(Level *level,
390                              const char *rigid_rect_id)
391 {
392     trace_assert(level);
393     trace_assert(rigid_rect_id);
394
395     Rigid_rect *rigid_rect = player_rigid_rect(level->player,
396                                                rigid_rect_id);
397     if (rigid_rect != NULL) {
398         return rigid_rect;
399     }
400
401     rigid_rect = boxes_rigid_rect(level->boxes, rigid_rect_id);
402     if (rigid_rect != NULL) {
403         return rigid_rect;
404     }
405
406     return NULL;
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     }
427
428     return unknown_target(gc, "level", target);
429 }