]> git.lizzy.rs Git - nothing.git/blob - src/game/level.c
Move camera to game/level "subpackage"
[nothing.git] / src / game / level.c
1 #include <assert.h>
2 #include <SDL2/SDL.h>
3
4 #include "./lt.h"
5 #include "./game/level/player.h"
6 #include "./game/level/platforms.h"
7 #include "./game/level.h"
8 #include "./game/level/camera.h"
9 #include "./error.h"
10 #include "./goals.h"
11 #include "./lava.h"
12
13 /* TODO(#118): Level doesn't play the Nothing and Something sounds when the goal get into view */
14
15 struct level_t
16 {
17     lt_t *lt;
18     player_t *player;
19     platforms_t *platforms;
20     camera_t *camera;
21     goals_t *goals;
22     lava_t *lava;
23     color_t background_color;
24 };
25
26 level_t *create_level_from_file(const char *file_name)
27 {
28     assert(file_name);
29
30     lt_t *const lt = create_lt();
31     if (lt == NULL) {
32         return NULL;
33     }
34
35     level_t *const level = PUSH_LT(lt, malloc(sizeof(level_t)), free);
36     if (level == NULL) {
37         throw_error(ERROR_TYPE_LIBC);
38         RETURN_LT(lt, NULL);
39     }
40
41     FILE *level_file = PUSH_LT(lt, fopen(file_name, "r"), fclose);
42     if (level_file == NULL) {
43         throw_error(ERROR_TYPE_LIBC);
44         RETURN_LT(lt, NULL);
45     }
46
47     char color[7];
48     if (fscanf(level_file, "%6s", color) == EOF) {
49         throw_error(ERROR_TYPE_LIBC);
50         RETURN_LT(lt, NULL);
51     }
52     level->background_color = color_from_hexstr(color);
53
54     level->player = PUSH_LT(lt, create_player_from_stream(level_file), destroy_player);
55     if (level->player == NULL) {
56         RETURN_LT(lt, NULL);
57     }
58
59     level->platforms = PUSH_LT(lt, create_platforms_from_stream(level_file), destroy_platforms);
60     if (level->platforms == NULL) {
61         RETURN_LT(lt, NULL);
62     }
63
64     level->goals = PUSH_LT(lt, create_goals_from_stream(level_file), destroy_goals);
65     if (level->goals == NULL) {
66         RETURN_LT(lt, NULL);
67     }
68
69     level->lava = PUSH_LT(lt, create_lava_from_stream(level_file), destroy_lava);
70     if (level->lava == NULL) {
71         RETURN_LT(lt, NULL);
72     }
73
74     level->camera = PUSH_LT(lt, create_camera(vec(0.0f, 0.0f)), destroy_camera);
75     if (level->camera == NULL) {
76         RETURN_LT(lt, NULL);
77     }
78
79     level->lt = lt;
80
81     fclose(RELEASE_LT(lt, level_file));
82
83     return level;
84 }
85
86 void destroy_level(level_t *level)
87 {
88     assert(level);
89     RETURN_LT0(level->lt);
90 }
91
92 int level_render(const level_t *level, SDL_Renderer *renderer)
93 {
94     assert(level);
95     assert(renderer);
96
97     if (camera_clear_background(level->camera, renderer, level->background_color) < 0) {
98         return -1;
99     }
100
101     if (player_render(level->player, renderer, level->camera) < 0) {
102         return -1;
103     }
104
105     if (lava_render(level->lava, renderer, level->camera) < 0) {
106         return -1;
107     }
108
109     if (platforms_render(level->platforms, renderer, level->camera) < 0) {
110         return -1;
111     }
112
113     if (goals_render(level->goals, renderer, level->camera) < 0) {
114         return -1;
115     }
116
117     /* TODO(#157): goals_cue is not supposed to be invoked in level_render
118      *
119      * But I simply couldn't find a better place for it.
120      */
121     goals_cue(level->goals, renderer, level->camera);
122
123     return 0;
124 }
125
126 int level_update(level_t *level, float delta_time)
127 {
128     assert(level);
129     assert(delta_time > 0);
130
131     player_update(level->player, level->platforms, delta_time);
132     player_focus_camera(level->player, level->camera);
133     player_hide_goals(level->player, level->goals);
134     player_die_from_lava(level->player, level->lava);
135
136     goals_update(level->goals, delta_time);
137     goals_checkpoint(level->goals, level->player);
138     lava_update(level->lava, delta_time);
139
140     return 0;
141 }
142
143 int level_event(level_t *level, const SDL_Event *event)
144 {
145     assert(level);
146     assert(event);
147
148     switch (event->type) {
149     case SDL_KEYDOWN:
150         switch (event->key.keysym.sym) {
151         case SDLK_SPACE:
152             player_jump(level->player);
153             break;
154         }
155         break;
156
157     case SDL_JOYBUTTONDOWN:
158         if (event->jbutton.button == 1) {
159             player_jump(level->player);
160         }
161         break;
162     }
163
164     return 0;
165 }
166
167 int level_input(level_t *level,
168                 const Uint8 *const keyboard_state,
169                 SDL_Joystick *the_stick_of_joy)
170 {
171     assert(level);
172     assert(keyboard_state);
173     (void) the_stick_of_joy;
174
175     if (keyboard_state[SDL_SCANCODE_A]) {
176         player_move_left(level->player);
177     } else if (keyboard_state[SDL_SCANCODE_D]) {
178         player_move_right(level->player);
179     } else if (the_stick_of_joy && SDL_JoystickGetAxis(the_stick_of_joy, 0) < 0) {
180         player_move_left(level->player);
181     } else if (the_stick_of_joy && SDL_JoystickGetAxis(the_stick_of_joy, 0) > 0) {
182         player_move_right(level->player);
183     } else {
184         player_stop(level->player);
185     }
186
187     return 0;
188 }
189
190 void level_toggle_debug_mode(level_t *level)
191 {
192     camera_toggle_debug_mode(level->camera);
193 }
194
195 void level_toggle_pause_mode(level_t *level)
196 {
197     camera_toggle_blackwhite_mode(level->camera);
198 }
199
200 int level_reload_preserve_player(level_t *level, const char *file_name)
201 {
202     lt_t * const lt = create_lt();
203     if (lt == NULL) {
204         return -1;
205     }
206
207     /* TODO(#104): duplicate code in create_level_from_file and level_reload_preserve_player */
208
209     FILE * const level_file = PUSH_LT(lt, fopen(file_name, "r"), fclose);
210     if (level_file == NULL) {
211         throw_error(ERROR_TYPE_LIBC);
212         RETURN_LT(lt, -1);
213     }
214
215     char color[7];
216     if (fscanf(level_file, "%6s", color) == EOF) {
217         throw_error(ERROR_TYPE_LIBC);
218         RETURN_LT(lt, -1);
219     }
220     level->background_color = color_from_hexstr(color);
221
222     player_t * const skipped_player = create_player_from_stream(level_file);
223     if (skipped_player == NULL) {
224         RETURN_LT(lt, -1);
225     }
226     destroy_player(skipped_player);
227
228     platforms_t * const platforms = create_platforms_from_stream(level_file);
229     if (platforms == NULL) {
230         RETURN_LT(lt, -1);
231     }
232     level->platforms = RESET_LT(level->lt, level->platforms, platforms);
233
234     goals_t * const goals = create_goals_from_stream(level_file);
235     if (goals == NULL) {
236         RETURN_LT(lt, -1);
237     }
238     level->goals = RESET_LT(level->lt, level->goals, goals);
239
240     lava_t * const lava = create_lava_from_stream(level_file);
241     if (lava == NULL) {
242         RETURN_LT(lt, -1);
243     }
244
245     RETURN_LT(lt, 0);
246 }
247
248 int level_sound(level_t *level, sound_medium_t *sound_medium)
249 {
250     if (goals_sound(level->goals, sound_medium) < 0) {
251         return -1;
252     }
253
254     return 0;
255 }