Copy to Clipboard Test

Oracle Trampoline [2.55] Code

namespace Trampoline
{
	const int TRAMPOLINE_AFRAMES = 8; // Animation speed for the bounce animation
	const int TRAMPOLINE_MAX_Z = 176; // The Z height where Link bonks his head on the ceiling
	const int CF_TRAMPOLINE_WARP = CF_SCRIPT1; // The flag marking which positions on the screen you can jump up a floor at

	const int TRAMPOLINE_WARP_JUMP = 3.2; // Movement speed when bouncing up to another floor

	@Flag0("Is Warp"),
	@FlagHelp0("If true, the trampoline will bounce Link all the way off the top of the screen and warp him. If CF_TRAMPOLINE_WARP is not placed on any layer above him, he'll bonk off the ceiling."),
	@Flag1("Direct Warp"),
	@FlagHelp1("If true, the warp keeps Link's position."),
	@Attribyte0("Bounce SFX"),
	@AttribyteHelp0("This is the sound to play when bouncing off the trampoline"),
	@Attribyte1("Bonk SFX"),
	@AttribyteHelp1("This is the sound to play when bonking off a ceiling"),
	@Attribyte2("Extra Floors"),
	@AttribyteHelp2("If >0, the trampoline will send Link up this many extra floors"),
	@InitD0("Jump Height"),
	@InitDHelp0("How high to jump after bouncing on the trampoline or jumping out of a pit"),
	@InitD1("Tile Warp ID"),
	@InitDHelp1("Which tile warp to use for the warp")
	combodata script Trampoline
	{
		void run(int jumpHeight, int tileWarpID)
		{
			bool fromAbove = Link->Z>0;
			bool isWarp = this->Flags[0];
			bool direct = this->Flags[1];
			int bounceSFX = this->Attribytes[0];
			int bonkSFX = this->Attribytes[1];
			int extraFloors = this->Attribytes[2];
			
			mapdata lyr = Game->LoadTempScreen(this->Layer);
			
			int animCounter;
			bool attemptingWarp;
			bool warpFailed;
			while(true)
			{
				// Keep the trampoline animating and turn off solidity in the air
				UpdateSolid(this);
				DrawBounce(this, lyr, animCounter);
				if(animCounter)
					--animCounter;
				// Detect collision with the trampoline from above
				if(!attemptingWarp&&Link->Z==0&&Link->FakeZ==0&&ComboAt(Link->X+7, Link->Y+12)==this->Pos)
				{
					int x = Link->X;
					int y = Link->Y;
					// Snap Link to the trampoline's position
					while(Distance(x, y, this->X, this->Y)>4)
					{
						int ang = Angle(x, y, this->X, this->Y);
						x += VectorX(4, ang);
						y += VectorY(4, ang);
						NoAction();
						Waitdraw();
						Link->X = x;
						Link->Y = y;
						Waitframe();
					}
					Link->X = this->X;
					Link->Y = this->Y;
						
					// If entering from the screen above, bounce off
					if(fromAbove)
					{
						Link->Dir = FindExitDir();
						NoAction();
						Waitframe();
						if(bounceSFX)
							Game->PlaySound(bounceSFX);
						Link->Jump = 1.2;
						animCounter = TRAMPOLINE_AFRAMES*3;
						// Bounce off animation
						for(int i=0; i<16; ++i)
						{
							UpdateSolid(this);
							DrawBounce(this, lyr, animCounter);
							if(animCounter)
								--animCounter;
							Link->MoveXY(DirX(Link->Dir), DirY(Link->Dir));
							NoAction();
							Waitframe();
						}
						fromAbove = false;
					}
					else
					{
						if(bounceSFX)
							Game->PlaySound(bounceSFX);
						Link->Jump = jumpHeight;
						
						if(isWarp)
						{
							attemptingWarp = true;
							warpFailed = false;
						}
						animCounter = TRAMPOLINE_AFRAMES*3;
					}
				}
				if(attemptingWarp)
				{
					// Prevent inputs warping
					NoAction();
					Waitdraw();
					// And also snap Link into place
					Link->X = this->X;
					Link->Y = this->Y;
					// If he's already hit the ceiling, move him downwards
					if(warpFailed)
					{
						// Bounce off the trampoline when hitting the ground
						if(Link->Z<=0)
						{
							Link->Dir = FindExitDir();
							NoAction();
							Waitframe();
							if(bounceSFX)
								Game->PlaySound(bounceSFX);
							Link->Jump = 1.2;
							animCounter = TRAMPOLINE_AFRAMES*3;
							// Bounce off animation
							for(int i=0; i<16; ++i)
							{
								UpdateSolid(this);
								DrawBounce(this, lyr, animCounter);
								if(animCounter)
									--animCounter;
								Link->MoveXY(DirX(Link->Dir), DirY(Link->Dir));
								NoAction();
								Waitframe();
							}
							NoAction();
							Waitdraw();
							attemptingWarp = false;
							warpFailed = false;
						}
					}
					// Otherwise he's rising
					else
					{
						if(Link->Jump<0)
							Link->Jump = 0;
						Link->Z += TRAMPOLINE_WARP_JUMP-Link->Jump;
						int maxZ = TRAMPOLINE_MAX_Z;
						if(maxZ<0)
							maxZ = Link->Y+16;
						// Check for bonking and warping
						if(Link->Z>=maxZ)
						{
							// Check all 7 layers for the warp marker flag
							bool blocked = true;
							for(int i=0; i<=6; ++i)
							{
								mapdata md = Game->LoadTempScreen(i);
								if(md->ComboF[this->Pos]==CF_TRAMPOLINE_WARP||md->ComboI[this->Pos]==CF_TRAMPOLINE_WARP)
								{
									blocked = false;
									break;
								}
							}
							if(blocked)
							{
								Screen->Quake = 10;
								if(bonkSFX)
									Game->PlaySound(bonkSFX);
								warpFailed = true;
							}
							else
							{
								// Run a generic script to handle jumping out of the pit on the next screen
								genericdata gd = Game->LoadGenericData(Game->GetGenericScript("TrampolineGeneric"));
								gd->DataSize = TDI_SIZE;
								gd->Data[TDI_RUNFROMCOMBO] = true;
								gd->Data[TDI_JUMP] = jumpHeight;
								gd->Data[TDI_FALLING] = false;
								gd->Data[TDI_FREEZEFALL] = false;
								gd->Data[TDI_EXTRAFLOORS] = extraFloors;
								gd->Data[TDI_WARPID] = tileWarpID;
								gd->Data[TDI_DIRECTWARP] = direct;
								gd->Data[TDI_BONKSFX] = bonkSFX;
								gd->Running = true;
								
								Link->MoveFlags[HEROMV_CAN_PITFALL] = false;
								Link->Invisible = true;
								Link->Z = 0;
								
								TakeTileWarp(tileWarpID, direct);
							}
						}
					}
				}
				Waitframe();
			}
		}
		bool IsPushBlock(int flag)
		{
			switch(flag)
			{
				case CF_PUSHUPDOWN:
				case CF_PUSH4WAY:
				case CF_PUSHLR...CF_PUSHRIGHTINS:
				case CF_PUSHED:
					return true;
			}
			return false;
		}
		void DrawBounce(combodata this, mapdata lyr, int animCounter)
		{
			int drawLayer = this->Layer;
			// If the trampoline is a push block, its draw layer must change
			if(IsPushBlock(lyr->ComboF[this->Pos]))
				drawLayer = SPLAYER_PUSHBLOCK;
				
			if(animCounter>0)
			{
				// The combo animation is handled by drawing tiles over the combo on its layer
				int frame = Floor((animCounter-1)/TRAMPOLINE_AFRAMES);
				if(frame==0)
					Screen->FastTile(drawLayer, this->X, this->Y, this->Tile+2, lyr->ComboC[this->Pos], OP_OPAQUE);
				else if(frame==2)
					Screen->FastTile(drawLayer, this->X, this->Y, this->Tile+1, lyr->ComboC[this->Pos], OP_OPAQUE);
			}
		}
		void UpdateSolid(combodata this)
		{
			// Swap out solidity and no enemy flags
			// This combo cannot have an inherent flag
			if(Link->Z>0||Link->FakeZ>0)
			{
				this->Walk = 0x0;
				this->Flag = CF_NOGROUNDENEMY;
			}
			else
			{
				this->Walk = 0xF;
				this->Flag = 0;
			}
		}
		int FindExitDir()
		{
			int vX = DirX(Link->Dir)*16;
			int vY = DirY(Link->Dir)*16;
			// Try the direction Link is facing
			if(Link->CanMoveXY(vX, vY))
				return Link->Dir;
			// Then the opposite one
			else if(Link->CanMoveXY(-vX, -vY))
				return OppositeDir(Link->Dir);
			// Then all four others starting from down
			for(int i=0; i<4; ++i)
			{
				vX = DirX(OppositeDir(i))*16;
				vY = DirY(OppositeDir(i))*16;
				if(Link->CanMoveXY(vX, vY))
					return OppositeDir(i);
			}
			// Default to facing down
			return DIR_DOWN;
		}
		void TakeTileWarp(int i, bool direct)
		{
			int effect;
			int type = Screen->GetTileWarpType(i);
			int dmap = Screen->GetTileWarpDMap(i);
			int scrn = Screen->GetTileWarpScreen(i);
			int x = -1;
			int y = Screen->TileWarpReturnSquare[i];
			if(direct)
			{
				x = Link->X;
				y = Link->Y;
			}
			int flags = WARP_FLAG_PLAYMUSIC;
			switch(type)
			{
				case WT_NOWARP:
					return;
				case WT_ENTRANCEEXIT:
					effect = WARPEFFECT_OPENWIPE;
					flags |= WARP_FLAG_SETENTRANCESCREEN;
					flags |= WARP_FLAG_SETENTRANCEDMAP;
					flags |= WARP_FLAG_SETCONTINUESCREEN;
					flags |= WARP_FLAG_SETCONTINUEDMAP;
					break;
				case WT_IWARPBLACKOUT:
					effect = WARPEFFECT_INSTANT;
					break;
				case WT_IWARPOPENWIPE:
					effect = WARPEFFECT_OPENWIPE;
					break;
				case WT_IWARPZAP:
					effect = WARPEFFECT_ZAP;
					break;
				case WT_IWARPWAVE:
					effect = WARPEFFECT_WAVE;
					break;
			}
			Link->WarpEx({type, dmap, scrn, x, y, effect, 0, flags});
		}
	}
	
	@InitD0("Z Height"),
	@InitDHelp0("How high to place Link in the air after scrolling onto the new screen"),
	@InitD1("Freeze"),
	@InitDHelp1("If >0, Link can't move while in the air")
	combodata script PitfallZAxis
	{
		void run(int zheight, int freeze)
		{
			if(!zheight)
			{
				if(TRAMPOLINE_MAX_Z<0)
					zheight = this->Y+16;
				else
					zheight = TRAMPOLINE_MAX_Z;
			}
			while(true)
			{	
				// Wait for Link to fall in the pit
				int LinkPos = ComboAt(Link->X+8, Link->Y+12);
				if(Link->Falling==70&&LinkPos==this->Pos) // nice
				{
					// Run the generic script
					genericdata gd = Game->LoadGenericData(Game->GetGenericScript("TrampolineGeneric"));
					gd->DataSize = TDI_SIZE;
					gd->Data[TDI_RUNFROMCOMBO] = true;
					gd->Data[TDI_JUMP] = zheight;
					gd->Data[TDI_FALLING] = true;
					gd->Data[TDI_FREEZEFALL] = freeze;
					gd->Running = true;
					
					Quit();
				}
				Waitframe();
			}
		}
	}
	
	enum TrampolineDataIndex
	{
		TDI_RUNFROMCOMBO,
		TDI_JUMP,
		TDI_FALLING,
		TDI_FREEZEFALL,
		TDI_EXTRAFLOORS,
		TDI_WARPID,
		TDI_DIRECTWARP,
		TDI_BONKSFX,
		
		TDI_SIZE
	};

	generic script TrampolineGeneric
	{
		void run()
		{
			this->DataSize = TDI_SIZE;
			this->ReloadState[GENSCR_ST_RELOAD] = true;
			this->ReloadState[GENSCR_ST_CONTINUE] = true;
			// If this script was restarted via F6, quit out
			if(!this->Data[TDI_RUNFROMCOMBO])
			{
				Link->MoveFlags[HEROMV_CAN_PITFALL] = true;
				Link->Invisible = false;
				Quit();
			}
			int dmap = Game->GetCurDMap();
			int scrn = Game->GetCurDMapScreen();
			this->Data[TDI_RUNFROMCOMBO] = false;
			// Wait for the screen to change
			while(dmap==Game->GetCurDMap()&&scrn==Game->GetCurDMapScreen())
			{
				Waitframe();
			}
			// If falling from a previous screen
			if(this->Data[TDI_FALLING])
			{
				Link->Invisible = false;
				Link->Z = this->Data[TDI_JUMP];
				int x = Link->X;
				int y = Link->Y;
				while(Link->Z>0)
				{
					WaitTo(SCR_TIMING_POST_POLL_INPUT);
					if(this->Data[TDI_FREEZEFALL])
						NoAction();
					WaitTo(SCR_TIMING_WAITDRAW);
					if(this->Data[TDI_FREEZEFALL])
					{
						Link->X = x;
						Link->Y = y;
					}
					Waitframe();
				}
			}
			// If jumping out of a pit
			else
			{
				int x = Link->X;
				int y = Link->Y;
				// If bounced up extra floors
				bool hitCeiling;
				while(this->Data[TDI_EXTRAFLOORS]>0&&!hitCeiling)
				{
					int maxZ = TRAMPOLINE_MAX_Z;
					if(maxZ<0)
						maxZ = Link->Y+16;
					Link->Invisible = false;
					Link->MoveFlags[HEROMV_CAN_PITFALL] = true;
					
					// Launch to the top
					while(Link->Z<maxZ)
					{
						if(Link->Jump<0)
							Link->Jump = 0;
						Link->Z += TRAMPOLINE_WARP_JUMP-Link->Jump;
						WaitTo(SCR_TIMING_POST_POLL_INPUT);
						NoAction();
						Waitframe();
					}	
					
					// Check for bonking or warping
					
					// Check all 7 layers for the warp marker flag
					bool blocked = true;
					int pos = ComboAt(Link->X+8, Link->Y+8);
					for(int i=0; i<=6; ++i)
					{
						mapdata md = Game->LoadTempScreen(i);
						if(md->ComboF[pos]==CF_TRAMPOLINE_WARP||md->ComboI[pos]==CF_TRAMPOLINE_WARP)
						{
							blocked = false;
							break;
						}
					}
					
					if(blocked)
					{
						Screen->Quake = 10;
						if(this->Data[TDI_BONKSFX])
							Game->PlaySound(this->Data[TDI_BONKSFX]);
						hitCeiling = true;
						Link->MoveFlags[HEROMV_CAN_PITFALL] = true;
					}
					else
					{
						Link->MoveFlags[HEROMV_CAN_PITFALL] = false;
						Link->Invisible = true;
						Link->Z = 0;
						
						Trampoline.TakeTileWarp(this->Data[TDI_WARPID], this->Data[TDI_DIRECTWARP]);
						WaitTo(SCR_TIMING_POST_POLL_INPUT);
						NoAction();
						Waitframe();
					}
					--this->Data[TDI_EXTRAFLOORS];
				}
				if(hitCeiling)
				{
					// Fall to the ground after hitting a ceiling
					while(Link->Z>0)
					{
						WaitTo(SCR_TIMING_POST_POLL_INPUT);
						NoAction();
						Waitframe();
					}
				}
				else
				{
					Link->Invisible = false;
					Link->Jump = this->Data[TDI_JUMP];
					Link->MoveFlags[HEROMV_CAN_PITFALL] = true;
				}
			}
		}
	}
}