const int STATUE_PUZZLE_INVISIBLE_COMBO = 2487; //Solid Combo used to mimic solidity. Must use transparent tile.
const int STATUE_PUZZLE_MOVE = 50; //Sound to play, when puzzle statue moves.
const int STATUE_PUZZLE_TURN = 16; //Sound to play, when puzzle statue rotates.
const int CMB_STATUE_PUZZLE_SHADOW=910;//Combo used to render shadow beheath jumping statue. 0 for no jumping
const int CSET_STATUE_PUZZLE_SHADOW=7;//CSet used to render shadow beheath jumping statue. 0 for no jumping
//Statue puzzle from Zelda: Twilight Princess. Stand on remote controller, then press Ex2 to rotate all statues anti-clockwise, or press Ex1
//to move each statue one combo in direction they facing, unless obstructed. Beware of collisions! Land all statues on trigger spaces simultaneously to solve the puzzle.
//1. Surround puzzle area with NoPushblocks flags. Build the floor on background layer
//2. Place flags 62 - 65 (Pushblock, one direction, many) to define starting position and orientation of statues.
//3. Flag trigger spaces with flag 66 (Block trigger).
//4. Set up 4 combos in sequence for statues Up-Down-Left-Right
//5. Place FFC with TP_StatuePuzzle script
//D0 - ID of 1st statue combo (statue facing upwards)
//D1 - Music to play until puzzle is solved. 0 - Use Screen Midi. Setting this argument to >0 also causes MIDI to stop when puzzle is solved.
ffc script TP_StatuePuzzle{
void run(int cmb, int music){
if (Screen->State[ST_SECRET]) Quit();
if (music>0) Game->PlayMIDI(music);
int str[] = "TP_PuzzleStatue";
int scr = Game->GetFFCScript(str);
for (int i=0; i<176; i++){
if (Screen->ComboF[i]<62) continue;
if (Screen->ComboF[i]>65) continue;
int args[8] = {music,0,0,0,0,0,0, Screen->ComboF[i]-62};
ffc f = RunFFCScriptOrQuit(scr, args);
f->X = ComboX(i);
f->Y = ComboY(i);
f->CSet = this->CSet;
f->Data=cmb;
Screen->ComboF[i]=0;
}
int animcounter=0;
for (int i=0;i<176;i++){
if (ComboFI(i, CF_BLOCKTRIGGER))Screen->ComboF[i] = CF_BLOCKTRIGGER;
}
while(true){
if (animcounter==0){
if (RectCollision(Link->X+7, Link->Y+7, Link->X+8, Link->Y+8, this->X, this->Y, this->X+this->EffectWidth-1, this->Y+this->EffectHeight-1)){
if (Link->PressEx2){
Game->PlaySound(STATUE_PUZZLE_TURN);
for (int i = 1; i<=32; i++){
ffc f = Screen->LoadFFC(i);
if (f->InitD[6]>0) continue;
if (f->InitD[5]==2) continue;
f->InitD[7]=TpRotDir(f->InitD[7], -2);
}
}
if(Link->PressEx1){
for (int i = 1; i<=32; i++){
ffc f = Screen->LoadFFC(i);
if (f->InitD[5]==2) continue;
f->InitD[6]=1;
}
animcounter=16;
}
}
}
else{
animcounter--;
}
if (Screen->State[ST_SECRET]) Quit();//Paste code line here, TacoChopper.
Waitframe();
}
}
}
ffc script TP_PuzzleStatue{
void run(int music){
int origcmb = this->Data;
int curcmb = this->Data;
int cmb = ComboAt(this->X, this->Y);
int ucmb = Screen->ComboD[cmb];
int ucset = Screen->ComboC[cmb];
Screen->ComboD[cmb] = STATUE_PUZZLE_INVISIBLE_COMBO;
int animcounter=0;
int adjcmb = -1;
int jumpz=0;
while(true){
if (animcounter==0){
if (this->InitD[6]>0){
bool solid = false;
adjcmb = TpStatueAdjacentComboFix(cmb, this->InitD[7]);
if (Screen->ComboS[adjcmb]>0) solid = true;
if (ComboFI(adjcmb, CF_NOBLOCKS))solid =true;
if (!solid){
Game->PlaySound(STATUE_PUZZLE_MOVE);
Screen->ComboC[cmb]=ucset;
Screen->ComboD[cmb]=ucmb;
this->InitD[5]=0;
animcounter=16;
}
else this->InitD[6]=0;
}
}
else{
if (this->InitD[7]==DIR_UP) this->Y--;
if (this->InitD[7]==DIR_DOWN) this->Y++;
if (this->InitD[7]==DIR_LEFT) this->X--;
if (this->InitD[7]==DIR_RIGHT) this->X++;
if (animcounter>12) jumpz+=2;
else if (animcounter>8) jumpz+=1;
else if (animcounter>4) jumpz-=1;
else jumpz-=2;
animcounter--;
if (animcounter==0){
if (StatueCollision(this)){
lweapon l = CreateLWeaponAt(LW_BOMBBLAST, this->X, this->Y);
l->CollDetection=false;
Waitframe();
for(int i=1;i<=32;i++){
ffc f = Screen->LoadFFC(i);
if (f->Script!=this->Script)continue;
f->InitD[5]=2;
}
this->Data=0;
Quit();
}
cmb=adjcmb;
this->InitD[6]=0;
if (this->InitD[5]!=2)this->InitD[5]=0;
ucmb = Screen->ComboD[cmb];
ucset = Screen->ComboC[cmb];
Screen->ComboD[cmb] = STATUE_PUZZLE_INVISIBLE_COMBO;
if (ComboFI(cmb, CF_BLOCKTRIGGER)&&(this->InitD[5]==0))this->InitD[5]=1;
for(int i=1;i<=33;i++){
if (i==33){
Game->PlaySound(SFX_SECRET);
Screen->TriggerSecrets();
Screen->State[ST_SECRET]=true;
if (music>0)Game->PlayMIDI(0);
break;
}
ffc f = Screen->LoadFFC(i);
if (f->Script!=this->Script)continue;
if (f->InitD[5]!=1)break;
}
}
}
curcmb = origcmb+this->InitD[7];
if ((CMB_STATUE_PUZZLE_SHADOW>0) && (animcounter>0)){
this->Data=FFCS_INVISIBLE_COMBO;
Screen->FastCombo(Cond(animcounter>0, 4,1), this->X, this->Y-jumpz, curcmb, this->CSet, OP_OPAQUE);
Screen->FastCombo(1, this->X, this->Y,CMB_STATUE_PUZZLE_SHADOW, CSET_STATUE_PUZZLE_SHADOW, OP_TRANS);
}
else this->Data=curcmb;
Waitframe();
}
}
}
//Fixed variant of AdjacentCombo function from std_extension.zh
int TpStatueAdjacentComboFix(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;
}
//Rotated the given direction num times clockwise in 8-way rose wind
//Use negative values for anti-clockwise direction.
int TpRotDir(int dir, int num){
int dirs[8] = {DIR_UP, DIR_RIGHTUP, DIR_RIGHT, DIR_RIGHTDOWN, DIR_DOWN, DIR_LEFTDOWN, DIR_LEFT, DIR_LEFTUP};
int idx=-1;
for (int i=0; i<8; i++){
if (dirs[i] == dir){
idx=i;
break;
}
}
if (idx<0) return -1;
idx+=num;
while (idx<0) idx+=8;
while (idx>=8) idx-=8;
return dirs[idx];
}
bool StatueCollision(ffc f){
for(int i=1;i<=32;i++){
ffc n = Screen->LoadFFC(i);
if (f==n)continue;
if (n->Script!=f->Script)continue;
if (RectCollision(f->X, f->Y, f->X + 15, f->Y +15, n->X, n->Y, n->X +15, n->Y +15)) return true;
}
return false;
}