const int NPC_SPINNING_TILE_PLACEHOLDER = 85; //Enemy used as a placeholder for spinning tile rooms (to hold shutters closed)
//D0: Layer to check for flags on (use one >2 so it doesn't do additional behaviors)
//D1: Combo for spinning tile spawner, 0 to advance the combo at each position by 1
//D2: CSet for spinning tile spawner, 0 to ignore
//D3: Delay between each tile that spawns in the sequence (60ths of a second)
//D4: Initial delay before spawning tiles (60ths of a second)
//D5: If >0 auto generate a spinning tile pattern on all combos with this flag (on layer specified by D0)
//D6: If >0, create a placeholder enemy that exists until all tiles have been spawned
ffc script SpinningTileSpawner{
void run(int whichlayer, int cmb, int cset, int delay, int initdelay, int autopattern, int useplaceholder){
int i; int j;
//Create a placeholder enemy to hold shutters open if needed
npc placeholder;
if(useplaceholder){
placeholder = CreateNPCAt(NPC_SPINNING_TILE_PLACEHOLDER, 120, -32);
placeholder->CollDetection = false;
placeholder->HP = 10000;
}
mapdata lyr = Game->LoadTempScreen(whichlayer);
int patternStep = 1;
int maxSteps;
//Figure out auto generated patterns
int numTiles = 0;
int pattern[176];
int orderPos[176];
int seed[2];
seed[0] = autopattern;
seed[1] = 111;
if(autopattern>0){
//Populate an array with all the flagged positions
for(i=0; i<176; ++i){
if(lyr->ComboF[i]==autopattern){
orderPos[numTiles] = i;
++numTiles;
}
}
//If the flag isn't found, report something went wrong
if(numTiles==0){
printf("No instance of flag %d (D5) found on layer %d (D0) onscreen. Are your D0 and D5 set up correctly?\n", autopattern, whichlayer);
}
else{
//Scramble the positions in the array
for(i=0; i<704; ++i){
int a = STS_srand(seed, numTiles);
int b = STS_srand(seed, numTiles);
int backup = orderPos[a];
orderPos[a] = orderPos[b];
orderPos[b] = backup;
}
//Set pattern flag array based on the scrambled positions
for(i=0; i<numTiles; ++i){
pattern[orderPos[i]] = i+1;
}
}
maxSteps = numTiles;
}
else{
//If not auto generated, get the max number of steps by the highest numbered flag
for(i=0; i<176; ++i){
if(lyr->ComboF[i]>0){
if(lyr->ComboF[i]>maxSteps)
maxSteps = lyr->ComboF[i];
}
}
}
Waitframes(initdelay);
//Repeat for as many combos as there can be on screen
for(i=0; i<maxSteps; ++i){
//Scan the screen for the marker flags
for(j=0; j<176; ++j){
int orderFlag = lyr->ComboF[j];
//Use the array if pattern is auto generated
if(autopattern>0&&orderFlag==autopattern){
orderFlag = pattern[j];
}
if(orderFlag==patternStep){
if(cmb>0)
Screen->ComboD[j] = cmb;
else
++Screen->ComboD[j];
if(cset>0)
Screen->ComboC[j] = cset;
}
}
++patternStep;
Waitframes(delay);
}
Waitframes(30);
while(true){
if(NumNPCsOf(NPC_SPINTILE)==0)
break;
Waitframe();
}
Waitframes(30);
//Kill the placeholder enemy if it was created
if(placeholder->isValid()){
placeholder->Remove();
}
}
//Seeded randomization stuff for autogenerated patterns
void STS_XORShift_SetSeed(int seed, int x, int y) {
seed[0] = x & 0xFFFF;
seed[1] = y & 0xFFFF;
}
int STS_XORShift_Next(int seed) {
int t = seed[0] ^ ((seed[0] << 5) & 0xFFFF);
seed[0] = seed[1];
seed[1] = (seed[1] ^ (seed[1] >> 1));
seed[1] ^= t ^ (t >> 3);
return seed[1];
}
int STS_srand(int seed, int max){
if(seed[0]==-1)
return Rand(max);
else
return STS_XORShift_Next(seed)%max;
}
}
const int SPR_SPINNINGTILEBREAK = 95; //Sprite to display when the tile breaks
const int SIZE_SPINNINTILEBREAK = 1; //Width/Height of the sprite
const int SFX_SPINNINGTILEBREAK = 4; //Sound to play when the tile breaks
const bool SPINNINGTILE_BREAK_SOLID = true; //Whether or not solidity breaks the tile
npc script SpinningTileBreakSolid{
void run(){
int tileYOff;
int yOffTimer;
while(true){
++yOffTimer;
tileYOff = -(yOffTimer>>4);
//Wait for the enemy to touch solidity or die
if((SPINNINGTILE_BREAK_SOLID&&SpinningTileIsSolid(this->X+8, this->Y+8))||this->HP<=0){
lweapon l = CreateLWeaponAt(LW_SPARKLE, this->X, this->Y+tileYOff);
l->UseSprite(SPR_SPINNINGTILEBREAK);
if(SIZE_SPINNINTILEBREAK>1){
l->Extend = 3;
l->TileWidth = SIZE_SPINNINTILEBREAK;
l->TileHeight = SIZE_SPINNINTILEBREAK;
l->DrawXOffset = -(SIZE_SPINNINTILEBREAK-1)*8;
l->DrawYOffset = -(SIZE_SPINNINTILEBREAK-1)*8;
}
l->CollDetection = false;
//Only play the sound when the enemy is onscreen
if(this->X>=0&&this->X<=240&&this->Y>=0&&this->Y<=160)
Game->PlaySound(SFX_SPINNINGTILEBREAK);
this->Remove();
}
Waitframe();
}
}
bool SpinningTileIsSolid(int x, int y){
mapdata l1 = Game->LoadTempScreen(1);
mapdata l2 = Game->LoadTempScreen(2);
int pos = ComboAt(x, y);
int bit;
if(x%16<8){
if(y%16<8)
bit = 0001b;
else
bit = 0010b;
}
else{
if(y%16<8)
bit = 0100b;
else
bit = 1000b;
}
int solidity;
switch(Screen->ComboT[pos]){
case CT_LADDERHOOKSHOT:
case CT_LADDERONLY:
case CT_HOOKSHOTONLY:
case CT_WATER:
case CT_SWIMWARP:
case CT_SWIMWARPB:
case CT_SWIMWARPC:
case CT_SWIMWARPD:
case CT_DIVEWARP:
case CT_DIVEWARPB:
case CT_DIVEWARPC:
case CT_DIVEWARPD:
break;
default:
solidity |= Screen->ComboS[pos];
break;
}
switch(l1->ComboT[pos]){
case CT_LADDERHOOKSHOT:
case CT_LADDERONLY:
case CT_HOOKSHOTONLY:
case CT_WATER:
case CT_SWIMWARP:
case CT_SWIMWARPB:
case CT_SWIMWARPC:
case CT_SWIMWARPD:
case CT_DIVEWARP:
case CT_DIVEWARPB:
case CT_DIVEWARPC:
case CT_DIVEWARPD:
break;
default:
solidity |= l1->ComboS[pos];
break;
}
switch(l2->ComboT[pos]){
case CT_LADDERHOOKSHOT:
case CT_LADDERONLY:
case CT_HOOKSHOTONLY:
case CT_WATER:
case CT_SWIMWARP:
case CT_SWIMWARPB:
case CT_SWIMWARPC:
case CT_SWIMWARPD:
case CT_DIVEWARP:
case CT_DIVEWARPB:
case CT_DIVEWARPC:
case CT_DIVEWARPD:
break;
default:
solidity |= l2->ComboS[pos];
break;
}
return solidity&bit;
}
}