// C Header File // Created 12/2/2002; 7:03:15 AM #define MAX_ACTING_DISTANCE (5<<6) void InitSpriteData(short idx) { (sprites + idx)->clientdata = (unsigned char*)(&sprites_extra[idx]); memset((sprites + idx)->clientdata,0x00,sizeof(SpriteStruct)); } short life(short which) { switch(which) { case skeleton: return 40; case spider: return 25; case ceilingSentry: return 30; case redDemon: return 50; case whiteSeeker: return 70; case fatty: return 1200; default: return 0; } } void SetSprite(short idx) { FATSPRITE *cursprite = sprites + idx; SpriteStruct *curextra = sprites_extra + idx; switch(curextra->type) { case flamingShip: curextra->src = sprites_s; curextra->first_idx = TEXIDX_flamingShipA; cursprite->nr_textures = 1; cursprite->drawmode = SPRITEMODE_SIZE64; curextra->animated = TRUE; curextra->lastframe = 2; curextra->becomesHealth = FALSE; curextra->desire = ENEMY_NONE; curextra->life = INVINCIBLE; break; case deathAnim: curextra->src = projectilemodels; curextra->first_idx = SPRIDX_death1; cursprite->nr_textures = 1; cursprite->drawmode = SPRITEMODE_SIZE64; curextra->animated = TRUE; curextra->lastframe = 4; curextra->becomesHealth = FALSE; curextra->desire = ENEMY_NONE; curextra->life = 6; curextra->noexplode = TRUE; break; /*case instantHitShow: curextra->src = enemysprites; curextra->first_idx = TEXIDX_rexA; cursprite->nr_textures = 1; cursprite->drawmode = SPRITEMODE_SIZE64; curextra->animated = TRUE; curextra->lastframe = 0; curextra->becomesHealth = FALSE; curextra->desire = ENEMY_NONE; curextra->life = 2; curextra->noexplode = TRUE; break;*/ case mushroom: curextra->src = sprites_s; curextra->first_idx = TEXIDX_mushroom; cursprite->nr_textures = 1; curextra->animated = FALSE; cursprite->drawmode = SPRITEMODE_LOWER32; curextra->becomesHealth = FALSE; curextra->desire = ENEMY_NONE; curextra->life = 1; curextra->noexplode = TRUE; break; case grass: curextra->src = sprites_s; curextra->first_idx = TEXIDX_grass; cursprite->nr_textures = 1; curextra->animated = FALSE; cursprite->drawmode = SPRITEMODE_LOWER32; curextra->becomesHealth = FALSE; curextra->desire = ENEMY_NONE; curextra->life = 1; curextra->noexplode = TRUE; break; case plantA: curextra->src = sprites_s; curextra->first_idx = TEXIDX_plantA; cursprite->nr_textures = 1; curextra->animated = FALSE; cursprite->drawmode = SPRITEMODE_LOWER32; curextra->becomesHealth = FALSE; curextra->desire = ENEMY_NONE; curextra->life = 1; curextra->noexplode = TRUE; break; case treeA: curextra->src = sprites_s; curextra->first_idx = TEXIDX_treeA; cursprite->nr_textures = 1; curextra->animated = FALSE; cursprite->drawmode = SPRITEMODE_SIZE64; curextra->becomesHealth = FALSE; curextra->desire = ENEMY_NONE; curextra->life = INVINCIBLE; break; case tableA: curextra->src = sprites_s; curextra->first_idx = TEXIDX_tableA; cursprite->nr_textures = 1; curextra->animated = FALSE; cursprite->drawmode = SPRITEMODE_LOWER32; curextra->becomesHealth = FALSE; curextra->desire = ENEMY_NONE; curextra->life = INVINCIBLE; break; case tableB: curextra->src = sprites_s; curextra->first_idx = TEXIDX_tableB; cursprite->nr_textures = 1; curextra->animated = FALSE; cursprite->drawmode = SPRITEMODE_LOWER32; curextra->becomesHealth = FALSE; curextra->desire = ENEMY_NONE; curextra->life = INVINCIBLE; break; case healthLarge: curextra->src = sprites_s; curextra->first_idx = TEXIDX_healthLarge; cursprite->nr_textures = 1; curextra->animated = FALSE; cursprite->drawmode = SPRITEMODE_LOWER32; curextra->becomesHealth = FALSE; curextra->desire = ENEMY_NONE; curextra->life = INVINCIBLE; curextra->pickup = TRUE; break; case healthSmall: curextra->src = sprites_s; curextra->first_idx = TEXIDX_healthSmall; cursprite->nr_textures = 1; curextra->animated = FALSE; cursprite->drawmode = SPRITEMODE_LOWER32; curextra->becomesHealth = FALSE; curextra->desire = ENEMY_NONE; curextra->life = INVINCIBLE; curextra->pickup = TRUE; break; case healthPlant: curextra->src = sprites_s; curextra->first_idx = TEXIDX_healthPlant; cursprite->nr_textures = 1; curextra->animated = FALSE; cursprite->drawmode = SPRITEMODE_LOWER32; curextra->becomesHealth = FALSE; curextra->desire = ENEMY_NONE; curextra->life = INVINCIBLE; curextra->pickup = TRUE; break; case armorLight: curextra->src = sprites_s; curextra->first_idx = TEXIDX_armorLight; cursprite->nr_textures = 1; curextra->animated = FALSE; cursprite->drawmode = SPRITEMODE_LOWER32; curextra->becomesHealth = FALSE; curextra->desire = ENEMY_NONE; curextra->life = INVINCIBLE; curextra->pickup = TRUE; break; case armorHeavy: curextra->src = sprites_s; curextra->first_idx = TEXIDX_armorHeavy; cursprite->nr_textures = 1; curextra->animated = FALSE; cursprite->drawmode = SPRITEMODE_LOWER32; curextra->becomesHealth = FALSE; curextra->desire = ENEMY_NONE; curextra->life = INVINCIBLE; curextra->pickup = TRUE; break; case pistolAmmo: curextra->src = sprites_s; curextra->first_idx = TEXIDX_pistolAmmo; cursprite->nr_textures = 1; curextra->animated = FALSE; cursprite->drawmode = SPRITEMODE_SIZE64; curextra->becomesHealth = FALSE; curextra->desire = ENEMY_NONE; curextra->life = INVINCIBLE; curextra->pickup = TRUE; break; case machinegunAmmo: curextra->src = sprites_s; curextra->first_idx = TEXIDX_machinegunAmmo; cursprite->nr_textures = 1; curextra->animated = FALSE; cursprite->drawmode = SPRITEMODE_LOWER32; curextra->becomesHealth = FALSE; curextra->desire = ENEMY_NONE; curextra->life = INVINCIBLE; curextra->pickup = TRUE; break; case rocketlauncherAmmo: curextra->src = sprites_s; curextra->first_idx = TEXIDX_rocketlauncherAmmo; cursprite->nr_textures = 1; curextra->animated = FALSE; cursprite->drawmode = SPRITEMODE_LOWER32; curextra->becomesHealth = FALSE; curextra->desire = ENEMY_NONE; curextra->life = INVINCIBLE; curextra->pickup = TRUE; break; case plasmagunAmmo: curextra->src = sprites_s; curextra->first_idx = TEXIDX_plasmagunAmmo; cursprite->nr_textures = 1; curextra->animated = FALSE; cursprite->drawmode = SPRITEMODE_LOWER32; curextra->becomesHealth = FALSE; curextra->desire = ENEMY_NONE; curextra->life = INVINCIBLE; curextra->pickup = TRUE; break; case pistol: curextra->src = sprites_s; curextra->first_idx = TEXIDX_pistol; cursprite->nr_textures = 1; curextra->animated = FALSE; cursprite->drawmode = SPRITEMODE_LOWER32; curextra->becomesHealth = FALSE; curextra->desire = ENEMY_NONE; curextra->life = INVINCIBLE; curextra->pickup = TRUE; break; case machinegun: curextra->src = sprites_s; curextra->first_idx = TEXIDX_machinegun; cursprite->nr_textures = 1; curextra->animated = FALSE; cursprite->drawmode = SPRITEMODE_LOWER32; curextra->becomesHealth = FALSE; curextra->desire = ENEMY_NONE; curextra->life = INVINCIBLE; curextra->pickup = TRUE; break; case rocketlauncher: curextra->src = sprites_s; curextra->first_idx = TEXIDX_rocketlauncher; cursprite->nr_textures = 1; curextra->animated = FALSE; cursprite->drawmode = SPRITEMODE_LOWER32; curextra->becomesHealth = FALSE; curextra->desire = ENEMY_NONE; curextra->life = INVINCIBLE; curextra->pickup = TRUE; break; case plasmagun: curextra->src = sprites_s; curextra->first_idx = TEXIDX_plasmagun; cursprite->nr_textures = 1; curextra->animated = FALSE; cursprite->drawmode = SPRITEMODE_LOWER32; curextra->becomesHealth = FALSE; curextra->desire = ENEMY_NONE; curextra->life = INVINCIBLE; curextra->pickup = TRUE; break; case skeleton: curextra->src = enemysprites; curextra->first_idx = TEXIDX_skeleton05; cursprite->nr_textures = 1; cursprite->drawmode = SPRITEMODE_SIZE64; curextra->life = life(skeleton); curextra->pickup = FALSE; curextra->desire = ENEMY_SHOOTER | ENEMY_CHASER; curextra->speed = 2; curextra->weapon = WIset(Pistol); curextra->becomesHealth = becomesSmallHealth; curextra->animated = TRUE; curextra->lastframe = 3; curextra->pattern = walkingAnimation; curextra->delaycycles = 3; curextra->nr_textures = 2; break; case spider: curextra->src = enemysprites; curextra->first_idx = TEXIDX_spider05; cursprite->nr_textures = 1; cursprite->drawmode = SPRITEMODE_LOWER32; curextra->life = life(spider); curextra->pickup = FALSE; curextra->desire = ENEMY_SHOOTER | ENEMY_CHASER; curextra->speed = 4; curextra->weapon = WIset(SpiderGun); curextra->becomesHealth = becomesSmallHealth; curextra->animated = TRUE; curextra->lastframe = 3; curextra->pattern = walkingAnimation; curextra->delaycycles = 3; curextra->nr_textures = 2; break; case redDemon: curextra->src = enemysprites; curextra->first_idx = TEXIDX_redDemon05; cursprite->nr_textures = 1; cursprite->drawmode = SPRITEMODE_LOWER32; curextra->life = life(redDemon); curextra->pickup = FALSE; curextra->desire = ENEMY_SHOOTER | ENEMY_CHASER; curextra->speed = 2; curextra->weapon = WIset(RocketLauncher); curextra->becomesHealth = becomesLargeHealth; curextra->animated = TRUE; curextra->lastframe = 3; curextra->pattern = walkingAnimation; curextra->delaycycles = 3; curextra->nr_textures = 2; break; case whiteSeeker: curextra->src = enemysprites; curextra->first_idx = TEXIDX_whiteSeeker05; cursprite->nr_textures = 1; cursprite->drawmode = SPRITEMODE_SIZE64; curextra->life = life(whiteSeeker); curextra->pickup = FALSE; curextra->desire = ENEMY_SHOOTER | ENEMY_CHASER; curextra->speed = 3; curextra->weapon = WIset(RocketLauncher); curextra->becomesHealth = becomesLargeHealth; curextra->animated = TRUE; curextra->lastframe = 3; curextra->pattern = walkingAnimation; curextra->delaycycles = 3; curextra->nr_textures = 2; break; case fatty: curextra->src = enemysprites; curextra->first_idx = TEXIDX_fatty05; cursprite->nr_textures = 1; curextra->animated = FALSE; cursprite->drawmode = SPRITEMODE_SIZE64; curextra->life = life(fatty); curextra->pickup = FALSE; curextra->desire = ENEMY_SHOOTER | ENEMY_CHASER; curextra->speed = 1; curextra->weapon = WIset(SuperSlowRocketLauncher); curextra->becomesHealth = becomesLargeHealth; curextra->nr_textures = 2; break; case ceilingSentry: curextra->src = enemysprites; curextra->first_idx = TEXIDX_roofsentry; curextra->firesprite = TEXIDX_roofsentryFiring; cursprite->nr_textures = 1; curextra->animated = FALSE; cursprite->drawmode = SPRITEMODE_SIZE64; curextra->life = life(ceilingSentry); curextra->pickup = FALSE; curextra->desire = ENEMY_SHOOTER; curextra->weapon = WIset(MachineGun); curextra->becomesHealth = FALSE; curextra->nr_textures = 1; break; case deathBox: curextra->src = sprites_s; curextra->first_idx = TEXIDX_deathBox1; cursprite->nr_textures = 16; curextra->animated = FALSE; cursprite->drawmode = SPRITEMODE_LOWER32; curextra->becomesHealth = FALSE; curextra->desire = ENEMY_NONE; curextra->life = 1; curextra->pickup = FALSE; break; } cursprite->textures = curextra->src+curextra->first_idx; } static inline void InitSprite(char type, short x, short y, short idx) { FATSPRITE *cursprite = sprites + idx; SpriteStruct *curextra = sprites_extra + idx; //Set general info FAT_InitSprite (cursprite); InitSpriteData (idx); cursprite->xpos = x; cursprite->ypos = y; curextra->type = type; curextra->speed = 1; //Set specific info SetSprite(idx); /* if(cursprite->drawmode == SPRITEMODE_SIZE64) cursprite->drawmode = SPRITEMODE_UPPER32; else cursprite->drawmode = SPRITEMODE_UNUSED;//Water fakeness*/ curextra->weapon.ammo = curextra->weapon.cur->start_ammo; curextra->weapon.curdelay = curextra->weapon.cur->repeatdelay; curextra->weapon.cur_persistence = 0; curextra->drawmode = cursprite->drawmode; } static inline void SpriteLife(short idx, short adjust) { SpriteStruct *extra = sprites_extra + idx; if(extra->life == INVINCIBLE) return; extra->life += adjust; if(extra->life <= 0) { extra->life = 0; KillSprite(idx); } } void PlayerLife(short a) { short b = a; if(a<0) { if(player.armor) { a = (a*(MAX_ARMOR-player.armor))/((MAX_ARMOR*3)/2); player.armor -= (abs(b-a)); if(player.armor<0) player.armor = 0; } invert++; } player.life += a; if(player.life<=0) { player.life = 0; quitting = PLAYER_DIED; } } static inline void SpawnExplosion(short idx); static inline void KillSprite(short idx) { short a; FATSPRITE *f = sprites + idx; SpriteStruct *s = sprites_extra+idx; char h = s->becomesHealth; short x = f->xpos; short y = f->ypos; if(s->type==deathBox) SpawnExplosion(idx); if((sprite_count < MAX_SPRITES) && !s->life && !s->noexplode) { InitSprite(deathAnim,f->xpos,f->ypos,sprite_count); sprite_count++; } /* if(h) { InitSpriteData(idx); if(h == becomesLargeHealth) s->type = healthLarge; else s->type = healthSmall; SetSprite(idx); } else*/ { for(a=idx;amap_width*fc->map_height;a++) { if(sprite_count == MAX_SPRITES) return; //Can't handle any more Xvalue = fc->map_data[a]; if(Xvalue>=0) continue; if(Xvalue <= SPRITE_CODE_MIN && Xvalue >= SPRITE_CODE_MAX) { InitSprite(Xvalue,(a%fc->map_width)*64+32, (a/fc->map_width)*64+32,sprite_count); sprite_count++; } } } static inline void SpawnExplosion(short idx) { #define MAX_EXPLOSION_DAMAGE 200 #define MAX_EXPLOSION_DISTANCE_TILES (3L) #define MAX_EXPLOSION_DISTANCE ((MAX_EXPLOSION_DISTANCE_TILES<<6)*(MAX_EXPLOSION_DISTANCE_TILES<<6)) short a; FATSPRITE *spr = sprites + idx; short x = spr->xpos; short y = spr->ypos; short dx,dy,damage; long distance_squared; for(a=0;apickup) continue; dx = abs(s->xpos - x); dy = abs(s->ypos - y); if(dx>>6>5 || dy>>6>5) continue; distance_squared = ((long)dx*dx + (long)dy*dy); if(distance_squared>=MAX_EXPLOSION_DISTANCE) continue; distance_squared = MAX_EXPLOSION_DISTANCE - distance_squared; damage = (distance_squared*MAX_EXPLOSION_DAMAGE)/MAX_EXPLOSION_DISTANCE; SpriteLife(a,-damage); } dx = abs(fc->cam_xpos - x); dy = abs(fc->cam_ypos - y); if(dx>>6>5 || dy>>6>5) return; distance_squared = ((long)dx*dx + (long)dy*dy); if(distance_squared>=MAX_EXPLOSION_DISTANCE) return; distance_squared = MAX_EXPLOSION_DISTANCE - distance_squared; damage = (distance_squared*MAX_EXPLOSION_DAMAGE)/MAX_EXPLOSION_DISTANCE; PlayerLife(-damage); } static inline void AnimateStaticSprites(void) { short a = 0; for(a=0;aanimated == FALSE) continue; FATSPRITE *sprite = sprites + a; if(extra->cycles==extra->delaycycles) { extra->cycles = 0; if(extra->type==deathAnim || extra->type==instantHitShow) SpriteLife(a,-1); if(extra->pos < extra->lastframe) { extra->pos++; if(extra->pattern) sprite->textures = extra->src + extra->first_idx + extra->pattern[extra->pos]; else sprite->textures++; } else { sprite->textures = extra->src + extra->first_idx; extra->pos = 0; } } else if(extra->delaycycles) extra->cycles++; } } short giveAmmo(short i) { if(WeaponsValid[i]) { if(weapons[i].ammo < weapons[i].cur->maxammo) { weapons[i].ammo += weapons[i].cur->clip_ammo; if(weapons[i].ammo > weapons[i].cur->maxammo) weapons[i].ammo = weapons[i].cur->maxammo; return 1; //Successful } } return 0; //Unsuccessful } static inline void RunCustomSpriteTasks(short idx, short alive_idx) { short type = (sprites_extra + idx)->type; short success = 0; if(alive_idx==-1) { switch(type) { case healthSmall: if(player.lifeMAX_HEALTH) player.life = MAX_HEALTH; textEnqueue("+10 Health!"); } break; case healthLarge: if(player.lifeMAX_HEALTH) player.life = MAX_HEALTH; textEnqueue("+25 Health!"); } break; case healthPlant: if(player.lifeMAX_HEALTH) player.life = MAX_HEALTH; textEnqueue("+15 Health!"); } break; case armorLight: if(player.armorMAX_ARMOR) player.armor = MAX_ARMOR; textEnqueue("+50 Armor!"); } break; case armorHeavy: if(player.armorMAX_ARMOR) player.armor = MAX_ARMOR; textEnqueue("+100 Armor!"); } break; case pistolAmmo: if((success+=giveAmmo(PISTOL))) textEnqueue("Pistol Ammo!"); break; case machinegunAmmo: if((success+=giveAmmo(MACHINEGUN))) textEnqueue("MachGun Ammo!"); break; case rocketlauncherAmmo: if((success+=giveAmmo(ROCKETLAUNCHER))) textEnqueue("Rocket Ammo!"); break; case plasmagunAmmo: if((success+=giveAmmo(PLASMAGUN))) textEnqueue("Plasma Ammo!"); break; case deathBox: KillSprite(idx); success++; break; case pistol: GiveWeapon(PISTOL); success++; textEnqueue("Pistol!"); break; case machinegun: GiveWeapon(MACHINEGUN); success++; textEnqueue("Machinegun!"); break; case rocketlauncher: GiveWeapon(ROCKETLAUNCHER); success++; textEnqueue("RocketLauncher!"); break; case plasmagun: GiveWeapon(PLASMAGUN); success++; textEnqueue("PlasmaGun!"); break; } if((sprites_extra + idx)->pickup && success) KillSprite(idx); if(player.life<=0) quitting = PLAYER_DIED; } else { switch(type) { case deathBox: KillSprite(idx); success++; break; } if((sprites_extra + idx)->pickup && success) KillSprite(idx); } } static inline short SpriteHasCollisionCode(short type) { switch(type) { case deathBox: return 1; default: return 0; } } static inline void CheckForSpriteCollisions(void) { short a,i; FATSPRITE *alive = sprites; SpriteStruct *alive_extra = sprites_extra; FATSPRITE *spr = sprites; for(a=0;acam_xpos,spr->xpos,6) && iRoundEqual(fc->cam_ypos,spr->ypos,6)) //Collision { //textEnqueueF("Collision with #%i (%i).",a,(sprites_extra+a)->type); RunCustomSpriteTasks(a,-1); } ++spr; } for(i=0;idesire!=ENEMY_NONE) { spr = sprites; SpriteStruct *e = sprites_extra; for(a=0;atype)) { if(iRoundEqual(alive->xpos,spr->xpos,6) && iRoundEqual(alive->ypos,spr->ypos,6)) //Collision { RunCustomSpriteTasks(a,i); } } ++spr; ++e; } } ++alive; ++alive_extra; } } static inline void EnemyShoot(short idx); static inline void EnemyChase(short idx, unsigned long distance); static inline void SpritesExecute(void) { short a; FATSPRITE *spr = sprites; SpriteStruct *extra = sprites_extra; for(a=0;adesire; if(desire) { short px = fc->cam_xpos; short py = fc->cam_ypos; short ex = spr->xpos; short ey = spr->ypos; short dx = px - ex; //Must be this order short dy = py - ey; //Must be this order long distance_squared = (long)dx*dx + (long)dy*dy; unsigned long distance = FAT_FastSqrt(distance_squared); if(!extra->active) { if(spr->height == -1) continue; if(distance > (unsigned long)MAX_ACTING_DISTANCE) { if(extra->life == life(extra->type))//current health == starting health //(lets the enemy do stuff it it has been hit) { spr->orientation = FAT_ClampAngle(FAT_GetArcTanYX (dy, dx)-ANGLE_180); if(extra->nr_textures==2) { if(extra->pattern) spr->textures = extra->src + extra->first_idx + extra->pattern[extra->lastframe+1]; else spr->textures = extra->src + extra->first_idx + extra->lastframe + 1; } continue; } } if(RayBlocked(px,py,ex,ey)) //Wall in between player/enemy { if(extra->nr_textures==2) { if(extra->pattern) spr->textures = extra->src + extra->first_idx + extra->pattern[extra->lastframe+1]; else spr->textures = extra->src + extra->first_idx + extra->lastframe + 1; } continue; } extra->active = TRUE; } short angle = FAT_GetArcTanYX (dy, dx); if(desire & ENEMY_SHOOTER) { spr->orientation = angle; EnemyShoot(a); } if(desire & ENEMY_CHASER) { spr->orientation = angle; EnemyChase(a,distance); } } } }