//import "std.zh"
//import "string.zh"
//import "ghost.zh"
// Idle settings
const int BTEK_MIN_IDLE_TIME = 45;
const int BTEK_MAX_IDLE_TIME = 90;
const int BTEK_MIN_FIRE_TIME = 25;
const int BTEK_MAX_FIRE_TIME = 60;
// Regular jump settings
const int BTEK_JUMP_CHARGE_TIME = 15;
const float BTEK_MIN_JUMPS_FACTOR = 0.5; // Used with halt rate
const float BTEK_MAX_JUMPS_FACTOR = 1.5; // Used with halt rate
const float BTEK_MIN_JUMP_VEL = 2;
const float BTEK_MAX_JUMP_VEL = 4.5;
// Super jump settings
const int BTEK_SJUMP_CHARGE_TIME = 60;
const float BTEK_SJUMP_DAMAGE_MULTIPLIER = 2;
const float BTEK_SJUMP_SPEED = 8;
const int BTEK_SJUMP_MIN_TIME = 240;
const int BTEK_SJUMP_MAX_TIME = 360;
const int BTEK_SJUMP_QUAKE_TIME = 90;
// Super jump - launched debris
const int BTEK_EW_DEBRIS = 31; // Also applies to dropped debris
const int BTEK_MIN_DEBRIS_LAUNCHED = 8;
const int BTEK_MAX_DEBRIS_LAUNCHED = 12;
const int BTEK_MIN_DEBRIS_STEP = 100;
const int BTEK_MAX_DEBRIS_STEP = 200;
const float BTEK_MIN_DEBRIS_VEL = 1.5;
const float BTEK_MAX_DEBRIS_VEL = 2.5;
// Super jump - falling objects
const int BTEK_ENEMY_LIMIT = 10;
const int BTEK_MIN_ENEMIES_DROPPED = 4; // Fewer will be used if near the limit
const int BTEK_MAX_ENEMIES_DROPPED = 5;
const int BTEK_MIN_DEBRIS_DROPPED = 10;
const int BTEK_MAX_DEBRIS_DROPPED = 15;
// Default settings (can be overridden by attributes)
const int BTEK_DEFAULT_SIZE = 2;
const int BTEK_DEFAULT_DEBRIS_SPRITE = 18;
const int BTEK_DEFAULT_STUN_TIME = 90;
const int BTEK_DEFAULT_DEBRIS_DAMAGE = 4; // Overridden by weapon damage
// npc->Attributes[] indices
const int BTEK_ATTR_SIZE = 0;
const int BTEK_ATTR_FLAGS = 1;
const int BTEK_ATTR_STUN_TIME = 2;
const int BTEK_ATTR_DROPPED_OBJECT = 3;
const int BTEK_ATTR_SFX_JUMP = 4;
const int BTEK_ATTR_SFX_SJUMP = 5;
const int BTEK_ATTR_SFX_FALL = 6;
const int BTEK_ATTR_SFX_LAND = 7;
const int BTEK_ATTR_DEBRIS_SPRITE = 8;
const int BTEK_ATTR_DEATH_TYPE = 9;
// ffc->Misc[] indices
const int BTEK_IDX_BASE_COMBO = 0;
const int BTEK_IDX_LINK_STUN_TIME = 1;
// Combo offsets
const int BTEK_CMB_IDLE = 0;
const int BTEK_CMB_CROUCH = 1;
const int BTEK_CMB_JUMP = 2;
const int BTEK_CMB_STUN = 3;
// Flags
const int BTEK_FLAG_FREEZABLE = 00000001b; // 1
const int BTEK_FLAG_NO_SJUMP = 00000010b; // 2
const int BTEK_FLAG_STUN_LINK = 00000100b; // 4
const int BTEK_FLAG_DEBRIS = 00001000b; // 8
const int BTEK_FLAG_DROP_OBJECTS = 00010000b; // 16
const int BTEK_FLAG_STUN_TO_HIT = 00100000b; // 32
const int BTEK_FLAG_UNBLOCKABLE = 01000000b; // 64
const int BTEK_FLAG_INTRO = 10000000b; // 128
ffc script BigTektite
{
void run(int enemyID)
{
npc ghost=Ghost_InitAutoGhost(this, enemyID);
this->Flags[FFCF_OVERLAY]=true;
Ghost_SetFlag(GHF_IGNORE_ALL_TERRAIN);
if((ghost->Attributes[BTEK_ATTR_FLAGS]&BTEK_FLAG_FREEZABLE)!=0)
{
Ghost_SetFlag(GHF_STUN);
Ghost_SetFlag(GHF_CLOCK);
}
this->Misc[BTEK_IDX_BASE_COMBO]=Ghost_Data;
int defenses[18];
if((ghost->Attributes[BTEK_ATTR_FLAGS]&BTEK_FLAG_STUN_TO_HIT)!=0)
{
Ghost_StoreDefenses(ghost, defenses);
Ghost_SetAllDefenses(ghost, NPCDT_BLOCK);
}
Ghost_TileWidth=Ghost_GetAttribute(ghost, BTEK_ATTR_SIZE, BTEK_DEFAULT_SIZE, 1, 4);
Ghost_TileHeight=Ghost_TileWidth;
if(Ghost_TileHeight>1)
Ghost_SetHitOffsets(ghost, 0.33, 0.1, 0, 0);
if((ghost->Attributes[BTEK_ATTR_FLAGS]&BTEK_FLAG_INTRO)!=0)
DoIntro(this, ghost);
else if(Ghost_Z==0)
Ghost_SpawnAnimationPuff(this, ghost);
else // Dropping from the ceiling?
{
while(Ghost_Z>0)
BTWaitframe(this, ghost);
}
int numJumps;
while(true)
{
// The for loop doesn't make much sense if super jumps are disabled,
// but whatever...
for(int i=Rand(3, 5); i>0; i--)
{
Idle(this, ghost);
// Number of successive jumps is based on halt rate
numJumps=Rand((16-ghost->Haltrate)*BTEK_MIN_JUMPS_FACTOR,
(16-ghost->Haltrate)*BTEK_MAX_JUMPS_FACTOR);
if(numJumps<1)
numJumps=1;
for(; numJumps>0; numJumps--)
Jump(this, ghost);
}
if((ghost->Attributes[BTEK_ATTR_FLAGS]&BTEK_FLAG_NO_SJUMP)==0)
{
Idle(this, ghost);
SuperJump(this, ghost, defenses);
}
}
}
// ========== Idle ==========
// Just stand in place, maybe shooting fireballs or something.
void Idle(ffc this, npc ghost)
{
int idleTime=Rand(BTEK_MIN_IDLE_TIME, BTEK_MAX_IDLE_TIME);
int fireTime=Rand(BTEK_MIN_FIRE_TIME, BTEK_MAX_FIRE_TIME);
Ghost_Data=this->Misc[BTEK_IDX_BASE_COMBO]+BTEK_CMB_IDLE;
// Wait until timer counts down or the tektite is hit
for(; idleTime>0 && !Ghost_GotHit(); idleTime--)
{
fireTime--;
if(fireTime==0)
{
FireIdleWeapon(ghost);
fireTime=Rand(BTEK_MIN_FIRE_TIME, BTEK_MAX_FIRE_TIME);
}
BTWaitframe(this, ghost);
}
}
// Fire any weapons used while standing on the ground
void FireIdleWeapon(npc ghost)
{
int weaponID=WeaponTypeToID(ghost->Weapon);
int flags=0;
if((ghost->Attributes[BTEK_ATTR_FLAGS]&BTEK_FLAG_UNBLOCKABLE)!=0)
flags|=EWF_UNBLOCKABLE;
// Angular weapons
if(weaponID==EW_FIREBALL || weaponID==EW_BRANG)
{
FireAimedEWeapon(weaponID, BTCenterX()-8, BTCenterY()-8,
0, 200, ghost->WeaponDamage, -1, -1, flags);
}
// Fireball 2: 3-way
else if(weaponID==EW_FIREBALL2)
{
eweapon fb;
fb=FireAimedEWeapon(weaponID, BTCenterX()-8, BTCenterY()-8,
0, 200, ghost->WeaponDamage, -1, -1, flags);
SetEWeaponMovement(fb, EWM_DRIFT_WAIT, DIR_UP, 0);
fb=FireAimedEWeapon(weaponID, BTCenterX()-8, BTCenterY()-8,
0, 200, ghost->WeaponDamage, -1, -1, flags);
SetEWeaponMovement(fb, EWM_DRIFT_WAIT, DIR_UP, 0.5);
fb=FireAimedEWeapon(weaponID, BTCenterX()-8, BTCenterY()-8,
0, 200, ghost->WeaponDamage, -1, -1, flags);
SetEWeaponMovement(fb, EWM_DRIFT_WAIT, DIR_DOWN, 0.5);
}
// Non-angular weapons
else if(weaponID==EW_WIND || weaponID==EW_ARROW || weaponID==EW_BEAM ||
weaponID==EW_ROCK || weaponID==EW_MAGIC)
{
if(!(weaponID==EW_WIND || weaponID==EW_ROCK))
flags|=EWF_ROTATE;
int dir=AngleDir4(Angle(BTCenterX(), BTCenterY(),
Link->X+8, Link->Y+8));
FireNonAngularEWeapon(weaponID, BTCenterX()-8, BTCenterY()-8,
dir, 200, ghost->WeaponDamage, -1, -1, flags);
}
}
// ========== Jumping ==========
void Jump(ffc this, npc ghost)
{
float jumpHeight;
float angle;
lweapon bait;
// Find bait if there's any chance the tektite will go for it
if(ghost->Hunger>0)
bait=LoadLWeaponOf(LW_BAIT);
// Charge for a moment
Ghost_Data=this->Misc[BTEK_IDX_BASE_COMBO]+BTEK_CMB_CROUCH;
for(int i=0; i<BTEK_JUMP_CHARGE_TIME; i++)
BTWaitframe(this, ghost);
// Decide where to jump
// Go for bait?
if(Rand(4)<ghost->Hunger && bait->isValid())
{
float dist=BTDistanceTo(bait->X+8, bait->Y+8);
jumpHeight=GH_GRAVITY*dist/(ghost->Step/50);
angle=Angle(BTCenterX(), BTCenterY(), bait->X+8, bait->Y+8);
}
// Try to hit Link?
else if(Rand(256)<ghost->Homing)
{
// A rough approximation, but it works pretty well.
float dist=BTDistanceTo(Link->X+8, Link->Y+8);
jumpHeight=GH_GRAVITY*dist/(ghost->Step/50);
angle=Angle(BTCenterX(), BTCenterY(), Link->X+8, Link->Y+8);
}
// Jump randomly
else
{
jumpHeight=Rand(BTEK_MIN_JUMP_VEL, BTEK_MAX_JUMP_VEL);
angle=Rand(360);
}
// In case the target wasn't actually in range...
jumpHeight=Clamp(jumpHeight, BTEK_MIN_JUMP_VEL, BTEK_MAX_JUMP_VEL);
// Jump
Ghost_Data=this->Misc[BTEK_IDX_BASE_COMBO]+BTEK_CMB_JUMP;
Game->PlaySound(ghost->Attributes[BTEK_ATTR_SFX_JUMP]);
Ghost_Jump=jumpHeight;
Ghost_Vx=VectorX(ghost->Step/100, angle);
Ghost_Vy=VectorY(ghost->Step/100, angle);
FireJumpingWeapon(ghost);
// And then just wait to land
do
{
BounceOffScreenEdges();
BTWaitframe(this, ghost);
} while(Ghost_Z>0);
FireLandingWeapon(ghost);
Ghost_Vx=0;
Ghost_Vy=0;
}
// Leave a weapon behind when jumping (bomb, super bomb, fire trail)
void FireJumpingWeapon(npc ghost)
{
eweapon wpn;
if(ghost->Weapon==WPN_ENEMYLITBOMB)
{
// Always unblockable
wpn=FireNonAngularEWeapon(EW_BOMB, BTCenterX()-8, BTCenterY()-8,
0, 0, 0, -1, -1, EWF_UNBLOCKABLE);
SetEWeaponLifespan(wpn, EWL_TIMER, 30);
SetEWeaponDeathEffect(wpn, EWD_EXPLODE, ghost->WeaponDamage);
}
else if(ghost->Weapon==WPN_ENEMYLITSBOMB)
{
wpn=FireNonAngularEWeapon(EW_SBOMB, BTCenterX()-8, BTCenterY()-8,
0, 0, 0, -1, -1, EWF_UNBLOCKABLE);
SetEWeaponLifespan(wpn, EWL_TIMER, 30);
SetEWeaponDeathEffect(wpn, EWD_SBOMB_EXPLODE, ghost->WeaponDamage);
}
else if(ghost->Weapon==WPN_ENEMYFIRETRAIL)
{
// 1x1: Single flame
if(Ghost_TileWidth==1)
{
wpn=FireNonAngularEWeapon(EW_FIRETRAIL, Ghost_X, Ghost_Y,
0, 0, ghost->WeaponDamage, -1, -1, EWF_UNBLOCKABLE);
SetEWeaponLifespan(wpn, EWL_TIMER, 30);
SetEWeaponDeathEffect(wpn, EWD_VANISH, 0);
}
// 2x2 or bigger: One on each tile except on the top row
else
{
for(int row=1; row<Ghost_TileHeight; row++)
{
for(int col=0; col<Ghost_TileWidth; col++)
{
wpn=FireNonAngularEWeapon(EW_FIRETRAIL,
Ghost_X+16*col, Ghost_Y+16*row,
0, 0, ghost->WeaponDamage,
-1, -1, EWF_UNBLOCKABLE);
SetEWeaponLifespan(wpn, EWL_TIMER, 60);
SetEWeaponDeathEffect(wpn, EWD_VANISH, 0);
}
}
}
}
}
// Use weapons when landing (fire, explosion)
void FireLandingWeapon(npc ghost)
{
int weaponID;
// Explosions are disabled for other-type enemies in 2.50.
// This should work in 2.50.1, though...
if(ghost->Weapon==WPN_ENEMYBOMB || ghost->Weapon==WPN_ENEMYSBOMB)
{
eweapon wpn;
if(ghost->Weapon==WPN_ENEMYBOMB)
weaponID=EW_BOMBBLAST;
else
weaponID=EW_SBOMBBLAST;
wpn=Screen->CreateEWeapon(weaponID);
wpn->Damage=ghost->WeaponDamage;
wpn->Dir=-1;
wpn->X=BTCenterX()-8;
wpn->Y=BTCenterY()-8;
}
// 8-way fires
// WPN_ENEMYFLAME2 is incorrect...
else if(ghost->Weapon==WPN_ENEMYFLAME || ghost->Weapon==142)
{
int i;
int flags=0;
if((ghost->Attributes[BTEK_ATTR_FLAGS]&BTEK_FLAG_UNBLOCKABLE)!=0)
flags|=EWF_UNBLOCKABLE;
if(ghost->Weapon==WPN_ENEMYFLAME)
weaponID=EW_FIRE;
else
weaponID=EW_FIRE2;
for(i=0; i<4; i++)
FireNonAngularEWeapon(weaponID,
BTCenterX()-8, BTCenterY()-8,
i, 100, ghost->WeaponDamage, -1, 0, flags);
for(i=4; i<8; i++)
FireNonAngularEWeapon(weaponID,
BTCenterX()-8, BTCenterY()-8,
i, 71, ghost->WeaponDamage, -1, 0, flags);
Game->PlaySound(SFX_FIRE);
}
}
void BounceOffScreenEdges()
{
if(Ghost_X<=16)
Ghost_Vx=Abs(Ghost_Vx);
else if(Ghost_X+16*Ghost_TileWidth>=240)
Ghost_Vx=-Abs(Ghost_Vx);
if(Ghost_Y<=16)
Ghost_Vy=Abs(Ghost_Vy);
else if(Ghost_Y+16*Ghost_TileHeight>=160)
Ghost_Vy=-Abs(Ghost_Vy);
}
// ========== Super jump ==========
void SuperJump(ffc this, npc ghost, int defenses)
{
int timer;
float targetAngle;
float targetSpeed;
float dist;
float targetVx;
float targetVy;
// Charge up
Ghost_Data=this->Misc[BTEK_IDX_BASE_COMBO]+BTEK_CMB_CROUCH;
for(timer=0; timer<BTEK_SJUMP_CHARGE_TIME; timer++)
BTWaitframe(this, ghost);
// Jump...
Ghost_SetFlag(GHF_NO_FALL); // Override gravity handling
Ghost_Data=this->Misc[BTEK_IDX_BASE_COMBO]+BTEK_CMB_JUMP;
Game->PlaySound(ghost->Attributes[BTEK_ATTR_SFX_SJUMP]);
while(Ghost_Z<176)
{
Ghost_Z+=BTEK_SJUMP_SPEED;
BTWaitframe(this, ghost);
}
// Follow Link around for a while
for(timer=Rand(BTEK_SJUMP_MIN_TIME, BTEK_SJUMP_MAX_TIME); timer>0; timer--)
{
dist=BTDistanceTo(Link->X+8, Link->Y+8);
// If far from Link, adjust aim; otherwise, try to stop
if(dist>4)
{
targetAngle=Angle(BTCenterX(), BTCenterY(), Link->X+8, Link->Y+8);
targetSpeed=ghost->Step/200;
}
else
targetSpeed=0;
// Don't change direction suddenly
targetVx=targetSpeed*Cos(targetAngle);
targetVy=targetSpeed*Sin(targetAngle);
if(Ghost_Vx<targetVx)
Ghost_Vx=Min(Ghost_Vx+0.05, targetVx);
else if(Ghost_Vx>targetVx)
Ghost_Vx=Max(Ghost_Vx-0.05, targetVx);
if(Ghost_Vy<targetVy)
Ghost_Vy=Min(Ghost_Vy+0.05, targetVy);
else if(Ghost_Vy>targetVy)
Ghost_Vy=Max(Ghost_Vy-0.05, targetVy);
BTWaitframe(this, ghost);
}
// Fall
Ghost_Vx=0;
Ghost_Vy=0;
ghost->Damage*=BTEK_SJUMP_DAMAGE_MULTIPLIER;
Game->PlaySound(ghost->Attributes[BTEK_ATTR_SFX_FALL]);
while(Ghost_Z>0)
{
Ghost_Z-=BTEK_SJUMP_SPEED;
BTWaitframe(this, ghost);
}
// Land
Ghost_Z=0;
ghost->Damage/=BTEK_SJUMP_DAMAGE_MULTIPLIER;
LaunchDebris(ghost);
DropObjects(ghost);
Screen->Quake=BTEK_SJUMP_QUAKE_TIME;
Game->PlaySound(ghost->Attributes[BTEK_ATTR_SFX_LAND]);
if((ghost->Attributes[BTEK_ATTR_FLAGS]&BTEK_FLAG_STUN_LINK)!=0 && Link->Z==0)
this->Misc[BTEK_IDX_LINK_STUN_TIME]=BTEK_SJUMP_QUAKE_TIME;
// Be stunned for a bit
if((ghost->Attributes[BTEK_ATTR_FLAGS]&BTEK_FLAG_STUN_TO_HIT)!=0)
Ghost_SetDefenses(ghost, defenses);
Ghost_Data=this->Misc[BTEK_IDX_BASE_COMBO]+BTEK_CMB_STUN;
timer=Ghost_GetAttribute(ghost, BTEK_ATTR_STUN_TIME, BTEK_DEFAULT_STUN_TIME);
for(; timer>0; timer--)
BTWaitframe(this, ghost);
if((ghost->Attributes[BTEK_ATTR_FLAGS]&BTEK_FLAG_STUN_TO_HIT)!=0)
Ghost_SetAllDefenses(ghost, NPCDT_BLOCK);
Ghost_UnsetFlag(GHF_NO_FALL);
}
void LaunchDebris(npc ghost)
{
if((ghost->Attributes[BTEK_ATTR_FLAGS]&BTEK_FLAG_DEBRIS)==0)
return;
int count=Rand(BTEK_MIN_DEBRIS_LAUNCHED, BTEK_MAX_DEBRIS_LAUNCHED);
int sprite=Ghost_GetAttribute(ghost, BTEK_ATTR_DEBRIS_SPRITE,
BTEK_DEFAULT_DEBRIS_SPRITE);
int damage=ghost->WeaponDamage;
if(damage==0)
damage=BTEK_DEFAULT_DEBRIS_DAMAGE;
float angle;
eweapon debris;
for(; count>0; count--)
{
angle=Randf(PI2);
// Large function calls!
debris=FireEWeapon(BTEK_EW_DEBRIS,
BTCenterX()-8+8*RadianCos(angle),
BTCenterY()-8+8*RadianSin(angle),
angle,
Rand(BTEK_MIN_DEBRIS_STEP, BTEK_MAX_DEBRIS_STEP),
damage,
sprite,
0,
EWF_SHADOW|EWF_UNBLOCKABLE);
SetEWeaponMovement(debris,
EWM_THROW,
Randf(BTEK_MIN_DEBRIS_VEL, BTEK_MAX_DEBRIS_VEL),
EWMF_DIE);
SetEWeaponDeathEffect(debris, EWD_VANISH, 0);
}
}
void DropObjects(npc ghost)
{
// Don't do anything if the flag isn't set.
if((ghost->Attributes[BTEK_ATTR_FLAGS]&BTEK_FLAG_DROP_OBJECTS)==0)
return;
// Or if the object to drop isn't valid.
int objID=ghost->Attributes[BTEK_ATTR_DROPPED_OBJECT];
if(objID!=0 && !(objID>=20 && objID<=511))
return;
// Drop debris
if(objID==0)
{
eweapon debris;
int count=Rand(BTEK_MIN_DEBRIS_DROPPED, BTEK_MAX_DEBRIS_DROPPED);
int sprite=Ghost_GetAttribute(ghost, BTEK_ATTR_DEBRIS_SPRITE,
BTEK_DEFAULT_DEBRIS_SPRITE);
int damage=ghost->WeaponDamage;
if(damage==0)
damage=BTEK_DEFAULT_DEBRIS_DAMAGE;
for(; count>0; count--)
{
debris=FireEWeapon(BTEK_EW_DEBRIS, Rand(16, 224), Rand(16, 144),
0, 0, damage, sprite, 0,
EWF_SHADOW|EWF_UNBLOCKABLE);
SetEWeaponMovement(debris, EWM_FALL, Rand(160, 224), EWMF_DIE);
SetEWeaponDeathEffect(debris, EWD_VANISH, 0);
}
}
// Drop enemies
else
{
// Don't do anything if there are too many already
if(Screen->NumNPCs()>=BTEK_ENEMY_LIMIT)
return;
npc enemy;
int count=Rand(BTEK_MIN_ENEMIES_DROPPED, BTEK_MAX_ENEMIES_DROPPED);
if(Screen->NumNPCs()+count>BTEK_ENEMY_LIMIT)
count=BTEK_ENEMY_LIMIT-Screen->NumNPCs();
for(; count>0; count--)
{
enemy=SpawnNPC(objID);
enemy->Z=Rand(160, 224);
}
}
}
// ========== Other ==========
// Fall onto the screen at the start of the fight
void DoIntro(ffc this, npc ghost)
{
Ghost_X=128-8*Ghost_TileWidth;
Ghost_Y=96-16*Ghost_TileHeight;
Ghost_Z=176;
Ghost_SetFlag(GHF_NO_FALL);
Ghost_SetFlag(GHF_STATIC_SHADOW);
ghost->CollDetection=false;
// Wait half a second, then fall
Ghost_Data=this->Misc[BTEK_IDX_BASE_COMBO]+BTEK_CMB_JUMP;
Ghost_Waitframes(this, ghost, true, true, 60);
Game->PlaySound(ghost->Attributes[BTEK_ATTR_SFX_FALL]);
while(Ghost_Z>0)
{
Ghost_Z-=BTEK_SJUMP_SPEED;
Ghost_Waitframe(this, ghost, true, true);
}
// On impact, shake the screen and disable Link for a moment
Screen->Quake=BTEK_SJUMP_QUAKE_TIME;
Ghost_Data=this->Misc[BTEK_IDX_BASE_COMBO]+BTEK_CMB_CROUCH;
Game->PlaySound(ghost->Attributes[BTEK_ATTR_SFX_LAND]);
while(Screen->Quake>0)
{
NoAction();
Ghost_Waitframe(this, ghost, true, true);
}
// Back to normal
Ghost_UnsetFlag(GHF_NO_FALL);
Ghost_UnsetFlag(GHF_STATIC_SHADOW);
ghost->CollDetection=true;
}
void BTWaitframe(ffc this, npc ghost)
{
// Is Link stunned?
if(this->Misc[BTEK_IDX_LINK_STUN_TIME]>0)
{
NoAction();
this->Misc[BTEK_IDX_LINK_STUN_TIME]--;
}
bool noDeathAnim=ghost->Attributes[BTEK_ATTR_DEATH_TYPE]==0;
if(!Ghost_Waitframe(this, ghost, noDeathAnim, noDeathAnim))
{
Ghost_DeathAnimation(this, ghost, ghost->Attributes[BTEK_ATTR_DEATH_TYPE]);
Quit();
}
}
float BTCenterX()
{
return Ghost_X+8*Ghost_TileWidth;
}
// Doesn't really return the center unless the tektite is 1x1; if it's
// bigger, it's actually the center of the shadow, which might be either
// 1x1 or 2x2 depending on size and ghost.zh settings.
float BTCenterY()
{
if(GH_FAKE_Z!=0 && GH_LARGE_SHADOW_TILE!=0)
{
if(Ghost_TileHeight==1) // 1x1 shadow
return Ghost_Y+8;
else if(Ghost_TileHeight==2) // 1x1
return Ghost_Y+24;
else if(Ghost_TileHeight==3) // 2x2
return Ghost_Y+32;
else // 2x2
return Ghost_Y+48;
}
else // 1x1
{
if(Ghost_TileHeight==1)
return Ghost_Y+8;
else
return Ghost_Y+16+8*(Ghost_TileHeight-1); // 24, 40, 56
}
}
// Distance from "center" to a given point
float BTDistanceTo(float x, float y)
{
float dx=BTCenterX()-x;
float dy=BTCenterY()-y;
return Sqrt(dx*dx+dy*dy);
}
}