00001 #include <gccore.h>
00002
00003 #include "defines.h"
00004 #include "globals.h"
00005 #include "PowerupUtils.h"
00006 #include "pieces_bin.h"
00007
00008 #define EMU
00009
00010 int timing[]={30,27,24,21,18,15,12,9,6,3};
00011
00012 void InitPieceDescriptions()
00013 {
00014
00015 for (int i = 0; i < NUMTYPES; ++i)
00016 {
00017
00018 for (int rot = 0; rot < 4; ++rot)
00019 {
00020 TetrisPieceDesc& desc = g_pieceDesc[i][rot];
00021
00022
00023
00024
00025
00026
00027
00028 for (int y = 0; y < 4; ++y)
00029 {
00030 for (int x = 0; x < 4; ++x)
00031 {
00032
00033 desc.map[x][y] = pieces_bin[i * 4 * 4 * 4 + rot * 4 * 4 + y * 4 + x];
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043
00044
00045
00046
00047
00048
00049
00050
00051
00052
00053 }
00054 }
00055 }
00056 }
00057 }
00058
00059 void InitPieces()
00060 {
00061 for (int i = 0; i < MAX_PLAYERS; ++i)
00062 {
00063
00064 g_currPiece[i].speed = timing[0];
00065 g_currPiece[i].linesinframe = 0;
00066 g_currPiece[i].waitnum = 0;
00067 g_currPiece[i].haspressed = 0;
00068 g_currPiece[i].speedpressed = 0;
00069
00070 g_currPiece[i].down_enabled = false;
00071 g_currPiece[i].accel_enabled = false;
00072 g_currPiece[i].downctr = 0;
00073
00074 g_nextPiece[i].blocknum = (int)(genrand() * NUMTYPES);
00075 g_nextPiece[i].powerupId = PowerupUtils::GetNextId(i);
00076 }
00077 }
00078
00079
00080
00081 void NewPiece(int player)
00082 {
00083 TetrisPiece& cb = g_currPiece[player];
00084 TetrisPiece& nb = g_nextPiece[player];
00085
00086 int width = g_players[player].playfieldWidth;
00087
00088 cb.powerupId = nb.powerupId;
00089 cb.blocknum = nb.blocknum;
00090 cb.rot = 0;
00091 cb.ResetDesc();
00092
00093 cb.gfx = cb.blocknum;
00094 cb.xtile = (width >> 1) - 2;
00095 cb.ytile = -1;
00096 cb.down_enabled = false;
00097 cb.accel_enabled = false;
00098 cb.downctr = 0;
00099
00100 if (g_options.players > 1)
00101 {
00102 nb.powerupId = PowerupUtils::GetNextId(player);
00103 }
00104
00105 nb.blocknum = (int)(genrand() * NUMTYPES);
00106 nb.rot = 0;
00107 nb.ResetDesc();
00108
00109 nb.gfx = nb.blocknum;
00110 nb.xtile = (width >> 1) - 2;
00111 nb.ytile = -1;
00112 }
00113
00114 void NewPieceAll()
00115 {
00116 for (int i = 0; i < g_options.players; ++i)
00117 NewPiece(i);
00118 }
00119
00120
00121 void DrawMap(int player)
00122 {
00123 int width = g_players[player].playfieldWidth;
00124 int height = g_players[player].playfieldHeight;
00125 bool isDead = g_players[player].gameData.isDead;
00126
00127 TetrisPieceConnectivityInfo *connectivityInfo = NULL;
00128 GuiImageData *imgData = NULL;
00129
00130 for (int y = 0; y < height; ++y)
00131 {
00132 for (int x = 0; x < width; ++x)
00133 {
00134 u8 gfx = map[player][x][y];
00135
00136 if (gfx != 9)
00137 {
00138 if (isDead)
00139 {
00140 gfx = COLOR_ID_DEAD;
00141 }
00142 else
00143 {
00144 connectivityInfo = g_players[player].gameData.playfield[x][y].connectivityInfo;
00145 imgData = !connectivityInfo ?
00146 NULL : PowerupUtils::GetImageData(connectivityInfo->powerupId);
00147 }
00148
00149 DrawBlockAsCube(player, x, y, gfx, 255, imgData);
00150 }
00151 }
00152 }
00153 }
00154
00155 void DrawMapAll()
00156 {
00157 for (int i = 0; i < g_options.players; ++i)
00158 DrawMap(i);
00159 }
00160
00161
00162
00163 void DrawPiece(int player, TetrisPiece* cp, u8 alpha)
00164 {
00165 TetrisPiece& p = !cp ? g_currPiece[player] : *cp;
00166 TetrisPieceDesc& desc = g_pieceDesc[p.blocknum][p.rot];
00167
00168 int width = g_players[player].playfieldWidth;
00169
00170 u8 gfx = p.blocknum;
00171 GuiImageData *imgData = (p.powerupId == POWERUP_ID_NONE) ?
00172 NULL : PowerupUtils::GetImageData(p.powerupId);
00173
00174 if (g_players[player].gameData.isDead)
00175 {
00176 gfx = COLOR_ID_DEAD;
00177 imgData = NULL;
00178 }
00179
00180
00181 for (int y = 0; y < 4; ++y)
00182 {
00183 for (int x = 0; x < 4; ++x)
00184 {
00185
00186
00187
00188
00189
00190
00191 int calcx = x + p.xtile;
00192 int calcy = y + p.ytile;
00193
00194
00195 if (calcx >= width)
00196 calcx -= width;
00197 else if (calcx < 0)
00198 calcx += width;
00199
00200 if (desc.map[x][y] && calcx >= 0 && calcy >= 0)
00201 DrawBlockAsCube(player, calcx, calcy, gfx, alpha, imgData);
00202 }
00203 }
00204 }
00205
00206 void DrawPieceAll()
00207 {
00208 for (int i = 0; i < g_options.players; ++i)
00209 DrawPiece(i);
00210 }
00211
00212
00213
00214 void DrawPieceShadow(int player)
00215 {
00216 if (!g_players[player].isShadowEnabled)
00217 return;
00218
00219
00220
00221
00222 TetrisPiece p = g_currPiece[player];
00223 int y = p.ytile;
00224 while (MoveBlock(DOWN, player, &p));
00225 if (y < 7 && p.ytile > 11)
00226 DrawPiece(player, &p, 145);
00227 }
00228
00229 void DrawPieceShadowAll()
00230 {
00231 for (int i = 0; i < g_options.players; ++i)
00232 DrawPieceShadow(i);
00233 }
00234
00235 void DrawNextPiece(int player)
00236 {
00237 if (g_players[player].isPreviewEnabled)
00238 DrawPiece(player, &g_nextPiece[player], 145);
00239 }
00240
00241 void DrawNextPieceAll()
00242 {
00243 for (int i = 0; i < g_options.players; ++i)
00244 DrawNextPiece(i);
00245 }
00246
00247
00248 void DrawBase(int player)
00249 {
00250 int w = g_players[player].playfieldWidth;
00251 int h = g_players[player].playfieldHeight;
00252 int ci = g_players[player].gameData.cycleIdx;
00253 GXColor color;
00254
00255 for (int x = 0; x < w; ++x)
00256 {
00257 int idx = (x + ci) % w;
00258 u8 c = idx * 8;
00259 color = (GXColor){c, c, c, 255};
00260 SetBaseColor(color);
00261 DrawBlockAsCube(player, x, h, 8);
00262 }
00263 }
00264
00265 void DrawBaseAll()
00266 {
00267 for (int i = 0; i < g_options.players; ++i)
00268 DrawBase(i);
00269 }
00270
00271 bool canPlace(int player, TetrisPiece* cp)
00272 {
00273 TetrisPiece& p = !cp ? g_currPiece[player] : *cp;
00274 TetrisPieceDesc& desc = g_pieceDesc[p.blocknum][p.rot];
00275
00276 int width = g_players[player].playfieldWidth;
00277 int height = g_players[player].playfieldHeight;
00278
00279 for (int y = 0; y < 4; y++)
00280 {
00281 for (int x = 0; x < 4; x++)
00282 {
00283
00284 if (desc.map[x][y])
00285 {
00286
00287
00288 int calcx = p.xtile + x;
00289 int calcy = p.ytile + y;
00290
00291
00292
00293 if (calcx >= width)
00294 calcx -= width;
00295 else if (calcx < 0)
00296 calcx += width;
00297
00298
00299 if (calcy >= height)
00300 return false;
00301
00302
00303 if (calcy >= 0 && map[player][calcx][calcy] != 9)
00304 return false;
00305 }
00306 }
00307 }
00308
00309 return true;
00310 }
00311
00312 void rotate(int rot, int player)
00313 {
00314 TetrisPiece& p = g_currPiece[player];
00315
00316 int oldRot = p.rot;
00317
00318 while(rot < 0)
00319 rot += 4;
00320
00321 while(rot > 3)
00322 rot -= 4;
00323
00324 p.rot = rot;
00325
00326 p.ResetDesc();
00327
00328
00329 if (!canPlace(player))
00330 {
00331 p.rot = oldRot;
00332 p.ResetDesc();
00333 }
00334 }
00335
00336 int GetWinner()
00337 {
00338 int ndead = 0;
00339 int winner = -1;
00340
00341 for (int i = 0; i < g_options.players; ++i)
00342 {
00343 if (g_players[i].gameData.isDead)
00344 ++ndead;
00345 else
00346 winner = i;
00347 }
00348
00349 if (ndead == g_options.players - 1)
00350 return winner;
00351
00352 return -1;
00353 }
00354
00355 bool MovePlayfield(int type, int player)
00356 {
00357
00358
00359 TetrisPiece& p = g_currPiece[player];
00360
00361 int oldx = p.xtile;
00362 int width = g_players[player].playfieldWidth;
00363 int height = g_players[player].playfieldHeight;
00364
00365 switch(type)
00366 {
00367 case RIGHT:
00368 p.xtile--;
00369 if (p.xtile < 0)
00370 p.xtile += width;
00371 break;
00372
00373 case LEFT:
00374 p.xtile++;
00375 if (p.xtile >= width)
00376 p.xtile -= width;
00377 break;
00378 }
00379
00380 bool bCanPlace = canPlace(player);
00381
00382
00383 p.xtile = oldx;
00384
00385 if (!bCanPlace)
00386 return false;
00387
00388
00389 u8 *playfield = &map[player][0][0];
00390 u8 tmp;
00391 u8 *prev, *curr;
00392
00393 TetrisPieceBlock *playfieldTPB = &g_players[player].gameData.playfield[0][0];
00394 TetrisPieceBlock tmpTPB;
00395 TetrisPieceBlock *prevTPB, *currTPB;
00396
00397 if (type == LEFT)
00398 {
00399 for (int r = 0; r < height; ++r)
00400 {
00401 prev = playfield + r;
00402 curr = prev + MAX_PLAYFIELD_HEIGHT;
00403 tmp = *prev;
00404
00405 prevTPB = playfieldTPB + r;
00406 currTPB = prevTPB + MAX_PLAYFIELD_HEIGHT;
00407 tmpTPB = *prevTPB;
00408
00409 for (int c = 0; c < width - 1; ++c, prev += MAX_PLAYFIELD_HEIGHT, curr += MAX_PLAYFIELD_HEIGHT, prevTPB += MAX_PLAYFIELD_HEIGHT, currTPB += MAX_PLAYFIELD_HEIGHT)
00410 {
00411 *prev = *curr;
00412 *prevTPB = *currTPB;
00413 }
00414
00415 *prev = tmp;
00416 *prevTPB = tmpTPB;
00417 }
00418 }
00419 else if (type == RIGHT)
00420 {
00421 for (int r = 0; r < height; ++r)
00422 {
00423 prev = playfield + (width - 1) * MAX_PLAYFIELD_HEIGHT + r;
00424 curr = prev - MAX_PLAYFIELD_HEIGHT;
00425 tmp = *prev;
00426
00427 prevTPB = playfieldTPB + (width - 1) * MAX_PLAYFIELD_HEIGHT + r;
00428 currTPB = prevTPB - MAX_PLAYFIELD_HEIGHT;
00429 tmpTPB = *prevTPB;
00430
00431 for (int c = 0; c < width - 1; ++c, prev -= MAX_PLAYFIELD_HEIGHT, curr -= MAX_PLAYFIELD_HEIGHT, prevTPB -= MAX_PLAYFIELD_HEIGHT, currTPB -= MAX_PLAYFIELD_HEIGHT)
00432 {
00433 *prev = *curr;
00434 *prevTPB = *currTPB;
00435 }
00436
00437 *prev = tmp;
00438 *prevTPB = tmpTPB;
00439 }
00440 }
00441
00442 return true;
00443 }
00444
00445 bool MoveBlock(int type, int player, TetrisPiece* cp)
00446 {
00447 TetrisPiece& p = !cp ? g_currPiece[player] : *cp;
00448
00449 int oldx = p.xtile;
00450 int oldy = p.ytile;
00451 int width = g_players[player].playfieldWidth;
00452
00453 switch(type)
00454 {
00455 case DOWN:
00456 p.ytile++;
00457 break;
00458
00459 case LEFT:
00460 p.xtile--;
00461 if (p.xtile < 0)
00462 p.xtile += width;
00463 break;
00464
00465 case RIGHT:
00466 p.xtile++;
00467 if (p.xtile >= width)
00468 p.xtile -= width;
00469 break;
00470 }
00471
00472 if (canPlace(player, &p))
00473 return true;
00474
00475 p.xtile = oldx;
00476 p.ytile = oldy;
00477 return false;
00478 }
00479
00480 void DoMovement(int player)
00481 {
00482 if (g_players[player].gameData.isDead)
00483 return;
00484
00485 TetrisPiece& p = g_currPiece[player];
00486
00487 bool move = MoveBlock(DOWN, player);
00488 bool place = canPlace(player);
00489 int width = g_players[player].playfieldWidth;
00490 int height = g_players[player].playfieldHeight;
00491
00492
00493 if (!move)
00494 {
00495 TetrisPieceConnectivityInfo *info = (p.powerupId == POWERUP_ID_NONE) ?
00496 NULL : new TetrisPieceConnectivityInfo(p.powerupId);
00497
00498 for (int y = 0; y < 4; ++y)
00499 {
00500 for (int x = 0; x < 4; ++x)
00501 {
00502 int calcx = p.xtile + x;
00503 int calcy = p.ytile + y;
00504
00505
00506 if (calcx >= width)
00507 calcx -= width;
00508 else if (calcx < 0)
00509 calcx += width;
00510
00511 if (calcx >= 0 && calcx < width && calcy >= 0 && calcy < height && p.desc->map[x][y])
00512 {
00513 map[player][calcx][calcy] = p.gfx;
00514 g_players[player].gameData.playfield[calcx][calcy].SetConnectivityInfo(info);
00515 }
00516 }
00517 }
00518 }
00519
00520
00521 if (!move && !place)
00522 {
00523 if (g_options.players==1)
00524 {
00525 g_players[player].gameData.isDead = true;
00526 Pause("Game Over");
00527 return;
00528 }
00529
00530 g_players[player].gameData.isDead = true;
00531 int win = GetWinner();
00532 if (win >= 0)
00533 winner(win);
00534
00535 return;
00536 }
00537
00538
00539 if (!move)
00540 {
00541 while (RemoveLines(player)) { p.linesinframe++; }
00542
00543 if (p.linesinframe == 3)
00544 {
00545 g_players[player].gameData.score++;
00546 }
00547 else if (p.linesinframe == 4)
00548 {
00549 g_players[player].gameData.score += 2;
00550 MODPlay_Pause(&g_modPlay, 1);
00551 g_tetrisCheerSound->Play();
00552 }
00553
00554 p.linesinframe = 0;
00555 NewPiece(player);
00556 }
00557
00558 #ifndef EMU
00559 if(g_options.netplay==1 && player==0)
00560 GC_BBA_SendUDPE(9003,(unsigned char*)map[0],200);
00561 #endif
00562 }
00563
00564 void IncreaseLevel(int player)
00565 {
00566 if (g_players[player].gameData.level < 9)
00567 {
00568 g_players[player].gameData.level++;
00569 g_currPiece[player].speed = timing[g_players[player].gameData.level];
00570 }
00571 }
00572
00573 void IncreaseLevelAllBut(int player)
00574 {
00575 for (int i = 0; i < g_options.players; ++i)
00576 {
00577 if (i != player)
00578 IncreaseLevel(i);
00579 }
00580 }
00581
00582 int RemoveLines(int player)
00583 {
00584 int sil = 0;
00585 int width = g_players[player].playfieldWidth;
00586 int height = g_players[player].playfieldHeight;
00587
00588 for (int y = 0; y < height; ++y)
00589 {
00590 for (int x = 0; x < width; ++x)
00591 {
00592 if (map[player][x][y] != 9)
00593 ++sil;
00594 }
00595
00596 if (sil == width)
00597 {
00598 RemoveLine(y, player);
00599 return 1;
00600 }
00601
00602 sil = 0;
00603 }
00604
00605 return 0;
00606 }
00607
00608 void RemoveLine(int line, int player)
00609 {
00610 int width = g_players[player].playfieldWidth;
00611 TetrisPieceConnectivityInfo *connectivityInfo = NULL;
00612
00613 for (int x = 0; x < width; ++x)
00614 {
00615
00616 connectivityInfo = g_players[player].gameData.playfield[x][line].connectivityInfo;
00617 if (connectivityInfo)
00618 {
00619 connectivityInfo->counter--;
00620
00621 if (connectivityInfo->counter == 0)
00622 {
00623
00624 int slot = g_players[player].GetPowerupQueueSlot();
00625 if (slot >= 0)
00626 {
00627 Powerup *powerup = PowerupUtils::GetInstance(connectivityInfo->powerupId);
00628 g_players[player].QueuePowerup(powerup, slot);
00629 }
00630
00631 g_players[player].gameData.playfield[x][line].Free();
00632 }
00633 }
00634 }
00635
00636 for (int y = line; y > 0; --y)
00637 {
00638 for (int x = 0; x < width; ++x)
00639 {
00640 map[player][x][y] = map[player][x][y-1];
00641 g_players[player].gameData.playfield[x][y] = g_players[player].gameData.playfield[x][y-1];
00642 }
00643 }
00644
00645 for (int x = 0; x < width; ++x)
00646 {
00647 map[player][x][0] = 9;
00648 g_players[player].gameData.playfield[x][0].SetConnectivityInfo(NULL);
00649 }
00650
00651 g_players[player].gameData.lines++;
00652 g_players[player].gameData.score++;
00653
00654 if (g_options.players == 1)
00655 {
00656 if (g_players[0].gameData.lines % 10 == 0)
00657 IncreaseLevel(0);
00658
00659 return;
00660 }
00661
00662 int maxLines = g_players[player].isHandicapEnabled ?
00663 g_players[player].maxLines : g_options.maxLines;
00664
00665 if (maxLines && g_players[player].gameData.lines == maxLines)
00666 {
00667 winner(player);
00668 return;
00669 }
00670
00671 int attackRate = g_players[player].isHandicapEnabled ?
00672 g_players[player].attackRate : g_options.attackRate;
00673
00674
00675 if (attackRate && g_players[player].gameData.score % attackRate == 0)
00676 {
00677 IncreaseLevelAllBut(player);
00678 }
00679 }
00680
00681 void Reset()
00682 {
00683
00684
00685 PowerupUtils::DeleteAllPowerups();
00686
00687 for (int i = 0; i < MAX_PLAYERS; ++i)
00688 {
00689 g_players[i].Reset();
00690
00691 for (int y = 0; y < MAX_PLAYFIELD_HEIGHT; ++y)
00692 {
00693 for (int x = 0; x < MAX_PLAYFIELD_WIDTH; ++x)
00694 map[i][x][y] = 9;
00695 }
00696 }
00697
00698
00699 InitPieces();
00700 }
00701
00702 void winner(int player)
00703 {
00704 char buf[15];
00705 sprintf(buf, "Player %d wins!", player + 1);
00706 Pause(buf);
00707 }
00708
00709 inline void SetBaseColor(GXColor &c) { g_cubeGradients[COLOR_ID_BASE].SetColor(c); }