//This function handles which button does the roll bool LinkRoll_PressRoll(){ return Link->PressL; //Replace this with whatever button you want } bool LinkRoll_DisableRollInput(){ //Same as above set these to the button you want Link->PressL = false; Link->InputL = false; } //This function handles interaction with other scripts that will need to interrupt the roll early bool LinkRoll_CheckInterrupt(){ //For use with MooshPits //if(MooshPit[_MP_FALLSTATE]==1)return true; return Link->Action!=LA_NONE&&Link->Action!=LA_WALKING; } const int TIL_LINKROLL4 = 33420; //The tile for Link's roll. First of four directions: Up, Down, Left, Right const int AFRAMES_LINKROLL4 = 3; //The number of frames per roll direction const int ASPEED_LINKROLL4 = 4; //The animation speed of the roll const int TIL_LINKBONK4 = 33260; //The tile for Link's wall bonk. First of four directions: Up, Down, Left, Right const int SFX_LINKROLL = 60; //Sound to play when Link rolls const int NUM_SFX_LINKROLL = 0; //Number of sounds to play when rolling at random const int SFX_LINKBONK = 21; //Sound of Link bonking on a wall const int SPR_LINKROLL_DUST = 89; //Sprite used for dust drawn behind Link const int FREQ_LINKROLL_DUST = 2; //How frequently dust particles are emitted in frame delay const int MPCOST_LINKROLL = 0; //MP to take when Link rolls const int LINKROLL_STEP = 4; //Step speed of the roll const int LINKROLL_MINSTEP = 2.5; //Minimum step speed with acceleration based rolling const int LINKROLL_FRAMES = 8; //How many frames Link rolls for before deccelerating const int LINKROLL_DECEL = 0.5; //Deacceleration of the roll const int LINKROLL_IFRAMES = 8; //How many frames of the roll Link is invincible for const int LINKROLL_IFRAMES_DELAY = 0; //Delay at the start of the roll where Link isn't invincible const int LINKROLL_COOLDOWN = 8; //Delay before Link can roll again after coming to a stop const int LINKROLL_TRACKINGLENGTH = 16; //If using acceleration based rolling, how many frames the script tracks Link's movement for const int LINKROLL_BONK_STEP = 0.5; //Step speed when bonking off a wall const int LINKROLL_BONK_JUMP = 1.2; //Jump height when bonking off a wall const int LINKROLL_8WAY = 1; //If 1, Link can roll 8 directions. If 2, Link rolls based on acceleration allowing for more than 8 directions. const int LINKROLL_VARIABLESPEED = 0; //If 1, Link's movement before rolling affects roll speed const int LINKROLL_STOPWALL = 1; //If 1, stops roll momentum when hitting a wall const int LINKROLL_CANBONK = 1; //If 1, Link can bonk against walls after rolling int LinkRoll[256]; const int _LRI_VX = 0; const int _LRI_VY = 1; const int _LRI_COUNTER = 2; const int _LRI_STEP = 3; const int _LRI_ANGLE = 4; const int _LRI_STATE = 5; const int _LRI_TRACKING = 16; void LinkRoll_Init(){ int i; for(i=0; i<_LRI_TRACKING; ++i){ LinkRoll[i] = 0; } LinkRoll_ResetTracking(); } void LinkRoll_Update(){ if(Link->Action==LA_SCROLLING){ LinkRoll[_LRI_STATE] = 0; LinkRoll[_LRI_COUNTER] = 0; LinkRoll_DisableRollInput(); return; } //Only update movement tracking if it's needed if(LINKROLL_8WAY==2||LINKROLL_VARIABLESPEED) LinkRoll_UpdateTracking(); int vX; int vY; //If Link is currently rolling if(LinkRoll[_LRI_STATE]){ if(LinkRoll[_LRI_STATE]==1){ //Rolling //Things that cancel rolling if(LinkRoll_CheckInterrupt()){ LinkRoll[_LRI_STATE] = 3; LinkRoll[_LRI_COUNTER] = LINKROLL_COOLDOWN; NoAction(); return; } //Make Link invincible during iframes if(LinkRoll[_LRI_COUNTER]>=LINKROLL_IFRAMES_DELAY&&LinkRoll[_LRI_COUNTER]<LINKROLL_IFRAMES_DELAY+LINKROLL_IFRAMES) TempLinkState_UnsetCollDetection(); int frame; int frameWidth = 1; if(TEMPLINKSTATE_BIG_LINK==2) frameWidth = 2; //Only animate if aframes and aspeed are set if(AFRAMES_LINKROLL4&&ASPEED_LINKROLL4) frame = Floor((LinkRoll[_LRI_COUNTER]%(AFRAMES_LINKROLL4*ASPEED_LINKROLL4))/ASPEED_LINKROLL4); TempLinkState_SetLinkTileOverride(TIL_LINKROLL4+Link->Dir*AFRAMES_LINKROLL4+frame*frameWidth); //Create dust particles periodically if(SPR_LINKROLL_DUST){ if(LinkRoll[_LRI_COUNTER]%FREQ_LINKROLL_DUST==0){ lweapon l = CreateLWeaponAt(LW_SPARKLE, Link->X+Rand(-4, 4), Link->Y+8+Rand(-4, 4)); l->UseSprite(SPR_LINKROLL_DUST); l->CollDetection = false; } } vX = VectorX(LinkRoll[_LRI_STEP], LinkRoll[_LRI_ANGLE]); vY = VectorY(LinkRoll[_LRI_STEP], LinkRoll[_LRI_ANGLE]); //If Link is rolling into a wall on X or Y axis, kill momentum on that axis if(LINKROLL_STOPWALL){ if((vX<0&&LinkRoll_CheckBlocked(Link->X, Link->Y, DIR_LEFT))||(vX>0&&LinkRoll_CheckBlocked(Link->X, Link->Y, DIR_RIGHT))){ LinkRoll[_LRI_VX] = 0; } if((vY<0&&LinkRoll_CheckBlocked(Link->X, Link->Y, DIR_UP))||(vY>0&&LinkRoll_CheckBlocked(Link->X, Link->Y, DIR_DOWN))){ LinkRoll[_LRI_VY] = 0; } } //If Link bonks off a wall if(LINKROLL_CANBONK){ if(LinkRoll_CheckBlocked(Link->X, Link->Y, Link->Dir)==1){ Game->PlaySound(SFX_LINKBONK); Link->Jump = LINKROLL_BONK_JUMP; LinkRoll[_LRI_ANGLE] = LinkRoll_DirAngle(OppositeDir(Link->Dir)); LinkRoll[_LRI_STEP] = LINKROLL_BONK_STEP; LinkRoll[_LRI_STATE] = 2; NoAction(); return; } } //Actually kill temporary momentum if flagged to do so if(LinkRoll[_LRI_VX]==0) vX = 0; if(LinkRoll[_LRI_VY]==0) vY = 0; LinkMovement_Push2NoEdge(vX, vY); ++LinkRoll[_LRI_COUNTER]; if(LinkRoll[_LRI_COUNTER]>=LINKROLL_FRAMES){ if(LINKROLL_DECEL!=0){ LinkRoll[_LRI_STEP] -= LINKROLL_DECEL; if(LinkRoll[_LRI_STEP]<=0){ LinkRoll[_LRI_STATE] = 3; LinkRoll[_LRI_COUNTER] = LINKROLL_COOLDOWN; } } else{ LinkRoll[_LRI_STATE] = 3; LinkRoll[_LRI_COUNTER] = LINKROLL_COOLDOWN; } } NoAction(); } else if(LinkRoll[_LRI_STATE]==2){ //Bonking vX = VectorX(LinkRoll[_LRI_STEP], LinkRoll[_LRI_ANGLE]); vY = VectorY(LinkRoll[_LRI_STEP], LinkRoll[_LRI_ANGLE]); int frameWidth = 1; if(TEMPLINKSTATE_BIG_LINK==2) frameWidth = 2; TempLinkState_SetLinkTileOverride(TIL_LINKBONK4+Link->Dir*frameWidth); LinkMovement_Push2NoEdge(vX, vY); if(LinkRoll_OnGround()||Link->Jump<-LINKROLL_BONK_JUMP){ LinkRoll[_LRI_STATE] = 3; LinkRoll[_LRI_COUNTER] = LINKROLL_COOLDOWN; } NoAction(); } else if(LinkRoll[_LRI_STATE]==3){ //Cooldown --LinkRoll[_LRI_COUNTER]; if(LinkRoll[_LRI_COUNTER]<=0) LinkRoll[_LRI_STATE] = 0; } } else{ //Begin roll when the button is pressed if(LinkRoll_OnGround()&&(Link->Action==LA_WALKING||Link->Action==LA_NONE)&&LinkRoll_PressRoll()&&Link->MP>=MPCOST_LINKROLL){ Link->MP -= MPCOST_LINKROLL; if(NUM_SFX_LINKROLL) Game->PlaySound(SFX_LINKROLL+Rand(NUM_SFX_LINKROLL)); else Game->PlaySound(SFX_LINKROLL); int rollDir; int rollDist; LinkRoll[_LRI_STEP] = LINKROLL_STEP; LinkRoll[_LRI_STATE] = 1; LinkRoll[_LRI_COUNTER] = 0; //360 degree rolling if(LINKROLL_8WAY==2){ rollDist = LinkRoll_GetAverageStep(); if(rollDist>0){ LinkRoll[_LRI_ANGLE] = LinkRoll_GetAverageAngle(); rollDir = AngleDir8(LinkRoll[_LRI_ANGLE]); //If Link isn't already partially facing the right direction, turn him to face it if(!LinkRoll_PartialDir(Link->Dir, rollDir)){ Link->Dir = AngleDir4(LinkRoll[_LRI_ANGLE]); } } else{ LinkRoll[_LRI_ANGLE] = LinkRoll_DirAngle(Link->Dir); } } //8-way rolling else if(LINKROLL_8WAY==1){ //If a direction is being held, use the stick if(LinkMovement_StickX()!=0||LinkMovement_StickY()!=0){ LinkRoll[_LRI_ANGLE] = Angle(0, 0, LinkMovement_StickX(), LinkMovement_StickY()); rollDir = AngleDir8(LinkRoll[_LRI_ANGLE]); //If Link isn't already partially facing the right direction, turn him to face it if(!LinkRoll_PartialDir(Link->Dir, rollDir)){ Link->Dir = AngleDir4(LinkRoll[_LRI_ANGLE]); } } //Else use Link's direction else{ LinkRoll[_LRI_ANGLE] = LinkRoll_DirAngle(Link->Dir); } } //4-way rolling else{ LinkRoll[_LRI_ANGLE] = LinkRoll_DirAngle(Link->Dir); } //If variable speed is set, change Link's roll speed based on that if(LINKROLL_VARIABLESPEED){ rollDist = LinkRoll_GetAverageStep(); LinkRoll[_LRI_STEP] = Max(LINKROLL_MINSTEP, LinkRoll[_LRI_STEP]*(rollDist/1.5)); } LinkRoll[_LRI_VX] = VectorX(LinkRoll[_LRI_STEP], LinkRoll[_LRI_ANGLE]); LinkRoll[_LRI_VY] = VectorY(LinkRoll[_LRI_STEP], LinkRoll[_LRI_ANGLE]); NoAction(); } } LinkRoll_DisableRollInput(); } //Returns true if Link is standing on something solid bool LinkRoll_OnGround(){ if(IsSideview()){ return OnSidePlatform(Link->X, Link->Y)&&Link->Jump<=0; } else{ return Link->Z==0&&Link->Jump<=0; } } //Reset the arrays tracking Link's movement void LinkRoll_ResetTracking(){ for(int i=LINKROLL_TRACKINGLENGTH-1; i>=0; --i){ LinkRoll[_LRI_TRACKING+2+i*2] = 0; LinkRoll[_LRI_TRACKING+2+i*2+1] = 0; } LinkRoll[_LRI_TRACKING] = Link->X; LinkRoll[_LRI_TRACKING+1] = Link->X; } //Updates the arrays tracking Link's movement void LinkRoll_UpdateTracking(){ for(int i=LINKROLL_TRACKINGLENGTH-1; i>=1; --i){ LinkRoll[_LRI_TRACKING+2+i*2] = LinkRoll[_LRI_TRACKING+2+i*2-2]; LinkRoll[_LRI_TRACKING+2+i*2+1] = LinkRoll[_LRI_TRACKING+2+i*2-2+1]; } int vX = Link->X-LinkRoll[_LRI_TRACKING]; int vY = Link->Y-LinkRoll[_LRI_TRACKING+1]; //If something messes with Link's speed, keep it clamped to his normal step //This should also prevent warps from having a noticeable effect on the tracking. Janky hack m8 if(Distance(0, 0, vX, vY)>2){ int angle = Angle(0, 0, vX, vY); vX = VectorX(1.5, angle); vY = VectorY(1.5, angle); } LinkRoll[_LRI_TRACKING+2] = vX; LinkRoll[_LRI_TRACKING+3] = vY; LinkRoll[_LRI_TRACKING] = Link->X; LinkRoll[_LRI_TRACKING+1] = Link->Y; } //Returns the angle of the average of Link's last 16 frames int LinkRoll_GetAverageAngle(){ int vX; int vY; for(int i=0; i<LINKROLL_TRACKINGLENGTH; ++i){ vX += LinkRoll[_LRI_TRACKING+2+i*2]; vY += LinkRoll[_LRI_TRACKING+2+i*2+1]; } vX /= LINKROLL_TRACKINGLENGTH; vY /= LINKROLL_TRACKINGLENGTH; return Angle(0, 0, vX, vY); } //Returns the step of the average of Link's last 16 frames int LinkRoll_GetAverageStep(){ int vX; int vY; for(int i=0; i<LINKROLL_TRACKINGLENGTH; ++i){ vX += LinkRoll[_LRI_TRACKING+2+i*2]; vY += LinkRoll[_LRI_TRACKING+2+i*2+1]; } vX /= LINKROLL_TRACKINGLENGTH; vY /= LINKROLL_TRACKINGLENGTH; return Distance(0, 0, vX, vY); } //Returns true if a 4-way direction is part of an 8-way direction bool LinkRoll_PartialDir(int dir4, int dir8){ if(dir4==DIR_UP) return dir8==DIR_UP||dir8==DIR_LEFTUP||dir8==DIR_RIGHTUP; else if(dir4==DIR_DOWN) return dir8==DIR_DOWN||dir8==DIR_LEFTDOWN||dir8==DIR_RIGHTDOWN; else if(dir4==DIR_LEFT) return dir8==DIR_LEFT||dir8==DIR_LEFTUP||dir8==DIR_LEFTDOWN; else return dir8==DIR_RIGHT||dir8==DIR_RIGHTUP||dir8==DIR_RIGHTDOWN; } //Returns an angle given a direction int LinkRoll_DirAngle(int dir){ if(dir==DIR_UP) return -90; else if(dir==DIR_DOWN) return 90; else if(dir==DIR_LEFT) return 180; else if(dir==DIR_RIGHT) return 0; else if(dir==DIR_LEFTUP) return -135; else if(dir==DIR_RIGHTUP) return -45; else if(dir==DIR_LEFTDOWN) return 135; else if(dir==DIR_RIGHTDOWN) return 45; return 0; } //Modified CanWalk() used to allow walking offscreen bool LinkRoll_CanWalkNoEdge(int x, int y, int dir, int step, bool full_tile) { int c=8; int xx = x+15; int yy = y+15; if(full_tile) c=0; if(dir==0) return !(Screen->isSolid(x,y+c-step)||Screen->isSolid(x+8,y+c-step)||Screen->isSolid(xx,y+c-step)); else if(dir==1) return !(Screen->isSolid(x,yy+step)||Screen->isSolid(x+8,yy+step)||Screen->isSolid(xx,yy+step)); else if(dir==2) return !(Screen->isSolid(x-step,y+c)||Screen->isSolid(x-step,y+c+7)||Screen->isSolid(x-step,yy)); else if(dir==3) return !(Screen->isSolid(xx+step,y+c)||Screen->isSolid(xx+step,y+c+7)||Screen->isSolid(xx+step,yy)); return false; //invalid direction } //Returns if Link can roll in a direction //0 - Can walk //1 - Bonked on wall //2 - Can't roll int LinkRoll_CheckBlocked(int x, int y, int dir){ if(Screen->MovingBlockX!=-1&&Screen->MovingBlockY!=-1){ if(RectCollision(Link->X-1, Link->Y-1, Link->X+16, Link->Y+16, Screen->MovingBlockX, Screen->MovingBlockY, Screen->MovingBlockX+15, Screen->MovingBlockY+15)){ if(AngleDir4(Angle(Link->X, Link->Y, Screen->MovingBlockX, Screen->MovingBlockY))==dir){ return 1; } } } if(IsSideview()){ if(dir==DIR_LEFT||dir==DIR_RIGHT){ if(!LinkRoll_CanWalkNoEdge(Link->X, Link->Y, dir, 1, true)) return 1; return 0; } return 2; } if(!LinkRoll_CanWalkNoEdge(Link->X, Link->Y, dir, 1, false)) return 1; return 0; } global script RollExample{ void run(){ LinkRoll_Init(); ScrollingDraws_Init(); LinkMovement_Init(); TempLinkState_Init(); while(true){ LinkRoll_Update(); ScrollingDraws_Update(); LinkMovement_Update1(); TempLinkState_Update1(); Waitdraw(); LinkMovement_Update2(); TempLinkState_Update2(); Waitframe(); } } }