Copy to Clipboard Test

LttP/Oracle Dungeon Map Subscreen Code

//+====================================+
//|        MooshMap Constants          |
//+====================================+

// === SETTINGS ===
//These are TRUE / FALSE constants. Set the to true to toggle features on, or false to toggle them off.

const bool MOOSHMAP_VISITED_CARRYOVER = true; //Set to true to make which screens on a floor have been visited carry over
const bool MOOSHMAP_ALL_CARRYOVER = true; //Set to true to make the other screen states carry over
const bool MOOSHMAP_D_CARRYOVER = 0; //Set to true to make Screen->D carry over

const bool MOOSHMAP_HIGHLIGHTCURRENTROOM = true; //Set to true to highlight whichever room Link is in
const bool MOOSHMAP_DRAWLINKPOSITION = true; //Set to true to draw a Link marker in whichever screen Link is in
const bool MOOSHMAP_PRECISELINKPOSITION = true; //Set to true if the Link marker's position should be based on Link's (Like in ALttP)

const bool MOOSHMAP_SHOW_TITLE = true; //Set to true to show the DMap name on the subscreen
const bool MOOSHMAP_SHOW_LITEMS = true; //Set to true to show icons for the level items on the subscreen

//Okay this one actually isn't true/false. I lied.
const int MOOSHMAP_PRECISELINKPOSITIONBORDER = 1; //How thick the border around screens is (to keep the Link Marker from going offscreen)


// === NUMBER COMBOS ===
//These script uses a couple blocks of combos to mark decimal and hex numbers. These constants determine where those start and end.

const int CMB_DEC = 25856; //Combo of the first decimal combo
const int MAX_DEC = 511; //Decimal value of the last decimal combo

const int CMB_HEX = 25600; //Combo of the first hex combo
const int MAX_HEX = 0x8F; //Hex value of the last hex combo

// === SCREEN FREEZE ===
//These constants allow the script to freeze the screen.

const int CMB_SCREENFREEZEA = 2; //An invisible Screen Freeze (Except FFCs) combo
const int CMB_SCREENFREEZEB = 3; //An invisible Screen Freeze (FFCs Only) combo

//These two FFC slots will become screen freeze when the map is opened
const int FFC_SCREENFREEZEA = 31;
const int FFC_SCREENFREEZEB = 32;

// === GFX ===
//These constants are for graphical things, combos, csets, and colors that are used by the script

//Combo and CSet of the marker showing which floor Link is on
const int CMB_MOOSHMAP_LINKFLOORMARKER = 7876;
const int CS_MOOSHMAP_LINKFLOORMARKER = 6;

//Combo and CSet of Link's position marker
const int CMB_MOOSHMAP_LINKPOSITIONMARKER = 7877;
const int CS_MOOSHMAP_LINKPOSITIONMARKER = 6;

//Combo and CSet of the map icon on the subscreen
const int CMB_MOOSHMAP_MAP = 7828;
const int CS_MOOSHMAP_MAP = 6;

//Combo and CSet of the compass icon on the subscreen
const int CMB_MOOSHMAP_COMPASS = 7829;
const int CS_MOOSHMAP_COMPASS = 7;

//Combo and CSet of the boss key icon on the subscreen
const int CMB_MOOSHMAP_BOSSKEY = 7830;
const int CS_MOOSHMAP_BOSSKEY = 8;

const int C_WHITE = 0x01; //The color white
const int C_BLACK = 0x0F; //The color black

// === SFX ===
//Sounds

const int SFX_MAPSUBSCREEN_OPEN = 70; //SFX when the map is opened
const int SFX_MAPSUBSCREEN_CLOSE = 70; //SFX when the map is closed
const int SFX_MAPSUBSCREEN_FLOORCHANGE = 5; //SFX when the selected floor is changed

// === MAPS AND SCREENS ===
//The script references specific maps and screens in order to find map data.

//Map the script grabs the layout of level maps from. Each screen corresponds to a level number of the same value in decimal
const int MAP_MOOSHMAP_DATA = 1;

//Map and Screen for default map subscreen background
const int MAP_MOOSHMAP_BG = 1;
const int SCREEN_MOOSHMAP_BG = 0x87;

// === MISC CONSTANTS ===
//Other loose odds and ends

const int DMF_ALLOWMAP = 11; //DMap flag that enables the dungeon map. Script 1 by default.

//X,Y position of the map
const int MOOSHMAP_MAP_X = 120;
const int MOOSHMAP_MAP_Y = 24;
//Spacing between map tiles
const int MOOSHMAP_MAP_SQUARE_SCALE = 14;

//X,Y position of the center position for the floor listing
const int MOOSHMAP_FLOOR_X = 48;
const int MOOSHMAP_FLOOR_Y = 60;
//Width,Height of each floor combo
const int MOOSHMAP_FLOOR_WIDTH = 2;
const int MOOSHMAP_FLOOR_HEIGHT = 1;
//Y spacing between floor combos
const int MOOSHMAP_FLOOR_SPACING = 16;

//Font of the map title. See FONT_ in std_constants.zh
const int MOOSHMAP_TITLE_FONT = 1;
//X,Y position of the map title
const int MOOSHMAP_TITLE_X = 176;
const int MOOSHMAP_TITLE_Y = 148;

//Y,Y position of the map icon
const int MOOSHMAP_MAPICON_X = 32;
const int MOOSHMAP_MAPICON_Y = 140;

//Y,Y position of the compass icon
const int MOOSHMAP_COMPASSICON_X = 48;
const int MOOSHMAP_COMPASSICON_Y = 140;

//Y,Y position of the boss key icon
const int MOOSHMAP_BOSSKEYICON_X = 64;
const int MOOSHMAP_BOSSKEYICON_Y = 140;


// === INTERNALS ===
//DON'T CHANGE ANY CONSTANTS PAST THIS POINT.
//Everything past here is just to make the script more readable for me. 
//No more constants past this point need to be changed.

int MooshMap[163];

const int MM_NUMFLOORS = 0;
const int MM_LINKFLOOR = 1;
const int MM_SELECTEDFLOOR = 2;
const int MM_BGMAP = 3;
const int MM_BGSCREEN = 4;
const int MM_FLOORSWITCHFRAMES = 5;
const int MM_LASTDMAP = 6;
const int MM_LASTSCREEN = 7;
const int MM_LASTSCREENSTATES = 8;
const int MM_TITLEREFSTR = 9;

const int MM_STARTFLOORINDEX = 20;
const int MM_NUMFLOORINDEX = 11;

const int MM_F_FLOORCMB = 0;
const int MM_F_FLOORCS = 1;
const int MM_F_REFMAP = 2;
const int MM_F_REFSCREEN = 3;
const int MM_F_MARKERSCREEN = 4;
const int MM_F_MARKERSCREEN2 = 5;
const int MM_F_NUMFLOORS = 6;
const int MM_F_FLOORDMAPS = 7;

// === END OF INTERNALS ===

//+=======================================+
//|       MooshMap Generic Script         |
//+=======================================+
// Set this script to run from start in init data

generic script MooshMap_Global
{
	void run()
	{
		WaitTo(SCR_TIMING_POST_FFCS);
		MooshMap_Init();
		while(true)
		{
			WaitTo(SCR_TIMING_POST_FFCS);
			MooshMap_Update();
			Waitframe();
		}
	}
}

//Run in the first frame of the global
void MooshMap_Init(){
	MooshMap[MM_LASTDMAP] = Game->GetCurDMap();
	MooshMap[MM_LASTSCREEN] = Game->GetCurScreen();
	MooshMap[MM_LASTSCREENSTATES] = MooshMap_GetAllScreenStates();
}

//Run every frame in the global to run the map script
void MooshMap_Update(){
	if(MOOSHMAP_VISITED_CARRYOVER){
		MooshMap_UpdateCarryover();
	}
}

//Get a decimal number from a combo on a screen.
//If it's not a decimal marker combo, return -1.
int MooshMap_GetCMBDec(int map, int scrn, int pos){
	int cd = Game->GetComboData(map, scrn, pos);
	if(cd>=CMB_DEC&&cd<=CMB_DEC+MAX_DEC)
		return cd-CMB_DEC;
	return -1;
}

//Get a hexadecimal number from a combo on a screen.
//If it's not a hex marker combo, return -1.
int MooshMap_GetCMBHex(int map, int scrn, int pos){
	int cd = Game->GetComboData(map, scrn, pos);
	if(cd>=CMB_HEX&&cd<=CMB_HEX+MAX_HEX)
		return cd-CMB_HEX;
	return -1;
}

//Get map data from a screen based on the current Level
bool MooshMap_LoadLevelMapData(){
	if(Game->GetCurScreen()>=0x80)
		return false;
	
	int num;
	int level = Game->GetCurLevel();
	
	int dataMap = MAP_MOOSHMAP_DATA;
	int dataScreen = level;
	
	num = MooshMap_GetCMBDec(dataMap, dataScreen, 3);
	MooshMap[MM_BGMAP] = MAP_MOOSHMAP_BG;
	MooshMap[MM_BGSCREEN] = SCREEN_MOOSHMAP_BG;
	if(num>-1){ //If there's a custom background map set, store that
		MooshMap[MM_BGMAP] = num;
		num = MooshMap_GetCMBHex(dataMap, dataScreen, 4);
		if(num>-1) //Same for background screen
			MooshMap[MM_BGSCREEN] = num;
	}
	MooshMap[MM_NUMFLOORS] = 0;
	MooshMap[MM_SELECTEDFLOOR] = 0;
	MooshMap[MM_LINKFLOOR] = -1;
	for(int i=0; i<13; i++){ //Cycle through all valid floor slots
		num = MooshMap_GetCMBDec(dataMap, dataScreen, 51+i);
		if(num==-1) //If there's no valid ref map at this position
			break; //Break the loop
		else{ //Otherwise, assume this is a valid floor
			MooshMap[MM_NUMFLOORS]++;
			
			//Combo, CSet
			MooshMap[MM_STARTFLOORINDEX+MM_NUMFLOORINDEX*i+MM_F_FLOORCMB] = Game->GetComboData(MAP_MOOSHMAP_DATA, level, 35+i);
			MooshMap[MM_STARTFLOORINDEX+MM_NUMFLOORINDEX*i+MM_F_FLOORCS] = Game->GetComboCSet(MAP_MOOSHMAP_DATA, level, 35+i);
			
			//Ref Map (Also used earlier to check if it's a valid floor)
			MooshMap[MM_STARTFLOORINDEX+MM_NUMFLOORINDEX*i+MM_F_REFMAP] = num;
			
			//Ref Screen
			num = MooshMap_GetCMBHex(dataMap, dataScreen, 67+i);
			if(num==-1){ //Error
				printf("MOOSHMAP ERROR: Could not find reference screen at position %d.\n[MAP %d SCREEN %d]\n", 67+i, dataMap, dataScreen);
				return false;
			}
			MooshMap[MM_STARTFLOORINDEX+MM_NUMFLOORINDEX*i+MM_F_REFSCREEN] = num;
			
			//Marker Screen
			num = MooshMap_GetCMBHex(dataMap, dataScreen, 83+i);
			if(num==-1){ //Error
				printf("MOOSHMAP ERROR: Could not find marker screen at position %d.\n[MAP %d SCREEN %d]\n", 83+i, dataMap, dataScreen);
				return false;
			}
			MooshMap[MM_STARTFLOORINDEX+MM_NUMFLOORINDEX*i+MM_F_MARKERSCREEN] = num;
			
			//Marker Screen 2
			num = MooshMap_GetCMBHex(dataMap, dataScreen, 99+i);
			MooshMap[MM_STARTFLOORINDEX+MM_NUMFLOORINDEX*i+MM_F_MARKERSCREEN2] = num;
			
			//Floor DMap 1
			num = MooshMap_GetCMBDec(dataMap, dataScreen, 115+i);
			if(num==-1){ //Error
				printf("MOOSHMAP ERROR: Could not find floor DMap at position %d.\n[MAP %d SCREEN %d]\n", 115+i, dataMap, dataScreen);
				return false;
			}
			if(Game->GetCurDMap()==num){ //If Link is on the current DMap, set Link's floor and the current floor
				MooshMap[MM_LINKFLOOR] = i;
				MooshMap[MM_SELECTEDFLOOR] = i;
			}
			MooshMap[MM_STARTFLOORINDEX+MM_NUMFLOORINDEX*i+MM_F_FLOORDMAPS] = num;
			
			//Floor DMap 2 (Optional)
			num = MooshMap_GetCMBDec(dataMap, dataScreen, 131+i);
			if(Game->GetCurDMap()==num){ //If Link is on the current DMap, set Link's floor and the current floor
				MooshMap[MM_LINKFLOOR] = i;
				MooshMap[MM_SELECTEDFLOOR] = i;
			}
			MooshMap[MM_STARTFLOORINDEX+MM_NUMFLOORINDEX*i+MM_F_FLOORDMAPS+1] = num;
			
			//Floor DMap 3 (Optional)
			num = MooshMap_GetCMBDec(dataMap, dataScreen, 147+i);
			if(Game->GetCurDMap()==num){ //If Link is on the current DMap, set Link's floor and the current floor
				MooshMap[MM_LINKFLOOR] = i;
				MooshMap[MM_SELECTEDFLOOR] = i;
			}
			MooshMap[MM_STARTFLOORINDEX+MM_NUMFLOORINDEX*i+MM_F_FLOORDMAPS+2] = num;
			
			//Floor DMap 4 (Optional)
			num = MooshMap_GetCMBDec(dataMap, dataScreen, 163+i);
			if(Game->GetCurDMap()==num){ //If Link is on the current DMap, set Link's floor and the current floor
				MooshMap[MM_LINKFLOOR] = i;
				MooshMap[MM_SELECTEDFLOOR] = i;
			}
			MooshMap[MM_STARTFLOORINDEX+MM_NUMFLOORINDEX*i+MM_F_FLOORDMAPS+3] = num;
		}
	}
	if(MooshMap[MM_NUMFLOORS]==0){ //Can't have a map with 0 floors
		printf("MOOSHMAP ERROR: Map has no valid floors.\n[MAP %d SCREEN %d]\n", dataMap, dataScreen);
		return false;
	}
	return true; //Heyyy, we've loaded everything with no issue
}

//Draw the map and markers to various parts of the bitmap
void MooshMap_UpdateBitmap(bitmap b, int floor){
	int cmb; int cs; int flag; 
	int cmbMarker; int csMarker; int ctMarker; 
	int pos; 
	int i; int j; int k;
	bool test1; bool test2;

	b->Clear(0);
	
	int refMap = MooshMap[MM_STARTFLOORINDEX+MM_NUMFLOORINDEX*floor+MM_F_REFMAP];
	int refScreen = MooshMap[MM_STARTFLOORINDEX+MM_NUMFLOORINDEX*floor+MM_F_REFSCREEN];
	int markerScreen = MooshMap[MM_STARTFLOORINDEX+MM_NUMFLOORINDEX*floor+MM_F_MARKERSCREEN];
	
	//Find the first DMap associated with the current floor
	int dmap = MooshMap[MM_STARTFLOORINDEX+MM_NUMFLOORINDEX*floor+MM_F_FLOORDMAPS];
	if(MooshMap[MM_LINKFLOOR]==floor)
		dmap = Game->GetCurDMap();
	int srcMap = Game->DMapMap[dmap];
	int offset = Game->DMapOffset[dmap];
	
	bool visited[64];
	//Cycle through all screens once to find which have been visited
	for(i=0; i<64; i++){
		pos = i%8+Floor(i/8)*16;
		if(Game->GetScreenState(srcMap, pos+offset, ST_VISITED)){
			flag = Game->GetComboFlag(refMap, refScreen, pos);
			if(flag>0&&!visited[i]){ //If a combo has a flag, it's part of a screen group
				for(j=0; j<64; j++){ //Cycle through every screen again to
					pos = j%8+Floor(j/8)*16;
					if(Game->GetComboFlag(refMap, refScreen, pos)==flag) //All screens that share the flag should be marked as visited
						visited[j] = true;
				}
			}
			visited[i] = true;
		}
	}
	
	//Cycle through all the screens again and draw them this time
	for(i=0; i<64; i++){
		pos = i%8+Floor(i/8)*16;
		cmb = Game->GetComboData(refMap, refScreen, pos);
		cs = Game->GetComboCSet(refMap, refScreen, pos);
		
		if(Game->LItems[Game->GetCurLevel()]&LI_MAP) //If Link has the map, draw unvisited screens
			b->FastCombo(0, MOOSHMAP_MAP_SQUARE_SCALE*(i%8), MOOSHMAP_MAP_SQUARE_SCALE*Floor(i/8), cmb, cs, OP_OPAQUE);
		if(visited[i]) //If Link has been to the screen, or one grouped with it, draw it
			b->FastCombo(0, MOOSHMAP_MAP_SQUARE_SCALE*(i%8)+256, MOOSHMAP_MAP_SQUARE_SCALE*Floor(i/8), cmb+1, cs, OP_OPAQUE);
		if(Game->LItems[Game->GetCurLevel()]&LI_COMPASS){ //If Link has the compass, show markers
			for(k=0; k<2; k++){ //Repeat twice if MarkerScreen2 is set
				int markerLayerOffset = 256*k;
				markerScreen = MooshMap[MM_STARTFLOORINDEX+MM_NUMFLOORINDEX*floor+MM_F_MARKERSCREEN];
				if(MooshMap[MM_STARTFLOORINDEX+MM_NUMFLOORINDEX*floor+MM_F_MARKERSCREEN2]<0) //jesus christ, man, why are you doing it this way
					k = 2; //seriously dude, no!
				else if(k==1)
					markerScreen = MooshMap[MM_STARTFLOORINDEX+MM_NUMFLOORINDEX*floor+MM_F_MARKERSCREEN2];
				ctMarker = Game->GetComboType(refMap, markerScreen, pos);
				if(ctMarker==CT_CHEST){ //Marker for Screen Item or Special Item (2 states: 0 = Item present, 1 = Item taken)
					cmbMarker = Game->GetComboData(refMap, markerScreen, pos);
					csMarker = Game->GetComboCSet(refMap, markerScreen, pos);
					test1 = Game->GetScreenState(srcMap, pos+offset, ST_ITEM) || Game->GetScreenState(srcMap, pos+offset, ST_SPECIALITEM);
					if(test1)
						b->FastCombo(0, MOOSHMAP_MAP_SQUARE_SCALE*(i%8)+markerLayerOffset, MOOSHMAP_MAP_SQUARE_SCALE*Floor(i/8)+176, cmbMarker+1, csMarker, OP_OPAQUE);
					else
						b->FastCombo(0, MOOSHMAP_MAP_SQUARE_SCALE*(i%8)+markerLayerOffset, MOOSHMAP_MAP_SQUARE_SCALE*Floor(i/8)+176, cmbMarker, csMarker, OP_OPAQUE);
				}
				else if(ctMarker==CT_BOSSCHEST){ //Marker for Screen Item and Special Item (4 states: 0 = Both items present, 1 = Screen item present, 2 = Special item present, 3 = Both items taken);
					cmbMarker = Game->GetComboData(refMap, markerScreen, pos);
					csMarker = Game->GetComboCSet(refMap, markerScreen, pos);
					test1 = Game->GetScreenState(srcMap, pos+offset, ST_ITEM);
					test2 = Game->GetScreenState(srcMap, pos+offset, ST_SPECIALITEM);
					if(test1&&test2)
						b->FastCombo(0, MOOSHMAP_MAP_SQUARE_SCALE*(i%8)+markerLayerOffset, MOOSHMAP_MAP_SQUARE_SCALE*Floor(i/8)+176, cmbMarker+3, csMarker, OP_OPAQUE);
					else if(test1)
						b->FastCombo(0, MOOSHMAP_MAP_SQUARE_SCALE*(i%8)+markerLayerOffset, MOOSHMAP_MAP_SQUARE_SCALE*Floor(i/8)+176, cmbMarker+2, csMarker, OP_OPAQUE);
					else if(test2)
						b->FastCombo(0, MOOSHMAP_MAP_SQUARE_SCALE*(i%8)+markerLayerOffset, MOOSHMAP_MAP_SQUARE_SCALE*Floor(i/8)+176, cmbMarker+1, csMarker, OP_OPAQUE);
					else
						b->FastCombo(0, MOOSHMAP_MAP_SQUARE_SCALE*(i%8)+markerLayerOffset, MOOSHMAP_MAP_SQUARE_SCALE*Floor(i/8)+176, cmbMarker, csMarker, OP_OPAQUE);
				}
				else if(ctMarker==CT_DAMAGE1){ //Marker for bosses. Removed based on the level's boss flag. (2 states: 0 = Boss alive, 1 = Boss dead)
					cmbMarker = Game->GetComboData(refMap, markerScreen, pos);
					csMarker = Game->GetComboCSet(refMap, markerScreen, pos);
					if(Game->LItems[Game->GetCurLevel()]&LI_BOSS)
						b->FastCombo(0, MOOSHMAP_MAP_SQUARE_SCALE*(i%8)+markerLayerOffset, MOOSHMAP_MAP_SQUARE_SCALE*Floor(i/8)+176, cmbMarker+1, csMarker, OP_OPAQUE);
					else
						b->FastCombo(0, MOOSHMAP_MAP_SQUARE_SCALE*(i%8)+markerLayerOffset, MOOSHMAP_MAP_SQUARE_SCALE*Floor(i/8)+176, cmbMarker, csMarker, OP_OPAQUE);
				}
				else if(ctMarker==CT_STEP){ //Marker for screen secrets. (2 states: 0 = Secret not triggered, 1 = Secret triggered)
					cmbMarker = Game->GetComboData(refMap, markerScreen, pos);
					csMarker = Game->GetComboCSet(refMap, markerScreen, pos);
					test1 = Game->GetScreenState(srcMap, pos+offset, ST_SECRET);
					if(test1)
						b->FastCombo(0, MOOSHMAP_MAP_SQUARE_SCALE*(i%8)+markerLayerOffset, MOOSHMAP_MAP_SQUARE_SCALE*Floor(i/8)+176, cmbMarker+1, csMarker, OP_OPAQUE);
					else
						b->FastCombo(0, MOOSHMAP_MAP_SQUARE_SCALE*(i%8)+markerLayerOffset, MOOSHMAP_MAP_SQUARE_SCALE*Floor(i/8)+176, cmbMarker, csMarker, OP_OPAQUE);
				}
				else if(ctMarker==CT_LOCKBLOCK){ //Marker for lock blocks. (2 states: 0 = Locked, 1 = Unlocked)
					cmbMarker = Game->GetComboData(refMap, markerScreen, pos);
					csMarker = Game->GetComboCSet(refMap, markerScreen, pos);
					test1 = Game->GetScreenState(srcMap, pos+offset, ST_LOCKBLOCK);
					if(test1)
						b->FastCombo(0, MOOSHMAP_MAP_SQUARE_SCALE*(i%8)+markerLayerOffset, MOOSHMAP_MAP_SQUARE_SCALE*Floor(i/8)+176, cmbMarker+1, csMarker, OP_OPAQUE);
					else
						b->FastCombo(0, MOOSHMAP_MAP_SQUARE_SCALE*(i%8)+markerLayerOffset, MOOSHMAP_MAP_SQUARE_SCALE*Floor(i/8)+176, cmbMarker, csMarker, OP_OPAQUE);
				}
				else if(ctMarker==CT_BOSSLOCKBLOCK){ //Marker for boss lock blocks. (2 states: 0 = Locked, 1 = Unlocked)
					cmbMarker = Game->GetComboData(refMap, markerScreen, pos);
					csMarker = Game->GetComboCSet(refMap, markerScreen, pos);
					test1 = Game->GetScreenState(srcMap, pos+offset, ST_BOSSLOCKBLOCK);
					if(test1)
						b->FastCombo(0, MOOSHMAP_MAP_SQUARE_SCALE*(i%8)+markerLayerOffset, MOOSHMAP_MAP_SQUARE_SCALE*Floor(i/8)+176, cmbMarker+1, csMarker, OP_OPAQUE);
					else
						b->FastCombo(0, MOOSHMAP_MAP_SQUARE_SCALE*(i%8)+markerLayerOffset, MOOSHMAP_MAP_SQUARE_SCALE*Floor(i/8)+176, cmbMarker, csMarker, OP_OPAQUE);
				}
			}
		}
	}
}

//Draw the entire map
void MooshMap_DrawAll(bitmap b, int floor){
	Screen->DrawScreen(6, MooshMap[MM_BGMAP], MooshMap[MM_BGSCREEN], 0, 0, 0);
	MooshMap_DrawMap(b, floor, MOOSHMAP_MAP_X, MOOSHMAP_MAP_Y);
	MooshMap_DrawFloors(floor);
	if(MOOSHMAP_SHOW_LITEMS)
		MooshMap_DrawDungeonItems();
	if(MOOSHMAP_SHOW_TITLE)
		MooshMap_DrawTitle();
}

//Draw just the map block
void MooshMap_DrawMap(bitmap b, int floor, int x, int y){
	int refMap = MooshMap[MM_STARTFLOORINDEX+MM_NUMFLOORINDEX*floor+MM_F_REFMAP];
	int refScreen = MooshMap[MM_STARTFLOORINDEX+MM_NUMFLOORINDEX*floor+MM_F_REFSCREEN];
	
	int cmb; int cs; int flag; 
	int pos; 
	int i;
	int maxScale = MOOSHMAP_MAP_SQUARE_SCALE*8+1;
	//Draw unvisited layer
	b->Blit(6, RT_SCREEN, 0, 0, maxScale, maxScale, x, y, maxScale, maxScale, 0, 0, 0, BITDX_NORMAL, 0, true);
	//Draw visited layer
	b->Blit(6, RT_SCREEN, 256, 0, maxScale, maxScale, x, y, maxScale, maxScale, 0, 0, 0, BITDX_NORMAL, 0, true);
	//Draw current screen layer
	if(floor==MooshMap[MM_LINKFLOOR]&&MOOSHMAP_HIGHLIGHTCURRENTROOM){
		pos = Game->GetCurDMapScreen();
		flag = Game->GetComboFlag(refMap, refScreen, pos);
		if(flag>0){ //Link is in a grouped screen
			for(i=0; i<64; i++){
				pos = i%8+Floor(i/8)*16;
				if(Game->GetComboFlag(refMap, refScreen, pos)==flag){
					cmb = Game->GetComboData(refMap, refScreen, pos);
					cs = Game->GetComboCSet(refMap, refScreen, pos);
					Screen->FastCombo(6, x+MOOSHMAP_MAP_SQUARE_SCALE*(i%8), y+MOOSHMAP_MAP_SQUARE_SCALE*Floor(i/8), cmb+2, cs, OP_OPAQUE);
				}
			}
		}
		else{
			cmb = Game->GetComboData(refMap, refScreen, pos);
			cs = Game->GetComboCSet(refMap, refScreen, pos);
			Screen->FastCombo(6, x+MOOSHMAP_MAP_SQUARE_SCALE*(pos%16), y+MOOSHMAP_MAP_SQUARE_SCALE*Floor(pos/16), cmb+2, cs, OP_OPAQUE);
		}
	}
	//Draw marker layer
	b->Blit(6, RT_SCREEN, 0, 176, maxScale, maxScale, x, y, maxScale, maxScale, 0, 0, 0, BITDX_NORMAL, 0, true);
	//Draw second marker layer
	b->Blit(6, RT_SCREEN, 256, 176, maxScale, maxScale, x, y, maxScale, maxScale, 0, 0, 0, BITDX_NORMAL, 0, true);
	//If set to draw Link's position, do so
	if(MOOSHMAP_DRAWLINKPOSITION&&floor==MooshMap[MM_LINKFLOOR]){
		pos = Game->GetCurDMapScreen();
		int linkx = x+(pos%16)*MOOSHMAP_MAP_SQUARE_SCALE;
		int linky = y+Floor(pos/16)*MOOSHMAP_MAP_SQUARE_SCALE;
		if(MOOSHMAP_PRECISELINKPOSITION){
			linkx += MOOSHMAP_PRECISELINKPOSITIONBORDER+(Link->X/240)*(MOOSHMAP_MAP_SQUARE_SCALE-MOOSHMAP_PRECISELINKPOSITIONBORDER*2)-8;
			linky += MOOSHMAP_PRECISELINKPOSITIONBORDER+(Link->Y/160)*(MOOSHMAP_MAP_SQUARE_SCALE-MOOSHMAP_PRECISELINKPOSITIONBORDER*2)-8;
		}
		else{
			linkx += MOOSHMAP_MAP_SQUARE_SCALE/2-8;
			linky += MOOSHMAP_MAP_SQUARE_SCALE/2-8;
		}
		Screen->FastCombo(6, linkx, linky, CMB_MOOSHMAP_LINKPOSITIONMARKER, CS_MOOSHMAP_LINKPOSITIONMARKER, OP_OPAQUE);
	}
}

//Draw floor numbers next to the map
void MooshMap_DrawFloors(int floor){
	int x; int y;
	int cmb; int cs;
	for(int i=0; i<MooshMap[MM_NUMFLOORS]; i++){
		cmb = MooshMap[MM_STARTFLOORINDEX+MM_NUMFLOORINDEX*i+MM_F_FLOORCMB];
		cs = MooshMap[MM_STARTFLOORINDEX+MM_NUMFLOORINDEX*i+MM_F_FLOORCS];
		x = MOOSHMAP_FLOOR_X;
		y = MOOSHMAP_FLOOR_Y+(MOOSHMAP_FLOOR_SPACING*(MooshMap[MM_NUMFLOORS]-1))/2-MOOSHMAP_FLOOR_SPACING*i;
		if(i==MooshMap[MM_SELECTEDFLOOR])
			Screen->DrawCombo(6, x, y, cmb+1, MOOSHMAP_FLOOR_WIDTH, MOOSHMAP_FLOOR_HEIGHT, cs, -1, -1, 0, 0, 0, -1, 0, true, OP_OPAQUE);
		else
			Screen->DrawCombo(6, x, y, cmb, MOOSHMAP_FLOOR_WIDTH, MOOSHMAP_FLOOR_HEIGHT, cs, -1, -1, 0, 0, 0, -1, 0, true, OP_OPAQUE);
		if(i==MooshMap[MM_LINKFLOOR])
			Screen->FastCombo(6, x-16, y+((MOOSHMAP_FLOOR_HEIGHT-1)*8), CMB_MOOSHMAP_LINKFLOORMARKER, CS_MOOSHMAP_LINKFLOORMARKER, OP_OPAQUE);
	}
}

void MooshMap_DrawTitle(){
	int str[256];
	if(MooshMap[MM_TITLEREFSTR])
		Game->GetMessage(MooshMap[MM_TITLEREFSTR], str);
	else
		Game->GetDMapTitle(Game->GetCurDMap(), str);
	int sz = SizeOfArray(str);
	for(int i=sz-1; i>=0; i--){
		if(str[i]==' ')
			str[i] = 0;
		else
			break;
	}
	Screen->DrawString(6, MOOSHMAP_TITLE_X, MOOSHMAP_TITLE_Y, MOOSHMAP_TITLE_FONT, C_WHITE, -1, TF_CENTERED, str, OP_OPAQUE, SHD_OUTLINEDPLUS, C_BLACK);
}

void MooshMap_DrawDungeonItems(){
	int level = Game->GetCurLevel();
	if(Game->LItems[level]&LI_MAP)
		Screen->FastCombo(6, MOOSHMAP_MAPICON_X, MOOSHMAP_MAPICON_Y, CMB_MOOSHMAP_MAP, CS_MOOSHMAP_MAP, OP_OPAQUE);
	if(Game->LItems[level]&LI_COMPASS)
		Screen->FastCombo(6, MOOSHMAP_COMPASSICON_X, MOOSHMAP_COMPASSICON_Y, CMB_MOOSHMAP_COMPASS, CS_MOOSHMAP_COMPASS, OP_OPAQUE);
	if(Game->LItems[level]&LI_BOSSKEY)
		Screen->FastCombo(6, MOOSHMAP_BOSSKEYICON_X, MOOSHMAP_BOSSKEYICON_Y, CMB_MOOSHMAP_BOSSKEY, CS_MOOSHMAP_BOSSKEY, OP_OPAQUE);
}

int MooshMap_GetAllScreenStates(){
	int states;
	for(int i=0; i<14; i++){
		if(Screen->State[i])
			states |= 1<<i;
	}
	return states;
}

void MooshMap_UpdateCarryover(){
	if(Link->Action!=LA_SCROLLING){
		int allStates = MooshMap_GetAllScreenStates();
		if(MooshMap[MM_LASTDMAP]!=Game->GetCurDMap()||MooshMap[MM_LASTSCREEN]!=Game->GetCurScreen()||MooshMap[MM_LASTSCREENSTATES]!=allStates){
			if(MooshMap[MM_LASTDMAP]!=Game->GetCurDMap()){
				MooshMap_LoadLevelMapData(); //If what changed was the DMap, make sure the correct level is loaded
			}
			MooshMap[MM_LASTDMAP] = Game->GetCurDMap();
			MooshMap[MM_LASTSCREEN] = Game->GetCurScreen();
			MooshMap[MM_LASTSCREENSTATES] = allStates;
			if(Game->DMapFlags[Game->GetCurDMap()]&(1<<DMF_ALLOWMAP)){ //Only bother running carryovers if the allow map flag is set
				int i; int j; int k;
				int currentFloor = -1;
				for(i=0; i<13; i++){ //Cycle through all floors until we find which one Link is on
					for(j=0; j<4; j++){
						k = MooshMap[MM_STARTFLOORINDEX+MM_NUMFLOORINDEX*i+MM_F_FLOORDMAPS+j];
						if(Game->GetCurDMap()==k){
							currentFloor = i;
							break;
						}
					}
				}
				if(currentFloor>0){
					int map; int offset;
					for(i=0; i<4; i++){ //Cycle through all DMaps on the current floor and set their states
						k = MooshMap[MM_STARTFLOORINDEX+MM_NUMFLOORINDEX*currentFloor+MM_F_FLOORDMAPS+i];
						if(k>-1&&k!=Game->GetCurDMap()){ //Don't bother for the current DMap or invalid ones
							map = Game->DMapMap[k];
							offset = Game->DMapOffset[k];
							
							//Start with carrying over the visited state
							Game->SetScreenState(map, Game->GetCurDMapScreen()+offset, ST_VISITED, true);
							//If set to do so, carry over the other 13
							if(MOOSHMAP_ALL_CARRYOVER){
								for(j=0; j<14; j++){
									Game->SetScreenState(map, Game->GetCurDMapScreen()+offset, j, Screen->State[j]);
								}
							}
							//Carry over Screen->D if set
							if(MOOSHMAP_D_CARRYOVER){
								for(j=0; j<8; j++){
									Game->SetDMapScreenD(k, Game->GetCurDMapScreen(), j, Screen->D[j]);
								}
							}
						}
					}
				}
			}
		}
	}
}

@InitD0("Title String"),
@InitDHelp0("If >0, use this string instead of the DMap title for the map title.")
dmapdata script LttPMap
{
	void run(int titleStr)
	{
		MooshMap[MM_TITLEREFSTR] = titleStr;
		bitmap b = Game->CreateBitmap(512, 512);
		b->Own();
		if(!MooshMap_LoadLevelMapData()) //Only continue if we can get the floor data successfully
			Quit();
		MooshMap_UpdateBitmap(b, MooshMap[MM_SELECTEDFLOOR]); //Update the non animated parts of the map before loading (screens, markers)
		
		Game->PlaySound(SFX_MAPSUBSCREEN_OPEN);
		for(int i=0; i<12; ++i)
		{
			if(i<3){
				Screen->Rectangle(6, 0, 0, 255, 175, C_BLACK, 1, 0, 0, 0, true, OP_TRANS);
			}
			else if(i<6){
				Screen->Rectangle(6, 0, 0, 255, 175, C_BLACK, 1, 0, 0, 0, true, OP_TRANS);
				Screen->Rectangle(6, 0, 0, 255, 175, C_BLACK, 1, 0, 0, 0, true, OP_TRANS);
			}
			else if(i<9){
				MooshMap_DrawAll(b, MooshMap[MM_SELECTEDFLOOR]);
				Screen->Rectangle(6, 0, 0, 255, 175, C_BLACK, 1, 0, 0, 0, true, OP_TRANS);
				Screen->Rectangle(6, 0, 0, 255, 175, C_BLACK, 1, 0, 0, 0, true, OP_TRANS);
			}
			else{
				MooshMap_DrawAll(b, MooshMap[MM_SELECTEDFLOOR]);
				Screen->Rectangle(6, 0, 0, 255, 175, C_BLACK, 1, 0, 0, 0, true, OP_TRANS);
			}
			Waitframe();
		}
		while(!Link->PressMap)
		{
			MooshMap_DrawAll(b, MooshMap[MM_SELECTEDFLOOR]);
			if(MooshMap[MM_NUMFLOORS]>1){
				if(Link->InputUp||Link->InputDown){
					if(Link->InputUp&&!Link->InputDown){
						if(MooshMap[MM_FLOORSWITCHFRAMES]>0)
							MooshMap[MM_FLOORSWITCHFRAMES]--;
					}
					else if(Link->InputDown&&!Link->InputUp){
						if(MooshMap[MM_FLOORSWITCHFRAMES]>0)
							MooshMap[MM_FLOORSWITCHFRAMES]--;
					}
					else
						MooshMap[MM_FLOORSWITCHFRAMES] = 16;
				}
				if(Link->PressUp||(Link->InputUp&&MooshMap[MM_FLOORSWITCHFRAMES]<=0)){
					MooshMap[MM_FLOORSWITCHFRAMES] = 16;
					Game->PlaySound(SFX_MAPSUBSCREEN_FLOORCHANGE);
					MooshMap[MM_SELECTEDFLOOR]++;
					//Wrap selection
					if(MooshMap[MM_SELECTEDFLOOR]>MooshMap[MM_NUMFLOORS]-1)
						MooshMap[MM_SELECTEDFLOOR] = 0;
					MooshMap_UpdateBitmap(b, MooshMap[MM_SELECTEDFLOOR]); //The floor has changed, so we must update the bitmaps
				}
				else if(Link->PressDown||(Link->InputDown&&MooshMap[MM_FLOORSWITCHFRAMES]<=0)){
					MooshMap[MM_FLOORSWITCHFRAMES] = 16;
					Game->PlaySound(SFX_MAPSUBSCREEN_FLOORCHANGE);
					MooshMap[MM_SELECTEDFLOOR]--;
					//Wrap selection
					if(MooshMap[MM_SELECTEDFLOOR]<0)
						MooshMap[MM_SELECTEDFLOOR] = MooshMap[MM_NUMFLOORS]-1;
					MooshMap_UpdateBitmap(b, MooshMap[MM_SELECTEDFLOOR]); //The floor has changed, so we must update the bitmaps
					
				}
			}
			Waitframe();
		}
		Game->PlaySound(SFX_MAPSUBSCREEN_CLOSE);
		for(int i=11; i>=0; --i)
		{
			if(i<3){
				Screen->Rectangle(6, 0, 0, 255, 175, C_BLACK, 1, 0, 0, 0, true, OP_TRANS);
			}
			else if(i<6){
				Screen->Rectangle(6, 0, 0, 255, 175, C_BLACK, 1, 0, 0, 0, true, OP_TRANS);
				Screen->Rectangle(6, 0, 0, 255, 175, C_BLACK, 1, 0, 0, 0, true, OP_TRANS);
			}
			else if(i<9){
				MooshMap_DrawAll(b, MooshMap[MM_SELECTEDFLOOR]);
				Screen->Rectangle(6, 0, 0, 255, 175, C_BLACK, 1, 0, 0, 0, true, OP_TRANS);
				Screen->Rectangle(6, 0, 0, 255, 175, C_BLACK, 1, 0, 0, 0, true, OP_TRANS);
			}
			else{
				MooshMap_DrawAll(b, MooshMap[MM_SELECTEDFLOOR]);
				Screen->Rectangle(6, 0, 0, 255, 175, C_BLACK, 1, 0, 0, 0, true, OP_TRANS);
			}
			Waitframe();
		}
	}
}