1 #include "game_state.h"
9 #include "../ui/display.h"
10 #include "../ui/event.h"
11 #include "../ui/sprite.h"
12 #include "../util/util.h"
19 static vec2d
gravity () { return (vec2d
) { FIXED_POINT(0, 0), FIXED_POINT(0, 56) }; }
20 static vec2d
move_max () { return (vec2d
) { FIXED_POINT(0, 600), FIXED_POINT(1, 300) }; }
21 static fixed_point
accel_horiz () { return FIXED_POINT(0, 50); }
22 static fixed_point
accel_vert () { return FIXED_POINT(0, 167); }
23 static fixed_point
drag_factor () { return FIXED_POINT(0, 854); }
25 static inline int imax(int x
, int y
) {
29 static inline fixed_point
hacker_left (vec2d
const *pos
, jumpnrun_game_state
const *state
) { (void) state
; return pos
->x
; }
30 static inline fixed_point
hacker_top (vec2d
const *pos
, jumpnrun_game_state
const *state
) { (void) state
; return pos
->y
; }
31 static inline fixed_point
hacker_right (vec2d
const *pos
, jumpnrun_game_state
const *state
) { return fixed_point_add(hacker_left(pos
, state
), jumpnrun_player_extents().x
); }
32 static inline fixed_point
hacker_bottom(vec2d
const *pos
, jumpnrun_game_state
const *state
) { return fixed_point_add(hacker_top (pos
, state
), jumpnrun_player_extents().y
); }
34 int jumpnrun_level_assert_left_side(jumpnrun_game_state
const *state
) {
35 static int const lmargin
= 20;
36 static int const rmargin
= 50;
38 int pos_cur
= fixed_point_cast_int(state
->player
.base
.hitbox
.pos
.x
);
39 int pos_rel
= pos_cur
- state
->screen_left
;
41 if(pos_rel
< lmargin
) {
42 return imax(0, pos_cur
- lmargin
);
43 } else if(pos_rel
> BADGE_DISPLAY_WIDTH
- rmargin
) {
44 return pos_cur
- (BADGE_DISPLAY_WIDTH
- rmargin
);
47 return state
->screen_left
;
50 static int jumpnrun_bsearch_tile(jumpnrun_level
const *lv
, jumpnrun_game_state
const *state
) {
52 int len
= lv
->header
.tile_count
;
55 int mid
= front
+ len
/ 2;
57 if(fixed_point_lt(tile_right(&lv
->tiles
[mid
]), FIXED_INT(state
->screen_left
- JUMPNRUN_MAX_SPAWN_MARGIN
))) {
68 jumpnrun_tile_range
jumpnrun_visible_tiles(jumpnrun_level
const *lv
,
69 jumpnrun_game_state
const *state
) {
70 jumpnrun_tile_range r
;
72 r
.first
= jumpnrun_bsearch_tile(lv
, state
);
75 r
.last
< lv
->header
.tile_count
&& lv
->tiles
[r
.last
].pos
.x
* JUMPNRUN_TILE_PIXEL_WIDTH
< state
->screen_left
+ BADGE_DISPLAY_WIDTH
+ JUMPNRUN_MAX_SPAWN_MARGIN
;
82 void jumpnrun_apply_gravity(vec2d
*inertia
) {
83 *inertia
= vec2d_add(*inertia
, gravity());
86 void jumpnrun_passive_movement(vec2d
*inertia
)
88 jumpnrun_apply_gravity(inertia
);
90 inertia
->x
= fixed_point_min(fixed_point_max(fixed_point_neg(move_max().x
), inertia
->x
), move_max().x
);
91 inertia
->y
= fixed_point_min(fixed_point_max(fixed_point_neg(move_max().y
), inertia
->y
), move_max().y
);
94 static void jumpnrun_apply_movement(jumpnrun_level
const *lv
,
95 jumpnrun_tile_range
const *tilerange
,
96 jumpnrun_game_state
*state
,
98 switch(badge_event_current_input_state() &
99 (BADGE_EVENT_KEY_LEFT
|
100 BADGE_EVENT_KEY_RIGHT
)) {
101 case BADGE_EVENT_KEY_LEFT
:
102 // state->player.base.inertia.x = state->player.touching_ground ? fixed_point_sub(state->player.base.inertia.x, accel_horiz()) : fixed_point_neg(speed_jump_x());
103 state
->player
.base
.inertia
.x
= fixed_point_sub(state
->player
.base
.inertia
.x
, accel_horiz());
104 state
->player
.base
.flags
|= JUMPNRUN_MOVEABLE_MIRRORED
;
106 case BADGE_EVENT_KEY_RIGHT
:
107 // state->player.base.inertia.x = state->player.touching_ground ? fixed_point_add(state->player.base.inertia.x, accel_horiz()) : speed_jump_x();
108 state
->player
.base
.inertia
.x
= fixed_point_add(state
->player
.base
.inertia
.x
, accel_horiz());
109 state
->player
.base
.flags
&= ~JUMPNRUN_MOVEABLE_MIRRORED
;
112 if(jumpnrun_moveable_touching_ground(&state
->player
.base
)) {
113 state
->player
.base
.inertia
.x
= fixed_point_mul(state
->player
.base
.inertia
.x
, drag_factor());
115 //state->player.base.inertia.x = FIXED_INT(0);
121 if(state
->player
.base
.jumpable_frames
== 0) {
122 // intentionally left blank.
123 } else if(badge_event_current_input_state() & BADGE_EVENT_KEY_BTN_A
) {
124 state
->player
.base
.inertia
.y
= fixed_point_sub(state
->player
.base
.inertia
.y
, accel_vert());
125 --state
->player
.base
.jumpable_frames
;
127 state
->player
.base
.jumpable_frames
= 0;
130 jumpnrun_passive_movement(&state
->player
.base
.inertia
);
132 vec2d new_pos
= vec2d_add(state
->player
.base
.hitbox
.pos
, state
->player
.base
.inertia
);
134 if(fixed_point_lt(new_pos
.x
, FIXED_INT(state
->screen_left
))) {
135 new_pos
.x
= FIXED_INT(state
->screen_left
);
136 state
->player
.base
.inertia
.x
= FIXED_INT(0);
139 *inertia_mod
= state
->player
.base
.inertia
;
140 bool killed
= collisions_tiles_displace(&new_pos
, &state
->player
.base
, lv
, tilerange
, inertia_mod
);
141 state
->player
.base
.inertia
= *inertia_mod
;
143 if(fixed_point_gt(rectangle_top(&state
->player
.base
.hitbox
), FIXED_INT(BADGE_DISPLAY_HEIGHT
))) {
144 jumpnrun_player_despawn(&state
->player
);
146 jumpnrun_player_kill (&state
->player
);
150 void jumpnrun_level_tick(jumpnrun_level
*lv
,
151 jumpnrun_game_state
*state
)
153 jumpnrun_tile_range tilerange
= jumpnrun_visible_tiles(lv
, state
);
154 vec2d inertia_mod
= state
->player
.base
.inertia
;
156 if(jumpnrun_player_alive(&state
->player
)) {
157 jumpnrun_apply_movement(lv
, &tilerange
, state
, &inertia_mod
);
160 state
->screen_left
= jumpnrun_level_assert_left_side(state
);
162 if(state
->tick
== 0) {
163 badge_framebuffer fb
= { { { 0 } } };
165 for(uint16_t tile
= tilerange
.first
; tile
< tilerange
.last
; ++tile
) {
166 jumpnrun_render_tile(&fb
, state
, &lv
->tiles
[tile
]);
169 for(uint16_t item
= 0; item
< lv
->header
.item_count
; ++item
) {
170 jumpnrun_item
*item_obj
= &lv
->items
[item
];
172 if(item_obj
->flags
& JUMPNRUN_ITEM_COLLECTED
) {
176 int screenpos
= fixed_point_cast_int(item_obj
->pos
.x
) - state
->screen_left
;
177 if(screenpos
> -item_obj
->type
->sprite
.width
&&
178 screenpos
< BADGE_DISPLAY_WIDTH
) {
179 rectangle item_rect
= rect_from_item(item_obj
);
181 if(rectangle_intersect(&state
->player
.base
.hitbox
, &item_rect
)) {
182 item_obj
->type
->on_collect(item_obj
, state
, lv
);
185 jumpnrun_render_item(&fb
, state
, item_obj
);
189 for(uint16_t shot_ix
= 0; shot_ix
< JUMPNRUN_MAX_SHOTS
; ++shot_ix
) {
190 jumpnrun_shot
*shot
= &state
->shots
[shot_ix
];
191 jumpnrun_shot_process(shot
);
192 if(jumpnrun_shot_spawned(shot
)) {
193 jumpnrun_render_shot(&fb
, state
, shot
);
197 for(uint16_t enemy_ix
= 0; enemy_ix
< lv
->header
.enemy_count
; ++enemy_ix
) {
198 jumpnrun_enemy
*enemy
= &lv
->enemies
[enemy_ix
];
199 jumpnrun_process_enemy(enemy
, &fb
, state
, lv
, &tilerange
, &inertia_mod
);
202 if(jumpnrun_player_alive(&state
->player
)) {
203 jumpnrun_render_player(&fb
, state
);
204 jumpnrun_player_advance_animation(&state
->player
);
205 } else if(jumpnrun_moveable_finished_dying(&state
->player
.base
)) {
206 jumpnrun_player_despawn(&state
->player
);
207 } else if(jumpnrun_moveable_dying(&state
->player
.base
)) {
208 jumpnrun_render_splosion(&fb
, state
, &state
->player
.base
);
209 state
->player
.base
.tick_minor
+= JUMPNRUN_STATE_TICKS_PER_FRAME
;
212 badge_framebuffer_flush(&fb
);
214 if(!jumpnrun_moveable_touching_ground(&state
->player
.base
)) {
215 state
->player
.base
.anim_frame
= 2;
216 } else if(fixed_point_gt(fixed_point_abs(state
->player
.base
.inertia
.x
), FIXED_POINT(0, 200))) {
217 state
->player
.base
.anim_frame
= (state
->player
.base
.anim_frame
+ 1) % JUMPNRUN_PLAYER_FRAMES
;
219 state
->player
.base
.anim_frame
= 0;
222 for(uint16_t shot_ix
= 0; shot_ix
< JUMPNRUN_MAX_SHOTS
; ++shot_ix
) {
223 jumpnrun_shot_process(&state
->shots
[shot_ix
]);
226 for(uint16_t enemy_ix
= 0; enemy_ix
< lv
->header
.enemy_count
; ++enemy_ix
) {
227 jumpnrun_enemy
*enemy
= &lv
->enemies
[enemy_ix
];
228 jumpnrun_process_enemy(enemy
, NULL
, state
, lv
, &tilerange
, &inertia_mod
);
232 state
->player
.base
.inertia
= inertia_mod
;
234 if(state
->tick
== JUMPNRUN_STATE_TICKS_PER_FRAME
) {
239 uint8_t jumpnrun_play_level(char const *lvname
) {
242 JUMPNRUN_LEVEL_LOAD(lv
, lvname
);
244 jumpnrun_game_state gs
;
246 jumpnrun_game_state_init(&gs
, &lv
);
249 jumpnrun_show_lives_screen(&gs
);
250 jumpnrun_game_state_respawn(&gs
, &lv
);
252 while((gs
.player
.base
.flags
& JUMPNRUN_PLAYER_DEAD
) == 0 &&
253 (gs
. flags
& JUMPNRUN_STATE_WON
) == 0) {
254 badge_event_t ev
= badge_event_wait();
256 switch(badge_event_type(ev
)) {
257 case BADGE_EVENT_USER_INPUT
:
259 uint8_t old_state
= badge_event_old_input_state(ev
);
260 uint8_t new_state
= badge_event_new_input_state(ev
);
261 uint8_t new_buttons
= new_state
& (old_state
^ new_state
);
263 if((new_buttons
& BADGE_EVENT_KEY_BTN_A
) && jumpnrun_moveable_touching_ground(&gs
.player
.base
)) {
264 gs
.player
.base
.jumpable_frames
= 12;
267 if((new_buttons
& BADGE_EVENT_KEY_BTN_B
)) {
269 for(i
= 0; i
< JUMPNRUN_MAX_SHOTS
&& jumpnrun_shot_spawned(&gs
.shots
[i
]); ++i
)
272 if(i
< JUMPNRUN_MAX_SHOTS
) {
273 jumpnrun_shot_spawn(gs
.shots
+ i
, &gs
);
279 case BADGE_EVENT_GAME_TICK
:
281 jumpnrun_level_tick(&lv
, &gs
);
286 } while((gs
.flags
& JUMPNRUN_STATE_WON
) == 0 && gs
.player
.lives
-- != 0);
288 if(gs
.flags
& JUMPNRUN_STATE_WON
) {
289 jumpnrun_show_you_rock();
293 if(++gs
.player
.lives
== 0) {
294 jumpnrun_show_game_over();
295 return JUMPNRUN_LOST
;
298 return JUMPNRUN_ERROR
;