Copy to Clipboard Test

Moosh`s Scrolling Backgrounds for 3.0 Code

namespace ScrollBG{
	//CONFIG SCROLLINGLAYER_DEBUG = 0; //Set to 1 to show positions of onscreen layers
	
	//int this->Data[272]; //Size should be at least _SBG_START + 6 * _SBG_BLOCK
	
	//Global array indices
	enum {_SBG_LINKX =0,_SBG_LINKY, _SBG_VIEWPORTX, _SBG_VIEWPORTY, _SBG_OLD_SCREEN_ID};
	
	//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
	enum{_SBG_X=0,_SBG_Y, _SBG_XSTEP, _SBG_YSTEP, _SBG_MAP, _SBG_SCREEN, _SBG_LAYER, _SBG_WIDTH, _SBG_HEIGHT, _SBG_OPACITY, _SBG_FLAGS, _SBG_DMAP};
	
	//Flag constants
	@Bitflags("int")
	enum{_SBGF_NORESET = 00000001b,_SBGF_NOPOSITION, _SBGF_TRACKX, _SBGF_TRACKY, _SBGF_TRACKXSCROLL, _SBGF_TRACKYSCROLL, _SBGF_TRACK_VIEWPORT, _SBGF_FORCEDRAWSCREEN};
	
	//Generic script that runs the main code. Must run at quest start and restart on Level/DMap change, F6 and Reload.
	@InitD0("Number of Slots"),	@InitDHelp0("Number of scrolling Background Slots. \n Fewer slots - better performance."),
	@InitD1("Debug scrolling"),	@InitDHelp1("Set to 1 to show various debug information. \n See lines 181 - 195 for more info.")
	generic script ScrollingLayer{
		void run(int NumSlots, int SCROLLINGLAYER_DEBUG){
			Screen->DrawOrigin = DRAW_ORIGIN_PLAYING_FIELD;	
			this->DataSize = _SBG_START + _SBG_BLOCK*NumSlots;
			ScrollingLayer_Init(this);
			
			while(true){
				ScrollingLayer_Update(this, SCROLLINGLAYER_DEBUG);	
				
				Waitframe();
			}
		}
	}
	
	void ScrollingLayer_Init(genericdata this){
		int i; int j; int k;
		for(i=0; i<this->InitD[0]; ++i){
			j = _SBG_START+i*_SBG_BLOCK;
			//If the layer hasn't been assigned by an FFC this frame, clear it on F6
			if((this->Data[j+_SBG_FLAGS]&_SBGF_NORESET)==0){
				for(k=0; k<_SBG_BLOCK; ++k){
					this->Data[j+k] = 0;
				}
			}
		}
		this->Data[_SBG_LINKX] = GlobalHeroX(this);
		this->Data[_SBG_LINKY] = GlobalHeroY(this);
		this->Data[_SBG_VIEWPORTX] = GlobalViewportX(this);
		this->Data[_SBG_VIEWPORTY] = GlobalViewportY(this);
	}
	
	void ScrollingLayer_Update(genericdata this, int SCROLLINGLAYER_DEBUG){
		int i; int j; int k;
		
		int x; int y; int w; int h;
		bool trackLinkX; bool trackScrollX;
		bool trackLinkY; bool trackScrollY;
		
		int dX = 0;
		int dY = 0;
		
		int dvX = 0;
		int dvY = 0;
		
		int dhX = 0;
		int dhY = 0;
		
		
		
		//Get delta for viewport and hero coordinates.
		if (Game->Scrolling[SCROLL_DIR]==-1)this->Data[_SBG_OLD_SCREEN_ID] = Game->CurScreen;
		
		int NewViewportX = GlobalViewportX(this);
		int NewViewportY = GlobalViewportY(this);
		int NewHeroX = GlobalHeroX(this);
		int NewHeroY = GlobalHeroY(this);
		
		dvX = (NewViewportX-this->Data[_SBG_VIEWPORTX]);
		dvY = (NewViewportY-this->Data[_SBG_VIEWPORTY]);
		this->Data[_SBG_VIEWPORTX] =NewViewportX;
		this->Data[_SBG_VIEWPORTY] =NewViewportY;
		
		dhX = (NewHeroX-this->Data[_SBG_LINKX]);
		dhY = (NewHeroY-this->Data[_SBG_LINKY]);
		this->Data[_SBG_LINKX] = NewHeroX;
		this->Data[_SBG_LINKY] = NewHeroY;
		
		for(i=0; i<this->InitD[0]; ++i){
			j = _SBG_START+i*_SBG_BLOCK;			
			
			//Skip drawing, if source map is ivalid, opacity is set to 0 (invisible), DMap is not vurrent DMap, or Hero in in passageway or item cellar (Screens>=0x80).
			if (!this->Data[j+_SBG_MAP]) continue;
			if (!this->Data[j+_SBG_OPACITY]) continue;
			if (this->Data[j+_SBG_DMAP]!=Game->CurDMap) continue;
			if (Game->HeroScreen>= 0x80)continue;
			
			//determine if this layer tracks player or viewport coordinates.
			if(this->Data[j+_SBG_FLAGS]&_SBGF_TRACK_VIEWPORT){
				dX = dvX;
				dY = dvY;
			}
			else {
				dX = dhX;
				dY = dhY;
			}
			
			//Check the rest of  scrolling layer flag.
			if(this->Data[j+_SBG_FLAGS]&_SBGF_TRACKX)
			trackLinkX = true;
			if(this->Data[j+_SBG_FLAGS]&_SBGF_TRACKY)
			trackLinkY = true;
			if(this->Data[j+_SBG_FLAGS]&_SBGF_TRACKXSCROLL)
			trackScrollX = true;
			if(this->Data[j+_SBG_FLAGS]&_SBGF_TRACKYSCROLL)
			trackScrollY = true;
			
			w = 256*this->Data[j+_SBG_WIDTH];
			h = 176*this->Data[j+_SBG_HEIGHT];
			
			//Process scrolling animation
			//X-axis
			if(Game->Scrolling[SCROLL_DIR]>=0){
				//During the scroll animation, move the background if set to track scrolling
				if(trackScrollX){
					this->Data[j+_SBG_X] += this->Data[j+_SBG_XSTEP]*dX;
				}
				//Scroll automatically if neither Link nor scrolling are being tracked
				else if(!trackLinkX){
					this->Data[j+_SBG_X] += this->Data[j+_SBG_XSTEP];
				}
			}
			else{
				//Only move when Link does if tracking him
				if(trackLinkX){
					this->Data[j+_SBG_X] += this->Data[j+_SBG_XSTEP]*dX;
				}
				else if(!trackScrollX) //Otherwise scroll automatically
				this->Data[j+_SBG_X] += this->Data[j+_SBG_XSTEP];
			}
			//Y-axis
			if(Game->Scrolling[SCROLL_DIR]>=0){
				//During the scroll animation, move the background if set to track scrolling
				if(trackScrollY){
					this->Data[j+_SBG_Y] += this->Data[j+_SBG_YSTEP]*dY;
				}
				//Scroll automatically if neither Link nor scrolling are being tracked
				else if(!trackLinkY){
					this->Data[j+_SBG_Y] += this->Data[j+_SBG_YSTEP];
				}
			}
			else{
				//Only move when Link does if tracking him
				if(trackLinkY){
					this->Data[j+_SBG_Y] += this->Data[j+_SBG_YSTEP]*dY;
				}
				else if(!trackScrollY)
				this->Data[j+_SBG_Y] += this->Data[j+_SBG_YSTEP];
			}
			
			//Keep the X and Y positions of the background wrapped based on size
			if(this->Data[j+_SBG_X]<0)
			this->Data[j+_SBG_X] += w;
			if(this->Data[j+_SBG_X]>=w)
			this->Data[j+_SBG_X] -= w;
			
			if(this->Data[j+_SBG_Y]<0)
			this->Data[j+_SBG_Y] += h;
			if(this->Data[j+_SBG_Y]>=h)
			this->Data[j+_SBG_Y] -= h;
			
			x = this->Data[j+_SBG_X];
			y = this->Data[j+_SBG_Y];
			
			//All set. Time to perform actual drawing.
			ScrollingLayer_DrawLayer(this->Data[j+_SBG_LAYER], this->Data[j+_SBG_MAP], this->Data[j+_SBG_SCREEN], x, y, this->Data[j+_SBG_WIDTH], this->Data[j+_SBG_HEIGHT], this->Data[j+_SBG_OPACITY], this->Data[j+_SBG_FLAGS]&_SBGF_FORCEDRAWSCREEN);
			
			//debug rendering
			if(SCROLLINGLAYER_DEBUG){
				Screen->DrawInteger(6, 24*i, 0, FONT_Z3SMALL, 0x01, 0x0F, -1, -1, x, 0, OP_OPAQUE);
				Screen->DrawInteger(6, 24*i, 8, FONT_Z3SMALL, 0x01, 0x0F, -1, -1, y, 0, OP_OPAQUE);
				Screen->DrawInteger(6, 24*i, 16, FONT_Z3SMALL, 0x01, 0x0F, -1, -1, this->Data[j+_SBG_FLAGS], 0, OP_OPAQUE);
				Screen->DrawInteger(6, 12, 24, FONT_Z3SMALL, 0x01, 0x0F, -1, -1, NewViewportX, 0, OP_OPAQUE);
				Screen->DrawInteger(6, 12, 32, FONT_Z3SMALL, 0x01, 0x0F, -1, -1, NewViewportY, 0, OP_OPAQUE);
				Screen->DrawInteger(6, 36, 24, FONT_Z3SMALL, 0x01, 0x0F, -1, -1, Viewport->X, 0, OP_OPAQUE);
				Screen->DrawInteger(6, 36, 32, FONT_Z3SMALL, 0x01, 0x0F, -1, -1, Viewport->Y, 0, OP_OPAQUE);
				Screen->DrawInteger(6, 60, 24, FONT_Z3SMALL, 0x01, 0x0F, -1, -1, NewHeroX, 0, OP_OPAQUE);
				Screen->DrawInteger(6, 60, 32, FONT_Z3SMALL, 0x01, 0x0F, -1, -1, NewHeroY, 0, OP_OPAQUE);
				Screen->DrawInteger(6, 84, 24, FONT_Z3SMALL, 0x01, 0x0F, -1, -1, dX, 0, OP_OPAQUE);
				Screen->DrawInteger(6, 84, 32, FONT_Z3SMALL, 0x01, 0x0F, -1, -1, dY, 0, OP_OPAQUE);
				Screen->DrawInteger(6, 108, 24, FONT_Z3SMALL, 0x01, 0x0F, -1, -1, Game->Scrolling[SCROLL_NX], 0, OP_OPAQUE);
				Screen->DrawInteger(6, 108, 32, FONT_Z3SMALL, 0x01, 0x0F, -1, -1, Game->Scrolling[SCROLL_NY], 0, OP_OPAQUE);
				Screen->DrawInteger(6, 12, 40, FONT_Z3SMALL, 0x01, 0x0F, -1, -1, Region->OriginScreenIndex, 0, OP_OPAQUE);
				Screen->DrawInteger(6, 36, 40, FONT_Z3SMALL, 0x01, 0x0F, -1, -1, Game->Scrolling[SCROLL_DIR], 0, OP_OPAQUE);
			}
			
			//Unmark flags telling the global not to reset it and the FFC not to reposition it
			// if(this->Data[j+_SBG_FLAGS]&_SBGF_NORESET)
			// this->Data[j+_SBG_FLAGS] &= ~_SBGF_NORESET;
			// if(this->Data[j+_SBG_FLAGS]&_SBGF_NOPOSITION)
			// this->Data[j+_SBG_FLAGS] &= ~_SBGF_NOPOSITION;
		}
	}
	
	int ScreenAt(int X, int Y){
		if (Game->Scrolling[SCROLL_DIR]>=0) return Game->HeroScreen;
		int screenX = (X - X%256)/256;
		int screenY = (Y - Y%176)/176;
		int origScreen = Game->CurScreen;
		return origScreen + 16*screenY + screenX;
	}
	
	int GlobalHeroX(genericdata this){
		if (Game->Scrolling[SCROLL_DIR]>=0) return this->Data[_SBG_LINKX];
		int X = CenterLinkX()%256;
		int screenID = ScreenAt(CenterLinkX(), CenterLinkY());
		int ScreenX = screenID%16;
		return ScreenX*256 + X;
	}
	
	int GlobalHeroY(genericdata this){
		if (Game->Scrolling[SCROLL_DIR]>=0) return this->Data[_SBG_LINKY];
		int Y = CenterLinkY() % 176;
		int screenID = ScreenAt(CenterLinkX(), CenterLinkY());
		int ScreenY =  Floor(screenID/16);
		return ScreenY*176+Y;
	}
	
	int GlobalViewportX(genericdata this){
		return ((this->Data[_SBG_OLD_SCREEN_ID]%16)*256)+Viewport->X;
	}
	
	int GlobalViewportY(genericdata this){
		return (Floor(this->Data[_SBG_OLD_SCREEN_ID]/16)*176)+Viewport->Y;
	}
	
	void ScrollingLayer_DrawLayer(int layer, int srcMap, int srcScreen, int x, int y, int w, int h, int op, bool forceDrawScreen){
		bool useDrawScreen = false;
		if(forceDrawScreen)	useDrawScreen = true;
		
		int tmpScreen;
		int scrnX;
		int scrnY;
		
		for(int 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 combo script registers scrolling layer to given slot, overwriting previous data in this slot.
	@Attribute0("Scroll Slot"), @AttributeHelp0("Which scroll slot this scrolling layer uses. (0-5)"),
	@Attribute1("Draw Layer"), @AttributeHelp1("Which layer used to render scrolling background. (0-7)"),
	@Attribute2("Source Map"), @AttributeHelp2("Which map used to draw scrolling layer from."),
	@Attribute3("Source Screen"), @AttributeHelp3("Which screen ID used to draw scrolling layer from. \n In case of using larger than 1x1 block of screens, ID is top left corner of that block."),
	@Attribute4("X Speed"), @AttributeHelp4("Background horizontal scrolling speed, in pixels per frame.\n In case of tracking scrolling, hero or viewport coordinates, this attribute is multiple of object speed."),
	@Attribute5("Y Speed"), @AttributeHelp5("Background vertical scrolling speed, in pixels per frame.\n In case of tracking scrolling, hero or viewport coordinates, this attribute is multiple of object speed."),	
	@Attribute6("Opacity"), @AttributeHelp6("Background opacity. \n 0 - Invisible, 64 - Translucent, 128 - Opaque."),
	@Attribute7("Init X"), @AttributeHelp7("Background scroll ininial X coordinate, relative to top left corner of the screen."),
	@Attribute8("Init Y"), @AttributeHelp8("Background scroll ininial Y coordinate, relative to top left corner of the screen."),
	@Attribute9("Width"), @AttributeHelp9("Background scroll width, in screens."),
	@Attribute10("Height"), @AttributeHelp10("Background scroll height, in screens."),
	
	@Flag0("Track Hero X"), @FlagHelp0("If set, this scroll slot tracks Hero`s X coordinate and adjusts scroll position accorfing to that."),
	@Flag1("Track Hero Y"), @FlagHelp1("If set, this scroll slot tracks Hero`s Y coordinate and adjusts scroll position accorfing to that."),
	@Flag2("Track Scroll X"), @FlagHelp2("If set, this scroll slot tracks X coordinate during trans-region scrolling and adjusts scroll position according to that."),
	@Flag3("Track Scroll Y"), @FlagHelp3("If set, this scroll slot tracks Y coordinate during trans-region scrolling and adjusts scroll position according to that."),
	@Flag4("Use Screen->DrawScreen"), @FlagHelp4("If set, this scroll slot always uses Screen->DrawScreen to render backround."),
	@Flag5("No reposition"), @FlagHelp5("If set, don`t reposition background, if it is already assigned and is running, \n when player re-enters screen with this combo placed without exiting DMap."),
	@Flag6("Track Viewport"), @FlagHelp6("If set, tracks viewport, instead of hero`s actual coordinates. \n Best used in scrolling regions."),
	@Flag7("Continue Point Offset"), @FlagHelp7("If set, initial position of scrolling background will be offset depending on delta between warp coordinates.\n Does not work unless Screen->Data->No Continue Here After Warp flag is set.")
	
	combodata script RegisterScrollingLayer{
		void run(){
			int whichScrollingLayer = this->Attributes[0];
			whichScrollingLayer = Clamp(whichScrollingLayer, 0, 5);
			int drawLayer =  this->Attributes[1];
			int srcMap = this->Attributes[2];
			int srcScreen = this->Attributes[3];
			int xStep = this->Attributes[4];
			int yStep = this->Attributes[5];
			int opacity = this->Attributes[6];
			int initX = this->Attributes[7];
			int initY = this->Attributes[8];
			int width = this->Attributes[9];
			int height = this->Attributes[10];
			
			bool trackX = this->Flags[0];
			bool trackY = this->Flags[1];
			bool trackScrollX = this->Flags[2];
			bool trackScrollY = this->Flags[3];
			bool ForceDrawScreen = this->Flags[4];
			bool NoReposition = this->Flags[5];
			bool TrackViewport = this->Flags[6];
			bool OffsetContinuePoint = this->Flags[7];
			
			int str[] = "ScrollingLayer";
			int scr = Game->GetGenericScript(str);
			genericdata gd = Game->LoadGenericData(scr);
			
			//Trace(gd-> DataSize);
			if (gd->DataSize<(_SBG_START+(whichScrollingLayer+1)*_SBG_BLOCK)){
				int err[] = "ScrollingLayer: Slot number (Combo Attribute 0) exceeds number of available slots (0-indexed). See InitD[0] in Scrolling Layer generic script. Also that script must have Run from Start checkbox on and Reload states checkboxes set for Reload, Continue, Change DMap and Change Level.";
				TraceS(err);
				Quit();
			}
			
			dmapdata dm = Game->LoadDMapData(Game->CurDMap);
			int X = GlobalHeroX(gd);
			int Y = GlobalHeroY(gd);
			int origScreen = MapToDMap(dm->Continue, Game->CurDMap);
			mapdata md = Game->LoadMapData(Game->CurMap,origScreen);
			int origX = (origScreen%16)*256+md->WarpReturnX[0];
			int origY = (Floor(origScreen/16))*176+md->WarpReturnY[0];
			int deltaX = X - origX;
			int deltaY = Y - origY;
			
			int i = _SBG_START+whichScrollingLayer*_SBG_BLOCK;
			int j = 0;
			if (!NoReposition || gd->Data[i+_SBG_MAP]!=srcMap){
				if (Screen->Flag[SFL_NO_CONTINUE_HERE] && OffsetContinuePoint){
					initX += deltaX*xStep;
					initY += deltaY*yStep;
				}
				gd->Data[i+_SBG_X] = initX;
				gd->Data[i+_SBG_Y] = initY;
			}
			
			gd->Data[i+_SBG_XSTEP] = xStep;
			gd->Data[i+_SBG_YSTEP] = yStep;
			gd->Data[i+_SBG_MAP] = srcMap;
			gd->Data[i+_SBG_SCREEN] = srcScreen;
			gd->Data[i+_SBG_LAYER] = drawLayer;
			gd->Data[i+_SBG_OPACITY] = opacity;
			
			
			srcMap = Clamp(srcMap, 1, 256);
			width = Clamp(width, 1, 16);
			height = Clamp(height, 1, 8);
			
			gd->Data[i+_SBG_WIDTH] = width;
			gd->Data[i+_SBG_HEIGHT] = height;
			
			
			
			//Assign the flag that tells the global not to reset gd index, as well as any others specified
			gd->Data[i+_SBG_FLAGS]=0;
			
			if(trackX)
			gd->Data[i+_SBG_FLAGS] |= _SBGF_TRACKX;
			if(trackY)
			gd->Data[i+_SBG_FLAGS] |= _SBGF_TRACKY;
			if(trackScrollX)
			gd->Data[i+_SBG_FLAGS] |= _SBGF_TRACKXSCROLL;
			if(trackScrollY)
			gd->Data[i+_SBG_FLAGS] |= _SBGF_TRACKYSCROLL;
			if(ForceDrawScreen)
			gd->Data[i+_SBG_FLAGS] |= _SBGF_FORCEDRAWSCREEN;
			if (TrackViewport)
			gd->Data[i+_SBG_FLAGS] |= _SBGF_TRACK_VIEWPORT;
			gd->Data[i+_SBG_DMAP] = Game->CurDMap;			
		}
	}
	
	//Clears the giver srolling background slot
	@Attribute0("Scroll Slot"), @AttributeHelp0("Which scroll slot to clear. (0-5)")
	combodata script ClearScrollingLayer{
		void run(){
			int whichScrollingLayer = this->Attributes[0];
			int i = _SBG_START+whichScrollingLayer*_SBG_BLOCK;
			int j = 0;
			
			int str[] = "ScrollingLayer";
			int scr = Game->GetGenericScript(str);
			genericdata gd = Game->LoadGenericData(scr);
			
			gd->Data[i+_SBG_X] = 0;
			gd->Data[i+_SBG_Y] = 0;			
			gd->Data[i+_SBG_XSTEP] = 0;
			gd->Data[i+_SBG_YSTEP] = 0;
			gd->Data[i+_SBG_MAP] = 0;
			gd->Data[i+_SBG_SCREEN] = 0;
			gd->Data[i+_SBG_LAYER] = 0;
			gd->Data[i+_SBG_OPACITY] = 0;			
			gd->Data[i+_SBG_WIDTH] = 1;
			gd->Data[i+_SBG_HEIGHT] = 1;			
			gd->Data[i+_SBG_FLAGS] = 0;			
			gd->Data[i+_SBG_DMAP] = 0;
			
		}
	}
}