Copy to Clipboard Test

Ceiling Master (autoghost) Code

const int CF_CEILING_MASTER_TRIGGER_NEXT = 98;//If Ceiling Master lands onto combo flagged with this flag, that combo will change to next in list.
const int CF_CEILING_MASTER_TRIGGER_SECRET = 99;//If Ceiling Master lands onto combo flagged with this flag, secrets will trigger.

const int CEILING_MASTER_QUAKE_THRESHOLD = 6.4;//If Ceiling Master falls too fast, it can cause screen quake.
const int CEILING_MASTER_QUAKE_POWER = 60;//Power of quake caused by falling Ceiling Master.

const int SFX_CEILING_MASTER_EAT_BAIT = 0;//Sound that plays when monster eats bait.

//Ceiling Master.

//A hand-shaped enemy that lurks on the ceiling. Follows Link, then tries to drop on him. If he grabs hero, it will either hold in place, draining HP and a counter, or tossing him at the dungeon entrance. Use Overhead combos for protection. Also certain flagged combos can change to next in list or trigger secrets, if Ceiling Master lands onto them.

//Animation: 3 frames. 1 empty frame, then 1 for open hand and 1 for closed hand.
//Step Speed and other constant movement params are used for moving on ceiling, including hunger factor.

ffc script CeilingMaster{
	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 Warntime = Ghost_GetAttribute(ghost, 0, 300);//How long Ceiling Master walks on ceiling before being ready to drop, in frames.
		int DropSFX = Ghost_GetAttribute(ghost, 1, 0);//Sound played on drop off ceiling.
		int LandSFX = Ghost_GetAttribute(ghost, 2, 0);//Sound played on landing.
		int Fallspeed = Ghost_GetAttribute(ghost, 3, 0);//Drop speed. 0 - default gravity and acceleration. >CEILING_MASTER_QUAKE_THRESHOLD - causes quake on landing.
		int chargetime = Ghost_GetAttribute(ghost, 4, 0);//Time (in frames) between stopping moving on ceiling and falling.
		int counterdrain = Ghost_GetAttribute(ghost, 5, 0);//Counter to drain on grabbing hero. <0 - send him to dungeon entrance.
		int countertime = Ghost_GetAttribute(ghost, 6, 30);//Interval (in frames) between counter drains.
		int countercost = Ghost_GetAttribute(ghost, 7, 0);//Counter amount to drain on each timer cycle.
		int InitZ = Ghost_GetAttribute(ghost, 8, 128);//Z position of enemy when it moves on ceiling.
		int RiseSpeed = Ghost_GetAttribute(ghost,9, SPD);//Rising speed after fall.
		
		ghost->Extend=3;
		
		Ghost_SetFlag(GHF_NORMAL);
		Ghost_SetFlag(GHF_NO_FALL);
		Ghost_SetFlag(GHF_IGNORE_ALL_TERRAIN);
		Ghost_SetFlag(GHF_FLYING_ENEMY);
		Ghost_UnsetFlag(GHF_KNOCKBACK);
		
		int OrigTile = ghost->OriginalTile;
		int State = 0;
		Ghost_Z = InitZ;
		int timer = Warntime;
		int cmb = -1;
		int haltcounter = -1;
		
		int defs[18];
		Ghost_StoreDefenses(ghost,defs);
		ghost->CollDetection = false;
		
		while(true){
			if (State==0){
				if (HF>=255) Ghost_MoveTowardLink(SPD/100, 2);
				else haltcounter = Ghost_ConstantWalk4(haltcounter, SPD, RR, HF, HNG);
				timer--;
				if (timer<=0){
					State=1;
					timer=chargetime;
				}
			}
			else if (State==1){
				timer--;
				if (timer<=0){
					Game->PlaySound(DropSFX);
					State=2;
					if (Fallspeed==0) Ghost_UnsetFlag(GHF_NO_FALL);
				}
			}
			else if (State==2){
				Ghost_Z-=Fallspeed/100;
				if (Ghost_Z<=0){
					Game->PlaySound(LandSFX);
					if (Fallspeed>CEILING_MASTER_QUAKE_THRESHOLD)Screen->Quake+=CEILING_MASTER_QUAKE_POWER;
					cmb = ComboAt(CenterX(ghost), CenterY(ghost));
					if (ComboFI(cmb,CF_CEILING_MASTER_TRIGGER_NEXT)){
						Screen->ComboF[cmb]=0;
						Screen->ComboD[cmb]++;
					}
					if (ComboFI(cmb,CF_CEILING_MASTER_TRIGGER_SECRET)&&!Screen->State[ST_SECRET]){
						Screen->ComboD[cmb]++;
						Game->PlaySound(SFX_SECRET);
						Screen->TriggerSecrets();
						Screen->State[ST_SECRET]=true;
					}
					if (HNG>=3){
						for (int i=1; i<=Screen->NumLWeapons(); i++){
							lweapon b = Screen->LoadLWeapon(i);
							if (b->ID!=LW_BAIT) continue;
							if (!Collision(b, ghost)) continue;
							Game->PlaySound(SFX_CEILING_MASTER_EAT_BAIT);
							Remove(b);
							break;
						}
					}
					if (LinkCollision(ghost) && Screen->ComboT[cmb]!=CT_OVERHEAD){
						Game->PlaySound(SFX_OUCH);
						Link->HP-=ghost->Damage;
						Ghost_X=Link->X;
						Ghost_Y=Link->Y;
						if (counterdrain<0){
							timer=60;
							State=4;
						}
						else{
							ghost->CollDetection = true;
							timer = countertime;
							State=5;
						}
					}
					else{
						if (Screen->ComboT[cmb]!=CT_OVERHEAD) ghost->CollDetection = true;
						timer=120;
						State=3;
					}					
				}
			}
			else if (State==3){
				if (timer>0)timer--;
				else{
					ghost->CollDetection = false;
					Ghost_SetFlag(GHF_NO_FALL);
					Ghost_Z+=RiseSpeed/100;
					if (Ghost_Z>=InitZ){
						Ghost_Z=InitZ;
						timer = Warntime;
						State=0;
					}
				}
			}
			else if (State==4){
				NoAction();
				Link->Action=LA_WALKING;
				Link->Jump=0;
				if (timer>0)timer--;
				else{
					ghost->CollDetection = false;
					Ghost_SetFlag(GHF_NO_FALL);
					Ghost_Z+=RiseSpeed/100;
					Link->Z=Ghost_Z;
					if (Ghost_Z>=InitZ){
						Link->Warp(Game->LastEntranceDMap, Game->LastEntranceScreen);
					}
				}
			}
			else if (State==5){
				Link->X=ghost->X;
				Link->Y=ghost->Y;
				timer--;
				if (timer<=0){
					if (Game->Counter[counterdrain]>=countercost)Game->Counter[counterdrain]-=countercost;
					else Game->Counter[counterdrain]=0;
					timer = countertime;
				}
			}
			Animation(ghost, OrigTile, State, 2);
			Ghost_Waitframe(this, ghost);
		}
	}
}

void Animation(npc ghost, int origtile, int state, int numframes){
	int offset = 1;
	if (state==4||state==5)offset=2;
	ghost->OriginalTile = origtile + offset;
}