// C Header File // Created 12/27/2002; 6:08:36 PM static inline void AITurn (FATSPRITE * spr, short right, short increment); static inline void AIMove (FATSPRITE * spr, short newxpos, short newypos); static inline void EnemyShoot(short idx); static inline void EnemyChase(short idx, unsigned long distance); static inline short ValidMoveEnemies (short x, short y); #ifndef __ENEMIES_H__ #define __ENEMIES_H__ #include "sprites_init.h" #include "gfx.h" //#include "weapons.h" //============================================================================= // returns 0 if move would bring the character too near to a wall //============================================================================= static inline short ValidMoveEnemies (register short x, register short y) { #define WALLDST_ENM 29 register char *ptr = fc->map_data; register short width = fc->map_width; char val1 = *(ptr+((y+WALLDST_ENM)>>6)*width+((x+WALLDST_ENM)>>6)); char val2 = *(ptr+((y-WALLDST_ENM)>>6)*width+((x-WALLDST_ENM)>>6)); char val3 = *(ptr+((y+WALLDST_ENM)>>6)*width+((x-WALLDST_ENM)>>6)); char val4 = *(ptr+((y-WALLDST_ENM)>>6)*width+((x+WALLDST_ENM)>>6)); // if we would come too near to a solid square... if ((val1>0)||(val2>0)||(val3>0)||(val4>0)|| (val1==solidSprite)||(val2==solidSprite)|| (val3==solidSprite)||(val4==solidSprite)|| (val1==impassibleWall)||(val2==impassibleWall)|| (val3==impassibleWall)||(val4==impassibleWall)) return 0; // false return 1; // true } static inline void AITurn (FATSPRITE * spr, short right, short increment) { spr->orientation += (right) ? increment : -increment; // now clamp the camera orientation to the valid range of 0 ... 575 spr->orientation = FAT_ClampAngle (spr->orientation); } static inline void AIMove (FATSPRITE * spr, short newxpos, short newypos) { short newxmod = newxpos >> 6; short newymod = newypos >> 6; short set = fc->map_data[(spr->ypos>>6)*fc->map_width+(spr->xpos>>6)] == solidSprite; char *tile =&(((SpriteStruct*)spr->clientdata)->tile); //!(((SpriteStruct*)spr->clientdata)->passthrough || //((SpriteStruct*)spr->clientdata)->pickup); //char fc->map_data[(spr->ypos>>6)*fc->map_width+(spr->xpos>>6)] = fc->map_data + (spr->ypos>>6)*fc->map_width+(spr->xpos>>6); if(set) fc->map_data[(spr->ypos>>6)*fc->map_width+(spr->xpos>>6)]=*tile; //------------------------------------------------------------------------- // check if we are still within the map //------------------------------------------------------------------------- if (newxmod > fc->map_width - 1 || newymod > fc->map_height - 1 || newxmod < 0 || newymod < 0) { if(set) { //*tile = fc->map_data[(spr->ypos>>6)*fc->map_width+(spr->xpos>>6)]; fc->map_data[(spr->ypos>>6)*fc->map_width+(spr->xpos>>6)]=solidSprite; } return; } if (!ValidMoveEnemies ( newxpos, newypos)) { // if new position is invalid ... // ...but old position is OK, we're against a wall to east or west if (ValidMoveEnemies ( spr->xpos, newypos)) { // don't change angle if movement is perpendicular to the wall //if (stepy != 0) { // still allow movement along y = parallel to wall spr->ypos = newypos; } } // ...but (new x, old y) is OK, we're against a wall to north or south if (ValidMoveEnemies ( newxpos, spr->ypos)) { // don't change angle if movement is perpendicular to the wall //if (stepx != 0) { // still allow movement along x = parallel to wall spr->xpos = newxpos; } } // ...otherwise we're in a corner, so do nothing if(set) { *tile = fc->map_data[(spr->ypos>>6)*fc->map_width+(spr->xpos>>6)]; //if(*tile==solidSprite||*tile==impassibleWall) textEnqueue("WARNING"); fc->map_data[(spr->ypos>>6)*fc->map_width+(spr->xpos>>6)]=solidSprite; } return; } //------------------------------------------------------------------------- // everything okay -> let's set the new position //------------------------------------------------------------------------- spr->xpos = newxpos; spr->ypos = newypos; if(set) { *tile = fc->map_data[(spr->ypos>>6)*fc->map_width+(spr->xpos>>6)]; //if(*tile==solidSprite||*tile==impassibleWall) textEnqueue("WARNING"); fc->map_data[(spr->ypos>>6)*fc->map_width+(spr->xpos>>6)]=solidSprite; } } static inline void EnemyShoot(short idx) { register GameGlobals *ggg asm("a4") = gg; register FATSPRITE *spr = ggg->sprites + idx; register SpriteStruct *extra = ggg->sprites_extra + idx; WeaponInstance *cur_weapon = &extra->weapon; if(cur_weapon->curdelay == cur_weapon->cur->repeatdelay) { if(!RayBlocked(fc->cam_xpos,fc->cam_ypos,spr->xpos,spr->ypos)) { short range = ANGLE_045; short r = random_long (range) - range/2; short range2 = range>>2; if(r<0 && r>-range2) r = -range2; //Don't be too accurate if(r>0 && rorientation = spr->orientation; spr->orientation += r; cur_weapon->curdelay = 0; if(cur_weapon->cur->instant_hit) FireInstantWeapon(RIGHT,spr,extra); else SpawnProjectile(RIGHT,spr,extra); if(extra->firesprite) spr->textures = extra->src + extra->firesprite; if(cur_weapon->ammo != INFINITE_AMMO) cur_weapon->ammo--; } } else { cur_weapon->curdelay++; if(extra->firesprite) spr->textures = extra->src + extra->first_idx; } if(!cur_weapon->ammo/* && cur_weapon->ammo != INFINITE_AMMO*/) { extra->weapon = WIset(Foot); extra->weapon.ammo = extra->weapon.cur->start_ammo; extra->weapon.curdelay = extra->weapon.cur->repeatdelay; extra->weapon.cur_persistence = 0; } } static inline void EnemyChase(short idx, unsigned long distance) { register GameGlobals *ggg asm("a4") = gg; register FATSPRITE *spr = ggg->sprites+idx; SpriteStruct *s = ggg->sprites_extra + idx; short stepx; short stepy; short newxpos; short newypos; short orientation; short speed = s->speed; char *move = &s->moving; if(distance>112 && *move==GOING) *move = COMING; if(distance<96 && *move==COMING) *move = GOING; if(*move!=COMING) spr->orientation = FAT_ClampAngle(spr->orientation-ANGLE_180); spr->orientation += random_long (ANGLE_060) - (ANGLE_060/2); orientation = spr->orientation; //Try to go in the facing direction stepx = FAT_Cos16384 (orientation) >> 12; stepy = FAT_Sin16384 (orientation) >> 12; stepx*=speed; stepy*=speed; newxpos = spr->xpos + stepx; newypos = spr->ypos + stepy; AIMove (spr, newxpos, newypos); if(s->life < life(s->type)>>2) //flee if the enemy life is 25% or less { *move = FLEEING; s->desire &= ~ENEMY_SHOOTER; } } //The boss enemy is more unpredictable than the normal enemies. //Mainly it follows the same 'random movement' patterns, EXCEPT: //-Occasionally, it charges the player at high speed (2x faster). //-Also, when the boss has less than 25% health left, instead // of fleeing, it begins to move 2x as fast as normal, AND // it switches to a plasmagun. // Basically, it goes berserk :-) static inline void EnemyBoss(short idx, unsigned long distance) { register GameGlobals *ggg asm("a4") = gg; register FATSPRITE *spr = ggg->sprites+idx; SpriteStruct *s = ggg->sprites_extra + idx; short stepx; short stepy; short newxpos; short newypos; short orientation; short speed = s->speed; char *move = &s->moving; short berserk; if(s->life != ggg->oldBossHealth) { short xdist = (short)((long)((long)s->life*96)/(long)life(boss)); short x = xdist>>3; short a; char bits[] = {0b00000000,0b10000000,0b11000000,0b11100000,0b11110000,0b11111000,0b11111100,0b11111110}; for(a=0;a<12*3;a+=12) { memset((char*)ggg->HUDlight+a,0x00,12); memset((char*)ggg->HUDdark+a,0x00,12); memset((char*)ggg->HUDmask+a,0xFF,12); memset((char*)ggg->HUDlight+a,0xFF,x); memset((char*)ggg->HUDdark+a,0xFF,x); memset((char*)ggg->HUDmask+a,0x00,x); *((unsigned char*)ggg->HUDlight+a+x) |= bits[(xdist&7)]; *((unsigned char*)ggg->HUDdark+a+x) |= bits[(xdist&7)]; *((unsigned char*)ggg->HUDmask+a+x) &= ~bits[(xdist&7)]; } } ggg->oldBossHealth = s->life; //Firing WeaponInstance *cur_weapon = &s->weapon; berserk = cur_weapon->cur == &BossPlasma; if(cur_weapon->curdelay == cur_weapon->cur->repeatdelay) { if(!RayBlocked(fc->cam_xpos,fc->cam_ypos,spr->xpos,spr->ypos)) { short range = berserk?ANGLE_030:ANGLE_045; short r = random_long (range) - range/2; short range2 = range>>2; if(r<0 && r>-range2) r = -range2; //Don't be too accurate if(r>0 && rorientation = spr->orientation; spr->orientation += r; cur_weapon->curdelay = 0; if(cur_weapon->cur->instant_hit) FireInstantWeapon(RIGHT,spr,s); else SpawnProjectile(RIGHT,spr,s); if(s->firesprite) spr->textures = s->src + s->firesprite; if(cur_weapon->ammo != INFINITE_AMMO) cur_weapon->ammo--; } } else { cur_weapon->curdelay++; if(s->firesprite) spr->textures = s->src + s->first_idx; } if(!cur_weapon->ammo/* && cur_weapon->ammo != INFINITE_AMMO*/) { s->weapon = WIset(Foot); s->weapon.ammo = s->weapon.cur->start_ammo; s->weapon.curdelay = s->weapon.cur->repeatdelay; s->weapon.cur_persistence = 0; } //Moving s->curwait+=s->curinc; if(s->curwait == 85) { s->curinc = -5; s->speed *=2; *move = COMING; } else if(!s->curwait) { s->curinc = 1; s->speed /= 2; *move = GOING; } if(s->curinc==-5) { if(distance>112 && *move==GOING) *move = COMING; if(distance<96 && *move==COMING) *move = GOING; } else { if(distance>232 && *move==GOING) *move = COMING; if(distance<196 && *move==COMING) *move = GOING; } if(*move==GOING) spr->orientation = FAT_ClampAngle(spr->orientation-ANGLE_180); spr->orientation += random_long (ANGLE_060) - (ANGLE_060/2); orientation = spr->orientation; //Try to go in the facing direction stepx = FAT_Cos16384 (orientation) >> 12; stepy = FAT_Sin16384 (orientation) >> 12; stepx*=speed; stepy*=speed; newxpos = spr->xpos + stepx; newypos = spr->ypos + stepy; AIMove (spr, newxpos, newypos); if(s->life < life(s->type)>>2) //if the enemy life is 25% or less { //Oh snap! Go berserk! s->weapon = WIset(BossPlasma); s->weapon.ammo = s->weapon.cur->maxammo; s->weapon.curdelay = s->weapon.cur->repeatdelay; s->weapon.cur_persistence = 0; if(s->speed==1) s->speed = 2; else s->speed = 4; } } #endif