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
const gravity
= { FIXED_POINT_I(0, 0), FIXED_POINT_I(0, 56) };
20 static vec2d
const move_max
= { FIXED_POINT_I(0, 600), FIXED_POINT_I(1, 300) };
21 static fixed_point
const accel_horiz
= FIXED_POINT_I(0, 50);
22 static fixed_point
const accel_vert
= FIXED_POINT_I(0, 167);
23 static fixed_point
const drag_factor
= FIXED_POINT_I(0, 854);
24 static fixed_point
const speed_jump_x
= FIXED_POINT_I(0, 600);
26 static inline int imax(int x
, int y
) {
30 static inline fixed_point
hacker_left (vec2d
const *pos
, jumpnrun_game_state
const *state
) { (void) state
; return pos
->x
; }
31 static inline fixed_point
hacker_top (vec2d
const *pos
, jumpnrun_game_state
const *state
) { (void) state
; return pos
->y
; }
32 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
); }
33 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
); }
35 int jumpnrun_level_assert_left_side(jumpnrun_game_state
const *state
) {
36 static int const lmargin
= 20;
37 static int const rmargin
= 50;
39 int pos_cur
= fixed_point_cast_int(state
->player
.base
.hitbox
.pos
.x
);
40 int pos_rel
= pos_cur
- state
->screen_left
;
42 if(pos_rel
< lmargin
) {
43 return imax(0, pos_cur
- lmargin
);
44 } else if(pos_rel
> BADGE_DISPLAY_WIDTH
- rmargin
) {
45 return pos_cur
- (BADGE_DISPLAY_WIDTH
- rmargin
);
48 return state
->screen_left
;
51 static int jumpnrun_bsearch_tile(jumpnrun_level
const *lv
, jumpnrun_game_state
const *state
) {
53 int len
= lv
->header
.tile_count
;
56 int mid
= front
+ len
/ 2;
58 if(fixed_point_lt(tile_right(&lv
->tiles
[mid
]), FIXED_INT(state
->screen_left
- JUMPNRUN_MAX_SPAWN_MARGIN
))) {
69 jumpnrun_tile_range
jumpnrun_visible_tiles(jumpnrun_level
const *lv
,
70 jumpnrun_game_state
const *state
) {
71 jumpnrun_tile_range r
;
73 r
.first
= jumpnrun_bsearch_tile(lv
, state
);
76 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
;
83 void jumpnrun_apply_gravity(vec2d
*inertia
) {
84 *inertia
= vec2d_add(*inertia
, gravity
);
87 void jumpnrun_passive_movement(vec2d
*inertia
)
89 jumpnrun_apply_gravity(inertia
);
91 inertia
->x
= fixed_point_min(fixed_point_max(fixed_point_neg(move_max
.x
), inertia
->x
), move_max
.x
);
92 inertia
->y
= fixed_point_min(fixed_point_max(fixed_point_neg(move_max
.y
), inertia
->y
), move_max
.y
);
95 static void jumpnrun_apply_movement(jumpnrun_level
const *lv
,
96 jumpnrun_tile_range
const *tilerange
,
97 jumpnrun_game_state
*state
,
99 switch(badge_event_current_input_state() &
100 (BADGE_EVENT_KEY_LEFT
|
101 BADGE_EVENT_KEY_RIGHT
)) {
102 case BADGE_EVENT_KEY_LEFT
:
103 // 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);
104 state
->player
.base
.inertia
.x
= fixed_point_sub(state
->player
.base
.inertia
.x
, accel_horiz
);
105 state
->player
.base
.flags
|= JUMPNRUN_MOVEABLE_MIRRORED
;
107 case BADGE_EVENT_KEY_RIGHT
:
108 // state->player.base.inertia.x = state->player.touching_ground ? fixed_point_add(state->player.base.inertia.x, accel_horiz) : speed_jump_x;
109 state
->player
.base
.inertia
.x
= fixed_point_add(state
->player
.base
.inertia
.x
, accel_horiz
);
110 state
->player
.base
.flags
&= ~JUMPNRUN_MOVEABLE_MIRRORED
;
113 if(jumpnrun_moveable_touching_ground(&state
->player
.base
)) {
114 state
->player
.base
.inertia
.x
= fixed_point_mul(state
->player
.base
.inertia
.x
, drag_factor
);
116 //state->player.base.inertia.x = FIXED_INT(0);
122 if(state
->player
.base
.jumpable_frames
== 0) {
123 // intentionally left blank.
124 } else if(badge_event_current_input_state() & BADGE_EVENT_KEY_BTN_A
) {
125 state
->player
.base
.inertia
.y
= fixed_point_sub(state
->player
.base
.inertia
.y
, accel_vert
);
126 --state
->player
.base
.jumpable_frames
;
128 state
->player
.base
.jumpable_frames
= 0;
131 jumpnrun_passive_movement(&state
->player
.base
.inertia
);
133 vec2d new_pos
= vec2d_add(state
->player
.base
.hitbox
.pos
, state
->player
.base
.inertia
);
135 if(fixed_point_lt(new_pos
.x
, FIXED_INT(state
->screen_left
))) {
136 new_pos
.x
= FIXED_INT(state
->screen_left
);
137 state
->player
.base
.inertia
.x
= FIXED_INT(0);
140 *inertia_mod
= state
->player
.base
.inertia
;
141 bool killed
= collisions_tiles_displace(&new_pos
, &state
->player
.base
, lv
, tilerange
, inertia_mod
);
142 state
->player
.base
.inertia
= *inertia_mod
;
144 if(fixed_point_gt(state
->player
.base
.hitbox
.pos
.y
, FIXED_INT(BADGE_DISPLAY_HEIGHT
))) {
145 jumpnrun_player_despawn(&state
->player
);
147 jumpnrun_player_kill (&state
->player
);
151 void jumpnrun_level_tick(jumpnrun_level
*lv
,
152 jumpnrun_game_state
*state
)
154 jumpnrun_tile_range tilerange
= jumpnrun_visible_tiles(lv
, state
);
155 vec2d inertia_mod
= state
->player
.base
.inertia
;
157 if(jumpnrun_player_alive(&state
->player
)) {
158 jumpnrun_apply_movement(lv
, &tilerange
, state
, &inertia_mod
);
161 state
->screen_left
= jumpnrun_level_assert_left_side(state
);
163 if(state
->tick
== 0) {
164 badge_framebuffer fb
= { { { 0 } } };
166 for(size_t tile
= tilerange
.first
; tile
< tilerange
.last
; ++tile
) {
167 jumpnrun_render_tile(&fb
, state
, &lv
->tiles
[tile
]);
170 for(size_t item
= 0; item
< lv
->header
.item_count
; ++item
) {
171 jumpnrun_item
*item_obj
= &lv
->items
[item
];
173 if(item_obj
->flags
& JUMPNRUN_ITEM_COLLECTED
) {
177 int screenpos
= fixed_point_cast_int(item_obj
->pos
.x
) - state
->screen_left
;
178 if(screenpos
> -item_obj
->type
->sprite
.width
&&
179 screenpos
< BADGE_DISPLAY_WIDTH
) {
180 rectangle item_rect
= rect_from_item(item_obj
);
182 if(rectangle_intersect(&state
->player
.base
.hitbox
, &item_rect
)) {
183 item_obj
->type
->on_collect(item_obj
, state
, lv
);
186 jumpnrun_render_item(&fb
, state
, item_obj
);
190 for(size_t shot_ix
= 0; shot_ix
< JUMPNRUN_MAX_SHOTS
; ++shot_ix
) {
191 jumpnrun_shot
*shot
= &state
->shots
[shot_ix
];
192 jumpnrun_shot_process(shot
);
193 if(jumpnrun_shot_spawned(shot
)) {
194 jumpnrun_render_shot(&fb
, state
, shot
);
198 for(size_t enemy_ix
= 0; enemy_ix
< lv
->header
.enemy_count
; ++enemy_ix
) {
199 jumpnrun_enemy
*enemy
= &lv
->enemies
[enemy_ix
];
200 jumpnrun_process_enemy(enemy
, &fb
, state
, lv
, &tilerange
, &inertia_mod
);
203 if(jumpnrun_player_alive(&state
->player
)) {
204 jumpnrun_render_player(&fb
, state
);
205 jumpnrun_player_advance_animation(&state
->player
);
206 } else if(jumpnrun_moveable_finished_dying(&state
->player
.base
)) {
207 jumpnrun_player_despawn(&state
->player
);
209 jumpnrun_render_splosion(&fb
, state
, &state
->player
.base
);
210 state
->player
.base
.tick_minor
+= JUMPNRUN_STATE_TICKS_PER_FRAME
;
213 badge_framebuffer_flush(&fb
);
215 if(!jumpnrun_moveable_touching_ground(&state
->player
.base
)) {
216 state
->player
.base
.anim_frame
= 2;
217 } else if(fixed_point_gt(fixed_point_abs(state
->player
.base
.inertia
.x
), FIXED_POINT(0, 200))) {
218 state
->player
.base
.anim_frame
= (state
->player
.base
.anim_frame
+ 1) % JUMPNRUN_PLAYER_FRAMES
;
220 state
->player
.base
.anim_frame
= 0;
223 for(size_t shot_ix
= 0; shot_ix
< JUMPNRUN_MAX_SHOTS
; ++shot_ix
) {
224 jumpnrun_shot_process(&state
->shots
[shot_ix
]);
227 for(size_t enemy_ix
= 0; enemy_ix
< lv
->header
.enemy_count
; ++enemy_ix
) {
228 jumpnrun_enemy
*enemy
= &lv
->enemies
[enemy_ix
];
229 jumpnrun_process_enemy(enemy
, NULL
, state
, lv
, &tilerange
, &inertia_mod
);
233 state
->player
.base
.inertia
= inertia_mod
;
235 if(state
->tick
== JUMPNRUN_STATE_TICKS_PER_FRAME
) {
240 uint8_t jumpnrun_play(char const *lvname
) {
243 JUMPNRUN_LEVEL_LOAD(lv
, lvname
);
245 jumpnrun_game_state gs
;
247 for(jumpnrun_game_state_init(&gs
, &lv
); (gs
.flags
& JUMPNRUN_STATE_WON
) == 0 && gs
.player
.lives
!= 0; --gs
.player
.lives
) {
248 jumpnrun_show_lives_screen(&gs
);
249 jumpnrun_game_state_respawn(&gs
, &lv
);
251 while((gs
.player
.base
.flags
& JUMPNRUN_PLAYER_DEAD
) == 0 &&
252 (gs
. flags
& JUMPNRUN_STATE_WON
) == 0) {
253 badge_event_t ev
= badge_event_wait();
255 switch(badge_event_type(ev
)) {
256 case BADGE_EVENT_USER_INPUT
:
258 uint8_t old_state
= badge_event_old_input_state(ev
);
259 uint8_t new_state
= badge_event_new_input_state(ev
);
260 uint8_t new_buttons
= new_state
& (old_state
^ new_state
);
262 if((new_buttons
& BADGE_EVENT_KEY_BTN_A
) && jumpnrun_moveable_touching_ground(&gs
.player
.base
)) {
263 gs
.player
.base
.jumpable_frames
= 12;
266 if((new_buttons
& BADGE_EVENT_KEY_BTN_B
)) {
268 for(i
= 0; i
< JUMPNRUN_MAX_SHOTS
&& jumpnrun_shot_spawned(&gs
.shots
[i
]); ++i
)
271 if(i
< JUMPNRUN_MAX_SHOTS
) {
272 jumpnrun_shot_spawn(gs
.shots
+ i
, &gs
);
278 case BADGE_EVENT_GAME_TICK
:
280 jumpnrun_level_tick(&lv
, &gs
);
287 if(gs
.flags
& JUMPNRUN_STATE_WON
) { return JUMPNRUN_WON
; }
288 if(gs
.player
.lives
== 0) return JUMPNRUN_LOST
;
289 return JUMPNRUN_ERROR
;