Copy to Clipboard Test

Light Path Puzzle Code

const int SFX_LIGHTPANEL_TURN = 6; //Sound when a light panel turns

ffc script LightPath{
	void run(int cmbNode, int cmbRotatepath, int cmbFloorPath, int sfx){
		bool alreadyTriggered;
		if(Screen->State[ST_SECRET])
			alreadyTriggered = true;
		if(sfx==0)
			sfx = 27;
		int mainState[176]; //The type of each combo
		int dirState[176]; //Binary flag for the four directions of the combo
		int active[176]; //Whether or not the combo is activated
		int rotTime[176]; //Timer for rotating panels
		//First we cycle through every combo on the screen and
		//assign it a type based on ComboD.
		for(int i=0; i<176; i++){
			int cd = Screen->ComboD[i];
			if(cd>=cmbFloorPath&&cd<=cmbFloorPath+31){
				mainState[i] = 1; //Fixed Path
				dirState[i] = (cd-cmbFloorPath)%16;
			}
			else if(cd>=cmbRotatepath&&cd<=cmbRotatepath+31){
				mainState[i] = 2; //Rotating Path
				dirState[i] = (cd-cmbRotatepath)%16;
			}
			else if(cd>=cmbNode&&cd<=cmbNode+15){
				mainState[i] = 3; //Trigger Node
				dirState[i] = (cd-cmbNode)%16;
			}
			else if(cd>=cmbNode+16&&cd<=cmbNode+31){
				mainState[i] = 4; //Source Node
				dirState[i] = (cd-cmbNode+16)%16;
			}
		}
		int vars[16] = {1, cmbNode, cmbRotatepath, cmbFloorPath};
		
		//Update the path for the start of the puzzle
		LightPath_UpdatePath(mainState, dirState, active);
		while(true){
			//If there's no active nodes left and the puzzle hasn't already been
			//solved, play the chime and solve it.
			if(vars[0]<=0&&!alreadyTriggered){
				alreadyTriggered = true;
				Game->PlaySound(sfx);
				Screen->TriggerSecrets();
				Screen->State[ST_SECRET] = true;
			}
			//Keep the combos up to date and keep track of active nodes
			LightPath_UpdateCombos(vars, mainState, dirState, active, rotTime);
			Waitframe();
		}
	}
	//Finds the combo next to another combo
	int LightPath_AdjacentPos(int pos, int dir){
		if(dir==DIR_UP){
			if(pos<16)
				return -1;
			return pos-16;
		}
		if(dir==DIR_DOWN){
			if(pos>159)
				return -1;
			return pos+16;
		}
		if(dir==DIR_LEFT){
			if(pos%16==0)
				return -1;
			return pos-1;
		}
		if(dir==DIR_RIGHT){
			if(pos%16==15)
				return -1;
			return pos+1;
		}
		return 0;
	}
	//Finds the next direction in a node with two or less directions
	int LightPath_FindNextDir(int node, int dirState, int lastDir){
		for(int i=0; i<4; i++){
			if(i!=OppositeDir(lastDir)){
				if(dirState[node]&(1<<i)){
					return i;
				}
			}
		}
		return lastDir;
	}
	//Rotate a panel's directions clockwise
	void LightPath_Rotate(int node, int dirState){
		int newState;
		if(dirState[node]&(1<<DIR_UP))
			newState |= (1<<DIR_RIGHT);
		if(dirState[node]&(1<<DIR_RIGHT))
			newState |= (1<<DIR_DOWN);
		if(dirState[node]&(1<<DIR_DOWN))
			newState |= (1<<DIR_LEFT);
		if(dirState[node]&(1<<DIR_LEFT))
			newState |= (1<<DIR_UP);
		dirState[node] = newState;
	}
	//Find a panel combo under a sword weapon
	int LightPath_SwordCombo(lweapon sword, int mainState){
		//A charged sword doesn't flip panels
		if(Link->Action==LA_CHARGING)
			return -1;
		//Only flip panels when the sword is held in front of Link
		if((Link->Dir==DIR_UP||Link->Dir==DIR_DOWN)&&Abs(sword->X-Link->X)>4)
			return -1;
		if((Link->Dir==DIR_LEFT||Link->Dir==DIR_RIGHT)&&Abs(sword->Y-Link->Y)>4)
			return -1;
		int uc = ComboAt(sword->X+8, sword->Y+8);
		if(mainState[uc]==2)
			return uc;
		for(int x=0; x<2; x++){
			for(int y=0; y<2; y++){
				uc = ComboAt(sword->X+6+x*3, sword->Y+6+y*3);
				if(mainState[uc]==2)
					return uc;
			}
		}
		return -1;
	}
	//Run the light path from a branching node until it hits a dead end
	//or runs through all directions.
	void LightPath_RunFromNode(int node, int mainState, int dirState, int active){
		int nextPos;
		for(int i=0; i<4; i++){ //Run through all directions
			if(dirState[node]&(1<<i)){ //If that direction is valid
				nextPos = LightPath_AdjacentPos(node, i);
				int dir = i;
				while(!active[nextPos]&&dirState[nextPos]&(1<<OppositeDir(dir))){
					active[nextPos] = 1;
					if(dirState[nextPos]==1111b||dirState[nextPos]==0111b||dirState[nextPos]==1011b||dirState[nextPos]==1101b||dirState[nextPos]==1110b){
						//You play a dangerous game there, Moosh...
						//Working with powers beyond your understanding...
						//Will ZScript break under the strain of the recursive functions?
						//Only time will tell...
						LightPath_RunFromNode(nextPos, mainState, dirState, active);
						break;
					}
					dir = LightPath_FindNextDir(nextPos, dirState, dir);
					nextPos = LightPath_AdjacentPos(nextPos, dir);
					if(nextPos==-1) //If it goes off the screen, break out
						break;
				}
			}
		}
	}
	//Update the entire path
	void LightPath_UpdatePath(int mainState, int dirState, int active){
		//Cycle through an deactivate all nodes but the start
		for(int i=0; i<176; i++){
			if(mainState[i]!=4) //Don't deactivate source nodes
				active[i] = 0;
		}
		//Cycle to any start nodes and run from there
		for(int i=0; i<176; i++){
			if(mainState[i]==4){ //Source Node
				LightPath_RunFromNode(i, mainState, dirState, active);
			}
		}
	}
	//Set combo GFX and keep track of if the puzzle has been solved
	void LightPath_UpdateCombos(int vars, int mainState, int dirState, int active, int rotTime){
		vars[0] = 0;
		//Detect the sword collision
		lweapon sword = LoadLWeaponOf(LW_SWORD);
		if(sword->isValid()){
			int pos = LightPath_SwordCombo(sword, mainState);
			if(pos>-1){ //Rotating path
				if(Distance(Link->X, Link->Y, sword->X, sword->Y)>10){
					if(rotTime[pos]==0){
						Game->PlaySound(SFX_LIGHTPANEL_TURN);
						rotTime[pos] = 1;
					}
				}
			}
		}
		for(int i=0; i<176; i++){
			if(mainState[i]==1){ //Fixed Path
				Screen->ComboD[i] = vars[3]+dirState[i]+16*active[i];
			}
			if(mainState[i]==2){ //Rotating Path
				Screen->ComboD[i] = vars[2]+dirState[i]+16*active[i];
				if(rotTime[i]>0){
					Screen->FastCombo(2, ComboX(i), ComboY(i), Screen->UnderCombo, Screen->UnderCSet, 128);
					Screen->DrawCombo(2, ComboX(i), ComboY(i), Screen->ComboD[i], 1, 1, Screen->ComboC[i], -1, -1, ComboX(i), ComboY(i), rotTime[i]*6, -1, 0, true, 128);
					rotTime[i]++;
					if(rotTime[i]>=15){
						rotTime[i] = 0;
						LightPath_Rotate(i, dirState);
						LightPath_UpdatePath(mainState, dirState, active);
						//A weird bug happened here, so I added this hack fix
						//to ensure the puzzle can't be solved on the same frame
						//as a panel was flipped.
						vars[0] = 1;
					}
				}
			}
			else if(mainState[i]==3){ //Trigger Node
				if(!active[i])
					vars[0]++;
				Screen->ComboD[i] = vars[1]+dirState[i]+16*active[i];
			}
		}
	}
}