const int FF_WIZZROBE2_DEFAULT_SUMMON_ID = 106;//Default summoned enemy ID.
//FF Phasing Wizzrobe.
//Walks around, ocassionally phasing through solid combos. Shoots eweapons, when facing Link.
//Step speed, Homiong factor, Random Rate, Hunger, Halt Rate are used.
ffc script FF_PhaseWizzrobe{
void run(int enemyID){
npc ghost = Ghost_InitAutoGhost(this, enemyID);
int HF = ghost->Homing;
int HR = ghost->Haltrate;
int RR = ghost->Rate;
int HNG = ghost->Hunger;
int SPD = ghost->Step;
int WPND = ghost->WeaponDamage;
int Shotspeed = Ghost_GetAttribute(ghost, 0, 30);//Delay between shots, in frames.
int Teledelay = Ghost_GetAttribute(ghost, 1, 8);//Delay between halting and phassing.
int Wpn = Ghost_GetAttribute(ghost, 2, 0);//Attack type: 0 - 1 shot, 1- 4 shots orthogonally, 2-8 shots in orthogonal and diagonal directions, 3 - summon enemies, 4 - nukes the whole screen for damage anywhere!, 5 - Aimed eweapon at Link, 6 - tripled aimed eweapon.
int EWType = Ghost_GetAttribute(ghost, 3, -1);//Weapon sprite / Enemy ID / nuke flash color
int ewSound = Ghost_GetAttribute(ghost, 4, -1);// Weapon fire sound / enemy count
int WPNSPD = Ghost_GetAttribute(ghost, 5, 300);//Eweapon speed.
ghost->Extend=3;
Ghost_SetFlag(GHF_NORMAL);
Ghost_SetFlag(GHF_NO_FALL);
Ghost_UnsetFlag(GHF_KNOCKBACK);
int OrigTile = ghost->OriginalTile;
int State = 0;
int statecounter = 0;
int haltcounter = -1;
int shotcounter = 0;
int phasedir=-1;
int posarray[8]={0,0,0,0,0,0,0,0};
int cmb=-1;
eweapon e;
int ewflags=0;
if (ghost->Weapon<139 || ghost->Weapon>=143)ewflags|=EWF_ROTATE;
int defs[18];
Ghost_StoreDefenses(ghost,defs);
while(true){
if (State==0){
haltcounter=Ghost_HaltingWalk4(haltcounter, SPD, RR, HF, HNG, HR, Teledelay);
if (WPND>0 && shotcounter==0 && LinkAlign(ghost)){
if (Wpn == 0){
e = FireNonAngularEWeapon(ghost->Weapon, Ghost_X, Ghost_Y, ghost->Dir, WPNSPD, WPND, EWType,ewSound, ewflags);
}
else if (Wpn == 1){
int dirs[4]= {DIR_UP, DIR_DOWN, DIR_LEFT, DIR_RIGHT};
Game->PlaySound(ewSound);
for (int i=0;i<SizeOfArray(dirs);i++){
e = FireNonAngularEWeapon(ghost->Weapon, Ghost_X, Ghost_Y, dirs[i], WPNSPD, WPND, EWType,0, ewflags);
}
}
else if (Wpn == 2){
int dirs[8] = {DIR_UP, DIR_RIGHTUP, DIR_RIGHT, DIR_RIGHTDOWN, DIR_DOWN, DIR_LEFTDOWN, DIR_LEFT, DIR_LEFTUP};
Game->PlaySound(ewSound);
for (int i=0;i<SizeOfArray(dirs);i++){
e = FireNonAngularEWeapon(ghost->Weapon, Ghost_X, Ghost_Y, dirs[i], WPNSPD, WPND, EWType,0, ewflags);
}
}
else if (Wpn == 3){
Game->PlaySound(SFX_SUMMON);
for (int i=1; i<=ewSound;i++){
npc en = CreateNPCAt(Cond(EWType>0,EWType,FF_WIZZROBE2_DEFAULT_SUMMON_ID), Ghost_X,Ghost_Y);
}
}
else if (Wpn == 4){
e = FireEWeapon(EW_SCRIPT10, Link->X+InFrontX(Link->Dir, 12), Link->Y+InFrontY(Link->Dir, 12), 0, 0, WPND, 22, ewSound, EWF_UNBLOCKABLE);
e->Dir = Link->Dir;
e->DrawYOffset = -1000;
SetEWeaponLifespan(e, EWL_TIMER, 1);
SetEWeaponDeathEffect(e, EWD_VANISH, 0);
for (int i=1; i<=60;i++){
if(i % 2 == 0) Screen->Rectangle(6, 0, 0, 256, 172, Cond(EWType>0,EWType,22), 1, 0, 0, 0, true, 64);
Ghost_Waitframe(this, ghost);
}
}
else if (Wpn == 5){
e = FireAimedEWeapon(ghost->Weapon, CenterX(ghost)-8, CenterY(ghost)-8, 0, WPNSPD, WPND, EWType, -1, ewflags);
}
else if (Wpn == 6){
e = FireAimedEWeapon(ghost->Weapon, CenterX(ghost)-8, CenterY(ghost)-8, 0, WPNSPD, WPND, EWType, -1, ewflags);
e = FireAimedEWeapon(ghost->Weapon, CenterX(ghost)-8, CenterY(ghost)-8, 0.2, WPNSPD, WPND, EWType, -1, ewflags);
e = FireAimedEWeapon(ghost->Weapon, CenterX(ghost)-8, CenterY(ghost)-8, -0.2, WPNSPD, WPND, EWType, -1, ewflags);
}
shotcounter=Shotspeed;
}
cmb = ComboAt(CenterX(ghost), CenterY(ghost));
if (haltcounter==1){
for (int i=0;i<8;i++){
posarray[i]=AdjacentComboFix(cmb, i, 2);
int curcmb= posarray[i];
if (curcmb<0)continue;
if (Screen->ComboS[curcmb]>0) posarray[i]=-1;
if (ComboFI(curcmb, CF_NOENEMY)) posarray[i]=-1;
if (IsWater(curcmb))posarray[i]=-1;
if (IsPit(curcmb))posarray[i]=-1;
}
while(phasedir<0){
bool nospaces=false;
for (int i=0;i<=8;i++){
if (i==8){
nospaces=true;
break;
}
if (posarray[i]<0)continue;
else break;
}
if (nospaces){
phasedir=-1;
break;
}
if (Rand(255)<HF){
phasedir = Cursor_ClosestToPos(posarray, ComboAt(CenterLinkX(),CenterLinkY()));
}
else{
phasedir = Rand(7);
while(posarray[phasedir]<0)phasedir = Rand(7);
}
if (posarray[phasedir]>=0)break;
}
if (phasedir>=0){
statecounter=32;
State=1;
}
}
}
else if (State==1){
if (phasedir==DIR_UP){
Ghost_Y--;
}
if (phasedir==DIR_DOWN){
Ghost_Y++;
}
if (phasedir==DIR_LEFT){
Ghost_X--;
}
if (phasedir==DIR_RIGHT){
Ghost_X++;
}
if (phasedir==DIR_LEFTUP){
Ghost_Y--;
Ghost_X--;
}
if (phasedir==DIR_RIGHTUP){
Ghost_Y--;
Ghost_X++;
}
if (phasedir==DIR_LEFTDOWN){
Ghost_X--;
Ghost_Y++;
}
if (phasedir==DIR_RIGHTDOWN){
Ghost_X++;
Ghost_Y++;
}
statecounter--;
if (IsOdd(statecounter))ghost->DrawXOffset=1000;
else ghost->DrawXOffset=0;
if (statecounter==0){
ghost->DrawXOffset=0;
phasedir=-1;
State=0;
}
}
if (shotcounter>0)shotcounter--;
Ghost_Waitframe(this, ghost);
}
}
int Cursor_ClosestToPos(int list, int pos){
int dist = 9999;
int ret = 0;
int pos2 = 0;
int dist2 = 0;
for (int i = 0; i<SizeOfArray(list);i++){
if (list[i]==-1)continue;
pos2 = list[i];
dist2 = Distance(ComboX(pos2), ComboY(pos2), ComboX(pos), ComboY(pos));
if (dist2<dist)dist=dist2;
else continue;
ret=i;
}
return ret;
}
//! Returns the combo ID of a combo based on a location, in a givem direction, N combos away.
int AdjacentComboFix(int cmb, int dir, int dist){
int combooffsets[13]={-0x10, 0x10, -1, 1, -0x11, -0x0F, 0x0F, 0x11};
if ( cmb % 16 == 0 ) combooffsets[9] = -1;//if it's the left edge
if ( (cmb % 16) == 15 ) combooffsets[10] = -1; //if it's the right edge
if ( cmb < 0x10 ) combooffsets[11] = -1; //if it's the top row
if ( cmb > 0x9F ) combooffsets[12] = -1; //if it's on the bottom row
if ( combooffsets[9] && ( dir == DIR_LEFT || dir == DIR_LEFTUP || dir == DIR_LEFTDOWN || dir == DIR_LEFTUP ) ) return -1; //if the left columb
if ( combooffsets[10] && ( dir == DIR_RIGHT || dir == DIR_RIGHTUP || dir == DIR_RIGHTDOWN ) ) return -1; //if the right column
if ( combooffsets[11] && ( dir == DIR_UP || dir == DIR_RIGHTUP || dir == DIR_LEFTUP || dir == DIR_LEFTUP ) ) return -1; //if the top row
if ( combooffsets[12] && ( dir == DIR_DOWN || dir == DIR_RIGHTDOWN || dir == DIR_LEFTDOWN ) ) return -1; //if the bottom row
else if ( cmb >= 0 && cmb < 176 )
{
int cmbs[2];//needs a for loop to ensure that t returns the most valid combo
for ( cmbs[1] = 0; cmbs[1] < dist; cmbs[1]++ )
{
cmbs[0] = cmb;
cmb += (combooffsets[dir]);
if ( cmb < 0 || cmb > 175 ) return -1;
if ( cmb % 16 == 0 ) combooffsets[9] = -1;//if it's the left edge
if ( (cmb % 16) == 15 ) combooffsets[10] = -1; //if it's the right edge
if ( cmb < 0x10 ) combooffsets[11] = -1; //if it's the top row
if ( cmb > 0x9F ) combooffsets[12] = -1; //if it's on the bottom row
if ( combooffsets[9] && ( dir == DIR_LEFT || dir == DIR_LEFTUP || dir == DIR_LEFTDOWN || dir == DIR_LEFTUP ) ) return -1; //if the left columb
if ( combooffsets[10] && ( dir == DIR_RIGHT || dir == DIR_RIGHTUP || dir == DIR_RIGHTDOWN ) ) return -1; //if the right column
if ( combooffsets[11] && ( dir == DIR_UP || dir == DIR_RIGHTUP || dir == DIR_LEFTUP || dir == DIR_LEFTUP ) ) return -1; //if the top row
if ( combooffsets[12] && ( dir == DIR_DOWN || dir == DIR_RIGHTDOWN || dir == DIR_LEFTDOWN ) ) return -1; //if the bottom row
}
return cmb;
}
else return -1;
}
bool LinkAlign(npc ghost){
int cmb = ComboAt(CenterX(ghost), CenterY(ghost));
int linkcmb = ComboAt(CenterLinkX(),CenterLinkY());
if (ComboX(cmb)!=ComboX(linkcmb) && ComboY(cmb)!=ComboY(linkcmb)) return false;
int angle = Angle(Ghost_X+Ghost_TileWidth*8-8,Ghost_Y+Ghost_TileHeight*8-8,CenterLinkX(),CenterLinkY());
angle=AngleDir4(angle);
return ghost->Dir == angle;
}
}