//* collide.c - Collision detection handling routines //* The modified 'shifted grid' algorithm implemented here is //* based on an original article by Tom Moertel. //* //* Note: Collision detection is achieved using the bounding-box //* defined for each sprite with the bbXOff, bbYOff, bbW and bbH //* members of the SCTRL structure associated to each sprite. //* The bounded area basically defines the 'floor' space occupied //* by the given sprite, while taking into account the forced 3D viewpoint. //* System library headers #include #include //for debug printf #include //* Application headers #include "main.h" #include "procs.h" #include "firing.h" #include "preset.h" //* Sprite bounding-box intersection inline functions: #define IntersectX(s1, s2) \ ((s1) > (s2) ? (s1) - (s2) < (scp2->bbW) : (s2) - (s1) < (scp1->bbW)) #define IntersectY(s1, s2) \ ((s1) > (s2) ? (s1) - (s2) < (scp2->bbH) : (s2) - (s1) < (scp1->bbH)) #define IntersectH(s1, s2) \ ((s1->high > s2->high) ? (s1->high < (s2->high + s2->topOff)) \ : (s2->high < (s1->high + s1->topOff))) //* Constants to control collision detection grid enum { sectorSize = 128, //* Length of a sector's side in pixels sectorShift = sectorSize / 2, //* Sector offset for 4-shift operation findSector = 7, //* Right bit-shift to get sector from coord numXSectors = 6, //* Horizontal sectors in grid numYSectors = 6, //* Vertical sectors in grid numSectors = numXSectors * numYSectors //* Total sectors in grid }; //* Linked list heads - one for each sector in the collision detection grid static struct SCTRL *sectorHead[numXSectors][numYSectors]; //* Static function prototypes static void UpdateSectors(int shiftXOff, int shiftYOff); static void CheckAllSectors(void); static void CheckSectorCollisions(struct SCTRL *spriteList); //static void test(void); //* Static collision handler function prototypes static void bu_en(struct SCTRL *scp1, struct SCTRL *scp2); static void bb_en(struct SCTRL *scp1, struct SCTRL *scp2); static void bb_tv(struct SCTRL *scp1, struct SCTRL *scp2); static void en_en(struct SCTRL *scp1, struct SCTRL *scp2); static void bb_ba(struct SCTRL *scp1, struct SCTRL *scp2); static void en_ba(struct SCTRL *scp1, struct SCTRL *scp2); static void bu_ba(struct SCTRL *scp1, struct SCTRL *scp2); static void bb_up(struct SCTRL *scp1, struct SCTRL *scp2); static void bb_mi(struct SCTRL *scp1, struct SCTRL *scp2); static void mo_en(struct SCTRL *scp1, struct SCTRL *scp2); static void ba_mi(struct SCTRL *scp1, struct SCTRL *scp2); static void en_de(struct SCTRL *scp1, struct SCTRL *scp2); static void bu_mi(struct SCTRL *scp1, struct SCTRL *scp2); //* Initialize jump table - matrix array of pointers to all collision handlers. //* Note: this is indexed using the ID member of both collided sprites so //* that, for example, a collision of [1][2] or [2][1] calls the same handler. static void (*hit_func[10][10]) (struct SCTRL *, struct SCTRL *) = { {NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, //* Row/col 0: ID_SOFT_ICSAL (not used) {NULL, NULL, NULL, bb_en, bb_tv, bb_ba, bb_up, bb_mi, NULL, NULL}, //* Row/col 1: ID_BLITTER_BOY {NULL, NULL, NULL, bu_en, NULL, bu_ba, NULL, bu_mi, NULL, NULL}, //* Row/col 2: ID_BULLET {NULL, bb_en, bu_en, en_en, NULL, en_ba, NULL, NULL, mo_en, en_de}, //* Row/col 3: ID_ENEMY {NULL, bb_tv, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, //* Row/col 4: ID_TELEVATOR {NULL, bb_ba, bu_ba, en_ba, NULL, NULL, NULL, ba_mi, NULL, NULL}, //* Row/col 5: ID_BABY {NULL, bb_up, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL}, //* Row/col 6: ID_PICKUP {NULL, bb_mi, bu_mi, NULL, NULL, ba_mi, NULL, NULL, NULL, NULL}, //* Row/col 7: ID_MINE {NULL, NULL, NULL, mo_en, NULL, NULL, NULL, NULL, NULL, NULL}, //* Row/col 8: ID_MOONBALL {NULL, NULL, NULL, en_de, NULL, NULL, NULL, NULL, NULL, NULL} //* Row/col 9: ID_DECOY }; //* Functions for when an enemy is hit by one of the various bullet types static void normalBulletKill(struct SCTRL *scp1, struct SCTRL *scp2); static void rainDeathKill(struct SCTRL *scp1, struct SCTRL *scp2); static void fruitDropKill(struct SCTRL *scp1, struct SCTRL *scp2); //* Array of pointers to the above functions - indexed using bullet's .subID member static void (*kill_func[]) (struct SCTRL *, struct SCTRL *) = { NULL, //* Element 0 not used (0 is invalid bullet subID) normalBulletKill, rainDeathKill, fruitDropKill }; //* List of pointers to all sprites to take part in detection //static struct SCTRL *aliveScp[256+1]; //*------------------------------------------------------------------------------------------ //* ActOnCollisions //*------------------------------------------------------------------------------------- void ActOnCollisions(void) { register struct SCTRL **aliveScp = (struct SCTRL **) 0x1f800000; //* Put list in DCache (max 256 sprites) register int i = 0; register struct SCTRL *scp; //* First, make a list of pointers to all alive sprites. for (scp=aliveSprites; scp; scp=scp->aliveLinkF) if (scp->ID) //* Ignore 'soft' sprites aliveScp[i++] = scp; //* Add sprite to list aliveScp[i] = NULL; //* Terminating NULL pointer //* 4 shifts of the sectorization grid are necessary to catch any //* sprites that have collided across a sector boundary. //* Note: each x,y shift needs to be at least one 'sectorShift' in //* order to compensate for any sprites which are just off the top //* or left of the scroll area and so will have negative coords. UpdateSectors(sectorShift, sectorShift); CheckAllSectors(); UpdateSectors(sectorShift + sectorShift, sectorShift); CheckAllSectors(); UpdateSectors(sectorShift, sectorShift + sectorShift); CheckAllSectors(); UpdateSectors(sectorShift + sectorShift, sectorShift + sectorShift); CheckAllSectors(); } //*------------------------------------------------------------------------------------------ //* UpdateSectors //*------------------------------------------------------------------------------------- static void UpdateSectors(register int shiftXOff, register int shiftYOff) { register struct SCTRL **aliveScp = (struct SCTRL **) 0x1f800000; //* Point to DCache list register int i; register struct SCTRL *scp; register struct SCTRL **sh; sh = (struct SCTRL **) sectorHead; //* Reset sector linked list heads for (i=0; iwx + scp->bbXOff + shiftXOff) >> findSector] [(scp->wy - scp->bbYOff + shiftYOff) >> findSector]; scp->sectorLink = *sh; *sh = scp; } } //*------------------------------------------------------------------------------------------ //* CheckAllSectors //*------------------------------------------------------------------------------------- static void CheckAllSectors(void) { register int x, y; for (x=0; x static void CheckSectorCollisions(register struct SCTRL *spriteList) { register struct SCTRL *scp1, *scp2; register int s1x, s1y, s2x, s2y; //* Used in IntersectX/IntersectY macros //* Ignore sectors with 0 or only 1 sprite in them if (spriteList==NULL || spriteList->sectorLink==NULL) return; for (scp1=spriteList; scp1->sectorLink; scp1=scp1->sectorLink) { s1x = scp1->wx + scp1->bbXOff; s1y = scp1->wy - scp1->bbYOff; for (scp2=scp1->sectorLink; scp2; scp2=scp2->sectorLink) { //* Make preliminary checks to authorize //* a bounding box collision test... if (hit_func[scp1->ID][scp2->ID] && //* A collision handler exists scp1->alive && scp2->alive) { //* Both sprites still alive s2x = scp2->wx + scp2->bbXOff; s2y = scp2->wy - scp2->bbYOff; //* Check for bounding box collision... //* Note: intersect tests are in order //* of 'most likely to fail' for speed if (IntersectX(s1x, s2x) && //* Intersection on the X plane IntersectY(s1y, s2y) && //* Intersection on the Y plane IntersectH(scp1, scp2)) { //* Intersection in height //* These 2 objects have collided... //* Use both sprite IDs to index the function matrix //* and call appropriate collision handler. (*hit_func[scp1->ID][scp2->ID]) (scp1, scp2); //* Set debug flag scp1->imHit = scp2->imHit = 1; } } } } } /* //*------------------------------------------------------------------------------------------ //* test //*------------------------------------------------------------------------------------- static void test(void) { register int i,loop,test1,test2; register struct SCTRL *scp; register struct SCTRL **sh; VSync(0); test1 = VSync(1); for (loop=100; loop; --loop) { sh = (struct SCTRL **) sectorHead; for (i=0; i static void bu_en(register struct SCTRL *scp1, register struct SCTRL *scp2) { register struct SCTRL *temp; //* Ensure enemy is scp1 and bullet is scp2 if (scp1->ID==ID_BULLET) temp = scp2, scp2 = scp1, scp1 = temp; //* Reduce alive count for this enemy type & total --enemyAlive[scp1->subID][0]; --enemyAlive[TOTAL_ALIVE][0]; //* Increment enemy kill count for pickup management ++killCount; //* Call appropriate function for this bullet type (*kill_func[scp2->subID]) (scp1, scp2); } //*------------------------------------------------------------------------------------------ //* normalBulletKill //*------------------------------------------------------------------------------------- static void normalBulletKill(register struct SCTRL *scp1, register struct SCTRL *scp2) { static u_int fl[7][2] = { //* Fireball lock pairs (order:frameLock,currLock) {LOCK_NOFRAMES, LOCK_NOFRAMES}, {0xaaaaeeee, 0xeeefffff}, {0xeeeeeeee, LOCK_NOFRAMES}, {LOCK_ALTFRAMES, 0xeeeeffff}, {LOCK_NOFRAMES, LOCK_NOFRAMES}, //* Starts repeating here! {0xaaaaeeee, 0xeeefffff}, {0xeeeeeeee, LOCK_NOFRAMES} }; static int idx=0; register int enemyX, enemyY; register int i; register struct SCTRL *scp, *startp; //* Note: enemy is scp1 and bullet/moonball is scp2 enemyX = scp1->wx; //* Get killed enemy's world X coord enemyY = scp1->wy; //* Get killed enemy's world Y coord //* Get pointer to linked sprites (no coin in MODE_SURVIVE) startp = scp = GetSprite(gameType == MODE_MISSION ? 6 : 5); //* Initialize common sprite settings for the 4 ICSAL explosion fireball sprites for (i=4; i; scp=scp->freeLink, i--) { PresetSprite(scp, PRESET_EXPLODE_FIREBALL); scp->wx = enemyX + 8; scp->wy = enemyY - 2; } //* Initialize sprite settings for the 1 ICSAL explosion body sprite PresetSprite(scp, PRESET_EXPLODE_BODY); scp->wx = enemyX; scp->wy = enemyY; scp->bbH = scp1->bbH; scp->bbYOff = scp1->bbYOff; //* Initialize sprite settings for the 1 coin sprite if (gameType == MODE_MISSION) { //* No coin in SURVIVE mode! scp = scp->freeLink;//GetSprite(1); PresetSprite(scp, PRESET_EXPLODE_COIN); scp->wx = enemyX + 8; scp->wy = enemyY - 5; //* Set random coin colour scp->spriteLink->cy += (i=rand()%3); scp->subID = i+1; //* PICKUP_x_COIN } //* Initialize differing sprite control structure members i = (scp=startp)->speed; if (scp2->accelX && scp2->accelY) { //* Bullet moving diagonally so initiate '+' explode scp->accelX = i; scp->accelY = 0; //* Right movement scp->frameLock = fl[idx][0]; scp->currLock = fl[idx][1]; scp->dir = DIR_RIGHT; scp = scp->freeLink; scp->accelX = 0; scp->accelY = -i; //* Up movement scp->frameLock = fl[idx+1][0]; scp->currLock = fl[idx+1][1]; scp->dir = DIR_UP; scp = scp->freeLink; scp->accelX = -i; scp->accelY = 0; //* Left movement scp->frameLock = fl[idx+2][0]; scp->currLock = fl[idx+2][1]; scp->dir = DIR_LEFT; scp = scp->freeLink; scp->accelX = 0; scp->accelY = i; //* Down movement scp->frameLock = fl[idx+3][0]; scp->currLock = fl[idx+3][1]; scp->dir = DIR_DOWN; } else { //* Bullet moving horizontally/vertically so initiate 'X' explode scp->accelX = i; scp->accelY = i; //* Down-right movement scp->frameLock = fl[idx][0]; scp->currLock = fl[idx][1]; scp->dir = DIR_DOWN_RIGHT; scp = scp->freeLink; scp->accelX = i; scp->accelY = -i; //* Up-right movement scp->frameLock = fl[idx+1][0]; scp->currLock = fl[idx+1][1]; scp->dir = DIR_UP_RIGHT; scp = scp->freeLink; scp->accelX = -i; scp->accelY = -i; //* Up-left movement scp->frameLock = fl[idx+2][0]; scp->currLock = fl[idx+2][1]; scp->dir = DIR_UP_LEFT; scp = scp->freeLink; scp->accelX = -i; scp->accelY = i; //* Down-left movement scp->frameLock = fl[idx+3][0]; scp->currLock = fl[idx+3][1]; scp->dir = DIR_DOWN_LEFT; } //* Change currLock index - accounting for wrap-around (idx==0x03) ? idx=0 : ++idx; if (scp2->ID == ID_BULLET) { //* Kill both bullet blur sprites if they're active yet //* Note: the 3 linked bullet sprites MUST be destroyed in reverse //* order else the .freeLink chain would be broken if (scp2->freeLink->alive) { //* The 2 bullet blur sprites are active... AliveLinkOut(scp2->freeLink->freeLink); AliveLinkOut(scp2->freeLink); } else { //* The 2 bullet blur sprites aren't active yet... PutSprite(scp2->freeLink->freeLink); PutSprite(scp2->freeLink); } } if (scp1->subID==ENEMY_UFO) { //* Enemy is a UFO... scp = GetSprite(1); PresetSprite(scp, PRESET_SHOT_UFO); scp->wx = enemyX; scp->wy = enemyY; scp->high = scp1->high; scp->freeLink = startp; //* Link unseen explosion/coin sprites to UFO AliveLinkIn(scp); if (scp=scp1->target) { //* UFO was in process of laying mine so... PutGrid(scp->wx, scp->wy); //* Make mine grid position available PutSprite(scp); //* Return unused mine sprite to pool } } else { //* Enemy is a ghost... for (scp=startp; scp; scp=scp->freeLink) AliveLinkIn(scp); //* Display coin/explosion sprites immediately } AliveLinkOut(scp2); //* Kill actual bullet/moonball sprite AliveLinkOut(scp1); //* Kill enemy sprite } //*------------------------------------------------------------------------------------------ //* rainDeathKill //*------------------------------------------------------------------------------------- static void rainDeathKill(register struct SCTRL *scp1, register struct SCTRL *scp2) { register int i; register struct SCTRL *scp; register GsSPRITE *sp; int aX, aY; //* Note: enemy is scp1 and bullet is scp2 scp = GetSprite(4); //* Get pointer to 1st of 4 linked sprites sp = scp->spriteLink; //* Set pointer to 1st sprite's GsSPRITE aX = (scp2->accelX?(scp2->accelX<0?-1:1):0); aY = (scp2->accelY?(scp2->accelY<0?-1:1):0); //* //* Initialize sprite settings for the 3 RGB component sprites //* for (i=3; i; sp=(scp=scp->freeLink)->spriteLink, i--) { sp->attribute = (A_BRIGHT_ON + A_4BIT_CLUT + A_ROTATE_OFF + A_TRATE_2 + A_TRANS_ON + A_DISPLAY_ON); sp->w = sp->h = 32; sp->tpage = scp1->spriteLink->tpage; sp->u = scp1->spriteLink->u; sp->v = scp1->spriteLink->v; sp->cx = timData[TIM_EN_WCLUT].cx; sp->cy = timData[TIM_EN_WCLUT].cy; sp->r = (i==3?128:0); sp->b = (i==2?128:0); sp->g = (i==1?128:0); //* Initialize common sprite control structure members scp->wx = scp1->wx; scp->wy = scp1->wy; //scp->speed = i; //scp->bbW = 12; //* Set BB params for ICSAL handle_sprite_over_wall command //scp->bbH = 8; //scp->bbXOff = 2; //scp->bbYOff = 8; scp->high = scp1->high; scp->shadStat = 0; //scp->shadHW = 8; scp->isLight = 1; //scp->shadLink->u = SHAD_16WIDE; //scp->shadLink->w = 16; scp->currLock = RunNextTime(LOCK_NOFRAMES); //* Initialize sprite's ICSAL procedure pointer if (i==3) { scp->ICSALop = rainbowDeathR; scp->accelX = aX<<2; scp->accelY = aY<<2; } else if (i==2) { scp->ICSALop = rainbowDeathB; scp->accelX = aX<<3; scp->accelY = aY<<3; } else { scp->ICSALop = rainbowDeathG; scp->accelX = aX; scp->accelY = aY; } //* Make sprite displayable AliveLinkIn(scp); } //* //* Initialize sprite settings for the 1 coin sprite //* PresetSprite(scp, PRESET_EXPLODE_COIN); scp->wx = scp1->wx + 8; scp->wy = scp1->wy - 5; scp->ICSALop = coinEnemy2; //* Set random coin colour scp->spriteLink->cy += (i=rand()%3); scp->subID = i+1; //* PICKUP_x_COIN //* Make sprite displayable AliveLinkIn(scp); //* Kill both bullet blur sprites if they're active yet //* Note: the 3 linked bullet sprites MUST be destroyed in reverse //* order else the .freeLink chain would be broken if (scp2->freeLink->alive) { //* The 2 bullet blur sprites are active... AliveLinkOut(scp2->freeLink->freeLink); AliveLinkOut(scp2->freeLink); } else { //* The 2 bullet blur sprites aren't active yet... PutSprite(scp2->freeLink->freeLink); PutSprite(scp2->freeLink); } if (scp1->subID==ENEMY_UFO && (scp=scp1->target)) { //* Enemy is a UFO holding a mine... PutGrid(scp->wx, scp->wy); //* Make mine grid position available PutSprite(scp); //* Return unused mine sprite to pool } AliveLinkOut(scp2); //* Kill actual bullet sprite AliveLinkOut(scp1); //* Kill enemy sprite } //*------------------------------------------------------------------------------------------ //* fruitDropKill //*------------------------------------------------------------------------------------- static void fruitDropKill(register struct SCTRL *scp1, register struct SCTRL *scp2) { register int i; register struct SCTRL *scp; register GsSPRITE *sp; //* Note: enemy is scp1 and bullet is scp2 if (scp1->subID==ENEMY_UFO) { //* Initialize sprite settings for falling UFO scp = GetSprite(1); sp = scp->spriteLink; sp->attribute = (A_BRIGHT_ON + A_4BIT_CLUT + A_ROTATE_ON + A_TRATE_1 + A_TRANS_OFF + A_DISPLAY_ON); sp->w = sp->h = 32; sp->tpage = GetTPage(0, 0, timData[TIM_UFO].px, timData[TIM_UFO].py); sp->u = 0; sp->v = timData[TIM_UFO].py; sp->cx = timData[TIM_UFO].cx; sp->cy = timData[TIM_UFO].cy; sp->mx = sp->my = 16; scp->dir = 0; //* Used in update_ufo_fall as array index! scp->wx = scp1->wx; scp->wy = scp1->wy; scp->high = scp1->high; scp->wxMid = scp1->wxMid; scp->wyMid = scp1->wyMid; scp->bbW = scp1->bbW; scp->bbH = scp1->bbH-4; //* Shrink BB to allow fruit to display over UFO scp->bbXOff = scp1->bbXOff; scp->bbYOff = scp1->bbYOff-2; scp->ICSALop = ufoSplat; AliveLinkIn(scp); if (scp1->subID==ENEMY_UFO && (scp=scp1->target)) { //* Enemy is a UFO holding a mine... PutGrid(scp->wx, scp->wy); //* Make mine grid position available PutSprite(scp); //* Return unused mine sprite to pool } } else { //* Initialize sprite settings for scared monster scp = GetSprite(1); sp = scp->spriteLink; sp->attribute = (A_BRIGHT_ON + A_4BIT_CLUT + A_ROTATE_OFF + A_TRATE_1 + A_TRANS_ON + A_DISPLAY_OFF); sp->w = sp->h = 32; sp->tpage = GetTPage(0, 0, timData[TIM_FRUIT_SPLAT].px, timData[TIM_FRUIT_SPLAT].py); sp->u = 0; sp->v = timData[TIM_FRUIT_SPLAT].py; sp->cx = scp1->spriteLink->cx; sp->cy = scp1->spriteLink->cy; scp->wx = scp1->wx; scp->wy = scp1->wy; scp->wxMid = scp1->wxMid; scp->wyMid = scp1->wyMid; scp->bbW = scp1->bbW; //* Set BB params for ICSAL handle_sprite_over_wall command scp->bbH = scp1->bbH-4; //* Shrink BB to allow fruit to display over monster scp->bbXOff = scp1->bbXOff; scp->bbYOff = scp1->bbYOff-2; scp->shadStat = 0; scp->ICSALop = enemySplat; AliveLinkIn(scp); //* Initialize sprite settings for white flash monster (displayed for 2 frames only!) scp = GetSprite(1); sp = scp->spriteLink; sp->attribute = (A_BRIGHT_ON + A_4BIT_CLUT + A_ROTATE_OFF + A_TRATE_1 + A_TRANS_OFF + A_DISPLAY_ON); sp->w = sp->h = 32; sp->tpage = GetTPage(0, 0, timData[TIM_FRUIT_SPLAT].px, timData[TIM_FRUIT_SPLAT].py); sp->u = 0; sp->v = timData[TIM_FRUIT_SPLAT].py; sp->cx = timData[TIM_MASKCLUT].cx; sp->cy = timData[TIM_MASKCLUT].cy; scp->shadLink->attribute = (A_BRIGHT_ON + A_4BIT_CLUT + A_ROTATE_OFF + A_TRATE_2 + A_TRANS_ON + A_DISPLAY_ON); scp->wx = scp1->wx; scp->wy = scp1->wy; scp->wxMid = scp1->wxMid; scp->wyMid = scp1->wyMid; scp->bbW = scp1->bbW; //* Set BB params for ICSAL handle_sprite_over_wall command scp->bbH = scp1->bbH; scp->bbXOff = scp1->bbXOff; scp->bbYOff = scp1->bbYOff; scp->isLight = 1; scp->ICSALop = enemyFlash; AliveLinkIn(scp); } //* Initialize falling fruit sprite scp = GetSprite(1); PresetSprite(scp, PRESET_FRUIT_APPLE+(*psdcnt & 0x00000007)); scp->wx = scp1->wx; scp->wy = (scp1->wyMid-(scp->bbH>>1))+scp->bbYOff; //* Centre fruit on enemy scp->wxMid = scp1->wxMid; scp->wyMid = scp1->wyMid; AliveLinkIn(scp); //* Kill both bullet blur sprites if they're active yet //* Note: the 3 linked bullet sprites MUST be destroyed in reverse //* order else the .freeLink chain would be broken if (scp2->freeLink->alive) { //* The 2 bullet blur sprites are active... AliveLinkOut(scp2->freeLink->freeLink); AliveLinkOut(scp2->freeLink); } else { //* The 2 bullet blur sprites aren't active yet... PutSprite(scp2->freeLink->freeLink); PutSprite(scp2->freeLink); } AliveLinkOut(scp2); //* Kill actual bullet sprite AliveLinkOut(scp1); //* Kill enemy sprite } //*------------------------------------------------------------------------------------------ //* bb_en - 'blitter boy hits enemy' collision handler function //*------------------------------------------------------------------------------------- static void bb_en(register struct SCTRL *scp1, register struct SCTRL *scp2) { struct SCTRL *temp; //* Ensure Blitter Boy is scp1 and enemy is scp2 if (scp1->ID==ID_ENEMY) temp = scp2, scp2 = scp1, scp1 = temp; if (gameType == MODE_MISSION) { if (!flashCnt && //* BB is invincible while flashing !scp1->unintICSAL) { //* Can be interrupted (i.e. not on TV) SsUtKeyOn(0, SFX_SAY_OW, 0, 60, 0, 127, 127); //* Release any following babies while (scp1!=lastInLine) { temp = lastInLine; lastInLine = lastInLine->target; //* Will be BB at end temp->target = NULL; temp->ICSALop = babyWander; //* Make them wander again temp->subID = BABY_WANDER; temp->frameLock = LOCK_3FRAMES; temp->currLock = RunNextTime(temp->frameLock); } if (polyStat) //* Eject any loaded PolyCart EjectPolyCart(); bullLoop = ((bullRange=2)<<3); //* Range returns to minimum flashCnt = 60; //* BB flashes & is invincible for 1 sec energyAdd -= energyHit; //* Authorize energy reduction } } else { //* In SURVIVE mode... if (!flashCnt) { //* Only do this for 1st hit! SsUtKeyOn(0, SFX_SAY_OW, 0, 60, 0, 127, 127); flashCnt = 1; } } //* Consider enemy if (!scp2->unintICSAL) { //* Set enemy's new direction to opposite direction switch (scp2->dir = (scp2->dir+4) & 0x07) { case DIR_DOWN_LEFT: scp2->accelX = -scp2->speed; scp2->accelY = scp2->speed; break; case DIR_UP_LEFT: scp2->accelX = -scp2->speed; scp2->accelY = -scp2->speed; break; case DIR_DOWN: scp2->accelX = 0; scp2->accelY = scp2->speed; break; case DIR_DOWN_RIGHT: scp2->accelX = scp2->speed; scp2->accelY = scp2->speed; break; case DIR_UP_RIGHT: scp2->accelX = scp2->speed; scp2->accelY = -scp2->speed; break; case DIR_UP: scp2->accelX = 0; scp2->accelY = -scp2->speed; break; case DIR_LEFT: scp2->accelX = -scp2->speed; scp2->accelY = 0; break; case DIR_RIGHT: scp2->accelX = scp2->speed; scp2->accelY = 0; break; } scp2->ICSALop = (scp2->subID == ENEMY_UFO ? ufoBump : enemyBump); scp2->unintICSAL = 1; } } //*------------------------------------------------------------------------------------------ //* bb_tv - 'blitter boy hits TeleVator' collision handler function //*------------------------------------------------------------------------------------- static void bb_tv(register struct SCTRL *scp1, register struct SCTRL *scp2) { struct SCTRL *temp; //* Ensure Blitter Boy is scp1 and TV is scp2 if (scp1->ID==ID_TELEVATOR) temp = scp2, scp2 = scp1, scp1 = temp; if (gameType==MODE_MISSION) { if (scp1->subID==BOY_GAME_OVER && tvStat==TV_OFF) { //* No energy left - GAME OVER! if (messHudScp) { //* An OSD message HUD exists... if (messHudScp->freeLink) { //* Ensures this is done once! messHudScp = messHudScp->freeLink; //* Kill Message HUD text sprite messHudScp->ICSALop = messHudClose; //* Set blank HUD box to close } } scp2->ICSALop = turnOnTV; } else if (lastInLine!=scp1 && tvStat==TV_OFF) { //* A baby is following BB scp2->ICSALop = turnOnTV; scp1->unintICSAL = 1; //* Turn off enemy collision detection scp1->frameCount = 1; //* For immediate movement next time scp1->currFrame = 0; //* Reset to 'standing still' frame sprt->u = (sprt->u < 128 ? 0 : 128); } else if (ok2beam && !scp1->unintICSAL) { //* Level has been completed... if (scp1->ICSALop) { //* BB is in middle of jump... //scp1->unintICSAL = 1; //* Turn off enemy collision detection scp1->frameCount = 1; //* For immediate movement next time scp1->currFrame = 0; //* Reset to 'standing still' frame sprt->u = (sprt->u < 128 ? 0 : 128); //* Reset to 1st frame of animation scp1->subID = BOY_LEVEL_UP; //* Tells check_landing_action what to do! } else { //* BB not in middle of jump... scp1->target = scp2; scp1->ICSALop = gotoTV; //* Set BB to move into position for beam-up } messHudScp = messHudScp->freeLink; //* Kill Message HUD text sprite messHudScp->ICSALop = messHudClose; //* Set blank HUD box to close scp2->ICSALop = turnOnTV; scp1->unintICSAL = 1; //* Turn off enemy collision detection } } else { if (scp1->subID==BOY_GAME_OVER && tvStat==TV_OFF) //* GAME OVER! scp2->ICSALop = turnOnTV; } } //*------------------------------------------------------------------------------------------ //* en_en - 'enemy hits enemy' collision handler function //*------------------------------------------------------------------------------------- static void en_en(register struct SCTRL *scp1, register struct SCTRL *scp2) { struct SCTRL *temp; register GsSPRITE *sp=scp1->spriteLink; int dir1=scp1->dir; //* Consider 1st hit enemy if (!scp1->unintICSAL) { //* Set enemy's new direction to right angles of scp2's direction switch (scp1->dir = (scp2->dir+2) & 0x07) { case DIR_DOWN_LEFT: scp1->accelX = -scp1->speed; scp1->accelY = scp1->speed; break; case DIR_UP_LEFT: scp1->accelX = -scp1->speed; scp1->accelY = -scp1->speed; break; case DIR_DOWN: scp1->accelX = 0; scp1->accelY = scp1->speed; break; case DIR_DOWN_RIGHT: scp1->accelX = scp1->speed; scp1->accelY = scp1->speed; break; case DIR_UP_RIGHT: scp1->accelX = scp1->speed; scp1->accelY = -scp1->speed; break; case DIR_UP: scp1->accelX = 0; scp1->accelY = -scp1->speed; break; case DIR_LEFT: scp1->accelX = -scp1->speed; scp1->accelY = 0; break; case DIR_RIGHT: scp1->accelX = scp1->speed; scp1->accelY = 0; break; } scp1->ICSALop = (scp1->subID == ENEMY_UFO ? ufoBump : enemyBump); scp1->unintICSAL = 1; } else if (scp2->unintICSAL && (scp1->dir == scp2->dir)) { //* Here, both collided enemies are running uninterruptible //* procs and travelling in the same direction (yuk!) so //* allow them to split up //scp2->unintICSAL = 0; } //* Consider 2nd hit enemy if (!scp2->unintICSAL) { //* Set enemy's new direction to right angles of scp1's direction switch (scp2->dir = (dir1+2) & 0x07) { case DIR_DOWN_LEFT: scp2->accelX = -scp2->speed; scp2->accelY = scp2->speed; break; case DIR_UP_LEFT: scp2->accelX = -scp2->speed; scp2->accelY = -scp2->speed; break; case DIR_DOWN: scp2->accelX = 0; scp2->accelY = scp2->speed; break; case DIR_DOWN_RIGHT: scp2->accelX = scp2->speed; scp2->accelY = scp2->speed; break; case DIR_UP_RIGHT: scp2->accelX = scp2->speed; scp2->accelY = -scp2->speed; break; case DIR_UP: scp2->accelX = 0; scp2->accelY = -scp2->speed; break; case DIR_LEFT: scp2->accelX = -scp2->speed; scp2->accelY = 0; break; case DIR_RIGHT: scp2->accelX = scp2->speed; scp2->accelY = 0; break; } scp2->ICSALop = (scp2->subID == ENEMY_UFO ? ufoBump : enemyBump); scp2->unintICSAL = 1; SsUtKeyOn(0, SFX_ENEMY_EXPLODE1, 0, 108, 0, 127, 127); } } //*------------------------------------------------------------------------------------------ //* bb_ba - 'blitter boy hits baby' collision handler function //*------------------------------------------------------------------------------------- static void bb_ba(register struct SCTRL *scp1, register struct SCTRL *scp2) { struct SCTRL *temp; register struct SCTRL *scp; register GsSPRITE *sp; register GsIMAGE *imp; //* Ensure Blitter Boy is scp1 and baby is scp2 if (scp1->ID==ID_BABY) temp = scp2, scp2 = scp1, scp1 = temp; if (scp1->unintICSAL) return; //* Ignore - BB is using TV if (scp2->subID==BABY_WANDER && !flashCnt) { scp2->ICSALop = babyFollow; scp2->target = lastInLine; scp2->subID = BABY_FOLLOW; lastInLine = scp2; //* Make this baby the one to be followed scp2->frameLock = scp1->frameLock; scp2->currLock = scp1->currLock; //scp2->unintICSAL = 1; } else return; //* Baby already following/crying or BB is flashing scp = GetSprite(1); //* Get a heart sprite sp = scp->spriteLink; //* Set pointer to actual GsSPRITE imp = &timData[TIM_HEART]; //* Point to heart image data //* //* Initialize heart sprite settings //* sp->attribute = (A_BRIGHT_ON + A_4BIT_CLUT + A_ROTATE_ON + A_TRATE_2 + A_TRANS_OFF + A_DISPLAY_ON); sp->w = sp->h = 16; sp->tpage = GetTPage(0, 1, imp->px, imp->py); //sp->u = 0; sp->v = imp->py; sp->cx = imp->cx; sp->cy = imp->cy; scp->bbW = 8; //* Set BB params for shadow positioning scp->bbH = scp2->bbH+2; //* and to ensure heart is rendered scp->bbXOff = 4; //* after/on-top-of the baby sprite scp->bbYOff = (scp2->bbH >> 1); scp->wx = scp2->wx + 8; scp->wy = scp2->wy - (scp2->bbYOff - (scp2->bbH >> 1)); scp->speed = 1; scp->high = 10; scp->shadHW = 8; scp->shadLink->u = SHAD_16WIDE; scp->shadLink->w = 16; //scp->frameLock = LOCK_ALTFRAMES; scp->currLock = RunNextTime(scp->frameLock); //* No initialization ICSAL call needed //* Initialize sprite's ICSAL procedure pointer scp->ICSALop = babyHeart; SsUtKeyOn(0, SFX_WOOHOO, 0, 60, 0, 127, 127); //* Make sprite displayable AliveLinkIn(scp); } //*------------------------------------------------------------------------------------------ //* en_ba - 'enemy hits baby' collision handler function //*------------------------------------------------------------------------------------- static void en_ba(register struct SCTRL *scp1, register struct SCTRL *scp2) { struct SCTRL *temp; //* Ensure enemy is scp1 and baby is scp2 if (scp1->ID==ID_BABY) temp = scp2, scp2 = scp1, scp1 = temp; //* Ignore hit if TV is being used with this baby if (scp2->subID!=BABY_WANDER && sCtrl->unintICSAL) return; //* Make baby cry if not already crying if (scp2->subID!=BABY_CRY && !babyStrong) { if (scp2->subID==BABY_FOLLOW) { //* Hit baby is in chain... while (scp2!=lastInLine) { //* Remove all babies behind hit baby from chain temp = lastInLine; lastInLine = lastInLine->target; temp->target = NULL; temp->ICSALop = babyWander; //* Make them wander again temp->subID = BABY_WANDER; temp->frameLock = LOCK_3FRAMES; temp->currLock = RunNextTime(temp->frameLock); } lastInLine = scp2->target; //* Remove hit baby from chain scp2->target = NULL; } scp2->ICSALop = babyCry; scp2->subID = BABY_CRY; scp2->unintICSAL = 1; scp2->bbW = 18; //* Needed for shadow positioning scp2->bbH = 8; scp2->bbXOff = 7; scp2->bbYOff = 8; scp2->spriteLink->u = 0; scp2->spriteLink->v = 128; //* Centre baby sprite on central world coords scp2->wx = scp2->wxMid - (scp2->bbXOff + (scp2->bbW >> 1)); scp2->wy = scp2->wyMid + (scp2->bbYOff - (scp2->bbH >> 1)); } //* Consider enemy if (!scp1->unintICSAL) { //* Set new random direction for enemy switch (scp1->dir = (rand()%8)) { case DIR_DOWN_LEFT: scp1->accelX = -scp1->speed; scp1->accelY = scp1->speed; break; case DIR_UP_LEFT: scp1->accelX = -scp1->speed; scp1->accelY = -scp1->speed; break; case DIR_DOWN: scp1->accelX = 0; scp1->accelY = scp1->speed; break; case DIR_DOWN_RIGHT: scp1->accelX = scp1->speed; scp1->accelY = scp1->speed; break; case DIR_UP_RIGHT: scp1->accelX = scp1->speed; scp1->accelY = -scp1->speed; break; case DIR_UP: scp1->accelX = 0; scp1->accelY = -scp1->speed; break; case DIR_LEFT: scp1->accelX = -scp1->speed; scp1->accelY = 0; break; case DIR_RIGHT: scp1->accelX = scp1->speed; scp1->accelY = 0; break; } scp1->ICSALop = (scp1->subID == ENEMY_UFO ? ufoBump : enemyBump); scp1->unintICSAL = 1; } } //*------------------------------------------------------------------------------------------ //* bu_ba - 'bullet hits baby' collision handler function //*------------------------------------------------------------------------------------- static void bu_ba(register struct SCTRL *scp1, register struct SCTRL *scp2) { struct SCTRL *temp; register struct SCTRL *scp; register GsSPRITE *sp; //* Ensure bullet is scp1 and baby is scp2 if (scp1->ID==ID_BABY) temp = scp2, scp2 = scp1, scp1 = temp; //* Make baby cry if not already crying if (scp1->subID==BULLET_NORMAL && scp2->subID!=BABY_CRY) { if (scp2->subID==BABY_FOLLOW) { //* Hit baby is in chain... while (scp2!=lastInLine) { //* Remove all babies behind hit baby from chain temp = lastInLine; lastInLine = lastInLine->target; temp->target = NULL; temp->ICSALop = babyWander; //* Make them wander again temp->subID = BABY_WANDER; temp->frameLock = LOCK_3FRAMES; temp->currLock = RunNextTime(temp->frameLock); } lastInLine = scp2->target; //* Remove hit baby from chain scp2->target = NULL; } scp2->ICSALop = babyCry; scp2->subID = BABY_CRY; scp2->unintICSAL = 1; scp2->bbW = 18; //* Needed for shadow positioning scp2->bbH = 8; scp2->bbXOff = 7; scp2->bbYOff = 8; scp2->spriteLink->u = 0; scp2->spriteLink->v = 128; //* Centre baby sprite on central world coords scp2->wx = scp2->wxMid - (scp2->bbXOff + (scp2->bbW >> 1)); scp2->wy = scp2->wyMid + (scp2->bbYOff - (scp2->bbH >> 1)); } //* //* Expire bullet //* scp = GetSprite(1); //* Get a 'bullet expire' sprite sp = scp->spriteLink; //* Point to actual sprite sp->attribute = (A_4BIT_CLUT + A_BRIGHT_ON + A_ROTATE_OFF + A_TRATE_2 + A_TRANS_ON + A_DISPLAY_ON); sp->w = sp->h = 16; sp->tpage = GetTPage(0, 1, timData[TIM_BULLBANG].px, timData[TIM_BULLBANG].py); sp->u = 0; sp->v = 16; sp->cx = timData[TIM_BULLBANG].cx; sp->cy = timData[TIM_BULLBANG].cy; scp->shadStat = 0; //* Shadow off //scp2->frameLock = scp2->currLock = LOCK_ALTFRAMES; scp->ICSALop = bulletExpire; scp->wx = scp1->wxMid - 8; scp->wy = scp1->wy + 1; //* +1 so is drawn over bullet scp->high = scp1->high-(8-(scp1->spriteLink->h>>1))+1; scp->currLock = RunNextTime(LOCK_NOFRAMES); AliveLinkIn(scp); //* Activate 'expire' service sprite //* Kill both bullet blur sprites //* Note: the 3 linked bullet sprites MUST be destroyed in reverse //* order else the .freeLink chain would be broken if (scp1->freeLink->alive) { //* The 2 bullet blur sprites are active... AliveLinkOut(scp1->freeLink->freeLink); AliveLinkOut(scp1->freeLink); } else { //* The 2 bullet blur sprites aren't active yet... PutSprite(scp1->freeLink->freeLink); PutSprite(scp1->freeLink); } AliveLinkOut(scp1); //* Kill actual bullet sprite } static u_int FRbonusVal[8][10] = { //* Fruit bonus score multiplication LUT { 100, 200, 300, 400, 500, 600, 700, 800, 900, 1000}, //* 100pt cherry { 200, 400, 600, 800, 1000, 1200, 1400, 1600, 1800, 2000}, //* 200pt strawberry { 300, 600, 900, 1200, 1500, 1800, 2100, 2400, 2700, 3000}, //* 300pt orange { 400, 800, 1200, 1600, 2000, 2400, 2800, 3200, 3600, 4000}, //* 400pt lemon { 500, 1000, 1500, 2000, 2500, 3000, 3500, 4000, 4500, 5000}, //* 500pt pear { 600, 1200, 1800, 2400, 3000, 3600, 4200, 4800, 5400, 6000}, //* 600pt apple { 700, 1400, 2100, 2800, 3500, 4200, 4900, 5600, 6300, 7000}, //* 700pt grape { 800, 1600, 2400, 3200, 4000, 4800, 5600, 6400, 7200, 8000} //* 800pt melon }; //*------------------------------------------------------------------------------------------ //* bb_up - 'blitter boy hits a pickup' collision handler function //*------------------------------------------------------------------------------------- static void bb_up(register struct SCTRL *scp1, register struct SCTRL *scp2) { register struct SCTRL *temp; register struct SCTRL *scp; register GsSPRITE *sp; register int i, n; //* Ensure Blitter Boy is scp1 and pickup is scp2 if (scp1->ID==ID_PICKUP) temp = scp2, scp2 = scp1, scp1 = temp; if (scp1->unintICSAL) //* BB cant get pickups when dead return; if (scp2->subID & 0x100) { //* Pickup is a PolyCart... switch (scp2->subID) { case CART_RAIN_DEATH: FireFunc = RainDeathFire; fireDelay = 4; bullLoop = (bullRange<<3); ammoUse = ONE>>1; MakeAmmoBarText("RAINBOW DEATH"); break; case CART_FRUIT_DROP: FireFunc = FruitDropFire; fireDelay = 8; bullLoop = (bullRange<<3); ammoUse = ONE; MakeAmmoBarText("FRUIT DROPS"); break; } //* Set full, red2yellow ammo bar ammoLeft = BAR_MAX; for (i=0; i<8; i++) { ammoBarp[i].x1 = AMMO_BAR_X+BAR_MAX-1; ammoBarp[i].r0 = 255; ammoBarp[i].g0 = 0; ammoBarp[i].b0 = 0; ammoBarp[i].r1 = 0; ammoBarp[i].g1 = 255; ammoBarp[i].b1 = 0; } SsUtKeyOn(0, SFX_CART, 0, 60, 0, 127, 127); polyStat = 1; PutGrid(scp2->wx, scp2->wy); //* Make PolyCart grid position available scp2->freeLink->ICSALop++; //* Authorize linked HUD box to close AliveLinkOut(scp2); //* Kill PolyCart } else if (scp2->subID & 0x200) { //* Pickup is a fruit... //* Initialize collected fruit 'splash' sprite sp = (scp = GetSprite(1))->spriteLink; PresetSprite(scp, PRESET_FRUIT_SPLASH); sp->cx = scp2->spriteLink->cx; //* Use same CLUT as fruit! sp->cy = scp2->spriteLink->cy; scp->wx = scp2->wx; scp->wy = scp2->wy; scp->wxMid = scp2->wxMid; scp->wyMid = scp2->wyMid; AliveLinkIn(scp); if ((n=(scp2->subID & 0xFF)) == lastFruit) {//* This fruit matches the last fruit collected... currScore += FRbonusVal[n][bonusMulty]; //* Add correct bonus to score //* Initialize bonus score sprite scp = GetSprite(2); PresetSprite(scp, PRESET_FRUIT_BONUS); scp->spriteLink->v += (n<<4); scp->wx = scp2->wx; scp->wy = scp2->wy+600; scp->wxMid = scp2->wxMid; scp->wyMid = scp2->wyMid; AliveLinkIn(scp); //* Initialize bonus multiplier sprite scp = scp->freeLink; PresetSprite(scp, PRESET_FRUIT_MULTY); scp->spriteLink->v += (bonusMulty<<4); scp->wx = scp2->wx+32; scp->wy = scp2->wy+600; scp->wxMid = scp2->wxMid; scp->wyMid = scp2->wyMid; AliveLinkIn(scp); } lastFruit = n; //* Record code of most recently collected fruit if (BAR_MAX-energyLeft) energyAdd += (10<<12); //* Restore some energy! SsUtKeyOn(0, SFX_GUN_RAINBOW, 0, 48, 0, 127, 127); AliveLinkOut(scp2); //* Kill actual fruit sprite } else if (scp2->subID & 0x400) { //* Pickup is a goodie... switch (scp2->subID) { case PICKUP_MILKBOT: //* Protects babies from bullets & enemies temp = &sCtrl[SPRT_BABY6]; babyStrong = 1; for (i=6; i; temp--, i--) if (temp->alive) { scp = GetSprite(1); PresetSprite(scp, PRESET_POWFLASH); scp->target = temp; //* Point to baby for update_powflash_xy AliveLinkIn(scp); } SsUtKeyOn(0, SFX_BONUS, 0, 52, 0, 127, 127); break; case PICKUP_MOONRING: //* Produce ring of 8 orbiting balls... for (i=0; i<8; i++) if (moonScp[i]->ID == ID_MOONBALL) AliveLinkOut(moonScp[i]); //* Kill any old existing moons scp = GetSprite(8); for (i=0, n=0; scp; scp=scp->freeLink, i+=20) { PresetSprite(scp, PRESET_MOONBALL1+n); moonScp[n] = scp; scp->dir = n++; //* For handle_sprite_over_wall scp->ICSALdata = orbitBall+i; //* Point to correct data AliveLinkIn(scp); } SsUtKeyOn(0, SFX_MELT_SCREEN, 0, 84, 0, 127, 127); break; case PICKUP_PIGGY: //* Bonus dollar score currScore += bonusVal[bonusMulty][0]; scp = GetSprite(2); PresetSprite(scp, PRESET_BONUS_SCORE); scp->wx = scp2->wx-8; scp->wy = scp2->wy+600; AliveLinkIn(scp); scp = scp->freeLink; PresetSprite(scp, PRESET_BONUS_MULTY); scp->wx = scp2->wx-8; scp->wy = scp2->wy+600; scp->spriteLink->v += (bonusMulty<<4)-(bonusMulty<<2); AliveLinkIn(scp); SsUtKeyOn(0, SFX_PIGGY_BUST, 0, 60, 0, 127, 127); break; case PICKUP_BURGER: //* Fill up energy bar... energyAdd = ((BAR_MAX-energyLeft)<<12); SsUtKeyOn(0, SFX_BONUS, 0, 70, 0, 127, 127); break; case PICKUP_BEANS: //* Reduce hit energy drain... if ((energyHit>>12) > 8) //* Dont go below min of 8! energyHit -= (ONE<<2); //* Reduce hit by 4 scp = GetSprite(1); PresetSprite(scp, PRESET_POWFLASH); scp->target = scp1; //* Point to BB for update_powflash_xy AliveLinkIn(scp); SsUtKeyOn(0, SFX_BONUS, 0, 52, 0, 127, 127); break; case PICKUP_HOLOBALL: //* Create decoy Blitter Boy... if (decoyScp) decoyScp->ICSALop = killDecoy; //* Kill already active decoy decoyScp = temp = scp = GetSprite(4); //* Initialize main red decoy sprite PresetSprite(scp, PRESET_DECOY_BOYR); scp->wx = ((scp->wxMid=scp2->wxMid) - scp->bbXOff - (scp->bbW >> 1)); scp->wy = ((scp->wyMid=scp2->wyMid) + scp->bbYOff - (scp->bbH >> 1)); AliveLinkIn(scp); chaseTarget = scp; //* This is the sprite that monsters will chase! //* Initialize green decoy sprite scp = scp->freeLink; PresetSprite(scp, PRESET_DECOY_BOYG); scp->wx = temp->wx - 2; scp->wy = temp->wy; AliveLinkIn(scp); //* Initialize blue decoy sprite scp = scp->freeLink; PresetSprite(scp, PRESET_DECOY_BOYB); scp->wx = temp->wx + 2; scp->wy = temp->wy; AliveLinkIn(scp); //* Initialize new HoloSphere sprite scp = scp->freeLink; PresetSprite(scp, PRESET_PICKUP_HOLOBALL); scp->wxMid = scp2->wxMid; scp->wyMid = scp2->wyMid; scp->wx = scp2->wx; scp->wy = scp2->wy; scp->ID = ID_SOFT_ICSAL; scp->subID = NORMAL; scp->lifeCount = 0; //* This HoloSphere is NOT a pickup! scp->high = scp2->high; scp->shadStat = 0; //* Have new HoloSphere take over old one's ICSAL scp->ICSALop = scp2->ICSALop; scp->ICSALloopStart = scp2->ICSALloopStart; scp->ICSALdataLoopStart = scp2->ICSALdataLoopStart; scp->ICSALdataStart = scp2->ICSALdataStart; scp->ICSALdata = scp2->ICSALdata; scp->ICSALloopCount = scp2->ICSALloopCount; scp->spriteLink->u = scp2->spriteLink->u; scp->spriteLink->v = scp2->spriteLink->v; AliveLinkIn(scp); SsUtKeyOn(0, SFX_COMPUTER, 0, 60, 0, 127, 127); break; case PICKUP_BATTERY: //* Activate range selection HUD... if (rangeScp) AliveLinkOut(rangeScp); //* Kill active range HUD sprite rangeScp = scp = GetSprite(1); PresetSprite(scp, PRESET_RANGE_HUD); AliveLinkIn(scp); SsUtKeyOn(0, SFX_LASER, 0, 60, 0, 127, 127); break; case PICKUP_FLAG: //* Flag for SURVIVE mode only! flagGone = 1; flagCnt++; AliveLinkOut(scp2); //* Kill flag sprite SsUtKeyOn(0, SFX_BONUS, 0, 72, 0, 127, 127); return; //* No more to be done! } PutGrid(scp2->wx, scp2->wy); //* Make grid position available AliveLinkOut(scp2); //* Kill actual pickup sprite } else { //* Pickup is a coin... switch (scp2->subID) { case PICKUP_GOLD_COIN: currScore += 15; break; case PICKUP_SILVER_COIN: currScore += 5; break; case PICKUP_BRONZE_COIN: currScore += 1; break; } AliveLinkOut(scp2); //* Kill actual coin sprite SsUtKeyOn(0, SFX_PING, 0, 60, 0, 127, 127); if (coinsInBag < MAX_COINS_IN_BAG) { //* Bag not full yet scp = moneybagScp->freeLink; sp = scp->spriteLink; if (++coinsInBag == MAX_COINS_IN_BAG) { //* Add a coin to bag and scp->ICSALop = animMoneybag; //* start ICSAL if bag is NOW full sp->v = timData[TIM_MONEYBAG].py; //* Set sprite to full 18x18 size... sp->h = 18; sp->y = 5; coinsInBag = 0; if (bonusMulty<9) { //* Increase bonus upto max of 10 bonusMulty++; bonusMultyScp->spriteLink->v += 18; } else { //* Bonus at max so give bonus score currScore += 1000; } } else { //* Update full level sprite... if (!scp->ICSALop) { //* only if money bag is not flashing! i = (coinsInBag<<12)/FIX_COINS_PER_SCAN; sp->v = timData[TIM_MONEYBAG].py+(17-i); sp->h = i; sp->y = 6+(16-i); } } } } } //*------------------------------------------------------------------------------------------ //* bb_mi - 'blitter boy hits mine' collision handler function //*------------------------------------------------------------------------------------- static void bb_mi(register struct SCTRL *scp1, register struct SCTRL *scp2) { struct SCTRL *temp; register struct SCTRL *scp; register GsSPRITE *sp; register int i; //* Ensure Blitter Boy is scp1 and mine is scp2 if (scp1->ID==ID_MINE) temp = scp2, scp2 = scp1, scp1 = temp; if (scp2->subID==MINE_PRIMED) { //* Detonate mine... SsUtKeyOn(0, SFX_ENEMY_EXPLODE1, 0, 76, 0, 127, 127); //* Release any following babies while (scp1!=lastInLine) { temp = lastInLine; lastInLine = lastInLine->target; //* Will be BB at end temp->target = NULL; temp->ICSALop = babyWander; //* Make them wander again temp->subID = BABY_WANDER; temp->frameLock = LOCK_3FRAMES; temp->currLock = RunNextTime(temp->frameLock); } scp = GetSprite(16); for (i=0; i<16; scp=scp->freeLink, i++) { PresetSprite(scp, PRESET_MINE_COMP1+i); scp->wx = scp2->wx+4; scp->wy = scp2->wy-2; //scp->spriteLink->u = (i & 3)<<3; AliveLinkIn(scp); } PutGrid(scp2->wx, scp2->wy); //* Make mine grid position available AliveLinkOut(scp2); if (polyStat) //* Eject any loaded PolyCart EjectPolyCart(); --mineCount; bullLoop = ((bullRange=2)<<3); //* Range returns to minimum if (!flashCnt) { //* Not invincible, so... flashCnt = 60; //* BB flashes & is invincible for 1 sec energyAdd -= energyHit; //* Authorize energy reduction } } else { } } //*------------------------------------------------------------------------------------------ //* ba_mi - 'baby hits mine' collision handler function //*------------------------------------------------------------------------------------- static void ba_mi(register struct SCTRL *scp1, register struct SCTRL *scp2) { struct SCTRL *temp; register struct SCTRL *scp; register GsSPRITE *sp; register int i; //* Ensure baby is scp1 and mine is scp2 if (scp1->ID==ID_MINE) temp = scp2, scp2 = scp1, scp1 = temp; if (scp1->subID==BABY_FOLLOW && tvStat!=TV_OFF) return; if (scp2->subID==MINE_PRIMED) { //* Detonate mine... SsUtKeyOn(0, SFX_ENEMY_EXPLODE1, 0, 76, 0, 127, 127); //* Initialize 16 explosion component sprites scp = GetSprite(16); for (i=0; i<16; scp=scp->freeLink, i++) { PresetSprite(scp, PRESET_MINE_COMP1+i); scp->wx = scp2->wx+4; scp->wy = scp2->wy-2; //scp->spriteLink->u = (i & 3)<<3; AliveLinkIn(scp); } PutGrid(scp2->wx, scp2->wy); //* Make mine grid position available AliveLinkOut(scp2); //* Kill mine sprite --mineCount; //* Make baby cry if not already crying if (scp1->subID!=BABY_CRY) { if (scp1->subID==BABY_FOLLOW) { //* Hit baby is in chain... while (scp1!=lastInLine) { //* Remove all babies behind hit baby from chain temp = lastInLine; lastInLine = lastInLine->target; temp->target = NULL; temp->ICSALop = babyWander; //* Make them wander again temp->subID = BABY_WANDER; temp->frameLock = LOCK_3FRAMES; temp->currLock = RunNextTime(temp->frameLock); } lastInLine = scp1->target; //* Remove hit baby from chain scp1->target = NULL; } //* Setup to show crying baby scp1->ICSALop = babyCry; scp1->subID = BABY_CRY; scp1->unintICSAL = 1; scp1->bbW = 18; //* Needed for shadow positioning scp1->bbH = 8; scp1->bbXOff = 7; scp1->bbYOff = 8; scp1->spriteLink->u = 0; scp1->spriteLink->v = 128; //* Centre baby sprite on central world coords scp1->wx = scp1->wxMid - (scp1->bbXOff + (scp1->bbW >> 1)); scp1->wy = scp1->wyMid + (scp1->bbYOff - (scp1->bbH >> 1)); } } } //*------------------------------------------------------------------------------------------ //* mo_en - 'moon hits enemy' collision handler function //*------------------------------------------------------------------------------------- static void mo_en(register struct SCTRL *scp1, register struct SCTRL *scp2) { register struct SCTRL *temp; //* Ensure enemy is scp1 and moon is scp2 if (scp1->ID==ID_MOONBALL) temp = scp2, scp2 = scp1, scp1 = temp; //* Reduce alive count for this enemy type & total --enemyAlive[scp1->subID][0]; --enemyAlive[TOTAL_ALIVE][0]; //* Increment enemy kill count for pickup management ++killCount; //* Kill enemy as if hit by a normal bullet normalBulletKill(scp1, scp2); } //*------------------------------------------------------------------------------------------ //* en_de - 'enemy hits decoy' collision handler function //*------------------------------------------------------------------------------------- static void en_de(register struct SCTRL *scp1, register struct SCTRL *scp2) { struct SCTRL *temp; //* Ensure decoy BB is scp1 and enemy is scp2 if (scp1->ID==ID_ENEMY) temp = scp2, scp2 = scp1, scp1 = temp; //* Consider enemy if (!scp2->unintICSAL) { //* Set enemy's new direction to opposite direction switch (scp2->dir = (scp2->dir+4) & 0x07) { case DIR_DOWN_LEFT: scp2->accelX = -scp2->speed; scp2->accelY = scp2->speed; break; case DIR_UP_LEFT: scp2->accelX = -scp2->speed; scp2->accelY = -scp2->speed; break; case DIR_DOWN: scp2->accelX = 0; scp2->accelY = scp2->speed; break; case DIR_DOWN_RIGHT: scp2->accelX = scp2->speed; scp2->accelY = scp2->speed; break; case DIR_UP_RIGHT: scp2->accelX = scp2->speed; scp2->accelY = -scp2->speed; break; case DIR_UP: scp2->accelX = 0; scp2->accelY = -scp2->speed; break; case DIR_LEFT: scp2->accelX = -scp2->speed; scp2->accelY = 0; break; case DIR_RIGHT: scp2->accelX = scp2->speed; scp2->accelY = 0; break; } scp2->ICSALop = (scp2->subID == ENEMY_UFO ? ufoBump : enemyBump); scp2->unintICSAL = 1; } } //*------------------------------------------------------------------------------------------ //* bu_mi - 'bullet hits mine' collision handler function //*------------------------------------------------------------------------------------- static void bu_mi(register struct SCTRL *scp1, register struct SCTRL *scp2) { struct SCTRL *temp; register struct SCTRL *scp; register GsSPRITE *sp; register int i; //* Ensure bullet is scp1 and mine is scp2 if (scp1->ID==ID_MINE) temp = scp2, scp2 = scp1, scp1 = temp; if (scp2->subID==MINE_PRIMED) { //* Detonate mine... SsUtKeyOn(0, SFX_ENEMY_EXPLODE1, 0, 76, 0, 127, 127); //* Initialize 16 explosion component sprites scp = GetSprite(16); for (i=0; i<16; scp=scp->freeLink, i++) { PresetSprite(scp, PRESET_MINE_COMP1+i); scp->wx = scp2->wx+4; scp->wy = scp2->wy-2; //scp->spriteLink->u = (i & 3)<<3; AliveLinkIn(scp); } PutGrid(scp2->wx, scp2->wy); //* Make mine grid position available AliveLinkOut(scp2); //* Kill mine sprite --mineCount; //* Kill both bullet blur sprites //* Note: the 3 linked bullet sprites MUST be destroyed in reverse //* order else the .freeLink chain would be broken if (scp1->freeLink->alive) { //* The 2 bullet blur sprites are active... AliveLinkOut(scp1->freeLink->freeLink); AliveLinkOut(scp1->freeLink); } else { //* The 2 bullet blur sprites aren't active yet... PutSprite(scp1->freeLink->freeLink); PutSprite(scp1->freeLink); } AliveLinkOut(scp1); //* Kill actual bullet sprite } }