Copy to Clipboard Test

Non-solid Klotski Blocks Code

// const int CMB_KLOTSKI_SOLID= 1; //combo used to mimic solidity.
// const int CMB_KLOTSKI_NONSOLID= 2239; //combo used to mimic non-solid obstacle, must have CF_NOBLOCKS inherent flag (#67).

const int CF_KLOTSKI_ICE_STOP = 98;//Combo flag to define non-solid catcher for ice blocks.
const int CF_KLOTSKI_ICE_STOP_PLUS_TRIGGER = 99;//Combo flag to define non-solid catcher for ice blocks and trigger in the same combo position.
const int CF_AUTOMATIC_KLOTSKI_MAKER = 100;//Combo flag used to label combos for AutomaticKlotski1x1Maker script to make Klotski blocks out of.

const int SFX_KLOTSKI_MOVE = 50; //Sound to play when moving a block.

//Non-solid Klotski puzzle block. Stand on it, face desired direction and press EX1 to move it. 
//Land all colored blocks on the same-colored triggers to solve the puzzle.

//Effect Width and Height used and is automatically set to be tile dimensions multiplied by 16.

//D0 - bracelet level requirement.
//D1 - allowed push directions. Add together - 1-up, 2-down, 4-left, 8-right. 0 for all directions.
//D2 - 1 - require same-colord trigger combo for trigger secrets. 
//D3 - 0 - not count for trigger
//D4 - +1 - allow pushing onto same-colored #67 flags, +2 -> Icy variant, continues moving in direction, +4 - bounce off another ice block.

ffc script NonSolidKlotskiBlock{
	void run(int weight, int dirs, int colortrigger, int trigger, int colorblock){
		if (dirs==0) dirs=15;
		int pos = ComboAt (this->X+1, this->Y+1);
		int ucmb[16];
		int ucset[16];
		int tmppos = pos;
		for (int i=0;i<16;i++){
			if ((i%4) >= this->TileWidth){
				ucmb[i]=0;
				ucset[i]=0;
				continue;
			}
			if (Floor(i/4) >= this->TileHeight){
				ucmb[i]=0;
				ucset[i]=0;
				continue;
			}
			int x = this->X+(i%4)*16;
			int y = this->Y+Floor(i/4)*16;
			tmppos = ComboAt(x,y);
			ucmb[i] = Screen->UnderCombo;
			ucset[i] = Screen->UnderCSet;
			Screen->ComboF[tmppos]=CF_NOBLOCKS;
		}
		//KLOTSKI_ReplaceCombosUnderFFC(this, ucmb, ucset);
		this->InitD[7] = -1;
		int movecounter = 0;
		while(true){
			if (this->InitD[7]<0){
				if (RectCollision(Link->X+7, Link->Y+7, Link->X+8, Link->Y+8, this->X, this->Y, this->X+this->TileWidth*16-1, this->Y+this->TileHeight*16-1)){
					if (Link->PressEx1){
						if (KlotskiCanBePushed(this, Link->Dir, weight, dirs)){
							this->InitD[7] = Link->Dir;
							Game->PlaySound(SFX_KLOTSKI_MOVE);
							movecounter = 16;
							if ((this->InitD[4]&2)>0)movecounter=8;
							for (int i=0;i<16;i++){
								if ((i%4) >= this->TileWidth) continue;
								if (Floor(i/4) >= this->TileHeight) continue;
								int x = this->X+(i%4)*16;
								int y = this->Y+Floor(i/4)*16;
								tmppos = ComboAt(x,y);
								Screen->ComboF[tmppos]=0;
							}
							if ((this->InitD[4]&6)!=6)KLOTSKI_ReplaceCombosUnderFFC(this, ucmb, ucset);
						}
						
					}
				}
			}
			else{
				NoAction();
				movecounter--;
				if (this->InitD[7]==DIR_UP){
					this->Y--;
					if ((this->InitD[4]&2)>0)this->Y--;
				}
				if (this->InitD[7]==DIR_DOWN){
					if ((this->InitD[4]&2)>0)this->Y++;
					this->Y++;
				}
				if (this->InitD[7]==DIR_LEFT){
					if ((this->InitD[4]&2)>0)this->X--;
					this->X--;
				}
				if (this->InitD[7]==DIR_RIGHT){
					if ((this->InitD[4]&2)>0)this->X++;
					this->X++;
				}
				if (movecounter==0){
					if (IceKlotskiCanContinueSlide(this)) movecounter=8;
					else{
						pos = ComboAt (this->X+1, this->Y+1);					
						this->InitD[7]=-1;						
						for (int i=0;i<16;i++){
							if ((i%4) >= this->TileWidth) continue;
							if (Floor(i/4) >= this->TileHeight) continue;
							int x = this->X+(i%4)*16;
							int y = this->Y+Floor(i/4)*16;
							tmppos = ComboAt(x,y);
							Screen->ComboF[tmppos]=CF_NOBLOCKS;
						}
						if (trigger)KlotskiTriggerUpdate(this, colortrigger>0, ucset);
						if ((this->InitD[4]&6)!=6)KLOTSKI_ReplaceCombosUnderFFC(this, ucmb, ucset);						
					}
				}
			}
			Waitframe();
		}
	}
}

//Automatic 1x1 Klotski block mass-maker, for non-solid anagram puzzles.
//1.Flag all combos to be transformed into Klotski Blocks with CF_AUTOMATIC_KLOTSKI_MAKER combo flag
//2.Place invisible FFC with script anywhere oin the screen. No arguments needed.
ffc script AutomaticKlotski1x1Maker{
	void run(){
		int str[]="NonSolidKlotskiBlock";
		int scr = Game->GetFFCScript(str);
		for (int i=0;i<176;i++){
			if (!ComboFI(i, CF_AUTOMATIC_KLOTSKI_MAKER))continue;
			int args[8] = {0,0,0,0,0,0,0,0};
			ffc n = RunFFCScriptOrQuit(scr, args);
			n->Data = Screen->ComboD[i];
			n->CSet = Screen->ComboC[i];
			n->X = ComboX(i);
			n->Y = ComboY(i);
		}
	}
}

//returns true, if Link tries to push this block
bool KlotskiIsPushed(ffc this, int dir, int margin){
	if((Link->X == this->X - 16 && (Link->Y < this->Y+this->TileHeight*16 - 8+margin && Link->Y > this->Y - margin) && Link->InputRight && dir == DIR_RIGHT) || // Right
	(Link->X == this->X + this->TileWidth*16 && (Link->Y < this->Y+this->TileHeight*16 - 8+margin && Link->Y > this->Y - margin) && Link->InputLeft && dir == DIR_LEFT) || // Left
	(Link->Y == this->Y - 16 && (Link->X < (this->X + this->TileWidth*16-16+margin) && Link->X > this->X - margin) && Link->InputDown && dir == DIR_DOWN) || // Down
	(Link->Y == this->Y + this->TileHeight*16-8 && (Link->X < (this->X + this->TileWidth*16-16+margin) && Link->X > this->X - margin) && Link->InputUp && dir == DIR_UP)) { // Up
		return true;
	}
	return false;
}

//Returns true, if block can be pushed in the given direction
bool KlotskiCanBePushed(ffc this, 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 br = GetHighestLevelItemOwned(IC_BRACELET);
	int power = 0;
	if (br>=0){
		itemdata it = Game->LoadItemData(br);
		power = it->Power;
	}
	if (power<weight) return false;
	int x1 = 0;
	int y1 = 0;
	int x2 = 0;
	int y2 = 0;
	if (dir==DIR_UP){
		x1 = this->X+1;
		y1 = this->Y-15;
		x2 = this->X+this->TileWidth*16-2;
		y2 = this->Y-1;
	}
	if (dir==DIR_DOWN){
		x1 = this->X+1;
		y1 = this->Y+ this->TileHeight*16+1;
		x2 = this->X + this->TileWidth*16-2;
		y2 = y1+14;
	}
	if (dir==DIR_LEFT){
		x1 = this->X-14;
		y1 = this->Y+1;
		x2 = this->X-1;
		y2 = y1+this->TileHeight*16-3;
	}
	if (dir==DIR_RIGHT){
		x1 = this->X+this->TileWidth*16+1;
		y1 = this->Y+1;
		x2 = x1+14;
		y2 = y1+this->TileHeight*16-3;
	}
	// Screen->Rectangle(3, x1, y1, x2, y2, 1, -1,0, 0, 0,false, OP_OPAQUE);
	for (int i=0;i<176;i++){
		int cx1 = ComboX(i);
		int cy1 = ComboY(i);
		int cx2 = cx1+15;
		int cy2 = cy1+15;
		if (!RectCollision(x1,y1,x2,y2,cx1,cy1,cx2,cy2))continue;
		if (ComboFI(i, CF_NOBLOCKS)){
			if (OccupiedByKlotskiBlock(i, this)){
				if ((this->InitD[4]&6)==6 && this->InitD[7]>=0){
					for (int i=0;i<16;i++){
						if ((i%4) >= this->TileWidth) continue;
						if (Floor(i/4) >= this->TileHeight) continue;
						int x = this->X+(i%4)*16;
						int y = this->Y+Floor(i/4)*16;
						int tmppos = ComboAt(x,y);
						if (!ComboFI(tmppos,CF_KLOTSKI_ICE_STOP) && !ComboFI(tmppos,CF_KLOTSKI_ICE_STOP_PLUS_TRIGGER) ){
							this->InitD[7]=OppositeDir(this->InitD[7]);
							break;
						}
					}
				}
				return false;
			}
			if ((this->InitD[4]&1)==0)return false;
			if (Screen->ComboC[i]!=this->CSet)return false;
		}
		if (Screen->ComboS[i]>0)return false;
		int comboS=0;
		if(Screen->LayerMap(1)>0) comboS |= GetLayerComboS(1, i);
		if(Screen->LayerMap(2)>0) comboS |= GetLayerComboS(2, i);
		if(comboS>0)return false;
	}
	return true;
}

//Returns true, if the given space is occupied by a Klotski block
bool OccupiedByKlotskiBlock(int pos, ffc f){
	int scr = f->Script;
	for(int i=1;i<=32;i++){
		ffc n = Screen->LoadFFC(i);
		if (n->Script!=f->Script)continue;
		for (int i=0;i<16;i++){
			if ((i%4) >= f->TileWidth) continue;
			if (Floor(i/4) >= f->TileHeight) continue;
			int x = n->X+(i%4)*16;
			int y = n->Y+Floor(i/4)*16;
			if (ComboAt(x,y)==pos) return true;		
		}
	}
	return false;
}

//Returns true if Icy klotski block can continue slide
bool IceKlotskiCanContinueSlide(ffc f){
	int dir = f->InitD[7];
	if ((f->InitD[4]&2)==0)return false;
	if (!KlotskiCanBePushed(f, dir, 0, 15)){
		if (f->InitD[7]!=dir)return true;
		else return false;
	}
	for (int i=0;i<16;i++){
		if ((i%4) >= f->TileWidth) continue;
		if (Floor(i/4) >= f->TileHeight) continue;
		int x = f->X+(i%4)*16;
		int y = f->Y+Floor(i/4)*16;
		int tmppos = ComboAt(x,y);
		if (!ComboFI(tmppos,CF_KLOTSKI_ICE_STOP) && !ComboFI(tmppos,CF_KLOTSKI_ICE_STOP_PLUS_TRIGGER) ) return true;
	}
	return false;
}

//Stores and replaces combos under FFC to mimic solidity.
void KLOTSKI_ReplaceCombosUnderFFC(ffc this, int ucmb, int ucset){
	for (int i=0;i<16;i++){
		if ((i%4) >= this->TileWidth) continue;
		if (Floor(i/4) >= this->TileHeight) continue;
		int x = this->X+(i%4)*16;
		int y = this->Y+Floor(i/4)*16;
		int tmppos = ComboAt(x,y);
		int rcmb = Screen->ComboD[tmppos];
		int rcset = Screen->ComboC[tmppos];
		Screen->ComboD[tmppos] = ucmb[i];
		Screen->ComboC[tmppos] = ucset[i];
		ucmb[i] = rcmb;
		ucset[i] = rcset;
	}
}



//Checks, if all pushblocks are on triggers and triggers secrets, if it`s true;
void KlotskiTriggerUpdate(ffc f, bool color, int ucset){	
	f->InitD[6]=1;
	for (int i=0;i<16;i++){
		if ((i%4) >= f->TileWidth) continue;
		if (Floor(i/4) >= f->TileHeight) continue;
		int x = f->X+(i%4)*16;
		int y = f->Y+Floor(i/4)*16;
		int tmppos = ComboAt(x,y);
		// Screen->Rectangle(3, x, y, x+15, y+15, 1, -1,0, 0, 0,false, OP_OPAQUE);
		if (!ComboFI(tmppos, CF_BLOCKTRIGGER) && !ComboFI(tmppos, CF_KLOTSKI_ICE_STOP_PLUS_TRIGGER)) f->InitD[6]=0;
		if (color && Screen->ComboC[tmppos]!=f->CSet)f->InitD[6]=0;
	}
	for(int i=1;i<=33;i++){
		if (Screen->State[ST_SECRET]) break;
		if (i==33){
			Game->PlaySound(SFX_SECRET);
			Screen->TriggerSecrets();
			Screen->State[ST_SECRET]=true;
			break;
		}
		ffc n = Screen->LoadFFC(i);
		if (n->Script!=f->Script)continue;
		if (n->InitD[3]==0)continue;
		if (n->InitD[6]==0)break;
	}
}