/*=========================================== GRRLIB (GX Version) - Template Code - Minimum Code To Use GRRLIB ============================================*/ #include #include #include #include "custcolors.h" #include "objects.h" #include "font.h" #include "font.c" extern const unsigned char font[]; extern const int font_size; struct cursor { int x; int y; int rot; }; struct gametile { unsigned char chunk_id; GRRLIB_texImg *tile_bottom; // this is the backgrond tile GRRLIB_texImg *tile_top; // this is the foreground tile GRRLIB_texImg *tile_col; // this one will be used for collision }; /*---------------------------------------------------------------------- These level index collapse/uncollapse functions were provided by GerbilSoft from Sonic Retro, they're for turning 2d tile coordinates into a 1d level array value and vice-versa ----------------------------------------------------------------------*/ inline int rowcolToIdx(int row, int col) { return (col + (row * 256)); } inline int rowFromIdx(int idx) { return (idx >> 8); } inline int colFromIdx(int idx) { return (idx & 0xFF); } /*---------------------------------------------------------------------- LoadMap() is a direct hack from PieChart at the moment. Remember that the new level format is a series of horizontal tiles and wraps every 256 entries. It'll also probably need to be split somehow to load object layouts as well. tilearray[] is the series of u8 values for tileID's that make up the level. tuledata[] is the struct that holds all of the PNG images for each tile. -----------------------------------------------------------------------*/ struct gametile tiledata[256]; // This holds the tile structure data u8 tilearray[10][256]; bool LoadMap(u8 level) { u16 h; FILE *leveldata; //u8 levelarray[2560]; char levelpath[35]; char FG_path[35]; char BG_path[35]; char COL_path[35]; char sizemessage[30]; unsigned short filesize; GRRLIB_texImg *tex_load = GRRLIB_LoadTextureFromFile("images/loading.png"); GRRLIB_ttfFont *error_font = GRRLIB_LoadTTF(font, font_size); sprintf(levelpath,"sd:/apps/SonicWii/maps/%d/%d.bin", level, level); GRRLIB_FillScreen(GRRLIB_BLACK); GRRLIB_DrawImg(140, 300, tex_load, 0,1,1,GRRLIB_WHITE); GRRLIB_Render(); leveldata = fopen(levelpath, "rb"); if (leveldata == NULL) { while(1) { WPAD_ScanPads(); GRRLIB_FillScreen(GRRLIB_BLACK); GRRLIB_PrintfTTF(90,90, error_font, "Error Loading Map", 18, GRRLIB_WHITE); GRRLIB_PrintfTTF(90,110, error_font, levelpath, 18, GRRLIB_WHITE); GRRLIB_PrintfTTF(90,130, error_font, "Press [HOME] to exit.", 14, GRRLIB_WHITE); if (WPAD_ButtonsDown(0) & WIIMOTE_BUTTON_HOME) { GRRLIB_Exit(); exit(0); } GRRLIB_Render(); } return false; // error protection, file didn't load } /*if (false) // old tile debugging routine. Use for checking GRRLIB_LoadTextureFromFile { tile_error: while(1) { WPAD_ScanPads(); GRRLIB_FillScreen(GRRLIB_BLACK); GRRLIB_PrintfTTF(90,90, error_font, "Error Loading Tile", 18, GRRLIB_WHITE); GRRLIB_PrintfTTF(90,110, error_font, COL_path, 18, GRRLIB_WHITE); GRRLIB_PrintfTTF(90,130, error_font, "Press [HOME] to exit.", 14, GRRLIB_WHITE); if (WPAD_ButtonsDown(0) & WIIMOTE_BUTTON_HOME) { GRRLIB_Exit(); exit(0); } GRRLIB_Render(); } return false; // error protection, file didn't load }*/ fseek(leveldata, 0 , SEEK_END); filesize = ftell(leveldata); if (ftell(leveldata) != 0xa00) { sprintf(sizemessage,"filesize is %d, expected 2560", filesize); while(1) { WPAD_ScanPads(); GRRLIB_FillScreen(GRRLIB_BLACK); GRRLIB_PrintfTTF(90,90, error_font, "Error Loading Map: Size Mismatch", 18, GRRLIB_WHITE); GRRLIB_PrintfTTF(90,110, error_font, sizemessage, 18, GRRLIB_WHITE); GRRLIB_PrintfTTF(90,130, error_font, "Press [HOME] to exit.", 14, GRRLIB_WHITE); if (WPAD_ButtonsDown(0) & WIIMOTE_BUTTON_HOME) { GRRLIB_Exit(); exit(0); } GRRLIB_Render(); } return false; // error protection, file didn't load } rewind(leveldata); fread(&tilearray, 1,2560,leveldata); /*for(h=0;h < 2560;h++) { tilearray[h] = levelarray[h];// walk through file byte by byte and assign tile ID's accordingly }*/ for(h=0; h <= 255; h++) { sprintf(COL_path,"maps/%d/%02d.png", level, h); tiledata[h].chunk_id = h; if (GRRLIB_LoadTextureFromFile(COL_path) != NULL) { tiledata[h].tile_col = GRRLIB_LoadTextureFromFile(COL_path); //GRRLIB_InitTileSet(tiledata[h].tile_col, 256, 256, 0); } /*tiledata[h].tile_top = GRRLIB_CreateEmptyTexture(2,2); tiledata[h].tile_bottom = GRRLIB_CreateEmptyTexture(2,2);*/ } fclose(leveldata); return true; } /*------------------------------------------------------------------------------------------ ProcessPlayer() ---------------- All of the player's physics and collision calculations will be processed by this function. First, the player's state gets passed to a switch statement that decides wether to apply air physics, ground physics, etc. -------------------------------------------------------------------------------------------*/ void ProcessPlayer(struct PLAYEROBJ *player) { enum player_states playerstate; player->gnd_speed = player->x_speed + player->y_speed; switch(playerstate) { case(PLAYER_GROUND): // All ground physics/collision calcs go here. if ((WPAD_ButtonsUp(0) & WIIMOTE_BUTTON_UP) && (WPAD_ButtonsUp(0) & WIIMOTE_BUTTON_DOWN)) { // no D-Pad input, we need to apply surface friction value // We'll also need to add Collision checks here and at the D-Pad hold to make sure // sonic doesn't go out of bounds if (abs(player->x_speed) < FRICTION) player->x_speed = 0; else if (player->x_speed < 0) player->x_speed = player->x_speed += (FRICTION*-1); else if (player->x_speed > 0) player->x_speed = player->x_speed += (FRICTION*1); } if (WPAD_ButtonsHeld(0) & WIIMOTE_BUTTON_UP) { /* we're holding left on the D-Pad. First we'll need to check our ground speed is over the limit, then if it isn't, start adding acceleration to the x_speed value. We'll also want to branch out of this if collision to the left returns true.*/ if (abs(player->x_speed) > MAXSPEED) { if (player->x_speed > 0) player->x_speed = MAXSPEED; if (player->x_speed < 0) player->x_speed = NEG_MAXSPD; } if ((player->x_speed != MAXSPEED) && (player->x_speed != NEG_MAXSPD)) { if (player->x_speed > 0) player->x_speed += DECELERATION; if (player->x_speed < 0) player->x_speed -= ACCELERATION; } } else if (WPAD_ButtonsHeld(0) & WIIMOTE_BUTTON_DOWN) { /* we're holding right on the D-Pad. First we'll need to check our ground speed is over the limit, then if it isn't, start adding acceleration to the x_speed value. We'll also want to branch out of this if collision to the left returns true.*/ if (abs(player->x_speed) > MAXSPEED) { if (player->x_speed > 0) player->x_speed = MAXSPEED; if (player->x_speed < 0) player->x_speed = NEG_MAXSPD; } if ((player->x_speed != MAXSPEED) && (player->x_speed != NEG_MAXSPD)) { if (player->x_speed < 0) player->x_speed += DECELERATION; if (player->x_speed > 0) player->x_speed -= ACCELERATION; } } break; // Done with ground physics case(PLAYER_AIR): // All player air physics/collision goes here break; case(PLAYER_LEFT_WALL): // player left wall physics break; case(PLAYER_RIGHT_WALL): // player right wall physics break; case(PLAYER_CEILING): // player right wall physics break; default: // insert some error handling here, as this state is 'unreachable' break; } } /*--------------------------------------------------------------------------------- GameLoop() is where most of the real action happens, all of the game's runtime calculations occur here or are initiated here. Expect this function to change the most. The first thing we need to do is to load all of P1's graphics. Don't forget that the wii's screen real estate is 640x480, approximately double the Genesis' 320x244, with some black bars on top for proper aspect ratio keeping. -----------------------------------------------------------------------------------*/ void GameLoop() { ir_t ir; u8 level=1; // sonic GRRLIB_texImg *tex_sonic_walk = GRRLIB_LoadTextureFromFile("images/sonic/sonic_walk.png"); GRRLIB_texImg *tex_sonic_stand = GRRLIB_LoadTextureFromFile("images/sonic/sonic_stand.png"); GRRLIB_texImg *tex_sonic_ball = GRRLIB_LoadTextureFromFile("images/sonic/sonic_ball.png"); GRRLIB_texImg *tex_sonic_run = GRRLIB_LoadTextureFromFile("images/sonic/sonic_run.png"); GRRLIB_InitTileSet(tex_sonic_walk, 80, 80, 0); GRRLIB_InitTileSet(tex_sonic_stand, 80, 80, 0); GRRLIB_InitTileSet(tex_sonic_ball, 80, 80, 0); GRRLIB_InitTileSet(tex_sonic_run, 80, 80, 0); unsigned int count; struct CAMOBJ camera; struct PLAYEROBJ sonic_obj; sonic_obj.score = 0; sonic_obj.lives = 4; sonic_obj.anim = 0; sonic_obj.frame = 0; sonic_obj.x_coord = 368; sonic_obj.y_coord = 434; // ring GRRLIB_texImg *tex_ring = GRRLIB_LoadTextureFromFile("images/ring.png"); GRRLIB_ttfFont *font1 = GRRLIB_LoadTTF(font, font_size); GRRLIB_texImg *tex_pointer = GRRLIB_LoadTextureFromFile("images/cursor.png"); // cursor for collision picking struct cursor pointer; pointer.x=200; pointer.y=200; /*----------------======Game Variables=========----------------------*/ unsigned int tickcounter=0; u8 framecounter=0; u8 secondcounter=0; unsigned int minutecounter=0; char time_string[15]=""; char debug_ln1[30]=""; char debug_ln2[30]=""; char debug_ln3[30]=""; char debug_ln4[30]=""; bool debugflag=false; bool mapflag=false; camera.x=6; camera.y=222; sonic_obj.score = 0; sonic_obj.rings = 0; sprintf(sonic_obj.score_string, "Score: %d", sonic_obj.score); sprintf(sonic_obj.ring_string, "Rings: %d", sonic_obj.rings); /*----------------======END Game Variables=====-----------------------*/ /*----------------=======Level Processing=====------------------------*/ unsigned int vis_tiles[4][4]; unsigned int first_tile[2]; unsigned int x_current,y_current; unsigned short h,v; LoadMap(level); /*---------------------=========END level processing====----------------------------*/ // Done loading, begin game loop. while(1) { tickcounter++; if (framecounter < 60) framecounter ++; if ((framecounter == 60) && (secondcounter < 60)) { framecounter = 0; secondcounter++; } if ((framecounter == 60) && (secondcounter == 60)) { framecounter = 0; secondcounter = 0; minutecounter++; } /*------------====Control Collection/Gameplay Calc=====------------- Obviously comes before drawing =P --------------------------------------------------------------------*/ WPAD_ScanPads(); // Scan the Wiimotes WPAD_IR(WPAD_CHAN_0, &ir); pointer.x = ir.x; pointer.y = ir.y; if ((WPAD_ButtonsHeld(0) & WPAD_BUTTON_RIGHT) && (camera.y >= 1)) camera.y--; else if ((WPAD_ButtonsHeld(0) & WPAD_BUTTON_RIGHT) && (camera.y == 0)) camera.y = 0; if (WPAD_ButtonsHeld(0) & WPAD_BUTTON_LEFT) camera.y++; if ((WPAD_ButtonsHeld(0) & WPAD_BUTTON_UP) && (camera.x >= 1)) camera.x--; else if((WPAD_ButtonsHeld(0) & WPAD_BUTTON_UP) && (camera.x == 0)) camera.x = 0; if (WPAD_ButtonsHeld(0) & WPAD_BUTTON_DOWN) camera.x++; if ((WPAD_ButtonsDown(0) & WIIMOTE_BUTTON_ONE)) debugflag = !debugflag; if (WPAD_ButtonsDown(0) & WIIMOTE_BUTTON_TWO) mapflag = true; /*--------------========END game calc===========--------------------*/ GRRLIB_FillScreen(GRRLIB_BLACK); /*--------------------------------------------------------------- Map drawing will need to be split into 2 different passes, so that the characters can be drawn inbetween the 2 layers. This is the first of 2 passes. At any given time, 16 tiles will be in the level renderer. To figure out which tiles to draw and how far to offset them, we'll need to use the camera's coordinates and divide by 256 to find our starting tile. The rest can be filled in automatically, as each line is 4 tiles long. 4 arrays were created for holding this render queue, named "vis_tiles_x[4]" respectively. A member of CAMOBJ called in_tile was added for computing where in the main tile array to search. -------------------------------------------------------------------*/ // first we fill our screen cache camera.in_tile[0] = camera.x/256; // row camera.in_tile[1] = camera.y/256; // column first_tile[0] = camera.in_tile[0]; first_tile[1] = camera.in_tile[1]; for(h=0; h < 4; h++) // first row { for(v=0; v < 4; v++) { vis_tiles[h][v] = tilearray[first_tile[1] + v][first_tile[0] + h]; } } // this is the actual display loop, first pass for(h = 0; h < 4; h++) { for(v=0; v < 4; v++) { x_current = (first_tile[0] + h) * 256; y_current = (first_tile[1] + v) * 256; GRRLIB_DrawImg(x_current - camera.x, y_current - camera.y, tiledata[vis_tiles[h][v]].tile_col, 0, 1, 1, GRRLIB_WHITE); } } // Draw Sonic 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); GRRLIB_Rectangle(pointer.x, pointer.y, 4, 4, GRRLIB_RED, true); // second level render pass goes here. // Drawing HUD - this will need to get moved to the end of the drawchain to show up // on top of everything else sprintf(time_string, "Time: %d:%d:%d", minutecounter,secondcounter,framecounter); GRRLIB_PrintfTTF(32,32, font1, sonic_obj.score_string, 26, GRRLIB_WHITE); GRRLIB_PrintfTTF(32,64, font1, time_string, 26, GRRLIB_WHITE); GRRLIB_PrintfTTF(32,98, font1, sonic_obj.ring_string, 26, GRRLIB_WHITE); if ((debugflag == true) && (mapflag == false)) { sprintf(debug_ln1, "x: %d", camera.x); sprintf(debug_ln2, "y: %d", camera.y); sprintf(debug_ln3, "it: %d:%d | %d x %d", camera.in_tile[0], camera.in_tile[1], (camera.x % 256), (camera.y % 256)); GRRLIB_PrintfTTF(420,32, font1, debug_ln1, 18, GRRLIB_RED); GRRLIB_PrintfTTF(420,64, font1, debug_ln2, 18, GRRLIB_RED); GRRLIB_PrintfTTF(420,98, font1, debug_ln3, 18, GRRLIB_RED); } while (mapflag == true) { WPAD_ScanPads(); debugflag = false; sprintf(debug_ln1, "%d | %d | %d | %d", vis_tiles[0][0], vis_tiles[1][0], vis_tiles[2][0], vis_tiles[3][0]); sprintf(debug_ln2, "%d | %d | %d | %d", vis_tiles[0][1], vis_tiles[1][1], vis_tiles[2][1], vis_tiles[3][1]); sprintf(debug_ln3, "%d | %d | %d | %d", vis_tiles[0][2], vis_tiles[1][2], vis_tiles[2][2], vis_tiles[3][2]); sprintf(debug_ln4, "%d | %d | %d | %d", vis_tiles[0][3], vis_tiles[1][3], vis_tiles[2][3], vis_tiles[3][3]); GRRLIB_PrintfTTF(50,32, font1, debug_ln1, 22, GRRLIB_WHITE); GRRLIB_PrintfTTF(50,64, font1, debug_ln2, 22, GRRLIB_WHITE); GRRLIB_PrintfTTF(50,98, font1, debug_ln3, 22, GRRLIB_WHITE); //GRRLIB_PrintfTTF(50,130, font1, debug_ln4, 22, GRRLIB_WHITE); if (WPAD_ButtonsHeld(0) & WIIMOTE_BUTTON_ONE) { mapflag = false; break; } else if (WPAD_ButtonsHeld(0) & WPAD_BUTTON_HOME) { GRRLIB_Exit(); exit(0); } GRRLIB_Render(); } if (WPAD_ButtonsDown(0) & WPAD_BUTTON_HOME) break; if (WPAD_ButtonsDown(0) & WIIMOTE_BUTTON_A) GRRLIB_ScrShot("screenshot.png"); GRRLIB_Render(); } return; } int main(int argc, char **argv) { // Initialise the Graphics & Video subsystem GRRLIB_Init(); // Initialise the Wiimotes WPAD_Init(); WPAD_SetDataFormat(WPAD_CHAN_0, WPAD_FMT_BTNS_ACC_IR); GameLoop(); GRRLIB_Exit(); // Be a good boy, clear the memory allocated by GRRLIB exit(0); // Use exit() to exit a program, do not use 'return' from main() }