Copy to Clipboard Test

Metroidvania Minimap Code

//Used for defining minimap sectors on a large map
void Minimap_GetSectorDefinitions(){
	//MiniMap_DefineSector(int map, int sector, int dmap, int xoff, int yoff)
	//Safe ranges for 16x8 maps:
	//		X: -16, 16
	//		Y: -20, 20
}

const int MINIMAP_LAYER = 7; //Layer the minimap is drawn to
//X and Y position of the minimap on the subscreen
const int MINIMAP_X = 27;
const int MINIMAP_Y = 16;
const int MINIMAP_SQUARESIZE = 6; //Width/Height of a minimap square in pixels
//Width and height of the minimap in screens
const int MINIMAP_TILEWIDTH = 7;
const int MINIMAP_TILEHEIGHT = 5;

const int MINIMAP_MAPWIDTH = 16; //Max width of a map 

const int MINIMAP_MAPS = 10; //First of 4 maps used for minimaps
const int MINIMAP_MARKERMAPS = 14; //First of 4 maps used for minimap markers

const int MINIMAP_USELEVELNUM = 1; //Set to 1 to use level numbers instead of DMaps for getting reference maps
const int MINIMAP_CLAMPPOSITION = 0; //Set to 1 to clamp the position of the minimap to the play area, if 0, Link's position will always be centered
const int MINIMAP_MARKERSREQUIREVISITED = 1; //Set to 1 to make it so markers require you have visited the screen to display
const int MINIMAP_REQUIREDUNGEONMAP = 1; //Set to 1 to require the dungeon map to view unexplored (not hidden) rooms
const int MINIMAP_REQUIRECOMPASS = 1; //Set to 1 to require the compass to view map markers

const int MINIMAP_LARGEMAP_ENABLED = 1; //Set to 1 to enable opening the large map with the "Map" button
const int MINIMAP_LARGEMAP_OPENCLOSEFRAMES = 32; //Frames taken to open the large map
const int MINIMAP_LARGEMAP_FRAMEDRAWCAP = 500; //Rough maximum of draw instructions to run per frame when loading the large map
const int MINIMAP_LARGEMAP_DEBUG = 0; //Set to 1 to show all hidden squares on the large map

const int C_MINIMAP_OUTLINE = 0x01; //Outline color for the minimap
const int C_MINIMAP_BG = 0x0F; //Background color for the minimap
const int C_MINIMAP_DEBUG = 0x02; //Color used for debug draws

const int CF_HIDDENROOM = 98; //Combo flag marking minimap screens as hidden

const int DMF_ENABLEMINIMAP = 1; //DMap flag used for minimaps (1-5, Script 1 by default)

//Two off-screen bitmaps (0-6) to use for drawing the large map
const int RT_LARGEMAP1 = 0;
const int RT_LARGEMAP2 = 1;

const int FFC_SCREENFREEZEA = 31;
const int CMB_SCREENFREEZEA = 2;
const int FFC_SCREENFREEZEB = 32;
const int CMB_SCREENFREEZEB = 3;

//Functions for freezing/unfreezing the screen
void FreezeScreen(){
	ffc a = Screen->LoadFFC(FFC_SCREENFREEZEA);
	a->Data = CMB_SCREENFREEZEA;
	ffc b = Screen->LoadFFC(FFC_SCREENFREEZEB);
	b->Data = CMB_SCREENFREEZEB;
}
void UnfreezeScreen(){
	ffc a = Screen->LoadFFC(FFC_SCREENFREEZEA);
	a->Data = 0;
	ffc b = Screen->LoadFFC(FFC_SCREENFREEZEB);
	b->Data = 0;
}

const int _LML_BGCMB = 0;
const int _LML_BGCS = 2304;
const int _LML_LAYOUTCMB = 4608;
const int _LML_LAYOUTCS = 6912;
const int _LML_MARKERCMB = 9216;
const int _LML_MARKERCS = 11520;

int LargeMapLayout[13824]; //Size should be 48*48*6

//Indices for global variables
const int _LMD_CURDRAWINDEX = 0;
const int _LMD_DRAWCOUNT = 1;
const int _LMD_MINX = 2;
const int _LMD_MAXX = 3;
const int _LMD_MINY = 4;
const int _LMD_MAXY = 5;
const int _LMD_LINKPOS = 6;
const int _LMD_LARGEMAPSTATE = 7;
const int _LMD_LARGEMAPFRAMES = 8;
const int _LMD_MAPVIEWX = 9;
const int _LMD_MAPVIEWY = 10;
const int _LMD_MAPDEFS = 16;

//Indices for map sectors (3-dimensional, map x sector x index)
const int _LMD_DMAP = 0;
const int _LMD_XOFF = 1;
const int _LMD_YOFF = 2;
const int _LMD_SECTORSIZE = 3;
//
const int _LMD_MAXMAPS = 4; //Max number of large maps
const int _LMD_MAXMAPSECTORS = 32; //Max number of sectors for maps

int LargeMapData[400]; //Size should be at least _LMD_MAPDEFS+_LMD_MAXMAPS*_LMD_MAXMAPSECTORS*_LMD_SECTORSIZE

void MiniMap_Init(){
	LargeMapData[_LMD_LARGEMAPSTATE] = 0;
	for(int i=0; i<_LMD_MAXMAPS*_LMD_MAXMAPSECTORS; ++i){
		LargeMapData[_LMD_MAPDEFS+_LMD_SECTORSIZE*i+_LMD_DMAP] = -1;
	}
	Minimap_GetSectorDefinitions();
}

//Defines a single sector in the large map
void MiniMap_DefineSector(int map, int sector, int dmap, int xoff, int yoff){
	LargeMapData[_LMD_MAPDEFS+_LMD_MAXMAPSECTORS*_LMD_SECTORSIZE*map+_LMD_SECTORSIZE*sector+_LMD_DMAP] = dmap;
	LargeMapData[_LMD_MAPDEFS+_LMD_MAXMAPSECTORS*_LMD_SECTORSIZE*map+_LMD_SECTORSIZE*sector+_LMD_XOFF] = 16+xoff;
	LargeMapData[_LMD_MAPDEFS+_LMD_MAXMAPSECTORS*_LMD_SECTORSIZE*map+_LMD_SECTORSIZE*sector+_LMD_YOFF] = 20+yoff;
}

void Minimap_Update(){
	//Don't bother with either map if the flag is checked
	if(!(Game->DMapFlags[Game->GetCurDMap()]&(1<<(10+DMF_ENABLEMINIMAP)))||Game->GetCurScreen()>=0x80){
		if(MINIMAP_LARGEMAP_ENABLED){
			Link->InputMap = false; Link->PressMap = false;
		}
		return;
	}
	
	Minimap_UpdateMapSubscreen();
	
	//Don't bother with the minimap if the large map is open
	if(LargeMapData[_LMD_LARGEMAPSTATE]!=0)
		return;
	
	int refMap; int refMarkerMap; int refScrn;
	//Get the maps to reference for map drawing
	if(MINIMAP_USELEVELNUM){
		refMap = MINIMAP_MAPS+Floor(Game->GetCurLevel()/128);
		refMarkerMap = MINIMAP_MARKERMAPS+Floor(Game->GetCurLevel()/128);
		refScrn = Game->GetCurLevel()%128;
	}
	else{
		refMap = MINIMAP_MAPS+Floor(Game->GetCurDMap()/128);
		refMarkerMap = MINIMAP_MARKERMAPS+Floor(Game->GetCurDMap()/128);
		refScrn = Game->GetCurDMap()%128;
	}
	
	int dmapOffset = Game->DMapOffset[Game->GetCurDMap()];
	
	//Get the map X and Y position of the top-left corner of the minimap
	int offX = (Game->GetCurDMapScreen()%16)-Floor((MINIMAP_TILEWIDTH-1)/2);
	int offY = Floor(Game->GetCurDMapScreen()/16)-Floor((MINIMAP_TILEHEIGHT-1)/2);
	if(MINIMAP_CLAMPPOSITION){
		offX = Clamp(offX, 0, MINIMAP_MAPWIDTH-MINIMAP_TILEWIDTH);
		offY = Clamp(offY, 0, 8-MINIMAP_TILEHEIGHT);
	}
	
	int bgCD = Game->GetComboData(refMap, refScrn, 160);
	
	int roomX; int roomY; int roomPos; 
	int roomVisited; int markerState;
	int roomCD; int roomCS; int roomCF;
	int markerCD; int markerCS; int markerCT;
	
	//Draw the rectangle background
	if(C_MINIMAP_BG)
		Screen->Rectangle(MINIMAP_LAYER, MINIMAP_X, MINIMAP_Y-56, MINIMAP_X+MINIMAP_SQUARESIZE*MINIMAP_TILEWIDTH, MINIMAP_Y-56+MINIMAP_SQUARESIZE*MINIMAP_TILEHEIGHT, C_MINIMAP_BG, 1, 0, 0, 0, true, 128);
	
	//Cycle through all minimap squares to draw
	for(int x=0; x<MINIMAP_TILEWIDTH; ++x){
		for(int y=0; y<MINIMAP_TILEHEIGHT; ++y){
			roomX = offX+x;
			roomY = offY+y;
			roomPos = roomX+roomY*16;
			if(roomX>=0&&roomX<=MINIMAP_MAPWIDTH-1&&roomY>=0&&roomY<=7){
				roomCD = Game->GetComboData(refMap, refScrn, roomPos);
				roomCS = Game->GetComboCSet(refMap, refScrn, roomPos);
				roomCF = Game->GetComboFlag(refMap, refScrn, roomPos);
				markerCD = Game->GetComboData(refMarkerMap, refScrn, roomPos);
				markerCS = Game->GetComboCSet(refMarkerMap, refScrn, roomPos);
				markerCT = Game->GetComboType(refMarkerMap, refScrn, roomPos);
				roomVisited = 0;
				markerState = 0;
				
				//Check if Link has visited or is in this screen
				if(roomPos==Game->GetCurDMapScreen())
					roomVisited = 2;
				else if(roomPos+dmapOffset<0x80&&Game->GetScreenState(Game->GetCurMap(), roomPos+dmapOffset, ST_VISITED))
					roomVisited = 1;
				//Hidden rooms are marked with a flag so they're visible in the editor but not in normal play until visited
				if(roomCF==CF_HIDDENROOM&&roomVisited==0)
					roomCD = 0;
				//Prevent showing unvisited rooms without the map
				if(!(Game->LItems[Game->GetCurLevel()]&LI_MAP)&&MINIMAP_REQUIREDUNGEONMAP&&roomVisited==0)
					roomCD = 0;
				//If the combo at that position >0, this is a valid screen to draw
				if(roomCD){
					//If the background combo was set, draw that under the screen first
					if(bgCD){
						Screen->FastCombo(MINIMAP_LAYER, MINIMAP_X+x*MINIMAP_SQUARESIZE, MINIMAP_Y-56+y*MINIMAP_SQUARESIZE, bgCD-1+roomVisited, roomCS, 128);
						Screen->FastCombo(MINIMAP_LAYER, MINIMAP_X+x*MINIMAP_SQUARESIZE, MINIMAP_Y-56+y*MINIMAP_SQUARESIZE, roomCD, roomCS, 128);
					}
					else
						Screen->FastCombo(MINIMAP_LAYER, MINIMAP_X+x*MINIMAP_SQUARESIZE, MINIMAP_Y-56+y*MINIMAP_SQUARESIZE, roomCD+roomVisited, roomCS, 128);
					//Check for markers on the layer screen
					if(markerCD&&(roomVisited||!MINIMAP_MARKERSREQUIREVISITED)&&(Game->LItems[Game->GetCurLevel()]&LI_COMPASS||!MINIMAP_REQUIRECOMPASS)&&roomPos+dmapOffset<0x80){
						//Item or Special Item
						if(markerCT==CT_CHEST){
							if(Game->GetScreenState(Game->GetCurMap(), roomPos+dmapOffset, ST_ITEM)||Game->GetScreenState(Game->GetCurMap(), roomPos+dmapOffset, ST_SPECIALITEM))
								markerState = 1;
						}
						//Item and Special Item (4 states)
						else if(markerCT==CT_BOSSCHEST){
							if(Game->GetScreenState(Game->GetCurMap(), roomPos+dmapOffset, ST_SPECIALITEM))
								++markerState;
							if(Game->GetScreenState(Game->GetCurMap(), roomPos+dmapOffset, ST_ITEM))
								markerState += 2;
						}
						//Boss level state
						else if(markerCT==CT_DAMAGE1){
							if(Game->LItems[Game->GetCurLevel()]&LI_BOSS)
								markerState = 1;
						}
						//Lock block
						else if(markerCT==CT_LOCKBLOCK){
							if(Game->GetScreenState(Game->GetCurMap(), roomPos+dmapOffset, ST_LOCKBLOCK))
								markerState = 1;
						}
						//Boss lock
						else if(markerCT==CT_BOSSLOCKBLOCK){
							if(Game->GetScreenState(Game->GetCurMap(), roomPos+dmapOffset, ST_BOSSLOCKBLOCK))
								markerState = 1;
						}
						//Secret combo
						else if(markerCT==CT_STEP){
							if(Game->GetScreenState(Game->GetCurMap(), roomPos+dmapOffset, ST_SECRET))
								markerState = 1;
						}
						Screen->FastCombo(MINIMAP_LAYER, MINIMAP_X+x*MINIMAP_SQUARESIZE, MINIMAP_Y-56+y*MINIMAP_SQUARESIZE, markerCD+markerState, markerCS, 128);
					}
				}
			}
		}
	}

	//Draw the rectangle outline
	if(C_MINIMAP_OUTLINE)
		Screen->Rectangle(MINIMAP_LAYER, MINIMAP_X, MINIMAP_Y-56, MINIMAP_X+MINIMAP_SQUARESIZE*MINIMAP_TILEWIDTH, MINIMAP_Y-56+MINIMAP_SQUARESIZE*MINIMAP_TILEHEIGHT, C_MINIMAP_OUTLINE, 1, 0, 0, 0, false, 128);
}

void Minimap_UpdateMapSubscreen(){
	int i; int j;
	if(!MINIMAP_LARGEMAP_ENABLED)
		return;
	if(LargeMapData[_LMD_LARGEMAPSTATE]==0){ //Map is closed
		if(Link->PressMap){
			LargeMapData[_LMD_LARGEMAPSTATE] = 1;
			LargeMapData[_LMD_LARGEMAPFRAMES] = 0;
			FreezeScreen();
			int currentMap = Minimap_GetCurrentLargeMap();
			for(i=0; i<2304; ++i){
				LargeMapLayout[_LML_BGCMB+i] = 0;
				LargeMapLayout[_LML_LAYOUTCMB+i] = 0;
				LargeMapLayout[_LML_MARKERCMB+i] = 0;
			}
			//If the current DMap isn't in the map array, just load that one
			if(currentMap==-1){
				Minimap_Overlay(Game->GetCurDMap(), 16, 20);
			}
			//Else load every other DMap in the same large map
			else{
				for(i=0; i<_LMD_MAXMAPSECTORS; ++i){
					int dmap = LargeMapData[_LMD_MAPDEFS+_LMD_MAXMAPSECTORS*_LMD_SECTORSIZE*currentMap+_LMD_SECTORSIZE*i+_LMD_DMAP]; 
					if(dmap>-1){
						int xoff = LargeMapData[_LMD_MAPDEFS+_LMD_MAXMAPSECTORS*_LMD_SECTORSIZE*currentMap+_LMD_SECTORSIZE*i+_LMD_XOFF];
						int yoff = LargeMapData[_LMD_MAPDEFS+_LMD_MAXMAPSECTORS*_LMD_SECTORSIZE*currentMap+_LMD_SECTORSIZE*i+_LMD_YOFF];
						Minimap_Overlay(dmap, xoff, yoff);
					}
				}
			}
			Screen->SetRenderTarget(RT_LARGEMAP1);
			Screen->Rectangle(0, 0, 0, 511, 511, 0x00, 1, 0, 0, 0, true, 128);
			Screen->SetRenderTarget(RT_LARGEMAP2);
			Screen->Rectangle(0, 0, 0, 511, 511, 0x00, 1, 0, 0, 0, true, 128);
			LargeMapData[_LMD_CURDRAWINDEX] = 0;
			LargeMapData[_LMD_DRAWCOUNT] = 0;
			LargeMapData[_LMD_MINX] = -1;
			LargeMapData[_LMD_MINY] = -1;
			LargeMapData[_LMD_MAXX] = -1;
			LargeMapData[_LMD_MAXY] = -1;
		}
	}
	else if(LargeMapData[_LMD_LARGEMAPSTATE]==1){ //Map is opening
		Minimap_LoadLargeMapData();
		if(LargeMapData[_LMD_LARGEMAPFRAMES]>MINIMAP_LARGEMAP_OPENCLOSEFRAMES*0.6666){
			Screen->Rectangle(MINIMAP_LAYER, 0, -56, 255, 175, C_MINIMAP_BG, 1, 0, 0, 0, true, 128);
		}
		else{
			if(LargeMapData[_LMD_LARGEMAPFRAMES]>MINIMAP_LARGEMAP_OPENCLOSEFRAMES*0.3333)
				Screen->Rectangle(MINIMAP_LAYER, 0, -56, 255, 175, C_MINIMAP_BG, 1, 0, 0, 0, true, 64);
			Screen->Rectangle(MINIMAP_LAYER, 0, -56, 255, 175, C_MINIMAP_BG, 1, 0, 0, 0, true, 64);
		}
		if(LargeMapData[_LMD_LARGEMAPFRAMES]<MINIMAP_LARGEMAP_OPENCLOSEFRAMES)
			++LargeMapData[_LMD_LARGEMAPFRAMES];
		else{
			LargeMapData[_LMD_LARGEMAPSTATE] = 2;
			//Position the camera in the center of the map
			LargeMapData[_LMD_MAPVIEWX] = Floor((LargeMapData[_LMD_MINX]+LargeMapData[_LMD_MAXX]+1)*MINIMAP_SQUARESIZE/2)-128;
			LargeMapData[_LMD_MAPVIEWY] = Floor((LargeMapData[_LMD_MINY]+LargeMapData[_LMD_MAXY]+1)*MINIMAP_SQUARESIZE/2)-128;
		}
	}
	else if(LargeMapData[_LMD_LARGEMAPSTATE]==2){ //Map is open
		Screen->Rectangle(MINIMAP_LAYER, 0, -56, 255, 175, C_MINIMAP_BG, 1, 0, 0, 0, true, 128);
		Minimap_DrawBitmaps(LargeMapData[_LMD_MAPVIEWX], LargeMapData[_LMD_MAPVIEWY]);
		if(Link->PressMap){
			LargeMapData[_LMD_LARGEMAPSTATE] = 3;
			LargeMapData[_LMD_LARGEMAPFRAMES] = MINIMAP_LARGEMAP_OPENCLOSEFRAMES;
		}
		//Allow the camera to pan around
		LargeMapData[_LMD_MAPVIEWX] += Cond(Link->InputLeft, -2, 0) + Cond(Link->InputRight, 2, 0);
		LargeMapData[_LMD_MAPVIEWY] += Cond(Link->InputUp, -2, 0) + Cond(Link->InputDown, 2, 0);
		int edgeDist = 48;
		if(LargeMapData[_LMD_MAPVIEWX]<LargeMapData[_LMD_MINX]*MINIMAP_SQUARESIZE-edgeDist&&LargeMapData[_LMD_MAPVIEWX]>(LargeMapData[_LMD_MAXX]+1)*MINIMAP_SQUARESIZE+edgeDist-256)
			LargeMapData[_LMD_MAPVIEWX] = Floor((LargeMapData[_LMD_MINX]+LargeMapData[_LMD_MAXX]+1)*MINIMAP_SQUARESIZE/2)-128;
		else if(LargeMapData[_LMD_MAPVIEWX]<LargeMapData[_LMD_MINX]*MINIMAP_SQUARESIZE-edgeDist)
			LargeMapData[_LMD_MAPVIEWX] = LargeMapData[_LMD_MINX]*MINIMAP_SQUARESIZE-edgeDist;
		else if(LargeMapData[_LMD_MAPVIEWX]>(LargeMapData[_LMD_MAXX]+1)*MINIMAP_SQUARESIZE+edgeDist-256)
			LargeMapData[_LMD_MAPVIEWX] = (LargeMapData[_LMD_MAXX]+1)*MINIMAP_SQUARESIZE+edgeDist-256;
		
		if(LargeMapData[_LMD_MAPVIEWY]<LargeMapData[_LMD_MINY]*MINIMAP_SQUARESIZE-edgeDist&&LargeMapData[_LMD_MAPVIEWY]>(LargeMapData[_LMD_MAXY]+1)*MINIMAP_SQUARESIZE+24+edgeDist-256)
			LargeMapData[_LMD_MAPVIEWY] = Floor((LargeMapData[_LMD_MINY]+LargeMapData[_LMD_MAXY]+1)*MINIMAP_SQUARESIZE/2)-128;
		else if(LargeMapData[_LMD_MAPVIEWY]<LargeMapData[_LMD_MINY]*MINIMAP_SQUARESIZE-edgeDist)
			LargeMapData[_LMD_MAPVIEWY] = LargeMapData[_LMD_MINY]*MINIMAP_SQUARESIZE-edgeDist;
		else if(LargeMapData[_LMD_MAPVIEWY]>(LargeMapData[_LMD_MAXY]+1)*MINIMAP_SQUARESIZE+24+edgeDist-256)
			LargeMapData[_LMD_MAPVIEWY] = (LargeMapData[_LMD_MAXY]+1)*MINIMAP_SQUARESIZE+24+edgeDist-256;
		
	}
	else if(LargeMapData[_LMD_LARGEMAPSTATE]==3){ //Map is closed
		if(LargeMapData[_LMD_LARGEMAPFRAMES]>MINIMAP_LARGEMAP_OPENCLOSEFRAMES*0.6666){
			Screen->Rectangle(MINIMAP_LAYER, 0, -56, 255, 175, C_MINIMAP_BG, 1, 0, 0, 0, true, 128);
		}
		else{
			if(LargeMapData[_LMD_LARGEMAPFRAMES]>MINIMAP_LARGEMAP_OPENCLOSEFRAMES*0.3333)
				Screen->Rectangle(MINIMAP_LAYER, 0, -56, 255, 175, C_MINIMAP_BG, 1, 0, 0, 0, true, 64);
			Screen->Rectangle(MINIMAP_LAYER, 0, -56, 255, 175, C_MINIMAP_BG, 1, 0, 0, 0, true, 64);
		}
		if(LargeMapData[_LMD_LARGEMAPFRAMES])
			--LargeMapData[_LMD_LARGEMAPFRAMES];
		else{
			LargeMapData[_LMD_LARGEMAPSTATE] = 0;
			UnfreezeScreen();
		}
	}
	Screen->SetRenderTarget(RT_SCREEN);
	Link->InputMap = false; Link->PressMap = false;
}

void Minimap_DrawBitmaps(int x, int y){
	int origX = x;
	int origY = y;
	int sx = 0;
	int sy = 0;
	int w = 256;
	int h = 256;
	int offsc;
	//Allow the bitmap draws to go off the edges of the bitmap.
	//I don't like this math and I don't trust it.
	//Me and spatial awareness never got along great.
	if(x<0){
		offsc = -x;
		x = 0;
		sx += offsc;
		w -= offsc;
	}
	else if(x+w-1>511){
		offsc = ((x+w-1)-511);
		w -= offsc;
	}
	if(y<0){
		offsc = -y;
		y = 0;
		sy += offsc;
		h -= offsc;
	}
	else if(y+h-1>511){
		offsc = ((y+h-1)-511);
		h -= offsc;
	}
		
	//Draw Link's position to the bitmap so it can animate (this is the only exception for animated icons on the large map)
	Screen->SetRenderTarget(RT_LARGEMAP1);
	int linkPos = LargeMapData[_LMD_LINKPOS];
	int roomX = (linkPos%48)*MINIMAP_SQUARESIZE;
	int roomY = Floor(linkPos/48)*MINIMAP_SQUARESIZE;
	if(LargeMapLayout[_LML_LAYOUTCMB+linkPos]){
		if(LargeMapLayout[_LML_BGCMB+linkPos]){
			Screen->FastCombo(0, roomX, roomY, LargeMapLayout[_LML_BGCMB+linkPos]+1, LargeMapLayout[_LML_LAYOUTCS+linkPos], 128);
			Screen->FastCombo(0, roomX, roomY, LargeMapLayout[_LML_LAYOUTCMB+linkPos], LargeMapLayout[_LML_LAYOUTCS+linkPos], 128);
		}
		else{
			Screen->FastCombo(0, roomX, roomY, LargeMapLayout[_LML_LAYOUTCMB+linkPos]+1, LargeMapLayout[_LML_LAYOUTCS+linkPos], 128);
		}
	}
	Screen->SetRenderTarget(RT_SCREEN);
	Screen->DrawBitmap(MINIMAP_LAYER, RT_LARGEMAP1, x, y, w, h, sx, -56+sy, w, h, 0, true);
	Screen->DrawBitmap(MINIMAP_LAYER, RT_LARGEMAP2, x, y, w, h, sx, -56+sy, w, h, 0, true);
}

int Minimap_GetCurrentLargeMap(){
	for(int i=0; i<_LMD_MAXMAPS; ++i){
		for(int j=0; j<_LMD_MAXMAPSECTORS; ++j){
			if(LargeMapData[_LMD_MAPDEFS+_LMD_MAXMAPSECTORS*_LMD_SECTORSIZE*i+_LMD_SECTORSIZE*j+_LMD_DMAP]==Game->GetCurDMap())
				return i;
		}
	}
	return -1;
}

void Minimap_Overlay(int dmap, int xoff, int yoff){
	int refMap; int refMarkerMap; int refScrn;
	int level = Game->DMapLevel[dmap];
	//Get the maps to reference for map drawing
	if(MINIMAP_USELEVELNUM){
		refMap = MINIMAP_MAPS+Floor(level/128);
		refMarkerMap = MINIMAP_MARKERMAPS+Floor(level/128);
		refScrn = level%128;
	}
	else{
		refMap = MINIMAP_MAPS+Floor(dmap/128);
		refMarkerMap = MINIMAP_MARKERMAPS+Floor(dmap/128);
		refScrn = dmap%128;
	}
	
	int map = Game->DMapMap[dmap];
	int dmapOffset = Game->DMapOffset[dmap];
	
	int bgCD = Game->GetComboData(refMap, refScrn, 160);
	
	int xpos;
	int totalX; int totalY;
	int roomX;
	int roomVisited; int markerState;
	int roomCD; int roomCS; int roomCF;
	int markerCD; int markerCS; int markerCT;
	
	for(int i=0; i<128; ++i){
		xpos = i%16+dmapOffset;
		roomCD = Game->GetComboData(refMap, refScrn, i);
		if(roomCD&&xpos>=0&&xpos<=15){
			totalX = (i%16)+xoff;
			totalY = Floor(i/16)+yoff;
			roomX = (i%16)+dmapOffset;
			if(totalX>=0&&totalX<=47&&totalY>=0&&totalY<=47&&roomX>=0&&roomX<=15){
				roomCS = Game->GetComboCSet(refMap, refScrn, i);
				roomCF = Game->GetComboFlag(refMap, refScrn, i);
				markerCD = Game->GetComboData(refMarkerMap, refScrn, i);
				markerCS = Game->GetComboCSet(refMarkerMap, refScrn, i);
				markerCT = Game->GetComboType(refMarkerMap, refScrn, i);
				roomVisited = 0;
				markerState = 0;
				//Check if Link has visited or is in this screen
				if(dmap==Game->GetCurDMap()&&i==Game->GetCurDMapScreen())
					LargeMapData[_LMD_LINKPOS] = ((i%16)+xoff)+48*(Floor(i/16)+yoff);
				if(Game->GetScreenState(map, i+dmapOffset, ST_VISITED)&&i+dmapOffset>-1)
					roomVisited = 1;
				//Hidden rooms are marked with a flag so they're visible in the editor but not in normal play until visited
				if(roomCF==CF_HIDDENROOM&&roomVisited==0)
					roomCD = 0;
				//Prevent showing unvisited rooms without the map
				if(!(Game->LItems[level]&LI_MAP)&&MINIMAP_REQUIREDUNGEONMAP&&roomVisited==0)
					roomCD = 0;
				if(roomCD){
					//If the background combo was set, draw that under the screen first
					if(bgCD){
						LargeMapLayout[_LML_BGCMB+totalX+48*totalY] = bgCD-1+roomVisited;
						LargeMapLayout[_LML_LAYOUTCMB+totalX+48*totalY] = roomCD;
						LargeMapLayout[_LML_LAYOUTCS+totalX+48*totalY] = roomCS;
					}
					else{
						LargeMapLayout[_LML_LAYOUTCMB+totalX+48*totalY] = roomCD+roomVisited;
						LargeMapLayout[_LML_LAYOUTCS+totalX+48*totalY] = roomCS;
					}
					//Check for markers on the layer screen
					if(markerCD&&(roomVisited||!MINIMAP_MARKERSREQUIREVISITED)&&(Game->LItems[level]&LI_COMPASS||!MINIMAP_REQUIRECOMPASS)){
						//Item or Special Item
						if(markerCT==CT_CHEST){
							if(Game->GetScreenState(map, i+dmapOffset, ST_ITEM)||Game->GetScreenState(map, i+dmapOffset, ST_SPECIALITEM))
								markerState = 1;
						}
						//Item and Special Item (4 states)
						else if(markerCT==CT_BOSSCHEST){
							if(Game->GetScreenState(map, i+dmapOffset, ST_SPECIALITEM))
								++markerState;
							if(Game->GetScreenState(map, i+dmapOffset, ST_ITEM))
								markerState += 2;
						}
						//Boss level state
						else if(markerCT==CT_DAMAGE1){
							if(Game->LItems[Game->DMapLevel[dmap]]&LI_BOSS)
								markerState = 1;
						}
						//Lock block
						else if(markerCT==CT_LOCKBLOCK){
							if(Game->GetScreenState(map, i+dmapOffset, ST_LOCKBLOCK))
								markerState = 1;
						}
						//Boss lock
						else if(markerCT==CT_BOSSLOCKBLOCK){
							if(Game->GetScreenState(map, i+dmapOffset, ST_BOSSLOCKBLOCK))
								markerState = 1;
						}
						//Secret combo
						else if(markerCT==CT_STEP){
							if(Game->GetScreenState(map, i+dmapOffset, ST_SECRET))
								markerState = 1;
						}
						LargeMapLayout[_LML_MARKERCMB+totalX+48*totalY] = markerCD+markerState;
						LargeMapLayout[_LML_MARKERCS+totalX+48*totalY] = markerCS;
					}
				}
			}
		}
	}
}

void Minimap_LoadLargeMapData(){
	//No need to keep drawing if everything has already been drawn
	if(LargeMapData[_LMD_CURDRAWINDEX]==4608)
		return;
	int i;
	int x; int y;
	
	//Start with the layout layer
	Screen->SetRenderTarget(RT_LARGEMAP1);
	for(i=LargeMapData[_LMD_CURDRAWINDEX]; i<2304&&LargeMapData[_LMD_DRAWCOUNT]<=MINIMAP_LARGEMAP_FRAMEDRAWCAP; ++i){
		if(LargeMapLayout[_LML_LAYOUTCMB+i]){
			x = i%48;
			y = Floor(i/48);
			//If this is the first room discovered, set the edges up
			//This should prevent it from reading the top left corner as the edge when there's nothing there
			if(LargeMapData[_LMD_MINX]==-1){
				LargeMapData[_LMD_MINX] = x;
				LargeMapData[_LMD_MAXX] = x;
				LargeMapData[_LMD_MINY] = y;
				LargeMapData[_LMD_MAXY] = y;
			}
			//Record edges of the map space
			if(x<LargeMapData[_LMD_MINX])
				LargeMapData[_LMD_MINX] = x;
			if(x>LargeMapData[_LMD_MAXX])
				LargeMapData[_LMD_MAXX] = x;
			if(y<LargeMapData[_LMD_MINY])
				LargeMapData[_LMD_MINY] = y;
			if(y>LargeMapData[_LMD_MAXY])
				LargeMapData[_LMD_MAXY] = y;
			
			x = i%48*MINIMAP_SQUARESIZE;
			y = Floor(i/48)*MINIMAP_SQUARESIZE;
			if(LargeMapLayout[_LML_BGCMB+i]){
				Screen->FastCombo(0, x, y, LargeMapLayout[_LML_BGCMB+i], LargeMapLayout[_LML_LAYOUTCS+i], 128);
				++LargeMapData[_LMD_DRAWCOUNT];
			}
			Screen->FastCombo(0, x, y, LargeMapLayout[_LML_LAYOUTCMB+i], LargeMapLayout[_LML_LAYOUTCS+i], 128);
			++LargeMapData[_LMD_DRAWCOUNT];
		}
		else if(MINIMAP_LARGEMAP_DEBUG){
			x = i%48;
			y = Floor(i/48);
			//If this is the first room discovered, set the edges up
			//This should prevent it from reading the top left corner as the edge when there's nothing there
			if(LargeMapData[_LMD_MINX]==-1){
				LargeMapData[_LMD_MINX] = x;
				LargeMapData[_LMD_MAXX] = x;
				LargeMapData[_LMD_MINY] = y;
				LargeMapData[_LMD_MAXY] = y;
			}
			//Record edges of the map space
			if(x<LargeMapData[_LMD_MINX])
				LargeMapData[_LMD_MINX] = x;
			if(x>LargeMapData[_LMD_MAXX])
				LargeMapData[_LMD_MAXX] = x;
			if(y<LargeMapData[_LMD_MINY])
				LargeMapData[_LMD_MINY] = y;
			if(y>LargeMapData[_LMD_MAXY])
				LargeMapData[_LMD_MAXY] = y;
			
			x *= MINIMAP_SQUARESIZE;
			y *= MINIMAP_SQUARESIZE;
			Screen->Rectangle(0, x+2, y+2, x+MINIMAP_SQUARESIZE-2, y+MINIMAP_SQUARESIZE-2, C_MINIMAP_DEBUG, 1, 0, 0, 0, false, 128);
			++LargeMapData[_LMD_DRAWCOUNT];
		}
	}
	LargeMapData[_LMD_CURDRAWINDEX] = i;
	if(LargeMapData[_LMD_DRAWCOUNT]>MINIMAP_LARGEMAP_FRAMEDRAWCAP){
		Screen->SetRenderTarget(RT_SCREEN);
		LargeMapData[_LMD_DRAWCOUNT] = 0;
		return;
	}
	
	//Next draw the marker layer
	Screen->SetRenderTarget(RT_LARGEMAP2);
	for(i=LargeMapData[_LMD_CURDRAWINDEX]-2304; i<2304&&LargeMapData[_LMD_DRAWCOUNT]<=MINIMAP_LARGEMAP_FRAMEDRAWCAP; ++i){
		if(LargeMapLayout[_LML_MARKERCMB+i]){
			x = i%48*MINIMAP_SQUARESIZE;
			y = Floor(i/48)*MINIMAP_SQUARESIZE;
			Screen->FastCombo(0, x, y, LargeMapLayout[_LML_MARKERCMB+i], LargeMapLayout[_LML_MARKERCS+i], 128);
			++LargeMapData[_LMD_DRAWCOUNT];
		}
	}
	LargeMapData[_LMD_CURDRAWINDEX] = 2304+i;
	Screen->SetRenderTarget(RT_SCREEN);
	LargeMapData[_LMD_DRAWCOUNT] = 0;
}

global script MetroidvaniaMinimap_Example{
	void run(){
		MiniMap_Init();
		while(true){
			Minimap_Update();
			Waitdraw();
			Waitframe();
		}
	}
}