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