Copy to Clipboard Test

Freeform LttP Doors and Shutters Code

const int SHUTTER_INVISIBLE_COMBO = 1; //Set this to a combo that when placed on a 4x4 FFC will be invisible.

const int CMB_BOMBWALL_MARKER = 0; //Combo used for the hint marker when using the lens on a bomb wall
const int CS_BOMBWALL_MARKER = 7; //CSet of the hint marker

const int D_LTTPDOORS = 0; //Screen->D[] (0-7) index used for directional doors
const int D_LTTPDOORID = 1; //Screen->D[] (0-7) index used for ID'd doors
const int BIT_PERMENEMYSHUTTER = 15; //Bit in D_LTTPDOORID used for permanent enemy shutters

const int SHUTTER_USE_FFC_CSET = 0; //If 1, will set shutters to the FFC's CSet. Else keep the CSets below the FFC.

const int SHUTTER_OPEN_FRAMES = 6; //How long in frames (60ths of a second) an animated shutter takes to open
const int SHUTTER_LOCK_OPEN_FRAMES = 12; //How long in frames (60ths of a second) an animated locked door takes to open
const int SHUTTER_BOSS_LOCK_OPEN_FRAMES = 12; //How long in frames (60ths of a second) an animated boss lock door takes to open

const int SHUTTER_REPEAT_DELAY_FRAMES = 12; //Delay between a shutter closing and opening
const int SHUTTER_LOCK_ACTIVATION_FRAMES = 12; //Delay when pushing against a locked door before it opens

const int SFX_SHUTTER_OPEN = 9; //SFX of a shutter opening
const int SFX_SHUTTER_CLOSE = 9; //SFX of a shutter closing
const int SFX_LOCK_OPEN = 9; //SFX of a lock opening
const int SFX_BOSS_LOCK_OPEN = 9; //SFX of a boss lock opening
const int SFX_BOMB_WALL_OPEN = 9; //SFX of a bomb wall being blasted open

const int LENS_MP_COST = 1; //The MP cost of the lens item

//If you have different/more lens items in your quest, this function will need to be changed
bool UsingLens(){
	if(Link->MP<LENS_MP_COST)
		return false;
	if(GetEquipmentA()==I_LENS&&Link->InputA)
		return true;
	if(GetEquipmentB()==I_LENS&&Link->InputB)
		return true;
	return false;
}

bool BombWall_HoldingItemButton(int id){
		if(GetEquipmentA()==id&&Link->InputA)
			return true;
		if(GetEquipmentB()==id&&Link->InputB)
			return true;
		return false;
	}

//LttP Shutter
//D0: Direction to the wall the shutter is on - 0: Up, 1: Down, 2: Left, 3: Right
//D1: Trigger type for the shutter
//		0: One way
//		1: Kill enemies
//		2: Push block
//		3: Combo change
//		4: Perm secret
//D2: Special argument based on the value of D1:
//		-For enemy shutters, the shutter stays open and uses door ID 16 for the state if this is set to 1
//		-For combo change shutters, this is the combo position of the trigger combo
//D6: If using an opening animation, this is the first of the combo groups, starting with a closed door.
//	  Opening combo groups should be arranged vertically and be made up of doors the same size as the FFC.
//D7: If using an opening animation, this is the number of frames in the animation.

ffc script LttP_Shutter{
	void run(int dir, int openTrigger, int triggerMisc, int d3, int d4, int layer, int openingCmb, int openingFrames){
		int comboPos = ComboAt(this->X+8, this->Y+8);
		
		int closedCmb = this->Data;
		int openCmb = this->Data+4*this->TileHeight;
		this->Data = SHUTTER_INVISIBLE_COMBO;
		
		//Uncheck flags that make the FFC invisible during scrolling
		this->Flags[FFCF_LENSVIS] = false;
		this->Flags[FFCF_CHANGER] = false;
		
		int triggerCD = Screen->ComboD[triggerMisc];
		int triggerCC = Screen->ComboC[triggerMisc];
		int triggerCF = Screen->ComboF[triggerMisc];
		
		//Set opening combo relative to the open and closed states if not set
		if(openingCmb==0&&openingFrames>0){
			openingCmb = closedCmb+2*(4*this->TileHeight);
		}
		
		int linkX = Link->X;
		int linkY = Link->Y;
		//If the script is running while the screen is scrolling, Link's position will be flipped.
		if(this->Flags[FFCF_PRELOAD]){
			if(dir==DIR_UP||dir==DIR_DOWN){
				if(linkY<=0)
					linkY = Region->Height-16;
				else if(linkY>=Region->Height-16)
					linkY = 0;
			}
			else{
				if(linkX<=0)
					linkX = Region->Width-16;
				else if(linkX>=Region->Width-16)
					linkX = 0;
			}
		}
		
		// >using variables as labels
		// shiggydiggy
		int SHUTTER_STATE = 0; //0 = Closed, 1 = Opening, 2 = Open, 3 = Closing
		int SHUTTER_FRAMES = 1;
		int SHUTTER_OPENCMB = 2;
		int SHUTTER_CLOSEDCMB = 3;
		int SHUTTER_COMBOPOS = 4;
		int shutter[16] = {0, 0, openCmb, closedCmb, comboPos};
		
		if(Shutter_StaysOpen(openTrigger, triggerMisc)){ //Doors triggered by secret state don't need to run if it's already set
			Shutter_SetCombos(this, layer, comboPos, openCmb);
			Quit();
		}
			
		int shutterCooldown;
		
		//If Link entered from this shutter, push him out and close it
		if(Shutter_LinkCollide(this, dir, linkX, linkY, false)){
			shutterCooldown = SHUTTER_REPEAT_DELAY_FRAMES;
			shutter[SHUTTER_STATE] = 2; //Open
			Shutter_SetCombos(this, layer, comboPos, openCmb);
			Waitframe();
			while(Link->Action==LA_SCROLLING||Shutter_LinkCollide(this, dir, Link->X, Link->Y, true)){
				if(Link->Action!=LA_SCROLLING)
					Shutter_MoveLink(dir);
				Waitframe();
			}
			Shutter_EjectLink(this, dir);
			Shutter_Close(this, layer, shutter, openingFrames);
		}
		else{
			if(this->Flags[FFCF_PRELOAD]){
				Shutter_SetCombos(this, layer, comboPos, closedCmb);
				//Wait for screen to finish scrolling so Link doesn't get teleported during the animation
				Waitframe();
				while(Link->Action==LA_SCROLLING){
					Waitframe();
				}
			}
			else
				Shutter_Close(this, layer, shutter, openingFrames);
		}
		
		bool open = false;
		bool wasBlock = false;
		bool blockPuzzle = false;
		if(Shutter_CountBlockTriggers()>0)
			blockPuzzle = true;
			
		int enemyCooldown = 12;
		
		while(true){
			if(openTrigger==1){ //Enemies
				if(enemyCooldown>0)
					enemyCooldown--;
				else{
					if(Shutter_CountNPCs()==0){
						if(triggerMisc) //If the shutter is permanent
							Screen->D[D_LTTPDOORID] |= (1<<BIT_PERMENEMYSHUTTER);
						open = true;
					}
				}
			}
			else if(openTrigger==2){ //Block
				if(blockPuzzle){ //Block puzzle secrets should override screens that just have blocks
					if(Shutter_CountBlockTriggers()==0)
						open = true;
				}
				else{
					if(Screen->MovingBlockX>-1||Screen->MovingBlockY>-1)
						wasBlock = true;
					else if(wasBlock)
						open = true;
				}
			}
			else if(openTrigger==3){ //Combo change
				if(triggerCD!=Screen->ComboD[triggerMisc]||triggerCC!=Screen->ComboC[triggerMisc]||triggerCF!=Screen->ComboF[triggerMisc]){
					open = true;
				}
				else{
					if(!Shutter_LinkCollide(this, dir, Link->X, Link->Y, true))
						open = false;
				}
			}
			else if(openTrigger==4){ //Perm secret
				if(Screen->State[ST_SECRET])
					open = true;
			}
			
			if(shutter[SHUTTER_STATE]==0&&Shutter_LinkCollide(this, dir, Link->X, Link->Y, false))
				Shutter_EjectLink(this, dir);
			
			//Open and close the shutter if it should be closed/open but isn't
			if(open&&shutter[SHUTTER_STATE]==0){
				if(shutterCooldown>0)
					shutterCooldown--;
				else
					Shutter_Open(this, layer, shutter, openingFrames);
			}
			else if(!open&&shutter[SHUTTER_STATE]==2){
				Shutter_Close(this, layer, shutter, openingFrames);
				shutterCooldown = SHUTTER_REPEAT_DELAY_FRAMES;
			}
				
			Shutter_Update(this, layer, shutter, openingCmb, openingFrames);
			Waitframe();
		}
	}
	bool Shutter_StaysOpen(int openTrigger, int triggerMisc){
		if(openTrigger==1&&triggerMisc&&Screen->D[D_LTTPDOORID]&(1<<BIT_PERMENEMYSHUTTER))
			return true;
		if(openTrigger==4&&Screen->State[ST_SECRET])
			return true;
			
		return false;
	}
	void Shutter_Update(ffc this, int layer, int[] shutter, int openingCmb, int openingFrames){
		int SHUTTER_STATE = 0;
		int SHUTTER_FRAMES = 1;
		int SHUTTER_OPENCMB = 2;
		int SHUTTER_CLOSEDCMB = 3;
		int SHUTTER_COMBOPOS = 4;
		int curFrame;
		if(shutter[SHUTTER_STATE]==1){ //Opening
			if(shutter[SHUTTER_FRAMES]>0){
				shutter[SHUTTER_FRAMES]--;
				curFrame = Clamp(Floor((SHUTTER_OPEN_FRAMES-shutter[SHUTTER_FRAMES])/(SHUTTER_OPEN_FRAMES/openingFrames)), 0, openingFrames-1);
				Shutter_SetCombos(this, layer, shutter[SHUTTER_COMBOPOS], openingCmb+curFrame*(4*this->TileHeight));
			}
			else{
				Shutter_SetCombos(this, layer, shutter[SHUTTER_COMBOPOS], shutter[SHUTTER_OPENCMB]);
				shutter[SHUTTER_STATE] = 2; //Open
			}
		}
		else if(shutter[SHUTTER_STATE]==3){ //Closing
			if(shutter[SHUTTER_FRAMES]>0){
				shutter[SHUTTER_FRAMES]--;
				curFrame = Clamp(Floor(shutter[SHUTTER_FRAMES]/(SHUTTER_OPEN_FRAMES/openingFrames)), 0, openingFrames-1);
				Shutter_SetCombos(this, layer, shutter[SHUTTER_COMBOPOS], openingCmb+curFrame*(4*this->TileHeight));
			}
			else{
				Shutter_SetCombos(this, layer, shutter[SHUTTER_COMBOPOS], shutter[SHUTTER_CLOSEDCMB]);
				shutter[SHUTTER_STATE] = 0; //Closed
			}
		}
	}
	void Shutter_Open(ffc this, int layer, int[] shutter, int openingFrames){
		int SHUTTER_STATE = 0;
		int SHUTTER_FRAMES = 1;
		int SHUTTER_OPENCMB = 2;
		int SHUTTER_CLOSEDCMB = 3;
		int SHUTTER_COMBOPOS = 4;
		Game->PlaySound(SFX_SHUTTER_OPEN);
		if(openingFrames==0){
			Shutter_SetCombos(this, layer, shutter[SHUTTER_COMBOPOS], shutter[SHUTTER_OPENCMB]);
			shutter[SHUTTER_STATE] = 2; //Open
		}
		else{
			shutter[SHUTTER_STATE] = 1; //Opening
			shutter[SHUTTER_FRAMES] = SHUTTER_OPEN_FRAMES;
		}
	}
	void Shutter_Close(ffc this, int layer, int[] shutter, int openingFrames){
		int SHUTTER_STATE = 0;
		int SHUTTER_FRAMES = 1;
		int SHUTTER_OPENCMB = 2;
		int SHUTTER_CLOSEDCMB = 3;
		int SHUTTER_COMBOPOS = 4;
		Game->PlaySound(SFX_SHUTTER_CLOSE);
		if(openingFrames==0){
			Shutter_SetCombos(this, layer, shutter[SHUTTER_COMBOPOS], shutter[SHUTTER_CLOSEDCMB]);
			shutter[SHUTTER_STATE] = 0; //Closed
		}
		else{
			shutter[SHUTTER_STATE] = 3; //Closing
			shutter[SHUTTER_FRAMES] = SHUTTER_OPEN_FRAMES;
		}
	}
	void Shutter_SetCombos(ffc this, int layer, int comboPos, int cmb){
		int w = this->TileWidth;
		int h = this->TileHeight;
		for(int x=0; x<w; x++){
			for(int y=0; y<h; y++){
				SetLayerComboD(layer, comboPos+x+y*16, cmb+x+y*4);
				if(SHUTTER_USE_FFC_CSET)
					__SetLayerComboC(layer, comboPos+x+y*16, this->CSet);
				//Screen->ComboD[comboPos+x+y*16] = cmb+x+y*4;
			}
		}
	}
	bool Shutter_LinkCollide(ffc this, int dir, int linkX, int linkY, bool fullTile){
		if(Link->Action==LA_SCROLLING||Link->X<0||Link->X>Region->Width-16||Link->Y<0||Link->Y>Region->Height-16)
			return false;
		int w = this->TileWidth;
		int h = this->TileHeight;
		int offset = 0;
		if(fullTile) 
			offset = 16;
		if(dir==DIR_UP){
			return (linkX>=this->X-16 && linkX<=this->X+w*16 && linkY <= Max(this->Y+h*16-16+offset-8, 0));
		}
		else if(dir==DIR_DOWN){
			return (linkX>=this->X-16 && linkX<=this->X+w*16 && linkY >= this->Y-offset);
		}
		else if(dir==DIR_LEFT){
			return (linkY>=this->Y-16 && linkY<=this->Y+h*16 && linkX <= this->X+w*16-16+offset);
		}
		else if(dir==DIR_RIGHT){
			return (linkY>=this->Y-16 && linkY<=this->Y+h*16 && linkX >= this->X-offset);
		}
		return false;
	}
	void Shutter_MoveLink(int dir){
		NoAction();
		if(dir==DIR_UP){
			Link->InputDown = true;
			if(!CanWalk(Link->X, Link->Y, DIR_DOWN, 1, false))
				Link->Y++;
		}
		else if(dir==DIR_DOWN){
			Link->InputUp = true;
			if(!CanWalk(Link->X, Link->Y, DIR_UP, 1, false))
				Link->Y--;
		}
		else if(dir==DIR_LEFT){
			Link->InputRight = true;
			if(!CanWalk(Link->X, Link->Y, DIR_RIGHT, 1, false))
				Link->X++;
		}
		else if(dir==DIR_RIGHT){
			Link->InputLeft = true;
			if(!CanWalk(Link->X, Link->Y, DIR_LEFT, 1, false))
				Link->X--;
		}
	}
	void Shutter_EjectLink(ffc this, int dir){
		if(dir==DIR_UP)
			Link->Y = this->Y+this->TileHeight*16-8;
		else if(dir==DIR_DOWN)
			Link->Y = this->Y-16;
		else if(dir==DIR_LEFT)
			Link->X = this->X+this->TileWidth*16;
		else if(dir==DIR_RIGHT)
			Link->X = this->X-16;
	}
	int Shutter_CountNPCs(){
		int count;
		for(int i=Screen->NumNPCs(); i>=1; i--){
			npc n = Screen->LoadNPC(i);
			if(n->MiscFlags&(1<<3)) //Doesn't count as a beatable enemy flag
				continue;
			if(n->Type==NPCT_GUY||n->Type==NPCT_TRAP||n->Type==NPCT_PROJECTILE||n->Type==NPCT_NONE||n->Type==NPCT_FAIRY)
				continue;
			if(n->Type==NPCT_ZORA) //Borderline if this should be a skippable enemy. I believe most ZC behaviors skip it, so I've put it here
				continue;
			count++;
		}
		return count;
	}
	int Shutter_CountBlockTriggers(){
		int count;
		for(int i=0; i<NUM_COMBO_POS; i++){
			if(ComboFI(i, CF_BLOCKTRIGGER))
				count++;
		}
		return count;
	}
	//A shorthand way to set a combo on the current layer.
	//Layer 0 is the screen itself.
	void __SetLayerComboC(int layer, int pos, int cset) {
	  if (layer == 0)
		Screen->ComboC[pos] = cset;
	  else
		Game->SetComboCSet(Screen->LayerMap(layer), Screen->LayerScreen(layer), pos, cset);
	}
}

//LttP Locked Door
//D0: Direction to the wall the door is on - 0: Up, 1: Down, 2: Left, 3: Right
//D1: Special ID of the door (0 for none, 1-16). Be sure to match it with the door on the other side so both open properly.
//D6: If using an opening animation, this is the first of the combo groups, starting with a closed door.
//	  Opening combo groups should be arranged vertically and be made up of doors the same size as the FFC.
//D7: If using an opening animation, this is the number of frames in the animation.
ffc script LttP_LockedDoor{
	void run(int dir, int id, int d2, int d3, int d4, int layer, int openingCmb, int openingFrames){
		int comboPos = ComboAt(this->X+8, this->Y+8);
		
		int closedCmb = this->Data;
		int openCmb = this->Data+4*this->TileHeight;
		this->Data = SHUTTER_INVISIBLE_COMBO;
		
		//Uncheck flags that make the FFC invisible during scrolling
		this->Flags[FFCF_LENSVIS] = false;
		this->Flags[FFCF_CHANGER] = false;
		
		//Set opening combo relative to the open and closed states if not set
		if(openingCmb==0&&openingFrames>0){
			openingCmb = closedCmb+2*(4*this->TileHeight);
		}
		
		// >using variables as labels
		// shiggydiggy
		int SHUTTER_STATE = 0; //0 = Closed, 1 = Opening, 2 = Open, 3 = Closing
		int SHUTTER_FRAMES = 1;
		int SHUTTER_OPENCMB = 2;
		int SHUTTER_CLOSEDCMB = 3;
		int SHUTTER_COMBOPOS = 4;
		int shutter[16] = {0, 0, openCmb, closedCmb, comboPos};
		
		//If screen D bit is set, open the door
		if(LockedDoor_CheckD(dir, id)){
			if(this->Flags[FFCF_PRELOAD]){
				LockedDoor_SetCombos(this, layer, comboPos, openCmb);
				Quit();
			}
			else{
				LockedDoor_Open(this, layer, shutter, openingFrames);
			}
		}
		else{
			LockedDoor_SetCombos(this, layer, comboPos, closedCmb);
		}
		
		int lockCount = SHUTTER_LOCK_ACTIVATION_FRAMES;
		while(true){
			if(shutter[SHUTTER_STATE]==0){ //Closed
				if(LockedDoor_DetectOpen(this, dir)){
					if(lockCount>0)
						lockCount--;
					else if(LockedDoor_HasKey()){
						LockedDoor_Open(this, layer, shutter, openingFrames);
						LockedDoor_SetD(dir, id);
						LockedDoor_TakeKey();
					}
				}
				else
					lockCount = SHUTTER_LOCK_ACTIVATION_FRAMES;
			}
			LockedDoor_Update(this, layer, shutter, openingCmb, openingFrames);
			Waitframe();
		}
	}
	void LockedDoor_Update(ffc this, int layer, int[] shutter, int openingCmb, int openingFrames){
		int SHUTTER_STATE = 0;
		int SHUTTER_FRAMES = 1;
		int SHUTTER_OPENCMB = 2;
		int SHUTTER_CLOSEDCMB = 3;
		int SHUTTER_COMBOPOS = 4;
		int curFrame;
		if(shutter[SHUTTER_STATE]==1){ //Opening
			if(shutter[SHUTTER_FRAMES]>0){
				shutter[SHUTTER_FRAMES]--;
				curFrame = Clamp(Floor((SHUTTER_LOCK_OPEN_FRAMES-shutter[SHUTTER_FRAMES])/(SHUTTER_LOCK_OPEN_FRAMES/openingFrames)), 0, openingFrames-1);
				LockedDoor_SetCombos(this, layer, shutter[SHUTTER_COMBOPOS], openingCmb+curFrame*(4*this->TileHeight));
			}
			else{
				LockedDoor_SetCombos(this, layer, shutter[SHUTTER_COMBOPOS], shutter[SHUTTER_OPENCMB]);
				shutter[SHUTTER_STATE] = 2; //Open
			}
		}
	}
	void LockedDoor_Open(ffc this, int layer, int[] shutter, int openingFrames){
		int SHUTTER_STATE = 0;
		int SHUTTER_FRAMES = 1;
		int SHUTTER_OPENCMB = 2;
		int SHUTTER_CLOSEDCMB = 3;
		int SHUTTER_COMBOPOS = 4;
		Game->PlaySound(SFX_LOCK_OPEN);
		if(openingFrames==0){
			LockedDoor_SetCombos(this, layer, shutter[SHUTTER_COMBOPOS], shutter[SHUTTER_OPENCMB]);
			shutter[SHUTTER_STATE] = 2; //Open
		}
		else{
			shutter[SHUTTER_STATE] = 1; //Opening
			shutter[SHUTTER_FRAMES] = SHUTTER_OPEN_FRAMES;
		}
	}
	void LockedDoor_SetCombos(ffc this, int layer, int comboPos, int cmb){
		int w = this->TileWidth;
		int h = this->TileHeight;
		for(int x=0; x<w; x++){
			for(int y=0; y<h; y++){
				SetLayerComboD(layer, comboPos+x+y*16, cmb+x+y*4);
				if(SHUTTER_USE_FFC_CSET)
					__SetLayerComboC(layer, comboPos+x+y*16, this->CSet);
				//Screen->ComboD[comboPos+x+y*16] = cmb+x+y*4;
			}
		}
	}
	bool LockedDoor_DetectOpen(ffc this, int dir){
		int hitboxX;
		int hitboxY;
		if(dir==DIR_UP){
			hitboxX = this->X+this->TileWidth*8-8;
			hitboxY = this->Y+this->TileHeight*16-16;
		}
		else if(dir==DIR_DOWN){
			hitboxX = this->X+this->TileWidth*8-8;
			hitboxY = this->Y;
		}
		else if(dir==DIR_LEFT){
			hitboxX = this->X+this->TileWidth*16-16;
			hitboxY = this->Y+this->TileHeight*8-8;
		}
		else if(dir==DIR_RIGHT){
			hitboxX = this->X;
			hitboxY = this->Y+this->TileHeight*8-8;
		}
		
		if(Link->X>=hitboxX-8&&Link->X<=hitboxX+8&&Link->Y>=hitboxY-16&&Link->Y<=hitboxY&&Link->InputDown&&Link->Dir==DIR_DOWN&&dir<2)
			return true;
		if(Link->X>=hitboxX-8&&Link->X<=hitboxX+8&&Link->Y>=hitboxY&&Link->Y<=hitboxY+8&&Link->InputUp&&Link->Dir==DIR_UP&&dir<2)
			return true;
		if(Link->X>=hitboxX-16&&Link->X<=hitboxX&&Link->Y>=hitboxY-8&&Link->Y<=hitboxY+8&&Link->InputRight&&Link->Dir==DIR_RIGHT&&dir>=2)
			return true;
		if(Link->X>=hitboxX&&Link->X<=hitboxX+16&&Link->Y>=hitboxY-8&&Link->Y<=hitboxY+8&&Link->InputLeft&&Link->Dir==DIR_LEFT&&dir>=2)
			return true;
		return false;
	}
	bool LockedDoor_CheckD(int dir, int id){
		if(id>0)
			return Screen->D[D_LTTPDOORID]&(1<<(Clamp(id-1, 0, 15)));
		else
			return Screen->D[D_LTTPDOORS]&(1<<Clamp(dir, 0, 3));
	}
	void LockedDoor_SetD(int dir, int id){
		int offset;
		if(dir==DIR_UP)
			offset = -16;
		else if(dir==DIR_DOWN)
			offset = 16;
		else if(dir==DIR_LEFT)
			offset = -1;
		else
			offset = 1;
		int nextD;
		if(id>0){
			Screen->D[D_LTTPDOORID] |= (1<<(Clamp(id-1, 0, 15)));
			nextD = Game->GetDMapScreenD(Game->GetCurDMap(), Game->GetCurDMapScreen()+offset, D_LTTPDOORID);
			Game->SetDMapScreenD(Game->GetCurDMap(), Game->GetCurDMapScreen()+offset, D_LTTPDOORID, nextD|(1<<(Clamp(id-1, 0, 15))));
		}
		else{
			Screen->D[D_LTTPDOORS] |= (1<<Clamp(dir, 0, 3));
			nextD = Game->GetDMapScreenD(Game->GetCurDMap(), Game->GetCurDMapScreen()+offset, D_LTTPDOORS);
			Game->SetDMapScreenD(Game->GetCurDMap(), Game->GetCurDMapScreen()+offset, D_LTTPDOORS, nextD|(1<<Clamp(OppositeDir(dir), 0, 3)));
		}
	}
	bool LockedDoor_HasKey(){
		if(Link->Item[I_MAGICKEY])
			return true;
		if(Game->Counter[CR_KEYS]>0)
			return true;
		if(Game->LKeys[Game->GetCurLevel()]>0)
			return true;
		return false;
	}
	void LockedDoor_TakeKey(){
		if(Link->Item[I_MAGICKEY])
			return;
		if(Game->LKeys[Game->GetCurLevel()]>0)
			Game->LKeys[Game->GetCurLevel()]--;
		else if(Game->Counter[CR_KEYS]>0)
			Game->Counter[CR_KEYS]--;
	}
	//A shorthand way to set a combo on the current layer.
	//Layer 0 is the screen itself.
	void __SetLayerComboC(int layer, int pos, int cset) {
	  if (layer == 0)
		Screen->ComboC[pos] = cset;
	  else
		Game->SetComboCSet(Screen->LayerMap(layer), Screen->LayerScreen(layer), pos, cset);
	}
}

//LttP Boss Locked Door
//D0: Direction to the wall the door is on - 0: Up, 1: Down, 2: Left, 3: Right
//D1: Special ID of the door (0 for none, 1-16). Be sure to match it with the door on the other side so both open properly.
//D6: If using an opening animation, this is the first of the combo groups, starting with a closed door.
//	  Opening combo groups should be arranged vertically and be made up of doors the same size as the FFC.
//D7: If using an opening animation, this is the number of frames in the animation.
ffc script LttP_BossLockedDoor{
	void run(int dir, int id, int d2, int d3, int d4, int layer, int openingCmb, int openingFrames){
		int comboPos = ComboAt(this->X+8, this->Y+8);
		
		int closedCmb = this->Data;
		int openCmb = this->Data+4*this->TileHeight;
		this->Data = SHUTTER_INVISIBLE_COMBO;
		
		//Uncheck flags that make the FFC invisible during scrolling
		this->Flags[FFCF_LENSVIS] = false;
		this->Flags[FFCF_CHANGER] = false;
		
		//Set opening combo relative to the open and closed states if not set
		if(openingCmb==0&&openingFrames>0){
			openingCmb = closedCmb+2*(4*this->TileHeight);
		}
		
		// >using variables as labels
		// shiggydiggy
		int SHUTTER_STATE = 0; //0 = Closed, 1 = Opening, 2 = Open, 3 = Closing
		int SHUTTER_FRAMES = 1;
		int SHUTTER_OPENCMB = 2;
		int SHUTTER_CLOSEDCMB = 3;
		int SHUTTER_COMBOPOS = 4;
		int shutter[16] = {0, 0, openCmb, closedCmb, comboPos};
		
		//If screen D bit is set, open the door
		if(BossLockedDoor_CheckD(dir, id)){
			if(this->Flags[FFCF_PRELOAD]){
				BossLockedDoor_SetCombos(this, layer, comboPos, openCmb);
				Quit();
			}
			else{
				BossLockedDoor_Open(this, layer, shutter, openingFrames);
			}
		}
		else{
			BossLockedDoor_SetCombos(this, layer, comboPos, closedCmb);
		}
		
		int lockCount = SHUTTER_LOCK_ACTIVATION_FRAMES;
		while(true){
			if(shutter[SHUTTER_STATE]==0){ //Closed
				if(BossLockedDoor_DetectOpen(this, dir)){
					if(lockCount>0)
						lockCount--;
					else if(BossLockedDoor_HasKey()){
						BossLockedDoor_Open(this, layer, shutter, openingFrames);
						BossLockedDoor_SetD(dir, id);
					}
				}
				else
					lockCount = SHUTTER_LOCK_ACTIVATION_FRAMES;
			}
			BossLockedDoor_Update(this, layer, shutter, openingCmb, openingFrames);
			Waitframe();
		}
	}
	void BossLockedDoor_Update(ffc this, int layer, int[] shutter, int openingCmb, int openingFrames){
		int SHUTTER_STATE = 0;
		int SHUTTER_FRAMES = 1;
		int SHUTTER_OPENCMB = 2;
		int SHUTTER_CLOSEDCMB = 3;
		int SHUTTER_COMBOPOS = 4;
		int curFrame;
		if(shutter[SHUTTER_STATE]==1){ //Opening
			if(shutter[SHUTTER_FRAMES]>0){
				shutter[SHUTTER_FRAMES]--;
				curFrame = Clamp(Floor((SHUTTER_LOCK_OPEN_FRAMES-shutter[SHUTTER_FRAMES])/(SHUTTER_LOCK_OPEN_FRAMES/openingFrames)), 0, openingFrames-1);
				BossLockedDoor_SetCombos(this, layer, shutter[SHUTTER_COMBOPOS], openingCmb+curFrame*(4*this->TileHeight));
			}
			else{
				BossLockedDoor_SetCombos(this, layer, shutter[SHUTTER_COMBOPOS], shutter[SHUTTER_OPENCMB]);
				shutter[SHUTTER_STATE] = 2; //Open
			}
		}
	}
	void BossLockedDoor_Open(ffc this, int layer, int[] shutter, int openingFrames){
		int SHUTTER_STATE = 0;
		int SHUTTER_FRAMES = 1;
		int SHUTTER_OPENCMB = 2;
		int SHUTTER_CLOSEDCMB = 3;
		int SHUTTER_COMBOPOS = 4;
		Game->PlaySound(SFX_BOSS_LOCK_OPEN);
		if(openingFrames==0){
			BossLockedDoor_SetCombos(this, layer, shutter[SHUTTER_COMBOPOS], shutter[SHUTTER_OPENCMB]);
			shutter[SHUTTER_STATE] = 2; //Open
		}
		else{
			shutter[SHUTTER_STATE] = 1; //Opening
			shutter[SHUTTER_FRAMES] = SHUTTER_OPEN_FRAMES;
		}
	}
	void BossLockedDoor_SetCombos(ffc this, int layer, int comboPos, int cmb){
		int w = this->TileWidth;
		int h = this->TileHeight;
		for(int x=0; x<w; x++){
			for(int y=0; y<h; y++){
				SetLayerComboD(layer, comboPos+x+y*16, cmb+x+y*4);
				if(SHUTTER_USE_FFC_CSET)
					__SetLayerComboC(layer, comboPos+x+y*16, this->CSet);
				//Screen->ComboD[comboPos+x+y*16] = cmb+x+y*4;
			}
		}
	}
	bool BossLockedDoor_DetectOpen(ffc this, int dir){
		int hitboxX;
		int hitboxY;
		if(dir==DIR_UP){
			hitboxX = this->X+this->TileWidth*8-8;
			hitboxY = this->Y+this->TileHeight*16-16;
		}
		else if(dir==DIR_DOWN){
			hitboxX = this->X+this->TileWidth*8-8;
			hitboxY = this->Y;
		}
		else if(dir==DIR_LEFT){
			hitboxX = this->X+this->TileWidth*16-16;
			hitboxY = this->Y+this->TileHeight*8-8;
		}
		else if(dir==DIR_RIGHT){
			hitboxX = this->X;
			hitboxY = this->Y+this->TileHeight*8-8;
		}
		
		if(Link->X>=hitboxX-8&&Link->X<=hitboxX+8&&Link->Y>=hitboxY-16&&Link->Y<=hitboxY&&Link->InputDown&&Link->Dir==DIR_DOWN&&dir<2)
			return true;
		if(Link->X>=hitboxX-8&&Link->X<=hitboxX+8&&Link->Y>=hitboxY&&Link->Y<=hitboxY+8&&Link->InputUp&&Link->Dir==DIR_UP&&dir<2)
			return true;
		if(Link->X>=hitboxX-16&&Link->X<=hitboxX&&Link->Y>=hitboxY-8&&Link->Y<=hitboxY+8&&Link->InputRight&&Link->Dir==DIR_RIGHT&&dir>=2)
			return true;
		if(Link->X>=hitboxX&&Link->X<=hitboxX+16&&Link->Y>=hitboxY-8&&Link->Y<=hitboxY+8&&Link->InputLeft&&Link->Dir==DIR_LEFT&&dir>=2)
			return true;
		return false;
	}
	bool BossLockedDoor_CheckD(int dir, int id){
		if(id>0)
			return Screen->D[D_LTTPDOORID]&(1<<(Clamp(id-1, 0, 15)));
		else
			return Screen->D[D_LTTPDOORS]&(1<<(4+Clamp(dir, 0, 3)));
	}
	void BossLockedDoor_SetD(int dir, int id){
		int offset;
		if(dir==DIR_UP)
			offset = -16;
		else if(dir==DIR_DOWN)
			offset = 16;
		else if(dir==DIR_LEFT)
			offset = -1;
		else
			offset = 1;
		int nextD;
		if(id>0){
			Screen->D[D_LTTPDOORID] |= (1<<(Clamp(id-1, 0, 15)));
			nextD = Game->GetDMapScreenD(Game->GetCurDMap(), Game->GetCurDMapScreen()+offset, D_LTTPDOORID);
			Game->SetDMapScreenD(Game->GetCurDMap(), Game->GetCurDMapScreen()+offset, D_LTTPDOORID, nextD|(1<<(Clamp(id-1, 0, 15))));
		}
		else{
			Screen->D[D_LTTPDOORS] |= (1<<(4+Clamp(dir, 0, 3)));
			nextD = Game->GetDMapScreenD(Game->GetCurDMap(), Game->GetCurDMapScreen()+offset, D_LTTPDOORS);
			Game->SetDMapScreenD(Game->GetCurDMap(), Game->GetCurDMapScreen()+offset, D_LTTPDOORS, nextD|(1<<(4+Clamp(OppositeDir(dir), 0, 3))));
		}
	}
	bool BossLockedDoor_HasKey(){
		if(Game->LItems[Game->GetCurLevel()]&LI_BOSSKEY)
			return true;
		return false;
	}
	//A shorthand way to set a combo on the current layer.
	//Layer 0 is the screen itself.
	void __SetLayerComboC(int layer, int pos, int cset) {
	  if (layer == 0)
		Screen->ComboC[pos] = cset;
	  else
		Game->SetComboCSet(Screen->LayerMap(layer), Screen->LayerScreen(layer), pos, cset);
	}
}

//LttP Bomb Wall
//D0: Direction to the wall the door is on - 0: Up, 1: Down, 2: Left, 3: Right
//D1: Special ID of the door (0 for none, 1-16). Be sure to match it with the door on the other side so both open properly.
ffc script LttP_BombWall{
	void run(int dir, int id, int d2, int d3, int d4, int layer, int d6, int d7){
		int comboPos = ComboAt(this->X+8, this->Y+8);
		
		int openCmb = this->Data;
		int rubbleCmb = this->Data+4*this->TileHeight;
		this->Data = SHUTTER_INVISIBLE_COMBO;
		
		//Uncheck flags that make the FFC invisible during scrolling
		this->Flags[FFCF_LENSVIS] = false;
		this->Flags[FFCF_CHANGER] = false;
		
		//If screen D bit is set, open the door
		if(BombWall_CheckD(dir, id)){
			BombWall_SetCombos(this, layer, comboPos, openCmb);
			BombWall_SetRubble(this, dir, rubbleCmb);
			Quit();
		}
		
		while(true){
			//Draw a hint graphic when using the lens
			if(UsingLens()){
				int x;
				int y;
				if(dir==DIR_UP){
					x = this->X+this->TileWidth*8-8;
					y = this->Y+this->TileHeight*16-16;
				}
				else if(dir==DIR_DOWN){
					x = this->X+this->TileWidth*8-8;
					y = this->Y;
				}
				else if(dir==DIR_LEFT){
					x = this->X+this->TileWidth*16-16;
					y = this->Y+this->TileHeight*8-8;
				}
				else if(dir==DIR_RIGHT){
					x = this->X;
					y = this->Y+this->TileHeight*8-8;
				}
				
				if(CMB_BOMBWALL_MARKER>0)
					Screen->FastCombo(6, x, y, CMB_BOMBWALL_MARKER, CS_BOMBWALL_MARKER, 128);
			}
			
			if(BombWall_DetectOpen(this, dir)){
				BombWall_SetCombos(this, layer, comboPos, openCmb);
				BombWall_SetRubble(this, dir, rubbleCmb);
				BombWall_SetD(dir, id);
				Game->PlaySound(SFX_BOMB_WALL_OPEN);
				Quit();
			}
			
			Waitframe();
		}
	}
	void BombWall_SetCombos(ffc this, int layer, int comboPos, int cmb){
		int w = this->TileWidth;
		int h = this->TileHeight;
		for(int x=0; x<w; x++){
			for(int y=0; y<h; y++){
				SetLayerComboD(layer, comboPos+x+y*16, cmb+x+y*4);
				if(SHUTTER_USE_FFC_CSET)
					__SetLayerComboC(layer, comboPos+x+y*16, this->CSet);
				//Screen->ComboD[comboPos+x+y*16] = cmb+x+y*4;
			}
		}
	}
	void BombWall_SetRubble(ffc this, int dir, int cmb){
		int rubbleX;
		int rubbleY;
		
		if(dir==DIR_UP){
			rubbleX = this->X+this->TileWidth*8-8;
			rubbleY = this->Y+this->TileHeight*16;
		}
		else if(dir==DIR_DOWN){
			rubbleX = this->X+this->TileWidth*8-8;
			rubbleY = this->Y-16;
		}
		else if(dir==DIR_LEFT){
			rubbleX = this->X+this->TileWidth*16;
			rubbleY = this->Y+this->TileHeight*8-8;
		}
		else if(dir==DIR_RIGHT){
			rubbleX = this->X-16;
			rubbleY = this->Y+this->TileHeight*8-8;
		}
		
		this->Data = cmb;
		this->X = rubbleX;
		this->Y = rubbleY;
		this->TileWidth = 1;
		this->TileHeight = 1;
	}
	bool BombWall_DetectOpen(ffc this, int dir){
		int hitboxX;
		int hitboxY;
		if(dir==DIR_UP){
			hitboxX = this->X+this->TileWidth*8-8;
			hitboxY = this->Y+this->TileHeight*16-16;
		}
		else if(dir==DIR_DOWN){
			hitboxX = this->X+this->TileWidth*8-8;
			hitboxY = this->Y;
		}
		else if(dir==DIR_LEFT){
			hitboxX = this->X+this->TileWidth*16-16;
			hitboxY = this->Y+this->TileHeight*8-8;
		}
		else if(dir==DIR_RIGHT){
			hitboxX = this->X;
			hitboxY = this->Y+this->TileHeight*8-8;
		}
		
		for(int i=Screen->NumLWeapons(); i>=1; i--){
			lweapon l = Screen->LoadLWeapon(i);
			if(l->CollDetection&&l->DeadState==WDS_ALIVE){
				if(l->ID==LW_BOMBBLAST||l->ID==LW_SBOMBBLAST){
					if(RectCollision(l->X+l->HitXOffset, l->Y+l->HitYOffset, l->X+l->HitXOffset+l->HitWidth-1, l->Y+l->HitYOffset+l->HitHeight-1, hitboxX, hitboxY, hitboxX+15, hitboxY+15)){
						return true;
					}
				}
			}
		}
		return false;
	}
	bool BombWall_CheckD(int dir, int id){
		if(id>0)
			return Screen->D[D_LTTPDOORID]&(1<<(Clamp(id-1, 0, 15)));
		else
			return Screen->D[D_LTTPDOORS]&(1<<(8+Clamp(dir, 0, 3)));
	}
	void BombWall_SetD(int dir, int id){
		int offset;
		if(dir==DIR_UP)
			offset = -16;
		else if(dir==DIR_DOWN)
			offset = 16;
		else if(dir==DIR_LEFT)
			offset = -1;
		else
			offset = 1;
		int nextD;
		if(id>0){
			Screen->D[D_LTTPDOORID] |= (1<<(Clamp(id-1, 0, 15)));
			nextD = Game->GetDMapScreenD(Game->GetCurDMap(), Game->GetCurDMapScreen()+offset, D_LTTPDOORID);
			Game->SetDMapScreenD(Game->GetCurDMap(), Game->GetCurDMapScreen()+offset, D_LTTPDOORID, nextD|(1<<(Clamp(id-1, 0, 15))));
		}
		else{
			Screen->D[D_LTTPDOORS] |= (1<<(8+Clamp(dir, 0, 3)));
			nextD = Game->GetDMapScreenD(Game->GetCurDMap(), Game->GetCurDMapScreen()+offset, D_LTTPDOORS);
			Game->SetDMapScreenD(Game->GetCurDMap(), Game->GetCurDMapScreen()+offset, D_LTTPDOORS, nextD|(1<<(8+Clamp(OppositeDir(dir), 0, 3))));
		}
	}
	//A shorthand way to set a combo on the current layer.
	//Layer 0 is the screen itself.
	void __SetLayerComboC(int layer, int pos, int cset) {
	  if (layer == 0)
		Screen->ComboC[pos] = cset;
	  else
		Game->SetComboCSet(Screen->LayerMap(layer), Screen->LayerScreen(layer), pos, cset);
	}
}