]> git.lizzy.rs Git - nothing.git/blob - src/game/level/player.c
3737be6c6cd0383009434f3a8febe12f12d923d6
[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
40 player_t *create_player(float x, float y, color_t color)
41 {
42     lt_t *lt = create_lt();
43
44     if (lt == NULL) {
45         return NULL;
46     }
47
48     player_t *player = PUSH_LT(lt, malloc(sizeof(player_t)), free);
49     if (player == NULL) {
50         throw_error(ERROR_TYPE_LIBC);
51         RETURN_LT(lt, NULL);
52     }
53
54     player->state = PLAYER_STATE_ALIVE;
55
56     player->alive_body = PUSH_LT(
57         lt,
58         create_rigid_rect(
59             rect(x, y, PLAYER_WIDTH, PLAYER_HEIGHT),
60             color),
61         destroy_rigid_rect);
62     if (player->alive_body == NULL) {
63         RETURN_LT(lt, NULL);
64     }
65
66     player->dying_body = PUSH_LT(
67         lt,
68         create_dying_rect(
69             rect(x, y, PLAYER_WIDTH, PLAYER_HEIGHT),
70             color,
71             PLAYER_DEATH_DURATION),
72         destroy_dying_rect);
73     if (player->dying_body == NULL) {
74         RETURN_LT(lt, NULL);
75     }
76
77     player->lt = lt;
78     player->jump_count = 0;
79     player->color = color;
80     player->checkpoint = vec(x, y);
81
82     return player;
83 }
84
85 player_t *create_player_from_stream(FILE *stream)
86 {
87     float x = 0.0f, y = 0.0f;
88
89     char color[7];
90     if (fscanf(stream, "%f%f%6s", &x, &y, color) == EOF) {
91         throw_error(ERROR_TYPE_LIBC);
92         return NULL;
93     }
94
95     return create_player(x, y, color_from_hexstr(color));
96 }
97
98 void destroy_player(player_t * player)
99 {
100     RETURN_LT0(player->lt);
101 }
102
103 int player_render(const player_t * player,
104                   SDL_Renderer *renderer,
105                   const camera_t *camera)
106 {
107     assert(player);
108     assert(renderer);
109     assert(camera);
110
111     switch (player->state) {
112     case PLAYER_STATE_ALIVE:
113         return rigid_rect_render(player->alive_body, renderer, camera);
114
115     case PLAYER_STATE_DYING:
116         return dying_rect_render(player->dying_body, renderer, camera);
117
118     default: {}
119     }
120
121     return 0;
122 }
123
124 void player_update(player_t *player,
125                    const platforms_t *platforms,
126                    float delta_time)
127 {
128     assert(player);
129     assert(platforms);
130
131     switch (player->state) {
132     case PLAYER_STATE_ALIVE: {
133         rigid_rect_update(player->alive_body, platforms, delta_time);
134
135         if (rigid_rect_touches_ground(player->alive_body)) {
136             player->jump_count = 0;
137         }
138
139         const rect_t hitbox = rigid_rect_hitbox(player->alive_body);
140
141         if (hitbox.y > 1000.0f) {
142             player_die(player);
143         }
144     } break;
145
146     case PLAYER_STATE_DYING: {
147         dying_rect_update(player->dying_body, delta_time);
148
149         if (dying_rect_is_dead(player->dying_body)) {
150             player->alive_body = RESET_LT(
151                 player->lt,
152                 player->alive_body,
153                 create_rigid_rect(
154                     rect_from_vecs(
155                         player->checkpoint,
156                         vec(PLAYER_WIDTH, PLAYER_HEIGHT)),
157                     player->color));
158             player->state = PLAYER_STATE_ALIVE;
159         }
160     } break;
161
162     default: {}
163     }
164 }
165
166 void player_move_left(player_t *player)
167 {
168     assert(player);
169     rigid_rect_move(player->alive_body, vec(-PLAYER_SPEED, 0.0f));
170 }
171
172 void player_move_right(player_t *player)
173 {
174     assert(player);
175
176     rigid_rect_move(player->alive_body, vec(PLAYER_SPEED, 0.0f));
177 }
178
179 void player_stop(player_t *player)
180 {
181     assert(player);
182
183     rigid_rect_move(player->alive_body, vec(0.0f, 0.0f));
184 }
185
186 void player_jump(player_t *player)
187 {
188     assert(player);
189     if (player->jump_count < 2) {
190         rigid_rect_jump(player->alive_body, PLAYER_JUMP);
191         player->jump_count++;
192     }
193 }
194
195 void player_die(player_t *player)
196 {
197     assert(player);
198
199     if (player->state == PLAYER_STATE_ALIVE) {
200         player->dying_body = RESET_LT(
201             player->lt,
202             player->dying_body,
203             create_dying_rect(
204                 rigid_rect_hitbox(player->alive_body),
205                 player->color,
206                 PLAYER_DEATH_DURATION));
207         player->state = PLAYER_STATE_DYING;
208     }
209 }
210
211 void player_focus_camera(player_t *player,
212                          camera_t *camera)
213 {
214     assert(player);
215     assert(camera);
216
217     const rect_t player_hitbox = rigid_rect_hitbox(player->alive_body);
218
219     camera_center_at(
220         camera,
221         vec_sum(
222             vec(player_hitbox.x, player_hitbox.y),
223             vec(0.0f, -player_hitbox.h * 0.5f)));
224 }
225
226 void player_hide_goals(const player_t *player,
227                        goals_t *goals)
228 {
229     assert(player);
230     assert(goals);
231     goals_hide(goals, rigid_rect_hitbox(player->alive_body));
232 }
233
234 void player_die_from_lava(player_t *player,
235                           const lava_t *lava)
236 {
237     if (lava_overlaps_rect(lava, rigid_rect_hitbox(player->alive_body))) {
238         player_die(player);
239     }
240 }
241
242 void player_checkpoint(player_t *player, vec_t checkpoint)
243 {
244     player->checkpoint = checkpoint;
245 }