Copy to Clipboard Test

Block Puzzle Undo Button Code

const int MAX_REWIND_STEPS = 100;
const int SFX_BLOCKPUZZLEUNDO = 56;

bool PressBlockPuzzleUndo()
{
	// Change to whatever button you want to use
	return Link->PressEx4;
}

class BlockPuzzleStep
{
	int LinkX;
	int LinkY;
	int LinkDir;
	int ChangeData[0];
	
	BlockPuzzleStep(int linkX, int linkY, int linkDir)
	{
		LinkX = linkX;
		LinkY = linkY;
		LinkDir = linkDir;
	}
	
	void AddChange(int lyr, int pos, int oldCD, int oldCC, int oldCF)
	{
		int sz = SizeOfArray(ChangeData);
		ResizeArray(ChangeData, sz+5);
		ChangeData[sz] = lyr;
		ChangeData[sz+1] = pos;
		ChangeData[sz+2] = oldCD;
		ChangeData[sz+3] = oldCC;
		ChangeData[sz+4] = oldCF;
	}
	void RevertChange(int[] lastComboD, int[] lastComboC, int[] lastComboF)
	{
		int sz = SizeOfArray(ChangeData)/5;
		// If this change was due to a block push, move Link to his stored position
		if(LinkX>-1)
		{
			Link->X = LinkX;
			Link->Y = LinkY;
			Link->Dir = LinkDir;
		}
		// Load the relevant temp screens
		mapdata lyr[3];
		for(int i=0; i<=2; ++i)
		{
			lyr[i] = Game->LoadTempScreen(i);
		}
		// Then cycle through all the changes
		for(int i=0; i<sz; ++i)
		{
			int j = i*5;
			int l = ChangeData[j];
			int pos = ChangeData[j+1];
			// Change the active combos first
			lyr[l]->ComboD[pos] = ChangeData[j+2];
			lyr[l]->ComboC[pos] = ChangeData[j+3];
			lyr[l]->ComboF[pos] = ChangeData[j+4];
			// Also update the arrays in the main script
			lastComboD[l*NUM_COMBO_POS+pos] = lyr[l]->ComboD[pos];
			lastComboC[l*NUM_COMBO_POS+pos] = lyr[l]->ComboC[pos];
			lastComboF[l*NUM_COMBO_POS+pos] = lyr[l]->ComboF[pos];
		}
	}
}

@InitD0("Ignore Flag"),
@InitDHelp0("If >0, this flag will prevent any combo positions with this flag on any layer from being stored. Use this for things like doors and lock blocks that you don't want to be rewound."),
@InitD1("Only Store Blocks"),
@InitDHelp1("If true, only push block changes will be saved"),
@InitD2("Quit On Secret"),
@InitDHelp2("If 1, quits on any triggered secret. If 2, quits on permanent secrets.")
ffc script BlockPuzzleRewind
{
	void run(int ignoreFlag, bool onlyBlocks, int quitOnSecret)
	{
		// These arrays store the last state of the screen to revert to
		int lastComboD[0];
		int lastComboC[0];
		int lastComboF[0];
		
		// Then this list is changes made to the lastCombo arrays
		BlockPuzzleStep StepsList[0];
		
		ResizeArray(lastComboD, NUM_COMBO_POS*3);
		ResizeArray(lastComboC, NUM_COMBO_POS*3);
		ResizeArray(lastComboF, NUM_COMBO_POS*3);
		
		// Load up all the temp screens for later use
		mapdata lyr[7];
		for(int i=0; i<7; ++i)
		{
			lyr[i] = Game->LoadTempScreen(i);
		}
		
		// Store the contents of the first three layers
		for(int i=0; i<=2; ++i)
		{
			for(int j=0; j<NUM_COMBO_POS; ++j)
			{
				lastComboD[i*NUM_COMBO_POS+j] = lyr[i]->ComboD[j];
				lastComboC[i*NUM_COMBO_POS+j] = lyr[i]->ComboC[j];
				lastComboF[i*NUM_COMBO_POS+j] = lyr[i]->ComboF[j];
			}
		}
		
		bool blockMoving;
		int pushBlockLayer = -1;
		int pushBlockPos = -1; // Starting position of the push block
		int pushBlockEndPos = -1; // Ending/current position of the push block
		// These are Link's position at the start of the push or secret
		int pushLinkX = -1;
		int pushLinkY = -1;
		int pushLinkDir;
		while(true)
		{
			// Check for secret triggering and quit out if applicable
			if(Screen->SecretsTriggered())
			{
				switch(quitOnSecret)
				{
					case 1:
						Quit();
						break;
					case 2:
						if(Screen->State[ST_SECRET])
							Quit();
						break;
				}
			}
			// These are the values that'll be rewound to when stepping backwards
			int tempX = Link->X;
			int tempY = Link->Y;
			int tempDir = Link->Dir;
			// If only push blocks count, default to assuming combo changes aren't relevant
			// If there's a push block in play
			if(Screen->MovingBlockX>-1)
			{
				// Was it just pushed?
				if(!blockMoving)
				{
					blockMoving = true;
					pushBlockLayer = Screen->MovingBlockLayer;
					pushBlockPos = ComboAt(Screen->MovingBlockX+8, Screen->MovingBlockY+8);
					// Store where Link was
					pushLinkX = Link->X;
					pushLinkY = Link->Y;
					pushLinkDir = Link->Dir;
				}
				pushBlockEndPos = ComboAt(Screen->MovingBlockX+8, Screen->MovingBlockY+8);
			}
			else
			{
				// Has it stopped moving?
				if(blockMoving)
				{
					blockMoving = false;
					// Update Link's position to that of the block
					tempX = pushLinkX;
					tempY = pushLinkY;
					tempDir = pushLinkDir;
				}
			}
			
			// Don't update changes until a push block has finished moving
			if(!blockMoving)
			{
				BlockPuzzleStep currentStep;
				
				// For each layer...
				for(int i=0; i<=2; ++i)
				{
					// And each combo position...
					for(int j=0; j<NUM_COMBO_POS; ++j)
					{
						bool ignore;
						// Ignore things that aren't push blocks if specified
						if(onlyBlocks&&i!=pushBlockLayer&&j!=pushBlockPos&&j!=pushBlockEndPos)
							ignore = true;
						// Check if combo data has changed
						if(lyr[i]->ComboD[j]!=lastComboD[i*NUM_COMBO_POS+j])
						{
							// If there's an ignore flag, check for it on all 7 layers
							if(ignoreFlag)
							{
								for(int k=0; k<7; ++k)
								{
									if(lyr[k]->ComboF[j]==ignoreFlag||lyr[k]->ComboI[j]==ignoreFlag)
										ignore = true;
								}
							}
							if(!ignore)
							{
								// If the array has already reached max size, shift it down and erase the last space
								int sz = SizeOfArray(StepsList);
								if(sz>=MAX_REWIND_STEPS)
								{
									if(StepsList[0])
										delete StepsList[0];
									for(int i=0; i<sz-1; ++i)
									{
										StepsList[i] = StepsList[i+1];
									}
									ResizeArray(StepsList, sz-1);
								}
								// If no changes have been recorded this frame, create a new object and expand the array
								if(currentStep==NULL)
								{
									currentStep = new BlockPuzzleStep(tempX, tempY, tempDir);
									sz = SizeOfArray(StepsList);
									ResizeArray(StepsList, sz+1);
									StepsList[sz] = currentStep;
								}
								// Add the steps and update the stored combos
								currentStep->AddChange(i, j, lastComboD[i*NUM_COMBO_POS+j], lastComboC[i*NUM_COMBO_POS+j], lastComboF[i*NUM_COMBO_POS+j]);
							}	
							lastComboD[i*NUM_COMBO_POS+j] = lyr[i]->ComboD[j];
							lastComboC[i*NUM_COMBO_POS+j] = lyr[i]->ComboC[j];
							lastComboF[i*NUM_COMBO_POS+j] = lyr[i]->ComboF[j];
						}
					}
				}
			}
			
			// Clean up block variables while the block isn't moving
			if(!blockMoving)
			{
				pushBlockLayer = -1;
				pushBlockPos = -1;
				pushBlockEndPos = -1;
			}
			
			if(!blockMoving&&PressBlockPuzzleUndo())
			{
				int sz = SizeOfArray(StepsList);
				// Don't undo if there's nothing in the array
				if(sz>0)
				{
					Game->PlaySound(SFX_BLOCKPUZZLEUNDO);
					StepsList[sz-1]->RevertChange(lastComboD, lastComboC, lastComboF);
					delete StepsList[sz-1];
					ResizeArray(StepsList, sz-1);
				}
			}
			Waitframe();
		}
	}
}