Copy to Clipboard Test

Dodge Roll Action Code

//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();
		}
	}
}