const int SFX_COLORSORT_PICK = 4;//Sound to play when picking ball from jar
const int SFX_COLORSORT_MOVE = 16;//Sound to play when putting a ball into jar
const int SFX_COLORSORT_SPECIAL_TRIGGER = 47;//Sound to play on special trigger
const int TILE_COLORSORT_TOP = 36169; //Tile used for jar top
const int TILE_COLORSORT_MIDDLE = 36170;//Tile used for jar middle
const int TILE_COLORSORT_BOTTOM = 36171;//Tile used for jar bottom
const int TILE_COLORSORT_BALL = 36147;//Tiles used to render balls. Must be ID of leftmost ball tile.
const int TILE_COLORSORT_GOAL = 36172;//Tile used to mark completed jars
const int TILE_COLORSORT_COLOR_HINT = 36189;//Tiles used to render color clues at jar bases. Must be ID of leftmost tile. This row and TILE_COLORSORT_BALL tile row must have the same color order.
const int TILE_COLORSORT_EMPTY_HINT = 36188;//Tile used to indicate that jar must be empty to solve the puzzle.
const int LINK_MISC_BALL_IN_HAND = 0;//Link Misc variable to track which ball Link has in hand.
const int LINK_MISC_PREV_JAR = 1;//Link Misc variable to track which jar Link was drawn from.
const int CSET_COLORSORT = 7; //CSet used to render jars.
const int CSET_COLORSORT_BALLS = 2;//CSet used to render balls.
const int CSET_COLORSORT_GOAL = 8;//CSet used to mark completed jars
const int CSET_COLORSORT_SPECIAL_GOAL = 5;//CSet used to mark special-triggered jars
const int COLORSORT_SIZE_PER_UNIT = 8;//Unit to pixel conversion rate. Used for rendering script draws.
const int SCREEN_D_COLORSORT_SPECIAL_TRIGGER = 0;//Screen D register used to track special-triggered jars
//Color/gem sorting puzzle.
//You have a number of jars. Some of them have colored gems stacked one on top of another.
//You can grab top gem from jap and drop it into another jar, adding it on top of the stack.
//If all jars are either empty, or full of same-colored gems, puzzle is solved.
//Some jars have colored base and can be completed oly if filled with gems of specific color.
//
//1. Set up tiles to render jars, balls and goal marks. Tiles must be as tall as COLORSORT_SIZE_PER_UNIT constant, rest should be transparent.
// Balls must be consecutive.
//2. Also for color targeted jars set up a sequence of tiles with the same color order as balls needed to render color clues on jar bases
// And one tile for clue that jar must be empty.
//3. Set up constants inside script file. For instance, TILE_COLORSORT_BALL to ID of leftmost ball tile.
//4. Set up combo for bottom part of the jar what looks like, if jar was empty.
//5. Place jar FFC`s with combo from step 4 as Data and assigned script. Make sure there is a solid combo underneath FFC and free spot 1 space south.
// D0 to D2. IDs of ball colors (0-6), starting from bottom. Color are read in pairs.
// #####.____ - lower ball
// _____.#### - higher ball
// D3 - #####.____ - jar capacity, in units.
// _____.#### - add together: 1 - It`s prohibited to place a ball onto different-colored one, unless placed back where it was taken from. 2 - ban placing colored balls into jar that had special trigger already activated (D5 != 0).
// D4 - Target color. The jar must be filled with gems of this color ID to count as complete. -1 - jar must be empty for puzzle to be solved. 0 - any color.
// D5 - Special trigger option in completing jar. >0 - ID of Item awarded, <0 - Instant secret trigger. Balls will disappear on special trigger.
// D6 - Screen D bit ID to track special triggers. Musi be unique for each jar in the screen.
ffc script ColorSortPipe{
void run(int ball1, int ball2, int ball3, int set, int targcolor, int specialtrigger, int dbit){
int balls[6]={GetHighFloat(ball1), GetLowFloat(ball1),GetHighFloat(ball2), GetLowFloat(ball2), GetHighFloat(ball3), GetLowFloat(ball3)};
BallGravity(balls);
Link->Misc[LINK_MISC_BALL_IN_HAND]=0;
Link->Misc[LINK_MISC_PREV_JAR]=0;
int cap = GetHighFloat(set);
int flags = GetLowFloat(set);
int drawy = this->Y;
if (JarCompleted(balls,cap,targcolor) && specialtrigger==0) this->InitD[7]=1;
else this->InitD[7]=0;
while(true){
if ((Link->Y == this->Y + 8 && (Link->X < this->X + 8 && Link->X > this->X - 8) && Link->Dir == DIR_UP)){
if (Link->PressEx1){
if (Link->Misc[LINK_MISC_BALL_IN_HAND] == 0){
if ( GetLastNonZero(balls)>0){
Game->PlaySound(SFX_COLORSORT_PICK);
Link->Misc[LINK_MISC_BALL_IN_HAND] = RemoveFromArray (balls);
Link->Misc[LINK_MISC_PREV_JAR] = FFCNum(this);
if (specialtrigger>0){
if (!GetScreenDBit(SCREEN_D_COLORSORT_SPECIAL_TRIGGER, dbit) && JarCompleted(balls,cap,targcolor)){
if (specialtrigger>0) item it = CreateItemAt(specialtrigger,Link->X,Link->Y);
else if (!Screen->State[ST_SECRET]) {
Game->PlaySound(SFX_SECRET);
Screen->TriggerSecrets();
Screen->State[ST_SECRET] = true;
}
SetScreenDBit(SCREEN_D_COLORSORT_SPECIAL_TRIGGER, dbit, true);
Game->PlaySound(SFX_COLORSORT_SPECIAL_TRIGGER);
for (int i=0;i<6;i++){
balls[i]=0;
}
}
}
if (!GetScreenDBit(SCREEN_D_COLORSORT_SPECIAL_TRIGGER, dbit))this->InitD[7]=0;
if (JarCompleted(balls, cap, targcolor)) this->InitD[7]=1;
}
}
else{
if (BallFitInJar(flags, this, balls)){
if (balls[cap-1]==0){
AppendToArray(balls, Link->Misc[LINK_MISC_BALL_IN_HAND]);
Link->Misc[LINK_MISC_BALL_IN_HAND]=0;
Link->Misc[LINK_MISC_PREV_JAR]=0;
Game->PlaySound(SFX_COLORSORT_MOVE);
if (specialtrigger==0){
this->InitD[7]=0;
if (JarCompleted(balls,cap,targcolor)) this->InitD[7]=1;
for (int i=1; i<=33; i++){
if (i==33){
Game->PlaySound(SFX_SECRET);
Screen->TriggerSecrets();
Screen->State[ST_SECRET] = true;
break;
}
ffc s = Screen->LoadFFC(i);
if (s->Script!=this->Script) continue;
if (s->InitD[7]==0) break;
}
}
else if (!GetScreenDBit(SCREEN_D_COLORSORT_SPECIAL_TRIGGER, dbit) && JarCompleted(balls,cap,targcolor)){
if (specialtrigger>0){
item it = CreateItemAt(specialtrigger,Link->X,Link->Y);
it->Pickup +=IP_HOLDUP;
}
else if (!Screen->State[ST_SECRET]) {
Game->PlaySound(SFX_SECRET);
Screen->TriggerSecrets();
Screen->State[ST_SECRET] = true;
}
SetScreenDBit(SCREEN_D_COLORSORT_SPECIAL_TRIGGER, dbit, true);
Game->PlaySound(SFX_COLORSORT_SPECIAL_TRIGGER);
for (int i=0;i<6;i++){
balls[i]=0;
}
Waitframe();
this->InitD[7]=1;
for (int i=1; i<=33; i++){
if (i==33){
Game->PlaySound(SFX_SECRET);
Screen->TriggerSecrets();
Screen->State[ST_SECRET] = true;
break;
}
ffc s = Screen->LoadFFC(i);
if (s->Script!=this->Script) continue;
if (s->InitD[7]==0) break;
}
}
}
}
}
}
}
//render jars and colored balls in them
drawy = this->Y;
for (int i = 0; i<SizeOfArray(balls); i++){
if (balls[i]>0)Screen->FastTile(Cond(i==0, 1, 4), this->X, drawy, TILE_COLORSORT_BALL+balls[i]-1,CSET_COLORSORT_BALLS , OP_OPAQUE);
drawy-=COLORSORT_SIZE_PER_UNIT;
}
drawy = this->Y;
for (int t=1; t<=cap+1; t++){
int tile = TILE_COLORSORT_MIDDLE;
if (t==1) tile = TILE_COLORSORT_BOTTOM;
if (t ==cap+1) tile = TILE_COLORSORT_TOP;
Screen->FastTile(Cond(t==1, 0, 4), this->X, drawy, tile, CSET_COLORSORT, OP_OPAQUE);
//Screen->DrawTile(Cond(t==1, 2, 4), this->X, drawy, tile, 1, 1, CSET_WATERJAR, -1, -1, 0, 0, 0, 0, true, OP_OPAQUE);
drawy-=COLORSORT_SIZE_PER_UNIT;
}
drawy+=COLORSORT_SIZE_PER_UNIT;
if (this->InitD[7]>0)Screen->FastTile(4, this->X, drawy, TILE_COLORSORT_GOAL,Cond(GetScreenDBit(SCREEN_D_COLORSORT_SPECIAL_TRIGGER, dbit),CSET_COLORSORT_SPECIAL_GOAL,CSET_COLORSORT_GOAL),OP_OPAQUE);
if (Link->Misc[LINK_MISC_BALL_IN_HAND]>0)Screen->FastTile(3, Link->X, Link->Y-8, TILE_COLORSORT_BALL+Link->Misc[LINK_MISC_BALL_IN_HAND]-1, CSET_COLORSORT_BALLS, OP_OPAQUE);
if (targcolor>0)Screen->FastTile(3,this->X, this->Y, TILE_COLORSORT_COLOR_HINT+targcolor-1,CSET_COLORSORT_BALLS,OP_OPAQUE);
if (targcolor<0)Screen->FastTile(3,this->X, this->Y, TILE_COLORSORT_EMPTY_HINT,CSET_COLORSORT_BALLS,OP_OPAQUE);
Waitframe();
}
}
}
bool BallFitInJar(int flags, ffc f, int balls){
if ((flags&2)>0 && GetScreenDBit(SCREEN_D_COLORSORT_SPECIAL_TRIGGER, f->InitD[6])) return false;
if (GetLastNonZero(balls)==0) return true;
if ((flags&1)>0 && Link->Misc[LINK_MISC_BALL_IN_HAND]!=GetLastNonZero(balls) && Link->Misc[LINK_MISC_PREV_JAR]!=FFCNum(f))return false;
return true;
}
bool JarCompleted(int arr, int cap, int targcolor){
if(GetLastNonZero(arr)==0 && targcolor==0)return true;
if(GetLastNonZero(arr)>0 && targcolor<0)return false;
int check = arr[0];
if (targcolor>0 && check!=targcolor) return false;
for(int i=0; i<cap; i++){
if (arr[i]!=check)return false;
}
return true;
}
void BallGravity(int arr){
for( int i=1; i<6; i++){
if (arr[i]==0)continue;
int p=i;
while(arr[p-1]==0){
SwapArray(arr, p, p-1);
p--;
}
}
}
void AppendToArray(int arr, int a){
for (int i =0; i<SizeOfArray(arr);i++){
if (arr[i]>0) continue;
arr[i]=a;
return;
}
}
int GetLastNonZero(int arr){
for (int i=SizeOfArray(arr)-1; i>=0;i--){
if (arr[i]==0) continue;
return arr[i];
}
return 0;
}
int RemoveFromArray (int arr){
for (int i=SizeOfArray(arr)-1; i>=0;i--){
if (arr[i]==0) continue;
int ret = arr[i];
arr[i]=0;
return ret;
}
}
//Swaps two elements in the given array
void SwapArray(int arr, int pos1, int pos2){
int r = arr[pos1];
arr[pos1]=arr[pos2];
arr[pos2]=r;
}