Copy to Clipboard Test

Remote Controlled Bulldozer Code

const int SFX_BULLDOZER_MOVE = 17;//Sound to play when bulldozer is moving. 
const int SFX_BULLDOZER_CRASH = 32;//Sound to play when bulldozer crashes.

const int SPR_BULLDOZER_CRASH = 22;//Sprite to display when bulldozer crashes.

const int CF_BULLDOZER_TRIGGER = 66;//Flag for secret trigger. Bulldoze all flagged combos to trigger secrets. 
const int CF_BULLDOZER_ALWAYS_CRASH = 67;//Flag used to crash bulldozer, when it attempts to run over it. 

const int BULLDOZER_SFX_TIMING = 10;// Counter used for timing ambient running SFX.
const int BULLDOZER_ALLOW_SOLID = 1;// If 0, bulldozer will crash on hitting any solid combo, in addition to  CF_BULLDOZER_ALWAYS_CRASH flags.
const int BULLDOZER_ALLOW_UTURN = 0;// If 0, bulldozer cannot U-turn.

const int BULLDOZER_COLLISION_MARGIN = 5;//How far Link can be off from center of bulldozer to activate it.

//Prompt GFX
const int CMB_BULLDOZER_PROMPT = 943;//Combo ID used to render prompt, when Link steps on spot.
const int CSET_BULLDOZER_PROMPT = 11;//CSet used to render prompt, when Link steps on spot.

//Magical bulldozer. Stand next to it and press Ex1 to activate it. You can control it by pressing direction buttons. 
//If it runs off course, it crashes. Bulldozed combos are replaced with screen`s undercombo. Bulldoze entire area to open secrets.

//1. Set up 4 consecutive combos for directions up->down->left->right.
//2. Import and compile the script. It requires ffcscript.zh, in addition to default libraries.
//3. Build the puzzle: place CF_BULLDOZER_TRIGGER over all combos that are needed to be bulldozed and CF_BULLDOZER_ALWAYS_CRASH for obstacles.
//   Also you can edit screen`s undercombo to have inherent CF_BULLDOZER_ALWAYS_CRASH flag for Hamiltonian style puzzles ala Link`s Awakening.
//4. Place FFC with 1st combo from step 1 and script assigned at it`s initial position. Grid snap it.
// D0 - Initial direction.
// D1 - Speed. 0.8 is default. Best ones are 0.5, 0.8, 1, 1.6, 2 and 4. 4 is VERY DIFFICULT to handle.
// D2 - 0-Prevent secret triggering, just bulldozing stuff.
// D3 - Use FFC`s CSet, instead of screen`s undercset to replace bulldozed combos
// D4 - ID of combo to use as replacement of bulldozed combo, defaults to screen`s undercombo
// D5 - If set to > 0 -> allow Link to terminate bulldozer prematurely by pressing EX1.

ffc script RemoteControlBulldozer{
	void run(int dir, int speed, int trigger, int usecset, int ucmb, int manualcrash){
		speed/=2;
		if (speed==0)speed=0.8;		
		int origdata = this->Data;
		this->Data = origdata + dir;
		int state = 0;
		bool crash = false;
		int counter = 0;
		if (ucmb==0) ucmb = Screen->UnderCombo;
		int uscet = Screen->UnderCSet;
		if (usecset>0) uscet = this->CSet;
		int cmb = ComboAt(CenterX(this), CenterY(this));
		int nextcmb =  BulldozerAdjacentComboFix(cmb, dir);
		int newdir = dir;
		while (true){
			if (state==0){
				if (BulldozerIsPushed(this, Link->Dir, BULLDOZER_COLLISION_MARGIN)){
					Screen->FastCombo(6, Link->X,Link->Y-15,CMB_BULLDOZER_PROMPT, CSET_BULLDOZER_PROMPT,OP_OPAQUE);
					if (Link->PressEx1){
						Link->PressEx1=false;
						state = 1;
						Screen->ComboD[cmb] = ucmb;
						Screen->ComboC[cmb] = uscet;					
						if (Screen->ComboF[cmb]== CF_BULLDOZER_TRIGGER ) Screen->ComboF[cmb] = 0;
						cmb=nextcmb;
					}
				}				
			}
			if (state==1){
				if ((counter%BULLDOZER_SFX_TIMING) == 0) Game->PlaySound(SFX_BULLDOZER_MOVE);
				counter++;
				if (counter>=360) counter=0;
				
				if (dir==DIR_UP){
					this->Y-=speed;
				}
				if (dir==DIR_DOWN){
					this->Y+=speed;
				}
				if (dir==DIR_LEFT){
					this->X-=speed;
				}
				if (dir==DIR_RIGHT){
					this->X+=speed;
				}
				
				if (Link->PressUp && (BULLDOZER_ALLOW_UTURN>0 || dir!= DIR_DOWN)) newdir = DIR_UP;
				if (Link->PressDown && (BULLDOZER_ALLOW_UTURN>0 || dir!= DIR_UP)) newdir = DIR_DOWN;
				if (Link->PressLeft && (BULLDOZER_ALLOW_UTURN>0 || dir!= DIR_RIGHT)) newdir = DIR_LEFT;
				if (Link->PressRight && (BULLDOZER_ALLOW_UTURN>0 || dir!= DIR_LEFT)) newdir = DIR_RIGHT;
				
				this->Data = origdata + newdir;
				
				if (Link->PressEx1 && manualcrash>0){//manual crashing via ex1
					Game->PlaySound(SFX_BULLDOZER_CRASH);
					Link->PressEx1=false;
					if (SPR_BULLDOZER_CRASH>0){
						lweapon l = CreateLWeaponAt(LW_SPARKLE, this->X, this->Y);
						l->UseSprite(SPR_BULLDOZER_CRASH);
						l->DeadState = Max(l->ASpeed*l->NumFrames-1, 1);
						l->CollDetection=false;
					}
					if (trigger>0){
						for (int i = 0; i<=176; i++){
							if (i==176){
								Game->PlaySound(SFX_SECRET);
								Screen->TriggerSecrets();
								Screen->State[ST_SECRET] = true;
							}
							else if (ComboFI(i, CF_BULLDOZER_TRIGGER))break;
						}
					}
					Link->CollDetection=true;
					this->Data = FFCS_INVISIBLE_COMBO;
					ClearFFC(FFCNum(this));
					Quit();
				}
				
				if (((this->X % 16)==0)&&(this->Y %16)==0){
					Screen->ComboD[cmb] = ucmb;
					Screen->ComboC[cmb] = uscet;
					if (Screen->ComboF[cmb]== CF_BULLDOZER_TRIGGER ) Screen->ComboF[cmb] = 0;
					nextcmb =  BulldozerAdjacentComboFix(cmb, newdir);
					if (ComboFI(nextcmb, CF_BULLDOZER_ALWAYS_CRASH)) crash = true;
					if ((Screen->ComboS[nextcmb]>0)&&(BULLDOZER_ALLOW_SOLID==0)) crash = true;
					if (crash){
						Game->PlaySound(SFX_BULLDOZER_CRASH);
						if (SPR_BULLDOZER_CRASH>0){
							lweapon l = CreateLWeaponAt(LW_SPARKLE, this->X, this->Y);
							l->UseSprite(SPR_BULLDOZER_CRASH);
							l->DeadState = Max(l->ASpeed*l->NumFrames-1, 1);
							l->CollDetection=false;
						}
						if (trigger>0){
							for (int i = 0; i<=176; i++){
								if (i==176){
									Game->PlaySound(SFX_SECRET);
									Screen->TriggerSecrets();
									Screen->State[ST_SECRET] = true;
								}
								else if (ComboFI(i, CF_BULLDOZER_TRIGGER)){
									break;
								}
							}
						}
						Link->CollDetection=true;
						this->Data = FFCS_INVISIBLE_COMBO;
						ClearFFC(FFCNum(this));
						Quit();
					}
					else{
						cmb = nextcmb;
						dir=newdir;
						//DirSpeedToVel(this, dir, speed);
					}					
				}
				
				NoAction();
				Link->CollDetection=false;
			}			
			Waitframe();
		}
	}
}

//Fixed variant of AdjacentCombo function from std_extension.zh
int BulldozerAdjacentComboFix(int cmb, int dir){
	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]==-1 && ( dir == DIR_LEFT || dir == DIR_LEFTUP || dir == DIR_LEFTDOWN ) ) return -1; //if the left columb
	if ( combooffsets[10]==-1 && ( dir == DIR_RIGHT || dir == DIR_RIGHTUP || dir == DIR_RIGHTDOWN ) ) return -1; //if the right column
	if ( combooffsets[11]==-1 && ( dir == DIR_UP || dir == DIR_RIGHTUP || dir == DIR_LEFTUP ) ) return -1; //if the top row
	if ( combooffsets[12]==-1 && ( dir == DIR_DOWN || dir == DIR_RIGHTDOWN || dir == DIR_LEFTDOWN ) ) return -1; //if the bottom row
	if ( cmb >= 0 && cmb < 176 ) return cmb + combooffsets[dir];
	else return -1;
}

bool BulldozerIsPushed(ffc this, int dir, int margin){
	if((Link->X == this->X - 16 && (Link->Y < this->Y+this->EffectHeight - 8+margin && Link->Y > this->Y - margin) && dir == DIR_RIGHT) || // Right
	(Link->X == this->X + this->EffectWidth && (Link->Y < this->Y+this->EffectHeight - 8+margin && Link->Y > this->Y - margin) && dir == DIR_LEFT) || // Left
	(Link->Y == this->Y - 16 && (Link->X < (this->X + this->EffectWidth-16+margin) && Link->X > this->X - margin) && dir == DIR_DOWN) || // Down
	(Link->Y == this->Y + this->EffectHeight-8 && (Link->X < (this->X + this->EffectWidth-16+margin) && Link->X > this->X - margin) && dir == DIR_UP)) { // Up
		return true;
	}
	return false;
}