Copy to Clipboard Test

Scrolling Backgrounds Code

const int SCROLLINGLAYER_LINKSTEP = 1.5; //Link's step speed
const int SCROLLINGLAYER_FASTSCROLLING = 1; //Set to 1 if Very Fast Scrolling is enabled
const int SCROLLINGLAYER_DEBUG = 0; //Set to 1 to show positions of onscreen layers

int ScrollingBG[112]; //Size should be at least _SBG_START + 6 * _SBG_BLOCK

//Global array indices
const int _SBG_LINKX			 = 0;
const int _SBG_LINKY			 = 1;
const int _SBG_SCROLLDIR		 = 2;
const int _SBG_SCROLLCOUNTER	 = 3;

//Start index of the 2D part of the global array and block size
const int _SBG_START = 16;
const int _SBG_BLOCK = 16;

//Indices for scrolling layer properties
const int _SBG_X		 = 0;
const int _SBG_Y		 = 1;
const int _SBG_XSTEP	 = 2;
const int _SBG_YSTEP	 = 3;
const int _SBG_MAP		 = 4;
const int _SBG_SCREEN	 = 5;
const int _SBG_LAYER	 = 6;
const int _SBG_WIDTH	 = 7;
const int _SBG_HEIGHT	 = 8;
const int _SBG_OPACITY	 = 9;
const int _SBG_FLAGS	 = 10;
const int _SBG_DMAP		 = 11;

//Flag constants
const int _SBGF_NORESET			 = 00000001b;
const int _SBGF_NOPOSITION		 = 00000010b;
const int _SBGF_TRACKX			 = 00000100b;
const int _SBGF_TRACKY			 = 00001000b;
const int _SBGF_TRACKXSCROLL	 = 00010000b;
const int _SBGF_TRACKYSCROLL	 = 00100000b;
const int _SBGF_FORCEDRAWSCREEN	 = 01000000b;

global script ScrollingLayer_Example{
	void run(){
		ScrollingBG_Init();
		while(true){
			ScrollingBG_Update();
			
			Waitdraw();
			
			Waitframe();
		}
	}
}

void ScrollingBG_Init(){
	int i; int j; int k;
	for(i=0; i<6; ++i){
		j = _SBG_START+i*_SBG_BLOCK;
		//If the layer hasn't been assigned by an FFC this frame, clear it on F6
		if(!(ScrollingBG[j+_SBG_FLAGS]&_SBGF_NORESET)){
			for(k=0; k<_SBG_BLOCK; ++k){
				ScrollingBG[j+k] = 0;
			}
		}
	}
	
	ScrollingBG[_SBG_LINKX] = Link->X;
	ScrollingBG[_SBG_LINKY] = Link->Y;
	ScrollingBG[_SBG_SCROLLDIR] = -1;
	ScrollingBG[_SBG_SCROLLCOUNTER] = 0;
}

void ScrollingBG_Update(){
	int i; int j; int k;
	
	int x; int y; int w; int h;
	bool trackLinkX; bool trackScrollX;
	bool trackLinkY; bool trackScrollY;
	
	int dX = (Link->X-ScrollingBG[_SBG_LINKX])/SCROLLINGLAYER_LINKSTEP;
	int dY = (Link->Y-ScrollingBG[_SBG_LINKY])/SCROLLINGLAYER_LINKSTEP;
	ScrollingBG[_SBG_LINKX] = Link->X;
	ScrollingBG[_SBG_LINKY] = Link->Y;
	
	if(Link->Action==LA_SCROLLING){
		if(ScrollingBG[_SBG_SCROLLDIR]==-1){
			if(Link->Y>160)
				ScrollingBG[_SBG_SCROLLDIR] = DIR_UP;
			else if(Link->Y<0)
				ScrollingBG[_SBG_SCROLLDIR] = DIR_DOWN;
			else if(Link->X>240)
				ScrollingBG[_SBG_SCROLLDIR] = DIR_LEFT;
			else
				ScrollingBG[_SBG_SCROLLDIR] = DIR_RIGHT;
			
			ScrollingBG[_SBG_SCROLLCOUNTER] = 0;
		}
		else
			++ScrollingBG[_SBG_SCROLLCOUNTER];
	}
	else
		ScrollingBG[_SBG_SCROLLDIR] = -1;
	
	for(i=0; i<6; ++i){
		j = _SBG_START+i*_SBG_BLOCK;
		if(ScrollingBG[j+_SBG_OPACITY]&&ScrollingBG[j+_SBG_DMAP]==Game->GetCurDMap()&&Game->GetCurScreen()<0x80){
			if(ScrollingBG[j+_SBG_FLAGS]&_SBGF_TRACKX)
				trackLinkX = true;
			if(ScrollingBG[j+_SBG_FLAGS]&_SBGF_TRACKY)
				trackLinkY = true;
			if(ScrollingBG[j+_SBG_FLAGS]&_SBGF_TRACKXSCROLL)
				trackScrollX = true;
			if(ScrollingBG[j+_SBG_FLAGS]&_SBGF_TRACKYSCROLL)
				trackScrollY = true;
			
			w = 256*ScrollingBG[j+_SBG_WIDTH];
			h = 176*ScrollingBG[j+_SBG_HEIGHT];
			
			//Process scrolling animation
			//X-axis
			if(ScrollingBG_IsScrolling()){
				//During the scroll animation, move the background if set to track scrolling
				if(trackScrollX){
					if(ScrollingBG[_SBG_SCROLLDIR]==DIR_LEFT)
						ScrollingBG[j+_SBG_X] -= ScrollingBG[j+_SBG_XSTEP];
					else if(ScrollingBG[_SBG_SCROLLDIR]==DIR_RIGHT)
						ScrollingBG[j+_SBG_X] += ScrollingBG[j+_SBG_XSTEP];
				}
				//Scroll automatically if neither Link nor scrolling are being tracked
				else if(!trackLinkX){
					ScrollingBG[j+_SBG_X] += ScrollingBG[j+_SBG_XSTEP];
				}
			}
			else{
				//Only move when Link does if tracking him
				if(trackLinkX){
					ScrollingBG[j+_SBG_X] += ScrollingBG[j+_SBG_XSTEP]*dX;
				}
				else if(!trackScrollX)
					ScrollingBG[j+_SBG_X] += ScrollingBG[j+_SBG_XSTEP];
			}
			//Y-axis
			if(ScrollingBG_IsScrolling()){
				//During the scroll animation, move the background if set to track scrolling
				if(trackScrollY){
					if(ScrollingBG[_SBG_SCROLLDIR]==DIR_UP)
						ScrollingBG[j+_SBG_Y] -= ScrollingBG[j+_SBG_YSTEP];
					else if(ScrollingBG[_SBG_SCROLLDIR]==DIR_DOWN)
						ScrollingBG[j+_SBG_Y] += ScrollingBG[j+_SBG_YSTEP];
				}
				//Scroll automatically if neither Link nor scrolling are being tracked
				else if(!trackLinkY){
					ScrollingBG[j+_SBG_Y] += ScrollingBG[j+_SBG_YSTEP];
				}
			}
			else{
				//Only move when Link does if tracking him
				if(trackLinkY){
					ScrollingBG[j+_SBG_Y] += ScrollingBG[j+_SBG_YSTEP]*dY;
				}
				else if(!trackScrollX)
					ScrollingBG[j+_SBG_Y] += ScrollingBG[j+_SBG_YSTEP];
			}
			
			//Keep the X and Y positions of the background wrapped based on size
			if(ScrollingBG[j+_SBG_X]<0)
				ScrollingBG[j+_SBG_X] += w;
			if(ScrollingBG[j+_SBG_X]>=w)
				ScrollingBG[j+_SBG_X] -= w;
			
			if(ScrollingBG[j+_SBG_Y]<0)
				ScrollingBG[j+_SBG_Y] += h;
			if(ScrollingBG[j+_SBG_Y]>=h)
				ScrollingBG[j+_SBG_Y] -= h;
			
			x = Floor(ScrollingBG[j+_SBG_X]);
			y = Floor(ScrollingBG[j+_SBG_Y]);
			
			ScrollingBG_DrawLayer(ScrollingBG[j+_SBG_LAYER], ScrollingBG[j+_SBG_MAP], ScrollingBG[j+_SBG_SCREEN], x, y, ScrollingBG[j+_SBG_WIDTH], ScrollingBG[j+_SBG_HEIGHT], ScrollingBG[j+_SBG_OPACITY], ScrollingBG[j+_SBG_FLAGS]&_SBGF_FORCEDRAWSCREEN);
		
			if(SCROLLINGLAYER_DEBUG){
				Screen->DrawInteger(6, 24*i, 0, FONT_Z3SMALL, 0x01, 0x0F, -1, -1, x, 0, 128);
				Screen->DrawInteger(6, 24*i, 8, FONT_Z3SMALL, 0x01, 0x0F, -1, -1, y, 0, 128);
			}
		}
		
		//Unmark flags telling the global not to reset it and the FFC not to reposition it
		if(ScrollingBG[j+_SBG_FLAGS]&_SBGF_NORESET)
			ScrollingBG[j+_SBG_FLAGS] &= ~_SBGF_NORESET;
		if(ScrollingBG[j+_SBG_FLAGS]&_SBGF_NOPOSITION)
			ScrollingBG[j+_SBG_FLAGS] &= ~_SBGF_NOPOSITION;
	}
}

bool ScrollingBG_IsScrolling(){
	int xFrames = 64;
	int yFrames = 44;
	if(SCROLLINGLAYER_FASTSCROLLING){
		xFrames = 16;
		yFrames = 11;
	}
	if(ScrollingBG[_SBG_SCROLLDIR]>-1&&ScrollingBG[_SBG_SCROLLCOUNTER]>0){
		if(ScrollingBG[_SBG_SCROLLDIR]<2&&ScrollingBG[_SBG_SCROLLCOUNTER]<yFrames+1)
			return true;
		if(ScrollingBG[_SBG_SCROLLDIR]>=2&&ScrollingBG[_SBG_SCROLLCOUNTER]<xFrames+1)
			return true;
	}
	return false;
}

void ScrollingBG_DrawLayer(int layer, int srcMap, int srcScreen, int x, int y, int w, int h, int op, bool forceDrawScreen){
	int i;
	bool useDrawScreen;
	if(forceDrawScreen)
		useDrawScreen = true;
	//While scrolling, avoid using DrawLayer on negative layers. ZC does not like this.
	if(op==128&&Link->Action==LA_SCROLLING){
		if(layer==2&&ScreenFlag(SF_VIEW, 4))
			useDrawScreen = true;
		if(layer==3&&ScreenFlag(SF_VIEW, 5))
			useDrawScreen = true;
	}
	
	int tmpScreen;
	int scrnX;
	int scrnY;
	
	for(i=0; i<4; ++i){
		scrnX = Floor(x/256)+(i%2);
		scrnY = Floor(y/176)+Floor(i/2);

		if(scrnX<0)
			scrnX += w;
		if(scrnX>w-1)
			scrnX -= w;
		if(scrnY<0)
			scrnY += h;
		if(scrnY>h-1)
			scrnY -= h;
		
		tmpScreen = srcScreen+scrnX+scrnY*16;
		if(useDrawScreen)
			Screen->DrawScreen(layer, srcMap, tmpScreen, -(x%256)+256*(i%2), -(y%176)+176*Floor(i/2), 0);
		else
			Screen->DrawLayer(layer, srcMap, tmpScreen, 0, -(x%256)+256*(i%2), -(y%176)+176*Floor(i/2), 0, op);
	}
}

//This FFC script is placed on the screen to assign or unassign a scrolling layer for the current DMap
//D0: Which of the simultaneous scrolling layers to assign (0-5)
//D1: Layer to draw to
//D2: Source Map for the layer
//D3: Source Screen for the layer (in decimal)
//D4: X speed for the layer
//D5: Y speed for the layer
//D6: Opacity for the layer (0 to clear, 64 for transparent, 128 for opaque)
//D7: Sum of all of these flags you want to use
//		1 - Track Link when walking on the X axis
//		2 - Track Link when walking on the Y axis
//		4 - Track Link when scrolling on the X axis
//		8 - Track Link when scrolling on the Y axis
//		16 - Use 2x2 screen block
//		32 - Use 3x3 screen block
//		64 - Use 4x4 screen block
//		128 - Always use drawscreen (slow)
ffc script ScrollingLayer_Assign{
	void run(int whichScrollingLayer, int drawLayer, int srcMap, int srcScreen, int xStep, int yStep, int opacity, int flags){	
		whichScrollingLayer = Clamp(whichScrollingLayer, 0, 5);
		
		int width = 1;
		if(flags&16)
			width = 2;
		if(flags&32)
			width = 3;
		if(flags&64)
			width = 4;
		
		//Get the starting array index for the scrolling layer block
		int i = _SBG_START+whichScrollingLayer*_SBG_BLOCK;
		int j;
		
		//This runs twice because of script order shenanigans. Hooraaaay! Script order!
		for(j=0; j<2; ++j){
			if(opacity!=0&&(ScrollingBG[i+_SBG_OPACITY]&&ScrollingBG[i+_SBG_DMAP]==Game->GetCurDMap())){
				Waitframe();
				continue;
			}
			
			//Assign that stuff
			if(!(ScrollingBG[i+_SBG_FLAGS]&_SBGF_NOPOSITION)){
				ScrollingBG[i+_SBG_X] = 0;
				ScrollingBG[i+_SBG_Y] = 0;
				ScrollingBG[i+_SBG_WIDTH] = width;
				ScrollingBG[i+_SBG_HEIGHT] = width;
			}
			ScrollingBG[i+_SBG_XSTEP] = xStep;
			ScrollingBG[i+_SBG_YSTEP] = yStep;
			ScrollingBG[i+_SBG_MAP] = srcMap;
			ScrollingBG[i+_SBG_SCREEN] = srcScreen;
			ScrollingBG[i+_SBG_LAYER] = drawLayer;
			ScrollingBG[i+_SBG_OPACITY] = opacity;
			
			//Assign the flag that tells the global not to reset this index, as well as any others specified
			ScrollingBG[i+_SBG_FLAGS] = _SBGF_NORESET;
			if(flags&1)
				ScrollingBG[i+_SBG_FLAGS] |= _SBGF_TRACKX;
			if(flags&2)
				ScrollingBG[i+_SBG_FLAGS] |= _SBGF_TRACKY;
			if(flags&4)
				ScrollingBG[i+_SBG_FLAGS] |= _SBGF_TRACKXSCROLL;
			if(flags&8)
				ScrollingBG[i+_SBG_FLAGS] |= _SBGF_TRACKYSCROLL;
			if(flags&128)
				ScrollingBG[i+_SBG_FLAGS] |= _SBGF_FORCEDRAWSCREEN;
			
			ScrollingBG[i+_SBG_DMAP] = Game->GetCurDMap();
			
			Quit();
		}
	}
}

//This FFC script is placed on the screen to change the position of an assigned layer (because the last script ran out of arguments)
//D0: Which of the simultaneous scrolling layers to assign (0-5)
//D1: X position to set the layer to
//D2: Y position to set the layer to
//D3: Width to set the layer to (overrides flags used by the previous script)
//D4: Height to set the layer to (overrides flags used by the previous script)
ffc script ScrollingLayer_Reposition{
	void run(int whichScrollingLayer, int x, int y, int newW, int newH){	
		whichScrollingLayer = Clamp(whichScrollingLayer, 0, 5);
		newW = Clamp(newW, 0, 16);
		newH = Clamp(newH, 0, 8);
		
		//Get the starting array index for the scrolling layer block
		int i = _SBG_START+whichScrollingLayer*_SBG_BLOCK;
		int j;
		
		int w = ScrollingBG[i+_SBG_WIDTH]*256;
		int h = ScrollingBG[i+_SBG_HEIGHT]*176;
		
		//This runs twice because of script order shenanigans. Hooraaaay! Script order!
		for(j=0; j<2; ++j){
			if(!ScrollingBG[i+_SBG_OPACITY]||ScrollingBG[i+_SBG_FLAGS]&_SBGF_NORESET){
				if(newW&&newH){
					ScrollingBG[i+_SBG_WIDTH] = newW;
					ScrollingBG[i+_SBG_HEIGHT] = newH;
					w = ScrollingBG[i+_SBG_WIDTH]*256;
					h = ScrollingBG[i+_SBG_HEIGHT]*176;
				}
				
				while(x<0)
					x += w;
				while(x>=w)
					x -= w;
				while(y<0)
					y += h;
				while(y>=h)
					y -= h;
				
				ScrollingBG[i+_SBG_X] = x;
				ScrollingBG[i+_SBG_Y] = y;
				
				//Flag that the ScrollingLayer_Assign script shouldn't reset the layer's position, if this one comes first
				ScrollingBG[i+_SBG_FLAGS] |= _SBGF_NOPOSITION;
				Quit();
			}
			
			Waitframe();
		}
	}
}