// Number of frames and A.Speed for animations
// Walking
CONFIG TERRORPIN_FRAMES_WALK = 2;
CONFIG TERRORPIN_ASPEED_WALK = 8;
// Flipped over
CONFIG TERRORPIN_FRAMES_FLIP = 2;
CONFIG TERRORPIN_ASPEED_FLIP = 8;
// Settings for the enemy's flip arc
CONFIG TERRORPIN_FLIP_JUMP = 2.4; // Initial jump
CONFIG TERRORPIN_FLIP_BOUNCES = 2; // Extra bounces
CONFIG TERRORPIN_FLIP_DECAY = 0.3; // Decay of the jump value for each bounce
CONFIG TERRORPIN_FLIP_SPEED_MULT = 1.0; // Multiplier for the enemy's movement speed in mid air
@InitD0("Hammer Distance"),
@InitDHelp0("Distance in pixels the enemy detects hammer hits for. 40 by default."),
@InitD1("Flip Time"),
@InitDHelp1("How many frames the enemy stays flipped for. 180 by default."),
@InitD2("Can Reflip?"),
@InitDHelp2("If true, the hammer can still flip the enemy when vulnerable.")
npc script Terrorpin
{
using namespace NPCAnim;
using namespace GhostBasedMovement;
enum Animations
{
ANIM_WALK,
ANIM_FLIP
};
void run(int hammerDist, int flipTime, bool canReflip)
{
Waitspawn(this);
if(!hammerDist)
hammerDist = 40;
if(!flipTime)
flipTime = 180;
AnimHandler aptr = new AnimHandler(this);
aptr->AddAnim(ANIM_WALK, 0, TERRORPIN_FRAMES_WALK, TERRORPIN_ASPEED_WALK, 0);
aptr->AddAnim(ANIM_FLIP, TERRORPIN_FRAMES_WALK, TERRORPIN_FRAMES_FLIP, TERRORPIN_ASPEED_FLIP, 0);
int defenses[MAX_DEFENSE];
for(int i=0; i<MAX_DEFENSE; ++i)
{
defenses[i] = this->Defense[i];
this->Defense[i] = i==NPCD_HAMMER?NPCDT_IGNORE:NPCDT_BLOCK;
}
int counter = -1;
while(true)
{
// Walking
while(true)
{
counter = ConstWalk4(this, counter);
if(DetectHammer(this, hammerDist, false))
break;
Waitframe(this);
}
// Flipping
aptr->PlayAnim(ANIM_FLIP);
int jump = TERRORPIN_FLIP_JUMP;
this->Dir = AngleDir4(Angle(Link->X, Link->Y, this->X, this->Y));
for(int i=0; i<TERRORPIN_FLIP_BOUNCES+1; ++i)
{
this->Jump = jump;
jump *= TERRORPIN_FLIP_DECAY;
while(this->Jump>0||this->Z>0)
{
this->Move(OppositeDir(this->Dir), this->Step/100*TERRORPIN_FLIP_SPEED_MULT);
Waitframe(this);
}
}
// Make vulnerable
for(int i=0; i<MAX_DEFENSE; ++i)
this->Defense[i] = defenses[i];
int frames = flipTime;
while(true)
{
if(frames)
--frames;
if(frames<60)
{
this->DrawXOffset = (frames%4<2)?-1:1;
}
if(frames==0||(canReflip&&DetectHammer(this, hammerDist, true)))
break;
Waitframe(this);
}
this->DrawXOffset = 0;
// Make invulnerable
for(int i=0; i<MAX_DEFENSE; ++i)
this->Defense[i] = i==NPCD_HAMMER?NPCDT_IGNORE:NPCDT_BLOCK;
// Flipping
aptr->PlayAnim(ANIM_WALK);
jump = TERRORPIN_FLIP_JUMP;
this->Dir = AngleDir4(Angle(this->X, this->Y, Link->X, Link->Y));
for(int i=0; i<TERRORPIN_FLIP_BOUNCES+1; ++i)
{
this->Jump = jump;
jump *= TERRORPIN_FLIP_DECAY;
while(this->Jump>0||this->Z>0)
{
this->Move(this->Dir, this->Step/100*TERRORPIN_FLIP_SPEED_MULT);
Waitframe(this);
}
}
// Slide back onto the grid
int tX = GridX(this->X+8);
int tY = GridY(this->Y+8);
int safety = Ceiling(32/(this->Step/100));
while(safety&&Distance(this->X, this->Y, tX, tY)>this->Step/100)
{
--safety;
this->MoveAtAngle(Angle(this->X, this->Y, tX, tY), this->Step/100);
Waitframe(this);
}
counter = -1;
}
}
bool DetectHammer(npc this, int hammerDist, bool checkInvFrames)
{
if(this->HitBy[HIT_BY_LWEAPON])
return false;
if(checkInvFrames&&this->InvFrames)
return false;
for(int i=Screen->NumLWeapons(); i>0; --i)
{
lweapon l = Screen->LoadLWeapon(i);
if(l->ID==LW_HAMMER)
{
if((l->Tile-l->OriginalTile)%3==2)
{
int cx = l->X+l->HitWidth/2;
int cy = l->Y+l->HitHeight/2;
if(Distance(this->X+8, this->Y+8, cx, cy)<hammerDist)
return true;
}
}
}
return false;
}
}