namespace tempPatches { const int BASE_LINK_Z = 1; } // ghost.zh // Version 2.8.0 const int SCRIPT_BARRIERS = 7; const int BARRIER_A_RAISED = 4656; const int BARRIER_B_RAISED = 4657; const int BARRIER_A_LOWERED = 4658; const int BARRIER_B_LOWERED = 4659; const int BARRIER_A_ANIMRAISE = 4660; const int BARRIER_B_ANIMRAISE = 4661; const int BARRIER_A_ANIMLOWER = 4662; const int BARRIER_B_ANIMLOWER = 4663; const int BARRIER_A_WALKABLE = 4664; const int BARRIER_B_WALKABLE = 4665; const int BARRIER_A_SWITCH = 4666; const int BARRIER_B_SWITCH = 4667; const int BARRIER_SWITCH_DUMMY = 177; const int BARRIER_SWITCH_DUMMY_HP = 32767; const int BIG_LINK = 0; // Pegasus Boots Constants const int CF_DASH = 102; const int INV_COMBO_ID = 4669; const int DashSFXLength = 9; const int SFX_DASH = 79; const int SFX_HITWALL = 3; const int SFX_PEGASUSBREAK = 74; const int T_DUST = 660; const int DustAFrames = 4; const int DustASpeed = 4; const int DustCSet = 8; const int I_ROCSFEATHER_INCREASER = 124; const int DASH_SWORD_DMG = 8; const int DASH_SWORD_MULT = 2; const int SPRITE_SWORD1 = 0; const int SPRITE_SWORD2 = 1; const int SPRITE_SWORD3 = 2; const int SPRITE_SWORD4 = 3; const int NPC_ITEMSET = 178; const int SPRITE_BUSH_CUT = 54; const int SPRITE_FLOWER_CUT = 54; const int SPRITE_GRASS_CUT = 54; const int CanDrown = 1; bool barriers[511]; // false = blue barriers raised, true = red barriers raised // to draw while Scrolling int scrollDir; int scrollCounter; int drawX; int drawY; //Pegasus Boots Global variables int PegasusDash = -1; bool PegasusCollision; int DashCounter; int StoreInput; int StoreHP; int dashSwordTile = 0; // for drawing dashsword while scrolling, script sets these. int dashSwordCSet = 0; //!!!!!!!!!!!!! this array size needs to be set, the number inside the [] int Dust_Array[2]; // Array size should be max number of dust animations active at one time // Formula is ( (DustAFrames*DustASpeed) / 4 ) //END Pegasus Boots global variables // Function that makes preparations for barriers on each screen and starts an FFC script void Barriers_NewScreen() { // Search for a barrier-related combo for (int i = 0; i <= 175; ++i) { int cd = Screen->ComboD[i]; if (cd == BARRIER_A_RAISED || cd == BARRIER_A_LOWERED || cd == BARRIER_A_SWITCH || cd == BARRIER_B_RAISED || cd == BARRIER_B_LOWERED || cd == BARRIER_B_SWITCH) { // A barrier-related combo was found // Make initial changes to combos if (barriers[Game->GetCurDMap()]) { for (int j = i; j <= 175; ++j) { int cd = Screen->ComboD[j]; if (cd == BARRIER_A_RAISED) Screen->ComboD[j] = BARRIER_A_LOWERED; else if (cd == BARRIER_B_LOWERED) Screen->ComboD[j] = BARRIER_B_RAISED; else if (cd == BARRIER_A_SWITCH) Screen->ComboD[j] = BARRIER_B_SWITCH;}} else { for (int j = i; j <= 175; j++) { int cd = Screen->ComboD[j]; if (cd == BARRIER_B_RAISED) Screen->ComboD[j] = BARRIER_B_LOWERED; else if (cd == BARRIER_A_LOWERED) Screen->ComboD[j] = BARRIER_A_RAISED; else if (cd == BARRIER_B_SWITCH) Screen->ComboD[j] = BARRIER_A_SWITCH;}} // So run FFCscript to control barriers int args[] = {0,0,0,0,0,0,0,0}; RunFFCScript(SCRIPT_BARRIERS, args); break;} }} // This lets you toggle barriers on any dmap bool ToggleBarriers(int dmap) { if (dmap == Game->GetCurDMap()) ToggleBarriers(); else barriers[dmap] = !barriers[dmap]; return barriers[dmap];} // This toggles barriers on the current dmap bool ToggleBarriers() { int curdmap = Game->GetCurDMap(); if (!barriers[curdmap]) { barriers[curdmap] = true; for (int i = 0; i <= 175; i++) { int cd = Screen->ComboD[i]; if (cd == BARRIER_A_RAISED || cd == BARRIER_A_WALKABLE || cd == BARRIER_A_ANIMRAISE) { Screen->ComboD[i] = BARRIER_A_ANIMLOWER;} else if (cd == BARRIER_B_LOWERED || cd == BARRIER_B_ANIMLOWER) { Screen->ComboD[i] = BARRIER_B_ANIMRAISE;} else if (cd == BARRIER_A_SWITCH) {Screen->ComboD[i] = BARRIER_B_SWITCH;}}} else { barriers[curdmap] = false; for (int i = 0; i <= 175; i++) { int cd = Screen->ComboD[i]; if (cd == BARRIER_B_RAISED || cd == BARRIER_B_WALKABLE || cd == BARRIER_B_ANIMRAISE) { Screen->ComboD[i] = BARRIER_B_ANIMLOWER;} else if (cd == BARRIER_A_LOWERED || cd == BARRIER_A_ANIMLOWER) { Screen->ComboD[i] = BARRIER_A_ANIMRAISE;} else if (cd == BARRIER_B_SWITCH) {Screen->ComboD[i] = BARRIER_A_SWITCH;}}} return barriers[curdmap]; } ffc script Barriers { void run() { // Initialize storage for bswitch hit dummies int bswitch_count; npc bswitch[8]; for (int i = 0; i <= 175; i++) { if (Screen->ComboD[i] == BARRIER_A_SWITCH || Screen->ComboD[i] == BARRIER_B_SWITCH) { npc bs = CreateNPCAt(BARRIER_SWITCH_DUMMY, ComboX(i), ComboY(i)); bs->HitWidth = 8; // Smaller hit box to avoid annoying collisions with Link bs->HitHeight = 8; bs->HP = BARRIER_SWITCH_DUMMY_HP; bswitch[bswitch_count++] = bs;}} // Change raised barriers to walkable ones if Link enters screen on a raised barrier int lcombo = LinkOnComboD(); bool onbarrier = (lcombo == BARRIER_A_RAISED || lcombo == BARRIER_B_RAISED); if (onbarrier) for (int i = 0; i < 176; i++) { if (Screen->ComboD[i] == BARRIER_A_RAISED) {Screen->ComboD[i] = BARRIER_A_WALKABLE;} else if (Screen->ComboD[i] == BARRIER_B_RAISED) {Screen->ComboD[i] = BARRIER_B_WALKABLE;}} while (true) { // Detect hits on bswitches, and change combos accordingly for (int j = 0; j < bswitch_count; j++) { if (bswitch[j]->HP < BARRIER_SWITCH_DUMMY_HP) { bswitch[j]->HP = BARRIER_SWITCH_DUMMY_HP; ToggleBarriers(); break;}} //break so that only one bswitch hit may register per frame // Make barriers walkable if Link is on raised barriers, or unwalkable if not lcombo = LinkOnComboD(); if (!onbarrier && (lcombo == BARRIER_A_RAISED || lcombo == BARRIER_B_RAISED)) { onbarrier = true; for (int i = 0; i <= 175; i++) { if (Screen->ComboD[i] == BARRIER_A_RAISED) {Screen->ComboD[i] = BARRIER_A_WALKABLE;} else if (Screen->ComboD[i] == BARRIER_B_RAISED) {Screen->ComboD[i] = BARRIER_B_WALKABLE;}}} else if (onbarrier && !(lcombo == BARRIER_A_WALKABLE || lcombo == BARRIER_B_WALKABLE)) { onbarrier = false; for (int i = 0; i <= 175; i++) { if (Screen->ComboD[i] == BARRIER_A_WALKABLE) {Screen->ComboD[i] = BARRIER_A_RAISED;} else if (Screen->ComboD[i] == BARRIER_B_WALKABLE) {Screen->ComboD[i] = BARRIER_B_RAISED;}}} Waitframe();} }} item script PegasusBoots{ void run(){ if(ComboFI(Link->X+8,Link->Y + Cond(BIG_LINK==0, 12, 8),CF_DASH)) Quit(); // can't dash on non-solid dash combos if(CanDrown == 0 && IsWater(TouchedComboLoc()) ) Quit(); // stop beside non-drownable water if(Link->Action == LA_SWIMMING || Link->Action == LA_DROWNING) Quit(); // can't use dash when in water if(IsJumping()) Quit(); // can't initiate dash while jumping // Link won't dash if facing up or down in sideview if(IsSideview()){ if(Link->Dir == DIR_UP || Link->Dir == DIR_DOWN) Quit(); } PegasusDash = Link->Dir; if(Link->InputB && !Link->InputA) StoreInput = 2; else if(Link->InputA && !Link->InputB) StoreInput = 1; } } // Stops dashing, no collision. Easy for other scripts to call. void StopDash(){ PegasusDash = -1; DashCounter = 0; } // takes care of all the PegasusBoots functions // call it in your global while loop, above the waitdraw and waitframe. void PegasusBoots(){ lweapon mysword; int loc = ComboAt(Link->X+8,Link->Y + Cond(BIG_LINK==0, 12, 8)); // this fixes the bug that makes Link dive immediately after dashing into water with flippers if(DashCounter < 0){ DashCounter++; if(Link->Action == LA_SWIMMING){ Link->InputA = false; }else if(Link->Action == LA_DIVING){ Link->Action == LA_SWIMMING; Link->InputA = false; }else{ DashCounter = 0; } } // fixs a bug with drownable water and diagonal movement off if(CanDrown == 1 && IsWater(loc) && Link->Action != LA_SWIMMING && Link->Action != LA_DROWNING && Link->Action != LA_DIVING){ if(Link->Dir == DIR_UP) Link->InputUp = true; else if(Link->Dir == DIR_DOWN) Link->InputDown = true; else if(Link->Dir == DIR_LEFT) Link->InputLeft = true; else if(Link->Dir == DIR_RIGHT) Link->InputRight = true; } if(PegasusDash >= 0){ if(!PegasusCollision){ if( (Link->Action != LA_SCROLLING && ((StoreInput == 1 && !Link->InputA) || (StoreInput == 2 && !Link->InputB))) || StoreHP > Link->HP || Link->Action == LA_RAFTING || Link->Action == LA_DROWNING || Link->Action == LA_SWIMMING || Link->Action == LA_DIVING || (CanDrown == 0 && IsWater(TouchedComboLoc())) || (CanDrown == 1 && IsWater(loc)) ){ StopDash(); if(Link->Action == LA_SWIMMING) DashCounter = -20; } if(!UsingItem(I_ROCSFEATHER_INCREASER)) NoAction(); if(Link->Item[I_ROCSFEATHER] && !Link->Item[I_ROCSFEATHER_INCREASER]){ Link->Item[I_ROCSFEATHER_INCREASER]=true; } // Link runs on spot for 10 frames before dash happens if(Link->Action != LA_SCROLLING && DashCounter > 10){ if(PegasusDash == DIR_UP && DashCheck(Link->X+8,Link->Y+6,true) != 2){ Link->Y--; drawY--; Link->InputUp = true; } else if(PegasusDash == DIR_DOWN && DashCheck(Link->X+8,Link->Y+17,true) != 2){ Link->Y++; drawY++; Link->InputDown = true; } else if(PegasusDash == DIR_LEFT && DashCheck(Link->X,Link->Y+12,false) != 2){ Link->X--; drawX--; Link->InputLeft = true; } else if(PegasusDash == DIR_RIGHT && DashCheck(Link->X+16,Link->Y+12,false) != 2){ Link->X++; drawX++; Link->InputRight = true; } else{ PegasusCollision = true; DashCounter = 0; } }//end DashCounter if // do stuff if not jumping if( !IsJumping() ){ // handles breaking of solid Pegasus combos if(ComboFI(loc,CF_DASH) && Screen->ComboS[loc] == 1111b){ Screen->ComboD[loc]=INV_COMBO_ID; if(Screen->ComboF[loc] == CF_DASH) Screen->ComboF[loc] = 0; Game->PlaySound(SFX_PEGASUSBREAK); } DustDrawTile(); // use new dust draw code that works while scrolling // DustDrawLW(); // use old dust draw code that doesn't work while scrolling (quest rule "no screen scrolling" on) if(DashCounter%DashSFXLength == 0) Game->PlaySound(SFX_DASH); } // draws dashSword when scrolling if(Link->Action == LA_SCROLLING && dashSwordTile > 0){ if(PegasusDash == DIR_UP) Screen->DrawTile(0,drawX+InFrontX(DIR_UP,6),drawY+InFrontY(DIR_UP,6), dashSwordTile,1,1,dashSwordCSet,-1,-1,0,0,0,0,true,128); else if(PegasusDash == DIR_DOWN) Screen->DrawTile(0,drawX+InFrontX(DIR_DOWN,6),drawY+InFrontY(DIR_DOWN,6), dashSwordTile,1,1,dashSwordCSet,-1,-1,0,0,0,3,true,128); else if(PegasusDash == DIR_LEFT) Screen->DrawTile(0,drawX+InFrontX(DIR_LEFT,6),drawY+InFrontY(DIR_LEFT,6)+3, dashSwordTile,1,1,dashSwordCSet,-1,-1,0,0,0,1,true,128); else if(PegasusDash == DIR_RIGHT) Screen->DrawTile(0,drawX+InFrontX(DIR_RIGHT,6),drawY+InFrontY(DIR_RIGHT,6)+3, dashSwordTile,1,1,dashSwordCSet,-1,-1,0,0,0,0,true,128); } int swordEquip = LinkSwordEquip(); if(swordEquip > -1 && DashCounter>10){ // draws dashSword when not scrolling mysword = LoadLWeaponOf(LW_SCRIPT3); if(!mysword->isValid()) mysword = Screen->CreateLWeapon(LW_SCRIPT3); if(swordEquip==I_SWORD4){ mysword->UseSprite(SPRITE_SWORD4); }else if(swordEquip==I_SWORD3){ mysword->UseSprite(SPRITE_SWORD3); }else if(swordEquip==I_SWORD2){ mysword->UseSprite(SPRITE_SWORD2); }else{ mysword->UseSprite(SPRITE_SWORD1); }//end sprite if if(DASH_SWORD_MULT > 0){ itemdata itm = Game->LoadItemData(swordEquip); mysword->Damage = itm->Power * DASH_SWORD_MULT; } else mysword->Damage = DASH_SWORD_DMG; mysword->X = Link->X+InFrontX(PegasusDash, 6); mysword->Y = Link->Y+InFrontY(PegasusDash, 6); mysword->Dir = PegasusDash; if(mysword->Dir < 2){ //dir is up or down if(mysword->Dir == DIR_DOWN) mysword->Flip = 3; } else{ //dir is left or right mysword->OriginalTile += 1; mysword->Tile = mysword->OriginalTile; if(mysword->Dir == DIR_LEFT) mysword->Flip = 1; mysword->Y += 3; }//end dir if // save our sword sprite for when scrolling dashSwordTile = mysword->Tile; dashSwordCSet = mysword->OriginalCSet; // slash stuff while dashing with sword DoDashSlash(ComboAt(TouchedX()+InFrontX(mysword->Dir,4),TouchedY()+InFrontY(mysword->Dir,4))); } else{ // we don't have dashSword mysword = LoadLWeaponOf(LW_SCRIPT3); Remove(mysword); dashSwordTile = 0; dashSwordCSet = 0; }//end mysword if }//end !PegasusCollision if else{ //is PegasusCollision, DashCounter has been reset to 0. NoAction(); // knockback code if(PegasusDash == DIR_UP && DashCheck(Link->X+8,Link->Y+18,true) == 0) Link->Y++; else if(PegasusDash == DIR_DOWN && DashCheck(Link->X+8,Link->Y+6,true) == 0) Link->Y--; else if(PegasusDash == DIR_LEFT && DashCheck(Link->X+18,Link->Y+8,false) == 0) Link->X++; else if(PegasusDash == DIR_RIGHT && DashCheck(Link->X-2,Link->Y+8,false) == 0) Link->X--; if(DashCounter == 1){ Screen->ClearSprites(SL_LWPNS); Game->PlaySound(SFX_HITWALL); Screen->Quake = 10; } else if(DashCounter == 10){ StopDash(); PegasusCollision = false; } if(DashCounter < 5) Link->Z++; }// end PegasusCollision if/else DashCounter++; }// end PegasusDash if else{ // !PegasusDash mysword = LoadLWeaponOf(LW_SCRIPT3); Remove(mysword); dashSwordTile = 0; dashSwordCSet = 0; if(Link->Item[I_ROCSFEATHER_INCREASER]) Link->Item[I_ROCSFEATHER_INCREASER]=false; }//end PegasusDash if/else StoreHP = Link->HP; }//end function // next two functions draw dust when dashing. first one allows drawing during scrolling. the second is the old code, and currently unused. void DustDrawTile(){ if(PegasusDash < 0) return; int dX; int dY; if(DashCounter%4 == 0){ for(int i = 0; i < SizeOfArray(Dust_Array); i++){ if(Dust_Array[i] == 0){ Dust_Array[i] = 1; break; } } } for(int i = 0; i < SizeOfArray(Dust_Array); i++){ if(Dust_Array[i]==0) continue; if(PegasusDash == DIR_UP){ dX = drawX; if(Dust_Array[i] < 4) dY = drawY+8; else dY = drawY+8+3+(Dust_Array[i]-1); }else if(PegasusDash == DIR_DOWN){ dX = drawX; if(Dust_Array[i] < 4) dY = drawY; else dY = drawY-3-(Dust_Array[i]-1); }else if(PegasusDash == DIR_LEFT){ if(Dust_Array[i] < 4) dX = drawX; else dX = drawX+3+(Dust_Array[i]-1); dY = drawY+8; }else if(PegasusDash == DIR_RIGHT){ if(Dust_Array[i] < 4) dX = drawX; else dX = drawX-3-(Dust_Array[i]-1); dY = drawY+8; } Screen->FastTile(1, dX, dY, T_DUST+ Floor(Dust_Array[i]/DustASpeed), DustCSet, 128); if(Dust_Array[i] == DustAFrames*DustASpeed) Dust_Array[i] = 0; else Dust_Array[i]++; } } void DustDrawLW(){ if(PegasusDash < 0) return; lweapon Dust; if(DashCounter%4 == 0){ for(int j=1;j<=Screen->NumLWeapons();j++){ Dust = Screen->LoadLWeapon(j); if(Dust->ID != LW_SCRIPT10) continue; if(PegasusDash < 1) Dust->Y-=3; else if(PegasusDash < 2) Dust->Y+=3; else if(PegasusDash < 3) Dust->X-=3; else if(PegasusDash < 4) Dust->X+=3; }//end for loop Dust = Screen->CreateLWeapon(LW_SCRIPT10); Dust->OriginalTile = T_DUST; Dust->CSet = DustCSet; Dust->Y = Link->Y+8; Dust->X = Link->X; Dust->NumFrames = DustAFrames; Dust->ASpeed = DustASpeed; Dust->DeadState = DustAFrames*DustASpeed; }//end dust if } // Check whether Link can dash onto a combo int DashCheck(int x, int y, bool xy){ int xoffset; int yoffset; bool Solid; if(xy) xoffset = 4; else yoffset = 3; if(Screen->isSolid(x-xoffset,y-yoffset) || Screen->isSolid(x+xoffset,y+yoffset)) Solid = true; if(Solid && CanDashSlash(x,y) ) Solid = false; //Not solid and not a Dash combo return 0 if(!Solid && !ComboFI(x-xoffset,y-yoffset,CF_DASH) && !ComboFI(x+xoffset,y+yoffset,CF_DASH)) return 0; //Is solid and is a Dash combo return 1 else if(Solid && (ComboFI(x-xoffset,y-yoffset,CF_DASH) || ComboFI(x+xoffset,y+yoffset,CF_DASH))) return 1; //Is either solid without a Dash combo, or not solid with a Dash combo else return 2; } //fixes a bug where you can't initiate a dash next to a slashable solid combo bool CanDashSlash(int x, int y){ if(LinkSwordEquip() == -1) return false; int loc; loc = ComboAt(x, y); // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // can add more combo types here by adding || Screen->ComboT[loc] == CT_ // CT_ types you might consider CT_SLASH, CT_SLASHNEXT, CT_SLASHNEXTITEM if those are solid in your quest if(Screen->ComboT[loc] == CT_BUSH || Screen->ComboT[loc] == CT_BUSHNEXT ) return true; return false; } // !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! // replaces the code in old Pegasus boots function for dash cutting through slashable combos. // allows easier customization to suit your needs. void DoDashSlash(int loc){ int ctype = Screen->ComboT[loc]; int sprite; int itmset = IS_COMBOS; // item set 12 (tall grass by default) bool playsound = false; // add sprites to certain types if(ctype == CT_BUSH || ctype == CT_BUSHNEXT) sprite = SPRITE_BUSH_CUT; else if(ctype == CT_TALLGRASS || ctype == CT_TALLGRASSNEXT) sprite = SPRITE_GRASS_CUT; else if(ctype == CT_FLOWERS) sprite = SPRITE_FLOWER_CUT; // no sprite is typically associated with Slash type combos, but it could be added. // itemset is changed here to the default for these types. could add more item sets based on type if(ctype == CT_SLASHITEM || ctype == CT_SLASHNEXTITEM) itmset = 10; // default for these combo types //these types default to undercombo if(ctype == CT_BUSH || ctype == CT_TALLGRASS || ctype == CT_FLOWERS || ctype == CT_SLASH || ctype == CT_SLASHITEM){ Screen->ComboD[loc] = Screen->UnderCombo; playsound = true; } //these types default to next combo if(ctype == CT_BUSHNEXT || ctype == CT_TALLGRASSNEXT || ctype == CT_FLOWERS || ctype == CT_SLASHNEXT || ctype == CT_SLASHNEXTITEM){ Screen->ComboD[loc] += 1; playsound = true; } //no functionality added for Continuous type combos. if(playsound){ CreateGraphicAt(sprite,ComboX(loc),ComboY(loc)); Game->PlaySound(SFX_GRASSCUT); // could have different sounds for different types too by modifyng above. ItemSetAt(itmset,loc); }//end playsound if } //returns the item# of an equipped sword. checks and returns A button first, then B button. returns -1 if no sword equipped. int LinkSwordEquip(){ itemdata itm = Game->LoadItemData( GetEquipmentA() ); if(itm->Family==IC_SWORD) return GetEquipmentA(); itm = Game->LoadItemData(GetEquipmentB()); if(itm->Family==IC_SWORD) return GetEquipmentB(); return -1; } // the combo that link is touching in the direction he's facing int TouchedComboLoc(){ int loc; loc = ComboAt( TouchedX(), TouchedY() ); return loc; } // the x coord that link is touching in the direction he's facing int TouchedX(){ int x = 0; if(Link->Dir == DIR_UP || Link->Dir == DIR_DOWN) x = Link->X+8; else if(Link->Dir == DIR_LEFT) x = Link->X-1; else if(Link->Dir == DIR_RIGHT) x = Link->X+17; return x; } // the y coord that link is touching in the direction he's facing int TouchedY(){ int y = 0; if(Link->Dir == DIR_UP) y = Link->Y+7; else if(Link->Dir == DIR_DOWN) y = Link->Y+17; else if(Link->Dir == DIR_LEFT || Link->Dir == DIR_RIGHT) y = Link->Y+8; return y; } // draws a sprite as a non-damaging eweapon int CreateGraphicAt(int sprite, int x, int y){ eweapon e = Screen->CreateEWeapon(EW_SCRIPT1); e->HitXOffset = 500; e->UseSprite(sprite); e->DeadState = Max(1,e->NumFrames*e->ASpeed); e->X = x; e->Y = y; return e->DeadState; } //creates and kills an enemy of type NPC_ITEMSET to fake an itemdropset. void ItemSetAt(int itemset,int loc){ npc e = Screen->CreateNPC(NPC_ITEMSET); e->ItemSet = itemset; if(e->isValid()){ e->X = loc%16*16; e->Y = loc-loc%16; } e->HP = HP_SILENT; } // utility function returns true if Link is jumping, necessary because SideView gravity sucks // might need tweaking depending on other SideView scripts you use. bool IsJumping(){ if(IsSideview()){ if(Link->Action == LA_SWIMMING || Link->Action == LA_DIVING || Link->Action == LA_DROWNING) return false; if(Link->Jump != 0) return true; if(!OnSidePlatform(Link->X,Link->Y)) return true; } else{ if(Link->Z > tempPatches::BASE_LINK_Z) return true; } return false; } // Allows drawing tiles to correct position on Link when scrolling // Sets drawX/drawY either to Link X/Y when not scrolling, or the visual position when scrolling. // currently only used by Pegasus Boots (which is also setting drawX/drawY to Link's new x/y as he dashes) void ScrollFix(){ //function by Saffith if(Link->Action==LA_SCROLLING){ if(scrollDir==-1) { if(Link->Y>160) { scrollDir=DIR_UP; scrollCounter=45; } else if(Link->Y<0) { scrollDir=DIR_DOWN; scrollCounter=45; } else if(Link->X>240) { scrollDir=DIR_LEFT; scrollCounter=65; } else { scrollDir=DIR_RIGHT; scrollCounter=65; } } if(scrollDir==DIR_UP && scrollCounter<45 && scrollCounter>4) drawY+=4; else if(scrollDir==DIR_DOWN && scrollCounter<45 && scrollCounter>4) drawY-=4; else if(scrollDir==DIR_LEFT && scrollCounter<65 && scrollCounter>4) drawX+=4; else if(scrollDir==DIR_RIGHT && scrollCounter<65 && scrollCounter>4) drawX-=4; scrollCounter--; } else { drawX=Link->X; drawY=Link->Y; if(scrollDir!=-1) scrollDir=-1; } } void LREx1Ex2ItemSwitch() { if (Link->PressL && Link->Action != LA_SCROLLING) { Link->SelectBWeapon(DIR_LEFT); } if (Link->PressR && Link->Action != LA_SCROLLING) { Link->SelectBWeapon(DIR_RIGHT); } if (Link->PressEx1 && Link->Action != LA_SCROLLING) { Link->SelectAWeapon(DIR_LEFT); } if (Link->PressEx2 && Link->Action != LA_SCROLLING) { Link->SelectAWeapon(DIR_RIGHT); } } int LinkOnComboD() { return Screen->ComboD[ComboAt(Link->X+8, Link->Y+13)]; } item script GhostZHClockScript { void run(int duration) { if(duration<=0) __ghzhData[__GHI_CLOCK_TIMER]=-1; else __ghzhData[__GHI_CLOCK_TIMER]=duration; } } //Constants used by Ice Combos const int CT_ICECOMBO = 142; //The combo type "Script1 by default" const int ICE_ACCELERATION = 1; const int ICE_DECELERATION = 1; const int ICE_MAXSTEP = 150; //Declare global variables used by Ice Combos. bool isScrolling; bool onice; float Ice_X; float Ice_Y; int Ice_XStep; int Ice_YStep; //End declaration //Active Script //Initializes global variables used for ice combos. void Setup_IceCombos() { isScrolling = false; onice = false; Ice_X = 0; Ice_Y = 0; Ice_XStep = 0; Ice_YStep = 0; } //Adds Ice Combo functionally to CT_ICECOMBO; void Update_IceCombos(int oldscreen) { //Update Variables if(Link->Action != LA_SCROLLING) { if(isScrolling || oldscreen != Game->GetCurScreen() || (!onice && OnIce())) { Ice_X = Link->X; Ice_Y = Link->Y; if(isScrolling) isScrolling = false; else { Ice_XStep = 0; Ice_YStep = 0; } } onice = OnIce(); } else { isScrolling = true; return; } //Ice Physics if(onice) { //Y Adjustment if(Link_Walking() && (Link->InputUp || Link->InputDown)) { if(Link->InputUp && !Link->InputDown) Ice_YStep -= ICE_ACCELERATION; else if(!Link->InputUp && Link->InputDown) Ice_YStep += ICE_ACCELERATION; } else if(Ice_YStep != 0) Ice_YStep = Cond(Abs(Ice_YStep) - ICE_DECELERATION > 0, Ice_YStep - Sign(Ice_YStep)*ICE_DECELERATION, 0); Ice_YStep = Clamp(Ice_YStep, -ICE_MAXSTEP, ICE_MAXSTEP); //X Adjustment if(Link_Walking() && (Link->InputLeft || Link->InputRight)) { if(Link->InputLeft && !Link->InputRight) Ice_XStep -= ICE_ACCELERATION; else if(!Link->InputLeft && Link->InputRight) Ice_XStep += ICE_ACCELERATION; } else if(Ice_XStep != 0) Ice_XStep = Cond(Abs(Ice_XStep) - ICE_DECELERATION > 0, Ice_XStep -Sign(Ice_XStep)*ICE_DECELERATION, 0); Ice_XStep = Clamp(Ice_XStep, -ICE_MAXSTEP, ICE_MAXSTEP); //Reset the Ice Position to Link's Actual Position if he's hurt or hopping out of water. if(Link->Action == LA_GOTHURTLAND || Link->Action == LA_HOPPING) { Ice_X = Link->X; Ice_Y = Link->Y; } //Initialize variables for solidity checking. int newx = (Ice_X + Ice_XStep/100)<<0; int newy = (Ice_Y + Ice_YStep/100)<<0; //Vertical Edge if(newx < Ice_X<<0) { for(int y = Ice_Y+Cond(BIG_LINK, 0, 8); y < (Ice_Y<<0) + 16 && Ice_XStep != 0; y++) { if(Screen->isSolid(newx, y)) Ice_XStep = 0; } } else if(newx > Ice_X<<0) { for(int y = Ice_Y+8; y < (Ice_Y<<0) + 16 && Ice_XStep != 0; y++) { if(Screen->isSolid(newx+15, y)) Ice_XStep = 0; } } //Horizontal Edge if(newy < Ice_Y<<0) { for(int x = Ice_X; x < (Ice_X<<0) + 16 && Ice_YStep != 0; x++) { if(Screen->isSolid(x, newy+Cond(BIG_LINK, 0, 8))) { Ice_YStep = 0; } } } else if(newy > Ice_Y<<0) { for(int x = Ice_X; x < (Ice_X<<0) + 16 && Ice_YStep != 0; x++) { if(Screen->isSolid(x, newy+15)) { Ice_YStep = 0; } } } Ice_X += Ice_XStep/100; Ice_Y += Ice_YStep/100; Link->X = Ice_X; Link->Y = Ice_Y; } else { Ice_XStep = 0; Ice_YStep = 0; } } //Function used to check if Link is over a ice combo. bool OnIce() { if(Link->Z != tempPatches::BASE_LINK_Z) return false; else { int comboLoc = ComboAt(Link->X + 8, Link->Y + 12); if(Screen->ComboT[comboLoc] == CT_ICECOMBO) return true; else if(Screen->LayerScreen(1) != -1 && GetLayerComboT(1, comboLoc) == CT_ICECOMBO) return true; else if(Screen->LayerScreen(2) != -1 && GetLayerComboT(2, comboLoc) == CT_ICECOMBO) return true; else return false; } } //Returns true, if keyboard input is moving Link. bool Link_Walking() { if(UsingItem(I_HAMMER)) return false; else return (Link->Action == LA_WALKING || Link->Action == LA_CHARGING || Link->Action == LA_SPINNING); } int MooshPit[16]; const int MP_LASTX = 0; const int MP_LASTY = 1; const int MP_LASTDMAP = 2; const int MP_LASTSCREEN = 3; const int MP_ENTRYX = 4; const int MP_ENTRYY = 5; const int MP_ENTRYDMAP = 6; const int MP_ENTRYSCREEN = 7; const int MP_FALLX = 8; const int MP_FALLY = 9; const int MP_FALLTIMER = 10; const int MP_FALLSTATE = 11; const int MP_DAMAGETYPE = 12; const int CT_HOLELAVA = 128; //Combo type for pits (No Ground Enemies by default) const int CF_LAVA = 98; //Combo flag marking pits as lava (Script 1 by default) const int SPR_FALLHOLE = 89; //Sprite for Link falling in a hole const int SPR_FALLLAVA = 90; //Sprite for Link falling in lava const int SFX_FALLHOLE = 87; //Sound for falling in a hole const int SFX_FALLLAVA = 13; //Sound for falling in lava const int DAMAGE_FALLHOLE = 8; //How much damage pits deal (1/2 heart default) const int DAMAGE_FALLLAVA = 16; //How much damage lava deals (1 heart default) const int FFC_MOOSHPIT_AUTOWARPA = 32; //FFC that turns into an auto side warp combo when you fall in a pit const int CMB_MOOSHPIT_AUTOWARPA = 6391; //Combo number of an invisible Auto Side Warp A combo const int SF_MISC_MOOSHPITWARP = 2; //Number of the screen flag under the Misc. section that makes pits warp (Script 1 by default) //All pit warps use Side Warp A const int MOOSHPIT_MIN_FALL_TIME = 60; //Minimum time for the pit's fall animation, to prevent repeated falling in pits const int MOOSHPIT_EXTRA_FALL_TIME = 0; //Extra frames at the end of the falling animation before Link respawns //Width and height of Link's hitbox for colliding with pits const int MOOSHPIT_LINKHITBOXWIDTH = 2; const int MOOSHPIT_LINKHITBOXHEIGHT = 2; //Width and height of Link's hitbox for colliding with pits/lava in sideview const int MOOSHPIT_SIDEVIEW_LINKHITBOXWIDTH = 2; const int MOOSHPIT_SIDEVIEW_LINKHITBOXHEIGHT = 2; const int MOOSHPIT_NO_GRID_SNAP = 0; //Set to 1 to prevent Link's falling sprite from snapping to the combo grid. bool MooshPit_OnPit(int LinkX, int LinkY, bool countFFCs){ if(Link->Action==LA_FROZEN) return false; if(countFFCs){ if(MooshPit_OnFFC(LinkX, LinkY)) return false; } bool sideview; if(Screen->Flags[SF_ROOMTYPE]&100b) sideview = true; //wew lad int width = MOOSHPIT_LINKHITBOXWIDTH; int height = MOOSHPIT_LINKHITBOXHEIGHT; for(int x=0; x<=1; x++){ for(int y=0; y<=1; y++){ int X; int Y; if(sideview){ //Hitbox functions differently in sideview width = MOOSHPIT_SIDEVIEW_LINKHITBOXWIDTH; height = MOOSHPIT_SIDEVIEW_LINKHITBOXHEIGHT; X = LinkX+7-width/2+width; Y = LinkY+7-height/2+height; } else{ X = LinkX+7-width/2+width; Y = LinkY+11-height/2+height; } //If one corner of Link's hitbox isn't on a pit, return false if(Screen->ComboT[ComboAt(X, Y)]!=CT_HOLELAVA){ return false; } } } return true; } bool MooshPit_OnFFC(int LinkX, int LinkY){ for(int i=1; i<=32; i++){ //Cycle through every FFC ffc f = Screen->LoadFFC(i); //Check if the FFC is solid if(f->Data>0&&!f->Flags[FFCF_CHANGER]&&!f->Flags[FFCF_ETHEREAL]){ //Check if Link collides with the FFC if(RectCollision(LinkX+4, LinkY+9, LinkX+11, LinkY+14, f->X, f->Y, f->X+f->EffectWidth-1, f->Y+f->EffectHeight-1)){ return true; } } } //If Link doesn't collide with any FFC, return false return false; } void MooshPit_Init(){ MooshPit[MP_LASTX] = Link->X; MooshPit[MP_LASTY] = Link->Y; MooshPit[MP_LASTDMAP] = Game->GetCurDMap(); MooshPit[MP_LASTSCREEN] = Game->GetCurDMapScreen(); MooshPit[MP_ENTRYX] = Link->X; MooshPit[MP_ENTRYY] = Link->Y; MooshPit[MP_ENTRYDMAP] = Game->GetCurDMap(); MooshPit[MP_ENTRYSCREEN] = Game->GetCurDMapScreen(); MooshPit[MP_FALLSTATE] = 0; MooshPit[MP_FALLTIMER] = 0; Link->CollDetection = true; Link->Invisible = false; } void MooshPit_Update() { bool isWarp; if(Screen->Flags[SF_MISC]&(1<Flags[SF_ROOMTYPE]&100b) sideview = true; if(Link->Action!=LA_SCROLLING){ //Update the entry point whenever the screen changes if(MooshPit[MP_ENTRYDMAP]!=Game->GetCurDMap()||MooshPit[MP_ENTRYSCREEN]!=Game->GetCurDMapScreen()){ MooshPit[MP_ENTRYX] = Link->X; MooshPit[MP_ENTRYY] = Link->Y; MooshPit[MP_ENTRYDMAP] = Game->GetCurDMap(); MooshPit[MP_ENTRYSCREEN] = Game->GetCurDMapScreen(); } if(MooshPit[MP_FALLSTATE]==0) { //Not falling in pit LogPrint("FallState == 0 %s \n", "checking if over a pit"); LogPrint("Link is over a pit 2? %s \n", ( (MooshPit_OnPit(Link->X, Link->Y, true)) ? "true" : "false")); if(Link->Z<=tempPatches::BASE_LINK_Z&&MooshPit_OnPit(Link->X, Link->Y, true)) { LogPrint("Link is over a pit 3? %s \n", ( (MooshPit_OnPit(Link->X, Link->Y, true)) ? "true" : "false")); //If Link steps on a pit int underLink; if(!sideview) underLink = ComboAt(Link->X+8, Link->Y+12); else underLink = ComboAt(Link->X+8, Link->Y+8); StopDash(); lweapon fall; //Check if the combo is lava if(ComboFI(underLink, CF_LAVA)) { LogPrint("We should be %s \n", "playing a sound for Lava"); //Play sound and display animation Game->PlaySound(SFX_FALLLAVA); fall = CreateLWeaponAt(LW_SCRIPT10, Link->X, Link->Y); if(!MOOSHPIT_NO_GRID_SNAP){ fall->X = ComboX(underLink); fall->Y = ComboY(underLink); } fall->UseSprite(SPR_FALLLAVA); fall->CollDetection = false; fall->DeadState = fall->ASpeed*fall->NumFrames; //Mark as lava damage MooshPit[MP_DAMAGETYPE] = 1; } //Otherwise it's a pit else { LogPrint("We should be %s \n", "playing a sound for a pit"); //Play sound and display animation Game->PlaySound(SFX_FALLHOLE); fall = CreateLWeaponAt(LW_SCRIPT10, Link->X, Link->Y); if(!MOOSHPIT_NO_GRID_SNAP){ fall->X = ComboX(underLink); fall->Y = ComboY(underLink); } fall->UseSprite(SPR_FALLHOLE); fall->CollDetection = false; fall->DeadState = fall->ASpeed*fall->NumFrames; //Mark as hole damage MooshPit[MP_DAMAGETYPE] = 0; } MooshPit[MP_FALLX] = Link->X; MooshPit[MP_FALLY] = Link->Y; //Cooldown should last as long as the fall animation MooshPit[MP_FALLSTATE] = 1; MooshPit[MP_FALLTIMER] = Max(MOOSHPIT_MIN_FALL_TIME, fall->DeadState+MOOSHPIT_EXTRA_FALL_TIME); //Render Link invisible and intangible Link->Invisible = true; Link->CollDetection = false; NoAction(); StopDash(); } else if(!MooshPit_OnPit(Link->X, Link->Y, false)&&Link->Action!=LA_FROZEN){ //All other times, while Link is on solid ground, record Link's last position if(sideview){ //Link has no Z value in sideview, so we check if he's on a platform instead if(OnSidePlatform(Link->X, Link->Y)){ MooshPit[MP_LASTDMAP] = Game->GetCurDMap(); MooshPit[MP_LASTSCREEN] = Game->GetCurDMapScreen(); MooshPit[MP_LASTX] = Link->X; MooshPit[MP_LASTY] = Link->Y; } } else{ if(Link->Z<=tempPatches::BASE_LINK_Z){ MooshPit[MP_LASTDMAP] = Game->GetCurDMap(); MooshPit[MP_LASTSCREEN] = Game->GetCurDMapScreen(); MooshPit[MP_LASTX] = Link->X; MooshPit[MP_LASTY] = Link->Y; } } } } else if(MooshPit[MP_FALLSTATE]==1){ //Falling animation if(MooshPit[MP_FALLTIMER]>0) MooshPit[MP_FALLTIMER]--; Link->Jump = 0; Link->Z = 0; //Keep Link invisible just in case Link->Invisible = true; Link->CollDetection = false; NoAction(); StopDash(); if(MooshPit[MP_FALLTIMER]==0){ if(!isWarp||MooshPit[MP_DAMAGETYPE]==1){ //If the pit isn't a warp, deal damage and move Link back to the return point //If the entry would dump Link back in the pit, dump him out at the failsafe position if(MooshPit_OnPit(MooshPit[MP_ENTRYX], MooshPit[MP_ENTRYY], false)){ Link->X = MooshPit[MP_LASTX]; Link->Y = MooshPit[MP_LASTY]; //If the failsafe position was on a different screen, warp there if(Game->GetCurDMap()!=MooshPit[MP_LASTDMAP]||Game->GetCurDMapScreen()!=MooshPit[MP_LASTSCREEN]){ Link->PitWarp(MooshPit[MP_LASTDMAP], MooshPit[MP_LASTSCREEN]); } Link->Invisible = false; Link->CollDetection = true; } else{ //Move Link to the start and make him visible Link->X = MooshPit[MP_ENTRYX]; Link->Y = MooshPit[MP_ENTRYY]; Link->Invisible = false; Link->CollDetection = true; } //Subtract HP based on damage type if(MooshPit[MP_DAMAGETYPE]==1) Link->HP -= DAMAGE_FALLLAVA; else Link->HP -= DAMAGE_FALLHOLE; //Play hurt sound and animation Link->Action = LA_GOTHURTLAND; Link->HitDir = -1; Game->PlaySound(SFX_OUCH); MooshPit[MP_FALLSTATE] = 0; } else{ MooshPit[MP_FALLSTATE] = 2; MooshPit[MP_FALLTIMER] = 1; ffc warp = Screen->LoadFFC(FFC_MOOSHPIT_AUTOWARPA); warp->Data = CMB_MOOSHPIT_AUTOWARPA; warp->Flags[FFCF_CARRYOVER] = false; } } } else if(MooshPit[MP_FALLSTATE]==2){ //Just warped if(sideview){ Link->X = MooshPit[MP_FALLX]; Link->Y = 0; } else{ Link->X = MooshPit[MP_FALLX]; Link->Y = MooshPit[MP_FALLY]; Link->Z = 176; } Link->Invisible = false; Link->CollDetection = true; MooshPit[MP_FALLSTATE] = 0; MooshPit[MP_FALLTIMER] = 0; } } } void MooshPit_ResetEntry(){ MooshPit[MP_ENTRYX] = Link->X; MooshPit[MP_ENTRYY] = Link->Y; MooshPit[MP_ENTRYDMAP] = Game->GetCurDMap(); MooshPit[MP_ENTRYSCREEN] = Game->GetCurDMapScreen(); } //Set this to the item's ID. const int POTION_ID = 146; //Set this to the number of the sound effect to be played when the medicine takes effect. const int POTION_SFX = 22; //Set to 1 if you want a message to be displayed when the medicine takes effect. //Set to 0 if you don't want a message to be displayed. const int DISPLAY_MESSAGE = 1; //Set this to the number of the message that will be displayed when the medicine takes effect. const int MESSAGE = 1; void onScreenChange() { Game->SkipF6 = false; } global script slot2 { void run() { TraceS("@Running globl"); // Initialize variables used to listen on screen changes int curscreen = -1; int lastScreen = Game->GetCurScreen(); int lastMap = Game->GetCurMap(); int oldscreen = Game->GetCurScreen(); StartGhostZH(); while (true) { ScrollFix(); UpdateGhostZH1(); PegasusBoots(); PushAnimation(); LogPrint("Link->Z is %d \n ", Link->Z); LogPrint("DashCounter is %d \n ", DashCounter); LogPrint("Link is over a pit? %s \n", ( (MooshPit_OnPit(Link->X, Link->Y, true)) ? "true" : "false")); MooshPit_Update(); if (Link->HP <= 0 && Link->Item[POTION_ID]) { Link->HP = Link->MaxHP; Link->Item[POTION_ID] = false; Game->PlaySound(POTION_SFX); if (DISPLAY_MESSAGE > 0) { Screen->Message(MESSAGE); } } if(lastScreen != Game->GetCurScreen() || lastMap != Game->GetCurMap()) { onScreenChange(); lastScreen = Game->GetCurScreen(); lastMap = Game->GetCurMap(); } Waitdraw(); UpdateGhostZH2(); // Keep track of screen changes // Run a Barrier script on every screen change if (Game->GetCurScreen() != curscreen) { curscreen = Game->GetCurScreen(); Barriers_NewScreen(); } LREx1Ex2ItemSwitch(); Update_IceCombos(oldscreen); oldscreen = Game->GetCurScreen(); Waitframe(); } } } global script Init { void run() { MooshPit_Init(); } }