#define __CKCM__
#define COMP_BORLAND
#include "include\keen1.h";
#include "include\game.h";
#include "include\sprites2.h";
#include "spacer.dat";
short CM_draw_level(short levelnum) {
short i,j, st_x, st_y;
Sprite *sp1, *sp2;
num_sprites = 1;
num_bodies = 0;
sprite_sync = 0;
ticks = ticks_sync = 0;
keen_invincible = god_mode = 0;
level_finished = 0;
keen_facing = 1;
// update CM_init_level with keen variables later
sprites[0].type = sprites[0].active = 1;
sprites[0].think = TH_keen_ground;
sprites[0].contact = CO_keen;
sprites[0].velX = sprites[0].velY = 0;
sprites[0].frame = 0;
CM_init_level(levelnum);
// set screen position
scrollX = sprites[0].posX - 0xA000;
scrollY = sprites[0].posY - 0x5000;
if (scrollX < scrollX_min)
scrollX = scrollX_min;
if (scrollY < scrollY_min)
scrollY = scrollY_min;
if (scrollX > scrollX_max)
scrollX = scrollX_max;
if (scrollY > scrollY_max)
scrollY = scrollY_max;
// draw screen
sync_drawing();
fade_out();
lights = 1;
clear_overlay();
draw_screen();
draw_screen();
fade_in();
scrollX_T = map2tile(scrollX);
scrollY_T = map2tile(scrollY);
// level loop
do {
sync_drawing();
memcpy(&input_new, handle_ctrl(&input_new, 1), sizeof(Input));
// think loop
for (i = 0; i < num_sprites; i++) {
// don't update if sprite ID = 0;
if(sprites[i].type != 0) {
memcpy(&temp_sprite, &sprites[i], sizeof(Sprite));
if (!CM_sprite_active_screen()) {
CM_update_sprite_hitbox();
temp_sprite.delX = temp_sprite.delY = 0;
temp_sprite.think();
temp_sprite.posX += temp_sprite.delX;
temp_sprite.posY += temp_sprite.delY;
CM_update_sprite_hitbox();
}
memcpy(&sprites[i], &temp_sprite, sizeof(Sprite));
}
}
CM_do_scrolling();
// collision loop
for (i = 0, sp1 = sprites; i < num_sprites; i++, sp1++) {
if(sp1->type && sp1->active) {
for(j = i + 1, sp2 = sp1+1; j < num_sprites; j++, sp2++) {
if(sp2->type && sp2->active) {
if (CM_detect_sprite_col(sp1, sp2)) {
sp1->contact(sp1, sp2);
sp2->contact(sp2, sp1);
}
}
}
}
}
scrollX_T = map2tile(scrollX);
scrollY_T = map2tile(scrollY);
// draw sprites
for(i = 0; i < num_sprites; i++)
if(sprites[i].type && sprites[i].type)
draw_sprite_at(sprites[i].posX, sprites[i].posY, sprites[i].frame);
// keen/tile collisions
CM_keen_bgtile_col();
// bodies
for (i = 0; i < num_bodies; i++)
if (bodies[i].type)
bodies[i].think(&bodies[i]);
//
draw_screen();
memcpy(&input_old, &input_new, sizeof(Input));
handle_cheat_keys();
if (handle_global_keys())
ticks_sync = ticks;
if (quit_to_title)
return LEVEL_DIE;
} while (level_finished == LEVEL_DIE && sprites[0].type != 0);
finish_cur_sound();
fade_out();
for (i = 0; i < 4; i++) {
keen_gp.stuff[5+i] = 0;
}
if (level_finished == LEVEL_DIE) {
// remove parts
}
return level_finished;
}
void CM_init_level(short levelnum) {
short x, y, t;
load_level_data(levelnum);
for(y = 0; y < map_height_T; y++) {
for(x = 0; x < map_width_T; x++) {
t = map_data_sprites[y * map_width_T + x];
switch (t) {
/* all the sprites
case 1:
add_janitor(x, y);
break;
case 2:
case 3:
case 4:
case 5:
*/
case 255:
sprites[0].posX = tile2map(x);
sprites[0].posY = tile2map(y);
break;
default:
break;
}
}
}
return;
}
Sprite *CM_add_sprite(void) {
int i;
Sprite *sp;
for(i = 1, sp = sprites + 1;
i < num_sprites && sp->type != 0;
i++, sp++);
if(i >= num_sprites) {
num_sprites++;
}
memset(sp, 0, sizeof(Sprite));
sp->think = TH_nop;
sp->contact = CO_nop;
sp->active = 1;
return sp;
}
Body *CM_add_body(void) {
int i;
Body *bp;
for(i = 0, bp = bodies+1;
i < num_bodies && bp->type != 0;
i++, bp++);
if(i >= num_sprites) {
num_sprites++;
}
memset(bp, 0, sizeof(Body));
bp->think = BO_nop;
return bp;
}
short CM_detect_sprite_col(Sprite *s1, Sprite *s2) {
if (!s1->type || s2 -> type)
return 0;
if (s1->boxX2 < s2->boxX1 ||
s1->boxX1 > s2->boxX2 ||
s1->boxY2 < s2->boxY1 ||
s1->boxY1 > s2->boxY2 )
return 0;
return 1;
}
void CM_update_sprite_hitbox(void) {
short f = (temp_sprite.frame * 4) + ((temp_sprite.posX >> 9) % 4);
void __far* fp = Masked_Entries + f;
movedata(FP_SEG(fp), FP_OFF(fp), _DS, (short)&temp_MSE, sizeof(MaskedSpriteEntry));
temp_sprite.boxX1 = (long)temp_MSE.hitbox_L + temp_sprite.posX;
temp_sprite.boxX2 = (long)temp_MSE.hitbox_R + temp_sprite.posX;
temp_sprite.boxY1 = (long)temp_MSE.hitbox_U + temp_sprite.posY;
temp_sprite.boxY2 = (long)temp_MSE.hitbox_D + temp_sprite.posY;
return;
}
void CM_update_sprite_hitbox_wmap(Sprite *sp) {
memcpy(&temp_sprite, sp, sizeof(Sprite));
CM_update_sprite_hitbox();
memcpy(sp, &temp_sprite, sizeof(Sprite));
return;
}
void CM_move_horz(short max, short min, short dv) {
int i;
for (i = 1; i <= sprite_sync; i++) {
temp_sprite.velX += dv;
if (temp_sprite.velX > max)
temp_sprite.velX = max;
else if (temp_sprite.velX < min)
temp_sprite.velX = min;
if (i != sprite_sync)
temp_sprite.delX += temp_sprite.velX;
}
return;
}
void CM_move_vert(short max, short min, short dv) {
int i;
for(i = 1; i <= sprite_sync; i++) {
temp_sprite.velY += dv;
if (temp_sprite.velY > max)
temp_sprite.velY = max;
else if (temp_sprite.velY < min)
temp_sprite.velY = min;
if (i != sprite_sync)
temp_sprite.delY += temp_sprite.velY;
}
return;
}
short CM_compute_sprite_delta(void) {
temp_sprite.delX += sprite_sync * temp_sprite.velX;
temp_sprite.delY += sprite_sync * temp_sprite.velY;
asm { DB 9Ah, 0F0h, 0Dh, 0BEh, 0EFh} //ckpatch far addr over FOODBEEF
return _AX;
}
short CM_sprite_active_screen(void) {
short tx, ty;
tx = map2tile(temp_sprite.posX);
ty = map2tile(temp_sprite.posY);
if (temp_sprite.posY < 0)
temp_sprite.posY = 0;
if (temp_sprite.posX < map_width &&
temp_sprite.posX >= 0 &&
temp_sprite.posY <= map_height) {
if (tx >= (scrollX_T - 8) &&
ty >= (scrollY_T - 8) &&
tx <= (scrollX_T +28) &&
ty <= (scrollY_T +14) )
return 0;
if (temp_sprite.type < 9)
temp_sprite.active = 0;
else
temp_sprite.type = 0;
}
return 1;
}
// If sprite exits screen, returns edge name
short CM_check_ceiling(void) {
short ret = 0;
if (temp_sprite.posX > ceilingX) {
temp_sprite.delX = temp_sprite.velX = 0;
temp_sprite.posX = ceilingX;
ret |= EDGE_R;
}
if (temp_sprite.posX < scrollX_min + 8) {
temp_sprite.delX = temp_sprite.velX = 0;
temp_sprite.posX = scrollX_min + 8;
ret |= EDGE_L;
}
if (temp_sprite.posY < scrollY_min + 8) {
temp_sprite.delY = temp_sprite.velY = 0;
temp_sprite.posY = scrollY_min + 8;
ret |= EDGE_U;
}
if (temp_sprite.posY > ceilingY) {
temp_sprite.delY = temp_sprite.velY = 0;
temp_sprite.posY = ceilingY;
ret |= EDGE_D;
}
return ret;
}
void CM_do_scrolling(void) {
short dx = sprites[0].delX;
short dy = sprites[0].delY;
if(sprites[0].think != TH_keen_exit && sprites[0].think != TH_keen_dead) {
// scroll left
if (dx > 0 && (sprites[0].posX - scrollX > 0xB000)) {
scrollX += (long)dx;
if (scrollX > scrollX_max)
scrollX = scrollX_max;
}
//right
if (dx < 0 && (sprites[0].posX - scrollX < 0x9000)) {
scrollX += (long)dx;
if (scrollX < scrollX_min)
scrollX = scrollX_min;
}
//down
if (dy > 0 && (sprites[0].posY - scrollY > 0x7000)) {
scrollY += (long)dy;
if (scrollY > scrollY_max)
scrollY = scrollY_max;
}
// up
if (dy < 0 && (sprites[0].posY - scrollY < 0x3000)) {
scrollY += (long)dy;
if (scrollY < scrollY_min)
scrollY = scrollY_min;
}
}
return;
}
void CM_change_palette(char *pal) {
delay(1);
asm {
mov AX, DS
mov ES, AX
mov DX, pal
mov AX, 1002h
int 10h
}
return;
}
void CM_toggle_lights(void) {
if(input_new.but2 && !input_old.but2) {
set_cur_sound(25);
if (lights != 0) {
CM_change_palette(&palette_1);
lights = 0;
} else {
CM_change_palette(&palette_3);
lights = 1;
}
}
return;
}
void CM_shake_screen(void) {
int i;
long sx = scrollX;
long sy = scrollY;
for (i = 0; i < 80; i++) {
sync_drawing();
scrollX = sx + (calc_jump_height(64) - 32) * 256;
scrollY = sy + (calc_jump_height(64) - 32) * 256;
draw_sprite_at(temp_sprite.posX, temp_sprite.posY, temp_sprite.frame);
draw_screen();
}
draw_box_opening2(5,1);
draw_string(aOops_);
delay(100);
level_finished = LEVEL_TANTALUS;
return;
}
// switch links to a bridge info tile in the sprite map
// sprite plane: bridge_info (bits C to F) is type 1
// bits 0 to 9 are replacement tile ID (0 thru 1023)
// bits A,B specify bridge direction
// tile_plane: switch on (25):
// switch off (26):
void CM_toggle_bridge(short tx, short ty) {
short n,tp,sp, li,lx, ly;
Body *bp;
n = ty * map_width_T + tx;
tp = map_data_tiles[n];
sp = map_data_sprites[n]; // the pointer tile
lx = (sp & 0xFF) + tx; // linked tile coords
ly = ((sp & 0xFF00) >> 8)+ ty;
li = map_data_sprites[ly * map_width_T + lx];
// return if link does not point to bridge control
if ((sp >> 12) != 1) return;
// spawn new bridge controller
bp = CM_add_body();
bp->posX = lx; // link coordinates
bp->posY = ly;
bp->type = 1; // type 1 = bridge
bp->time = 0;
bp->variant = ((li >> 10) % 4); // direction
bp->varB = n; // pointer tile in sprite plane
bp->varC = li & 0x03FF; //replacement tile
bp->think = BO_bridge;
// change tile_plane
if (TI_Type[tp] & 1)
map_data_tiles[n]--; // on --> off
else
map_data_tiles[n]++; // off --> on
return;
}
void BO_bridge(Body *bp) {
short lx, ly, n;
bp->time += sprite_sync;
if (bp->time <= 30) return;
bp->time = 0;
//move to next square
switch (bp->variant) {
case 0: bp->posY--; break; // up
case 1: bp->posX++; break; // right
case 2: bp->posY++; break; // down
case 3: bp->posX--; break; // left
default: break;
}
//update link tile pointer
lx = bp->varB % map_width_T;
ly = bp->varB / 13;
map_data_sprites[bp->varB] = (bp->posY - ly << 8) | (bp->posX - lx);
//if square is bridge control destroy body; else place tile
n = bp->posY * map_width_T + bp->posX;
if (map_data_sprites[n] >> 12 == 1) {
bp->type = 0;
num_bodies--;
} else {
map_data_tiles[n] = bp->varC;
}
return;
}
void CM_do_fall(void) {
CM_move_vert(200, -400, 3);
return;
}
// Keen
// Common
void CM_keen_walk(short dv) {
CM_move_horz(120, -120,dv);
return;
}
void CM_keen_pogo(void) {
CM_move_vert(200, -200, -1);
return;
}
void CM_keen_bgtile_col(void) {
short tx1, tx2, ty1, ty2, tx, ty, ti, tc;
register Sprite *k;
keen_switch = 0;
k = sprites;
if (k->think == TH_keen_dead)
return;
tx1 = map2tile(k->boxX1);
tx2 = map2tile(k->boxX2);
ty1 = map2tile(k->boxY1);
ty2 = map2tile(k->boxY2);
for (tx = tx1; tx <= tx2; tx++) {
for (ty = ty1; ty <= ty2; ty++) {
tc = ty * map_width_T + tx;
ti = TI_Type[map_data_tiles[tc]];
switch (ti) {
case 0:
break;
// enemies
case 1:
CM_kill_keen();
break;
//doors
case 2:
case 3:
case 4:
case 5:
if (keen_gp.stuff[5+(ti-1)]) { open_door(tx, ty); }
else if (k->delX > 0) { k->posX &= 0xFFFFF000L; }
else { k->posX = (k->posX + 0x1000) & 0xFFFFF000L; }
break;
// score items
case 6:
case 7:
case 8:
case 9:
case 10:
add_score(points_tbl[ti - 6]);
set_cur_sound(9);
goto replacetile;
break;
// parts
case 11:
case 12:
case 13:
case 14:
keen_gp.stuff[ti - 10] = 1;
add_score(10000);
set_cur_sound(11);
goto replacetile;
break;
// raygun
case 28:
keen_gp.ammo -= 4;
case 15:
keen_gp.ammo += 5;
set_cur_sound(10);
goto replacetile;
break;
// pogo
case 16:
break;
// exit
case 17:
if (k->think == TH_keen_ground) {
k->think = TH_keen_exit;
k->contact = CO_nop;
k->varA = tx;
k->varB = ty;
set_cur_sound(15);
}
break;
// keycards
case 18:
case 19:
case 20:
case 21:
keen_gp.stuff[5+ti-17] = 1;
set_cur_sound(32);
goto replacetile;
break;
// statue
case 22:
statue_message();
goto replacetile;
break;
// lightswitch
case 23:
keen_switch = 1;
CM_toggle_lights();
break;
// teleporter
case 24:
level_finished = LEVEL_SECRET;
break;
case 25:
case 26:
keen_switch = 1;
CM_toggle_bridge(tx, ty);
break;
default:
break;
replacetile:
map_data_tiles[tc] = (map_data_tiles[tc] / 13) * 13;
break;
}
}
}
return;
}
// Thinks
// varA temp vel x
// varB temp vel y
// varC temp frame
// varD = airborne: 1 if keen airborne, 0 otherwise
void TH_keen_ground(void) {
short x, y, vs, csd;
char t, t_sl, t_bl;
x = map2tile(((temp_sprite.boxX1 + temp_sprite.boxX2) >> 1));
y = map2tile((temp_sprite.boxY2 + 1));
t = TI_UEdge[map_data_tiles[y * map_width_T + x]];
t_bl = t & 3; // blocking
t_sl = t >> 4; // slope info
/* movement from input (slippery and blocking only) */
if (t_bl != 3) {
switch (input_new.dir) {
case 1:
case 2:
case 3:
CM_keen_walk(2);
if (temp_sprite.velX < 0) goto decelerate;
break;
case 5:
case 6:
case 7:
CM_keen_walk(-2);
if (temp_sprite.velX > 0) goto decelerate;
break;
// decelerate if on normal
decelerate:
case 0:
case 4:
case 8:
if (t_bl <= 1) {
if(temp_sprite.velX > 0) {
CM_keen_walk(-3);
if (temp_sprite.velX < 0)
temp_sprite.velX = 0;
}
if (temp_sprite.velX < 0) {
CM_keen_walk(3);
if (temp_sprite.velX > 0)
temp_sprite.velX = 0;
}
}
break;
default: break;
}
// scale velocity if moving uphill
if ( (t_sl & 8) && // is sloped
( ((temp_sprite.velX > 0) && (t_sl & 4)) || // move right positive slope
((temp_sprite.velX < 0) && !(t_sl & 4)) ) ) // move left negative slope
{
switch (t_sl & 3) {
case 0: vs = 25; break; // 1/1
case 1: vs = 28; break; // 1/2
case 2: vs = 30; break; // 1/4
case 3: vs = 31; break; // 1/8th (unused)
default: vs = 32; break; // ??
}
temp_sprite.velX = (temp_sprite.velX * vs) / 32;
}
}
/* movement from icy/slippery slopes */
if ( (t_bl >= 2) && (t_sl & 8) ) {
// increase velocity in downhill direction
switch (t_sl & 3) {
case 0: vs = 36; break; // 1/1
case 1: vs = 35; break; // 1/2
case 2: vs = 34; break; // 1/4
case 3: vs = 33; break; // 1/8th (unused)
default: vs = 32; break; // ??
}
temp_sprite.velX = (temp_sprite.velX * vs) / 32;
}
/* frames */
// standing l/r
if (keen_facing > 0)
temp_sprite.frame = 0;
else
temp_sprite.frame = 4;
// looking up down
switch (input_new.dir) {
case 0:
case 4:
temp_sprite.think = TH_keen_look;
temp_sprite.time = 0;
break;
//if no dir input, but on slippery, advance frames
case 8:
if (t_bl != 2) break;
case 1:
case 2:
case 3:
case 5:
case 6:
case 7:
if (t_bl <= 2)
temp_sprite.frame += (ticks >> 4) & 3;
if (temp_sprite.velX != 0)
keen_facing = temp_sprite.velX;
break;
default:
break;
}
// sound
if ( (temp_sprite.velX != 0) && ((ticks >> 4) & 1) && (t_bl < 3) ) {
if ((ticks >> 5) & 1)
set_cur_sound(30);
else
set_cur_sound(4);
}
// adjust position
CM_do_fall();
csd = CM_compute_sprite_delta();
if (CM_check_ceiling() & EDGE_D)
CM_kill_keen_temp();
// wall blokcing sound
if ( ((csd & EDGE_L) || (csd & EDGE_R)) && ((ticks >> 4) & 1) )
set_cur_sound(5);
// falling
if ( !(csd & EDGE_U) && !temp_sprite.varD ) {
temp_sprite.think = TH_keen_jump_air;
set_cur_sound(27);
} else {
// jump
if (input_new.but1) {
temp_sprite.varA = temp_sprite.velX;
temp_sprite.velX = temp_sprite.varB = temp_sprite.time = 0;
if (keen_facing > 0)
temp_sprite.varC = 8; //jumpright1
else
temp_sprite.varC = 14; //jumpleft1
temp_sprite.think = TH_keen_jump_ground;
}
// pogo
if (input_new.but2 && !input_old.but2 && !keen_switch && keen_gp.stuff[3]) {
temp_sprite.varA = temp_sprite.velX;
temp_sprite.time = temp_sprite.velX = 0;
temp_sprite.think = TH_keen_pogo_ground;
}
if (input_new.but3 && !input_old.but3) {
temp_sprite.think = TH_keen_shoot_ground;
temp_sprite.velX = temp_sprite.time = temp_sprite.varC = 0;
}
if (temp_sprite.varD)
temp_sprite.varD = 0;
}
return;
}
void TH_keen_jump_ground(void) {
// update frame
temp_sprite.frame = temp_sprite.varC + (temp_sprite.time / 6);
// increase temp velY
if (input_new.but1)
temp_sprite.varB = sprite_sync * 6 + temp_sprite.varB;
else if (temp_sprite.time < 12)
temp_sprite.time = 24 - temp_sprite.time;
// increase temp velX
switch (input_new.dir) {
case 1:
case 2:
case 3:
temp_sprite.varA += (sprite_sync << 1);
if (temp_sprite.varA > 120)
temp_sprite.varA = 120;
break;
case 5:
case 6:
case 7:
temp_sprite.varA -= (sprite_sync << 1);
if (temp_sprite.varA < -120)
temp_sprite.varA = -120;
break;
default:
break;
}
temp_sprite.velX = 0;
temp_sprite.time += sprite_sync;
if (temp_sprite.time >= 36) {
temp_sprite.think = TH_keen_jump_air;
temp_sprite.velY -= temp_sprite.varB;
temp_sprite.velX = temp_sprite.varA;
set_cur_sound(6);
}
CM_do_fall();
CM_compute_sprite_delta();
if (CM_check_ceiling() & EDGE_D)
CM_kill_keen_temp();
if (input_new.but3 && !input_old.but3) {
temp_sprite.think = TH_keen_shoot_ground;
temp_sprite.velX = temp_sprite.time = temp_sprite.varC = 0;
}
return;
}
void TH_keen_jump_air(void) {
short csd;
// handle movement
switch (input_new.dir) {
case 1:
case 2:
case 3:
CM_keen_walk(2);
if (temp_sprite.velX < 0)
goto decelerate;
break;
case 5:
case 6:
case 7:
CM_keen_walk(-2);
if (temp_sprite.velX > 0)
goto decelerate;
break;
decelerate:
default:
case 0:
case 4:
case 8:
if (temp_sprite.velX > 0) {
CM_keen_walk(-1);
if (temp_sprite.velX < 0)
temp_sprite.velX = 0;
} else if (temp_sprite.velX < 0) {
CM_keen_walk(1);
if (temp_sprite.velX > 0)
temp_sprite.velX = 0;
}
}
if (keen_facing > 0)
temp_sprite.frame = 13; //jumpright
else
temp_sprite.frame = 19; //jumpleft
if (temp_sprite.velX != 0)
keen_facing = temp_sprite.velX;
CM_do_fall();
csd = CM_compute_sprite_delta();
if ((csd & EDGE_L) || (csd & EDGE_R))
if ((ticks >> 4) & 1)
set_cur_sound(5);
if (csd & EDGE_U && temp_sprite.velY >= 0) {
temp_sprite.think = TH_keen_ground;
set_cur_sound(7);
} else {
if (csd & EDGE_D)
set_cur_sound(21);
if (CM_check_ceiling() & EDGE_D)
CM_kill_keen_temp();
if (input_new.but2 & !input_old.but2 && !keen_switch && keen_gp.stuff[3])
temp_sprite.think = TH_keen_pogo_air;
}
if (input_new.but3 && !input_old.but3) {
temp_sprite.think = TH_keen_shoot_air;
temp_sprite.velX = temp_sprite.time = temp_sprite.varC = 0;
}
return;
}
void TH_keen_pogo_ground(void) {
temp_sprite.velX = temp_sprite.delX = 0;
temp_sprite.time += sprite_sync;
if (temp_sprite.time < 22) {
if (keen_facing > 0)
temp_sprite.frame = 25;
else
temp_sprite.frame = 27;
} else {
temp_sprite.think = TH_keen_pogo_air;
temp_sprite.velY -= 200;
temp_sprite.velX = temp_sprite.varA;
set_cur_sound(6);
}
if (input_new.but2 && !input_old.but2)
temp_sprite.think = TH_keen_ground;
if (input_new.but3 && !input_old.but3) {
temp_sprite.think = TH_keen_shoot_ground;
temp_sprite.velX = temp_sprite.time = temp_sprite.varC = 0;
}
CM_compute_sprite_delta();
temp_sprite.varD = 0; // not airborne
return;
}
void TH_keen_pogo_air(void) {
int csd = 0;
// do movement
switch (input_new.dir) {
case 1:
case 2:
case 3:
CM_keen_walk(1);
break;
case 5:
case 6:
case 7:
CM_keen_walk(-1);
break;
default:
break;
}
if (input_new.but1 && temp_sprite.velY < 0)
CM_keen_pogo();
if (god_mode && input_new.but1)
temp_sprite.velY = -200;
if (temp_sprite.velX != 0)
keen_facing = temp_sprite.velX;
if (keen_facing >= 0)
temp_sprite.frame = 24;
else
temp_sprite.frame = 26;
CM_do_fall();
csd = CM_compute_sprite_delta();
// wall blocking sound
if ((csd & EDGE_L) || (csd & EDGE_R))
if ((ticks >> 4) & 1)
set_cur_sound(5);
if (csd & EDGE_U && temp_sprite.velY >= 0) {
temp_sprite.think = TH_keen_pogo_ground;
temp_sprite.varA = temp_sprite.velX;
temp_sprite.time = temp_sprite.velX = 0;
}
if (csd & EDGE_D)
set_cur_sound(21);
if (CM_check_ceiling() & EDGE_D)
CM_kill_keen_temp();
if (input_new.but2 && !input_old.but2 && !keen_switch)
temp_sprite.think = TH_keen_jump_air;
if (input_new.but3 && !input_old.but3) {
temp_sprite.think = TH_keen_shoot_air;
temp_sprite.velX = temp_sprite.time = temp_sprite.varC = 0;
}
if (temp_sprite.varD !=0)
temp_sprite.varD = 0;
return;
}
// varC: if set, cannot fire; if reset, can fire
void TH_keen_shoot_ground(void) {
short d;
long x, y;
temp_sprite.time += sprite_sync;
if (!temp_sprite.varC) {
x = temp_sprite.posX;
y = temp_sprite.posY + 0x800;
d = input_new.dir;
switch (d) {
case 1:
case 3:
case 2:
case 5:
case 7:
case 6:
break;
case 8:
if (keen_facing > 0)
d = 2;
else
d = 6;
break;
default:
case 4:
d = -1;
break;
}
if (d >= 0) {
temp_sprite.frame = 50 + d;
if (keen_gp.ammo > 0) {
ADD_stunner(x,y,d);
set_cur_sound(12);
keen_gp.ammo--;
} else {
set_cur_sound(36);
}
}
temp_sprite.varC = 1;
}
if (temp_sprite.time > 30 && !input_new.but3)
temp_sprite.think = TH_keen_ground;
if (temp_sprite.velX > 0) {
CM_keen_walk(-1);
if (temp_sprite.velX < 0)
temp_sprite.velX = 0;
}
if (temp_sprite.velX < 0) {
CM_keen_walk(1);
if (temp_sprite.velX > 0)
temp_sprite.velX = 0;
}
CM_do_fall();
CM_compute_sprite_delta();
if (CM_check_ceiling() & EDGE_D)
CM_kill_keen_temp();
return;
}
// varC: if set, cannot fire; if reset, can fire
void TH_keen_shoot_air(void) {
short d;
long x, y;
temp_sprite.time += sprite_sync;
if (!temp_sprite.varC) {
x = temp_sprite.posX;
y = temp_sprite.posY + 0x800;
d = input_new.dir;
switch (d) {
case 0:
case 1:
case 3:
case 2:
case 4:
case 5:
case 7:
case 6:
break;
default:
case 8:
if (keen_facing > 0)
d = 2;
else
d = 6;
break;
}
if (d >= 0) {
temp_sprite.frame = 60 + d;
if (keen_gp.ammo > 0) {
ADD_stunner(x,y,d);
set_cur_sound(12);
keen_gp.ammo--;
} else {
set_cur_sound(36);
}
}
temp_sprite.varC = 1;
}
if (temp_sprite.time > 30 && !input_new.but3)
temp_sprite.think = TH_keen_jump_air;
CM_do_fall();
CM_compute_sprite_delta();
if (CM_check_ceiling() & EDGE_D)
CM_kill_keen_temp();
return;
}
void TH_keen_look(void) {
temp_sprite.time += sprite_sync;
if (input_new.dir == 0 && input_old.dir == 0) {
temp_sprite.frame = 10;
if (temp_sprite.time > 60)
if ( (scrollY > scrollY_min) && (scrollY > temp_sprite.posY - 0xA000) )
scrollY -= 0x400;
} else if (input_new.dir == 4 && input_old.dir == 4) {
temp_sprite.frame = 20;
if (temp_sprite.time > 60)
if (scrollY < scrollY_max && scrollY < (temp_sprite.posY))
scrollY += 0x400;
} else {
temp_sprite.think = TH_keen_ground;
}
if (input_new.but3 && !input_old.but3) {
temp_sprite.think = TH_keen_shoot_air;
temp_sprite.velX = temp_sprite.time = temp_sprite.varC = 0;
}
if (temp_sprite.varD !=0)
temp_sprite.varD = 0;
/*
CM_do_fall();
CM_compute_sprite_delta();
if (CM_check_ceiling() & EDGE_D)
CM_kill_keen_temp();
*/
return;
}
void ADD_stunner(long x, long y, short dir) {
Sprite* s;
short vx, vy;
vx = vy = 0;
s = CM_add_sprite();
s->type = 10;
s->posX = x;
s->posY = y;
s->think = TH_stunner_flight;
s->contact = CO_stunner;
s->frame = 0x6c;
s->varD = 0; // spawning flag; set after first call to think
switch (dir) {
case 0:
vy = -400;
break;
case 1:
vy = -284;
vx = 284;
break;
case 2:
vx = 400;
break;
case 3:
vx = 284;
vy = 284;
break;
case 4:
vy = 400;
break;
case 5:
vx = -284;
vy = 284;
break;
case 6:
vx = -400;
break;
case 7:
vx = -284;
vy = -284;
break;
default:
break;
}
s->velX = vx;
s->velY = vy;
return;
}
void TH_stunner_flight(void) {
// check if spawned inside only on first think call, then csd
if ( (!temp_sprite.varD && CM_spawned_inside()) || CM_compute_sprite_delta()) {
set_cur_sound(37);
temp_sprite.type = 13;
temp_sprite.think = TH_stunner_zapzot;
temp_sprite.time = 0;
if (get_random() > 0x80)
temp_sprite.frame = 0x6e;
else
temp_sprite.frame = 0x6f;
}
temp_sprite.varD = 1;
return;
}
void TH_stunner_zapzot(void) {
temp_sprite.type = 0;
return;
}
void CO_stunner(Sprite *stunner, Sprite *sp) {
if (sp->type != 1 && sp->type != 14) {
set_cur_sound(37);
stunner->think = TH_stunner_zapzot;
stunner->contact = CO_nop;
stunner->velX = stunner->velY = 0;
if (get_random() > 0x80)
stunner->frame = 0x6e;
else
stunner->frame = 0x6f;
}
return;
}
void TH_keen_exit(void) {
long t1, t2;
t1 = (long)temp_sprite.varA; //tilex
t2 = (long)temp_sprite.varB; //tiley
temp_sprite.delX = sprite_sync * 60;
temp_sprite.frame = 0 + ((ticks>>4) & 3);
keen_facing = temp_sprite.velX;
// cover all four exit tiles
slide_sprite(tile2map(t1), tile2map(t2), 160);
slide_sprite(tile2map(t1), tile2map((t2+1)), 160);
slide_sprite(tile2map((t1+1)), tile2map(t2), 143);
slide_sprite(tile2map((t1+1)), tile2map((t2+1)), 143);
t1 = tile2map((t1+1));
if (t1 <= temp_sprite.posX) {
temp_sprite.type = 0;
level_finished = LEVEL_EXIT;
}
return;
}
void TH_keen_dead(void) {
temp_sprite.time += sprite_sync;
if (temp_sprite.time > 200) {
temp_sprite.time = -999;
temp_sprite.velX = get_random() - 0x80;
temp_sprite.velY = -400;
}
temp_sprite.frame = ((ticks >> 4) & 1) + 22;
temp_sprite.delX = temp_sprite.velX * sprite_sync;
temp_sprite.delY = temp_sprite.velY * sprite_sync;
if (temp_sprite.boxY2 < scrollY)
temp_sprite.type = 0;
return;
}
void CM_kill_keen(void) {
sprites[0].think = TH_keen_dead;
sprites[0].contact = CO_nop;
sprites[0].posY += 0x800;
sprites[0].time = sprites[0].velY = sprites[0].velX = 0;
sprites[0].frame = 22;
set_cur_sound(8);
return;
}
void CM_kill_keen_temp(void) {
temp_sprite.think = TH_keen_dead;
temp_sprite.contact = CO_nop;
temp_sprite.posY += 0x800;
temp_sprite.time = temp_sprite.velY = temp_sprite.velX = 0;
temp_sprite.frame = 22;
set_cur_sound(8);
return;
}
void CO_keen(Sprite *s1, Sprite *s2) {
return;
}
// called from first think-call after spawning a sprite
// returns nonzero if sprite was spawned inside block
short CM_spawned_inside(void) {
short tx, ty, tx1, tx2, ty1, ty2, bl;
tx1 = map2tile(temp_sprite.boxX1);
tx2 = map2tile(temp_sprite.boxX2);
ty1 = map2tile(temp_sprite.boxY1);
ty2 = map2tile(temp_sprite.boxY2);
bl = 0;
// check x if velx != 0
if (temp_sprite.velX > 0) {
for (tx = tx2 - 1, ty = ty1; ty <= ty2; ty++)
if (TI_LEdge[map_data_tiles[ty * map_width_T + tx]])
bl | EDGE_L;
} else if (temp_sprite.velX < 0) {
for (tx = tx1 + 1, ty = ty1; ty <= ty2; ty++)
if (TI_REdge[map_data_tiles[ty * map_width_T + tx]])
bl | EDGE_R;
}
// check y if vely != 0
if (temp_sprite.velY > 0) {
for (ty = ty2 - 1, tx = tx1; tx <= tx2; tx++)
if (TI_UEdge[map_data_tiles[ty * map_width_T + tx]])
bl | EDGE_U;
} else if (temp_sprite.velY < 0) {
for (ty = ty1 + 1, tx = tx1; tx <= tx2; tx++)
if (TI_DEdge[map_data_tiles[ty * map_width_T + tx]])
bl | EDGE_D;
}
return bl;
}
void TH_nop(void) {
return;
}
void CO_nop(Sprite *s1, Sprite *s2) {
return;
}
void BO_nop(Body *bp) {
return;
}
/*
void janitor_walk(void) {
int bl;
// varA stores temp velX
// if varC, sprite is walking, else it is pausing
if (temp_sprite.varC) {
// 2 frame loop; base frame is 100 for right, 102 for left, speed 5
if (temp_sprite.velX > 0)
temp_sprite.frame = 100;
else
temp_sprite.frame = 102;
temp_sprite.frame += ((ticks >> 5) & 1);
} else {
// look at screen for random amount of time
temp_sprite.frame = 104;
if (get_random() > 0xE0) {
temp_sprite.velX = temp_sprite.varA;
temp_sprite.varC = 1;
}
}
do_fall();
bl = compute_sprite_delta();
//bird falls off edge
if (!(bl & EDGE_U)) {
temp_sprite.delX = -(temp_sprite.delX << 1); // set bird back on edge
temp_sprite.delY = 0; // no vertical displacement
temp_sprite.velX = -temp_sprite.velX; // reverse velocity
temp_sprite.frame = 104;
return;
}
// hits a wall, turns around
if (bl & EDGE_L) {
temp_sprite.varA = -90;
temp_sprite.frame = 104;
temp_sprite.velX = temp_sprite.varC = 0;
return;
}
if (bl & EDGE_R) {
temp_sprite.varA = 90;
temp_sprite.frame = 100;
temp_sprite.velX = temp_sprite.varC = 0;
return;
}
return;
}
void janitor_start(void) {
do_fall();
if (compute_sprite_delta() & 2)
temp_sprite.think = loc_func(janitor_walk);
}
// no operation
void janitor_contact(Sprite *janitor, Sprite *other) {
if (other->type == 10 || other->type == 11) {
janitor->time = 0;
janitor->frame = 105;
janitor->think = loc_func(sprite_kill);
janitor->contact = loc_func(contact_none);
janitor->velY = -80;
set_cur_sound(34);
}
return;
}
//
void add_janitor(short tx, short ty) {
Sprite *b;
b = add_sprite();
b->type = 2;
b->posX = tile2map(tx);
b->posY = tile2map(ty);
b->velX = 90;
b->varC = 1;
if (b->posX > sprites[0].posX)
b->velX = -(b->velX);
b->think = loc_func(janitor_start);
b->contact = loc_func(janitor_contact);
b->frame = 100;
return;
}
// one frame sprite death
void sprite_kill(void) {
temp_sprite.type = 14;
if ((temp_sprite.time += sprite_sync) > 40) {
temp_sprite.time = 0;
temp_sprite.frame++;
temp_sprite.think = loc_func(sprite_dead);
}
temp_sprite.velX = 0;
do_fall();
compute_sprite_delta();
}
void sprite_dead(void) {
temp_sprite.time++;
do_fall();
compute_sprite_delta();
}
*/