]> git.lizzy.rs Git - nothing.git/blob - src/game/level.c
(#647) Commands for applying force to rigid bodies
[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_with_itself(level->rigid_bodies);
241     rigid_bodies_collide_with_platforms(level->rigid_bodies, level->platforms);
242     physical_world_collide_solids(level->physical_world, level->platforms);
243
244     player_hide_goals(level->player, level->goals);
245     player_die_from_lava(level->player, level->lava);
246     regions_player_enter(level->regions, level->player);
247     regions_player_leave(level->regions, level->player);
248
249     goals_update(level->goals, delta_time);
250     lava_update(level->lava, delta_time);
251     labels_update(level->labels, delta_time);
252
253     return 0;
254 }
255
256 int level_event(Level *level, const SDL_Event *event)
257 {
258     trace_assert(level);
259     trace_assert(event);
260
261     switch (event->type) {
262     case SDL_KEYDOWN:
263         switch (event->key.keysym.sym) {
264         case SDLK_SPACE:
265             player_jump(level->player);
266             break;
267         }
268         break;
269
270     case SDL_JOYBUTTONDOWN:
271         if (event->jbutton.button == 1) {
272             player_jump(level->player);
273         }
274         break;
275     }
276
277     return 0;
278 }
279
280 int level_input(Level *level,
281                 const Uint8 *const keyboard_state,
282                 SDL_Joystick *the_stick_of_joy)
283 {
284     trace_assert(level);
285     trace_assert(keyboard_state);
286     (void) the_stick_of_joy;
287
288     if (keyboard_state[SDL_SCANCODE_A]) {
289         player_move_left(level->player);
290     } else if (keyboard_state[SDL_SCANCODE_D]) {
291         player_move_right(level->player);
292     } else if (the_stick_of_joy && SDL_JoystickGetAxis(the_stick_of_joy, 0) < 0) {
293         player_move_left(level->player);
294     } else if (the_stick_of_joy && SDL_JoystickGetAxis(the_stick_of_joy, 0) > 0) {
295         player_move_right(level->player);
296     } else {
297         player_stop(level->player);
298     }
299
300     return 0;
301 }
302
303 int level_reload_preserve_player(Level *level, const char *file_name, Broadcast *broadcast)
304 {
305     Lt * const lt = create_lt();
306     if (lt == NULL) {
307         return -1;
308     }
309
310     /* TODO(#104): duplicate code in create_level_from_file and level_reload_preserve_player */
311
312     LineStream * const level_stream = PUSH_LT(
313         lt,
314         create_line_stream(
315             file_name,
316             "r",
317             LEVEL_LINE_MAX_LENGTH),
318         destroy_line_stream);
319     if (level_stream == NULL) {
320         RETURN_LT(lt, -1);
321     }
322
323     Background * const background = create_background_from_line_stream(level_stream);
324     if (background == NULL) {
325         RETURN_LT(lt, -1);
326     }
327     level->background = RESET_LT(level->lt, level->background, background);
328
329     Player * const skipped_player = create_player_from_line_stream(level_stream, broadcast);
330     if (skipped_player == NULL) {
331         RETURN_LT(lt, -1);
332     }
333     destroy_player(skipped_player);
334
335     Platforms * const platforms = create_platforms_from_line_stream(level_stream);
336     if (platforms == NULL) {
337         RETURN_LT(lt, -1);
338     }
339     level->platforms = RESET_LT(level->lt, level->platforms, platforms);
340
341     Goals * const goals = create_goals_from_line_stream(level_stream);
342     if (goals == NULL) {
343         RETURN_LT(lt, -1);
344     }
345     level->goals = RESET_LT(level->lt, level->goals, goals);
346
347     Lava * const lava = create_lava_from_line_stream(level_stream);
348     if (lava == NULL) {
349         RETURN_LT(lt, -1);
350     }
351     level->lava = RESET_LT(level->lt, level->lava, lava);
352
353     Platforms * const back_platforms = create_platforms_from_line_stream(level_stream);
354     if (back_platforms == NULL) {
355         RETURN_LT(lt, -1);
356     }
357     level->back_platforms = RESET_LT(level->lt, level->back_platforms, back_platforms);
358
359     Boxes * const boxes = create_boxes_from_line_stream(level_stream);
360     if (boxes == NULL) {
361         RETURN_LT(lt, -1);
362     }
363     level->boxes = RESET_LT(level->lt, level->boxes, boxes);
364
365     Labels * const labels = create_labels_from_line_stream(level_stream);
366     if (labels == NULL) {
367         RETURN_LT(lt, -1);
368     }
369     level->labels = RESET_LT(level->lt, level->labels, labels);
370
371     Regions * const regions = create_regions_from_line_stream(level_stream, broadcast);
372     if (regions == NULL) {
373         RETURN_LT(lt, -1);
374     }
375     level->regions = RESET_LT(level->lt, level->regions, regions);
376
377     physical_world_clean(level->physical_world);
378     if (physical_world_add_solid(
379             level->physical_world,
380             player_as_solid(level->player)) < 0) { RETURN_LT(lt, -1); }
381     if (boxes_add_to_physical_world(
382             level->boxes,
383             level->physical_world) < 0) { RETURN_LT(lt, -1); }
384
385     RETURN_LT(lt, 0);
386 }
387
388 int level_sound(Level *level, Sound_samples *sound_samples)
389 {
390     if (goals_sound(level->goals, sound_samples) < 0) {
391         return -1;
392     }
393
394     if (player_sound(level->player, sound_samples) < 0) {
395         return -1;
396     }
397
398     return 0;
399 }
400
401 void level_toggle_debug_mode(Level *level)
402 {
403     background_toggle_debug_mode(level->background);
404 }
405
406 int level_enter_camera_event(Level *level, Camera *camera)
407 {
408     player_focus_camera(level->player, camera);
409     goals_cue(level->goals, camera);
410     goals_checkpoint(level->goals, level->player);
411     labels_enter_camera_event(level->labels, camera);
412     return 0;
413 }
414
415 Rigid_rect *level_rigid_rect(Level *level,
416                              const char *rigid_rect_id)
417 {
418     trace_assert(level);
419     trace_assert(rigid_rect_id);
420
421     Rigid_rect *rigid_rect = player_rigid_rect(level->player,
422                                                rigid_rect_id);
423     if (rigid_rect != NULL) {
424         return rigid_rect;
425     }
426
427     rigid_rect = boxes_rigid_rect(level->boxes, rigid_rect_id);
428     if (rigid_rect != NULL) {
429         return rigid_rect;
430     }
431
432     return NULL;
433 }
434
435 struct EvalResult level_send(Level *level, Gc *gc, struct Scope *scope, struct Expr path)
436 {
437     trace_assert(level);
438     trace_assert(gc);
439     trace_assert(scope);
440
441     const char *target = NULL;
442     struct Expr rest = void_expr();
443     struct EvalResult res = match_list(gc, "q*", path, &target, &rest);
444     if (res.is_error) {
445         return res;
446     }
447
448     if (strcmp(target, "goal") == 0) {
449         return goals_send(level->goals, gc, scope, rest);
450     } else if (strcmp(target, "label") == 0) {
451         return labels_send(level->labels, gc, scope, rest);
452     } else if (strcmp(target, "box") == 0) {
453         return boxes_send(level->boxes, gc, scope, rest);
454     } else if (strcmp(target, "body") == 0) {
455         long int id = 0, x = 0, y = 0;
456         res = match_list(gc, "ddd", rest, &id, &x, &y);
457         if (res.is_error) {
458             return res;
459         }
460
461         rigid_bodies_apply_force(level->rigid_bodies, (size_t) id, vec((float) x, (float) y));
462         return eval_success(NIL(gc));
463     }
464
465     return unknown_target(gc, "level", target);
466 }