1 #include "game_state.h"
8 #include "../ui/display.h"
9 #include "../ui/event.h"
10 #include "../ui/sprite.h"
11 #include "../util/util.h"
18 static vec2d
const gravity
= { FIXED_POINT_I(0, 0), FIXED_POINT_I(0, 56) };
19 static vec2d
const move_max
= { FIXED_POINT_I(0, 600), FIXED_POINT_I(1, 300) };
20 static fixed_point
const accel_horiz
= FIXED_POINT_I(0, 50);
21 static fixed_point
const accel_vert
= FIXED_POINT_I(0, 167);
22 static fixed_point
const drag_factor
= FIXED_POINT_I(0, 854);
23 static fixed_point
const speed_jump_x
= FIXED_POINT_I(0, 600);
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(killed
|| fixed_point_gt(state
->player
.base
.hitbox
.pos
.y
, FIXED_INT(BADGE_DISPLAY_HEIGHT
))) {
144 state
->player
.base
.flags
|= JUMPNRUN_PLAYER_DEAD
;
148 void jumpnrun_level_tick(jumpnrun_level
*lv
,
149 jumpnrun_game_state
*state
)
151 jumpnrun_tile_range tilerange
= jumpnrun_visible_tiles(lv
, state
);
152 vec2d inertia_mod
= state
->player
.base
.inertia
;
154 jumpnrun_apply_movement(lv
, &tilerange
, state
, &inertia_mod
);
155 state
->screen_left
= jumpnrun_level_assert_left_side(state
);
157 if(state
->player
.base
.tick_minor
== 0) {
158 badge_framebuffer fb
;
159 badge_framebuffer_clear(&fb
);
161 for(size_t tile
= tilerange
.first
; tile
< tilerange
.last
; ++tile
) {
162 jumpnrun_render_tile(&fb
, state
, &lv
->tiles
[tile
]);
165 for(size_t item
= 0; item
< lv
->header
.item_count
; ++item
) {
166 jumpnrun_item
*item_obj
= &lv
->items
[item
];
168 if(item_obj
->flags
& JUMPNRUN_ITEM_COLLECTED
) {
172 int screenpos
= fixed_point_cast_int(item_obj
->pos
.x
) - state
->screen_left
;
173 if(screenpos
> -item_obj
->type
->sprite
.width
&&
174 screenpos
< BADGE_DISPLAY_WIDTH
) {
175 rectangle item_rect
= rect_from_item(item_obj
);
177 if(rectangle_intersect(&state
->player
.base
.hitbox
, &item_rect
)) {
178 item_obj
->type
->on_collect(item_obj
, state
, lv
);
181 jumpnrun_render_item(&fb
, state
, item_obj
);
185 for(size_t shot_ix
= 0; shot_ix
< JUMPNRUN_MAX_SHOTS
; ++shot_ix
) {
186 jumpnrun_shot
*shot
= &state
->shots
[shot_ix
];
187 jumpnrun_shot_process(shot
);
188 if(jumpnrun_shot_spawned(shot
)) {
189 jumpnrun_render_shot(&fb
, state
, shot
);
193 for(size_t enemy_ix
= 0; enemy_ix
< lv
->header
.enemy_count
; ++enemy_ix
) {
194 jumpnrun_enemy
*enemy
= &lv
->enemies
[enemy_ix
];
195 jumpnrun_process_enemy(enemy
, &fb
, state
, lv
, &tilerange
, &inertia_mod
);
198 jumpnrun_render_player(&fb
, state
);
200 badge_framebuffer_flush(&fb
);
202 if(!jumpnrun_moveable_touching_ground(&state
->player
.base
)) {
203 state
->player
.base
.anim_frame
= 2;
204 } else if(fixed_point_gt(fixed_point_abs(state
->player
.base
.inertia
.x
), FIXED_POINT(0, 200))) {
205 state
->player
.base
.anim_frame
= (state
->player
.base
.anim_frame
+ 1) % JUMPNRUN_PLAYER_FRAMES
;
207 state
->player
.base
.anim_frame
= 0;
210 for(size_t shot_ix
= 0; shot_ix
< JUMPNRUN_MAX_SHOTS
; ++shot_ix
) {
211 jumpnrun_shot_process(&state
->shots
[shot_ix
]);
214 for(size_t enemy_ix
= 0; enemy_ix
< lv
->header
.enemy_count
; ++enemy_ix
) {
215 jumpnrun_enemy
*enemy
= &lv
->enemies
[enemy_ix
];
216 jumpnrun_process_enemy(enemy
, NULL
, state
, lv
, &tilerange
, &inertia_mod
);
220 state
->player
.base
.inertia
= inertia_mod
;
221 ++state
->player
.base
.tick_minor
;
222 if(state
->player
.base
.tick_minor
== 3) {
223 state
->player
.base
.tick_minor
= 0;
227 uint8_t jumpnrun_play(char const *lvname
) {
230 JUMPNRUN_LEVEL_LOAD(lv
, lvname
);
232 jumpnrun_game_state gs
;
234 for(jumpnrun_game_state_init(&gs
, &lv
); (gs
.flags
& JUMPNRUN_STATE_WON
) == 0 && gs
.player
.lives
!= 0; --gs
.player
.lives
) {
235 jumpnrun_show_lives_screen(&gs
);
236 jumpnrun_game_state_respawn(&gs
, &lv
);
238 while((gs
.player
.base
.flags
& JUMPNRUN_PLAYER_DEAD
) == 0 &&
239 (gs
. flags
& JUMPNRUN_STATE_WON
) == 0) {
240 badge_event_t ev
= badge_event_wait();
242 switch(badge_event_type(ev
)) {
243 case BADGE_EVENT_USER_INPUT
:
245 uint8_t old_state
= badge_event_old_input_state(ev
);
246 uint8_t new_state
= badge_event_new_input_state(ev
);
247 uint8_t new_buttons
= new_state
& (old_state
^ new_state
);
249 if((new_buttons
& BADGE_EVENT_KEY_BTN_A
) && jumpnrun_moveable_touching_ground(&gs
.player
.base
)) {
250 gs
.player
.base
.jumpable_frames
= 12;
253 if((new_buttons
& BADGE_EVENT_KEY_BTN_B
)) {
255 for(i
= 0; i
< JUMPNRUN_MAX_SHOTS
&& jumpnrun_shot_spawned(&gs
.shots
[i
]); ++i
)
258 if(i
< JUMPNRUN_MAX_SHOTS
) {
259 jumpnrun_shot_spawn(gs
.shots
+ i
, &gs
);
265 case BADGE_EVENT_GAME_TICK
:
267 jumpnrun_level_tick(&lv
, &gs
);
274 if(gs
.flags
& JUMPNRUN_STATE_WON
) { return JUMPNRUN_WON
; }
275 if(gs
.player
.lives
== 0) return JUMPNRUN_LOST
;
276 return JUMPNRUN_ERROR
;