]> git.lizzy.rs Git - nothing.git/blob - src/game/level/player.c
Introduce solid interface
[nothing.git] / src / game / level / player.c
1 #include <SDL2/SDL.h>
2 #include <assert.h>
3 #include <stdio.h>
4 #include <stdlib.h>
5
6 #include "game/level/player/dying_rect.h"
7 #include "game/level/player/rigid_rect.h"
8 #include "goals.h"
9 #include "math/point.h"
10 #include "platforms.h"
11 #include "player.h"
12 #include "system/error.h"
13 #include "system/lt.h"
14
15 #define PLAYER_WIDTH 25.0f
16 #define PLAYER_HEIGHT 25.0f
17 #define PLAYER_SPEED 500.0f
18 #define PLAYER_JUMP 550.0f
19 #define PLAYER_DEATH_DURATION 0.75f
20
21 typedef enum player_state_t {
22     PLAYER_STATE_ALIVE = 0,
23     PLAYER_STATE_DYING
24 } player_state_t;
25
26 struct player_t {
27     lt_t *lt;
28     player_state_t state;
29
30     rigid_rect_t *alive_body;
31     dying_rect_t *dying_body;
32
33     int jump_count;
34     color_t color;
35
36     /* TODO(#110): introduce checkpoints */
37     vec_t checkpoint;
38
39     int play_die_cue;
40 };
41
42 player_t *create_player(float x, float y, color_t color)
43 {
44     lt_t *lt = create_lt();
45
46     if (lt == NULL) {
47         return NULL;
48     }
49
50     player_t *player = PUSH_LT(lt, malloc(sizeof(player_t)), free);
51     if (player == NULL) {
52         throw_error(ERROR_TYPE_LIBC);
53         RETURN_LT(lt, NULL);
54     }
55
56     player->state = PLAYER_STATE_ALIVE;
57
58     player->alive_body = PUSH_LT(
59         lt,
60         create_rigid_rect(
61             rect(x, y, PLAYER_WIDTH, PLAYER_HEIGHT),
62             color),
63         destroy_rigid_rect);
64     if (player->alive_body == NULL) {
65         RETURN_LT(lt, NULL);
66     }
67
68     player->dying_body = PUSH_LT(
69         lt,
70         create_dying_rect(
71             rect(x, y, PLAYER_WIDTH, PLAYER_HEIGHT),
72             color,
73             PLAYER_DEATH_DURATION),
74         destroy_dying_rect);
75     if (player->dying_body == NULL) {
76         RETURN_LT(lt, NULL);
77     }
78
79     player->lt = lt;
80     player->jump_count = 0;
81     player->color = color;
82     player->checkpoint = vec(x, y);
83     player->play_die_cue = 0;
84
85     return player;
86 }
87
88 player_t *create_player_from_stream(FILE *stream)
89 {
90     float x = 0.0f, y = 0.0f;
91
92     char color[7];
93     if (fscanf(stream, "%f%f%6s", &x, &y, color) == EOF) {
94         throw_error(ERROR_TYPE_LIBC);
95         return NULL;
96     }
97
98     return create_player(x, y, color_from_hexstr(color));
99 }
100
101 void destroy_player(player_t * player)
102 {
103     RETURN_LT0(player->lt);
104 }
105
106 int player_render(const player_t * player,
107                   const camera_t *camera)
108 {
109     assert(player);
110     assert(camera);
111
112     switch (player->state) {
113     case PLAYER_STATE_ALIVE:
114         return rigid_rect_render(player->alive_body, camera);
115
116     case PLAYER_STATE_DYING:
117         return dying_rect_render(player->dying_body, camera);
118
119     default: {}
120     }
121
122     return 0;
123 }
124
125 void player_update(player_t *player,
126                    float delta_time)
127 {
128     assert(player);
129
130     switch (player->state) {
131     case PLAYER_STATE_ALIVE: {
132         rigid_rect_update(player->alive_body, delta_time);
133
134         const rect_t hitbox = rigid_rect_hitbox(player->alive_body);
135
136         if (hitbox.y > 1000.0f) {
137             player_die(player);
138         }
139     } break;
140
141     case PLAYER_STATE_DYING: {
142         dying_rect_update(player->dying_body, delta_time);
143
144         if (dying_rect_is_dead(player->dying_body)) {
145             player->alive_body = RESET_LT(
146                 player->lt,
147                 player->alive_body,
148                 create_rigid_rect(
149                     rect_from_vecs(
150                         player->checkpoint,
151                         vec(PLAYER_WIDTH, PLAYER_HEIGHT)),
152                     player->color));
153             player->state = PLAYER_STATE_ALIVE;
154         }
155     } break;
156
157     default: {}
158     }
159 }
160
161 void player_collide_with_solid(player_t *player, solid_ref_t solid)
162 {
163     if (player->state == PLAYER_STATE_ALIVE) {
164         rigid_rect_collide_with_solid(player->alive_body, solid);
165
166         if (rigid_rect_touches_ground(player->alive_body)) {
167             player->jump_count = 0;
168         }
169     }
170 }
171
172 void player_collide_with_platforms(player_t * player,
173                                    const platforms_t *platforms)
174 {
175     if (player->state == PLAYER_STATE_ALIVE) {
176         rigid_rect_collide_with_platforms(player->alive_body, platforms);
177
178         if (rigid_rect_touches_ground(player->alive_body)) {
179             player->jump_count = 0;
180         }
181     }
182 }
183
184 void player_collide_with_boxes(player_t * player,
185                                const boxes_t * boxes)
186 {
187     if (player->state == PLAYER_STATE_ALIVE) {
188         rigid_rect_collide_with_boxes(player->alive_body, boxes);
189
190         if (rigid_rect_touches_ground(player->alive_body)) {
191             player->jump_count = 0;
192         }
193     }
194 }
195
196 void player_impact_rigid_rect(player_t * player,
197                               rigid_rect_t *rigid_rect)
198 {
199     if (player->state == PLAYER_STATE_ALIVE) {
200         rigid_rect_impact_rigid_rect(player->alive_body, rigid_rect);
201     }
202 }
203
204 void player_move_left(player_t *player)
205 {
206     assert(player);
207     rigid_rect_move(player->alive_body, vec(-PLAYER_SPEED, 0.0f));
208 }
209
210 void player_move_right(player_t *player)
211 {
212     assert(player);
213
214     rigid_rect_move(player->alive_body, vec(PLAYER_SPEED, 0.0f));
215 }
216
217 void player_stop(player_t *player)
218 {
219     assert(player);
220
221     rigid_rect_move(player->alive_body, vec(0.0f, 0.0f));
222 }
223
224 void player_jump(player_t *player)
225 {
226     assert(player);
227     if (player->jump_count < 2) {
228         rigid_rect_jump(player->alive_body, PLAYER_JUMP);
229         player->jump_count++;
230     }
231 }
232
233 void player_die(player_t *player)
234 {
235     assert(player);
236
237     if (player->state == PLAYER_STATE_ALIVE) {
238         player->play_die_cue = 1;
239         player->dying_body = RESET_LT(
240             player->lt,
241             player->dying_body,
242             create_dying_rect(
243                 rigid_rect_hitbox(player->alive_body),
244                 player->color,
245                 PLAYER_DEATH_DURATION));
246         player->state = PLAYER_STATE_DYING;
247     }
248 }
249
250 void player_focus_camera(player_t *player,
251                          camera_t *camera)
252 {
253     assert(player);
254     assert(camera);
255
256     const rect_t player_hitbox = rigid_rect_hitbox(player->alive_body);
257
258     camera_center_at(
259         camera,
260         vec_sum(
261             vec(player_hitbox.x, player_hitbox.y),
262             vec(0.0f, -player_hitbox.h * 0.5f)));
263 }
264
265 void player_hide_goals(const player_t *player,
266                        goals_t *goals)
267 {
268     assert(player);
269     assert(goals);
270     goals_hide(goals, rigid_rect_hitbox(player->alive_body));
271 }
272
273 void player_die_from_lava(player_t *player,
274                           const lava_t *lava)
275 {
276     if (lava_overlaps_rect(lava, rigid_rect_hitbox(player->alive_body))) {
277         player_die(player);
278     }
279 }
280
281 void player_checkpoint(player_t *player, vec_t checkpoint)
282 {
283     player->checkpoint = checkpoint;
284 }
285
286 int player_sound(player_t *player,
287                  sound_samples_t *sound_samples)
288 {
289     if (player->play_die_cue) {
290         player->play_die_cue = 0;
291
292         if (sound_samples_play_sound(sound_samples, 0, 0) < 0) {
293             return -1;
294         }
295     }
296
297     return 0;
298 }
299
300 rect_t player_hitbox(const player_t *player)
301 {
302     if (player->state == PLAYER_STATE_ALIVE) {
303         return rigid_rect_hitbox(player->alive_body);
304     } else {
305         return rect(0.0f, 0.0f, 0.0f, 0.0f);
306     }
307 }