+ running = 1;
+ return 0;
+}
+
+static void gamescr_stop(void)
+{
+ running = 0;
+
+ iwram_brk(prev_iwram_top);
+
+ wait_vblank();
+ /* clear sprites */
+ spr_clear();
+ /* reset background rot/scale state */
+ REG_BG2X = 0;
+ REG_BG2Y = 0;
+ REG_BG2PA = 0x100;
+ REG_BG2PB = 0;
+ REG_BG2PC = 0;
+ REG_BG2PD = 0x100;
+
+}
+
+static void gamescr_frame(void)
+{
+ backbuf = ++nframes & 1;
+ framebuf = vram[backbuf];
+
+ vox_framebuf(240, 160, framebuf, horizon);
+
+ if(update() == -1) {
+ return;
+ }
+ draw();
+
+ vblperf_end();
+ wait_vblank();
+ present(backbuf);
+
+ /*
+ if(!(nframes & 15)) {
+ emuprint("vbl: %d", vblperf_count);
+ }*/
+#ifdef VBLBAR
+ vblperf_begin();
+#else
+ vblperf_count = 0;
+#endif
+}
+
+#define NS(x) (SPRID_UINUM + ((x) << 1))
+static int numspr[][2] = {
+ {NS(0),NS(0)}, {NS(0),NS(1)}, {NS(0),NS(2)}, {NS(0),NS(3)}, {NS(0),NS(4)},
+ {NS(0),NS(5)}, {NS(0),NS(6)}, {NS(0),NS(7)}, {NS(0),NS(8)}, {NS(0),NS(9)},
+ {NS(1),NS(0)}, {NS(1),NS(1)}, {NS(1),NS(2)}, {NS(1),NS(3)}, {NS(1),NS(4)},
+ {NS(1),NS(5)}, {NS(1),NS(6)}, {NS(1),NS(7)}, {NS(1),NS(8)}, {NS(1),NS(9)},
+ {NS(2),NS(0)}, {NS(2),NS(1)}, {NS(2),NS(2)}, {NS(2),NS(3)}, {NS(2),NS(4)},
+ {NS(2),NS(5)}, {NS(2),NS(6)}, {NS(2),NS(7)}, {NS(2),NS(8)}, {NS(2),NS(9)},
+ {NS(3),NS(0)}, {NS(3),NS(1)}, {NS(3),NS(2)}, {NS(3),NS(3)}, {NS(3),NS(4)},
+ {NS(3),NS(5)}, {NS(3),NS(6)}, {NS(3),NS(7)}, {NS(3),NS(8)}, {NS(3),NS(9)}
+};
+
+#define WALK_SPEED 0x40000
+#define TURN_SPEED 0x200
+#define ELEV_SPEED 8
+
+#define MAX(a, b) ((a > (b) ? (a) : (b)))
+
+static int update(void)
+{
+ int32_t fwd[2], right[2];
+ int i, snum, ledspr;
+ struct enemy *enemy;
+ int did_strafe = 0;
+
+ hit_frame = 0;
+
+ update_keyb();
+
+ if(KEYPRESS(BN_START)) {
+ /* TODO pause menu */
+ change_screen(find_screen("menu"));
+ return -1;
+ }
+
+ if(gameover) {
+ goto skip_game_logic;
+ }
+
+ if(keystate) {
+ if(keystate & BN_LEFT) {
+ angle += TURN_SPEED;
+ }
+ if(keystate & BN_RIGHT) {
+ angle -= TURN_SPEED;
+ }
+
+ fwd[0] = -SIN(angle);
+ fwd[1] = COS(angle);
+ right[0] = fwd[1];
+ right[1] = -fwd[0];
+
+ if(keystate & BN_A) {
+ pos[0] = (pos[0] + fwd[0]) & POS_MASK;
+ pos[1] = (pos[1] + fwd[1]) & POS_MASK;
+ if(pos[0] < 0) pos[0] += VOX_SZ << 16;
+ if(pos[1] < 0) pos[1] += VOX_SZ << 16;
+ }
+
+ if((keystate & BN_B) && (timer_msec - last_shot >= P_RATE)) {
+ last_shot = timer_msec;
+ for(i=0; i<total_enemies; i++) {
+ if(enemies[i].hp && enemies[i].vobj.px >= 0) {
+ int dx = enemies[i].vobj.px - 120;
+ int dy = enemies[i].vobj.py - 80;
+ int rad = enemies[i].vobj.scale >> 5;
+
+ if(rad < 1) rad = 1;
+
+ if(abs(dx) < rad && abs(dy) < (rad << 1)) {
+ enemies[i].shot_frame = -1;
+ if(--enemies[i].hp <= 0) {
+ if(++num_kills >= total_enemies) {
+ victory();
+ }
+ }
+ hit_px = enemies[i].vobj.px;
+ hit_py = enemies[i].vobj.py;
+ hitfrm = nframes;
+ break;
+ }
+ }
+ }
+ }
+ if(keystate & BN_UP) {
+ if(horizon > 40) horizon -= ELEV_SPEED;
+ }
+ if(keystate & BN_DOWN) {
+ if(horizon < 200 - ELEV_SPEED) horizon += ELEV_SPEED;
+ }
+ if(keystate & BN_RT) {
+ pos[0] = (pos[0] + right[0]) & POS_MASK;
+ pos[1] = (pos[1] + right[1]) & POS_MASK;
+ if(pos[0] < 0) pos[0] += VOX_SZ << 16;
+ if(pos[1] < 0) pos[1] += VOX_SZ << 16;
+ did_strafe = 1;
+ }
+ if(keystate & BN_LT) {
+ pos[0] = (pos[0] - right[0]) & POS_MASK;
+ pos[1] = (pos[1] - right[1]) & POS_MASK;
+ if(pos[0] < 0) pos[0] += VOX_SZ << 16;
+ if(pos[1] < 0) pos[1] += VOX_SZ << 16;
+ did_strafe = 1;
+ }
+
+ pheight = vox_view(pos[0], pos[1], -40, angle);
+ }
+
+ /* enemy logic */
+ enemy = enemies;
+ for(i=0; i<total_enemies; i++) {
+ /* only consider enemies which are not dead */
+ if(enemy->hp <= 0) {
+ enemy++;
+ continue;
+ }
+
+ if(enemy->shot_frame >= 0) {
+ /* in the process of charging a shot */
+ if(++enemy->shot_frame >= NUM_SHOT_FRAMES - 1) {
+ /* only get hit if we can see the enemy and we didn't strafe */
+ if(!did_strafe && enemy->vobj.px >= 0) {
+ hit_frame = 1;
+ if(--energy <= 0) {
+ gameover = 1;
+ }
+ }
+ enemy->shot_frame = -1;
+ }
+ } else if(enemy->vobj.px >= 0) {
+ /* check rate of fire and start a shot if necessary */
+ if(enemy->last_shot == -1) {
+ enemy->last_shot = timer_msec;
+ } else if(timer_msec - enemy->last_shot >= E_RATE) {
+ enemy->last_shot = timer_msec;
+ enemy->shot_frame = 0;
+ }
+ }
+ enemy++;
+ }