Copy to Clipboard Test

Moosh's Pit Script Code

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