All pastes #517573 Raw Edit

Anonymous

public text v1 · immutable
#517573 ·published 2007-05-29 02:29 UTC
rendered paste body
#include "../devkitARM/arm-elf/include/math.h"
#include "../tonc_code_/code/tonclib/tonc_gba.h"
#include "./gfx/test.h"
#include "./gfx/font.h"
#include "./snd/Sound.h"
#include "./posprintf.h"

#define RGB8(r,g,b)	( (((b)>>3)<<10) | (((g)>>3)<<5) | ((r)>>3) )

#define TETR_O 0  //Pieces
#define TETR_S 1
#define TETR_Z 2
#define TETR_I 3
#define TETR_T 4
#define TETR_L 5
#define TETR_J 6

#define STANDARD 0 //Drop types
#define FIRM 1
#define SOFT 2

#define DAS 12 //Frame values, may use variable system in the future
#define ARE 18
#define LOCK 30
#define CLEAR 30

#define ROLLS 6 //Number of piece rolls before allowing use of piece in history

#define R 0
#define G 1
#define B 2

#define FONTBASE 9

#define GAMEPAK_RAM  ((u8*)0x0E000000)
u8 *Save = GAMEPAK_RAM;

//-------------------------tables-------------------------
const int block_x[7][4][4] = {
	{{1,2,1,2},{1,2,1,2},{1,2,1,2},{1,2,1,2},}, //O
	{{0,1,1,2},{1,1,2,2},{0,1,1,2},{1,1,2,2},}, //S
	{{2,1,1,0},{1,1,0,0},{2,1,1,0},{1,1,0,0},}, //Z
	{{0,1,2,3},{1,1,1,1},{0,1,2,3},{1,1,1,1},}, //I
	{{0,1,2,1},{1,0,1,1},{1,0,1,2},{1,2,1,1},}, //T
	{{0,1,2,0},{0,1,1,1},{2,0,1,2},{1,1,1,2},}, //L
	{{0,1,2,2},{0,1,1,1},{0,0,1,2},{1,1,1,2},}, //J
};

const int block_y[7][4][4] = {
	{{1,1,2,2},{1,1,2,2},{1,1,2,2},{1,1,2,2},}, //O
	{{1,1,2,2},{1,2,0,1},{1,1,2,2},{1,2,0,1},}, //S
	{{1,1,2,2},{1,2,0,1},{1,1,2,2},{1,2,0,1},}, //Z - same as S, change made in block_x
	{{1,1,1,1},{0,1,2,3},{1,1,1,1},{0,1,2,3},}, //I
	{{1,1,1,2},{0,1,1,2},{1,2,2,2},{0,1,1,2},}, //T
	{{1,1,1,2},{0,0,1,2},{1,2,2,2},{0,1,2,2},}, //L
	{{1,1,1,2},{2,0,1,2},{1,2,2,2},{2,1,0,0},}, //J
};

const int colors[7][3] = {
	{255,255,0,},{0,255,0,},{255,0,0},{0,255,255,},{191,0,255,},{255,127,0,},{0,0,255,},};

const float CGrav[15] = {0.125f, 0.25f, .5f, .75f, 1.0f, 1.2f, 1.8f, 2.0f, 2.4f, 3.0f, 4.0f, 5.0f, 6.0f, 8.0f, 20.0f};

const float MStartGrav[5] = {1.0f/64.0f, 1.0f/3.5f, 1/64.0f, 2.0f, 5.0f};
const float MEndGrav[5] = {1.0f/3.5f, 1.0f/1.8f, 1.0f, 4.0f, 3.0f};

const int ClassReq[20] = {0, 400, 800, 1400, 2000, 3500, 5500, 8000, 12000,
							16000, 22000, 30000, 40000, 52000, 66000, 82000,
							100000, 120000, 126000, 1000000};

//----------------------game text-------------------------
const char m_Modes[17][8] = {"Master \0",
							"Accel. \0",
							".125 G \0",
							".25 G  \0",
							".50 G  \0",
							".75 G  \0",
							"1.0 G  \0",
							"1.2 G  \0",
							"1.8 G  \0",
							"2.0 G  \0",
							"2.4 G  \0",
							"3.0 G  \0",
							"4.0 G  \0",
							"5.0 G  \0",
							"6.0 G  \0",
							"8.0 G  \0",
							"20  G  \0",};

const char Class[19][4] = {"9 \0",
							"8 \0",
							"7 \0",
							"6 \0",
							"5 \0",
							"4 \0",
							"3 \0",
							"2 \0",
							"1 \0",
							"S1\0",
							"S2\0",
							"S3\0",
							"S4\0",
							"S5\0",
							"S6\0",
							"S7\0",
							"S8\0",
							"S9\0",
							"GM\0",};

//----------------------global variables-------------------------
struct {int tx; int ty; int rot; int type;
		int vr; int vx; int prev_vx; int vy;
		int das; float drop; int lock;
		int clear; int live;
		int lines; int level; int mode;
		int score; int curr_lines; int row_forced; int combo;
		int bravo;
		int rank; int GM_Req;} P;

int history[4];
int are;
int playfield[10][23];

int timer[3];

int counter;

int frames;

BGINFO bg0;


typedef struct _SOUND_CHANNEL
{
   s8 *data;       // pointer to the raw sound data in ROM (0 for inactive channel)
   u32 pos;        // current position in the data (20.12 fixed-point)
   u32 inc;        // increment (20.12 fixed-point)
   u32 vol;        // volume (0-64, sort of 1.6 fixed-point)
   u32 length;     // length of the whole sound (20.12 fixed-point)
   u32 loopLength; // length of looped portion (20.12 fixed-point, 0 for no loop)

} SOUND_CHANNEL;

typedef struct _SOUND_VARS
{
   s8 *mixBufferBase;
   s8 *curMixBuffer;
   u32 mixBufferSize;
   u8 activeBuffer;
} SOUND_VARS;

extern SOUND_VARS soundVars; 
extern SOUND_CHANNEL channel[4];

//----------------------function definitions-------------------------
void GfxInit();
void VarInit();
void BgClear();

void Title();
void Menu();
void PrepGame();
void BgHudSetup();
void ReadyGo();
void GameLoop();
void Pause();
void GameOver();
void WaitForConfirmation();

void Timer();

void PieceControl(int in_are);
void MoveTetrimino(int in_are);
float GMaster(int level);
void ApplyGravity();
void LockTetrimino();
void ClearLine(int line);

void CueTetrimino();
int RandTetrimino(int first);

void CheckLoss();
int CollideTetrimino(int x, int y, int r);
void DispTetrimino();
void DispPlayfield();
void DispNext();
void DispHud();

int DimColor(int color, float dim);

void GfxUpdate();

void SramSaveInt(int val, int pos);
int SramLoadInt(int pos);

//***************************************************************

/*int rand(int max)
{
	seed++;
    seed *= 69069;
    return (unsigned int)(seed) % max;
}*/

#define RAND_A 1588635695
#define RAND_R 1117695901 
/*  linear congruential generator.
  GBA tuning by Clifton Cline (phirewind)*/
u32 seed = 93186752;
u32 random_seed_vbl = 93186752; 

// Seed the generator
void rand_seed (u32 init){
    if (init != 0)
        seed = init;}

// return a random number from 0 to n
u16 rand (u16 n){
    // generate a new 32-bit seed
    seed = RAND_A * ( seed & 1 ) - RAND_R * ( seed >> 1 );
   
    // apply the new seed to the range (n)
    return ( n * (seed >> 16) ) >> 16;} 

char string[32];
int convert(conv){
  if((conv >= 'a') && (conv <= 'z'))
	  conv -= 32;
  if((conv >= 'A') && (conv <= 'Z'))
    return conv - 0x41 + FONTBASE;
  else if((conv >= '0') && (conv <= '9'))
    return conv - '0' + 26 + FONTBASE;
  else if(conv == '-')
    return conv - '-' + 36 + FONTBASE;
	else if(conv == '.')
    return conv - '.' + 37 + FONTBASE;
  else
    return 0;}

void draw_string(int str_x, int str_y, const char *msg){
	int i;
	int offset = str_x + str_y * 32;
	for (i = 0; msg[i] != 0; ++i)
	  bg0.map[offset + i] = convert(msg[i]);}

int frames_this = 1, frames_last, vsyncs_this;
void DispFps(){
    posprintf(&string[0], "%02d", frames_last);
    draw_string(28,18, &string[0]);
	posprintf(&string[0], "%02d", vsyncs_this);
    draw_string(28,19, &string[0]);
}

void fps(){
	rand_seed(random_seed_vbl++);
    vsyncs_this++;
	if(vsyncs_this >= 60){
	  frames_last = frames_this;
	  frames_this = 0;
	  vsyncs_this = 0;}
    REG_IF |= IF_VBLANK;
}

void SetFpsInterrupt(){
    REG_INTMAIN= fps;
    REG_DISPSTAT |= DSTAT_VBL_IRQ;
    REG_IE |= IF_VBLANK;
    REG_IME= 1;
}

//***************************************************************

int screen = 0;
int main(){
	GfxInit();
	SoundInit();
	SetFpsInterrupt();
	void (*CurrentAction)();
	while(1){
		vid_vsync();
		SoundVsync();
		frames_this++;
		key_poll();
		if(key_is_down(KEY_RESET) == KEY_RESET){
			BgClear();
			int i;
			for(i = 0; i < 4; i++){
				oe_set_pos(&oe_buffer[i], 240, 160);
				SoundStop(i);}
			screen = 0;}

		switch(screen){
			case 0:
				CurrentAction = Title;
				break;
			case 1:
				CurrentAction = Menu;
				break;
			case 2:
				CurrentAction = PrepGame;
				break;
			case 3:
				CurrentAction = ReadyGo;
				break;
			case 4:
				CurrentAction = GameLoop;
				break;
			case 5:
				CurrentAction = GameOver;
				break;
			case 6:
				CurrentAction = Pause;
				break;
		}
		CurrentAction();
		DispFps();
		GfxUpdate();
	}
}

//******************************************************************
//******************************************************************
//******************************************************************

void GfxInit(){
	int i;

	dma_memcpy(&tile_mem[CBB_OBJ][0], testTiles, testTilesLen);  //copy gfx
	dma_memcpy(&tile_mem[0][0], testTiles, testTilesLen);
	dma_memcpy(&tile_mem[0][(FONTBASE<<1)], fontTiles, fontTilesLen);

	dma_memcpy(pal_obj_mem, testPal, testPalLen);
	dma_memcpy(pal_bg_mem, testPal, testPalLen);
	oam_init();
	
	for(i = 0; i < 7; i++)
		pal_bg_mem[i + 1] = DimColor(i, .7);
		//pal_bg_mem[i] = testPal[16 + i];

	REG_DISPCNT = DCNT_OBJ_ON | DCNT_OBJ_1D | _DCNT_MODE0 | DCNT_BG0_ON;  //set modes

	for(i = 0; i < 4; i++){
	  oe_set_attr(&oe_buffer[i], (_OE_A0_SQUARE | OE_A0_256C),
		  _OE_A1_SIZE_8,
		  2);
	  oe_set_pos(&oe_buffer[i], 240, 160);}
	
	bg_init(&bg0, 0, 0, 2, (_BG_REG_S32 | BG_256C));
	bg_set_pos(&bg0, 0, 0);
}

void VarInit(){
	counter = 0;
	P.live = 1; P.lines = 0; P.level = 0; P.score = 0;
	P.das = 0; P. clear = 0; P.curr_lines = 0; P.row_forced = 0; P.combo = 1;
	P.bravo = 1;
	P.rank = 0; P.GM_Req = 0;
	
	int i, ii;
	for(i = 0; i < 3; i++)
		timer[i] = 0;

	for(i = 0; i < 10; i++)
		for(ii = -3; ii < 20; ii++)
			playfield[i][ii + 3] = 0;

	int x = TETR_Z;
	for(i = 0; i < 4; i++){
		history[i] = x;
		x ^= TETR_S;}

	while(RandTetrimino(1));

	for(i = 0; i < 3; i++)
		RandTetrimino(0);
		CueTetrimino();
}

void BgClear(){
	int i;
	for(i = 0; i < 32 * 32; i++)
		bg0.map[i] = 0;}

void Title(){
	BgClear();
	int i;
	for(i = 0; i < 4; i++){
		oe_set_pos(&oe_buffer[i], 240, 160);
		SoundStop(i);}
	//seed = SramLoadInt(0);

	posprintf(&string[0], "tetris grand master");
	draw_string(0,0,&string[0]);

	posprintf(&string[0], "wip clone");
	draw_string(0,1,&string[0]);

	posprintf(&string[0], "speed select");
	draw_string(0,3,&string[0]);

	P.mode = 0;
	screen = 1;
}

void Menu(){
		posprintf(&string[0], "%s", &m_Modes[P.mode][0]);
		draw_string(1,4,&string[0]);
		if(key_hit(KEY_RIGHT))
			P.mode++;
		else if(key_hit(KEY_LEFT))
			P.mode--;
		if(P.mode < 0)
			P.mode = 16;
		P.mode %= 17;
		if(key_hit(KEY_A|KEY_START))
			screen = 2;}

void PrepGame(){
		BgClear();
		BgHudSetup();
		VarInit();
		screen = 3;}

void BgHudSetup(){
	int i;
	for(i = 10; i < 32 * 20; i += 32)
		bg0.map[i] = 8;

	posprintf(&string[0], "next");
	draw_string(12,0,&string[0]);
	
	posprintf(&string[0], "lines");
	draw_string(19,0,&string[0]);

	posprintf(&string[0], "level");
	draw_string(19,3,&string[0]);
	posprintf(&string[0], "---");
	draw_string(19,5,&string[0]);

	posprintf(&string[0], "points");
	draw_string(19,9,&string[0]);

	posprintf(&string[0], "rank");
	draw_string(12,12,&string[0]);
	
	posprintf(&string[0], "rank up at");
	draw_string(12,15,&string[0]);}

void ReadyGo(){
	DispTetrimino();

	counter++;
		if(counter < 60)
			posprintf(&string[0], "ready?");
		else if(counter < 120)
			posprintf(&string[0], " go   ");
		else{
			posprintf(&string[0], "      ");
			screen = 4;}
		draw_string(2,10,&string[0]);}

void GameLoop(){
	if(key_hit(KEY_START))
		screen = 6; //simple abuseable pause, will be replaced later

	Timer();

	if((!are)&&(!P.clear)){
		PieceControl(are);
		MoveTetrimino(are);
		DispTetrimino();
	}
	if(P.clear){
		if(P.clear == 1){
			SoundPlay(&LINE_CLEAR_DATA, LINE_CLEAR_LENGTH, 18157, 1, 64);

			P.score += ((int)(((P.level + P.curr_lines)/4) + 0.5f) + P.row_forced) * P.curr_lines * P.combo * P.bravo;
			if(P.score > 999999)
				P.score = 999999;}

		if(P.clear == CLEAR - 1)
			SoundPlay(&LINE_DROP_DATA, LINE_DROP_LENGTH, 18157, 2, 64);

		if(P.score > ClassReq[P.rank + 1]){
			P.rank++;
			if(P.rank == 8)
				if(P.level < 300)
					P.GM_Req++;
			if(P.rank == 12)
				if(P.level < 500)
					P.GM_Req++;
			if(P.rank == 17)
				P.GM_Req++;}
			//The last GM condition is that the player reach level 999 before 13:30:00, which is checked in Game Over.

		P.clear++;
		P.clear %= CLEAR;
	}
	else if(are){
		if(are == 1)
			CueTetrimino();
		if(are == ARE - 1){
			CheckLoss();
			MoveTetrimino(are);
			}
		PieceControl(are);
		are++;
		are %= ARE;
	}
	if(!P.clear)
		DispPlayfield();
	if(P.level >= 999){
		P.level = 999;
		P.live = 0;}
	DispHud();

	if(!P.live)
		screen = 5;
	//SramSaveInt(seed, 0);
}

void Pause(){
	if(key_hit(KEY_START))
		screen = 4;}

void GameOver(){
	posprintf(&string[0], "game over");
	draw_string(20,12,&string[0]);

	if(P.level == 999){
		if(P.GM_Req == 3)
			if(timer[2] * 60 + timer[1] < 810){
				P.rank++;
				DispHud();}

		posprintf(&string[0], "all clear");
		draw_string(20,12,&string[0]);}

	if(key_hit(KEY_START))
		screen = 0;
	}

void Timer(){
	timer[0]++;
	if(timer[0] >= 60){
		timer[1]++;
		timer[0] = 0;}
	if(timer[1] >= 60){
		timer[2]++;
		timer[1] = 0;}}

//******************************************************************
//******************************************************************
//******************************************************************

void PieceControl(int in_are){
	u16 (*key_are)(u16 key);
	if(in_are)
		key_are = key_is_down;
	else key_are = key_hit;

	if(key_are(KEY_B))
		P.vr = -1;
	else if(key_are(KEY_A))
		P.vr = 1;
	else P.vr = 0;

	P.prev_vx = P.vx;
	if(key_is_down(KEY_LEFT))
		P.vx = -1;
	else if(key_is_down(KEY_RIGHT))
		P.vx = 1;
	else P.vx = 0;

	if(P.prev_vx == P.vx)
		if(P.das < DAS)
			P.das++;
		else;
	else P.das = 0;
	
	if(!in_are){
		if(key_hit(KEY_UP))
			P.vy = FIRM;
		else if(key_is_down(KEY_DOWN))
			P.vy = SOFT;
		else P.vy = STANDARD;}
}

void MoveTetrimino(int in_are){
	if(!in_are){
		if(P.vx)
			if((P.das == 0)||(P.das == DAS))
				if(!CollideTetrimino(P.tx + P.vx, P.ty, P.rot))
					P.tx += P.vx;}

	int i = 1, ii = 0;
	if(P.vr)
		while(ii < 3){
			if(!CollideTetrimino((P.tx + (i - 1)), P.ty, P.rot + P.vr)){
				P.rot += P.vr;
				P.rot &= 3;
				P.tx += (i - 1);
				if(in_are)
					SoundPlay(&ARE_ROT_DATA, ARE_ROT_LENGTH, 18157, 0, 64);
				ii = 4;}
			if(in_are)
				break;
			i++; i %= 3;
			ii++;
			if((P.type == TETR_I)||(P.type == TETR_O))
				break;
			if((P.type == TETR_L)||(P.type == TETR_J))
				if(P.rot == 0)
					if(playfield[P.tx + 1][P.ty + 1])
						break;
			if(P.type == TETR_T)
				if(P.rot == 2)
					if(playfield[P.tx + 1][P.ty])
						break;
		}

	if(!P.mode)
		P.drop += GMaster(P.level);
	else if(P.mode == 1)
		P.drop += ((float)(P.level + 1)/60.0f);
	else P.drop += CGrav[P.mode - 2];

	if(!in_are){
		if(P.vy == FIRM){
			while(!CollideTetrimino(P.tx, P.ty + 1, P.rot)){
				P.lock = 0;
				P.ty++;
			    P.row_forced++;}}
		else if(P.vy == SOFT){
			if(P.drop < 1){
				P.drop = 1;
				P.row_forced++;}
			P.lock = 254;}
		ApplyGravity();}
}

float GMaster(int level){
	int lv_mod, ramp = 0, set = 0;
	float g;
	lv_mod = level % 100;
	ramp = (int)(lv_mod/10);           
	set = (int)(level/100);
	
	if(set < 2){
		ramp = (ramp>>1);
	}
	if(set > 4)
		return 20;

	g = (float)(((MStartGrav[set] * (float)((10 - ramp)/10.0f)) + (MEndGrav[set] * (float)((ramp)/10.0f))));
	return g;
}


void ApplyGravity(){
	if(!CollideTetrimino(P.tx, P.ty + 1, P.rot)){
		if(P.drop >= 1){
			pal_obj_mem[P.type + 1] = DimColor(P.type, 1);
			P.lock = 0;
			P.drop--;
			P.ty++;
			ApplyGravity();}}
	else{
		pal_obj_mem[P.type + 1] = DimColor(P.type, .5 + (.5 * ((float)LOCK - P.lock)/(float)LOCK));
		P.lock++; P.drop = 0;
		if(P.lock == 1)
			SoundPlay(&COLLIDE_DATA, COLLIDE_LENGTH, 18157, 1, 64);}
	if(P.lock > LOCK)
		LockTetrimino();}

void LockTetrimino(){
	are = 1;
	int i, ii, clear;
	for(i = 0; i < 4; i++)
		playfield[P.tx + block_x[P.type][P.rot][i]]
					[P.ty + block_y[P.type][P.rot][i] + 3] = P.type + 1;
		for(i = 0; i < 20; i++){
			clear = 1;
			for(ii = 0; ii < 10; ii++)
				if(!playfield[ii][i + 3]){
					clear = 0; ii = 11;}
			if(clear){
				ClearLine(i);
				P.curr_lines++;}
		}

	if(P.curr_lines){
		for(i = 0; i < 10; i++)
			for(ii = 0; ii < 20; ii++)
				if(!playfield[i][ii + 4])
					P.bravo = 4;
				else{P.bravo = 1; break;}
		P.combo = (P.combo - 1) + ((P.curr_lines<<1) - 1);}
	else P.combo = 1;

	//DispPlayfield();
	SoundPlay(&SET_DATA, SET_LENGTH, 18157, 2, 64);

	int mod;
	if(P.level < 900)
		mod = 99;
	else mod = 98;

	if(P.level % 100 != mod)
		P.level++;
	P.tx = 30;
	P.ty = 30;
	for(i = 0; i < 4; i++){
	  oe_set_attr(&oe_buffer[i], (_OE_A0_SQUARE | OE_A0_256C),
		  _OE_A1_SIZE_8,
		  0);
	  oe_set_pos(&oe_buffer[i], 240, 160);
	dma_memcpy(pal_obj_mem, testPal, testPalLen);}
}

void ClearLine(int line){
	int i, ii;
	for(i = 0; i < 10; i++)
		playfield[i][line + 3] = 0;

	for(i = 0; i < 10; i++)
		playfield[i][0] = 0;

	DispPlayfield();
	for(i = line; i >= -2; --i)
		for(ii = 0; ii < 10; ii++)
			playfield[ii][i + 3] = playfield[ii][i + 2];
	P.lines++; P.clear = 1;
	P.level++;
}

int CollideTetrimino(int x, int y, int r){
	int i, collision = 0;
	for(i = 0; i < 4; i++)
		if(!(y + block_y[P.type][(r&3)][i] < 0))
			if(playfield[x + block_x[P.type][(r&3)][i]]
						[y + block_y[P.type][(r&3)][i] + 3]){
							collision = 1; i = 5;}

	if(!collision)
		for(i = 0; i < 4; i++){
			if((x + block_x[P.type][(r&3)][i] < 0)||(x + block_x[P.type][(r&3)][i] > 9)){
				collision = 1; i = 5;}
			//else if((y + block_y[P.type][(r&3)][i] < 0)||(y + block_y[P.type][(r&3)][i] > 19))  //Okay, so apparently you can rotate out of the upper bound. My bad.
			else if(y + block_y[P.type][(r&3)][i] > 19){
				collision = 1; i = 5;}}

	return collision;}

void CueTetrimino(){
	P.tx = 3;   //standard tile positions for entering tetrimino
	P.ty = -1; //This works until IRS is implemented
	P.rot = 0;
	P.lock = 0;

	P.curr_lines = 0;
	P.row_forced = 0;

	P.type = history[0];
	RandTetrimino(0);
	DispNext();
}

int RandTetrimino(int first){
	int i, ii, piece, used;

	for(i = 0; i < ROLLS; i++){
		used = 0;
		piece = rand(7)%7;
		for(ii = 0; ii < 4; ii++)
			if(history[ii] == piece){
				used = 1; ii = 5;}
		if(!used)
			i = ROLLS + 1;}
	if(first)
		if((piece == TETR_S)||(piece == TETR_Z)||(piece == TETR_O))
			return 1;

	for(i = 0; i < 4; i++)
		history[i] = history[i + 1];
	history[3] = piece;
	
	return 0;
}

void CheckLoss(){
	if(CollideTetrimino(P.tx, P.ty, P.rot)){
			LockTetrimino();
			GfxUpdate();
			P.live = 0;}
}

void DispTetrimino(){
	int i;
	for(i = 0; i < 4; i++){
      oe_set_pos(&oe_buffer[i], ((P.tx + block_x[P.type][P.rot][i])<<3),
								((P.ty + block_y[P.type][P.rot][i])<<3));
	  oe_buffer[i].attr2 = ((P.type + 1)<<1)&OE_A2_ID_MASK;}}

void DispPlayfield(){
	int i, ii;
	for(i = 0; i < 10; i++)
		for(ii = 0; ii < 20; ii++)
			bg0.map[i + ii * 32] = playfield[i][ii + 3];
	for(i = 0; i < 10; i++)
		for(ii = 0; ii < 3; ii++)
			bg0.map[(i + 11) + (ii + 17) * 32] = playfield[i][ii];}

void DispNext(){
	int i, ii, iii;
	for(iii = 0; iii < 3; iii++){
	for(i = 0; i < 4; i++)
		for(ii = 0; ii < 4; ii++)
			bg0.map[(i + 12 + ((iii || 0)<<1)) + (ii + (iii * 3) + 1) * 32] = 0;
	for(i = 0; i < 4; i++)
		bg0.map[(block_x[history[iii]][0][i] + 12  + ((iii || 0)<<1)) + 
		(block_y[history[iii]][0][i] + (iii * 3) + 1) * 32] = history[iii] + 1;}}

void DispHud(){
	posprintf(&string[0], "%d", P.lines);
	draw_string(19,1,&string[0]);
	posprintf(&string[0], "%03d", P.level);
	draw_string(19,4,&string[0]);

	int set;
	set = (((int)(P.level/100)+1)*100);
	if(set == 1000)
		set--;
	posprintf(&string[0], "%03d", set);
	draw_string(19,6,&string[0]);

	posprintf(&string[0], "%6d", P.score);
	draw_string(19,10,&string[0]);
	
	posprintf(&string[0], "%s", Class[P.rank]);
	draw_string(13,13,&string[0]);

	posprintf(&string[0], "%d", ClassReq[P.rank + 1]);
	draw_string(13,16,&string[0]);

	posprintf(&string[0], "%02d.%02d.%02d", timer[2],timer[1],timer[0]);
	draw_string(12,19,&string[0]);}

int DimColor(int color, float dim){
	return RGB8((int)(colors[color][R]*dim), (int)(colors[color][G]*dim), (int)(colors[color][B]*dim));}

void GfxUpdate(){
	oe_update(0, 4);
	bg_update(&bg0);}

void SramSaveInt(int val, int pos){
	int i;
	for(i = 0; i < 4; i++)
		Save[(pos<<2)+i] = (val>>(i<<3))&255;}

int SramLoadInt(int pos){
	int val, i;
	for(i = 0; i < 4; i++)
		val |= ((Save[(pos<<2)+i])<<(i<<3));
	return val;}