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 _MP_SLIDETIMER = 13; const int MOOSHPIT_NO_GRID_SNAP = 0; //Set to 1 to prevent Link's falling sprite from snapping to the combo grid. const int MOOSHPIT_ENABLE_SLIDEYPITS = 0; //Set to 1 if Link should slide into pits he's partially on const int MOOSHPIT_NO_MOVE_WHILE_FALLING = 1; //Set to 1 if you don't want Link able to move while falling const int MOOSHPIT_NO_REENTER_STAIRS = 1; //Set to 1 to prevent Link reentering stairs when respawning from a pit. This uses an FFC slot to run the script const int MOOSHPIT_STUN_ENEMIES_WHILE_FALLING = 1; //Set to 1 to stun stunnable enemies while falling in a pit 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 = 88; //Sprite for Link falling in a hole const int SPR_FALLLAVA = 89; //Sprite for Link falling in lava const int SFX_FALLHOLE = 38; //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 = 2; //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_SLIDEYPIT_FREQ = 3; //Link will be pushed into slideypits every 1/n frames const int MOOSHPIT_SLIDEYPIT_MAXTIME = 20; //Link will be pushed into slideypits more intensely after n frames const int MOOSHPIT_SLIDEYPIT_ACCELFREQ = 8; //How often Link accelerates when falling in the pit int MooshPit_OnPit(int LinkX, int LinkY, bool countFFCs){ if(Link->Action==LA_FROZEN||Link->Action==LA_RAFTING||Link->Action==LA_INWIND) return -1; if(countFFCs){ if(MooshPit_OnFFC(LinkX, LinkY)) return -1; } bool sideview; if(Screen->Flags[SF_ROOMTYPE]&100b) sideview = true; //wew lad int width = MOOSHPIT_LINKHITBOXWIDTH; int height = MOOSHPIT_LINKHITBOXHEIGHT; int total; int solidTotal; 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 = Floor(LinkX+7-width/2+(width-1)*x)+1; Y = Floor(LinkY+7-height/2+(height-1)*y)+1; } else{ X = Floor(LinkX+7-width/2+(width-1)*x)+1; Y = Floor(LinkY+11-height/2+(height-1)*y)+1; } //If one corner of Link's hitbox is on a pit, flag that corner as covered if(Screen->ComboT[ComboAt(X, Y)]==CT_HOLELAVA){ total |= 1<<(1+(x+y*2)); } //If Link is on a solid combo, count that corner as a pit if(Screen->isSolid(X, Y)){ solidTotal |= 1<<(x+y*2); } } } if(total>0) //Assuming Link is on at least one actual pit, add up the solid and nonsolid pits return (total>>1)|(solidTotal<<4); return -1; } 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_StunEnemies(){ for(int i=Screen->NumNPCs(); i>=1; i--){ //Cycle through every enemy npc n = Screen->LoadNPC(i); //Make it so the enemy's stun never falls below 1 n->Stun = Max(n->Stun, 1); } } 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(){ int i; bool isWarp; if(Screen->Flags[SF_MISC]&(1<<SF_MISC_MOOSHPITWARP)) isWarp = true; bool sideview; if(Screen->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 int onPit = MooshPit_OnPit(Link->X, Link->Y, true); //Check if slidey pits are enabled and it's not sideview if(MOOSHPIT_ENABLE_SLIDEYPITS&&!IsSideview()){ if(Link->Z<=0&&onPit>-1){ //If Link is partially on a pit int slideVx; int slideVy; int reps = 1; //Check if it's a frame Link should be moved if(MooshPit[_MP_SLIDETIMER]%MOOSHPIT_SLIDEYPIT_FREQ==0||MooshPit[_MP_SLIDETIMER]>=MOOSHPIT_SLIDEYPIT_MAXTIME){ if((onPit&0111b)==0111b){ //Going up-left slideVx = -1; slideVy = -1; } else if((onPit&1011b)==1011b){ //Going up-right slideVx = 1; slideVy = -1; } else if((onPit&1101b)==1101b){ //Going down-left slideVx = -1; slideVy = 1; } else if((onPit&1110b)==1110b){ //Going down-right slideVx = 1; slideVy = 1; } else if((onPit&0011b)==0011b){ //Going up slideVy = -1; } else if((onPit&1100b)==1100b){ //Going down slideVy = 1; } else if((onPit&0101b)==0101b){ //Going left slideVx = -1; } else if((onPit&1010b)==1010b){ //Going right slideVx = 1; } else if((onPit&0001b)==0001b){ //Going up-left slideVx = -1; slideVy = -1; } else if((onPit&0010b)==0010b){ //Going up-right slideVx = 1; slideVy = -1; } else if((onPit&0100b)==0100b){ //Going down-left slideVx = -1; slideVy = 1; } else if((onPit&1000b)==1000b){ //Going down-right slideVx = 1; slideVy = 1; } //DEBUG DRAWS //VX // Screen->DrawInteger(6, 0, 0, FONT_Z1, 0x01, 0x0F, -1, -1, slideVx, 0, 128); //VY // Screen->DrawInteger(6, 0, 8, FONT_Z1, 0x01, 0x0F, -1, -1, slideVy, 0, 128); //ONPIT BITS // Screen->DrawInteger(6, 0, 16, FONT_Z1, 0x01, 0x0F, -1, -1, (onPit&1000b)>>3, 0, 128); // Screen->DrawInteger(6, 8, 16, FONT_Z1, 0x01, 0x0F, -1, -1, (onPit&0100b)>>2, 0, 128); // Screen->DrawInteger(6, 16, 16, FONT_Z1, 0x01, 0x0F, -1, -1, (onPit&0010b)>>1, 0, 128); // Screen->DrawInteger(6, 24, 16, FONT_Z1, 0x01, 0x0F, -1, -1, (onPit&0001b), 0, 128); //If Link is over the max slide time, increase the speed every 4 frames if(MooshPit[_MP_SLIDETIMER]>=MOOSHPIT_SLIDEYPIT_MAXTIME) reps += Floor((MooshPit[_MP_SLIDETIMER]-MOOSHPIT_SLIDEYPIT_MAXTIME)/MOOSHPIT_SLIDEYPIT_ACCELFREQ); } for(i=0; i<reps; i++){ if(slideVx<0&&CanWalk(Link->X, Link->Y, DIR_LEFT, 1, false)){ Link->X--; } else if(slideVx>0&&CanWalk(Link->X, Link->Y, DIR_RIGHT, 1, false)){ Link->X++; } if(slideVy<0&&CanWalk(Link->X, Link->Y, DIR_UP, 1, false)){ Link->Y--; } else if(slideVy>0&&CanWalk(Link->X, Link->Y, DIR_DOWN, 1, false)){ Link->Y++; } } MooshPit[_MP_SLIDETIMER]++; } else{ MooshPit[_MP_SLIDETIMER] = 0; } } if(onPit>-1){ //Combine solid combo bits with pit bits onPit |= (onPit>>4); //Remove non pit bits onPit &= 1111b; } if(Link->Z<=0&&onPit==15){ //If Link steps on a pit int underLink; if(!sideview){ underLink = ComboAt(Link->X+8, Link->Y+12); if(Screen->ComboT[underLink]!=CT_HOLELAVA){ for(i=0; i<4; i++){ underLink = ComboAt(Link->X+15*(i%2), Link->Y+8+7*Floor(i/2)); if(Screen->ComboT[underLink]==CT_HOLELAVA) break; } } } else{ underLink = ComboAt(Link->X+8, Link->Y+8); if(Screen->ComboT[underLink]!=CT_HOLELAVA){ for(i=0; i<4; i++){ underLink = ComboAt(Link->X+15*(i%2), Link->Y+15*Floor(i/2)); if(Screen->ComboT[underLink]==CT_HOLELAVA) break; } } } lweapon fall; //Check if the combo is lava if(ComboFI(underLink, CF_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{ //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); if(isWarp){ Link->X = ComboX(underLink); Link->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(); } else if(MooshPit_OnPit(Link->X, Link->Y, false)==-1&&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<=0){ 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]--; if(MOOSHPIT_STUN_ENEMIES_WHILE_FALLING) MooshPit_StunEnemies(); Link->Jump = 0; Link->Z = 0; //Keep Link invisible just in case Link->Invisible = true; Link->CollDetection = false; NoAction(); if(MooshPit[_MP_FALLTIMER]==0){ MooshPit[_MP_SLIDETIMER] = 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)==15){ if(MOOSHPIT_NO_REENTER_STAIRS){ //Call a script to place an FFC under Link to prevent reentering stairs int scriptName[] = "MooshPit_StairsFix"; int ffcNum = RunFFCScript(Game->GetFFCScript(scriptName), 0); if(ffcNum>0){ ffc f = Screen->LoadFFC(ffcNum); f->Flags[FFCF_ETHEREAL] = false; f->X = MooshPit[_MP_LASTX]; f->Y = MooshPit[_MP_LASTY]; } } 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{ if(MOOSHPIT_NO_REENTER_STAIRS){ //Call a script to place an FFC under Link to prevent reentering stairs int scriptName[] = "MooshPit_StairsFix"; int ffcNum = RunFFCScript(Game->GetFFCScript(scriptName), 0); if(ffcNum>0){ ffc f = Screen->LoadFFC(ffcNum); f->Flags[FFCF_ETHEREAL] = false; f->X = MooshPit[_MP_ENTRYX]; f->Y = MooshPit[_MP_ENTRYY]; } } //Move Link to the entry 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; if(MOOSHPIT_NO_MOVE_WHILE_FALLING){ MooshPit[_MP_FALLSTATE] = 3; NoAction(); } else MooshPit[_MP_FALLSTATE] = 0; MooshPit[_MP_FALLTIMER] = 0; } else if(MooshPit[_MP_FALLSTATE]==3){ //Falling into a new room (no action) if(MOOSHPIT_STUN_ENEMIES_WHILE_FALLING) MooshPit_StunEnemies(); NoAction(); if(IsSideview()){ if(OnSidePlatform(Link->X, Link->Y)) MooshPit[_MP_FALLSTATE] = 0; } else{ if(Link->Z<=0) MooshPit[_MP_FALLSTATE] = 0; } } } } void MooshPit_ResetEntry(){ MooshPit[_MP_ENTRYX] = Link->X; MooshPit[_MP_ENTRYY] = Link->Y; MooshPit[_MP_ENTRYDMAP] = Game->GetCurDMap(); MooshPit[_MP_ENTRYSCREEN] = Game->GetCurDMapScreen(); } ffc script MooshPit_StairsFix{ void run(){ this->Flags[FFCF_ETHEREAL] = false; while(LinkCollision(this)){ Waitframe(); } this->X = 0; this->Y = 0; this->Data = 0; } } global script MooshPitGlobal{ void run(){ MooshPit_Init(); while(true){ MooshPit_Update(); Waitframe(); } } }