Copy to Clipboard Test

FF Wizzrobe (autoghost) Code

const int SPR_FFWIZZROBE_ENEMY_HEAL = 96;//Ssprite to display, when Wizzrobe`s healing spell hits enemy for healing.
const int SFX_FFWIZZROBE_ENEMY_HEAL = 25;//Sound to play, when Wizzrobe`s healing spell hits enemy for healing.

const int FF_WIZZROBE_DEFAULT_SUMMON_ID = 106;//Default summoned enemy ID.

//Freeform teleporting Wizzrobe. Teleports around, fires eweapons at Link

ffc script FF_Wizzrobe{
	void run(int enemyID){
		npc ghost = Ghost_InitAutoGhost(this, enemyID);
		
		int MinLinkDistance = 128 - (ghost->Homing)/2;//Minimum distance to Link when teleporting
		int WPND = ghost->WeaponDamage;//Magic damage
		
		int Teledelay = Ghost_GetAttribute(ghost, 0, 120);//Delay between teleporting
		int MagicHealEnemies = Ghost_GetAttribute(ghost, 1, 0);//If magic hits enemy it heals that value
		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 firing speed, in 100th of pixel per frame.
		int proximitywarp = Ghost_GetAttribute(ghost, 6, 512);//Minimum Proximity distance for Wizzrobe to perform teleportation. Too close and it teleports.
		int flickertime = Ghost_GetAttribute(ghost, 7, 32);//Flickering duration, in frames.
		
		ghost->Extend=3;
		
		Ghost_SetFlag(GHF_NORMAL);
		Ghost_SetFlag(GHF_NO_FALL);
		Ghost_UnsetFlag(GHF_KNOCKBACK);
		
		int OrigTile = ghost->OriginalTile;
		int State = 0;
		int haltcounter = -1;
		int StateCounter =Teledelay;
		int dir = Ghost_Dir;
		int ewflags=0;
		int dist=0;
		if (ghost->Weapon<139 || ghost->Weapon>=143)ewflags|=EWF_ROTATE;
		
		while(true){
			if (State==0){
				ghost->DrawXOffset = 1000;
				ghost->HitXOffset = 1000;
				if (StateCounter == 0){
					ghost->DrawXOffset = 0;
					
					int combo = WizzrobeFindSuitableSpot(ghost, MinLinkDistance, true, false, false, false);
					if (combo>0)Ghost_X=ComboX(combo);
					if (combo>0)Ghost_Y=ComboY(combo);
					State = 1;
					StateCounter = flickertime;
					dir = WizzrobeFaceLink(ghost);
					Ghost_ForceDir(dir);
				}
			}
			if (State==1){
				if(IsOdd(StateCounter))  ghost->DrawXOffset=1000;
				else  ghost->DrawXOffset=0;
				if (StateCounter == 0){
					ghost->HitXOffset = 0;
					State = 2;
					StateCounter = 32;
				}
			}
			if (State==2){
				if (StateCounter == 0){
					State = 3;
					StateCounter = 32;
					eweapon e;
					if (Wpn == 0){
						e = FireNonAngularEWeapon(ghost->Weapon, Ghost_X, Ghost_Y, ghost->Dir, WPNSPD, WPND, EWType,ewSound, ewflags);
					}
					if (MagicHealEnemies>0){
						while(e->isValid()){
							for (int i=1; i<=Screen->NumNPCs(); i++){
								npc h = Screen->LoadNPC(i);
								if (h==ghost) continue;
								if (Collision(h,e)){
									h->HP+=MagicHealEnemies;
									Remove(e);
									Game->PlaySound(SFX_FFWIZZROBE_ENEMY_HEAL);
									lweapon s = CreateLWeaponAt(LW_SPARKLE, h->X, h->Y);
									s->UseSprite(SPR_FFWIZZROBE_ENEMY_HEAL);
									s->CollDetection=false; 
								}
							}
							Ghost_Waitframe(this, ghost);
						}
						
					}
					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_WIZZROBE_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);
					}
				}
			}
			if (State==3){
				if (StateCounter == 0){
					dist = Distance(Link->X,Link->Y,Ghost_X,Ghost_Y);
					if (dist<proximitywarp){
						State = 4;
						StateCounter = flickertime;
					}
					else StateCounter = 30;
				}
			}
			if (State==4){
				if(IsOdd(StateCounter))  ghost->DrawXOffset=1000;
				else  ghost->DrawXOffset=0;
				if (StateCounter == 0){
					ghost->DrawXOffset = 1000;
					ghost->HitXOffset = 1000;
					State = 0;
					StateCounter = Teledelay;
				}
			}
			StateCounter--;
			Ghost_ForceDir(dir);
			WizzrobeAnimation(ghost, OrigTile, State);
			Ghost_Waitframe(this, ghost);
		}
	}
	
	void WizzrobeAnimation(npc ghost, int origtile, int state){
		int offset = 0;
		if (state==2 )offset = 20*ghost->TileHeight;
		if (state==3 )offset = 40*ghost->TileHeight;
		ghost->OriginalTile = origtile + offset;
	}
	
	int WizzrobeFaceLink(npc ghost){
		int angle = Angle(Ghost_X+Ghost_TileWidth*8-8,Ghost_Y+Ghost_TileHeight*8-8,CenterLinkX(),CenterLinkY());
		return AngleDir4(angle);	
	}
	
	int  WizzrobeFindSuitableSpot(npc ghost, int MinLinkDistance, bool landOK, bool wallsOK, bool waterOK, bool pitsOK){
		int tileRatings[176];
		int checkCombo;
		int checkX;
		int checkY;
		int bestRating;
		int bestCount;
		int counter;
		int choice;
		int tries;
		npc otherNPC;    
		// First, rate each tile for suitability. Lower is better,
		// but negative means it's strictly off-limits.    
		
		for(int i=Screen->NumNPCs(); i>0; i--)    {// Tiles too close to other enemies are undesirable
			otherNPC=Screen->LoadNPC(i);
			checkCombo=ComboAt(otherNPC->X, otherNPC->Y);
			tileRatings[checkCombo]+=100;
			
			if(checkCombo>15)
			tileRatings[checkCombo-16]+=1;
			if(checkCombo<160)
			tileRatings[checkCombo+16]+=1;
			if(checkCombo%16>0)
			tileRatings[checkCombo-1]+=1;
			if(checkCombo%16<15)
			tileRatings[checkCombo+1]+=1;
		}    
		// Mark prohibited tiles
		for(int i=0; i<176; i++) {
			// Screen edges in NES dungeon
			if((Screen->Flags[SF_ROOMTYPE]&010b)!=0 && (i<32 || i>143 || i%16<2 || i%16>13))	tileRatings[i]=-1;        
			else if(IsWater(i)){// Water
				if(!waterOK)tileRatings[i]=-1;			
			}        
			else if(__IsPit(i)) {// Pits
				if(!pitsOK)tileRatings[i]=-1;			
			}
			// "No enemy" flag and combos
			else if(Screen->ComboF[i]==CF_NOENEMY || Screen->ComboI[i]==CF_NOENEMY ||
			Screen->ComboT[i]==CT_NOENEMY || Screen->ComboT[i]==CT_NOFLYZONE ||
			Screen->ComboT[i]==CT_NOJUMPZONE)
			tileRatings[i]=-1;
			// Too close to Link
			else if(Abs(ComboX(i)-Link->X)<32 && Abs(ComboY(i)-Link->Y)<32)
			tileRatings[i]+=150;
			// All other combos
			else        {// If land is okay, but not walls (i.e. walkable only)
				
				if(landOK && !wallsOK){
					checkX=ComboX(i);
					checkY=ComboY(i);
					
					if(Screen->isSolid(checkX, checkY) ||
					Screen->isSolid(checkX+8, checkY) ||
					Screen->isSolid(checkX, checkY+8) ||
					Screen->isSolid(checkX+8, checkY+8))
					tileRatings[i]=-1;
				}
				// If walls are okay, but not land (i.e. unwalkable only)
				else if(!landOK && wallsOK) {
					checkX=ComboX(i);
					checkY=ComboY(i);                
					if(!Screen->isSolid(checkX, checkY) ||
					!Screen->isSolid(checkX+8, checkY) ||
					!Screen->isSolid(checkX, checkY+8) ||
					!Screen->isSolid(checkX+8, checkY+8))
					tileRatings[i]=-1;
				}
				// Neither land nor walls are okay
				else if(!landOK && !wallsOK)tileRatings[i]=-1;			
			}
			if (tileRatings[i]>=0 && ghost->Homing>0){
				tileRatings[i]+=20;
				int cmb = ComboAt (CenterLinkX(), CenterLinkY());//Wizzrobes prefer aligning cardinally with Link
				if (ComboX(i)==ComboX(cmb))tileRatings[i]-=8;
				if (ComboY(i)==ComboY(cmb))tileRatings[i]-=8;
				if (Distance(ComboX(i), ComboY(i), ComboX(cmb), ComboY(cmb))<MinLinkDistance)tileRatings[i]+=12;
			}
		}	
		// Find the best rating and count the number of tiles with that rating
		bestRating=10000;
		bestCount=0;
		for(int i=0; i<176; i++){
			if(tileRatings[i]<0)	continue;		
			if(tileRatings[i]==bestRating)	bestCount++;
			else if(tileRatings[i]<bestRating)	{
				bestRating=tileRatings[i];
				bestCount=1;
			}
		}		
		// The loop below might hang if every tile is unusable
		if(bestCount==0)	return 0;	
		// Pick at random from the best rated tiles
		counter=Rand(bestCount)+1;
		for(choice=0; counter>0; choice++)   {
			if(tileRatings[choice]==bestRating)counter--;	
		}	
		return choice-1;// Subtract 1 because the for loop overshot
	}	
}