///////////////////////////////
/// Z3 Style Chest Minigame ///
/// v0.4.4 - 27-Nov-2016 ///
/// By: ZoriaRPG ///
///////////////////////////////
/// Final Release ///
///////////////////////////////
//! The initial combo used for this ffc (on the screen) should be a rupee icon with a numeric cost.
//! The next combo in the list, should be blank.
//Combos
const int CMB_CHEST_GAME_CLOSED = 896; //The 'closed' chest combo.
//! This should be combo type 'none', and solid.
const int CMB_CHEST_GAME_OPEN = 897; //The closed chest combo.
//! This should be the combo immediately after CMB_CHEST_GAME_CLOSED', with a type of 'none'.
//Strings
const int MSG_CHEST_GAME_RULES = 3; //Screen->Message string that explains the game rules to the player.
const int MSG_CHEST_GAME_OVER = 2; //Screen->Message string for the end of a game round.
//Sounds
const int SFX_OPEN_CHEST = 20; //The sound that will play when Link opens a chest, and an item is awarded.
const int SFX_CHEST_GAME_SPECIAL_PRIZE_FANFARE = 27; //The sound to play when the player finds the special item in a chest.
const int SFX_CHEST_GAME_START = 35; //The sound to play when the player starts the game.
//Other Settings and Options
const int CHEST_GAME_SCREEN_D_REGISTER = 5; //The Screen->D[index] used to store if the main prize has been awarded.
const int CHEST_GAME_HOLDUP_TYPE = 4; // The type of item hold-up tp use. 4 = LA_HOLD1LAND(one hand); 5 = LA_HOLD2LAND(two hands)
const int CHEST_GAME_ALLOW_REPLAY = 0; //Set to '1' to allow the player to play again without leaving the screen.
const int TIME_INPUT_UP_OPEN_CHEST = 50; //The number of frames of inputting up will open a closed chest.
const int OPEN_CHESTS_FROM_SIDES_OR_ABOVE = 1; //Set to '0' to only permit opening them from the bottom.
// Chest Game FFC
//~ //D0: Number of chests to allow the player to open, per play.
//~ //D1: ID of special prize to award.
//~ //D2: Percentage chance of awarding special prize.
//~ //D3: Backup prize, if special prize already awarded (e.g. 100 rupees, instead of a heart piece). Set to '0' to not offer a backup prize.
//~ //D4: Cost per play.
//~ //D5: String for game rules.
//~ //D6: String to play at end of game.
//~ //D7: Set to '1' to allow replaying without leaving the screen.
ffc script ChestMiniGame{
void run(int max_chests_Link_can_open, int specialPrize, int percentMainPrize, int backupPrize, int costPerPlay, int msgRules, int msgEnd, int allowReplay){
//Populate with the IDs of prizes to award. Each index is a 1% chance.
int chestPrizes[]= { 72, 0, 30, 0, 39, 0, 39, 0, 2, 29,
0, 0, 23, 72, 0, 72, 72, 79, 80, 30,
0, 2, 39, 38, 71, 60, 0, 40, 0, 87,
87, 0, 86, 80, 0, 0, 2, 29, 60, 29,
0, 71, 73, 0, 87, 73, 0, 79, 38, 0,
17, 80, 40, 23, 0, 0, 38, 0, 0, 0,
0, 38, 24, 0, 60, 71, 2, 1, 73, 81,
1, 70, 0, 29, 0, 0, 0, 80, 0, 86,
79, 0, 70, 0, 70, 0, 0, 0, 23, 0,
0, 2, 0, 10, 0, 38, 2, 70, 70, 86 };
int initialData = this->Data; //Store the initial combo, to revert, if replay is enabled.
int chestComboSpots[176];
int check;
int cmb;
int mainprize;
int timer = TIME_INPUT_UP_OPEN_CHEST;
bool openchest;
int has_opened_number_of_chests;
bool award_main_prize;
bool gameRunning;
bool gameOver;
item i;
bool giveprize;
bool awardnormalprize;
if ( msgRules ) Screen->Message(msgRules);
else Screen->Message(MSG_CHEST_GAME_RULES); //Show the string for the chest game rules.
while(true) {
if ( max_chests_Link_can_open == has_opened_number_of_chests ) gameOver = true;
if ( gameOver && ( !CHEST_GAME_ALLOW_REPLAY && !allowReplay ) || allowReplay < 0 ) break;
if ( gameOver && ( CHEST_GAME_ALLOW_REPLAY || allowReplay > 0 ) ) {
gameOver = false;
gameRunning = false;
this->Data = initialData;
for ( int q = 0; q < 176; q++ ) {
if ( Screen->ComboD[q] == CMB_CHEST_GAME_OPEN ) Screen->ComboD[q] = CMB_CHEST_GAME_CLOSED;
}
has_opened_number_of_chests = 0;
}
if ( LinkCollision(this) && Game->Counter[CR_RUPEES] > costPerPlay && ____Xor(Link->PressA,Link->PressB) && !gameRunning ) {
//If Link collides with the ffc, which should show the cost, and presses a button, start the game.
if ( SFX_CHEST_GAME_START ) Game->PlaySound(SFX_CHEST_GAME_START);
gameRunning = true;
Game->DCounter[CR_RUPEES] -= costPerPlay;
this->Data++; //increase to the next combo, removing the cost icon.
}
if ( gameRunning ) {
//Check to see if the combo above Link is a chest.
if ( Link->Dir == DIR_UP ){
cmb = Screen->ComboD[ ___AdjacentCombo(ComboAt(Link->X+8, Link->Y+8),Link->Dir) ];
if ( cmb == CMB_CHEST_GAME_CLOSED ) {
if ( timer && Link->InputUp ) timer--;
if ( timer <= 0 || ____Xor(Link->PressA,Link->PressB)) {
has_opened_number_of_chests++;
if ( SFX_OPEN_CHEST ) Game->PlaySound(SFX_OPEN_CHEST);
Screen->ComboD[ ___AdjacentCombo(ComboAt(Link->X+8, Link->Y+8),Link->Dir) ]++;
timer = TIME_INPUT_UP_OPEN_CHEST;
giveprize = true;
Link->InputUp = false;
}
}
else timer = TIME_INPUT_UP_OPEN_CHEST;
}
else if ( Link->Dir == DIR_DOWN && OPEN_CHESTS_FROM_SIDES_OR_ABOVE ){
cmb = Screen->ComboD[ ___AdjacentCombo(ComboAt(Link->X+8, Link->Y+8),Link->Dir) ];
if ( cmb == CMB_CHEST_GAME_CLOSED ) {
if ( timer > 0 && Link->InputDown ) timer--;
if ( timer <= 0 || ____Xor(Link->PressA,Link->PressB) ) {
has_opened_number_of_chests++;
if ( SFX_OPEN_CHEST ) Game->PlaySound(SFX_OPEN_CHEST);
Screen->ComboD[ ___AdjacentCombo(ComboAt(Link->X+8, Link->Y+8),Link->Dir) ]++;
timer = TIME_INPUT_UP_OPEN_CHEST;
giveprize = true;
Link->InputUp = false;
}
}
else timer = TIME_INPUT_UP_OPEN_CHEST;
}
else if ( Link->Dir == DIR_LEFT && OPEN_CHESTS_FROM_SIDES_OR_ABOVE ) {
cmb = Screen->ComboD[ ___AdjacentCombo(ComboAt(Link->X+8, Link->Y+8),Link->Dir) ];
if ( cmb == CMB_CHEST_GAME_CLOSED ) {
if ( timer > 0 && Link->InputLeft ) timer--;
if ( timer <= 0 || ____Xor(Link->PressA,Link->PressB) ) {
has_opened_number_of_chests++;
if ( SFX_OPEN_CHEST ) Game->PlaySound(SFX_OPEN_CHEST);
Screen->ComboD[ ___AdjacentCombo(ComboAt(Link->X+8, Link->Y+8),Link->Dir) ]++;
timer = TIME_INPUT_UP_OPEN_CHEST;
giveprize = true;
Link->InputUp = false;
}
}
else timer = TIME_INPUT_UP_OPEN_CHEST;
}
else if ( Link->Dir == DIR_RIGHT && OPEN_CHESTS_FROM_SIDES_OR_ABOVE ) {
cmb = Screen->ComboD[ ___AdjacentCombo(ComboAt(Link->X+8, Link->Y+8),Link->Dir)];
if ( cmb == CMB_CHEST_GAME_CLOSED ) {
if ( timer > 0 && Link->InputRight ) timer--;
if ( timer <= 0 || ____Xor(Link->PressA,Link->PressB) ) {
has_opened_number_of_chests++;
if ( SFX_OPEN_CHEST ) Game->PlaySound(SFX_OPEN_CHEST);
Screen->ComboD[ ___AdjacentCombo(ComboAt(Link->X+8, Link->Y+8),Link->Dir) ]++;
timer = TIME_INPUT_UP_OPEN_CHEST;
giveprize = true;
Link->InputUp = false;
}
}
else timer = TIME_INPUT_UP_OPEN_CHEST;
}
if ( giveprize ) {
check = Rand(1,100); //Make a check, to use for determining if the main prize should e awarded.
if ( check <= percentMainPrize ) award_main_prize = true; //If that check passes, then we will award the main prize.
if ( check > percentMainPrize ) { awardnormalprize = true; check = Rand(0,SizeOfArray(chestPrizes)); }//Otherwise, reuse that var, and make a new check to determine
//the prize to award from the table.
int itm;
if ( !awardnormalprize && award_main_prize && !Screen->D[CHEST_GAME_SCREEN_D_REGISTER] ) { //The main prize has not been awarded, and has been randomly chosen.
Game->PlaySound(SFX_CHEST_GAME_SPECIAL_PRIZE_FANFARE); //Play the fanfare...
i = Screen->CreateItem(specialPrize); //Assign the pointer, and make the item.
itm = specialPrize; //Set the value of the item ID to a var so that we can use it for holding it up.
}
if ( !awardnormalprize && award_main_prize && Screen->D[CHEST_GAME_SCREEN_D_REGISTER] && backupPrize ) { //The main prize has already been awarded, so recheck.
Game->PlaySound(SFX_CHEST_GAME_SPECIAL_PRIZE_FANFARE); //Play the special award fanfare...
i = Screen->CreateItem(backupPrize); //Assign the pointer, and make the item.
itm = backupPrize; //Set the value of the item ID to a var so that we can use it for holding it up.
}
if ( awardnormalprize && check ) { //Otherwise, if the check to award a special prize did not pass..
Game->PlaySound(SFX_OPEN_CHEST); //otherwise, play the default.
i = Screen->CreateItem(chestPrizes[check]); //Award a normal prize, from the list.
itm = chestPrizes[check]; //Set the value of the item ID to a var so that we can use it for holding it up.
}
if ( check ) { //if we're awarding a prize...
i -> X = Link->X;
i -> Y = Link->Y;
if ( CHEST_GAME_HOLDUP_TYPE ) { //If the setting to hold the item overhead is enabled...
Link->Action = CHEST_GAME_HOLDUP_TYPE; //Hold the item overhead, using the value of that setting.
Link->HeldItem = itm;
}
if ( award_main_prize ) { Screen->D[CHEST_GAME_SCREEN_D_REGISTER] = 1; award_main_prize = false; } //Set the register so that Link cannot collect the special prize again.
giveprize = false;
while( Link->Action == LA_HOLD1LAND ) Waitframe();
}
else Remove(i); //if check is zero, remove the item pointer.
//This allows chances of getting nothing at all.
}
if ( has_opened_number_of_chests >= max_chests_Link_can_open ) {
gameOver = true;
gameRunning = false;
if ( msgEnd ) Screen->Message(msgEnd);
else Screen->Message(MSG_CHEST_GAME_OVER);
}
}
Waitframe();
}
//If we reach here, then the chest game is over.
this->Data = 0;
this->Script = 0;
Quit();
}
//Constants for AdjacentCombo()
//This now uses DIR_* constants, so you can do AdjacentCombo(cmb,Link->Dir)
//Returns the Nth combo index of a combo based on a central point, and a direction.
//For example, combo 22 + COMBO_UPRIGHT returns '7',
//as combo 7 is to the upper-right of combo 22.
int ___AdjacentCombo(int cmb, int dir){
int combooffsets[13]={-0x10, 0x10, -1, 1, -0x11, -0x0F, 0x0F, 0x11};
if ( cmb % 16 == 0 ) combooffsets[9] = 1;
if ( (cmb & 15) == 1 ) combooffsets[10] = 1;
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] && ( dir == 7 || dir == 0 || dir == 6 || dir == 0 ) ) return 0; //if the left columb
if ( combooffsets[10] && ( dir == 3 || dir == 2 || dir == 4 ) ) return 0; //if the right column
if ( combooffsets[11] && ( dir == 1 || dir == 2 || dir == 0 || dir == 0 ) ) return 0; //if the top row
if ( combooffsets[12] && ( dir == 5 || dir == 4 || dir == 6 ) ) return 0; //if the bottom row
else if ( cmb >= 0 && cmb <= 176 ) return cmb + combooffsets[dir];
else return -1;
}
//Xor comparison of two boolean values.
bool ____Xor(bool valA, bool valB){
if ( !valA && valB ) return true;
else if ( valA && !valB ) return true;
return false;
}
}