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