AddButton(Str(4), "adjust 0_mod9 0 def 0 2 -1", d, 32.5, 0.8, 0.8, 0.6, 0xBBBBBB, 0xCCCCCC, 0x000000, 0x000000);
	PrintStrL("Snowball", ++d, 20, 0.6, 0xBBBBBB);
	v = max(0,min(2,GetConfig(GlobalConfig,"0_ModS",0)));
	PrintStrR(vector<string>({"Off","On","Reverse"})[v], d, 33, 0.6, 0xCCCCCC);
	AddButton(Str(19), "adjust 0_mods 0 inc 0 2 -1", ++d, 31.5, 0.8, 0.8, 0.6, 0xBBBBBB, 0xCCCCCC, 0x000000, 0x000000);
	AddButton(Str(4), "adjust 0_mods 0 def 0 2 -1", d, 32.5, 0.8, 0.8, 0.6, 0xBBBBBB, 0xCCCCCC, 0x000000, 0x000000);
	PrintStrL("Pentomino", ++d, 20, 0.6, 0xBBBBBB);
	v = max(0,min(2,GetConfig(GlobalConfig,"0_ModP",0)));
	PrintStrR(vector<string>({"Off","On","Reverse"})[v], d, 33, 0.6, 0xCCCCCC);
	AddButton(Str(19), "adjust 0_modp 0 inc 0 2 -1", ++d, 31.5, 0.8, 0.8, 0.6, 0xBBBBBB, 0xCCCCCC, 0x000000, 0x000000);
	AddButton(Str(4), "adjust 0_modp 0 def 0 2 -1", d, 32.5, 0.8, 0.8, 0.6, 0xBBBBBB, 0xCCCCCC, 0x000000, 0x000000);
	PrintStrL("Mutation", ++d, 20, 0.6, 0xBBBBBB);
	v = max(0,min(2,GetConfig(GlobalConfig,"0_ModM",0)));
	PrintStrR(vector<string>({"Off","On","Reverse"})[v], d, 33, 0.6, 0xCCCCCC);
	AddButton(Str(19), "adjust 0_modm 0 inc 0 2 -1", ++d, 31.5, 0.8, 0.8, 0.6, 0xBBBBBB, 0xCCCCCC, 0x000000, 0x000000);
	AddButton(Str(4), "adjust 0_modm 0 def 0 2 -1", d, 32.5, 0.8, 0.8, 0.6, 0xBBBBBB, 0xCCCCCC, 0x000000, 0x000000);
}
void PaintSelect() {
	PrintStrM("Custom Rules", 3, 35, 1.5, 0xDDDDDD, 0.15);
	AddButton(Str(12)+"Back", "home", 1, 5, 2, 8, 0.6, -1, -1, 0xBBBBBB, 0x66CCFF);
	AddButton("Settings", "settings", 1, 50, 2, 8, 0.6, -1, -1, 0xBBBBBB, 0x66CCFF);
	//AddButton("Start With Custom Rules", "", 7, 9, 2, 8, 0.6, -1, -1, 0xEEEEEE, 0xEEEEEE);
	AddButton("Start", "startcustom", 9, 9, 2, 8, 1, -1, -1, 0xCCCCCC, 0x66CCFF);
	AddButton("Game Presets", "", 11, 9, 2, 8, 0.6, -1, -1, 0xEEEEEE, 0xEEEEEE);
	AddButton("Marathon", "startcustom levline 10", 12.5, 9, 1, 8, 0.7, -1, -1, 0xCCCCCC, 0x66CCFF);
	AddButton("Sprint", "startcustom tarline 40", 14, 9, 1, 8, 0.7, -1, -1, 0xCCCCCC, 0x66CCFF);
	AddButton("Ultra", "startcustom tartime 120000 typetime 1", 15.5, 9, 1, 8, 0.7, -1, -1, 0xCCCCCC, 0x66CCFF);
	AddButton("Zen", "startcustom holdtype 2 useup 1 startdelay 0 clutchrule 2 usezone 1 stock -1 useflip 1 usewaste 1 useundo 1 uselock 1 useplace 1 autorespawn 1 resetlim 9999999 dropdelay -1 lockdelay -1", 17, 9, 1, 8, 0.7, -1, -1, 0xCCCCCC, 0x66CCFF);
	AddButton("Classic", "startcustom holdtype 0 use2rot 0 usehalf 0 useharddrop 0 comborule 13 spinrule 7 b2brule 6 allclearrule 7 rotatesystem 6 levline 10 shadowtype 0 randomtype 0 nextlen 1 resetlim 0", 18.5, 9, 1, 8, 0.7, -1, -1, 0xCCCCCC, 0x66CCFF);
	AddButton("Battle 2P", "startcustom player 2 qpmode 0", 20, 9, 1, 8, 0.7, -1, -1, 0xCCCCCC, 0x66CCFF);
	AddButton("Battle 10P", "startcustom player 10 qpmode 0", 21.5, 9, 1, 8, 0.7, -1, -1, 0xCCCCCC, 0x66CCFF);
	AddButton("Battle 30P", "startcustom player 30 qpmode 0", 23, 9, 1, 8, 0.7, -1, -1, 0xCCCCCC, 0x66CCFF);
	AddButton("Battle 100P", "startcustom player 100 qpmode 0", 24.5, 9, 1, 8, 0.7, -1, -1, 0xCCCCCC, 0x66CCFF);
	AddButton("Zenith", "startcustom player 16 qpmode 1 zenithbot 1 singlebonus 1 intrule 1", 26, 9, 1, 8, 0.7, -1, -1, 0xCCCCCC, 0x66CCFF);
	AddButton("Dig", "startcustom staticheight 10", 27.5, 9, 1, 8, 0.7, -1, -1, 0xCCCCCC, 0x66CCFF);
	AddButton("Clear Config", "clearconfig", 35, 9, 1, 8, 0.5, -1, -1, 0xFF6666, 0xEE0000);
	PrintStrL("d : "+IToS(AdjustDel), 6, 20, 0.6, 0xCCCCCC);
	AddButton("-", "adjustdel dec", 6, 26.5, 0.8, 0.8, 0.6, 0xBBBBBB, 0xCCCCCC, 0x000000, 0x000000);
	AddButton("+", "adjustdel inc", 6, 27.5, 0.8, 0.8, 0.6, 0xBBBBBB, 0xCCCCCC, 0x000000, 0x000000);
	AddButton("Basic", "seltab basic", 6, 32, 0.8, 4, 0.6, 0xBBBBBB, 0xCCCCCC, 0x000000, 0x000000);
	AddButton("Battle", "seltab battle", 6, 37, 0.8, 4, 0.6, 0xBBBBBB, 0xCCCCCC, 0x000000, 0x000000);
	AddButton("Level", "seltab level", 6, 42, 0.8, 4, 0.6, 0xBBBBBB, 0xCCCCCC, 0x000000, 0x000000);
	AddButton("Misc", "seltab misc", 6, 47, 0.8, 4, 0.6, 0xBBBBBB, 0xCCCCCC, 0x000000, 0x000000);
	AddButton("QPMod", "seltab qpmod", 6, 52, 0.8, 4, 0.6, 0xBBBBBB, 0xCCCCCC, 0x000000, 0x000000);
	if (SelTab=="basic") OptionBasic();
	if (SelTab=="battle") OptionBattle();
	if (SelTab=="level") OptionLevel();
	if (SelTab=="misc") OptionMisc();
	if (SelTab=="qpmod") OptionQPMod();
}
#define sq(xx,ss,clr) Square(x+sz*(xx),y+sz*(xx),sz*(ss),clr)
#define rec(xx,yy,sx,sy,clr) Rect(x+sz*(xx),y+sz*(yy),sz*(sx),sz*(sy),clr)
void PaintTile(double x, double y, int clr, double sz=1.0, EStyle Style=S_Default) {
	Square(x, y, sz, clr);
	if (1) {
		if ((Style==S_Default)||(Style==S_Active)) {
			sq(0, 1, Mix(0xFFFFFF,clr,0.1));
			sq(0.1, 0.9, Mix(0x000000,clr,0.2));
			sq(0.1, 0.8, clr);
			sq(0.2, 0.65, Mix(0xFFFFFF,clr,0.2));
			sq(0.2, 0.6, Mix(0x000000,clr,0.05));
			sq(0.3, 0.4, Mix(0xFFFFFF,clr,0.2));
			sq(0.35, 0.3, clr);
		}
		if (Style==S_Garbage) {
			sq(0, 1, Mix(0xFFFFFF,clr,0.1));
			sq(0.1, 0.9, Mix(0x000000,clr,0.2));
			sq(0.1, 0.8, clr);
			sq(0.2, 0.6, Mix(0x000000,clr,0.05));
			sq(0.25, 0.5, Mix(0x000000,clr,0.1));
			sq(0.3, 0.4, Mix(0x000000,clr,0.15));
		}
	}
}
#undef sq
#undef rec
void PaintPiece(const Piece &p, double x, double y, double sz, int bck=BoardColor, int clr=-1, EStyle Style=S_Active, int MinSize=4) {
	int LX=p.GetLX(); double ss=sz/double(LX);
	Square(x, y, sz, bck);
	if (LX<MinSize) {
		x+=0.5*sz*double(MinSize-LX)/double(MinSize); y+=0.5*sz*double(MinSize-LX)/double(MinSize);
		ss = sz/double(MinSize);
	}
	for (int i=0; i<LX; ++i) {
		for (int j=0; j<LX; ++j) {
			int val = p.GetGrid(LX-i-1,j);
			if (!val) continue;
			if (clr>=0) val=clr;
			PaintTile(x+i*ss, y+j*ss, abs(val), ss, Style);
		}
	}
}
void PaintPool() {
	AddButton(Str(12)+"Back", "select", 1, 5, 2, 8, 0.6, -1, -1, 0xBBBBBB, 0x66CCFF);
	int beg = max(0,int(EditPool.size())-200);
	PrintStrM("Delete "+Str(11), 15, 5, 0.8, 0xBBBBBB);
	for (int id=0; id<200; ++id) {
		int i=id/20, j=id%20;
		Piece p = P_Empty;
		if (EditPool.size()>beg+id) p=EditPool[beg+id];
		AddButton("", "pooldel "+IToS(beg+id), 3+1.5*(i+0.5), 10+1.5*(j+0.5), 1.5, 1.5, 0, -1, -1, 0x000000, 0x000000);
		PaintPiece(p, 3+1.5*i, 10+1.5*j, 1.5, (CheckButton(Buttons.back()))?0x555555:BoardColor);
	}
	AddButton("Piece Pool", "", 9, 50, 2, 8, 1.2, -1, -1, 0xDDDDDD, 0xDDDDDD);
	AddButton("Reset", "poolclear", 12, 50, 2, 8, 1, -1, -1, 0xBBBBBB, 0x66CCFF);
	AddButton("Cancel", "select", 15, 50, 2, 8, 1, -1, -1, 0xBBBBBB, 0x66CCFF);
	AddButton("Confirm", "poolconfirm", 18, 50, 2, 8, 1, -1, -1, 0xBBBBBB, 0x66CCFF);
	PrintStrM("Add "+Str(10), 23, 5, 0.8, 0xBBBBBB);
	for (int id=0; id<100; ++id) {
		Piece p = P_Empty;
		if (PieceList.size()>id) p=PieceList[id];
		int i=id/20, j=id%20;
		if (p.GetRealName().empty()) {
			PaintPiece(p,20+1.5*i,10+1.5*j,1.5); continue;
		}
		AddButton("", "poolins "+IToS(id), 20+1.5*(i+0.5), 10+1.5*(j+0.5), 1.5, 1.5, 0, -1, -1, 0x000000, 0x000000);
		PaintPiece(p, 20+1.5*i, 10+1.5*j, 1.5, (CheckButton(Buttons.back()))?0x555555:BoardColor);
	}
	AddButton("Custom Piece", "piece", 31, 5, 2, 8, 0.6, -1, -1, 0xBBBBBB, 0x66CCFF);
	beg = max(0,int(CustomPiece.size())-100);
	for (int id=0; id<100; ++id) {
		Piece p = P_Empty;
		if (CustomPiece.size()>id+beg) p=*CustomPiece[id+beg];
		int i=id/20, j=id%20;
		if (p.GetRealName().empty()) {
			PaintPiece(p,28+1.5*i,10+1.5*j,1.5); continue;
		}
		AddButton("", "custompoolins "+IToS(id+beg), 28+1.5*(i+0.5), 10+1.5*(j+0.5), 1.5, 1.5, 0, -1, -1, 0x000000, 0x000000);
		PaintPiece(p, 28+1.5*i, 10+1.5*j, 1.5, (CheckButton(Buttons.back()))?0x555555:BoardColor);
	}
}
void PaintCustomPiece() {
	AddButton(Str(12)+"Back", "poolback", 1, 5, 2, 8, 0.6, -1, -1, 0xBBBBBB, 0x66CCFF);
	AddButton("Reset", "clearcustompiece", 12, 50, 2, 8, 1, -1, -1, 0xBBBBBB, 0x66CCFF);
	AddButton("Cancel", "poolback", 15, 50, 2, 8, 1, -1, -1, 0xBBBBBB, 0x66CCFF);
	AddButton("Confirm", "addcustompiece", 18, 50, 2, 8, 1, -1, -1, 0xBBBBBB, 0x66CCFF);
	int Color = (EditPieceR<<16)|(EditPieceG<<8)|EditPieceB;
	for (int i=0; i<256; ++i) {
		AddButton("", "colorr "+IToS(i), 8, 10+i*0.1, (i%17)?0.3:0.45, 0.1, 0, i<<16, 0xFFFFFF, -1, -1);
		AddButton("", "colorg "+IToS(i), 9, 10+i*0.1, (i%17)?0.3:0.45, 0.1, 0, i<<8, 0xFFFFFF, -1, -1);
		AddButton("", "colorb "+IToS(i), 10, 10+i*0.1, (i%17)?0.3:0.45, 0.1, 0, i, 0xFFFFFF, -1, -1);
	}
	AddButton("", "", 8, 10+EditPieceR*0.1, 0.6, 0.1, 0, 0xFF6666, 0xFF6666, -1, -1);
	AddButton("", "", 9, 10+EditPieceG*0.1, 0.6, 0.1, 0, 0x66FF66, 0x66FF66, -1, -1);
	AddButton("", "", 10, 10+EditPieceB*0.1, 0.6, 0.1, 0, 0x6666FF, 0x6666FF, -1, -1);
	PrintStrM("R", 7.75, 9, 0.5, 0xAAAAAA);
	PrintStrM("G", 8.75, 9, 0.5, 0xAAAAAA);
	PrintStrM("B", 9.75, 9, 0.5, 0xAAAAAA);
	PrintStrM("Piece Color", 5.5, 22.8, 0.6, 0xAAAAAA);
	PrintStrM("#"+IToSH(Color), 6.5, 22.8, 0.7, Color);
	PrintStrL("Piece Size : "+IToS(EditPieceSize), 13, 15, 0.6, 0xAAAAAA);
	AddButton("-", "decpiecesize", 13, 25, 0.8, 0.8, 0.6, 0xBBBBBB, 0xCCCCCC, 0x000000, 0x000000);
	AddButton("+", "incpiecesize", 13, 26, 0.8, 0.8, 0.6, 0xBBBBBB, 0xCCCCCC, 0x000000, 0x000000);
	for (int i=0; i<EditPieceSize; ++i) {
		for (int j=0; j<EditPieceSize; ++j) {
			AddButton("", "piecegrid "+IToS(i)+" "+IToS(j), 15.5+i, 15.5+j, 1, 1, 0, -1, -1, -1, -1);
			if (EditPiece[i][j]) PaintTile(15+i,15+j,Color,1);
			else {
				Square(15+i, 15+j, 1, GridColor);
				Square(15+i+0.05, 15+j+0.05, 0.9, BoardColor);
			}
		}
	}
}
void PaintIngame() {
	AddButton(Str(12)+"Back", "select", 1, 2, 1, 3.5, 0.6, -1, -1, 0xBBBBBB, 0x66CCFF);
	AddButton((Pause)?"Resume":"Pause", "pause", 2, 2, 1, 3.5, 0.6, -1, -1, 0xBBBBBB, 0x66CCFF);
	AddButton("Retry", "retry", 3, 2, 1, 3.5, 0.6, -1, -1, 0xBBBBBB, 0x66CCFF);
}
void Paint() {
	if (Menu=="home") PaintHome();
	if (Menu=="settings") PaintSettings();
	if (Menu=="select") PaintSelect();
	if (Menu=="pool") PaintPool();
	if (Menu=="piece") PaintCustomPiece();
	if (Menu=="ingame") PaintIngame(); 
	PrintStrM(IToS(FrameQueue.size())+"T", 5, 2, 0.5, 0x66CCFF);
	PrintStrM(IToS(RenderQueue.size())+"F", 6, 2, 0.5, 0xEE82EE);
}
void Frame_View() {
	ll cl = GetClock();
	while ((!RenderQueue.empty())&&((RenderQueue.front()+1000LL<=cl)||(RenderQueue.size()>11000))) RenderQueue.pop();
	RenderQueue.push(cl);
	ArrangeBoards(Obs);
	int clr = BackgroundColor;
	int B=clr&255; clr>>=8;
	int G=clr&255; clr>>=8;
	int R = clr&255;
	Buttons.clear();
	glClearColor(min(1.0,max(0.0,double(R)/255.0)), min(1.0,max(0.0,double(G)/255.0)), min(1.0,max(0.0,double(B)/255.0)), 0);
	glClear(GL_COLOR_BUFFER_BIT);
	for (Board *p : Boards) p->Paint();
	Paint();
	for (Button &o : Buttons) {
		bool f = CheckButton(o);
		int clr = (f)?o.selclr:o.clr;
		if (clr>=0) Rect(o.x-o.lx*0.5,o.y-o.ly*0.5,o.lx,o.ly,clr);
		PrintStrM(o.Text, o.x-o.tsz*0.5, o.y-o.tsz*0.33, o.tsz, (f)?o.tselclr:o.tclr);
	}
	Poly(CurX, CurY, 0.1, 4, 0xFFFFFF, acos(-1.0)*0.25);
	glPushMatrix();
	glPopMatrix();
	SwapBuffers(hDC);
}
void Board::Paint() {
	if (K<0.001) return;
	if ((PlayerCount>1)&&(AliveCount>1)&&(Over)&&(Bot)&&(!ZenithBot)) return;
	bool Simple = ((Bot)&&(K*double(GetSize())<8.0));
	if ((QPMode)&&(!Simple)) PaintZenith();
	if (!Simple) {
		Rect(2, 0, SX-2, LY, ((QPBlighted)||(CurZone))?Mix(0xFFE211,GridColor,0.3):GridColor);
		Rect(0, 0, 2, LY, ((QPBlighted)||(CurZone))?Mix(0xFFE211,OverGridColor,0.3):OverGridColor);
		for (int i=0; i<SX; ++i) {
			for (int j=0; j<LY; ++j) {
				int clr = BoardColor;
				if (SX-i<=RoundHeight) clr=RoundColor;
				if ((VanishHeight)&&(SX-i>=VanishHeight)) clr=VanishColor;
				Square(i+0.025, j+0.025, 0.95, clr); 
			}
		}
	}
	else Rect(0,0,SX,LY,BoardColor);
	int FallDist = LX;
	for (int j=1; j<=LY; ++j) {
		int pre = 0;
		for (int i=1; i<=LX; ++i) {
			if (C[i][j]) FallDist=min(FallDist,i-pre-1);
			else if (B[i][j]) pre=i;
		}
	}
	bool Inactive=false, CDown=CheckDown();
	double FallRate=0.0, LockRate=0.0, ClearRate=0.0;
	if (CDown) {
		if ((DropDelay)&&(SmoothLevel>=1)) FallRate=max(0.0,min(1.0,double(FallTime)/double(DropDelay)));
		else FallRate=0.0;
	}
	else {
		if (LockDelay>0) LockRate=max(0.0,min(1.0,double(FallTime)/double(LockDelay)));
		else if (!LockDelay) LockRate=0.0;
	}
	if ((InactiveTime>0)&&(InactiveTot>0)) {
		Inactive = true;
		ClearRate = 1.0-max(0.0,min(1.0,double(InactiveTime)/double(InactiveTot)));
	}
	double grat = max(0.0,min(1.0,double(PushCD)/double(max(1,GarbagePush))));
	if (!SmoothLevel) grat=0.0;
	if ((ShadowType==2)&&(!Simple)) {
		for (int j=1; j<=LY; ++j) {
			bool f = false;
			for (int i=1; i<=LX; ++i) {
				if (C[i][j]) f=true;
			}
			if (!f) continue;
			for (int i=1; i<=SX; ++i)
				Square(SX-i+grat+0.025,j-1+0.025,0.95,ShadowColor);
		}
	}
	if ((UseTip)&&((TgD>=0)&&(TgD<4))) {
		int PLX = Tg.GetLX();
		for (int i=0; i<PLX; ++i) {
			for (int j=0; j<PLX; ++j) {
				int b = Tg.GetGrid(i,j,TgD);
				if (!b) continue;
				int x=i+TgX, y=j+TgY;
				Square(SX-x+0.025, y-1+0.025, 0.95, Mix(abs(b),BackgroundColor,0.3));
			}
		}
	}
	if ((ShadowType==1)&&(!Simple)) {
		for (int i=1; i<=SX; ++i) {
			for (int j=1; j<=LY; ++j) {
				if ((!CheckOut(i+FallDist,j))&&(C[i+FallDist][j])) Square(SX-i+grat+0.025,j-1+0.025,0.95,ShadowColor);
			}
		}
	}
	if (SelX) {
		int clr = 0x39C5BB;
		if (!CursorMode) clr=0x66CCFF;
		Square(SX-SelX+0.025, SelY-1+0.025, 0.95, Mix(clr,BackgroundColor,0.3));
	}
	int RiseHeight=GetRiseHeight(), GarbID=-1;
	for (int i=1; i<=LX; ++i) {
		for (int j=1; j<=LY; ++j) {
			if ((B[i][j]<0)&&(!C[i][j])) {
				++GarbID; break;
			}
		}
	}
	if ((NextLen)&&(!Simple)) PrintStrM("NEXT",0,LY+2.5,1,0xEEEEEE,0.12);
	if ((SmoothLevel<=3)||(NextLen<2)||((Bot)&&(BotSpeed<100))) DNext=0; 
	for (int i=0; i<NextLen; ++i) {
		Piece p = (Next.size()>i)?Next[i]:P_Empty;
		if (i<5) PaintPiece(p,(double(i)+DNext)*3.5+1,LY+1,3);
		else {
			int d = i-5;
			PaintPiece(p, 1.5+1.2*(d%15), LY+1.2*(d/15)+5, 1, p.GetColor(), S_Simple);
		}
	}
	if ((HoldType)&&(HoldLen)) {
		if (!Simple) PrintStrM("HOLD",0,-2.5,1,((HoldRem)||(HoldType==2))?0xEEEEEE:0x888888, 0.12);
		for (int i=0; i<HoldLen; ++i) {
			int clr = -1;
			if ((HoldType==1)&&(i+HoldLen-Hold.size()>=HoldRem)) clr=0x888888;
			PaintPiece((Hold.size()>i)?Hold[i]:P_Empty, i*3.5+1, -4, 3, clr);
		}
	}
	int ZoneID = 0;
	for (int i=1; i<=LX; ++i) {
		bool gb = false;
		for (int j=1; j<=LY; ++j) {
			int clr = abs(B[i][j]);
			if (B[i][j]<0) gb=true;
			if (!clr) continue;
			if (C[i][j]) continue;
			clr = Mix(0x000000,clr,0.1);
			clr = Mix(0xFFFFFF,clr,0.3*max(0.0,min(1.0,double((Age[i][j]<0)?500:Age[i][j])/500.0)));
			if (Mod7) {
				double vrat = Age[i][j]/500.0;
				if (Mod7==1) {
					int d = int(Time%5000LL);
					if (d<=500) vrat=max(vrat,double(500-d)/500.0);
				}
				if (B[i][j]<0) {
					if (Mod7==1) vrat=1.0;
					else vrat=max(vrat,double(3-GarbID)/3.0);
				}
				if ((B[i][j]>=GarbQuest)&&(B[i][j]<=GarbQuest+20)) vrat=1.0;
				if (Bot) vrat=max(vrat,0.2);
				if (vrat<0.001) continue;
				clr = Mix(clr,BackgroundColor,vrat);
			}
			double di = 0.0;
			if (Inactive) {
				if (Age[i][j]<0) di+=Age[i][j]*ClearRate;
				else if (FallDest[i]) di-=(i-FallDest[i])*ClearRate;
				else {
					double rat = max(0.0,min(1.0,ClearRate*LY-j+1));
					if (rat>0.999) continue;
					rat = 1.0-(1.0-rat)*(1.0-rat);
					clr = Mix(0xFFFFFF,clr,0.3);
					clr = Mix(BoardColor,clr,rat);
				}
			}
			PaintGrid(i+di-((i<=RiseHeight)?0.0:grat), j, abs(clr), (B[i][j]<0)?S_Garbage:S_Default,
			0, 0, 0,
			U[i+1][j]==U[i][j], U[i-1][j]==U[i][j], U[i][j-1]==U[i][j], U[i][j+1]==U[i][j]);
			if ((B[i][j]>=GarbQuest)&&(B[i][j]<=GarbQuest+20)) {
				PrintStrM(IToS(B[i][j]-GarbQuest), SX-i+0.3, j-0.7, 0.7, 0xCC99FF);
			}
			if ((j==1)&&(B[i][j]==GarbZone)) ++ZoneID;
		}
		if (gb) --GarbID;
	}
	if (ZoneID) {
		PrintStrM(IToS(ZoneID)+" Lines", SX-(ZoneID*0.5+0.5)+0.3-0.5, (LY*0.5+0.5)-0.7, 1, 0xFFFFFF);
		PrintStrM(IToS(ZoneAttack+GetZoneBonus(ZoneID))+" Attack", SX-(ZoneID*0.5+0.5)+0.3+0.5, (LY*0.5+0.5)-0.7, 1, 0xFFFFFF);
	}
	double Cx=PosX+0.5*Cur.GetLX()-FallRate+PDx-grat, Cy=PosY+0.5*Cur.GetLX()+PDy;
	for (int i=1; i<=LX; ++i) {
		for (int j=1; j<=LY; ++j) {
			int clr = abs(B[i][j]);
			if (!clr) continue;
			if (!C[i][j]) continue;
			clr = Mix(0x000000,clr,0.1*LockRate);
			PaintGrid(i-FallRate+PDx-grat, j+PDy, abs(clr), S_Active, PDth, Cx, Cy,
			C[i+1][j], C[i-1][j], C[i][j-1], C[i][j+1], LockRate);
		}
	}
	pair<int,int> NextPos = SpawnPlace(Next[0]);
	int PLX = Next[0].GetLX();
	double hrat = 1.0-max(0.0,min(1.0,(SX-GetHeight()-2)*0.15));
	if (!Simple) {
		for (int i=0; i<PLX; ++i) {
			for (int j=0; j<PLX; ++j) {
				if (!Next[0].GetGrid(i,j)) continue;
				int x=i+NextPos.first, y=j+NextPos.second;
				PrintStrM("*", SX-x+0.2, y-1+0.3, 1, Mix(0xFF0000,0xCCCCCC,hrat*0.3+0.7), 0.2*hrat);
			}
		}
	}
	for (int i=0; i<min(GarbagePreview,int(AttackIn.size())); ++i) {
		for (int j=1; j<=LY; ++j) {
			if (AttackIn[i].B[j]) PaintGrid(-i-0.5,j,Mix(abs(AttackIn[i].B[j]),BackgroundColor,max(0.2,0.8-0.1*i)),S_Garbage);
		}
	}
	for (int i=1; i<=min(100,max(SX,int(AttackIn.size()))); ++i) {
		int clr=0x333333; double w=0.15;
		if ((GarbageGap>=0)&&(i<=GarbageGap)) clr=0x888888;
		if (AttackIn.size()>=i) {
			int del = AttackIn[i-1].Delay;
			clr = Mix(0xFFE211,0xFF6666,max(0.0,min(1.0,double(del)*0.001)));
			if (!AttackIn[i-1].Delay) clr=0xEE0000;
			if (AttackIn[i-1].Delay<0) clr=0xDF3782;
			if (AttackIn[i-1].Delay>GarbageDelay-GarbagePass) clr=Mix(0xFFFFFF,clr,0.3);
			if ((GarbageGap>=0)&&(i>GarbageGap)) clr=Mix(0x000000,clr,0.3);
			w = 0.2;
		}
		else if (Simple) continue;
		Rect(SX-i+0.1, -0.2-0.5*w, 0.8, w, clr);
	}
	if ((AttackIn.size())&&(!Simple)) PrintStrR('-'+IToS(AttackIn.size()),SX,-0.5,0.8,0xFF6666,0.15);
	double rat = 1.0;
	if (!CDown) rat=1.0-LockRate;
	if ((!Simple)&&(QPMode)) {
		int sz = LevelColor.size();
		int CurClr = LevelColor[max(0,min(sz-1,QPRank))];
		int NextClr = LevelColor[max(0,min(sz-1,QPRank+1))];
		if (NextClr==CurClr) CurClr=Mix(0x000000,CurClr,0.1);
		double rat = max(0.0,min(1.0,double(QPExp)/double(QPExpReq())));
		Rect(SX+0.5, 0, 0.25, LY, CurClr);
		Rect(SX+0.5, LY*(1.0-rat)*0.5, 0.25, LY*rat, NextClr);
		rat = max(0.0,min(1.0,double(QPExpDecCD)*0.0002));
		Rect(SX+0.7, LY*(1.0-rat)*0.5, 0.05, LY*rat, 0xDDDDDD);
	}
	if (Simple) ;
	else if (Bot) {
		Rect(SX, 0, 0.5, LY, 0xCCCCCC);
		if ((!HideStat)||(SelX)) {
			PrintStrL(IToS(BotSpeed), SX+0.7, 0, 0.7, 0xDDDDDD);
			PrintStrR(GetBotName(ActType), SX+0.7, LY-0.5, 0.7, 0xDDDDDD);
		}
		else PrintStrM("Autoplay Demonstration",SX+0.7,LY*0.5,0.6,0x66CCFF);
		if (Menu=="home") PrintStrM("Click to Manually Control",SX+1.5,LY*0.5,0.5,0xBBBBBB);
	}
	else {
		if (Inactive) rat=1.0;
		Rect(SX, 0, 0.25, LY, 0xBBBBBB);
		Rect(SX, 0, 0.25, LY*rat, Mix(0x888888,0xFFE211,(Inactive)?0.5:0));
		PrintStrL((LockDelay<0)?"":IToS(((CDown)||(Inactive))?LockDelay:max(0,LockDelay-FallTime)), SX+0.7, 0, 0.7, 0xFFE211);
		PrintStrR((ResetRem==Max)?"":IToS(ResetRem), SX+0.7, LY, 0.7, 0x39C5BB);
		if (CursorMode) {
			if (UsePlace) PrintStrR("Edit Mode",SX+1.4,LY,0.5,0xAAAAAA);
		}
		else if ((UseClick)&&(!CursorMoveLock)) PrintStrR("Cursor Control",SX+1.4,LY,0.5,0xAAAAAA);
		if (UseUndo) {
			int v = max(0,int(History.size())-HistoryPos-1);
			PrintStrR(Str(12)+IToS(v),SX+0.25,-0.8,0.7,(v)?0x9999FF:0x888888);
			v = max(0,HistoryPos);
			PrintStrL(IToS(v)+Str(13),SX+0.25,LY+0.5,0.7,(v)?0x9999FF:0x888888);
		}
		rat = 0.0;
		if (ResetLim) rat=max(0.0,min(1.0,double(ResetRem)/double(ResetLim)));
		if (Inactive) rat=(ResetLim)?1.0:0.0;
		Rect(SX+0.25, 0, 0.25, LY, 0x888888);
		Rect(SX+0.25, 0, 0.25, LY*rat, Mix(0x888888,0x39C5BB,(Inactive)?0.5:0));
		if (Menu=="home") PrintStrM("Click to Autoplay",SX+1.5,LY*0.5,0.5,0xBBBBBB);
	}
	if ((!Simple)&&(UseZone)) {
		if (CurZone) PrintStrL("["+IToS(CurZone)+"] "+((ZoneRem>0)?IToS(ZoneRem):""),SX+1.5,0,0.7,0xFFE211);
		else if ((!ZoneReq)||(ZoneProgress>=ZoneSeg*ZoneReq)) PrintStrL("["+IToS(ZoneSeg)+"] MAX",SX+1.5,0,0.7,0xFFA500);
		else PrintStrL("["+IToS(ZoneProgress/ZoneReq)+"] +"+IToS(ZoneProgress%ZoneReq)+"/"+IToS(ZoneReq),SX+1.5,0,0.7,(ZoneProgress>=ZoneReq)?0xFFA500:0xAAAAAA);
	}
	if (Simple) ;
	else if ((PlayerCount>2)&&(!QPMode)) {
		PrintStrM(vector<string>({"None","All","Even","Elimination","Random",
		"Payback","Badges","Attackers","Backfire"})[TargetType], SX+2, LY*0.5, 0.8, 0xCCCCCC);
	}
	else if ((UsePlace)&&(!Bot)&&(CursorMode)) {
		PrintStrM("<"+vector<string>({"X","Z","L","O","S","I","J","T","G"})[PaintType]+">", SX+2, LY*0.5, 0.8, GetPaintColor());
	}
	if (Simple) ;
	else if (Inactive) {
		rat = max(0.0,min(1.0,double(InactiveTime)/double(max(1,InactiveTot))));
		Rect(SX, LY*0.5*(1.0-rat)*double(Bot), 0.5, LY*rat, 0x888888);
		//PrintStrL(IToS(InactiveTime), SX+1.4, 0, 0.7, 0x666666);
	}
	if (!Simple) {
		ModPos = 0;
		PrintQPMod(Mod2, 0xEE82EE, Str(22), "NH");
		PrintQPMod(Mod3, 0xFFA500, Str(23), "MS");
		PrintQPMod(Mod4, 0xFDD000, Str(24), "GV");
		PrintQPMod(Mod5, 0xEE0000, Str(25), "VL");
		PrintQPMod(Mod6, 0x66CCFF, Str(26), "DH");
		PrintQPMod(Mod7, 0x9999FF, Str(27), "IN");
		PrintQPMod(Mod8, 0x00FFCC, Str(28), "AS");
		PrintQPMod(Mod9, 0xFFE211, Str(29), "EX");
		PrintQPMod(ModS, 0x0080FF, Str(30), "SN");
		PrintQPMod(ModP, 0xCCCCCC, "?", "PT");
		PrintQPMod(ModM, 0xAAAAAA, "?", "MT");
	}
	if (AllClearAge>=5000) AllClearAge=5000;
	else if (!Simple) {
		double rat = 1.0-max(0.0,min(1.0,AllClearAge/5000.0));
		PrintStrM("ALL", 0.5*SX-1, 0.5*LY-0.4, 2, 0xFFE211, 0.15*rat);
		PrintStrM("CLEAR", 0.5*SX+1, 0.5*LY-0.4, 2, 0xFFE211, 0.15*rat);
	}
	if ((Mod8)&&((SpinText==LastClearType.first)||((Mod8==2)&&(LastClearType.first=="X")))&&(SpinText.size())) SpinAge=min(SpinAge,3000);
	if (SpinAge>=5000) SpinAge=5000;
	else if (!Simple) {
		double rat = 1.0-max(0.0,min(1.0,SpinAge/5000.0));
		double rr = 1.0-(1.0-rat)*(1.0-rat);
		if (SpinGrade==3) PrintAction(SpinText+"-Spin", 4.5, -2, 0.8, Mix(SpinColor,BackgroundColor,rr), 0.15*rr, 1.5-0.5*rat*rat*rat*rat*rat);
		else if (SpinGrade==2) PrintAction("Mini "+SpinText+"-Spin", 4.5, -2, 0.7, Mix(SpinColor,BackgroundColor,rr), 0.15*rr, 1.5-0.5*rat*rat*rat*rat*rat);
		else if (SpinGrade==1) {
			PrintAction("Mini", 4.3, -2, 0.55, Mix(SpinColor,BackgroundColor,rr), 0.15*rr,  1.5-0.5*rat*rat*rat*rat*rat);
			PrintAction(SpinText+"-Spin", 4.7, -2, 0.55, Mix(SpinColor,BackgroundColor,rr), 0.15*rr,  1.5-0.5*rat*rat*rat*rat*rat);
		}
		else PrintAction(SpinText, 4.5, -2, 0.8, Mix(SpinColor,BackgroundColor,rr), 0.15*rr, 1.5-0.5*rat*rat*rat*rat*rat);
	}
	if ((Mod8)&&(LineClearText==LastClearType.second)) LineClearAge=min(LineClearAge,3000);
	if (LineClearAge>=5000) LineClearAge=5000;
	else if (!Simple) {
		double rat = 1.0-max(0.0,min(1.0,LineClearAge/5000.0));
		double rr = 1.0-(1.0-rat)*(1.0-rat);
		string Text = (LineClearText>=0)?LineClearWord[min(LineClearText,int(LineClearWord.size()-1))]:"";
		double dd = 6.0/min(6,max(1,int(Text.size())));
		PrintAction(Text, 5.5, -2, 1.3, Mix(LineClearColor,BackgroundColor,rr), 0.15*rr, dd*(1.7-0.7*rat*rat*rat*rat*rat));
	}
	if (B2BAge>=5000) B2BAge=5000;
	else if (!Simple) {
		double rat = 1.0-max(0.0,min(1.0,B2BAge/5000.0));
		double rr = 1.0-(1.0-rat)*(1.0-rat);
		int clr = B2BColor(B2BText);
		if (!((B2BBlinkRem/150)&1)) {
			string str = "B2B *"+IToS(B2BText);
			if (B2BRule==BR_Guildline) str="Back-to-Back";
			if (B2BRule==BR_Techmino) str="B2B "+IToS(B2B/10)+"."+IToS(B2B%10)+"%";
			PrintAction(str, 7, -2, 0.8, Mix(clr,BackgroundColor,rr), 0.15*rr, 1.5-0.5*rat*rat*rat*rat*rat);
		}
	}
	if (ComboAge>=5000) ComboAge=5000;
	else if (!Simple) {
		double rat = 1.0-max(0.0,min(1.0,ComboAge/5000.0));
		double rr = 1.0-(1.0-rat)*(1.0-rat);
		int clr = (ComboText)?Mix(0xFFFFFF,0x39C5BB,(double(min(21,ComboText)-1)/20.0)):0x888888;
		PrintAction(IToS(ComboText)+" Combo", 8, -2, 0.8, Mix(clr,BackgroundColor,rr), 0.15*rr, 1.5-0.5*rat*rat*rat*rat*rat);
	}
	double StatEnd = max(SX,((SX<4)?20:22));
	if ((!HideStat)&&(!Simple)) {
		if (QPMode) {
			PrintStrR("Boost", StatEnd-12.8, -2, 0.7, 0xEEEEEE);
			PrintStrR(IToS(QPRank), StatEnd-12, -2.1, 1, 0x99FFFF, 0.13);
			if (PlayerCount>1) PrintStrR(IToS(KOCount)+" KO\'s", StatEnd-11, -2, 0.7, 0x99FFFF);
		}
		else if (PlayerCount>1) {
			PrintStrR("Badge", StatEnd-12.8, -2, 0.7, 0xEEEEEE);
			PrintStrR(IToS(Badge), StatEnd-12, -2.1, 1, 0x99FFFF, 0.13);
			PrintStrR(IToS(KOCount)+" KO\'s", StatEnd-11, -2, 0.7, 0x99FFFF);
		}
		else if (UseLeveling()) {
			PrintStrR("Level", StatEnd-11.8, -2, 0.7, 0xEEEEEE);
			PrintStrR(IToS((Level<0)?max(LevBase,LevStart):Level), StatEnd-11, -2.1, 1, 0x99FFFF, 0.13);
		}
		ll t = abs(Time);
		PrintStrR("Time", StatEnd-9.8, -2, 0.7, 0xEEEEEE);
		int clr = 0x99FFFF;
		if (Time<0LL) PrintStrR(Format("-%02d:%02d.",int(t/60000LL),int(t/1000LL%60LL)), StatEnd-9, -2.1, 1, 0x99FFFF, 0.13);
		else {
			if (TarTime) {
				if (t<TarTime) {
					clr=(TypeTime)?0xEE82EE:0xFDD000; t=TarTime-t;
				}
				else {
					clr=(TypeTime)?0xFF6666:0x00FFCC; if (TypeTime) t=TarTime;
				}
			}
			PrintStrR(Format("%02d:%02d.",int(t/60000LL),int(t/1000LL%60LL)), StatEnd-9, -2.1, 1, clr, 0.13);
		}
		PrintStrR(Format("%03lld",t%1000LL), StatEnd-8.8, -1, 0.65, clr, 0.14);
		t = max(Time,1LL);
		PrintStrR("Piece", StatEnd-7.8, -2, 0.7, 0xEEEEEE);
		int val=TPiece; clr=0x99FFFF;
		if (TarPiece) {
			if (TPiece<TarPiece) {
				clr=(TypePiece)?0xEE82EE:0xFDD000; val=TarPiece-TPiece;
			}
			else {
				clr=(TypePiece)?0xFF6666:0x00FFCC; if (TypePiece) val=TarPiece;
			}
		}
		PrintStrR(IToS(val), StatEnd-7, -2, 1, clr, 0.13);
		PrintStrR(Format("%.2f /s",double(TPiece)/double(t)*1000.0), StatEnd-6, -2, 0.7, 0x99FFFF);
		PrintStrR("Line", StatEnd-5.3, -2, 0.7, 0xEEEEEE);
		val=TLine; clr=0x99FFFF;
		if (TarLine) {
			if (TLine<TarLine) {
				clr=(TypeLine)?0xEE82EE:0xFDD000; val=TarLine-TLine;
			}
			else {
				clr=(TypeLine)?0xFF6666:0x00FFCC; if (TypeLine) val=TarLine;
			}
		}
		PrintStrR(IToS(val), StatEnd-4.5, -2, 1, clr, 0.13);
		if ((TDig)||(TarDig)) {
			int tmp = IToS(val).size();
			val=TDig; clr=0x99FFFF;
			if (TarDig) {
				if (TDig<TarDig) {
					clr=(TypeDig)?0xEE82EE:0xFDD000; val=TarDig-TDig;
				}
				else {
					clr=(TypeDig)?0xFF6666:0x00FFCC; if (TypeDig) val=TarDig;
				}
			}
			PrintStrR(IToS(val), StatEnd-4.3, -2-tmp*2.0/3.0, 0.7, clr, 0.13);
		}
		PrintStrR(Format("%.2f /m",double(TLine)/double(t)*60000.0), StatEnd-3.5, -2, 0.7, 0x99FFFF);
		PrintStrR("Attack", StatEnd-2.8, -2, 0.7, 0xEEEEEE);
		val=TAttack; clr=0x99FFFF;
		if (TarAttack) {
			if (TAttack<TarAttack) {
				clr=(TypeAttack)?0xEE82EE:0xFDD000; val=TarAttack-TAttack;
			}
			else {
				clr=(TypeAttack)?0xFF6666:0x00FFCC; if (TypeAttack) val=TarAttack;
			}
		}
		PrintStrR(IToS(val), StatEnd-2, -2, 1, clr, 0.13);
		if (IntRule==IR_Accumulate) {
			int val = max(0,min(99,int(round(AttackAcc*100.0))));
			PrintStrL(IToS(val/10)+IToS(val%10), StatEnd-1.7, -1.3, 0.7, 0x99FFFF, 0.13);
		}
		PrintStrR(Format("%.2f /m",double(TAttack)/double(t)*60000.0), StatEnd-1, -2, 0.7, 0x99FFFF);
		if (Stock>0) {
			PrintStrR("Stock", StatEnd-0.3, -2, 0.7, 0xEEEEEE);
			PrintStrR(IToS((Over)?0:StockRem+1), StatEnd+0.5, -2, 1, clr, 0.13);
			PrintStrR("/ "+IToS(Stock+1), StatEnd+1.5, -2, 0.7, 0x99FFFF);
		}
		if (QPMode) {
			PrintStrL("Score", StatEnd-2.8, LY+1, 0.7, 0xEEEEEE);
			ll val=DQPScore; clr=0x99FFFF;
			if (TarScore) {
				if (TQPScore<TarScore) {
					clr=(TypeScore)?0xEE82EE:0xFDD000; val=TarScore-DQPScore;
				}
				else {
					clr=(TypeScore)?0xFF6666:0x00FFCC; if (TypeScore) val=TarScore;
				}
			}
			PrintStrL(IToS(val/1000LL)+".", StatEnd-2, LY+1, 1, clr, 0.13);
			PrintStrL(Format("%02lld",val%1000LL/10LL), StatEnd-1.8, LY+1.4+IToS(val/1000LL).size()*2.0/3.0, 0.7, clr, 0.13);
			PrintStrL("Stage "+IToS(Level), StatEnd-1, LY+1, 0.7, 0x99FFFF);
			PrintStrL(Format("%.1f",double(QPLevTime)/1000.0), StatEnd, LY+1, 0.7, 0xDDDDDD);
		}
		else if (PlayerCount>1) {
			PrintStrL("Rank", StatEnd-2.8, LY+1, 0.7, 0xEEEEEE);
			PrintStrL(IToS(Rank), StatEnd-2, LY+1, 1, 0x99FFFF, 0.13);
			PrintStrL("/ "+IToS(PlayerCount), StatEnd-1, LY+1, 0.7, 0x99FFFF);
		}
		else {
			PrintStrL("Score", StatEnd-2.8, LY+1, 0.7, 0xEEEEEE);
			ll val=DScore; clr=0x99FFFF;
			if (TarScore) {
				if (TScore<TarScore) {
					clr=(TypeScore)?0xEE82EE:0xFDD000; val=TarScore-DScore;
				}
				else {
					clr=(TypeScore)?0xFF6666:0x00FFCC; if (TypeScore) val=TarScore;
				}
			}
			PrintStrL(IToS(val), StatEnd-2, LY+1, 1, clr, 0.13);
			PrintStrL(Format("%.2f /p",double(TScore)/double(max(1,TPiece))), StatEnd-1, LY+1, 0.7, 0x99FFFF);
		}
	}
	if (HideStat) {
		bool ff = ((UseClick)&&(!CursorMoveLock));
		PrintStrR("Guide", StatEnd-13, -2, 0.8, 0xFDD000);
		PrintStrR("Move Piece", StatEnd-11.8, -2, 0.7, 0xEEEEEE);
		PrintStrR((ff)?"Move Cursor":GetKeyChar(A_Left)+" "+GetKeyChar(A_Right), StatEnd-11, -2, (ff)?0.85:1, 0x99FFFF);
		PrintStrR("Rotate Piece", StatEnd-9.8, -2, 0.7, 0xEEEEEE);
		PrintStrR((ff)?"Tap Right":GetKeyChar(A_Rotate)+" "+GetKeyChar(A_CW)+" "+GetKeyChar(A_CCW), StatEnd-9, -2, (ff)?0.85:1, 0x99FFFF);
		PrintStrR("Drop Faster", StatEnd-7.8, -2, 0.7, 0xEEEEEE);
		PrintStrR((ff)?"Hold Left":GetKeyChar(A_SoftDrop)+" "+GetKeyChar(A_SonicDrop), StatEnd-7, -2, (ff)?0.85:1, 0x99FFFF);
		PrintStrR("Place Piece", StatEnd-5.8, -2, 0.7, 0xEEEEEE);
		PrintStrR((ff)?"Tap Left":GetKeyChar(A_HardDrop), StatEnd-5, -2, (ff)?0.85:1, 0x99FFFF);
		PrintStrR("Hold Piece", StatEnd-3.8, -2, 0.7, 0xEEEEEE);
		PrintStrR((ff)?"Hold Right":GetKeyChar(A_Hold), StatEnd-3, -2, (ff)?0.85:1, 0x99FFFF);
		PrintStrR("Retry", StatEnd-1.8, -2, 0.7, 0xEEEEEE);
		PrintStrR(GetKeyChar(A_Retry), StatEnd-1, -2, 1, 0x99FFFF);
		PrintStrL("Score", StatEnd-2.8, LY+1, 0.7, 0xEEEEEE);
		PrintStrL(IToS(DScore), StatEnd-2, LY+1, 1, 0x99FFFF, 0.13);
		PrintStrL(Format("%.2f /p",double(TScore)/double(max(1,TPiece))), StatEnd-1, LY+1, 0.7, 0x99FFFF);
	}
	for (Particle o : Particles) Poly(o.x,o.y,o.sz,o.n,o.clr,o.th);
	for (FloatText o : FloatTexts) PrintStrM(o.Text,o.x,o.y,o.sz,o.clr,o.bd,o.w,"",o.th);
	if (TargetSta==1) {
		PrintStrM("Complete", 0.5*SX-1, 0.5*LY-0.4, 1.5, 0xAAFFAA, 0.15);
		PrintStrM("Press "+GetKeyChar(A_Retry)+" to Retry", 0.5*SX+1, 0.5*LY-0.4, 0.75, 0xDDDDDD, 0.15);
	}
	else if (TargetSta==-1) {
		PrintStrM("Finish", 0.5*SX-1, 0.5*LY-0.4, 1.5, 0xFDD000, 0.15);
		PrintStrM("Press "+GetKeyChar(A_Retry)+" to Retry", 0.5*SX+1, 0.5*LY-0.4, 0.75, 0xDDDDDD, 0.15);
	}
	else if (Over) {
		PrintStrM("Game Over", 0.5*SX-1, 0.5*LY-0.4, 1.5, 0xFF6666, 0.15);
		PrintStrM("Press "+GetKeyChar(A_Retry)+" to Retry", 0.5*SX+1, 0.5*LY-0.4, 0.75, 0xDDDDDD, 0.15);
	}
	else if (Pause) {
		PrintStrM("Paused", 0.5*SX-1, 0.5*LY-0.4, 1.5, 0x66CCFF, 0.15);
		PrintStrM("Press "+KeyChar(KeyPause)+" to Resume", 0.5*SX+1, 0.5*LY-0.4, 0.75, 0xDDDDDD, 0.15);
	}
}

#warning Controller_Part

void Board::GetActions() {
	int TimeDel = GlobalRealTimeDel;
	vector<Action> res;
	bool Active = CheckActive();
	for (Monitor &o : Keys) {
		if ((Bot)&&(!AvailBotAction(o.Result))) continue;
		bool Ignore = false;
		if (o.Result==A_Left) Ignore=((!Active)||(!CheckLeft()));
		if (o.Result==A_Right) Ignore=((!Active)||(!CheckRight()));
		if (o.Result==A_SoftDrop) Ignore=((!Active)||((!CheckDown())&&(!UseDeep)));
		if (o.Result==A_Rotate) Ignore=((!PreActive)&&((!Active)||(!CheckRotate(DefaultRot))));
		if (o.Result==A_CW) Ignore=((!PreActive)&&((!Active)||(!CheckRotate(1))));
		if (o.Result==A_Half) Ignore=((!PreActive)&&((!Active)||(!CheckRotate(2))));
		if (o.Result==A_CCW) Ignore=((!PreActive)&&((!Active)||(!CheckRotate(3))));
		if (o.Result==A_Up) Ignore=((!Active)||(!CheckUp()));
		if ((o.Result==A_Hold)||(o.Result==A_Waste)||(o.Result==A_Reset)
		||(o.Result==A_Lock)||(o.Result==A_Flip)) Ignore=((!Active)&&(!PreActive));
		if (((Pause)||(Over))&&(o.Result!=A_Retry)) Ignore=true;
		int cnt = o.Output(TimeDel,Ignore);
		for (int i=0; i<cnt; ++i) res.push_back(o.Result);
	}
	Actions = res;
}
void UpdateKeySta() {
	for (int i=0; i<256; ++i) {
		if (Focus) UniformKeySta[i]=bool(GetAsyncKeyState(i)&0x8000);
		else UniformKeySta[i]=false;
	}
}
void Frame_Controller(int TimeDel, int RealTimeDel) {
	POINT p;
	int n = Boards.size();
	Focus = (hWnd==GetFocus());
	if (Focus) {
		GetCursorPos(&p); ScreenToClient(GetForegroundWindow(),&p);
	}
	else p.x=p.y=0;
	CurX = p.y*0.0412*500.0/double(Scale);
	CurY = p.x*0.04015*500.0/double(Scale);
	UpdateKeySta();
	int SelBoard = -1;
	for (Board *p : Boards) p->CheckCursor();
	LeftClick=RightClick=MidClick=false;
	LeftUp=RightUp=0;
	if (CheckKey(VK_LBUTTON)) LeftHold+=RealTimeDel;
	else {
		LeftUp=LeftHold; LeftHold=0;
	}
	if (CheckKey(VK_RBUTTON)) RightHold+=RealTimeDel;
	else {
		RightUp=RightHold; RightHold=0;
	}
	for (Board *p : Boards) p->GetActions();
}

#warning Main_Part

int LastPaint = 0;
const int MinFrameTime = 8;
void Frame(int TimeDel) {
	int RealTimeDel = TimeDel;
	LastPaint = min(MinFrameTime*2,TimeDel+LastPaint);
	if (LastPaint>=MinFrameTime) {
		Frame_View(); LastPaint-=MinFrameTime;
	}
	Frame_Controller(TimeDel, RealTimeDel);
	Frame_Model(TimeDel);
}
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpCmdLine, int iCmdShow) {
	wc.style = CS_OWNDC;
	wc.lpfnWndProc = WndProc;
	wc.cbClsExtra = 0;
	wc.cbWndExtra = 0;
	wc.hInstance = hInstance;
	wc.hIcon = LoadIcon(NULL, IDI_APPLICATION);
	wc.hCursor = LoadCursor(NULL, IDC_ARROW);
	wc.hbrBackground = HBRUSH(GetStockObject(BLACK_BRUSH));
	wc.lpszMenuName = NULL;
	wc.lpszClassName = "GLSample";
	RegisterClass(&wc);
	//hWnd = GetConsoleWindow();
	int SystemWidth = GetSystemMetrics(SM_CXSCREEN);
	int SystemHeight = GetSystemMetrics(SM_CYSCREEN);
	Scale = min(SystemWidth/3,SystemHeight/2);
	hWnd = CreateWindow("GLSample", "TETR", WS_CAPTION|WS_POPUPWINDOW|WS_VISIBLE, 0, 0, Scale*3, Scale*2, NULL, NULL, hInstance, NULL);
	EnableOpenGL(hWnd, &hDC, &hRC);
	//midiOutOpen(&SHnd, 0, 0, 0, CALLBACK_NULL);
	ll CurTime=GetClock(), CurRealTime=clock();
	ReadConfig();
	Init();
	while (!bQuit) {
		if (PeekMessage(&msg,NULL,0,0,PM_REMOVE)) {
			if (msg.message==WM_QUIT) bQuit=true;
			else {
				TranslateMessage(&msg);
				DispatchMessage(&msg);
			}
			continue;
		}
		else {
			ll NewTime=GetClock(), NewRealTime=clock();
			GlobalRealTimeDel = int(NewRealTime-CurRealTime);
			Frame(int(NewTime-CurTime));
			CurTime=NewTime; CurRealTime=NewRealTime;
		}
	}
	DisableOpenGL(hWnd, hDC, hRC);
	DestroyWindow(hWnd);
	WriteConfig();
	return msg.wParam;
}