]> git.lizzy.rs Git - nothing.git/blob - src/game/level/player.c
c27ae951ecc44c49160b93fa09e98b047f8ed16c
[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 32000.0f
19 #define PLAYER_DEATH_DURATION 0.75f
20 #define PLAYER_MAX_JUMP_COUNT 2
21
22 typedef enum player_state_t {
23     PLAYER_STATE_ALIVE = 0,
24     PLAYER_STATE_DYING
25 } player_state_t;
26
27 struct player_t {
28     lt_t *lt;
29     player_state_t state;
30
31     rigid_rect_t *alive_body;
32     dying_rect_t *dying_body;
33
34     int jump_count;
35     color_t color;
36
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             color,
72             PLAYER_DEATH_DURATION),
73         destroy_dying_rect);
74     if (player->dying_body == NULL) {
75         RETURN_LT(lt, NULL);
76     }
77
78     player->lt = lt;
79     player->jump_count = 0;
80     player->color = color;
81     player->checkpoint = vec(x, y);
82     player->play_die_cue = 0;
83
84     return player;
85 }
86
87 player_t *create_player_from_stream(FILE *stream)
88 {
89     float x = 0.0f, y = 0.0f;
90
91     char color[7];
92     if (fscanf(stream, "%f%f%6s", &x, &y, color) == EOF) {
93         throw_error(ERROR_TYPE_LIBC);
94         return NULL;
95     }
96
97     return create_player(x, y, color_from_hexstr(color));
98 }
99
100 void destroy_player(player_t * player)
101 {
102     RETURN_LT0(player->lt);
103 }
104
105 solid_ref_t player_as_solid(player_t *player)
106 {
107     solid_ref_t ref = {
108         .tag = SOLID_PLAYER,
109         .ptr = (void*) player
110     };
111
112     return ref;
113 }
114
115 int player_render(const player_t * player,
116                   camera_t *camera)
117 {
118     assert(player);
119     assert(camera);
120
121     switch (player->state) {
122     case PLAYER_STATE_ALIVE:
123         return rigid_rect_render(player->alive_body, camera);
124
125     case PLAYER_STATE_DYING:
126         return dying_rect_render(player->dying_body, camera);
127
128     default: {}
129     }
130
131     return 0;
132 }
133
134 void player_update(player_t *player,
135                    float delta_time)
136 {
137     assert(player);
138
139     switch (player->state) {
140     case PLAYER_STATE_ALIVE: {
141         rigid_rect_update(player->alive_body, delta_time);
142
143         const rect_t hitbox = rigid_rect_hitbox(player->alive_body);
144
145         if (hitbox.y > 1000.0f) {
146             player_die(player);
147         }
148     } break;
149
150     case PLAYER_STATE_DYING: {
151         dying_rect_update(player->dying_body, delta_time);
152
153         if (dying_rect_is_dead(player->dying_body)) {
154             rigid_rect_transform_velocity(
155                 player->alive_body,
156                 make_mat3x3(0.0f, 0.0f, 0.0f,
157                             0.0f, 0.0f, 0.0f,
158                             0.0f, 0.0f, 1.0f));
159             rigid_rect_teleport_to(player->alive_body, player->checkpoint);
160             player->state = PLAYER_STATE_ALIVE;
161         }
162     } break;
163
164     default: {}
165     }
166 }
167
168 void player_collide_with_solid(player_t *player, solid_ref_t solid)
169 {
170     if (player->state == PLAYER_STATE_ALIVE) {
171         rigid_rect_collide_with_solid(player->alive_body, solid);
172
173         if (rigid_rect_touches_ground(player->alive_body)) {
174             player->jump_count = 0;
175         }
176     }
177 }
178
179 void player_move_left(player_t *player)
180 {
181     assert(player);
182     rigid_rect_move(player->alive_body, vec(-PLAYER_SPEED, 0.0f));
183 }
184
185 void player_move_right(player_t *player)
186 {
187     assert(player);
188
189     rigid_rect_move(player->alive_body, vec(PLAYER_SPEED, 0.0f));
190 }
191
192 void player_stop(player_t *player)
193 {
194     assert(player);
195
196     rigid_rect_move(player->alive_body, vec(0.0f, 0.0f));
197 }
198
199 void player_jump(player_t *player)
200 {
201     assert(player);
202     if (player->jump_count < PLAYER_MAX_JUMP_COUNT) {
203         rigid_rect_transform_velocity(player->alive_body,
204                                       make_mat3x3(1.0f, 0.0f, 0.0f,
205                                                   0.0f, 0.0f, 0.0f,
206                                                   0.0f, 0.0f, 1.0f));
207         rigid_rect_apply_force(player->alive_body,
208                                vec(0.0f, -PLAYER_JUMP));
209         player->jump_count++;
210     }
211 }
212
213 void player_die(player_t *player)
214 {
215     assert(player);
216
217     if (player->state == PLAYER_STATE_ALIVE) {
218         const rect_t hitbox =
219             rigid_rect_hitbox(player->alive_body);
220
221         player->play_die_cue = 1;
222         dying_rect_start_dying(player->dying_body, vec(hitbox.x, hitbox.y));
223         player->state = PLAYER_STATE_DYING;
224     }
225 }
226
227 void player_focus_camera(player_t *player,
228                          camera_t *camera)
229 {
230     assert(player);
231     assert(camera);
232
233     const rect_t player_hitbox = rigid_rect_hitbox(player->alive_body);
234
235     camera_center_at(
236         camera,
237         vec_sum(
238             vec(player_hitbox.x, player_hitbox.y),
239             vec(0.0f, -player_hitbox.h * 0.5f)));
240 }
241
242 void player_hide_goals(const player_t *player,
243                        goals_t *goals)
244 {
245     assert(player);
246     assert(goals);
247     goals_hide(goals, rigid_rect_hitbox(player->alive_body));
248 }
249
250 void player_die_from_lava(player_t *player,
251                           const lava_t *lava)
252 {
253     if (lava_overlaps_rect(lava, rigid_rect_hitbox(player->alive_body))) {
254         player_die(player);
255     }
256 }
257
258 void player_checkpoint(player_t *player, vec_t checkpoint)
259 {
260     player->checkpoint = checkpoint;
261 }
262
263 int player_sound(player_t *player,
264                  sound_samples_t *sound_samples)
265 {
266     if (player->play_die_cue) {
267         player->play_die_cue = 0;
268
269         if (sound_samples_play_sound(sound_samples, 0, 0) < 0) {
270             return -1;
271         }
272     }
273
274     return 0;
275 }
276
277 void player_touches_rect_sides(player_t *player,
278                                rect_t object,
279                                int sides[RECT_SIDE_N])
280 {
281     if (player->state == PLAYER_STATE_ALIVE) {
282         rigid_rect_touches_rect_sides(player->alive_body, object, sides);
283     }
284 }
285
286 void player_apply_force(player_t *player, vec_t force)
287 {
288     if (player->state == PLAYER_STATE_ALIVE) {
289         rigid_rect_apply_force(player->alive_body, force);
290     }
291 }