1. /*===========================================
  2. GRRLIB (GX Version)
  3. - Template Code -
  4.  
  5. Minimum Code To Use GRRLIB
  6. ============================================*/
  7. #include <grrlib.h>
  8.  
  9. #include <stdlib.h>
  10. #include <wiiuse/wpad.h>
  11.  
  12. #include "custcolors.h"
  13. #include "objects.h"
  14. #include "font.h"
  15. #include "font.c"
  16.  
  17. extern const unsigned char font[];
  18. extern const int font_size;
  19.  
  20. struct cursor
  21. {
  22. int x;
  23. int y;
  24. int rot;
  25. };
  26.  
  27. struct gametile
  28. {
  29. unsigned char chunk_id;
  30. GRRLIB_texImg *tile_bottom; // this is the backgrond tile
  31. GRRLIB_texImg *tile_top; // this is the foreground tile
  32. GRRLIB_texImg *tile_col; // this one will be used for collision
  33. };
  34.  
  35. /*----------------------------------------------------------------------
  36. These level index collapse/uncollapse functions were provided by GerbilSoft from Sonic Retro,
  37. they're for turning 2d tile coordinates into a 1d level array value and vice-versa
  38. ----------------------------------------------------------------------*/
  39.  
  40. inline int rowcolToIdx(int row, int col)
  41. {
  42. return (col + (row * 256));
  43. }
  44.  
  45. inline int rowFromIdx(int idx)
  46. {
  47. return (idx >> 8);
  48. }
  49.  
  50. inline int colFromIdx(int idx)
  51. {
  52. return (idx & 0xFF);
  53. }
  54.  
  55. /*----------------------------------------------------------------------
  56. LoadMap() is a direct hack from PieChart at the moment. Remember that the new level format
  57. is a series of horizontal tiles and wraps every 256 entries. It'll also probably need to be
  58. split somehow to load object layouts as well. tilearray[] is the series of u8 values for
  59. tileID's that make up the level. tuledata[] is the struct that holds all of the PNG images
  60. for each tile.
  61. -----------------------------------------------------------------------*/
  62.  
  63. struct gametile tiledata[256]; // This holds the tile structure data
  64. u8 tilearray[10][256];
  65.  
  66. bool LoadMap(u8 level)
  67. {
  68. u16 h;
  69. FILE *leveldata;
  70. //u8 levelarray[2560];
  71. char levelpath[35];
  72. char FG_path[35];
  73. char BG_path[35];
  74. char COL_path[35];
  75. char sizemessage[30];
  76. unsigned short filesize;
  77. GRRLIB_texImg *tex_load = GRRLIB_LoadTextureFromFile("images/loading.png");
  78. GRRLIB_ttfFont *error_font = GRRLIB_LoadTTF(font, font_size);
  79.  
  80. sprintf(levelpath,"sd:/apps/SonicWii/maps/%d/%d.bin", level, level);
  81. GRRLIB_FillScreen(GRRLIB_BLACK);
  82. GRRLIB_DrawImg(140, 300, tex_load, 0,1,1,GRRLIB_WHITE);
  83. GRRLIB_Render();
  84.  
  85. leveldata = fopen(levelpath, "rb");
  86.  
  87. if (leveldata == NULL)
  88. {
  89. while(1)
  90. {
  91. WPAD_ScanPads();
  92. GRRLIB_FillScreen(GRRLIB_BLACK);
  93. GRRLIB_PrintfTTF(90,90, error_font, "Error Loading Map", 18, GRRLIB_WHITE);
  94. GRRLIB_PrintfTTF(90,110, error_font, levelpath, 18, GRRLIB_WHITE);
  95. GRRLIB_PrintfTTF(90,130, error_font, "Press [HOME] to exit.", 14, GRRLIB_WHITE);
  96.  
  97. if (WPAD_ButtonsDown(0) & WIIMOTE_BUTTON_HOME)
  98. {
  99. GRRLIB_Exit();
  100. exit(0);
  101. }
  102. GRRLIB_Render();
  103. }
  104. return false; // error protection, file didn't load
  105. }
  106.  
  107. /*if (false) // old tile debugging routine. Use for checking GRRLIB_LoadTextureFromFile
  108. {
  109. tile_error:
  110. while(1)
  111. {
  112. WPAD_ScanPads();
  113. GRRLIB_FillScreen(GRRLIB_BLACK);
  114. GRRLIB_PrintfTTF(90,90, error_font, "Error Loading Tile", 18, GRRLIB_WHITE);
  115. GRRLIB_PrintfTTF(90,110, error_font, COL_path, 18, GRRLIB_WHITE);
  116. GRRLIB_PrintfTTF(90,130, error_font, "Press [HOME] to exit.", 14, GRRLIB_WHITE);
  117.  
  118. if (WPAD_ButtonsDown(0) & WIIMOTE_BUTTON_HOME)
  119. {
  120. GRRLIB_Exit();
  121. exit(0);
  122. }
  123. GRRLIB_Render();
  124. }
  125. return false; // error protection, file didn't load
  126. }*/
  127.  
  128. fseek(leveldata, 0 , SEEK_END);
  129. filesize = ftell(leveldata);
  130. if (ftell(leveldata) != 0xa00)
  131. {
  132. sprintf(sizemessage,"filesize is %d, expected 2560", filesize);
  133. while(1)
  134. {
  135. WPAD_ScanPads();
  136. GRRLIB_FillScreen(GRRLIB_BLACK);
  137. GRRLIB_PrintfTTF(90,90, error_font, "Error Loading Map: Size Mismatch", 18, GRRLIB_WHITE);
  138. GRRLIB_PrintfTTF(90,110, error_font, sizemessage, 18, GRRLIB_WHITE);
  139. GRRLIB_PrintfTTF(90,130, error_font, "Press [HOME] to exit.", 14, GRRLIB_WHITE);
  140.  
  141. if (WPAD_ButtonsDown(0) & WIIMOTE_BUTTON_HOME)
  142. {
  143. GRRLIB_Exit();
  144. exit(0);
  145. }
  146. GRRLIB_Render();
  147. }
  148. return false; // error protection, file didn't load
  149. }
  150.  
  151. rewind(leveldata);
  152. fread(&tilearray, 1,2560,leveldata);
  153.  
  154. /*for(h=0;h < 2560;h++)
  155. {
  156. tilearray[h] = levelarray[h];// walk through file byte by byte and assign tile ID's accordingly
  157. }*/
  158.  
  159. for(h=0; h <= 255; h++)
  160. {
  161. sprintf(COL_path,"maps/%d/%02d.png", level, h);
  162. tiledata[h].chunk_id = h;
  163. if (GRRLIB_LoadTextureFromFile(COL_path) != NULL)
  164. {
  165. tiledata[h].tile_col = GRRLIB_LoadTextureFromFile(COL_path);
  166. //GRRLIB_InitTileSet(tiledata[h].tile_col, 256, 256, 0);
  167. }
  168. /*tiledata[h].tile_top = GRRLIB_CreateEmptyTexture(2,2);
  169. tiledata[h].tile_bottom = GRRLIB_CreateEmptyTexture(2,2);*/
  170. }
  171.  
  172. fclose(leveldata);
  173. return true;
  174. }
  175.  
  176. /*------------------------------------------------------------------------------------------
  177. ProcessPlayer()
  178. ----------------
  179. All of the player's physics and collision calculations will be processed by this function.
  180. First, the player's state gets passed to a switch statement that decides wether to apply air
  181. physics, ground physics, etc.
  182. -------------------------------------------------------------------------------------------*/
  183.  
  184. void ProcessPlayer(struct PLAYEROBJ *player)
  185. {
  186. enum player_states playerstate;
  187.  
  188. player->gnd_speed = player->x_speed + player->y_speed;
  189.  
  190. switch(playerstate)
  191. {
  192. case(PLAYER_GROUND): // All ground physics/collision calcs go here.
  193. if ((WPAD_ButtonsUp(0) & WIIMOTE_BUTTON_UP) && (WPAD_ButtonsUp(0) & WIIMOTE_BUTTON_DOWN))
  194. {
  195. // no D-Pad input, we need to apply surface friction value
  196. // We'll also need to add Collision checks here and at the D-Pad hold to make sure
  197. // sonic doesn't go out of bounds
  198. if (abs(player->x_speed) < FRICTION) player->x_speed = 0;
  199. else if (player->x_speed < 0) player->x_speed = player->x_speed += (FRICTION*-1);
  200. else if (player->x_speed > 0) player->x_speed = player->x_speed += (FRICTION*1);
  201. }
  202.  
  203. if (WPAD_ButtonsHeld(0) & WIIMOTE_BUTTON_UP)
  204. {
  205. /* we're holding left on the D-Pad. First we'll need to check our ground speed is
  206. over the limit, then if it isn't, start adding acceleration to the x_speed value.
  207. We'll also want to branch out of this if collision to the left returns true.*/
  208. if (abs(player->x_speed) > MAXSPEED)
  209. {
  210. if (player->x_speed > 0) player->x_speed = MAXSPEED;
  211. if (player->x_speed < 0) player->x_speed = NEG_MAXSPD;
  212. }
  213.  
  214. if ((player->x_speed != MAXSPEED) && (player->x_speed != NEG_MAXSPD))
  215. {
  216. if (player->x_speed > 0) player->x_speed += DECELERATION;
  217. if (player->x_speed < 0) player->x_speed -= ACCELERATION;
  218. }
  219. } else if (WPAD_ButtonsHeld(0) & WIIMOTE_BUTTON_DOWN)
  220. {
  221. /* we're holding right on the D-Pad. First we'll need to check our ground speed is
  222. over the limit, then if it isn't, start adding acceleration to the x_speed value.
  223. We'll also want to branch out of this if collision to the left returns true.*/
  224. if (abs(player->x_speed) > MAXSPEED)
  225. {
  226. if (player->x_speed > 0) player->x_speed = MAXSPEED;
  227. if (player->x_speed < 0) player->x_speed = NEG_MAXSPD;
  228. }
  229.  
  230. if ((player->x_speed != MAXSPEED) && (player->x_speed != NEG_MAXSPD))
  231. {
  232. if (player->x_speed < 0) player->x_speed += DECELERATION;
  233. if (player->x_speed > 0) player->x_speed -= ACCELERATION;
  234. }
  235. }
  236. break; // Done with ground physics
  237.  
  238. case(PLAYER_AIR): // All player air physics/collision goes here
  239. break;
  240.  
  241. case(PLAYER_LEFT_WALL): // player left wall physics
  242. break;
  243.  
  244. case(PLAYER_RIGHT_WALL): // player right wall physics
  245. break;
  246.  
  247. case(PLAYER_CEILING): // player right wall physics
  248. break;
  249.  
  250. default: // insert some error handling here, as this state is 'unreachable'
  251. break;
  252. }
  253. }
  254.  
  255. /*---------------------------------------------------------------------------------
  256. GameLoop() is where most of the real action happens, all of the game's runtime calculations
  257. occur here or are initiated here. Expect this function to change the most. The first thing
  258. we need to do is to load all of P1's graphics. Don't forget that the wii's screen real
  259. estate is 640x480, approximately double the Genesis' 320x244, with some black bars
  260. on top for proper aspect ratio keeping.
  261. -----------------------------------------------------------------------------------*/
  262. void GameLoop()
  263. {
  264. ir_t ir;
  265. u8 level=1;
  266.  
  267. // sonic
  268. GRRLIB_texImg *tex_sonic_walk = GRRLIB_LoadTextureFromFile("images/sonic/sonic_walk.png");
  269. GRRLIB_texImg *tex_sonic_stand = GRRLIB_LoadTextureFromFile("images/sonic/sonic_stand.png");
  270. GRRLIB_texImg *tex_sonic_ball = GRRLIB_LoadTextureFromFile("images/sonic/sonic_ball.png");
  271. GRRLIB_texImg *tex_sonic_run = GRRLIB_LoadTextureFromFile("images/sonic/sonic_run.png");
  272. GRRLIB_InitTileSet(tex_sonic_walk, 80, 80, 0);
  273. GRRLIB_InitTileSet(tex_sonic_stand, 80, 80, 0);
  274. GRRLIB_InitTileSet(tex_sonic_ball, 80, 80, 0);
  275. GRRLIB_InitTileSet(tex_sonic_run, 80, 80, 0);
  276. unsigned int count;
  277. struct CAMOBJ camera;
  278. struct PLAYEROBJ sonic_obj;
  279. sonic_obj.score = 0;
  280. sonic_obj.lives = 4;
  281. sonic_obj.anim = 0;
  282. sonic_obj.frame = 0;
  283. sonic_obj.x_coord = 368;
  284. sonic_obj.y_coord = 434;
  285.  
  286. // ring
  287. GRRLIB_texImg *tex_ring = GRRLIB_LoadTextureFromFile("images/ring.png");
  288. GRRLIB_ttfFont *font1 = GRRLIB_LoadTTF(font, font_size);
  289. GRRLIB_texImg *tex_pointer = GRRLIB_LoadTextureFromFile("images/cursor.png");
  290.  
  291. // cursor for collision picking
  292. struct cursor pointer;
  293. pointer.x=200;
  294. pointer.y=200;
  295.  
  296. /*----------------======Game Variables=========----------------------*/
  297. unsigned int tickcounter=0;
  298. u8 framecounter=0;
  299. u8 secondcounter=0;
  300. unsigned int minutecounter=0;
  301. char time_string[15]="";
  302. char debug_ln1[30]="";
  303. char debug_ln2[30]="";
  304. char debug_ln3[30]="";
  305. char debug_ln4[30]="";
  306. bool debugflag=false;
  307. bool mapflag=false;
  308. camera.x=6;
  309. camera.y=222;
  310. sonic_obj.score = 0;
  311. sonic_obj.rings = 0;
  312.  
  313. sprintf(sonic_obj.score_string, "Score: %d", sonic_obj.score);
  314. sprintf(sonic_obj.ring_string, "Rings: %d", sonic_obj.rings);
  315. /*----------------======END Game Variables=====-----------------------*/
  316.  
  317.  
  318. /*----------------=======Level Processing=====------------------------*/
  319. unsigned int vis_tiles[4][4];
  320. unsigned int first_tile[2];
  321. unsigned int x_current,y_current;
  322. unsigned short h,v;
  323. LoadMap(level);
  324. /*---------------------=========END level processing====----------------------------*/
  325.  
  326. // Done loading, begin game loop.
  327. while(1)
  328. {
  329. tickcounter++;
  330.  
  331. if (framecounter < 60) framecounter ++;
  332.  
  333. if ((framecounter == 60) && (secondcounter < 60))
  334. {
  335. framecounter = 0;
  336. secondcounter++;
  337. }
  338.  
  339. if ((framecounter == 60) && (secondcounter == 60))
  340. {
  341. framecounter = 0;
  342. secondcounter = 0;
  343. minutecounter++;
  344. }
  345.  
  346. /*------------====Control Collection/Gameplay Calc=====-------------
  347. Obviously comes before drawing =P
  348. --------------------------------------------------------------------*/
  349. WPAD_ScanPads(); // Scan the Wiimotes
  350. WPAD_IR(WPAD_CHAN_0, &ir);
  351.  
  352. pointer.x = ir.x;
  353. pointer.y = ir.y;
  354.  
  355. if ((WPAD_ButtonsHeld(0) & WPAD_BUTTON_RIGHT) && (camera.y >= 1)) camera.y--;
  356. else if ((WPAD_ButtonsHeld(0) & WPAD_BUTTON_RIGHT) && (camera.y == 0)) camera.y = 0;
  357.  
  358. if (WPAD_ButtonsHeld(0) & WPAD_BUTTON_LEFT) camera.y++;
  359.  
  360. if ((WPAD_ButtonsHeld(0) & WPAD_BUTTON_UP) && (camera.x >= 1)) camera.x--;
  361. else if((WPAD_ButtonsHeld(0) & WPAD_BUTTON_UP) && (camera.x == 0)) camera.x = 0;
  362.  
  363. if (WPAD_ButtonsHeld(0) & WPAD_BUTTON_DOWN) camera.x++;
  364.  
  365. if ((WPAD_ButtonsDown(0) & WIIMOTE_BUTTON_ONE)) debugflag = !debugflag;
  366.  
  367. if (WPAD_ButtonsDown(0) & WIIMOTE_BUTTON_TWO) mapflag = true;
  368.  
  369. /*--------------========END game calc===========--------------------*/
  370.  
  371. GRRLIB_FillScreen(GRRLIB_BLACK);
  372.  
  373. /*---------------------------------------------------------------
  374. Map drawing will need to be split into 2 different passes, so that the characters can
  375. be drawn inbetween the 2 layers. This is the first of 2 passes. At any given time, 16
  376. tiles will be in the level renderer. To figure out which tiles to draw and how far to
  377. offset them, we'll need to use the camera's coordinates and divide by 256 to find our
  378. starting tile. The rest can be filled in automatically, as each line is 4 tiles long.
  379. 4 arrays were created for holding this render queue, named "vis_tiles_x[4]"
  380. respectively. A member of CAMOBJ called in_tile was added for computing where in the
  381. main tile array to search.
  382. -------------------------------------------------------------------*/
  383. // first we fill our screen cache
  384. camera.in_tile[0] = camera.x/256; // row
  385. camera.in_tile[1] = camera.y/256; // column
  386.  
  387. first_tile[0] = camera.in_tile[0];
  388. first_tile[1] = camera.in_tile[1];
  389.  
  390. for(h=0; h < 4; h++) // first row
  391. {
  392. for(v=0; v < 4; v++)
  393. {
  394. vis_tiles[h][v] = tilearray[first_tile[1] + v][first_tile[0] + h];
  395. }
  396. }
  397.  
  398. // this is the actual display loop, first pass
  399. for(h = 0; h < 4; h++)
  400. {
  401. for(v=0; v < 4; v++)
  402. {
  403. x_current = (first_tile[0] + h) * 256;
  404. y_current = (first_tile[1] + v) * 256;
  405. GRRLIB_DrawImg(x_current - camera.x, y_current - camera.y, tiledata[vis_tiles[h][v]].tile_col, 0, 1, 1, GRRLIB_WHITE);
  406. }
  407. }
  408.  
  409. // Draw Sonic
  410. GRRLIB_DrawTile(sonic_obj.x_coord - camera.x, sonic_obj.y_coord - camera.y, tex_sonic_stand, 0,1,1,GRRLIB_WHITE,sonic_obj.frame);
  411. GRRLIB_Rectangle(pointer.x, pointer.y, 4, 4, GRRLIB_RED, true);
  412.  
  413. // second level render pass goes here.
  414.  
  415. // Drawing HUD - this will need to get moved to the end of the drawchain to show up
  416. // on top of everything else
  417. sprintf(time_string, "Time: %d:%d:%d", minutecounter,secondcounter,framecounter);
  418. GRRLIB_PrintfTTF(32,32, font1, sonic_obj.score_string, 26, GRRLIB_WHITE);
  419. GRRLIB_PrintfTTF(32,64, font1, time_string, 26, GRRLIB_WHITE);
  420. GRRLIB_PrintfTTF(32,98, font1, sonic_obj.ring_string, 26, GRRLIB_WHITE);
  421.  
  422. if ((debugflag == true) && (mapflag == false))
  423. {
  424. sprintf(debug_ln1, "x: %d", camera.x);
  425. sprintf(debug_ln2, "y: %d", camera.y);
  426. sprintf(debug_ln3, "it: %d:%d | %d x %d", camera.in_tile[0], camera.in_tile[1], (camera.x % 256), (camera.y % 256));
  427. GRRLIB_PrintfTTF(420,32, font1, debug_ln1, 18, GRRLIB_RED);
  428. GRRLIB_PrintfTTF(420,64, font1, debug_ln2, 18, GRRLIB_RED);
  429. GRRLIB_PrintfTTF(420,98, font1, debug_ln3, 18, GRRLIB_RED);
  430. }
  431.  
  432. while (mapflag == true)
  433. {
  434. WPAD_ScanPads();
  435. debugflag = false;
  436. sprintf(debug_ln1, "%d | %d | %d | %d", vis_tiles[0][0], vis_tiles[1][0], vis_tiles[2][0], vis_tiles[3][0]);
  437. sprintf(debug_ln2, "%d | %d | %d | %d", vis_tiles[0][1], vis_tiles[1][1], vis_tiles[2][1], vis_tiles[3][1]);
  438. sprintf(debug_ln3, "%d | %d | %d | %d", vis_tiles[0][2], vis_tiles[1][2], vis_tiles[2][2], vis_tiles[3][2]);
  439. sprintf(debug_ln4, "%d | %d | %d | %d", vis_tiles[0][3], vis_tiles[1][3], vis_tiles[2][3], vis_tiles[3][3]);
  440. GRRLIB_PrintfTTF(50,32, font1, debug_ln1, 22, GRRLIB_WHITE);
  441. GRRLIB_PrintfTTF(50,64, font1, debug_ln2, 22, GRRLIB_WHITE);
  442. GRRLIB_PrintfTTF(50,98, font1, debug_ln3, 22, GRRLIB_WHITE);
  443. //GRRLIB_PrintfTTF(50,130, font1, debug_ln4, 22, GRRLIB_WHITE);
  444.  
  445. if (WPAD_ButtonsHeld(0) & WIIMOTE_BUTTON_ONE)
  446. {
  447. mapflag = false;
  448. break;
  449. } else if (WPAD_ButtonsHeld(0) & WPAD_BUTTON_HOME)
  450. {
  451. GRRLIB_Exit();
  452. exit(0);
  453. }
  454.  
  455. GRRLIB_Render();
  456. }
  457.  
  458.  
  459. if (WPAD_ButtonsDown(0) & WPAD_BUTTON_HOME) break;
  460. if (WPAD_ButtonsDown(0) & WIIMOTE_BUTTON_A) GRRLIB_ScrShot("screenshot.png");
  461.  
  462. GRRLIB_Render();
  463. }
  464. return;
  465. }
  466.  
  467. int main(int argc, char **argv)
  468. {
  469. // Initialise the Graphics & Video subsystem
  470. GRRLIB_Init();
  471.  
  472. // Initialise the Wiimotes
  473. WPAD_Init();
  474. WPAD_SetDataFormat(WPAD_CHAN_0, WPAD_FMT_BTNS_ACC_IR);
  475.  
  476. GameLoop();
  477.  
  478. GRRLIB_Exit(); // Be a good boy, clear the memory allocated by GRRLIB
  479.  
  480. exit(0); // Use exit() to exit a program, do not use 'return' from main()
  481. }