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