#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;}