import "std.zh"
import "ffcscript.zh"
import "ghost.zh"
import "string.zh"
//Screen dimensions
const int screenWidth = 256;
const int screenHeight = 176;
//Acid pool constants
const int ACID_MAX_ON_SCREEN = 8; //Max number of acid puddles allowed on screen
//Mist colors (color = CSet# * 16 + color within CSet from 0 to 15)
const int COLOR_MIST_YELLOW = 6;
const int COLOR_MIST_RED = 5;
const int COLOR_MIST_GREEN = 82;
const int COLOR_MIST_BLUE = 3;
//AcidHandla enemy (place this one on screen)
//Type: Other
//Attrib 1: ID of head enemy
//Attrib 2: Max time between head sockets dropping acid (Recommend 120)
//Attrib 3: Acid combo (Set the CSet2 attribute so that it looks right with CSet 0)
//Attrib 4: Acid duration (120)
//Attrib 5: Maximum time between mist changes (240)
//Attrib 6: Minimum time between mist changes (180)
//Attrib 7: Time until heads respawn (240)
//Attrib 11: -1
//Attrib 12: Slot # of this script
//Weapon damage: Frames between acid damaging Link (10)
//Random rate: Frames before changing direction / 10 (e.g. 4 = 40 frames)
//HP, Damage, Step, Frame Rate: Set as normal
//Head enemy
//Type: Walking enemy
//Weapon Shot Type: Rapid Fire
//Set up all attributes as normal, including weapon of choice
ffc script acidHandla{
void run(int enemyID){
npc ghost = Ghost_InitAutoGhost(this, enemyID, GHF_NORMAL); //Load main enemy
npc heads[4]; //Acidhandla's heads - fireballs while alive, acid when dead
int headXY[8] = { 0,-15, 0,15, -15,0, 15,0 }; //Head X/Y offset in order UDLR
int acidTime[4]; //Time before each head socket drops acid
int storeDefense[18]; //Store defenses while invulnerable
int mistCounter; //Time before changing mist on/off
int mistType; //Currently active mist type
//0 = None
//1 = Yellow (switch button inputs)
//2 = Red (player and boss slow down)
//3 = Green (player and boss speed up)
//4 = Blue (instantly restore one head)
int curStep = ghost->Step; //Red/Green mists modify step speed
int headlessStep; //Step speed addition: (4-#heads)*15
int moveCountdown = ghost->Rate*10; //Time before changing direction
//==== Setup ====
//Load attributes
int headID = ghost->Attributes[0];
int acidDropRate = ghost->Attributes[1];
int acidSprite = ghost->Attributes[2];
int acidDuration = ghost->Attributes[3];
int hurtFreq = ghost->WeaponDamage;
int mistTimeMax = ghost->Attributes[4];
int mistTimeMin = ghost->Attributes[5];
int respawnRate = ghost->Attributes[6];
int respawnTime = respawnRate; //Time before heads respawn (all at once, starting when they're all gone)
//Find phase-change HP values
int phase2HP = ghost->HP / 2;
//int phase3HP = ghost->HP / 3;
//Create heads
for ( int i = 0; i < 4; i++ ){
heads[i] = Screen->CreateNPC(headID);
}
//Store defenses and make body invulnerable
for ( int i = 0; i < 18; i++ ){
storeDefense[i] = ghost->Defense[i];
ghost->Defense[i] = NPCDT_QUARTERDAMAGE;
}
//==== Begin loop ====
while ( ghost->HP > 0 ){
//Manage heads
int headCount;
for ( int i = 0; i < 4; i++ ){
if ( !heads[i]->isValid() ){ //If dead
if ( acidTime[i] <= 0 ){ //Acid time is up
int ffcScriptName[] = "acidPool";
int ffcScriptNum = Game->GetFFCScript(ffcScriptName);
if ( CountFFCsRunning(ffcScriptNum) < ACID_MAX_ON_SCREEN ){ //Fewer than max acid
int x = ghost->X + headXY[i*2];
int y = ghost->Y + headXY[i*2+1];
int args[5] = { x, y, acidSprite, hurtFreq, acidDuration };
RunFFCScript(ffcScriptNum, args); //Drop some!
}
acidTime[i] = Rand(acidDropRate) + 1; //Reset time to random length
}
else //Otherwise decrement acid counter
acidTime[i]--;
}
else{ //Alive: keep in place
headCount++;
//Heads should handle their own fireballs
heads[i]->Dir = i; //Keep it facing in right direction
heads[i]->X = ghost->X + headXY[i*2];
heads[i]->Y = ghost->Y + headXY[i*2 + 1];
}
}
//If all heads destroyed, restore defenses
if ( headCount <= 0 ){
if ( respawnTime <= 0 ){ //If respawn time up
respawnTime = respawnRate;
for ( int i = 0; i < 4; i++ ){ //Regenerate heads
heads[i] = Screen->CreateNPC(headID);
heads[i]->X = ghost->X + headXY[i*2];
heads[i]->Y = ghost->Y + ghost->Y + headXY[i*2+1];
}
}
else
respawnTime--; //If set, count down to respawn; regeneration handled above
for ( int i = 0; i < 18; i++ )
ghost->Defense[i] = storeDefense[i];
}
//Otherwise make invulnerable (if heads respawn, takes effect next frame
else{
for ( int i = 0; i < 18; i++ )
ghost->Defense[i] = NPCDT_QUARTERDAMAGE;
respawnTime = respawnRate;
}
//Mist change
if ( mistCounter <= 0 && ghost->HP <= phase2HP ){ //If counter hits 0 and boss is on phase 2
mistCounter = Rand(mistTimeMax-mistTimeMin) + mistTimeMin; //Set random time
if ( mistType > 0 ) //If mist is active, disable it
mistType = 0;
else{ //If mist is inactive, enable it
mistType = Rand(4) + 1;
if ( mistType == 4 ){ //If blue
for ( int i = 0; i < 4; i++ ){ //Check each head
if ( !heads[i]->isValid() ){ //If dead
heads[i] = Screen->CreateNPC(headID); //Regenerate it
heads[i]->X = ghost->X + headXY[i*2];
heads[i]->Y = ghost->Y + ghost->Y + headXY[i*2+1];
break; //Then stop. Only one head is regenerated.
}
}
}
}
}
else if ( mistCounter > 0 )
mistCounter--;
//Mist effects
if ( mistType == 0 ) //None: Reset speed
curStep = ghost->Step;
else if ( mistType == 1 ){ //Yellow: Switch inputs
Screen->Rectangle(6, 0, 0, screenWidth, screenHeight, COLOR_MIST_YELLOW, 1, 0, 0, 0, true, 64);
switchInputs();
curStep = ghost->Step;
}
else if ( mistType == 2 ){ //Red: Slow down
Screen->Rectangle(6, 0, 0, screenWidth, screenHeight, COLOR_MIST_RED, 1, 0, 0, 0, true, 64);
curStep = ghost->Step/2;
if ( Rand(2) )
NoMovement();
}
else if ( mistType == 3 ){ //Green: Speed up
Screen->Rectangle(6, 0, 0, screenWidth, screenHeight, COLOR_MIST_GREEN, 1, 0, 0, 0, true, 64);
curStep = ghost->Step*2;
fastWalk(1);
}
else if ( mistType == 4 ){ //Blue: Nothing; head was regenerated at beginning
Screen->Rectangle(6, 0, 0, screenWidth, screenHeight, COLOR_MIST_BLUE, 1, 0, 0, 0, true, 64);
curStep = ghost->Step;
}
//Finally, move the 'Handla
moveCountdown--;
if ( moveCountdown <= 0 ){
moveCountdown = ghost->Rate*10;
ghost->Dir = Rand(4);
}
headlessStep = (4 - headCount) * 15;
Ghost_Move(ghost->Dir, (curStep+headlessStep)/100, 0);
Ghost_Waitframe(this, ghost, false, false);
}
for ( int i = 0; i < 4; i++ ){ //Kill off heads
if ( heads[i]->isValid() )
heads[i]->HP = 0;
}
Ghost_Explode(this, ghost); //Play asploding animation
}
}
void switchInputs(){
bool temp = Link->InputDown;
Link->InputDown = Link->InputUp;
Link->InputUp = temp;
temp = Link->InputRight;
Link->InputRight = Link->InputLeft;
Link->InputLeft = temp;
temp = Link->InputB;
Link->InputB = Link->InputA;
Link->InputA = temp;
temp = Link->InputR;
Link->InputR = Link->InputL;
Link->InputL = temp;
}
//Don't set speed too high!
void fastWalk ( int speed ){
//Up
if( Link->InputUp
&& !Screen->isSolid(Link->X,Link->Y+6) //NW
&& !Screen->isSolid(Link->X+7,Link->Y+6) //N
&& !Screen->isSolid(Link->X+15,Link->Y+6) //NE
)
Link->Y -= speed;
//Down
else if( Link->InputDown
&& !Screen->isSolid(Link->X,Link->Y+17) //SW
&& !Screen->isSolid(Link->X+7,Link->Y+17) //S
&& !Screen->isSolid(Link->X+15,Link->Y+17) //SE
)
Link->Y += speed;
//Left
else if( Link->InputLeft
&& !Screen->isSolid(Link->X-2,Link->Y+8) //NW
&& !Screen->isSolid(Link->X-2,Link->Y+15) //SW
)
Link->X -= speed;
//Right
else if( Link->InputRight
&& !Screen->isSolid(Link->X+17,Link->Y+8) //NE
&& !Screen->isSolid(Link->X+17,Link->Y+15) //SE
)
Link->X += speed;
}
//Hurts Link as long as he touches it - no knockback, no invuln
//D0: X position
//D1: Y position
//D2: Combo to use
//D3: Frames between hurting Link
//D4: Frames before this dissappears
ffc script acidPool{
void run ( int x, int y, int combo, int freq, int duration ){
int hurtTimer;
this->Data = combo;
this->CSet = 0;
this->X = x;
this->Y = y;
while ( duration > 0 ){
if ( LinkCollision(this) && Link->Z <= 0 ){
hurtTimer = (hurtTimer + 1) % freq;
if ( hurtTimer == 0 ){
Link->HP--;
}
}
else
hurtTimer = 0;
duration--;
Waitframe();
}
this->Data = 0;
}
}
//Prevents moving in any direction
void NoMovement(){
Link->InputUp = false; Link->PressUp = false;
Link->InputDown = false; Link->PressDown = false;
Link->InputLeft = false; Link->PressLeft = false;
Link->InputRight = false; Link->PressRight = false;
}