Copy to Clipboard Test

Color Cubes fixed Code

const int PUSH_BLOCK_SENSIVITY	= 8;//How long to pause when pushing a block



//Set of 6 states of color cube.

const int COLOR_CUBE_STATE_RT_BF			= 0;//Red top, blue front

const int COLOR_CUBE_STATE_RT_YF			= 1;//Red top, yellow front

const int COLOR_CUBE_STATE_BT_RF			= 2;//Blue top, red front

const int COLOR_CUBE_STATE_BT_YF			= 3;//Blue top, yellow front

const int COLOR_CUBE_STATE_YT_RF			= 4;//Yellow top, red front

const int COLOR_CUBE_STATE_YT_BF			= 5;//Yellow top, blue front



const int CMB_COLOR_CUBE_ORIGCMB = 2652; //First of 6 combos representing the states of the cube

// The order of the combos matters

// Place the combos in the order of the states



//Combo flags for target landing spots and requested colors at the top

const int CF_COLOR_CUBE_TRIGGER_RED = 98;// Red

const int CF_COLOR_CUBE_TRIGGER_BLUE = 99;//Blue

const int CF_COLOR_CUBE_TRIGGER_YELLOW = 100;//Yellow



const int TILE_COLOR_CUBE_ANIMATION = 25220;//Place rotating combos in the order: up, down, left, right



const int SFX_COLOR_CUBE_PUSH = 50; //Sound to play when color cube is pushed.



//Color Cube. A cube colored red, blue and yellow on opposite sides. Your goal is to roll it so it lands 

//on trigger space same colored side up.  



//1.Set up tiles and animations as shown in screenshot. You should end up with 6 rows of tiles. 

//2.Set TILE_COLOR_CUBE_ANIMATION to 1st tile from previous step.

//3.Setup 6 solid combos using first tile from each row, in exact the same order up->down.

//4. Import and compile the script. It requires stdExtra.zh (packaged with ZC 2.53.1) or Classic.zh.

//5. Puzzle building: place floor on background layer, as layer 0 combo will be replaced with undercombo. Keep latter at 0.

//6. Place CF_COLOR_CUBE_TRIGGER_* combo flags at trigger spots. Color cubes must land on those positions correct side up to trigger secrets.

//7. If you want ColorCube to leave color-specific undercombos (color depends on cube`s top face), set up 6 consecutive combos: Red, Blue, Yellow, Red(solid), Blue(solid), Yellow(solid). 

//8. Place snd Grid-snap FFC at cube`s initial position. Assign Color Cube  script.

// D0 - Initial state of the color cube. See state list for accepted values

// D1 - Bracelet power needed to push color cubes.

// D2 - Add together: 1 - Cube will get stuck on correct landing on trigger 2 - Use colored undercombos to check triggers (D3>0), 4- Ice Color cube - continue rolling, until hitting obstacle.

// D3 - ID of 1st combo from step 7, 0 for default undercombo behavior.

// D4 - allowed push directions. Add together - 1-up, 2-down, 4-left, 8-right. 0 for all directions.

ffc script ColorCube{

	void run(int state, int weight, int flags, int colundercombo, int dirs){

		if (dirs==0) dirs=15;

		int combo= CMB_COLOR_CUBE_ORIGCMB+state; //Determine which combo to display		

		int restate= state; //Store initial state		

		int loc = ComboAt(this->X+8,this->Y+8);; //Used to determine position on screen		

		this->InitD[7] = -1;//Tells script what direction block is being pushed If not being pushed, set to -1		

		int Push_Counter=0;//Keeps track of how long you've pushed the block		

		int OldX;//Stores previous X and Y position of block

		int OldY;

		bool iceblock = (flags&4)>0;

		bool perm = (flags&1)>0;

		bool flagundercombos = (flags&2)>0;

		bool Movable= true;//Determines if the block can still be moved		

		int UnderCombo = Screen->UnderCombo;//Saves combo under the block

		int UnderCSet = Screen->UnderCSet; //And CSet also

		int flag;//Checks flag at block position		

		this->Data= combo;//Set the ffc to the proper appearance		

		int i;//Iterative variable. Used to render cube rotation animation

		if (colundercombo>0){

			if (restate==COLOR_CUBE_STATE_RT_YF || restate==COLOR_CUBE_STATE_RT_BF)Screen->ComboD[loc] = colundercombo+3;

			else if (restate==COLOR_CUBE_STATE_BT_RF || restate==COLOR_CUBE_STATE_BT_YF)Screen->ComboD[loc] = colundercombo+4;

			else if (restate==COLOR_CUBE_STATE_YT_RF || restate==COLOR_CUBE_STATE_YT_BF)Screen->ComboD[loc] = colundercombo+5;

			Screen->ComboC[loc]=this->CSet;

		}

		while(true){			

			while(Movable){		//The block can be moved		

				while(Link->X+16< this->X//Link is too far away from the block

				|| Link->X> this->X+16

				|| Link->Y> this->Y+16

				|| Link->Y+16< this->Y){					

					loc = ComboAt(this->X+8,this->Y+8);//Determine where the block is

					//Set combo at block position to ffc's

					//Used to fake solidity

					if (colundercombo==0)Screen->ComboD[loc]= this->Data;

					Screen->ComboC[loc]=this->CSet;

					Waitframe();

				}				

				while(this->InitD[7]==-1){//You're close enough to be pushing but not long enough to move the block					

					OldX= this->X;//Save the previous location of the block

					OldY= this->Y;

					// Check if Link is pushing against the block

					if((Link->X == this->X - 16 && (Link->Y < this->Y + 1 && Link->Y > this->Y - 12) && Link->InputRight && Link->Dir == DIR_RIGHT) || // Right

					(Link->X == this->X + 16 && (Link->Y < this->Y + 1 && Link->Y > this->Y - 12) && Link->InputLeft && Link->Dir == DIR_LEFT) || // Left

					(Link->Y == this->Y - 16 && (Link->X < this->X + 4 && Link->X > this->X - 4) && Link->InputDown && Link->Dir == DIR_DOWN) || // Down

					(Link->Y == this->Y + 8 && (Link->X < this->X + 4 && Link->X > this->X - 4) && Link->InputUp && Link->Dir == DIR_UP)) { // Up

						Push_Counter++;

					}

					else {					

						Push_Counter = 0;// Reset the frame counter

					}

					if (Push_Counter>=PUSH_BLOCK_SENSIVITY){

						if (ColorCubeCanBePushed(this, Link->Dir, weight, dirs)){

							Game->PlaySound(SFX_COLOR_CUBE_PUSH);

							this->InitD[7] = Link->Dir;

							this->Data = 1;

							this->InitD[6]=0;

							if (colundercombo>0){

								if (restate==COLOR_CUBE_STATE_RT_YF || restate==COLOR_CUBE_STATE_RT_BF)Screen->ComboD[loc] = colundercombo;

								else if (restate==COLOR_CUBE_STATE_BT_RF || restate==COLOR_CUBE_STATE_BT_YF)Screen->ComboD[loc] = colundercombo+1;

								else if (restate==COLOR_CUBE_STATE_YT_RF || restate==COLOR_CUBE_STATE_YT_BF)Screen->ComboD[loc] = colundercombo+2;

								Screen->ComboC[loc]=this->CSet;

							}

							else{

								Screen->ComboD[loc]= UnderCombo;//Revert combo at ffc location to saved value	

								Screen->ComboC[loc]= UnderCSet;//And CSet;

							}

							UnderCombo=0;//Clear variable for later use

						}

					}					

					//loc = ComboAt(this->X+8,this->Y+8);//Remember location of the block					

					//if (colundercombo==0)Screen->ComboD[loc]= this->Data;//Set combo at this location to ffc data.Used to fake solidity

					Waitframe();

				}				

				while(this->InitD[7]>=0){//The block is being pushed							

					if(this->InitD[7]==DIR_LEFT){//pushing left						

						while(this->X>=OldX-15){//Not done moving yet

							i++;

							if (iceblock)i++;

							RenderColorCubeRotation(this,restate, this->InitD[7], i);

							this->X--;

							if (iceblock)this->X--;

							WaitNoAction();

						}

						if(restate==COLOR_CUBE_STATE_RT_BF)//Change current block state. Depends on previous block state

						restate= COLOR_CUBE_STATE_YT_BF;

						else if(restate==COLOR_CUBE_STATE_RT_YF)

						restate= COLOR_CUBE_STATE_BT_YF;

						else if(restate==COLOR_CUBE_STATE_BT_RF)

						restate= COLOR_CUBE_STATE_YT_RF;

						else if(restate==COLOR_CUBE_STATE_BT_YF)

						restate= COLOR_CUBE_STATE_RT_YF;

						else if(restate==COLOR_CUBE_STATE_YT_BF)

						restate= COLOR_CUBE_STATE_RT_BF;

						else if(restate==COLOR_CUBE_STATE_YT_RF)

						restate= COLOR_CUBE_STATE_BT_RF;

						this->InitD[0] = restate;//No longer pushing						

						if (iceblock && ColorCubeCanBePushed(this, this->InitD[7], 0, 15)){

							i=0;

							OldX= this->X;//Save position of ffc

							OldY= this->Y;	

							loc = ComboAt(this->X+8, this->Y+8);//Reset block location				

							if (colundercombo>0){

									if (restate==COLOR_CUBE_STATE_RT_YF || restate==COLOR_CUBE_STATE_RT_BF)Screen->ComboD[loc] = colundercombo;

									else if (restate==COLOR_CUBE_STATE_BT_RF || restate==COLOR_CUBE_STATE_BT_YF)Screen->ComboD[loc] = colundercombo+1;

									else if (restate==COLOR_CUBE_STATE_YT_RF || restate==COLOR_CUBE_STATE_YT_BF)Screen->ComboD[loc] = colundercombo+2;

									Screen->ComboC[loc]=this->CSet;

								}

						}

						else this->InitD[7] = -1;

					}					

					else if(this->InitD[7]==DIR_RIGHT){	//pushing right

						while(this->X<=OldX+15){

							i++;

							if (iceblock)i++;

							RenderColorCubeRotation(this,restate, this->InitD[7], i);

							this->X++;

							if (iceblock)this->X++;

							WaitNoAction();

						}

						if(restate==COLOR_CUBE_STATE_RT_BF)

						restate= COLOR_CUBE_STATE_YT_BF;

						else if(restate==COLOR_CUBE_STATE_RT_YF)

						restate= COLOR_CUBE_STATE_BT_YF;

						else if(restate==COLOR_CUBE_STATE_BT_RF)

						restate= COLOR_CUBE_STATE_YT_RF;

						else if(restate==COLOR_CUBE_STATE_BT_YF)

						restate= COLOR_CUBE_STATE_RT_YF;

						else if(restate==COLOR_CUBE_STATE_YT_BF)

						restate= COLOR_CUBE_STATE_RT_BF;

						else if(restate==COLOR_CUBE_STATE_YT_RF)

						restate= COLOR_CUBE_STATE_BT_RF;

						this->InitD[0] = restate;

						if (iceblock && ColorCubeCanBePushed(this, this->InitD[7], 0, 15)){

							i=0;

							OldX= this->X;//Save position of ffc

							OldY= this->Y;	

							loc = ComboAt(this->X+8, this->Y+8);//Reset block location				

							if (colundercombo>0){

									if (restate==COLOR_CUBE_STATE_RT_YF || restate==COLOR_CUBE_STATE_RT_BF)Screen->ComboD[loc] = colundercombo;

									else if (restate==COLOR_CUBE_STATE_BT_RF || restate==COLOR_CUBE_STATE_BT_YF)Screen->ComboD[loc] = colundercombo+1;

									else if (restate==COLOR_CUBE_STATE_YT_RF || restate==COLOR_CUBE_STATE_YT_BF)Screen->ComboD[loc] = colundercombo+2;

									Screen->ComboC[loc]=this->CSet;

								}

						}

						else this->InitD[7] = -1;

					}					

					else if(this->InitD[7]==DIR_UP){//pushing up

						while(this->Y>=OldY-15){

							i++;

							if (iceblock)i++;

							RenderColorCubeRotation(this,restate, this->InitD[7], i);

							this->Y--;

							if (iceblock)this->Y--;

							WaitNoAction();

						}

						if(restate==COLOR_CUBE_STATE_RT_BF)

						restate= COLOR_CUBE_STATE_BT_RF;

						else if(restate==COLOR_CUBE_STATE_RT_YF)

						restate= COLOR_CUBE_STATE_YT_RF;

						else if(restate==COLOR_CUBE_STATE_BT_RF)

						restate= COLOR_CUBE_STATE_RT_BF;

						else if(restate==COLOR_CUBE_STATE_BT_YF)

						restate= COLOR_CUBE_STATE_YT_BF;

						else if(restate==COLOR_CUBE_STATE_YT_BF)

						restate= COLOR_CUBE_STATE_BT_YF;

						else if(restate==COLOR_CUBE_STATE_YT_RF)

						restate= COLOR_CUBE_STATE_RT_YF;

						this->InitD[0] = restate;

						if (iceblock && ColorCubeCanBePushed(this, this->InitD[7], 0, 15)){

							i=0;

							OldX= this->X;//Save position of ffc

							OldY= this->Y;	

							loc = ComboAt(this->X+8, this->Y+8);//Reset block location				

							if (colundercombo>0){

									if (restate==COLOR_CUBE_STATE_RT_YF || restate==COLOR_CUBE_STATE_RT_BF)Screen->ComboD[loc] = colundercombo;

									else if (restate==COLOR_CUBE_STATE_BT_RF || restate==COLOR_CUBE_STATE_BT_YF)Screen->ComboD[loc] = colundercombo+1;

									else if (restate==COLOR_CUBE_STATE_YT_RF || restate==COLOR_CUBE_STATE_YT_BF)Screen->ComboD[loc] = colundercombo+2;

									Screen->ComboC[loc]=this->CSet;

								}

						}

						else this->InitD[7] = -1;

					}					

					else if(this->InitD[7]==DIR_DOWN){//pushing down

						while(this->Y<=OldY+15){

							i++;

							if (iceblock)i++;

							RenderColorCubeRotation(this,restate, this->InitD[7], i);

							this->Y++;

							if (iceblock)this->Y++;

							WaitNoAction();

						}						

						if(restate==COLOR_CUBE_STATE_RT_BF)

						restate= COLOR_CUBE_STATE_BT_RF;

						else if(restate==COLOR_CUBE_STATE_RT_YF)

						restate= COLOR_CUBE_STATE_YT_RF;

						else if(restate==COLOR_CUBE_STATE_BT_RF)

						restate= COLOR_CUBE_STATE_RT_BF;

						else if(restate==COLOR_CUBE_STATE_BT_YF)

						restate= COLOR_CUBE_STATE_YT_BF;

						else if(restate==COLOR_CUBE_STATE_YT_BF)

						restate= COLOR_CUBE_STATE_BT_YF;

						else if(restate==COLOR_CUBE_STATE_YT_RF)

						restate= COLOR_CUBE_STATE_RT_YF;

						this->InitD[0] = restate;

						if (iceblock && ColorCubeCanBePushed(this, this->InitD[7], 0, 15)){

							i=0;

							OldX= this->X;//Save position of ffc

							OldY= this->Y;	

							loc = ComboAt(this->X+8, this->Y+8);//Reset block location				

							if (colundercombo>0){

									if (restate==COLOR_CUBE_STATE_RT_YF || restate==COLOR_CUBE_STATE_RT_BF)Screen->ComboD[loc] = colundercombo;

									else if (restate==COLOR_CUBE_STATE_BT_RF || restate==COLOR_CUBE_STATE_BT_YF)Screen->ComboD[loc] = colundercombo+1;

									else if (restate==COLOR_CUBE_STATE_YT_RF || restate==COLOR_CUBE_STATE_YT_BF)Screen->ComboD[loc] = colundercombo+2;

									Screen->ComboC[loc]=this->CSet;

								}

						}

						else this->InitD[7] = -1;

					}					

					WaitNoAction();

				}				

				loc = ComboAt(this->X+8, this->Y+8);//Reset block location				

				UnderCombo= Screen->ComboD[loc];//Save combo at location	

				UnderCSet = Screen->ComboC[loc];

				combo= CMB_COLOR_CUBE_ORIGCMB+restate;//Determine what combo to use for ffc				

				this->Data= combo;//Set ffc to combo	

				if (colundercombo>0){

						if (restate==COLOR_CUBE_STATE_RT_YF || restate==COLOR_CUBE_STATE_RT_BF)Screen->ComboD[loc] = colundercombo+3;

						else if (restate==COLOR_CUBE_STATE_BT_RF || restate==COLOR_CUBE_STATE_BT_YF)Screen->ComboD[loc] = colundercombo+4;

						else if (restate==COLOR_CUBE_STATE_YT_RF || restate==COLOR_CUBE_STATE_YT_BF)Screen->ComboD[loc] = colundercombo+5;

						Screen->ComboC[loc]=this->CSet;

					}

				else Screen->ComboD[loc]= this->Data;//Set combo at location to ffc's data

				Screen->ComboC[loc]=this->CSet;

				flag= Screen->ComboF[loc];//Determine what the flag at the ffc's location is

				

				//Flag is Script 1. Used for red switches

				if(flag==CF_COLOR_CUBE_TRIGGER_RED && !flagundercombos){					

					if(restate==COLOR_CUBE_STATE_RT_BF//Red is on top

					||restate==COLOR_CUBE_STATE_RT_YF){						

						if (perm)Movable= false;//This cube can't be moved again

						this->InitD[6]=1;

						ColorCubeTriggerUpdate(this);

					}

				}

				

				//Flag is Script 2. Used for blue switches				

				else if(flag==CF_COLOR_CUBE_TRIGGER_BLUE && !flagundercombos){					

					if(restate==COLOR_CUBE_STATE_BT_RF//Blue is on top

					||restate==COLOR_CUBE_STATE_BT_YF){

						if (perm)Movable= false;

						this->InitD[6]=1;

						ColorCubeTriggerUpdate(this);

					}

				}

				

				//Flag is Script 3. Used for yellow switches

				else if(flag==CF_COLOR_CUBE_TRIGGER_YELLOW && !flagundercombos){					

					if(restate==COLOR_CUBE_STATE_YT_BF//Yellow is on top

					||restate==COLOR_CUBE_STATE_YT_RF){

						if (perm)Movable= false;

						this->InitD[6]=1;

						ColorCubeTriggerUpdate(this);

					}

				}				

				OldX= this->X;//Save position of ffc

				OldY= this->Y;				

				Push_Counter = 0;//Reset push counter

				i=0;

				if (flagundercombos && !Screen->State[ST_SECRET]&& colundercombo>0){

					Game->PlaySound(16);

					for (int i=0;i<=176;i++){

						if (i==176){

							Game->PlaySound(SFX_SECRET);

							Screen->TriggerSecrets();

							Screen->State[ST_SECRET]=true;

							break;

						}

						if (ComboFI(i,CF_COLOR_CUBE_TRIGGER_RED)){

							if (Screen->ComboD[i]!=colundercombo && Screen->ComboD[i]!=colundercombo+3){

								// Trace(i);

								break;

							}

						}

						if (ComboFI(i,CF_COLOR_CUBE_TRIGGER_BLUE)){

							if (Screen->ComboD[i]!=colundercombo+1 && Screen->ComboD[i]!=colundercombo+4){

								// Trace(i);

								break;

							}

						}

						if (ComboFI(i,CF_COLOR_CUBE_TRIGGER_YELLOW)){

							if (Screen->ComboD[i]!=colundercombo+2 && Screen->ComboD[i]!=colundercombo+5){

								// Trace(i);

								break;

							}

						}

					}

				}

				Waitframe();

			}			

			loc = ComboAt(this->X+8,this->Y+8);//Determine location of block			

			if (colundercombo==0)Screen->ComboD[loc]= this->Data;//Set combo at location to ffc data			

			Game->PlaySound(SFX_SECRET);//Play secret sfx			

			//Screen->D[perm]++;//Increment Screen->D register			

			Quit();//This script is done

		}

	}

}



//Renders color cube rotation animation.

void RenderColorCubeRotation(ffc f, int state, int dir, int i){

	int origtile = TILE_COLOR_CUBE_ANIMATION+1;

	int anim = 0;

	if (i>10) anim = 2;

	else if (i>=5) anim=1;

	int offset = 20*state;

	offset+=3*dir;

	Screen->FastTile(1, f->X, f->Y, origtile+offset+anim, f->CSet, OP_OPAQUE);

}



//Returns true, if block can be pushed.

bool ColorCubeCanBePushed(ffc f, int dir, int weight, int dirs){

	if (dir==DIR_UP){

		if ((dirs&1)==0) return false;

	}

	if (dir==DIR_DOWN){

		if ((dirs&2)==0) return false;

	}

	if (dir==DIR_LEFT){

		if ((dirs&4)==0) return false;

	}

	if (dir==DIR_RIGHT){

		if ((dirs&8)==0) return false;

	}

	int power = 0;

	int itm = GetCurrentItem(IC_BRACELET);

	if (itm>0){

		itemdata it = Game->LoadItemData(itm);

		power = it->Power;

	}

	if (power<weight) return false;	

	int cmb=ComboAt (CenterX (f), CenterY (f));

	int adj = ColorCubeAdjacentComboFix(cmb, dir);

	if (adj<0) return false;	

	if (ComboFI(adj, CF_NOBLOCKS))return false;	

	if (Screen->ComboS[adj]>0) return false;

	return true;

}



//Fixed variant of AdjacentCombo function from std_extension.zh

int ColorCubeAdjacentComboFix(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;

}



//Checks triggers and trigger secrets if all color cubes moved onto correct positions with correct facing

void ColorCubeTriggerUpdate(ffc this){

	for (int i=1;i<=33;i++){

		if (i==33){

			Game->PlaySound(SFX_SECRET);

			Screen->TriggerSecrets();

			Screen->State[ST_SECRET] =true;

			Quit();

		}

		else {

			ffc f = Screen->LoadFFC(i);

			if (f->Script!=this->Script) continue;

			if (f->InitD[7]>=0)break;

			if (f->InitD[6]==0)break;

		}

	}

}