Copy to Clipboard Test

Snake Dragging puzzle Code

const int TILE_SNAKEDRAG_CURSOR=365;//Tile used to render cursor during snake dragging.

const int LAYER_SNAKEDRAG_TRIGGER = 2;//Layer used to define the CSet for trigger combos

const int SFX_SNAKEDRAG_MOVE = 16;//Sound to play, when dragging the snake.

//Snake dragging puzzle from Machinarium. You have a maze containing different colored snakes made of combos.
//You can move the snakes in the maze by dragging their heads and tails (Ex1). Occupy all colored spots with same colored snakes to solve the puzzle.
//
//Set up combos as shown in demo.
//Build the puzzle: use 1st combo and different CSets. Make sure that each snake part combo have no more than 2 adjacent combos of the same CSet.
//Triggers: Place combos with target Cset on  LAYER_SNAKEDRAG_TRIGGER layer. In the demo, it`s different colored rails. Flag those spaces with flag 66 on layer 0.
//Place invisible FFC with script and 1st combo from step 1 anywhere in the screen. No arguments needed.
ffc script SnakeDragPuzzle{
	void run(){
		int origcmb=this->Data;
		int dirstate[176];
		int curpos=-1;
		int curcset=-1;
		int cmb=-1;
		SnakeConnectDirectionalCombos(dirstate,origcmb, true);
		while(true){
			cmb = ComboAt (CenterLinkX(), CenterLinkY());
			if (curpos<0){
				if ((IsSnakeHead(cmb, dirstate)>=0) && Link->PressEx1){
					curpos = cmb;
					curcset = Screen->ComboC[cmb];
				}
			}
			else{
				Screen->FastTile(2, ComboX(curpos), ComboY(curpos), TILE_SNAKEDRAG_CURSOR, curcset, OP_OPAQUE);
				if(cmb==curpos-1||cmb==curpos+1||cmb==curpos-16||cmb==curpos+16){
					if (dirstate[cmb]<0 && !ComboFI(cmb,CF_NOBLOCKS)){
						Game->PlaySound(SFX_SNAKEDRAG_MOVE);
						//move head
						int dr = AdjacentComboDir(curpos, cmb);
						dirstate[cmb]=1<<OppositeDir(dr);
						dirstate[curpos]|=(1<<dr);
						Screen->ComboD[cmb]=origcmb+dirstate[cmb];
						Screen->ComboD[curpos]=origcmb+dirstate[curpos];
						Screen->ComboC[cmb]= curcset;
						//move tail
						for (int i=0;i<176;i++){
							if (Screen->ComboC[i]!=curcset)continue;
							
							if (cmb==i)continue;
							int dir = IsSnakeHead(i, dirstate);
							if (dir<0)continue;
							int adjcmb = AdjacentComboFix(i, dir);
							if (adjcmb<0)continue;
							dirstate[i]=-1;
							dirstate[adjcmb]&= ~(1<<OppositeDir(dir));
							Screen->ComboD[i]= Screen->UnderCombo;
							Screen->ComboC[i]= Screen->UnderCSet;
							Screen->ComboD[adjcmb]=origcmb+dirstate[adjcmb];
							break;
						}
						curpos=cmb;
						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_BLOCKTRIGGER))continue;
							if (dirstate[i]<0)break;
							int trcset = GetLayerComboC(LAYER_SNAKEDRAG_TRIGGER, i);
							if (Screen->ComboC[i]!=trcset)break;
						}
					}
				}
				if (Link->PressEx1){
					curpos=-1;
					curcset=-1;
				}
			}
			//debugValue(1, dirstate[cmb]);
			if (Link->InputEx2)SmartCombos_Debug_DrawDirstate(dirstate, 976, 972, 5);
			Waitframe();
		}
	}
}

void DefineDirstate(int dirstate, int origcmb){
	for (int i=0; i<176; i++){
		if (Screen->ComboD[i]<origcmb)dirstate[i]=-1;
		else if (Screen->ComboD[i]>(origcmb+15))dirstate[i]=-1;
		else dirstate[i]= Screen->ComboD[i]-origcmb;
	}
}

void SnakeConnectDirectionalCombos(int dirstate, int origcmb, bool color){
	for (int i=0; i<176; i++){
		if (Screen->ComboD[i]<origcmb)dirstate[i]=-1;
		else if (Screen->ComboD[i]>(origcmb+15))dirstate[i]=-1;
		else{
			dirstate[i]=0;
			for (int d=0;d<4;d++){
				int adjcmb = AdjacentComboFix(i, d);
				if (adjcmb<0)continue;
				if (Screen->ComboD[adjcmb]<origcmb)continue;
				if (Screen->ComboD[i]>(origcmb+15))continue;
				if (color){
					if (Screen->ComboC[adjcmb]!= Screen->ComboC[i]) continue;
				}
				dirstate[i]+=(1<<d);
			}
		}
	}
	for (int i=0; i<176; i++){
		if (dirstate[i]>=0) Screen->ComboD[i]=origcmb+dirstate[i];
	}
}

//Fixed variant of AdjacentCombo function from std_extension.zh
int AdjacentComboFix(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;
}

int IsSnakeHead(int cmb, int dirstate){
	if (dirstate[cmb]==1) return DIR_UP;
	if (dirstate[cmb]==2) return DIR_DOWN;
	if (dirstate[cmb]==4) return DIR_LEFT;
	if (dirstate[cmb]==8) return DIR_RIGHT;
	return -1;
}

//Defines adjacency direction for given combos, or -1, if combos are not adjacent.
int AdjacentComboDir(int cmb1, int cmb2){
	for (int i=0;i<4;i++){
		if (AdjacentComboFix(cmb1, i)==cmb2) return i;
	}
	return -1;
}


void SmartCombos_Debug_DrawDirstate(int dirstate, int origcmb, int minuscmb, int cset){
	for (int i=0; i<176; i++){
		int state = dirstate[i];
		if (state<0) Screen->FastCombo(7, ComboX(i), ComboY(i), minuscmb, cset, OP_OPAQUE);
		else Screen->FastCombo(7, ComboX(i), ComboY(i), origcmb+dirstate[i], cset, OP_OPAQUE);
	}
}