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