// C Header File // Created 12/2/2002; 7:03:35 AM #define WIset(w) (WeaponInstance){0,0,0,&w} #define W_OFF 0 #define W_ON 1 extern Weapon Foot; extern Weapon Pistol; extern Weapon MachineGun; extern Weapon RocketLauncher; extern Weapon PlasmaGun; extern Weapon SpiderGun; extern Weapon SuperSlowRocketLauncher; extern Weapon BossPlasma; extern char WeaponsValid[WEAPON_COUNT]; extern WeaponInstance weapons[WEAPON_COUNT]; extern WeaponInstance* current_weapon; static void ResetWeapons(void); static void InitWeapons(void); static void DrawWeapon(short idx, short status); static void GiveWeapon(short weapon); static inline void KillProjectile(short idx); static inline void ExplodeProjectile(short idx); static inline void SpawnProjectile( short which, FATSPRITE *s, SpriteStruct *e); static inline short ProjectileCollision(short idx); static inline void HandleProjectiles(void); static inline void FireInstantWeapon(short which, FATSPRITE *s, SpriteStruct *e); static short NextWeapon(void); static inline void ResetWeaponArea(void); static inline void SwitchWeaponTo(short idx); static inline void HandleWeapons(void); #ifndef __WEAPONS_H__ #define __WEAPONS_H__ #include "walls.txt" #include "level_exec.h" #include "rayline.h" #include "sprites_exec.h" //#include "enemies.h" /* typedef struct { register GameGlobals *ggg asm("a4") = gg; const short start_ammo; const short clip_ammo; const short maxammo; const short maxdamage; const short repeatdelay; // In game cycles, at least 1 const short persistence; const unsigned char instant_hit; const unsigned char splashdamage; const short max_tiles; //Max distance, in tiles, the ammo travels const unsigned char projectile_speed; //Distance in one cycle (if applicable) const short ammosprite; const short nr_sprites; short hud_x; //Size in pixels short hud_y; //Size in pixels unsigned char* lightdata[2]; unsigned char* darkdata[2]; unsigned char* maskdata[2]; } Weapon; */ Weapon Foot = {INFINITE_AMMO,0,INFINITE_AMMO,12,4,4,TRUE,FALSE,2,0,0,0, 0,0, {NULL},{NULL},{NULL}}; Weapon Pistol = {48,12,108,12,4,1,TRUE,FALSE,15,0,0,0, 0,0, {NULL},{NULL},{NULL}}; Weapon MachineGun = {150,50,250,10,1,1,TRUE,FALSE,15,0,0,0, 0,0, {NULL},{NULL},{NULL}}; Weapon RocketLauncher = {20,10,50,50,15,2,FALSE,TRUE,15,3,SPRIDX_rocket1,8 ,0,0, {NULL},{NULL},{NULL}}; Weapon PlasmaGun = {100,50,200,22,2,2,FALSE,TRUE,15,9,SPRIDX_plasma1,1 ,0,0, {NULL},{NULL},{NULL}}; Weapon RailGun = {20,10,50,175,19,2,TRUE,FALSE,15,0,0,0, 0,0, {NULL},{NULL},{NULL}}; /* const Weapon SuperGun = {1,1,200,0,TRUE,TRUE,NULL};*/ //Enemy Specific Weapons //(They also use Pistol, MachineGun, and RocketLauncher) Weapon SpiderGun = {45,15,100,5,3,1,TRUE,FALSE,5,0,0,0, 0,0, {NULL},{NULL},{NULL}}; Weapon SuperSlowRocketLauncher = {40,20,100,185,29,1,FALSE,FALSE,15,1,SPRIDX_rocket1,8 ,0,0, {NULL},{NULL},{NULL}}; Weapon BossPlasma = {100,50,200,7,9,1,FALSE,TRUE,15,5,SPRIDX_plasma1,1 ,0,0, {NULL},{NULL},{NULL}}; char WeaponsValid[WEAPON_COUNT]; WeaponInstance weapons[WEAPON_COUNT] = {WIset(Foot), WIset(Pistol),WIset(MachineGun),WIset(RocketLauncher), WIset(PlasmaGun),WIset(RailGun)}; WeaponInstance* current_weapon; static void ResetWeapons(void) { short a; for(a=0;arepeatdelay; weapons[a].cur_persistence = 0; } gg->cur_weapon = 0; memset(WeaponsValid,(char)FALSE,WEAPON_COUNT); GiveWeapon(DEFAULT_WEAPON); } static void InitWeapons(void) { register GameGlobals *ggg asm("a4") = gg; short a; ResetWeaponArea(); DrawWeapon(ggg->cur_weapon,W_OFF); for(a=0;aprojectiles[a],0x00,sizeof(ProjectileStruct)); } current_weapon = &weapons[ggg->cur_weapon]; } static void DrawWeapon(short idx, short status) { register GameGlobals *ggg asm("a4") = gg; Weapon *w = weapons[idx].cur; short x = 96-w->hud_x; short y = 96-w->hud_y; short bytewidth = w->hud_x >> 3; short height = w->hud_y; unsigned char* light = w->lightdata[status]; unsigned char* dark = w->darkdata[status]; unsigned char* mask = w->maskdata[status]; if(!light || !dark || !mask || ggg->levelstyle==level_vehicle) return; SpriteX8_AND_96x96(x,y,height,mask,bytewidth,ggg->HUDmask); SpriteX8_OR_96x96(x,y,height,mask,bytewidth,ggg->HUDmask); SpriteX8_AND_96x96(x,y,height,light,bytewidth,ggg->HUDlight); SpriteX8_OR_96x96(x,y,height,light,bytewidth,ggg->HUDlight); SpriteX8_AND_96x96(x,y,height,dark,bytewidth,ggg->HUDdark); SpriteX8_OR_96x96(x,y,height,dark,bytewidth,ggg->HUDdark); } static void GiveWeapon(short weapon) { register GameGlobals *ggg asm("a4") = gg; if(WeaponsValid[weapon]) { weapons[weapon].ammo += weapons[weapon].cur->start_ammo; if(weapons[weapon].ammo > weapons[weapon].cur->maxammo) weapons[weapon].ammo = weapons[weapon].cur->maxammo; } else { weapons[weapon].ammo = weapons[weapon].cur->start_ammo; WeaponsValid[weapon] = TRUE; } ggg->weapon_count++; if(weapon>=ggg->cur_weapon) SwitchWeaponTo(weapon); } static inline void KillProjectile(short idx) { register GameGlobals *ggg asm("a4") = gg; ggg->projectiles[idx].inuse = SPRITE_FREE; ggg->projectile_count--; } static inline void ExplodeProjectile(short idx) { register GameGlobals *ggg asm("a4") = gg; ProjectileStruct *this_p = ggg->projectiles+idx; this_p->inuse = -1; //To start showing the explosion pix this_p->spr.nr_textures = 1; this_p->spr.drawmode = SPRITEMODE_SIZE64; } #define PROJECTILE_SPEED 1 static inline void SpawnProjectile(short which, FATSPRITE *s, SpriteStruct *e) { register GameGlobals *ggg asm("a4") = gg; short projectile_idx,x,y; //Find a free projectile for (projectile_idx=0;projectile_idxprojectiles[projectile_idx].inuse == SPRITE_FREE) break; } if (projectile_idx == MAX_PROJECTILES) return; ProjectileStruct* projectile = &ggg->projectiles[projectile_idx]; Weapon* weap = which?e->weapon.cur:current_weapon->cur; if(which) projectile->maxdamage = weap->maxdamage*ggg->difficulty; else projectile->maxdamage = weap->maxdamage/ggg->difficulty; projectile->player_shot = !which; FATSPRITE *spr = &projectile->spr; FAT_InitSprite(spr); projectile->inuse = SPRITE_INUSE; spr->xpos = which?s->xpos:fc->cam_xpos; spr->ypos = which?s->ypos:fc->cam_ypos; projectile->x = ((long)spr->xpos)<<8L; projectile->y = ((long)spr->ypos)<<8L; spr->orientation = which?s->orientation:fc->cam_orientation; projectile->orientation = which?e->orientation:spr->orientation;//Special projectile->speed = weap->projectile_speed; projectile->splashdamage = weap->splashdamage; projectile->distance = 0; projectile->maxdistance = weap->max_tiles<<6; spr->drawmode = SPRITEMODE_SIZE64; spr->nr_textures = 8; spr->textures = (TEXCONFIG *) &ggg->projectilemodels[weap->ammosprite]; projectile->x += (((long)FAT_Cos16384 (spr->orientation) >> 3L) * PROJECTILE_SPEED); projectile->y += (((long)FAT_Sin16384 (spr->orientation) >> 3L) * PROJECTILE_SPEED); spr->xpos = (short)(projectile->x >> 8L); spr->ypos = (short)(projectile->y >> 8L); //Kickback if(which) { x = s->xpos-((((FAT_Cos16384(s->orientation))>>10) * weap->maxdamage)>>7); y = s->ypos-((((FAT_Sin16384(s->orientation))>>10) * weap->maxdamage)>>7); AIMove(s,x,y); } else { x=fc->cam_xpos-((((FAT_Cos16384(fc->cam_orientation))>>10) * weap->maxdamage)>>7); y=fc->cam_ypos-((((FAT_Sin16384(fc->cam_orientation))>>10) * weap->maxdamage)>>7); PlayerMove2(x,y); } ggg->projectile_count++; } #define NONACCURACY 6 static inline short ProjectileCollision(short idx) { register GameGlobals *ggg asm("a4") = gg; char hitenemy = FALSE; short spritedied=0; register ProjectileStruct *this_p = &ggg->projectiles[idx]; short a; short x = (short)((this_p->x + ((((long)FAT_Cos16384 (this_p->spr.orientation) >> 3) * PROJECTILE_SPEED)))>>8L); short y = (short)((this_p->y + ((((long)FAT_Sin16384 (this_p->spr.orientation) >> 3) * PROJECTILE_SPEED)))>>8L); short kx,ky; short location = (y >> 6) * fc->map_width + (x >> 6); short angle,dx,dy,bestangle,angledelta,damage; if (fc->map_data[location] > 0) { register long dx,dy; long distance_squared; unsigned long distance; CheckForCustomWallDeath(location); HandleShotButtons(location%fc->map_width,location/fc->map_width); //Splash Damage if(this_p->splashdamage) { #define WALL_SPLASH 100 //Check if all sprites are affected for(a=0;asprite_count;a++) { FATSPRITE *spr = ggg->sprites + a; dx = (long) x - spr->xpos; dy = (long) y - spr->ypos; dx *= dx; dy *= dy; distance_squared = dx + dy; distance = FAT_FastSqrt (distance_squared); if (distance <= (unsigned long) WALL_SPLASH) { if (!RayBlocked (this_p->spr.xpos,this_p->spr.ypos, spr->xpos,spr->ypos)) { damage = (signed short) (this_p->maxdamage * (WALL_SPLASH - (signed short) distance) / (WALL_SPLASH/2)); spritedied = SpriteLife(a,-damage); if(spritedied) break; } } } //Now check if the player was affected dx = (long) x - fc->cam_xpos; dy = (long) y - fc->cam_ypos; dx *= dx; dy *= dy; distance_squared = dx + dy; distance = FAT_FastSqrt (distance_squared); if (distance <= (unsigned long) WALL_SPLASH) { if (!RayBlocked (this_p->spr.xpos,this_p->spr.ypos, fc->cam_xpos,fc->cam_ypos)) { damage = (signed short) (this_p->maxdamage * (WALL_SPLASH - (signed short) distance) / (WALL_SPLASH/2)); PlayerLife(-damage); } } return spritedied?2:1; } } //Now if the player shot it, if(this_p->player_shot) { //See if it hit enemies for(a=0;asprite_count;a++) { FATSPRITE *spr = ggg->sprites + a; SpriteStruct *e = ggg->sprites_extra + a; if(e->pickup) continue; if(iRoundEqual(x,spr->xpos,NONACCURACY) && iRoundEqual(y,spr->ypos,NONACCURACY)) { hitenemy = TRUE; angle = fc->cam_orientation; dx = spr->xpos - this_p->spr.xpos; dy = spr->ypos - this_p->spr.ypos; bestangle = FAT_GetArcTanYX (dy,dx); angledelta = abs (angle - bestangle); if (angledelta > ANGLE_030) angledelta = ANGLE_030; damage = ((ANGLE_030 - angledelta)*this_p->maxdamage) / ((ANGLE_030*3)/4); if(damage>this_p->maxdamage) damage = this_p->maxdamage; spritedied = SpriteLife(a,-damage); if(spritedied) break; //Kickback if(e->desire & ENEMY_CHASER) { kx = spr->xpos + ((((FAT_Cos16384(this_p->spr.orientation))>>10) * this_p->maxdamage)>>7); ky = spr->ypos + ((((FAT_Sin16384(this_p->spr.orientation))>>10) * this_p->maxdamage)>>7); AIMove(spr,kx,ky); } return spritedied?2:1; } } } if(!this_p->player_shot || hitenemy)// (Now, player can get hurt even when others do) { // so check if it hit the player if(iRoundEqual(x,fc->cam_xpos,NONACCURACY) && iRoundEqual(y,fc->cam_ypos,NONACCURACY)) { angle = this_p->spr.orientation; bestangle = this_p->orientation; angledelta = abs (angle - bestangle); if (angledelta > ANGLE_030) angledelta = ANGLE_030; damage = ((ANGLE_030 - angledelta)*this_p->maxdamage) / ANGLE_030; if(damage<0) damage = 0; PlayerLife(-damage); //Kickback kx = fc->cam_xpos + ((((FAT_Cos16384(angle))>>10) * this_p->maxdamage)>>7); ky = fc->cam_ypos + ((((FAT_Sin16384(angle))>>10) * this_p->maxdamage)>>7); PlayerMove2(kx,ky); return 1; } } return 0; } static inline void HandleProjectiles(void) { register GameGlobals *ggg asm("a4") = gg; FATSPRITE *spr; long stepx; long stepy; short a; //short s; register ProjectileStruct *this_p = ggg->projectiles; for (a = 0;ainuse == SPRITE_FREE) continue; if(this_p->inuse == SPRITE_INUSE) { short i; for(i=0;ispeed;i++) { if (ProjectileCollision (a)) {ExplodeProjectile (a);break;} else { short orientation = this_p->spr.orientation; stepx = (((long)FAT_Cos16384 (orientation) >> 3L) * PROJECTILE_SPEED); stepy = (((long)FAT_Sin16384 (orientation) >> 3L) * PROJECTILE_SPEED); this_p->x += stepx; this_p->y += stepy; this_p->spr.xpos = (short)(this_p->x >> 8L); this_p->spr.ypos = (short)(this_p->y >> 8L); this_p->distance += (short)((max(stepx,stepy))>>8L); if(this_p->distance>=this_p->maxdistance) ExplodeProjectile(a); } } } if (this_p->inuse < SPRITE_FREE) { if (this_p->inuse == -7) { KillProjectile(a); } else { this_p->spr.textures = ggg->enemysprites + TEXIDX_rexA + abs (this_p->inuse) - 1; if(this_p->inuse==-4 || this_p->inuse==-5) this_p->spr.drawmode = SPRITEMODE_UPPER32; else this_p->spr.drawmode = SPRITEMODE_SIZE64; this_p->inuse--; } } } spr = ggg->sprites + ggg->sprite_count; this_p = ggg->projectiles; for (a = 0;ainuse == SPRITE_FREE) continue; memcpy(spr,&ggg->projectiles[a].spr,sizeof(FATSPRITE)); //copySprite(spr,&ggg->projectiles[a].spr); spr++; } } static inline void FireInstantWeapon(short which, FATSPRITE *s, SpriteStruct *e) { register GameGlobals *ggg asm("a4") = gg; short *origx = (which?&s->xpos:&fc->cam_xpos); short *origy = (which?&s->ypos:&fc->cam_ypos); long origlx = (((long)(*origx))<<8L); long origly = (((long)(*origy))<<8L); long x = origlx; long y = origly; short orientation = which?s->orientation:fc->cam_orientation; long stepx = (long)FAT_Cos16384 (orientation); long stepy = (long)FAT_Sin16384 (orientation); short kx,ky; short maxdamage = which? e->weapon.cur->maxdamage:current_weapon->cur->maxdamage; short damage; char tile; short a; short tx = (short)(x>>8L),ty = (short)(y>>8L); long max = which?((long)e->weapon.cur->max_tiles)<<14L: ((long)current_weapon->cur->max_tiles)<<14L; short dx,dy,bestangle,angledelta; if(((which?e->weapon.ammo:current_weapon->ammo)!=INFINITE_AMMO) && (which?(e->desire&ENEMY_CHASER):TRUE)) { //Kickback kx = *origx - ((((FAT_Cos16384(orientation))>>10) * maxdamage)>>7); ky = *origy - ((((FAT_Sin16384(orientation))>>10) * maxdamage)>>7); if(which) AIMove(s,kx,ky); else PlayerMove2(kx,ky); } if(which) maxdamage*=ggg->difficulty; else maxdamage/=ggg->difficulty; while(abs(x-origlx)>8L); ty = (short)(y>>8L); if(tx<0 || ty<0 || tx>>6 >= fc->map_width || ty>>6 >= fc->map_height) return; } // Sprites if(!which) { for(a=0;asprite_count;a++) { SpriteStruct *e = ggg->sprites_extra + a; if(e->pickup||e->passthrough) continue; FATSPRITE *spr = ggg->sprites + a; if(iRoundEqual(tx,spr->xpos,NONACCURACY) && iRoundEqual(ty,spr->ypos,NONACCURACY)) { dx = spr->xpos - *origx; dy = spr->ypos - *origy; bestangle = FAT_GetArcTanYX (dy,dx); angledelta = abs (orientation - bestangle); if (angledelta > ANGLE_045) angledelta = ANGLE_045; damage = ((ANGLE_045 - angledelta)*maxdamage) / (ANGLE_045); if(damage>maxdamage) damage = maxdamage; SpriteLife(a,-damage); //Kickback if(e->desire & ENEMY_CHASER) { kx = spr->xpos + ((((FAT_Cos16384(orientation))>>10) * maxdamage)>>7); ky = spr->ypos + ((((FAT_Sin16384(orientation))>>10) * maxdamage)>>7); AIMove(spr,kx,ky); } return; } } } else { if(iRoundEqual(tx,fc->cam_xpos,NONACCURACY) && iRoundEqual(ty,fc->cam_ypos,NONACCURACY)) { dx = fc->cam_xpos - *origx; dy = fc->cam_ypos - *origy; bestangle = FAT_GetArcTanYX (dy,dx); angledelta = abs (orientation - bestangle); if (angledelta > ANGLE_030) angledelta = ANGLE_030; damage = ((ANGLE_030 - angledelta)*maxdamage) / ANGLE_030; if(damage<0) damage = 0; PlayerLife(-damage); //Kickback kx = fc->cam_xpos + ((((FAT_Cos16384(orientation))>>10) * maxdamage)>>7); ky = fc->cam_ypos + ((((FAT_Sin16384(orientation))>>10) * maxdamage)>>7); PlayerMove2(kx,ky); return; } } // Walls short x6 = tx>>6; short y6 = ty>>6; tile = fc->map_data[y6*fc->map_width + x6]; if(tile>0) { CheckForCustomWallDeath(y6*fc->map_width + x6); HandleShotButtons(x6,y6); /*if(sprite_count < MAX_SPRITES) { if(x!=origx && y!=origy) {x-=stepx;y-=stepy;} InitSprite(instantHitShow,x,y,sprite_count); sprite_count++; }*/ return; } if(!which) { x += stepx; y += stepy; tx = (short)(x>>8L); ty = (short)(y>>8L); if(tx<0 || ty<0 || tx>>6 >= fc->map_width || ty>>6 >= fc->map_height) return; } } } static short NextWeapon(void) { register GameGlobals *ggg asm("a4") = gg; short a = ggg->cur_weapon; do { a++; if(a==WEAPON_COUNT) a=0; } while(!WeaponsValid[a]); return a; } /* void ResetHUD(void) { register GameGlobals *ggg asm("a4") = gg; memset(HUDlight,0x00,576*sizeof(short)); memset(HUDdark,0x00,576*sizeof(short)); memset(HUDmask,0xFF,576*sizeof(short)); } */ static inline void ResetWeaponArea(void) { register GameGlobals *ggg asm("a4") = gg; //Weapon *cur = weapons[cur_weapon].cur; if(ggg->levelstyle==level_vehicle) return; short length = 40*12; short offset = (576*sizeof(short)) - length; memset((BYTE*)ggg->HUDlight+offset,0x00,length); memset((BYTE*)ggg->HUDdark+offset,0x00,length); memset((BYTE*)ggg->HUDmask+offset,0xFF,length); } static inline void SwitchWeaponTo(short idx) { register GameGlobals *ggg asm("a4") = gg; ResetWeaponArea(); ggg->cur_weapon = idx; current_weapon = &weapons[ggg->cur_weapon]; if(ggg->levelstyle!=level_vehicle) DrawWeapon(ggg->cur_weapon,W_OFF); } static inline void HandleWeapons(void) { register GameGlobals *ggg asm("a4") = gg; short weaponIsFiring = FALSE; if(ggg->shoot_flag) { if(current_weapon->curdelay == current_weapon->cur->repeatdelay) { current_weapon->curdelay = 0; if(current_weapon->cur->instant_hit) FireInstantWeapon(LEFT,NULL,NULL); else SpawnProjectile(LEFT,NULL,NULL); weaponIsFiring = TRUE; current_weapon->cur_persistence = current_weapon->cur->persistence; if(current_weapon->ammo != INFINITE_AMMO) current_weapon->ammo--; } } if(weaponIsFiring || current_weapon->cur_persistence) DrawWeapon(ggg->cur_weapon,W_ON); else { if(!current_weapon->ammo && current_weapon->ammo != INFINITE_AMMO) SwitchWeaponTo(NextWeapon()); else if(ggg->oldWeaponIsFiring != weaponIsFiring) DrawWeapon(ggg->cur_weapon,W_OFF);//ELSE just not to do this 2x if(current_weapon->curdelay < current_weapon->cur->repeatdelay) current_weapon->curdelay++; } ggg->oldWeaponIsFiring = weaponIsFiring | current_weapon->cur_persistence; if(current_weapon->cur_persistence) current_weapon->cur_persistence--; if(ggg->switchweapon_flag == 1) SwitchWeaponTo(NextWeapon()); if(ggg->switchweapon_flag) ggg->switchweapon_flag--; } #endif