//============================================================================= // stdExtra.zh version 3.5 // Latest update: // * Added SCREEN_COMBOS // * Removed equipItemA/B() // * Removed SelectPressInput() and SetInput // * Added RadianVectorX/Y by Mero // * Removed cw() and ccw() and added rotateDir() by Mero //============================================================================= //Pre-requisites //import "std.zh" //import "ffcscript.zh" //import "string.zh" //==== * Quest-Specific Settings * ============================================ //Text colors const int COLOR_WHITE = 1; const int COLOR_BLACK = 15; //Combos const int CMB_BLANK = 0; //Leave this be - combo 0 should always be invisible and no properties const int CMB_FREEZEALL = 844; //Combo with "Freeze all (Except FFCs)" type const int CMB_FREEZEFFC = 845; //Combo with "Freeze all (FFCs only)" type //FFC Slots const int FFC_FREEZEALL = 31; const int FFC_FREEZEFFC = 32; //Other Settings const int MAX_ITEMS = 255; //Set to the highest item ID you use to make GetCurrentItem() more efficient //============================================================================= //Screen Dimensions const int SCREEN_WIDTH = 256; const int SCREEN_HEIGHT = 176; const int SCREEN_VISIBLEHEIGHT = 168; //The height of the screen you can see (bottom 8 pixels cut off) const int SCREEN_SSTOP = -64; //The top of the subscreen const int SCREEN_COMBOS = 176; // Number of combos on screen //Sprite Flips and rotations const int FLIP_NO = 0; //Not flipped const int FLIP_H = 1; //Horizontal const int FLIP_V = 2; //Vertical const int FLIP_B = 3; //Vertical & Horizontal const int ROTATE_CW = 4; //Clockwise const int ROTATE_CCW = 7; //Counter-clockwise const int ROTATE_CW_FLIP = 5; const int ROTATE_CCW_FLIP = 6; //=============================================================================== //Reusable scripts //=============================================================================== //Run an FFC script //D0-D6: Arguments for the FFC Script //D7: The script number to run item script ffcItem{ void run(int d0, int d1, int d2, int d3, int d4, int d5, int d6, int ffc_id){ int d[7] = {d0,d1,d2,d3,d4,d5,d6}; if(FindFFCRunning(ffc_id)<=0){ RunFFCScriptOrQuit(ffc_id, d); } } } //=============================================================================== //Position and movement //=============================================================================== //Prevents moving in any direction void NoMovement(){ Link->InputUp = false; Link->PressUp = false; Link->InputDown = false; Link->PressDown = false; Link->InputLeft = false; Link->PressLeft = false; Link->InputRight = false; Link->PressRight = false; } //Converts velocity into a direction. int VelocityToDir(float x, float y){ if(x == 0 && y == 0) return -1; return RadianAngleDir8(RadianAngle(0, x, 0, y)); } int VelocityToDir4(float x, float y){ if(x == 0 && y == 0) return -1; return RadianAngleDir4(RadianAngle(0, x, 0, y)); } //Converts the x component of a velocity into a direction. int XSpeedToDir(float x){ return VelocityToDir(x, 0); } //Converts the y component of a velocity into a direction. int YSpeedToDir(float y){ return VelocityToDir(0, y); } //Takes a direction of movement and gets the xspeed. float DirToXSpeed(int dir){ if(dir<4) return Cond((dir*2)-5 < -1, 0, (dir*2)-5); else return Cond(dir<6, -1.5, 1.5); } //Takes a direction of movement and gets the yspeed. float DirToYSpeed(int dir){ if(dir > 7) dir = toggleBlock(dir); if(dir < 2) return 0; float ret = 1; if(dir >= 4) ret += .5; return Cond(IsEven(dir), -ret, ret); } //Returns the angle in radians of a direction; used for weapon angles float dirToRad(int dir){ if ( dir == DIR_UP ) return 1.5 * PI; if ( dir == DIR_DOWN ) return .5 * PI; if ( dir == DIR_LEFT ) return PI; if ( dir == DIR_RIGHT ) return 0; } int dirToDeg(int dir){ if ( dir == DIR_UP ) return 90; if ( dir == DIR_DOWN ) return 270; if ( dir == DIR_LEFT ) return 180; if ( dir == DIR_RIGHT ) return 0; } // Returns value from 0 to 360 rather than -180 to 180 float AnglePos(int x1, int y1, int x2, int y2){ float angle = ArcTan(x2-x1, y2-y1)*57.2958; if(angle < 0) angle += 360; return angle; } // Returns the X component of a radian angle. float RadianVectorX(float len, float angle) { return len * RadianCos(angle); } // Returns the Y component of a radian angle. float RadianVectorY(float len, float angle) { return len * RadianSin(angle); } //Returns the distance between the given Coordinate and Link's Center. float DistanceLink(float x, float y) { return Distance(CenterLinkX(), CenterLinkY(), x, y); } //Returns the distance between Link's center and an object's center. float DistanceLink(ffc f) { return Distance(CenterLinkX(), CenterLinkY(), CenterX(f), CenterY(f)); } float DistanceLink(npc n) { return Distance(CenterLinkX(), CenterLinkY(), CenterX(n), CenterY(n)); } float DistanceLink(lweapon l) { return Distance(CenterLinkX(), CenterLinkY(), CenterX(l), CenterY(l)); } float DistanceLink(eweapon e) { return Distance(CenterLinkX(), CenterLinkY(), CenterX(e), CenterY(e)); } //Converts 8-way direction to 4-way int dir8ToDir4(int dir) { if(dir != Clamp(dir, 0, 15)) return -1; dir &= 7; if((dir & 4) == 0) return dir; else return Cond(IsEven(dir), DIR_UP, DIR_DOWN); } //Returns the reverse of the given direction. int reverseDir(int dir) { if(dir != Clamp(dir, 0, 15)) return -1; //Invalid direction return Cond(dir<8, OppositeDir(dir), ((dir+4)%8)+8); } // Rotates a given direction int rotateDir(int dir, bool ccw, bool eightway) { if(dir==DIR_UP) dir = 0; else if(dir==DIR_RIGHTUP) dir = 1; else if(dir==DIR_RIGHT) dir = 2; else if(dir==DIR_RIGHTDOWN) dir = 3; else if(dir==DIR_DOWN) dir = 4; else if(dir==DIR_LEFTDOWN) dir = 5; else if(dir==DIR_LEFT) dir = 6; else if(dir==DIR_LEFTUP) dir = 7; else return -1; //Invalid Direction; if(eightway) dir=(dir+Cond(ccw, 7,1))%8; else dir=(dir+Cond(ccw,6,2))%8; //Will never be odd. And needs to wrap by 8 regardless. if(dir==0) return DIR_UP; else if(dir==1) return DIR_RIGHTUP; else if(dir==2) return DIR_RIGHT; else if(dir==3) return DIR_RIGHTDOWN; else if(dir==4) return DIR_DOWN; else if(dir==5) return DIR_LEFTDOWN; else if(dir==6) return DIR_LEFT; else if(dir==7) return DIR_LEFTUP; } //Move the specified object a set distance in a set direction //Walkable: Don't move onto a solid space //PreventOffScreen: Don't move off screen bool moveLink ( int dir, int dist, bool walkable, bool preventOffScreen ) { //Can't move if ( walkable && !CanWalk(Link->X, Link->Y, dir, dist, false) ) return false; //Otherwise, check direction if ( dir == DIR_UP && (!preventOffScreen || Link->Y - dist > 0) ) Link->Y -= dist; else if ( dir == DIR_DOWN && (!preventOffScreen || Link->Y + dist < SCREEN_HEIGHT) ) Link->Y += dist; else if ( dir == DIR_LEFT && (!preventOffScreen || Link->X - dist > 0)) Link->X -= dist; else if ( dir == DIR_RIGHT && (!preventOffScreen || Link->X + dist < SCREEN_WIDTH) ) Link->X += dist; else return false; return true; } bool move ( ffc this, int dir, int dist, bool walkable, bool preventOffScreen ) { //Can't move if ( walkable && !CanWalk(this->X, this->Y, dir, dist, false) ) return false; //Otherwise, check direction if ( dir == DIR_UP && (!preventOffScreen || this->Y - dist > 0) ) this->Y -= dist; else if ( dir == DIR_DOWN && (!preventOffScreen || this->Y + dist < SCREEN_HEIGHT) ) this->Y += dist; else if ( dir == DIR_LEFT && (!preventOffScreen || this->X - dist > 0)) this->X -= dist; else if ( dir == DIR_RIGHT && (!preventOffScreen || this->X + dist < SCREEN_WIDTH) ) this->X += dist; else return false; return true; } bool move ( npc enem, int dir, int dist, bool walkable, bool preventOffScreen ) { //Can't move if ( walkable && !CanWalk(enem->X, enem->Y, dir, dist, false) ) return false; //Otherwise, check direction if ( dir == DIR_UP && (!preventOffScreen || enem->Y - dist > 0) ) enem->Y -= dist; else if ( dir == DIR_DOWN && (!preventOffScreen || enem->Y + dist < SCREEN_HEIGHT) ) enem->Y += dist; else if ( dir == DIR_LEFT && (!preventOffScreen || enem->X - dist > 0)) enem->X -= dist; else if ( dir == DIR_RIGHT && (!preventOffScreen || enem->X + dist < SCREEN_WIDTH) ) enem->X += dist; else return false; return true; } bool move ( lweapon weap, int dir, int dist, bool walkable, bool preventOffScreen ) { //Can't move if ( walkable && !CanWalk(weap->X, weap->Y, dir, dist, false) ) return false; //Otherwise, check direction if ( dir == DIR_UP && (!preventOffScreen || weap->Y - dist > 0) ) weap->Y -= dist; else if ( dir == DIR_DOWN && (!preventOffScreen || weap->Y + dist < SCREEN_HEIGHT) ) weap->Y += dist; else if ( dir == DIR_LEFT && (!preventOffScreen || weap->X - dist > 0)) weap->X -= dist; else if ( dir == DIR_RIGHT && (!preventOffScreen || weap->X + dist < SCREEN_WIDTH) ) weap->X += dist; else return false; return true; } bool move ( eweapon weap, int dir, int dist, bool walkable, bool preventOffScreen ) { //Can't move if ( walkable && !CanWalk(weap->X, weap->Y, dir, dist, false) ) return false; //Otherwise, check direction if ( dir == DIR_UP && (!preventOffScreen || weap->Y - dist > 0) ) weap->Y -= dist; else if ( dir == DIR_DOWN && (!preventOffScreen || weap->Y + dist < SCREEN_HEIGHT) ) weap->Y += dist; else if ( dir == DIR_LEFT && (!preventOffScreen || weap->X - dist > 0)) weap->X -= dist; else if ( dir == DIR_RIGHT && (!preventOffScreen || weap->X + dist < SCREEN_WIDTH) ) weap->X += dist; else return false; return true; } bool move ( item theItem, int dir, int dist, bool walkable, bool preventOffScreen ) { //Can't move if ( walkable && !CanWalk(theItem->X, theItem->Y, dir, dist, false) ) return false; //Otherwise, check direction if ( dir == DIR_UP && (!preventOffScreen || theItem->Y - dist > 0) ) theItem->Y -= dist; else if ( dir == DIR_DOWN && (!preventOffScreen || theItem->Y + dist < SCREEN_HEIGHT) ) theItem->Y += dist; else if ( dir == DIR_LEFT && (!preventOffScreen || theItem->X - dist > 0)) theItem->X -= dist; else if ( dir == DIR_RIGHT && (!preventOffScreen || theItem->X + dist < SCREEN_WIDTH) ) theItem->X += dist; else return false; return true; } //=============================================================================== //Items and Equipment //=============================================================================== //Returns true if Link is pressing the button for an item bool pressingItem(int id) { return ( (GetEquipmentA()==id && Link->PressA) ||(GetEquipmentB()==id && Link->PressB) ); } //Returns the id of the highest level item of the given class that Link has acquired. //Unlike GetHighestLevelItem(), only applies to items Link owns int GetCurrentItem(int itemclass) { itemdata id; int ret = -1; int curlevel = -1000; for(int i = 0; i < MAX_ITEMS; i++){ if(!Link->Item[i]) continue; id = Game->LoadItemData(i); if(id->Family != itemclass) continue; if(id->Level > curlevel){ curlevel = id->Level; ret = i; } } return ret; } //Gives the specified item with hold up animation and optional fanfare //keep: Whether to actually give the item //twoHand: Use 1 or 2 hand animation //sfx: Whether to play item fanfare void holdUpItem(int id, bool keep, bool twoHand, bool sfx) { if ( sfx ) Game->PlaySound(SFX_PICKUP); if ( twoHand ) Link->Action = LA_HOLD2LAND; else Link->Action = LA_HOLD1LAND; Link->HeldItem = id; //Give the item and its counter effects if(keep){ Link->Item[id] = true; itemdata data = Game->LoadItemData(id); //Increase capacity if(data->MaxIncrement > 0 && data->Max > Game->MCounter[data->Counter]){ Game->MCounter[data->Counter] = Min(Game->MCounter[data->Counter]+data->MaxIncrement, data->Max); } //Increase count if(data->Amount > 0) Game->Counter[data->Counter] = Min(Game->Counter[data->Counter]+data->Amount, Game->MCounter[data->Counter]); } } //=============================================================================== //Screen Freezing //=============================================================================== //WARNING: DO NOT USE IN AN FFC SCRIPT OR THE FFC WILL FREEZE ITSELF! //Use in global scripts only. void freezeScreen() { ffc freezeAll = Screen->LoadFFC(FFC_FREEZEALL); freezeAll->Data = CMB_FREEZEALL; ffc freezeFFC = Screen->LoadFFC(FFC_FREEZEFFC); freezeFFC->Data = CMB_FREEZEFFC; } void unfreezeScreen() { ffc freezeAll = Screen->LoadFFC(FFC_FREEZEALL); freezeAll->Data = CMB_BLANK; ffc freezeFFC = Screen->LoadFFC(FFC_FREEZEFFC); freezeFFC->Data = CMB_BLANK; } //=============================================================================== //Weapons //=============================================================================== //Toggles weapon blockability by adjusting its direction int toggleBlock (int dir) { if(dir == DIR_UP) return 8; if(dir == DIR_DOWN) return 12; if(dir == DIR_LEFT) return 14; if(dir == DIR_RIGHT) return 10; if(dir == DIR_LEFTUP) return 15; if(dir == DIR_RIGHTUP) return 9; if(dir == DIR_LEFTDOWN) return 13; if(dir == DIR_RIGHTDOWN) return 11; if(dir==8) return DIR_UP; if(dir==9) return DIR_RIGHTUP; if(dir==10) return DIR_RIGHT; if(dir==11) return DIR_RIGHTDOWN; if(dir==12) return DIR_DOWN; if(dir==13) return DIR_LEFTDOWN; if(dir==14) return DIR_LEFT; if(dir==15) return DIR_LEFTUP; return dir; } //Get the correct flip for a 4-dir weapon based on its direction int getFlip(int dir) { if ( dir == DIR_UP ) return FLIP_NO; if ( dir == DIR_DOWN ) return FLIP_B; if ( dir == DIR_LEFT ) return ROTATE_CCW; if ( dir == DIR_RIGHT ) return ROTATE_CW; return -1; } //Sets the flip for a 4-dir weapon based on a single sprite void setFlip ( lweapon weapon ) { int flip = getFlip(weapon->Dir); if(flip >= 0) weapon->Flip = flip; } void setFlip ( eweapon weapon ){ int flip = getFlip(weapon->Dir); if(flip >= 0) weapon->Flip = flip; } //Sets flip for a 4-dir sword based on two sprites (up and right) void setFlipSword ( lweapon weapon ){ if ( weapon->Dir >= DIR_LEFT ) weapon->Tile++; if ( weapon->Dir == DIR_DOWN ) weapon->Flip = FLIP_B; else if ( weapon->Dir == DIR_LEFT ) weapon->Flip = FLIP_H; } void setFlipSword ( eweapon weapon ){ if ( weapon->Dir >= DIR_LEFT ) weapon->Tile++; if ( weapon->Dir == DIR_DOWN ) weapon->Flip = FLIP_B; else if ( weapon->Dir == DIR_LEFT ) weapon->Flip = FLIP_H; } //=============================================================================== //Other //=============================================================================== void permaSecrets(){ Screen->TriggerSecrets(); Screen->State[ST_SECRET] = true; } void tempSecrets(){ Screen->TriggerSecrets(); Screen->State[ST_SECRET] = false; } //Makes secrets permanent if "Secrets are temporary" is not checked void screenSecrets(){ Screen->TriggerSecrets(); if(!(Screen->Flags[SF_SECRETS]&2)){ Screen->State[ST_SECRET] = true; } } bool WaitframeCheckScreenChange(){ int old_dmap_screen = Game->GetCurDMapScreen(); int old_dmap = Game->GetCurDMap(); Waitframe(); return (old_dmap!=Game->GetCurDMap() || old_dmap_screen!=Game->GetCurDMapScreen()); } bool WaitframeCheckWarp(){ return ( WaitframeCheckScreenChange() && !(Link->Action==LA_SCROLLING)); } //Draw an inverted circle (fill whole screen except circle) void InvertedCircle(int bitmapID, int layer, int x, int y, int radius, int scale, int fillcolor){ Screen->SetRenderTarget(bitmapID); //Set the render target to the bitmap. Screen->Rectangle(layer, 0, 0, 256, 176, fillcolor, 1, 0, 0, 0, true, 128); //Cover the screen Screen->Circle(layer, x, y, radius, 0, scale, 0, 0, 0, true, 128); //Draw a transparent circle. Screen->SetRenderTarget(RT_SCREEN); //Set the render target back to the screen. Screen->DrawBitmap(layer, bitmapID, 0, 0, 256, 176, 0, 56, 256, 176, 0, true); //Draw the bitmap } bool LinkOnComboType(int type){ return(Screen->ComboT[ComboAt(CenterLinkX(), CenterLinkY())] == type); } bool LinkOnTallGrass(){ return ( LinkOnComboType(CT_TALLGRASS) || LinkOnComboType(CT_TALLGRASSC) || LinkOnComboType(CT_TALLGRASSNEXT) ); } void closingWipe(int wipetime){ for(int i = wipetime; i > 0; i--){ InvertedCircle(4, 6, CenterLinkX(), CenterLinkY(), Floor(300/wipetime)*i, 1, 15); WaitNoAction(); } } void openingWipe(int wipetime){ for(int i = 0; i < wipetime; i++){ InvertedCircle(4, 6, CenterLinkX(), CenterLinkY(), Floor(300/wipetime)*i, 1, 15); WaitNoAction(); } } //Get the color value given CSet and in-CSet color int color(int cset, int csetColor){ return (cset*16) + csetColor; } //Simulates a 2D array //Returns the index of an array given row, column, and number of rows int arr2D ( int row, int col, int numCols ){ return (row * numCols) + col; } //Extracted this method from ffcscript.zh for common usage //If force is true, takes over the last FFC even if it's used ffc loadUnusedFFC(bool force){ ffc theFFC; for(int i = FFCS_MIN_FFC; i <= FFCS_MAX_FFC; i++){ theFFC=Screen->LoadFFC(i); //Check each FFC if ( ffcIsBlank(theFFC) ) return theFFC; //Return it if ( force && i == FFCS_MAX_FFC ) //Force last FFC return theFFC; } //No FFC found; return an invalid one ffc invalidFFC; return invalidFFC; } //Tells whether an FFC is blank and unused bool ffcIsBlank(ffc this){ return ( this->Script == 0 && //If not running a script ( this->Data == 0 || this->Data == FFCS_INVISIBLE_COMBO)); //And blank combo } //Draws the given time in frames as minutes and seconds //Taken from "Hot Rooms" by Zecora void drawTime(int layer, int x, int y, int frames){ drawTime(layer, x, y, FONT_S, COLOR_WHITE, COLOR_BLACK, TF_NORMAL, frames); } void drawTime(int layer, int x, int y, int font, int color, int bgcolor, int format, int frames){ int seconds = Div(frames, 60); //Total seconds int minutes = Div(seconds, 60); //Total minutes seconds %= 60; //Remaining seconds (0 - 59) int string[5]; //Create an array of characters. itoa(string, 0, minutes); //Add the minutes to the array. string[strlen(string)] = ':'; //Add the : after the minutes. if(seconds < 10) //Single-digit seconds: add '0' before count string[strlen(string)] = '0'; itoa(string, strlen(string), seconds); Screen->DrawString(layer, x, y, font, color, bgcolor, format, string, 128); } // Has a 1 in n chance of returning true bool nChance(int chance) { return Rand(chance) == 0; } // Given integer n, has an n% chance of returning true. bool percentChance(int chance) { return Rand(100) < chance; } //=============================================================================== //Debug //=============================================================================== //Shortcut for drawInteger for debug output //Each debug item should have a unique "num" (match between value and label) void debugValue ( int num, float value ){ debugValue(num, value, 0); } void debugValue ( int num, float value, int places ){ places = Clamp(places, 0, 4); Screen->DrawInteger(6, 100, 2+10*num, FONT_S, COLOR_WHITE, COLOR_BLACK, -1, -1, value, places, OP_OPAQUE); } void debugValue( int num, bool value){ int trueString[] = "true"; int falseString[] = "false"; Screen->DrawString(6, 100, 2+10*num, FONT_S, COLOR_WHITE, COLOR_BLACK, TF_NORMAL, Cond(value, trueString, falseString), OP_OPAQUE); } void debugLabel ( int num, int string ){ Screen->DrawString(6, 2, 2+10*num, FONT_S, COLOR_WHITE, COLOR_BLACK, TF_NORMAL, string, OP_OPAQUE); } //Both functions in one call, matching "num" void debug ( int num, int string, float value ){ debugLabel(num, string); debugValue(num, value, 0); } void debug ( int num, int string, float value, int places ){ debugLabel(num, string); debugValue(num, value, places); } void debug ( int num, int string, bool value ){ debugLabel(num, string); debugValue(num, value); }