const int CSET_ROT_MOLE_DEFAULT = 7;//SCet used to render crystals in default color
const int CSET_ROT_MOLE_HINT = 8;//Ditto for int one
const int CSET_ROT_MOLE_CORRECT = 5;//Ditto for correct one during resolution
const int CSET_ROT_MOLE_FAIL = 11;//Same for rendering other in case of hitting wrong crystal.
const int SFX_ROT_MOLE_HIT = 6;//Sount to play on hitting correct crystal.
const int SFX_ROT_MOLE_FAIL = 32;//Ditto for hitting wrong one.
const int ROT_MOLE_INIT_SPEED = 1;//Speed of crystals spreading during intro animation
const int ROT_MOLE_HINT_RENDER_TIME =90;//Time before correct crystal recolors into other cystals colors.
const int ROT_MOLE_COOLDOWN = 75;//Cooldown between registering hits, in frames.
const int SPR_ROT_MOLE = 91;//Sprite used for crystals.
const int SPR_ROT_MOLE_DISAPPEAR = 22;//Sprite used for appearing/disappearing of crystals.
const int FONT_ROT_MOLE_TIMER = 0;//Font used to render timer for minigame, if time is limited.
const int CSET_FONT_SHADOW_ROT_MOLE = 0x71;//Shadow for HUD font drawing (Color).
//Crystal rotating whack-a-mole challenge.
//Several crystals are rotating in circle, one of them has different color (can hide after certain of time). Hero must hit it with his sword. Afterwards alother crystal lights up and speed can change, including reversing direction. Hit the correct crystal enough times to win. If wrong crystal was hit several times or limited time runs out, the challenge is failed.
//1. Setup countdown combos for fail counter, 3->2->1->0...
//2. Set up sprite for crystal, set SPR_ROT_MOLE ti ID of that sprite.
//3. Place FFC in the center of the circle.
// D0 - number of crystals, max 12.
// D1 - Orbiting radius, in pixels.
// D2 - Minimum rotating speed, negative to reverse direction.
// D3 - Maximum orbiting speed.
// D4 - Add together: 1 - skip expanding introduction, 2 - hide correct crystal after ROT_MOLE_HINT_RENDER_TIME frames passed, 4 - invert rotation after each hit.
// D5 - Time limit, in frames. 0 for no time limit.
// D6 - Number of succeful hits to win the minigame.
// D7 - Number of failed hits before challenge is lost.
ffc script RotWhackMole{
void run (int number, int radius, int speed1, int speed2, int flags, int time, int target, int fails){
int timer = time;
lweapon moles[12];// Max 12 too restrictive? Expand it. Make sure that D1 is big enough to avoid sword hitbox issues.
number = Clamp(number, 0, 12);
int state = 0;
int angle = Rand(359);
int State=0;
int initradius = 0;
int cangle = angle;
int curspeed = speed1;
int curtarget = 0;
int hinttimer = -1;
int hitcounter = 0;
if ((flags&2)>0) hinttimer=ROT_MOLE_HINT_RENDER_TIME;
int mole_collision = -1;
int cooldown = 0;
int counter = 0;
if ((flags&1)>0)State=1;
for (int i=0; i<number; i++){
moles[i]=CreateLWeaponAt(LW_SCRIPT10, this->X, this->Y);
moles[i]->UseSprite(SPR_ROT_MOLE);
moles[i]->CollDetection=false;
moles[i]->CSet = CSET_ROT_MOLE_DEFAULT;
if ((flags&1)>0){
cangle = angle+i*360/number;
moles[i]->X =this->X + VectorX(radius, cangle);
moles[i]->Y =this->Y + VectorY(radius, cangle);
if (SPR_ROT_MOLE_DISAPPEAR>0){
lweapon s = CreateLWeaponAt(LW_SPARKLE, moles[i]->X, moles[i]->Y);
s->UseSprite(SPR_ROT_MOLE_DISAPPEAR);
s->CollDetection=false;
}
}
}
while(true){
if (State==0){
initradius+=ROT_MOLE_INIT_SPEED;
for (int i=0; i<number; i++){
cangle = angle+i*360/number;
moles[i]->X =this->X + VectorX(initradius, cangle);
moles[i]->Y =this->Y + VectorY(initradius, cangle);
moles[i]->DeadState=WDS_ALIVE;
}
if (initradius>=radius){
State=1;
}
}
if (State==1){
angle +=curspeed;
for (int i=0; i<number; i++){
cangle = angle+i*360/number;
moles[i]->X =this->X + VectorX(radius, cangle);
moles[i]->Y =this->Y + VectorY(radius, cangle);
moles[i]->CSet = CSET_ROT_MOLE_DEFAULT;
moles[i]->DeadState=WDS_ALIVE;
}
if (hinttimer>0) hinttimer--;
if (hinttimer!=0)moles[curtarget]->CSet = CSET_ROT_MOLE_HINT;
else moles[curtarget]->CSet = CSET_ROT_MOLE_DEFAULT;
for (int lw = 1; lw<=Screen->NumLWeapons(); lw++){
lweapon l = Screen->LoadLWeapon(lw);
if (l->ID!=LW_SWORD) continue;
for (int i=0; i<number; i++){
if (Collision(l,moles[i])){
mole_collision = i;
break;
}
}
}
if (mole_collision>=0){
if (curtarget == mole_collision){
Game->PlaySound(SFX_ROT_MOLE_HIT);
State=2;
counter++;
cooldown = ROT_MOLE_COOLDOWN;
curspeed = 0;
hitcounter++;
if (hitcounter>=target){
Game->PlaySound(SFX_SECRET);
Screen->TriggerSecrets();
Screen->State[ST_SECRET]=true;
this->CSet=CSET_ROT_MOLE_CORRECT;
for (int i=0; i<number; i++){
if (SPR_ROT_MOLE_DISAPPEAR>0){
lweapon s = CreateLWeaponAt(LW_SPARKLE, moles[i]->X, moles[i]->Y);
s->UseSprite(SPR_ROT_MOLE_DISAPPEAR);
s->CollDetection=false;
}
Remove(moles[i]);
}
Quit();
}
}
else {
Game->PlaySound(SFX_ROT_MOLE_FAIL);
fails--;
this->Data++;
if (fails<=0){
this->CSet=CSET_ROT_MOLE_FAIL;
for (int i=0; i<number; i++){
if (SPR_ROT_MOLE_DISAPPEAR>0){
lweapon s = CreateLWeaponAt(LW_SPARKLE, moles[i]->X, moles[i]->Y);
s->UseSprite(SPR_ROT_MOLE_DISAPPEAR);
s->CollDetection=false;
}
Remove(moles[i]);
}
Quit();
}
State=3;
cooldown = ROT_MOLE_COOLDOWN;
curspeed = 0;
}
}
if (timer>0){
if (CSET_FONT_SHADOW_ROT_MOLE>0){//Render timer
Screen->DrawInteger(6,this->X+1, this->Y-15,FONT_ROT_MOLE_TIMER, CSET_FONT_SHADOW_ROT_MOLE, -1, 0,0,Floor(timer/60),0, OP_OPAQUE);
}
Screen->DrawInteger(6,this->X, this->Y-16,FONT_ROT_MOLE_TIMER, 1, -1, 0,0, Floor(timer/60),0, OP_OPAQUE);
timer--;
if (timer<=0){
Game->PlaySound(SFX_ROT_MOLE_FAIL);
for (int i=0; i<number; i++){
if (SPR_ROT_MOLE_DISAPPEAR>0){
lweapon s = CreateLWeaponAt(LW_SPARKLE, moles[i]->X, moles[i]->Y);
s->UseSprite(SPR_ROT_MOLE_DISAPPEAR);
s->CollDetection=false;
}
Remove(moles[i]);
}
this->CSet=CSET_ROT_MOLE_FAIL;
Quit();
}
}
}
if (State==2){
for (int i=0; i<number; i++){
cangle = angle+i*360/number;
moles[i]->X =this->X + VectorX(radius, cangle);
moles[i]->Y =this->Y + VectorY(radius, cangle);
moles[i]->DeadState=WDS_ALIVE;
//moles[i]->CSet = CSET_ROT_MOLE_DEFAULT;
}
moles[mole_collision]->CSet = CSET_ROT_MOLE_CORRECT;
cooldown--;
if (cooldown<=0){
if ((flags&2)>0) hinttimer=ROT_MOLE_HINT_RENDER_TIME;
if (speed2>speed1) curspeed = speed1+Rand(10*speed2-10*speed1)/10;
if ((flags&4)>0 && IsOdd(counter)) curspeed*=-1;
mole_collision=-1;
curtarget = Rand(number);
State=1;
}
if (timer>0){
if (CSET_FONT_SHADOW_ROT_MOLE>0){//Render timer
Screen->DrawInteger(6,this->X+1, this->Y-15,FONT_ROT_MOLE_TIMER, CSET_FONT_SHADOW_ROT_MOLE, -1, 0,0,Floor(timer/60),0, OP_OPAQUE);
}
Screen->DrawInteger(6,this->X, this->Y-16,FONT_ROT_MOLE_TIMER, 1, -1, 0,0, Floor(timer/60),0, OP_OPAQUE);
}
}
if (State==3){
for (int i=0; i<number; i++){
cangle = angle+i*360/number;
moles[i]->X =this->X + VectorX(radius, cangle);
moles[i]->Y =this->Y + VectorY(radius, cangle);
moles[i]->DeadState=WDS_ALIVE;
moles[i]->CSet = CSET_ROT_MOLE_FAIL;
}
moles[curtarget]->CSet = CSET_ROT_MOLE_CORRECT;
cooldown--;
if (cooldown<=0){
if ((flags&2)>0) hinttimer=ROT_MOLE_HINT_RENDER_TIME;
if (speed2>speed1) curspeed = speed1+Rand(speed2-speed1);
if ((flags&4)>0 && IsOdd(counter)) curspeed*=-1;
mole_collision=-1;
curtarget = Rand(number);
State=1;
}
if (timer>0){
if (CSET_FONT_SHADOW_ROT_MOLE>0){//Render timer
Screen->DrawInteger(6,this->X+1, this->Y-15,FONT_ROT_MOLE_TIMER, CSET_FONT_SHADOW_ROT_MOLE, -1, 0,0,Floor(timer/60),0, OP_OPAQUE);
}
Screen->DrawInteger(6,this->X, this->Y-16,FONT_ROT_MOLE_TIMER, 1, -1, 0,0, Floor(timer/60),0, OP_OPAQUE);
}
}
Waitframe();
}
}
}