9f4df5630f552870f83cb340045cb5ed60d06e67
[hackover2013-badge-firmware.git] / badge / jumpnrun / jumpnrun.c
1 #include "jumpnrun.h"
2 #include "collision.h"
3 #include "levels.h"
4
5 #include "../ui/display.h"
6 #include "../ui/event.h"
7 #include "../ui/sprite.h"
8
9 #include <assert.h>
10 #include <math.h>
11 #include <stddef.h>
12 #include <stdio.h>
13
14 #define ARRAY_SIZE(arr) (sizeof(arr) / sizeof*(arr))
15
16 static vec2d const gravity = { FIXED_POINT_I(0, 0), FIXED_POINT_I(0, 56) };
17 static vec2d const move_max = { FIXED_POINT_I(0, 600), FIXED_POINT_I(1, 300) };
18 static fixed_point const accel_horiz = FIXED_POINT_I(0, 50);
19 static fixed_point const accel_vert = FIXED_POINT_I(0, 250);
20 static fixed_point const drag_factor = FIXED_POINT_I(0, 854);
21 static fixed_point const speed_jump_x = FIXED_POINT_I(0, 600);
22
23 static badge_sprite const anim_hacker[] = {
24 { 5, 8, (uint8_t const *) "\x1c\xff\xfd\x04\x04" },
25 { 5, 8, (uint8_t const *) "\x1c\xff\x3d\xc4\x04" },
26 { 5, 8, (uint8_t const *) "\xdc\x3f\x1d\x24\xc4" },
27 { 5, 8, (uint8_t const *) "\x1c\xff\x3d\xc4\x04" }
28
29 /*
30 { 5, 8, (uint8_t const *) "\x46\xfc\x73\x8c\x31" },
31 { 5, 8, (uint8_t const *) "\x46\xfc\x73\x8c\x52" },
32 { 5, 8, (uint8_t const *) "\x46\xfc\x73\x94\x8c" },
33 { 5, 8, (uint8_t const *) "\x46\xfc\x73\x8c\x52" }
34 */
35 /*
36 { 6, 8, (uint8_t const *) "\x0c\xe1\x3b\x0e\xc3\x30" },
37 { 6, 8, (uint8_t const *) "\x0c\xe1\x3b\x0e\x43\x51" },
38 { 6, 8, (uint8_t const *) "\x0c\xe1\x3b\x0e\x35\x82" },
39 { 6, 8, (uint8_t const *) "\x0c\xe1\x3b\x0e\x43\x51" }
40 */
41 /*
42 { 6, 8, (uint8_t const *) "\xff\xff\xff\xff\xff\xff" },
43 { 6, 8, (uint8_t const *) "\xff\xff\xff\xff\xff\xff" },
44 { 6, 8, (uint8_t const *) "\xff\xff\xff\xff\xff\xff" },
45 { 6, 8, (uint8_t const *) "\xff\xff\xff\xff\xff\xff" }
46 */
47 };
48
49
50 static inline int imax(int x, int y) {
51 return x < y ? y : x;
52 }
53
54 static inline fixed_point hacker_left (vec2d const *pos, jumpnrun_game_state const *state) { (void) state; return pos->x; }
55 static inline fixed_point hacker_top (vec2d const *pos, jumpnrun_game_state const *state) { (void) state; return pos->y; }
56 static inline fixed_point hacker_right (vec2d const *pos, jumpnrun_game_state const *state) { return fixed_point_add(hacker_left(pos, state), FIXED_POINT(anim_hacker[state->anim_frame].width , 0)); }
57 static inline fixed_point hacker_bottom(vec2d const *pos, jumpnrun_game_state const *state) { return fixed_point_add(hacker_top (pos, state), FIXED_POINT(anim_hacker[state->anim_frame].height, 0)); }
58
59 static inline rectangle hacker_rect(vec2d const *pos,
60 jumpnrun_game_state const *state) {
61 return (rectangle) { { hacker_left(pos, state), hacker_top(pos, state) }, { FIXED_POINT(anim_hacker[state->anim_frame].width, 0), FIXED_POINT(anim_hacker[state->anim_frame].height, 0) } };
62 }
63
64 rectangle hacker_rect_current(jumpnrun_game_state const *state) {
65 return hacker_rect(&state->current_pos, state);
66 }
67
68 int jumpnrun_level_assert_left_side(jumpnrun_game_state const *state) {
69 static int const lmargin = 20;
70 static int const rmargin = 50;
71
72 int pos_cur = fixed_point_cast_int(state->current_pos.x);
73 int pos_rel = pos_cur - state->left;
74
75 if(pos_rel < lmargin) {
76 return imax(0, pos_cur - lmargin);
77 } else if(pos_rel > BADGE_DISPLAY_WIDTH - rmargin) {
78 return pos_cur - (BADGE_DISPLAY_WIDTH - rmargin);
79 }
80
81 return state->left;
82 }
83
84 static int jumpnrun_bsearch_tile(jumpnrun_level const *lv, jumpnrun_game_state const *state) {
85 int front = 0;
86 int len = lv->header.tile_count;
87
88 while(len > 0) {
89 int mid = front + len / 2;
90
91 if(fixed_point_lt(tile_right(&lv->tiles[mid]), FIXED_POINT(state->left - JUMPNRUN_MAX_SPAWN_MARGIN, 0))) {
92 front = mid + 1;
93 len -= len / 2 + 1;
94 } else {
95 len /= 2;
96 }
97 }
98
99 return front;
100 }
101
102 jumpnrun_tile_range jumpnrun_visible_tiles(jumpnrun_level const *lv,
103 jumpnrun_game_state const *state) {
104 jumpnrun_tile_range r;
105
106 r.first = jumpnrun_bsearch_tile(lv, state);
107
108 for(r.last = r.first;
109 r.last < lv->header.tile_count && lv->tiles[r.last].pos.x * JUMPNRUN_TILE_PIXEL_WIDTH < state->left + BADGE_DISPLAY_WIDTH + JUMPNRUN_MAX_SPAWN_MARGIN;
110 ++r.last)
111 ;
112
113 return r;
114 }
115
116 void jumpnrun_passive_movement(vec2d *inertia)
117 {
118 *inertia = vec2d_add(*inertia, gravity);
119
120 inertia->x = fixed_point_min(fixed_point_max(fixed_point_neg(move_max.x), inertia->x), move_max.x);
121 inertia->y = fixed_point_min(fixed_point_max(fixed_point_neg(move_max.y), inertia->y), move_max.y);
122 }
123
124 static void jumpnrun_apply_movement(jumpnrun_level const *lv,
125 jumpnrun_tile_range const *tilerange,
126 jumpnrun_game_state *state) {
127 switch(badge_event_current_input_state() &
128 (BADGE_EVENT_KEY_LEFT |
129 BADGE_EVENT_KEY_RIGHT)) {
130 case BADGE_EVENT_KEY_LEFT:
131 // state->inertia.x = state->touching_ground ? fixed_point_sub(state->inertia.x, accel_horiz) : fixed_point_neg(speed_jump_x);
132 state->inertia.x = fixed_point_sub(state->inertia.x, accel_horiz);
133 state->anim_direction = BADGE_BLT_MIRRORED;
134 break;
135 case BADGE_EVENT_KEY_RIGHT:
136 // state->inertia.x = state->touching_ground ? fixed_point_add(state->inertia.x, accel_horiz) : speed_jump_x;
137 state->inertia.x = fixed_point_add(state->inertia.x, accel_horiz);
138 state->anim_direction = 0;
139 break;
140 default:
141 if(state->touching_ground) {
142 state->inertia.x = fixed_point_mul(state->inertia.x, drag_factor);
143 } //else {
144 //state->inertia.x = FIXED_POINT(0, 0);
145 //}
146
147 break;
148 }
149
150 if(state->jumpable_frames == 0) {
151 // intentionally left blank.
152 } else if(badge_event_current_input_state() & BADGE_EVENT_KEY_BTN_A) {
153 state->inertia.y = fixed_point_sub(state->inertia.y, accel_vert);
154 // fixed_point_neg(move_max.y)
155 --state->jumpable_frames;
156 } else {
157 state->jumpable_frames = 0;
158 }
159
160 jumpnrun_passive_movement(&state->inertia);
161
162 vec2d new_pos = vec2d_add(state->current_pos, state->inertia);
163
164 if(fixed_point_lt(new_pos.x, FIXED_POINT(state->left, 0))) {
165 new_pos.x = FIXED_POINT(state->left, 0);
166 state->inertia.x = FIXED_POINT(0, 0);
167 }
168
169 rectangle hacker_rect_c = hacker_rect(&state->current_pos, state);
170 collisions_tiles_displace(&new_pos, &hacker_rect_c, lv, tilerange, &state->inertia, &state->touching_ground);
171 state->inertia_mod = state->inertia;
172
173 state->current_pos = new_pos;
174
175 if(fixed_point_gt(state->current_pos.y, FIXED_POINT(BADGE_DISPLAY_HEIGHT, 0))) {
176 state->status = JUMPNRUN_DEAD;
177 }
178 }
179
180 void jumpnrun_level_tick(jumpnrun_level *lv,
181 jumpnrun_game_state *state)
182 {
183 jumpnrun_tile_range tilerange = jumpnrun_visible_tiles(lv, state);
184 jumpnrun_apply_movement(lv, &tilerange, state);
185
186 state->left = jumpnrun_level_assert_left_side(state);
187
188 if(state->tick_minor == 0) {
189 badge_framebuffer fb;
190 badge_framebuffer_clear(&fb);
191
192 for(size_t tile = tilerange.first; tile < tilerange.last; ++tile) {
193 badge_framebuffer_blt(&fb,
194 fixed_point_cast_int(tile_left(&lv->tiles[tile])) - state->left,
195 fixed_point_cast_int(tile_top (&lv->tiles[tile])),
196 &tile_type(&lv->tiles[tile])->sprite,
197 0);
198 }
199
200 for(size_t item = 0; item < lv->header.item_count; ++item) {
201 int screenpos = fixed_point_cast_int(lv->items[item].pos.x) - state->left;
202 if(screenpos > -lv->items[item].type->sprite.width &&
203 screenpos < BADGE_DISPLAY_WIDTH) {
204 rectangle hack_rect = hacker_rect(&state->current_pos, state);
205 rectangle item_rect = rect_from_item(&lv->items[item]);
206
207 if(rectangle_intersect(&hack_rect, &item_rect)) {
208 lv->items[item].type->on_collect(state);
209 }
210
211 badge_framebuffer_blt(&fb,
212 screenpos,
213 fixed_point_cast_int(lv->items[item].pos.y),
214 &lv->items[item].type->sprite,
215 0);
216 }
217 }
218
219 for(size_t enemy_ix = 0; enemy_ix < lv->header.enemy_count; ++enemy_ix) {
220 jumpnrun_enemy *enemy = &lv->enemies[enemy_ix];
221 jumpnrun_process_enemy(enemy, &fb, state, lv, &tilerange);
222 }
223
224 badge_framebuffer_blt(&fb,
225 fixed_point_cast_int(state->current_pos.x) - state->left,
226 fixed_point_cast_int(state->current_pos.y),
227 &anim_hacker[state->anim_frame],
228 state->anim_direction);
229
230 badge_framebuffer_flush(&fb);
231
232 if(!state->touching_ground) {
233 state->anim_frame = 2;
234 } else if(fixed_point_gt(fixed_point_abs(state->inertia.x), FIXED_POINT(0, 200))) {
235 state->anim_frame = (state->anim_frame + 1) % ARRAY_SIZE(anim_hacker);
236 } else {
237 state->anim_frame = 0;
238 }
239 } else {
240 for(size_t enemy_ix = 0; enemy_ix < lv->header.enemy_count; ++enemy_ix) {
241 jumpnrun_enemy *enemy = &lv->enemies[enemy_ix];
242 jumpnrun_process_enemy(enemy, NULL, state, lv, &tilerange);
243 }
244 }
245
246 state->inertia = state->inertia_mod;
247 state->tick_minor = (state->tick_minor + 1) % 3;
248 }
249
250 uint8_t jumpnrun_play(char const *lvname) {
251 jumpnrun_level lv;
252
253 JUMPNRUN_LEVEL_LOAD(lv, lvname);
254
255 jumpnrun_game_state gs;
256 memset(&gs, 0, sizeof(gs));
257
258 gs.current_pos = lv.start_pos;
259
260 while(gs.status == JUMPNRUN_PLAYING) {
261 badge_event_t ev = badge_event_wait();
262
263 switch(badge_event_type(ev)) {
264 case BADGE_EVENT_USER_INPUT:
265 {
266 uint8_t old_state = badge_event_old_input_state(ev);
267 uint8_t new_state = badge_event_new_input_state(ev);
268 uint8_t new_buttons = new_state & (old_state ^ new_state);
269
270 if((new_buttons & BADGE_EVENT_KEY_BTN_A) && gs.touching_ground) {
271 gs.jumpable_frames = 8;
272 }
273
274 break;
275 }
276 case BADGE_EVENT_GAME_TICK:
277 {
278 jumpnrun_level_tick(&lv, &gs);
279 break;
280 }
281 }
282 }
283
284 return gs.status;
285 }
This page took 0.066503 seconds and 3 git commands to generate.