-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathplayer.c
247 lines (222 loc) · 6.92 KB
/
player.c
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
#include "game.h"
#include "world.h"
// sound channels
#define S_CHAN_JUMP 0
#define S_CHAN_PICKUP 1
extern game_state_t* gs;
extern game_assets_t* g_assets;
// sets player position to current level's player start
void P_Spawn() {
// reset view
gs->view_x = 0;
gs->view_y = 0;
gs->scroll_x = 0;
// reset player jump
gs->ps.jump_timer = 0;
gs->ps.on_ground = 0;
gs->ps.do_jump = 0;
// reset direction and bullet
gs->ps.last_dir = 0;
gs->ps.bullet_px = 0;
gs->ps.bullet_py = 0;
gs->ps.do_fire = 0;
gs->ps.do_jetpack = 0;
gs->ps.do_up = 0; gs->ps.do_down = 0;
gs->ps.dead_timer = 0;
// hardcoded player starts
switch ( gs->current_level ) {
case 0: case 4: case 5: case 7: case 9: gs->ps.tx = 2; gs->ps.ty = 8; break;
case 1: gs->ps.tx = 1; gs->ps.ty = 8; break;
case 2: gs->ps.tx = 2; gs->ps.ty = 5; break;
case 3: gs->ps.tx = 1; gs->ps.ty = 5; break;
case 6: gs->ps.tx = 1; gs->ps.ty = 2; break;
case 8: gs->ps.tx = 6; gs->ps.ty = 1; break;
default: break;
}
gs->ps.px = gs->ps.tx * TILE_SIZE;
gs->ps.py = gs->ps.ty * TILE_SIZE;
}
// pickup functionality and remove from world
void P_PickupItem() {
uint8_t tx = gs->ps.check_pickup_x;
uint8_t ty = gs->ps.check_pickup_y;
if ( !tx && !ty ) return;
uint8_t til = gs->levels[gs->current_level].tiles[ty * 100 + tx];
// pickup functionality here
if ( til == 4 ) gs->ps.jetpack = 0xff;
if ( til == 10 ) {
gs->ps.score += 1000;
gs->ps.trophy = 1;
}
if ( til == 20 ) gs->ps.gun = 1;
// remove
gs->levels[gs->current_level].tiles[ty * 100 + tx] = 0;
gs->ps.check_pickup_x = 0;
gs->ps.check_pickup_y = 0;
// sfx
//Mix_PlayChannel( S_CHAN_PICKUP, g_assets->sfx[SFX_PICKUP], 0 );
}
// update collision point clear flags
void P_UpdateCollision() {
if ( gs->ps.dead_timer ) return;
// 8 points of collision; relative to top left of tile 56 neutral frame (20x16)
// 0, 1 = top left, top right
gs->ps.col_point[0] = W_IsClear( gs->ps.px + 4, gs->ps.py - 0, 1 );
gs->ps.col_point[1] = W_IsClear( gs->ps.px + 10, gs->ps.py - 0, 1 );
// 2, 3 = right edge
gs->ps.col_point[2] = W_IsClear( gs->ps.px + 12, gs->ps.py + 2, 1 );
gs->ps.col_point[3] = W_IsClear( gs->ps.px + 12, gs->ps.py + 14, 1 );
// 4, 5 = bottom edge
gs->ps.col_point[4] = W_IsClear( gs->ps.px + 10, gs->ps.py + 16, 1 );
gs->ps.col_point[5] = W_IsClear( gs->ps.px + 4, gs->ps.py + 16, 1 );
// 6, 7 = left edge
gs->ps.col_point[6] = W_IsClear( gs->ps.px + 2, gs->ps.py + 14, 1 );
gs->ps.col_point[7] = W_IsClear( gs->ps.px + 2, gs->ps.py + 2, 1 );
// update on_ground flag if a bottom point (4,5) is not clear
gs->ps.on_ground = (!gs->ps.col_point[4] || !gs->ps.col_point[5]);
}
// update bullet state
void P_UpdateBullet() {
// skip if no bullet in world
if ( !gs->ps.bullet_px || !gs->ps.bullet_py ) return;
// collision
if ( !W_IsClear( gs->ps.bullet_px, gs->ps.bullet_py, 0 ) )
gs->ps.bullet_px = gs->ps.bullet_py = 0;
// off-screen
uint8_t tx = gs->ps.bullet_px / TILE_SIZE;
uint8_t ty = gs->ps.bullet_py / TILE_SIZE;
if ( tx - gs->view_x < 0 || tx - gs->view_x > 20 )
gs->ps.bullet_px = gs->ps.bullet_py = 0;
if ( gs->ps.bullet_px ) {
gs->ps.bullet_px += gs->ps.bullet_dir * 4;
for ( int i = 0; i < sizeof(gs->ms) / sizeof(gs->ms[0]); ++i ) {
monster_state_t* m = &gs->ms[i];
if ( m->type && !m->dead_timer ) {
if ( (ty == m->ty || ty == m->ty + 1) && (tx == m->tx || tx == m->tx + 1) ) {
gs->ps.bullet_px = gs->ps.bullet_py = 0;
m->dead_timer = 30;
}
}
}
}
}
// validate input whose try flags were set
void P_VerifyInput() {
// no input when dead
if ( gs->ps.dead_timer ) return;
// right; col points 2, 3
if ( gs->ps.try_right && gs->ps.col_point[2] && gs->ps.col_point[3] ) {
gs->ps.do_right = 1;
}
// left; col points 6, 7
if ( gs->ps.try_left && gs->ps.col_point[6] && gs->ps.col_point[7] ) {
gs->ps.do_left = 1;
}
// jump; on_ground and col points 0, 1
if ( gs->ps.try_jump && gs->ps.on_ground && !gs->ps.do_jump && !gs->ps.do_jetpack
&& (gs->ps.col_point[0] && gs->ps.col_point[1]) ) {
gs->ps.do_jump = 1;
}
// reset jump timer if contact a ground while still "jumping"
if ( gs->ps.try_jump && gs->ps.on_ground && gs->ps.jump_timer )
gs->ps.jump_timer = 0;
// fire if have gun and no bullet in world
if ( gs->ps.try_fire && gs->ps.gun && !gs->ps.bullet_px && !gs->ps.bullet_py ) {
gs->ps.do_fire = 1;
}
// jetpack toggle
if ( gs->ps.try_jetpack && gs->ps.jetpack ) {
gs->ps.do_jetpack = !gs->ps.do_jetpack;
// stop jump
if ( gs->ps.do_jetpack ) {
gs->ps.do_jump = 0; gs->ps.jump_timer = 0;
}
}
// down if bottom is clear and jetpack
if ( gs->ps.try_down && gs->ps.do_jetpack && gs->ps.col_point[4] && gs->ps.col_point[5] ) {
gs->ps.do_down = 1;
}
// up if top is clear and jetpack
if ( gs->ps.try_up && gs->ps.do_jetpack && gs->ps.col_point[0] && gs->ps.col_point[1] ) {
gs->ps.do_up = 1;
}
}
// apply validated player movement
void P_Move() {
if ( gs->ps.dead_timer ) return;
// update player's tile pos
// sample x towards the center
gs->ps.tx = (gs->ps.px + TILE_SIZE / 2) / TILE_SIZE;
gs->ps.ty = gs->ps.py / TILE_SIZE;
// wrap to top if off level bottom
if ( gs->ps.py >= 10 * TILE_SIZE ) { gs->ps.ty = 0; gs->ps.py = -TILE_SIZE * 2; }
if ( gs->ps.do_right ) {
gs->ps.px += 2;
gs->ps.last_dir = 1;
gs->ps.do_right = 0;
gs->ps.tick++;
}
if ( gs->ps.do_left ) {
gs->ps.px -= 2;
gs->ps.last_dir = -1;
gs->ps.do_left = 0;
gs->ps.tick++;
}
// up and down
if ( gs->ps.do_up ) {
gs->ps.py -= 2;
gs->ps.do_up = 0;
gs->ps.tick++;
} else if ( gs->ps.do_down ) {
gs->ps.py += 2;
gs->ps.do_down = 0;
gs->ps.tick++;
// jump
} else if ( gs->ps.do_jump ) {
if ( !gs->ps.jump_timer ) {
gs->ps.jump_timer = 25;
//gs->ps.last_dir = 0;
//Mix_PlayChannel( S_CHAN_JUMP, g_assets->sfx[SFX_JUMP], 0 );
}
if ( gs->ps.col_point[0] && gs->ps.col_point[1] ) {
if ( gs->ps.jump_timer > 12 )
gs->ps.py -= 2;
if ( gs->ps.jump_timer >= 7 && gs->ps.jump_timer <= 12 )
gs->ps.py -= 1;
gs->ps.jump_timer--;
} else gs->ps.jump_timer = 0;
//gs->ps.jump_timer--;
if ( !gs->ps.jump_timer )
gs->ps.do_jump = 0;
}
// fire
if ( gs->ps.do_fire ) {
gs->ps.bullet_dir = gs->ps.last_dir;
// default right
if ( !gs->ps.bullet_dir ) gs->ps.bullet_dir = 1;
// start bullet in "front" of player
if ( gs->ps.bullet_dir == 1 )
gs->ps.bullet_px = gs->ps.px + 18;
if ( gs->ps.bullet_dir == -1 )
gs->ps.bullet_px = gs->ps.px - 8;
// halfway down player
gs->ps.bullet_py = gs->ps.py + 8;
gs->ps.do_fire = 0;
}
}
// apply gravity to player
void P_ApplyGravity() {
if ( gs->ps.dead_timer ) return;
if ( !gs->ps.do_jump && !gs->ps.on_ground && !gs->ps.do_jetpack ) {
// check below sprite
if ( W_IsClear( gs->ps.px + 4, gs->ps.py + 17, 0 ) && W_IsClear( gs->ps.px + 10, gs->ps.py + 17, 0 ) )
gs->ps.py += 2;
else { // align to tile
uint8_t not_align = gs->ps.py % TILE_SIZE;
if ( not_align ) {
gs->ps.py = not_align < (TILE_SIZE / 2) ?
gs->ps.py - not_align : gs->ps.py + TILE_SIZE - not_align;
}
}
}
}