From e168efe028dec4073d85d988de49cc8c642c6b39 Mon Sep 17 00:00:00 2001 From: Silvan Jegen Date: Fri, 19 Apr 2019 16:20:44 +0200 Subject: Use Unix line endings --- echidna_ai.c | 489 +++++++++++++++++++++++++++++------------------------------ game.c | 404 ++++++++++++++++++++++++------------------------ helpscreen.c | 60 ++++---- level.c | 466 ++++++++++++++++++++++++++++---------------------------- main.c | 218 +++++++++++++------------- render.c | 286 +++++++++++++++++----------------- 6 files changed, 961 insertions(+), 962 deletions(-) diff --git a/echidna_ai.c b/echidna_ai.c index 64759a4..5803b10 100644 --- a/echidna_ai.c +++ b/echidna_ai.c @@ -1,245 +1,244 @@ - -/* The echidna AI has three modes: - * - * MODE_HUNT - * The echidna tries to close the space between itself and the player - * - * MODE_FOLLOW_WALL - * The echidna has hit a wall and is now following it - * - * MODE_CORNER - * This mode makes the monster move around a corner after it has been following a wall - * - * See the developer documentation for more information about how this large algorithm works. - */ - -// Give each of the modes a numerical value -#define MODE_HUNT 1 -#define MODE_FOLLOW_WALL 2 - -// The echidna structure is declared in 'level.c' so it can be loaded - - -// is_square solid returns 1 if a square is not passable ( ie a wall or closed door ) -// and 0 if it is ( ie floor, button ) -int is_square_solid( struct level_struct *currentlevel, int xpos, int ypos ) -{ - - switch ( currentlevel->squares[ xpos ][ ypos ] ) - { - case T_WALL: return 1; - case T_BLUE_DOOR_UP: return 1; - case T_YELL_DOOR_UP: return 1; - case T_RED_DOOR_UP: return 1; - - // The default case occurs when none of the above are met - default: return 0; // ie the square is walkable on - } -} - -// run_all_echidnaAI_onestep runs one 'step' of echidna AI for every echidna in a level -void run_all_echidnaAI_onestep( struct level_struct *currentlevel ) -{ - // Ieterate through each and every echinda - int ech_no; // Echidna number - for ( ech_no = 0; ech_no < 10; ech_no++ ) - { - struct echidna *current_echidna = ¤tlevel->echidnas[ech_no]; - - - if ( current_echidna->this_echidna_exists == 1 ) - { - - // This echidna is alive and we can move it - - // How the echidna is going to move, similiar to the player code; - signed int x = 0; - signed int y = 0; - - - /////////////////////////////////////////////////////////////////////////////////////////////// HUNT MODE - if ( current_echidna->mode == MODE_HUNT ) - { - // Find the orthoganol ( x and y, not true or diagonal ) distances between the echidna and player - // NB abs() returns the absolute value of something, which fixes negative numbers by changing - // them to be position - int distx = abs( current_echidna->xpos - currentlevel->px ); - int disty = abs( current_echidna->ypos - currentlevel->py ); - - if ( distx > disty ) - { - - // Now check whether the player is to the left or right of the echidna - if ( currentlevel->px > current_echidna->xpos ) - { - // Echidna is left of player - x = 1; - } - else - { - // Echidna is right of player - x = -1; - } - } - else - { - // Is the player abov or below the echidna - if ( currentlevel->py > current_echidna->ypos ) - { - // Echidna is below player - y = 1; - } - else - { - // Echidna is above player - y = -1; - } - } - - // Now check if this is going to make the echidna try to walk into a wall - // If it will, change mode to MODE_FOLLOW_WALL - if ( is_square_solid( currentlevel, current_echidna->xpos + x, current_echidna->ypos + y ) == 1 ) - { - current_echidna->mode = MODE_FOLLOW_WALL; - - // Make a decision about which way to move - // This scheme ensures the echidna moves clockwise - current_echidna->move_x = 0; - current_echidna->move_y = 0; - if ( x == 1 ) current_echidna->move_y = 1; - else if ( x == -1 ) current_echidna->move_y = -1; - else if ( y == 1 ) current_echidna->move_x = -1; - else if ( y == -1 ) current_echidna->move_x = 1; - - // Make sure the echidna checks the wall its following - // to see if it still exists - current_echidna->check_x = x; - current_echidna->check_y = y; - } - } - - ///////////////////////////////////////////////////////////////////////////////////////////// FOLLOW WALL MODE - if ( current_echidna->mode == MODE_FOLLOW_WALL ) - { - - // Check the wall we are following - if ( is_square_solid( currentlevel, current_echidna->xpos + current_echidna->check_x, current_echidna->ypos + current_echidna->check_y ) == 1 ) - { - // The wall is still there, so keep following it - x = current_echidna->move_x; - y = current_echidna->move_y; - - // Check if a new wall is in the way - while ( is_square_solid( currentlevel, current_echidna->xpos + x, current_echidna->ypos + y ) == 1 ) - { - // We need to follow another wall now. Same clockwise logic as before - current_echidna->check_x = x; - current_echidna->check_y = y; - if ( x == 1 ) { x = 0; y = 1; } - else if ( x == -1 ) { x = 0; y =-1; } - else if ( y == 1 ) { x =-1; y = 0; } - else if ( y == -1 ) { x = 1; y = 0; } - current_echidna->move_x = x; - current_echidna->move_y = y; - } - } - else - { - // The wall has gone, so immediately go around the corner - x = current_echidna->check_x; - y = current_echidna->check_y; - - - // And resume HUNT mode next time - current_echidna->mode = MODE_HUNT; - } - } - - ////////////////////////////////////////////// MOVE THE ECHIDNA - - // Move - current_echidna->xpos = current_echidna->xpos + x; - current_echidna->ypos = current_echidna->ypos + y; - - // Check if we have moved ontop of an Echidna, and if so then move back to where we started - // ( it will appear as if we have not moved at all ) - - int target_num; - for ( target_num = 0; target_num < ech_no; target_num++ ) // Check all of the echidnas BEFORE the current one, so we can have right of way - { - struct echidna target_echidna = currentlevel->echidnas[target_num]; - if ( target_echidna.this_echidna_exists == 1 ) - { - if ( target_echidna.xpos == current_echidna->xpos && target_echidna.ypos == current_echidna->ypos ) - { - // We have a clash, so move back to the original square - current_echidna->xpos = current_echidna->xpos - x; - current_echidna->ypos = current_echidna->ypos - y; - } - } - } - } - } -} - - - -// Returns 1 if an echidna is in the same spot as the player -int check_echidna_proximity( struct level_struct *currentlevel ) -{ - // Ieterate through eacha and every echinda - int ech_no; // Echidna number - - - for ( ech_no = 0; ech_no < 10; ech_no++ ) - { - struct echidna currentechidna = currentlevel->echidnas[ech_no]; - - // First make sure we are checking an echidna that ACTUALLY EXISTS - // Until I wrote this code to check that, I was mysteriously dieing halfway - // throught some levels. Fun :) - if ( currentechidna.this_echidna_exists == 1 ) - { - if ( currentechidna.xpos == currentlevel->px && currentechidna.ypos == currentlevel->py ) // && means 'and' - { - // An echidna is atop a player - return 1; - } - } - } - - // No echidna is atop the player - return 0; -} - -// Echidnas are the onlt things that can toggler the red levers, and this loop checks to see if that need to happen -void check_echidna_on_switches( struct level_struct *currentlevel ) -{ - // Ieterate through each and every echinda - int ech_no; // Echidna number - - - for ( ech_no = 0; ech_no < 10; ech_no++ ) - { - struct echidna currentechidna = currentlevel->echidnas[ech_no]; - - if ( currentechidna.this_echidna_exists == 1 ) - { - // Get the square the echinda is standing on - char square = currentlevel->squares[ currentechidna.xpos ][ currentechidna.ypos ] ; - - if ( square == T_RED_SWITCH_ON ) - { - level_replace_tiles( currentlevel, T_RED_SWITCH_ON, T_RED_SWITCH_OFF ); // change ALL levers - level_replace_tiles( currentlevel, T_RED_DOOR_UP, T_RED_DOOR_DOWN ); // open all red doors - } - else if ( square == T_RED_SWITCH_OFF ) - { - level_replace_tiles( currentlevel, T_RED_SWITCH_OFF, T_RED_SWITCH_ON ); // change ALL levers - level_replace_tiles( currentlevel, T_RED_DOOR_DOWN, T_RED_DOOR_UP ); // close all red doors - } - } - } -} - - + +/* The echidna AI has three modes: + * + * MODE_HUNT + * The echidna tries to close the space between itself and the player + * + * MODE_FOLLOW_WALL + * The echidna has hit a wall and is now following it + * + * MODE_CORNER + * This mode makes the monster move around a corner after it has been following a wall + * + * See the developer documentation for more information about how this large algorithm works. + */ + +// Give each of the modes a numerical value +#define MODE_HUNT 1 +#define MODE_FOLLOW_WALL 2 + +// The echidna structure is declared in 'level.c' so it can be loaded + + +// is_square solid returns 1 if a square is not passable ( ie a wall or closed door ) +// and 0 if it is ( ie floor, button ) +int is_square_solid( struct level_struct *currentlevel, int xpos, int ypos ) +{ + + switch ( currentlevel->squares[ xpos ][ ypos ] ) + { + case T_WALL: return 1; + case T_BLUE_DOOR_UP: return 1; + case T_YELL_DOOR_UP: return 1; + case T_RED_DOOR_UP: return 1; + + // The default case occurs when none of the above are met + default: return 0; // ie the square is walkable on + } +} + +// run_all_echidnaAI_onestep runs one 'step' of echidna AI for every echidna in a level +void run_all_echidnaAI_onestep( struct level_struct *currentlevel ) +{ + // Ieterate through each and every echinda + int ech_no; // Echidna number + for ( ech_no = 0; ech_no < 10; ech_no++ ) + { + struct echidna *current_echidna = ¤tlevel->echidnas[ech_no]; + + + if ( current_echidna->this_echidna_exists == 1 ) + { + + // This echidna is alive and we can move it + + // How the echidna is going to move, similiar to the player code; + signed int x = 0; + signed int y = 0; + + + /////////////////////////////////////////////////////////////////////////////////////////////// HUNT MODE + if ( current_echidna->mode == MODE_HUNT ) + { + // Find the orthoganol ( x and y, not true or diagonal ) distances between the echidna and player + // NB abs() returns the absolute value of something, which fixes negative numbers by changing + // them to be position + int distx = abs( current_echidna->xpos - currentlevel->px ); + int disty = abs( current_echidna->ypos - currentlevel->py ); + + if ( distx > disty ) + { + + // Now check whether the player is to the left or right of the echidna + if ( currentlevel->px > current_echidna->xpos ) + { + // Echidna is left of player + x = 1; + } + else + { + // Echidna is right of player + x = -1; + } + } + else + { + // Is the player abov or below the echidna + if ( currentlevel->py > current_echidna->ypos ) + { + // Echidna is below player + y = 1; + } + else + { + // Echidna is above player + y = -1; + } + } + + // Now check if this is going to make the echidna try to walk into a wall + // If it will, change mode to MODE_FOLLOW_WALL + if ( is_square_solid( currentlevel, current_echidna->xpos + x, current_echidna->ypos + y ) == 1 ) + { + current_echidna->mode = MODE_FOLLOW_WALL; + + // Make a decision about which way to move + // This scheme ensures the echidna moves clockwise + current_echidna->move_x = 0; + current_echidna->move_y = 0; + if ( x == 1 ) current_echidna->move_y = 1; + else if ( x == -1 ) current_echidna->move_y = -1; + else if ( y == 1 ) current_echidna->move_x = -1; + else if ( y == -1 ) current_echidna->move_x = 1; + + // Make sure the echidna checks the wall its following + // to see if it still exists + current_echidna->check_x = x; + current_echidna->check_y = y; + } + } + + ///////////////////////////////////////////////////////////////////////////////////////////// FOLLOW WALL MODE + if ( current_echidna->mode == MODE_FOLLOW_WALL ) + { + + // Check the wall we are following + if ( is_square_solid( currentlevel, current_echidna->xpos + current_echidna->check_x, current_echidna->ypos + current_echidna->check_y ) == 1 ) + { + // The wall is still there, so keep following it + x = current_echidna->move_x; + y = current_echidna->move_y; + + // Check if a new wall is in the way + while ( is_square_solid( currentlevel, current_echidna->xpos + x, current_echidna->ypos + y ) == 1 ) + { + // We need to follow another wall now. Same clockwise logic as before + current_echidna->check_x = x; + current_echidna->check_y = y; + if ( x == 1 ) { x = 0; y = 1; } + else if ( x == -1 ) { x = 0; y =-1; } + else if ( y == 1 ) { x =-1; y = 0; } + else if ( y == -1 ) { x = 1; y = 0; } + current_echidna->move_x = x; + current_echidna->move_y = y; + } + } + else + { + // The wall has gone, so immediately go around the corner + x = current_echidna->check_x; + y = current_echidna->check_y; + + + // And resume HUNT mode next time + current_echidna->mode = MODE_HUNT; + } + } + + ////////////////////////////////////////////// MOVE THE ECHIDNA + + // Move + current_echidna->xpos = current_echidna->xpos + x; + current_echidna->ypos = current_echidna->ypos + y; + + // Check if we have moved ontop of an Echidna, and if so then move back to where we started + // ( it will appear as if we have not moved at all ) + + int target_num; + for ( target_num = 0; target_num < ech_no; target_num++ ) // Check all of the echidnas BEFORE the current one, so we can have right of way + { + struct echidna target_echidna = currentlevel->echidnas[target_num]; + if ( target_echidna.this_echidna_exists == 1 ) + { + if ( target_echidna.xpos == current_echidna->xpos && target_echidna.ypos == current_echidna->ypos ) + { + // We have a clash, so move back to the original square + current_echidna->xpos = current_echidna->xpos - x; + current_echidna->ypos = current_echidna->ypos - y; + } + } + } + } + } +} + + + +// Returns 1 if an echidna is in the same spot as the player +int check_echidna_proximity( struct level_struct *currentlevel ) +{ + // Ieterate through eacha and every echinda + int ech_no; // Echidna number + + for ( ech_no = 0; ech_no < 10; ech_no++ ) + { + struct echidna currentechidna = currentlevel->echidnas[ech_no]; + + // First make sure we are checking an echidna that ACTUALLY EXISTS + // Until I wrote this code to check that, I was mysteriously dieing halfway + // throught some levels. Fun :) + if ( currentechidna.this_echidna_exists == 1 ) + { + if ( currentechidna.xpos == currentlevel->px && currentechidna.ypos == currentlevel->py ) // && means 'and' + { + // An echidna is atop a player + return 1; + } + } + } + + // No echidna is atop the player + return 0; +} + +// Echidnas are the onlt things that can toggler the red levers, and this loop checks to see if that need to happen +void check_echidna_on_switches( struct level_struct *currentlevel ) +{ + // Ieterate through each and every echinda + int ech_no; // Echidna number + + + for ( ech_no = 0; ech_no < 10; ech_no++ ) + { + struct echidna currentechidna = currentlevel->echidnas[ech_no]; + + if ( currentechidna.this_echidna_exists == 1 ) + { + // Get the square the echinda is standing on + char square = currentlevel->squares[ currentechidna.xpos ][ currentechidna.ypos ] ; + + if ( square == T_RED_SWITCH_ON ) + { + level_replace_tiles( currentlevel, T_RED_SWITCH_ON, T_RED_SWITCH_OFF ); // change ALL levers + level_replace_tiles( currentlevel, T_RED_DOOR_UP, T_RED_DOOR_DOWN ); // open all red doors + } + else if ( square == T_RED_SWITCH_OFF ) + { + level_replace_tiles( currentlevel, T_RED_SWITCH_OFF, T_RED_SWITCH_ON ); // change ALL levers + level_replace_tiles( currentlevel, T_RED_DOOR_DOWN, T_RED_DOOR_UP ); // close all red doors + } + } + } +} + + diff --git a/game.c b/game.c index bffd98b..a77234c 100644 --- a/game.c +++ b/game.c @@ -1,202 +1,202 @@ -#define PLAYER_IS_OK 1 -#define PLAYER_IS_DEAD 2 -#define PLAYER_HAS_WON 3 - - - -// player_action moves the player -// It returns one of the three PLAYER_ define values ( top of this file ) -int player_action( signed int xdiff, signed int ydiff, struct level_struct *currentlevel ) -{ - // Find the tile the player moved into/onto using the player's current position - int newx = currentlevel->px + xdiff; - int newy = currentlevel->py + ydiff; - - // Cycle through the player's faces - currentlevel->player_face++; - if ( currentlevel->player_face == 4 ) currentlevel->player_face = 0; - - // Check to make sure these directions are not solid ( ie walls ) - // and also to see if they are interactive ( ie buttons ) - char targetsquare = currentlevel->squares[newx][newy]; - - if ( targetsquare == T_WALL || targetsquare == T_YELL_DOOR_UP || targetsquare == T_BLUE_DOOR_UP || targetsquare == T_RED_DOOR_UP ) // remember '||' means 'OR' - { - // Cannot move, so do nothing - return PLAYER_IS_OK; - } - else - { - // Move onto the new square - currentlevel->px = newx; - currentlevel->py = newy; - - // Check to see if the new square is interactive ( ie a button ) - - switch ( targetsquare ) - { - case T_BLUE_BUTT_UP: // Blue button - // Change the button to be depressed - currentlevel->squares[newx][newy] = T_BLUE_BUTT_DOWN; - // Change all blue doors in the level to be unlocked - level_replace_tiles( currentlevel, T_BLUE_DOOR_UP, T_BLUE_DOOR_DOWN ); - return PLAYER_IS_OK; - - case T_YELL_BUTT_UP: // Yellow button - // Change the button to be depressed - currentlevel->squares[newx][newy] = T_YELL_BUTT_DOWN; - // Change all blue doors in the level to be unlocked - level_replace_tiles( currentlevel, T_YELL_DOOR_UP, T_YELL_DOOR_DOWN ); - return PLAYER_IS_OK; - - case T_EXIT: - // User is on the exit - return PLAYER_HAS_WON; - - case T_SPIKE: - // The daft player walked onto spikes - return PLAYER_IS_DEAD; - - - - default: - // This has to be here to stop the compiler complaining - // Basically the 'default' is run if no other case is run - return PLAYER_IS_OK; - - } - } - - // Code should never reach this point - // The return line here is just to make the compiler happy - - return 0; - -} - - -int play_level(SDL_Renderer* renderer, struct level_struct *currentlevel) -{ - // Loop until the user completes the level - int player_status = PLAYER_IS_OK; - - while ( player_status == PLAYER_IS_OK ) - { - SDL_Event userinput; - - // WaitEvent is like PollEvent, but instead we pause the program's - // execution until we get input - SDL_WaitEvent( &userinput ); - render(renderer, currentlevel); - - // Player movement - signed int y, x; - - if ( userinput.type == SDL_KEYDOWN ) - { - switch ( userinput.key.keysym.sym ) - { - case SDLK_UP: - x = 0; - y = -1; - break; - - case SDLK_DOWN: - x = 0; - y = 1; - break; - - case SDLK_RIGHT: - x = 1; - y = 0; - break; - - case SDLK_LEFT: - x = -1; - y = 0; - break; - - case SDLK_q: - exit(0); // Quit the game - break; - - case SDLK_h: - // View the help screen - help(renderer); - x = 0; - y = 0; - - default: - // The user hits any other key - // ( do nothing ) - x = 0; - y = 0; - break; - } - - // Now move the player - player_status = player_action( x, y, currentlevel ); - - // Move all of the echidnas - run_all_echidnaAI_onestep( currentlevel ); - - // And make sure we open/close red doors if any - // of them are stepping on red levers - check_echidna_on_switches( currentlevel ); - - // Check to see if the player has run into an echidna - if ( check_echidna_proximity( currentlevel ) == 1 ) player_status = PLAYER_IS_DEAD; - - // Show a losing screen if applicable - if (player_status == PLAYER_IS_DEAD) render_a_losingscreen(renderer, currentlevel); - } - } - - // The code running play_level needs to know if too advance - // to the next level or just repeat the current one (if the - // player has died) - return player_status; -} - -void game_loop(SDL_Renderer* renderer) -{ - struct level_struct currentlevel; - level_load_resources(renderer, ¤tlevel ); // load pixmaps - - // Initialise initial player face - currentlevel.player_face = 2; - - int level_number; // Level's number in progression of game, also used as the filename to load the level from - - // Run through each of the levels - - for ( level_number = 2; level_number <= 10; level_number++ ) - { - int level_state = PLAYER_IS_OK; - - while ( level_state != PLAYER_HAS_WON ) - { - // Load the level - level_load( level_number, ¤tlevel ); - - // Set the player's new position - currentlevel.px = currentlevel.sx; - currentlevel.py = currentlevel.sy; - - // Main game loop - level_state = play_level(renderer, ¤tlevel); - - } - // When the game loop ends, then the user has successfully completed the level, and we can move on - } - - // The user has now completed all of the game - // Show them the winning screen - SDL_RenderClear(renderer); - SDL_RenderCopy(renderer, currentlevel.winning_screen, NULL, NULL); - SDL_RenderPresent(renderer); - - SDL_Delay(20 * 1000); // 20 * 1000msecs = 20 seconds - - // Once this function ends, we return to the menu -} +#define PLAYER_IS_OK 1 +#define PLAYER_IS_DEAD 2 +#define PLAYER_HAS_WON 3 + + + +// player_action moves the player +// It returns one of the three PLAYER_ define values ( top of this file ) +int player_action( signed int xdiff, signed int ydiff, struct level_struct *currentlevel ) +{ + // Find the tile the player moved into/onto using the player's current position + int newx = currentlevel->px + xdiff; + int newy = currentlevel->py + ydiff; + + // Cycle through the player's faces + currentlevel->player_face++; + if ( currentlevel->player_face == 4 ) currentlevel->player_face = 0; + + // Check to make sure these directions are not solid ( ie walls ) + // and also to see if they are interactive ( ie buttons ) + char targetsquare = currentlevel->squares[newx][newy]; + + if ( targetsquare == T_WALL || targetsquare == T_YELL_DOOR_UP || targetsquare == T_BLUE_DOOR_UP || targetsquare == T_RED_DOOR_UP ) // remember '||' means 'OR' + { + // Cannot move, so do nothing + return PLAYER_IS_OK; + } + else + { + // Move onto the new square + currentlevel->px = newx; + currentlevel->py = newy; + + // Check to see if the new square is interactive ( ie a button ) + + switch ( targetsquare ) + { + case T_BLUE_BUTT_UP: // Blue button + // Change the button to be depressed + currentlevel->squares[newx][newy] = T_BLUE_BUTT_DOWN; + // Change all blue doors in the level to be unlocked + level_replace_tiles( currentlevel, T_BLUE_DOOR_UP, T_BLUE_DOOR_DOWN ); + return PLAYER_IS_OK; + + case T_YELL_BUTT_UP: // Yellow button + // Change the button to be depressed + currentlevel->squares[newx][newy] = T_YELL_BUTT_DOWN; + // Change all blue doors in the level to be unlocked + level_replace_tiles( currentlevel, T_YELL_DOOR_UP, T_YELL_DOOR_DOWN ); + return PLAYER_IS_OK; + + case T_EXIT: + // User is on the exit + return PLAYER_HAS_WON; + + case T_SPIKE: + // The daft player walked onto spikes + return PLAYER_IS_DEAD; + + + + default: + // This has to be here to stop the compiler complaining + // Basically the 'default' is run if no other case is run + return PLAYER_IS_OK; + + } + } + + // Code should never reach this point + // The return line here is just to make the compiler happy + + return 0; + +} + + +int play_level(SDL_Renderer* renderer, struct level_struct *currentlevel) +{ + // Loop until the user completes the level + int player_status = PLAYER_IS_OK; + + while ( player_status == PLAYER_IS_OK ) + { + SDL_Event userinput; + + // WaitEvent is like PollEvent, but instead we pause the program's + // execution until we get input + SDL_WaitEvent( &userinput ); + render(renderer, currentlevel); + + // Player movement + signed int y, x; + + if ( userinput.type == SDL_KEYDOWN ) + { + switch ( userinput.key.keysym.sym ) + { + case SDLK_UP: + x = 0; + y = -1; + break; + + case SDLK_DOWN: + x = 0; + y = 1; + break; + + case SDLK_RIGHT: + x = 1; + y = 0; + break; + + case SDLK_LEFT: + x = -1; + y = 0; + break; + + case SDLK_q: + exit(0); // Quit the game + break; + + case SDLK_h: + // View the help screen + help(renderer); + x = 0; + y = 0; + + default: + // The user hits any other key + // ( do nothing ) + x = 0; + y = 0; + break; + } + + // Now move the player + player_status = player_action( x, y, currentlevel ); + + // Move all of the echidnas + run_all_echidnaAI_onestep( currentlevel ); + + // And make sure we open/close red doors if any + // of them are stepping on red levers + check_echidna_on_switches( currentlevel ); + + // Check to see if the player has run into an echidna + if ( check_echidna_proximity( currentlevel ) == 1 ) player_status = PLAYER_IS_DEAD; + + // Show a losing screen if applicable + if (player_status == PLAYER_IS_DEAD) render_a_losingscreen(renderer, currentlevel); + } + } + + // The code running play_level needs to know if too advance + // to the next level or just repeat the current one (if the + // player has died) + return player_status; +} + +void game_loop(SDL_Renderer* renderer) +{ + struct level_struct currentlevel; + level_load_resources(renderer, ¤tlevel ); // load pixmaps + + // Initialise initial player face + currentlevel.player_face = 2; + + int level_number; // Level's number in progression of game, also used as the filename to load the level from + + // Run through each of the levels + + for ( level_number = 2; level_number <= 10; level_number++ ) + { + int level_state = PLAYER_IS_OK; + + while ( level_state != PLAYER_HAS_WON ) + { + // Load the level + level_load( level_number, ¤tlevel ); + + // Set the player's new position + currentlevel.px = currentlevel.sx; + currentlevel.py = currentlevel.sy; + + // Main game loop + level_state = play_level(renderer, ¤tlevel); + + } + // When the game loop ends, then the user has successfully completed the level, and we can move on + } + + // The user has now completed all of the game + // Show them the winning screen + SDL_RenderClear(renderer); + SDL_RenderCopy(renderer, currentlevel.winning_screen, NULL, NULL); + SDL_RenderPresent(renderer); + + SDL_Delay(20 * 1000); // 20 * 1000msecs = 20 seconds + + // Once this function ends, we return to the menu +} diff --git a/helpscreen.c b/helpscreen.c index bb298bf..213376f 100644 --- a/helpscreen.c +++ b/helpscreen.c @@ -1,30 +1,30 @@ - -void help(SDL_Renderer* renderer) -{ - // Load help image - SDL_Surface* surf_help = IMG_Load("helpscreen.png"); - SDL_Texture* surf_help_tex = SDL_CreateTextureFromSurface(renderer, surf_help); - - SDL_RenderClear(renderer); - SDL_RenderCopy(renderer, surf_help_tex, NULL, NULL); - SDL_RenderPresent(renderer); - - // now free the memory we used to draw this image - SDL_FreeSurface(surf_help); - - // Now wait until the user presses a key - SDL_Event event; - int waiting_for_user = 1; - - while ( waiting_for_user ) - { - while ( SDL_PollEvent( &event ) ) - { - if ( event.type == SDL_KEYDOWN ) - { - waiting_for_user = 0; - break; - } - } - } -} + +void help(SDL_Renderer* renderer) +{ + // Load help image + SDL_Surface* surf_help = IMG_Load("helpscreen.png"); + SDL_Texture* surf_help_tex = SDL_CreateTextureFromSurface(renderer, surf_help); + + SDL_RenderClear(renderer); + SDL_RenderCopy(renderer, surf_help_tex, NULL, NULL); + SDL_RenderPresent(renderer); + + // now free the memory we used to draw this image + SDL_FreeSurface(surf_help); + + // Now wait until the user presses a key + SDL_Event event; + int waiting_for_user = 1; + + while ( waiting_for_user ) + { + while ( SDL_PollEvent( &event ) ) + { + if ( event.type == SDL_KEYDOWN ) + { + waiting_for_user = 0; + break; + } + } + } +} diff --git a/level.c b/level.c index d8a82e4..845f59b 100644 --- a/level.c +++ b/level.c @@ -1,233 +1,233 @@ -/* - * Each of the different types of tile in the levels are numbered - * These are also the values used in the level text files - * - * All of the level tiles are stored in one image, hence the 'Rows' - * below are literally the rows there are positioned in the image. - * Have a look at tiles.png in the game's folder to see them all. - */ - -// Row 1 - main squares -#define T_WALL 0 -#define T_FLOOR 1 -#define T_SPIKE 2 -#define T_EXIT 3 -// Row two - blue button related -#define T_BLUE_BUTT_UP 4 // unpressed button -#define T_BLUE_BUTT_DOWN 5 // depressed button -#define T_BLUE_DOOR_UP 6 // door unlocked by a button -#define T_BLUE_DOOR_DOWN 7 // door locked and impassible -// Row three - yellow button related -#define T_YELL_BUTT_UP 8 -#define T_YELL_BUTT_DOWN 9 -#define T_YELL_DOOR_UP 10 -#define T_YELL_DOOR_DOWN 11 -// Row four - red lever/switch related -#define T_RED_SWITCH_ON 12 -#define T_RED_SWITCH_OFF 13 -#define T_RED_DOOR_UP 14 -#define T_RED_DOOR_DOWN 15 - -// An echidna is one of the monsters -// The structure is define here in level.c so it can be loaded from the level files -struct echidna -{ - int xpos, ypos; // Position on map - int face; // Which of the echidna pictures to use - - int mode; // As described at the top of the document - - int this_echidna_exists; // When this is equal to 1, the echidna is alive and on the level - - // These movement directions are used in the AI code - signed int move_x; - signed int move_y; - // ditto here - signed int check_x; - signed int check_y; - -}; - -/* The level structure is passed to the rendering module, so everything that needs to be seen - * on-screen is also stored here as well - * - * Eg: - * - player position - * - resource locations ( graphics ) - */ -struct level_struct -{ - - char squares[SCREEN_WIDTH_SQUARES][SCREEN_HEIGHT_SQUARES]; // Squares of the level - - // Player details - int player_face; // Picture number ( player has 4 different looks ) - int px, py; // Player X and Y position, on the tiles - int sx, sy; // Player X and Y starting positions - - // Resources - SDL_Texture * entities; - SDL_Texture * tiles; - - // A various selection of losing screens - SDL_Texture * losingscreens[5]; - - // The screen to display when the game ends - SDL_Texture * winning_screen; - - // Up to ten echidnas - struct echidna echidnas[10]; -}; - - - -/* eat_until_newline will 'eat' one character at a time from a file until it detects it has - * finished a line, then stops. This is used to make sure a line of text in the level files - * has been finished before we move on to the next */ -void eat_until_newline( FILE *currentfile ) -{ - char letter='a'; - while ( letter != '\n' ) // The '\n' represents the invisible line ending characters (either CRLF or LF) - { - // Every time a character is read, the current position inside of the file is also moved forward - // one character - letter = fgetc( currentfile ); - } -} - - - -// Load the graphics used in levels -void level_load_resources(SDL_Renderer* renderer, struct level_struct *level ) -{ - // Load the images for things into their own 'surfaces' - SDL_Surface* surf_entities = IMG_Load("entities.png"); - level->entities = SDL_CreateTextureFromSurface(renderer, - surf_entities); - SDL_FreeSurface(surf_entities); - - SDL_Surface* surf_tiles = IMG_Load("tiles.png"); - level->tiles = SDL_CreateTextureFromSurface(renderer, - surf_tiles); - SDL_FreeSurface(surf_tiles); - - SDL_Surface* surf_losing = IMG_Load("failscreen_01.png"); - level->losingscreens[0] = SDL_CreateTextureFromSurface(renderer, - surf_losing); - SDL_FreeSurface(surf_losing); - - SDL_Surface* surf_win = IMG_Load("winningscreen.png"); - level->winning_screen = SDL_CreateTextureFromSurface(renderer, - surf_win); - SDL_FreeSurface(surf_win); -} - - - -// level_load will load a level from a text file into a level_struct -void level_load( int level_number, struct level_struct *level ) -{ - - // Create the level's actual filename from its number - char levelname[20]; - sprintf( levelname, "level_%02d.level", level_number ); - - // Attempt to open the file - FILE *levelfile = fopen( levelname, "r"); // r = reading mode - - - // Check if the file loaded, quit program if this has failed - if ( levelfile == NULL ) - { - // For some reason the file cannot be accessed - // This could be because it does not exist or we don't have permission to open it - - fprintf(stderr, "Error: Could not open level file %d, perhaps it is somewhere else or does not exist?\n", level_number ); - fprintf(stderr, "Exiting... \n\n" ); - - exit(1); - } - - // First we need to skip the first line of the file, which is dedicated to title, author etc information - eat_until_newline( levelfile ); - - // The second line contains the starting position of the player - fscanf( levelfile, "%d %d", &level->sx, &level->sy ); - eat_until_newline(levelfile); - - printf("Loading level %s, player at position: %d %d\n", levelname, level->sx, level->sy ); - - /* Ieterate through the file and load the squares into the level's structure - * Each row/line in the file represents a row of squares in the level - * Each square is represented in the rows by a number from 0 to 255 - * Open up one of the level files with a text editor to study its structure further - */ - - /* WARNING: This code assumes the file is structured correctly in the first place! - * The game will behave in an unexpected manner if the level files are not perfect! - */ - - int row, column; // Current position - - for ( row=0; row < SCREEN_HEIGHT_SQUARES; row++ ) - { - for ( column=0; column < SCREEN_WIDTH_SQUARES; column++ ) - { - /* we read each two letter number into a three letter character array ( string ) - * the third character is used to represent the end or 'termination' of the string - */ - char tempstring[3]; - fgets( tempstring, 3, levelfile ); - - // atoi() is a standard C function used to covert a string of numbers in character - // form into an actual number ( integer ) - level->squares[column][row] = atoi( tempstring ); - } - - // We need to make sure that we finish the current line of numbers/characters in the - // level file, and are up to the next, else bad things may happen - eat_until_newline( levelfile ); - } - - /* Now start loading the echidna positions - * Every line after the main level tile body is an echidna - * The format for each line is: - * xpos ypos face - * Where each of them is a number, face being a number from 0 to 3 choosing the particular face of the echidna - */ - - int ech_no; // Echidna number - - for ( ech_no = 0; ech_no < 10; ech_no++ ) - { - level->echidnas[ech_no].this_echidna_exists = 0; - level->echidnas[ech_no].mode = 1; // Start in hunt mode - - // Now load the echidna values in - if ( fscanf( levelfile, "%d %d %d", &level->echidnas[ech_no].xpos, &level->echidnas[ech_no].ypos, &level->echidnas[ech_no].face ) == 3 ) - { - // When fscanf returns a number other than '3' ( which is how many numbers we are reading per line ) - // we know there are no more lines in the file and we should stop, hence the == 3 - level->echidnas[ech_no].this_echidna_exists = 1; - - printf("Echidna loaded at x:%d y:%d\n", level->echidnas[ech_no].xpos, level->echidnas[ech_no].ypos ); - } - } - - -} - -// Replace every tile in the level of firsttype with secondtype -// Useful for eg opening doors -void level_replace_tiles( struct level_struct *currentlevel, char firsttype, char secondtype ) -{ - int tilex, tiley; // Where we are up to - - for ( tiley=0; tiley < SCREEN_WIDTH_SQUARES; tiley++ ) - { - for ( tilex=0; tilex < SCREEN_WIDTH_SQUARES; tilex++ ) - { - if ( currentlevel->squares[tilex][tiley] == firsttype ) currentlevel->squares[tilex][tiley] = secondtype; - } - } -} +/* + * Each of the different types of tile in the levels are numbered + * These are also the values used in the level text files + * + * All of the level tiles are stored in one image, hence the 'Rows' + * below are literally the rows there are positioned in the image. + * Have a look at tiles.png in the game's folder to see them all. + */ + +// Row 1 - main squares +#define T_WALL 0 +#define T_FLOOR 1 +#define T_SPIKE 2 +#define T_EXIT 3 +// Row two - blue button related +#define T_BLUE_BUTT_UP 4 // unpressed button +#define T_BLUE_BUTT_DOWN 5 // depressed button +#define T_BLUE_DOOR_UP 6 // door unlocked by a button +#define T_BLUE_DOOR_DOWN 7 // door locked and impassible +// Row three - yellow button related +#define T_YELL_BUTT_UP 8 +#define T_YELL_BUTT_DOWN 9 +#define T_YELL_DOOR_UP 10 +#define T_YELL_DOOR_DOWN 11 +// Row four - red lever/switch related +#define T_RED_SWITCH_ON 12 +#define T_RED_SWITCH_OFF 13 +#define T_RED_DOOR_UP 14 +#define T_RED_DOOR_DOWN 15 + +// An echidna is one of the monsters +// The structure is define here in level.c so it can be loaded from the level files +struct echidna +{ + int xpos, ypos; // Position on map + int face; // Which of the echidna pictures to use + + int mode; // As described at the top of the document + + int this_echidna_exists; // When this is equal to 1, the echidna is alive and on the level + + // These movement directions are used in the AI code + signed int move_x; + signed int move_y; + // ditto here + signed int check_x; + signed int check_y; + +}; + +/* The level structure is passed to the rendering module, so everything that needs to be seen + * on-screen is also stored here as well + * + * Eg: + * - player position + * - resource locations ( graphics ) + */ +struct level_struct +{ + + char squares[SCREEN_WIDTH_SQUARES][SCREEN_HEIGHT_SQUARES]; // Squares of the level + + // Player details + int player_face; // Picture number ( player has 4 different looks ) + int px, py; // Player X and Y position, on the tiles + int sx, sy; // Player X and Y starting positions + + // Resources + SDL_Texture * entities; + SDL_Texture * tiles; + + // A various selection of losing screens + SDL_Texture * losingscreens[5]; + + // The screen to display when the game ends + SDL_Texture * winning_screen; + + // Up to ten echidnas + struct echidna echidnas[10]; +}; + + + +/* eat_until_newline will 'eat' one character at a time from a file until it detects it has + * finished a line, then stops. This is used to make sure a line of text in the level files + * has been finished before we move on to the next */ +void eat_until_newline( FILE *currentfile ) +{ + char letter='a'; + while ( letter != '\n' ) // The '\n' represents the invisible line ending characters (either CRLF or LF) + { + // Every time a character is read, the current position inside of the file is also moved forward + // one character + letter = fgetc( currentfile ); + } +} + + + +// Load the graphics used in levels +void level_load_resources(SDL_Renderer* renderer, struct level_struct *level ) +{ + // Load the images for things into their own 'surfaces' + SDL_Surface* surf_entities = IMG_Load("entities.png"); + level->entities = SDL_CreateTextureFromSurface(renderer, + surf_entities); + SDL_FreeSurface(surf_entities); + + SDL_Surface* surf_tiles = IMG_Load("tiles.png"); + level->tiles = SDL_CreateTextureFromSurface(renderer, + surf_tiles); + SDL_FreeSurface(surf_tiles); + + SDL_Surface* surf_losing = IMG_Load("failscreen_01.png"); + level->losingscreens[0] = SDL_CreateTextureFromSurface(renderer, + surf_losing); + SDL_FreeSurface(surf_losing); + + SDL_Surface* surf_win = IMG_Load("winningscreen.png"); + level->winning_screen = SDL_CreateTextureFromSurface(renderer, + surf_win); + SDL_FreeSurface(surf_win); +} + + + +// level_load will load a level from a text file into a level_struct +void level_load( int level_number, struct level_struct *level ) +{ + + // Create the level's actual filename from its number + char levelname[20]; + sprintf( levelname, "level_%02d.level", level_number ); + + // Attempt to open the file + FILE *levelfile = fopen( levelname, "r"); // r = reading mode + + + // Check if the file loaded, quit program if this has failed + if ( levelfile == NULL ) + { + // For some reason the file cannot be accessed + // This could be because it does not exist or we don't have permission to open it + + fprintf(stderr, "Error: Could not open level file %d, perhaps it is somewhere else or does not exist?\n", level_number ); + fprintf(stderr, "Exiting... \n\n" ); + + exit(1); + } + + // First we need to skip the first line of the file, which is dedicated to title, author etc information + eat_until_newline( levelfile ); + + // The second line contains the starting position of the player + fscanf( levelfile, "%d %d", &level->sx, &level->sy ); + eat_until_newline(levelfile); + + printf("Loading level %s, player at position: %d %d\n", levelname, level->sx, level->sy ); + + /* Ieterate through the file and load the squares into the level's structure + * Each row/line in the file represents a row of squares in the level + * Each square is represented in the rows by a number from 0 to 255 + * Open up one of the level files with a text editor to study its structure further + */ + + /* WARNING: This code assumes the file is structured correctly in the first place! + * The game will behave in an unexpected manner if the level files are not perfect! + */ + + int row, column; // Current position + + for ( row=0; row < SCREEN_HEIGHT_SQUARES; row++ ) + { + for ( column=0; column < SCREEN_WIDTH_SQUARES; column++ ) + { + /* we read each two letter number into a three letter character array ( string ) + * the third character is used to represent the end or 'termination' of the string + */ + char tempstring[3]; + fgets( tempstring, 3, levelfile ); + + // atoi() is a standard C function used to covert a string of numbers in character + // form into an actual number ( integer ) + level->squares[column][row] = atoi( tempstring ); + } + + // We need to make sure that we finish the current line of numbers/characters in the + // level file, and are up to the next, else bad things may happen + eat_until_newline( levelfile ); + } + + /* Now start loading the echidna positions + * Every line after the main level tile body is an echidna + * The format for each line is: + * xpos ypos face + * Where each of them is a number, face being a number from 0 to 3 choosing the particular face of the echidna + */ + + int ech_no; // Echidna number + + for ( ech_no = 0; ech_no < 10; ech_no++ ) + { + level->echidnas[ech_no].this_echidna_exists = 0; + level->echidnas[ech_no].mode = 1; // Start in hunt mode + + // Now load the echidna values in + if ( fscanf( levelfile, "%d %d %d", &level->echidnas[ech_no].xpos, &level->echidnas[ech_no].ypos, &level->echidnas[ech_no].face ) == 3 ) + { + // When fscanf returns a number other than '3' ( which is how many numbers we are reading per line ) + // we know there are no more lines in the file and we should stop, hence the == 3 + level->echidnas[ech_no].this_echidna_exists = 1; + + printf("Echidna loaded at x:%d y:%d\n", level->echidnas[ech_no].xpos, level->echidnas[ech_no].ypos ); + } + } + + +} + +// Replace every tile in the level of firsttype with secondtype +// Useful for eg opening doors +void level_replace_tiles( struct level_struct *currentlevel, char firsttype, char secondtype ) +{ + int tilex, tiley; // Where we are up to + + for ( tiley=0; tiley < SCREEN_WIDTH_SQUARES; tiley++ ) + { + for ( tilex=0; tilex < SCREEN_WIDTH_SQUARES; tilex++ ) + { + if ( currentlevel->squares[tilex][tiley] == firsttype ) currentlevel->squares[tilex][tiley] = secondtype; + } + } +} diff --git a/main.c b/main.c index c1bc2e0..0bc0ec7 100644 --- a/main.c +++ b/main.c @@ -1,109 +1,109 @@ -// Standard libraries ( not written by me ) -#include -#include - -// SDL libraries ( not written by me ) -#include "SDL2/SDL.h" -#include "SDL2/SDL_image.h" - -#ifdef __WIN32__ - // Work around a TCC<->SDL bug regarding the redefintion of main. - // Note: the error messages from TCC make no sense - #undef main -#endif - - -// Window dimensions -#define SCREEN_WIDTH 640 -#define SCREEN_HEIGHT 480 -#define TILE_SIZE 32 // Width and height of 'squares' in the game - -#define SCREEN_WIDTH_SQUARES SCREEN_WIDTH / TILE_SIZE -#define SCREEN_HEIGHT_SQUARES SCREEN_HEIGHT / TILE_SIZE - -// Modules of the game I have written ( in directory with this source file ) -#include "level.c" // Level structures, loading, other levely stuff -#include "helpscreen.c" // Help-screen code -#include "echidna_ai.c" // Echidna movement A -#include "render.c" // Normal in-game rendering -#include "game.c" // Normal in-game code - -/* error_sdl is called when SDL 'throws' an error - * SDL can throw an error if the computer is for example incapable of displaying - * the game, because it is too old ( ie made before the year 1998 ) */ -void error_sdl( char* reason ) -{ - // Print the error message and then close the game - fprintf( stderr, "Error (SDL): %s: %s\n", reason, SDL_GetError() ); - exit(1); -} - -int main(int argc, char *argv[]) -{ - // Initialisation - - /* First we need to tell SDL to create a window. A 'surface' - * is created called surf_screen, 'surfaces' are simply images - * of what we see on-screen. - * - * To make things appear on the screen, I copy ('blit') images - * onto this surface at different locations - */ - - // Initialise SDL - printf("Initialising SDL... "); - if ( SDL_Init( SDL_INIT_VIDEO ) == -1 ) error_sdl("Could not initialise SDL"); - atexit(SDL_Quit); - - SDL_Window * sdlWindow = SDL_CreateWindow("EchidneaMenace", SDL_WINDOWPOS_UNDEFINED, - SDL_WINDOWPOS_UNDEFINED, 640, 480, SDL_WINDOW_FULLSCREEN | SDL_WINDOW_OPENGL ); - SDL_Renderer *renderer = SDL_CreateRenderer(sdlWindow, -1, 0); - - printf("done\n"); - - /// Menu - printf(" ==== Entered menu\n"); - - // Load the menu graphic into memory - SDL_Surface *surf_title = IMG_Load( "titlescreen.png" ); - if (!surf_title) { - error_sdl("could not get title surface\n"); - } - SDL_Texture *surf_title_tex = SDL_CreateTextureFromSurface(renderer, surf_title); - if (!surf_title_tex) { - error_sdl("could not get title texture from surface\n"); - } - - // Need to draw before the while loop otherwise we only do the - // initial drawing after a keydown-event - SDL_RenderClear(renderer); - SDL_RenderCopy(renderer, surf_title_tex, NULL, NULL); - SDL_RenderPresent(renderer); - - // Events are variables used to store user input - SDL_Event menuevent; - - while ( SDL_WaitEvent( &menuevent ) >= 0) - { - SDL_RenderClear(renderer); - SDL_RenderCopy(renderer, surf_title_tex, NULL, NULL); - SDL_RenderPresent(renderer); - - if ( menuevent.type == SDL_KEYDOWN ) - { - // Menu choice selection - if ( menuevent.key.keysym.sym == SDLK_p ) game_loop(renderer); - else if ( menuevent.key.keysym.sym == SDLK_h ) help(renderer); - else if ( menuevent.key.keysym.sym == SDLK_q || menuevent.key.keysym.sym == SDLK_ESCAPE ) exit(0); - } - } - - /* Technically the computer will never reach this point, - * because the while statement above will run forever until the - * user quits the game. - * - * The 'return' statement is still written here so the compiler - * does not complain */ - - return 0; -} +// Standard libraries ( not written by me ) +#include +#include + +// SDL libraries ( not written by me ) +#include "SDL2/SDL.h" +#include "SDL2/SDL_image.h" + +#ifdef __WIN32__ + // Work around a TCC<->SDL bug regarding the redefintion of main. + // Note: the error messages from TCC make no sense + #undef main +#endif + + +// Window dimensions +#define SCREEN_WIDTH 640 +#define SCREEN_HEIGHT 480 +#define TILE_SIZE 32 // Width and height of 'squares' in the game + +#define SCREEN_WIDTH_SQUARES SCREEN_WIDTH / TILE_SIZE +#define SCREEN_HEIGHT_SQUARES SCREEN_HEIGHT / TILE_SIZE + +// Modules of the game I have written ( in directory with this source file ) +#include "level.c" // Level structures, loading, other levely stuff +#include "helpscreen.c" // Help-screen code +#include "echidna_ai.c" // Echidna movement A +#include "render.c" // Normal in-game rendering +#include "game.c" // Normal in-game code + +/* error_sdl is called when SDL 'throws' an error + * SDL can throw an error if the computer is for example incapable of displaying + * the game, because it is too old ( ie made before the year 1998 ) */ +void error_sdl( char* reason ) +{ + // Print the error message and then close the game + fprintf( stderr, "Error (SDL): %s: %s\n", reason, SDL_GetError() ); + exit(1); +} + +int main(int argc, char *argv[]) +{ + // Initialisation + + /* First we need to tell SDL to create a window. A 'surface' + * is created called surf_screen, 'surfaces' are simply images + * of what we see on-screen. + * + * To make things appear on the screen, I copy ('blit') images + * onto this surface at different locations + */ + + // Initialise SDL + printf("Initialising SDL... "); + if ( SDL_Init( SDL_INIT_VIDEO ) == -1 ) error_sdl("Could not initialise SDL"); + atexit(SDL_Quit); + + SDL_Window * sdlWindow = SDL_CreateWindow("EchidneaMenace", SDL_WINDOWPOS_UNDEFINED, + SDL_WINDOWPOS_UNDEFINED, 640, 480, SDL_WINDOW_FULLSCREEN | SDL_WINDOW_OPENGL ); + SDL_Renderer *renderer = SDL_CreateRenderer(sdlWindow, -1, 0); + + printf("done\n"); + + /// Menu + printf(" ==== Entered menu\n"); + + // Load the menu graphic into memory + SDL_Surface *surf_title = IMG_Load( "titlescreen.png" ); + if (!surf_title) { + error_sdl("could not get title surface\n"); + } + SDL_Texture *surf_title_tex = SDL_CreateTextureFromSurface(renderer, surf_title); + if (!surf_title_tex) { + error_sdl("could not get title texture from surface\n"); + } + + // Need to draw before the while loop otherwise we only do the + // initial drawing after a keydown-event + SDL_RenderClear(renderer); + SDL_RenderCopy(renderer, surf_title_tex, NULL, NULL); + SDL_RenderPresent(renderer); + + // Events are variables used to store user input + SDL_Event menuevent; + + while ( SDL_WaitEvent( &menuevent ) >= 0) + { + SDL_RenderClear(renderer); + SDL_RenderCopy(renderer, surf_title_tex, NULL, NULL); + SDL_RenderPresent(renderer); + + if ( menuevent.type == SDL_KEYDOWN ) + { + // Menu choice selection + if ( menuevent.key.keysym.sym == SDLK_p ) game_loop(renderer); + else if ( menuevent.key.keysym.sym == SDLK_h ) help(renderer); + else if ( menuevent.key.keysym.sym == SDLK_q || menuevent.key.keysym.sym == SDLK_ESCAPE ) exit(0); + } + } + + /* Technically the computer will never reach this point, + * because the while statement above will run forever until the + * user quits the game. + * + * The 'return' statement is still written here so the compiler + * does not complain */ + + return 0; +} diff --git a/render.c b/render.c index a972c45..5f3484d 100644 --- a/render.c +++ b/render.c @@ -1,143 +1,143 @@ - - -/* Different areas of the entities.png image contain different things. - * For example the first row are player pictures whilst the second are - * of echidnas. - * - * 'SDL_Rect's are simple structures containing the location and size - * of these individual pictures inside of the larger 'entities.png' - * - * The same thing is done for the different level graphics - */ - -// Sets the SDL_Rect * crop to the location of specified echinida -// ( there are four echidna pictures, so which_echidna can be 0-3) -void get_pixmap_echidna(int which_echidna, SDL_Rect * crop ) -{ - crop->x = which_echidna * TILE_SIZE; - crop->y = 1 * TILE_SIZE; // all echidnas are in the second row -} - -// Same as above, but for the player (lemon) -void get_pixmap_lemon(int which_lemon, SDL_Rect * crop ) -{ - crop->x = which_lemon * TILE_SIZE; - crop->y = 0; // all lemons are in the first row -} - -// Getting the rectangle for level tiles is a little harder, as they are spread -// across multiple rows. Otherwise this does similarly to above modules -void get_pixmap_level( char number, SDL_Rect * crop ) -{ - int row, column; - - row = 0; - column = (int)number; - - // Compensate for the fact these pictures span multiple rows - while ( column >= 4 ) - { - column = column - 4; - row++; // add one to row's value - } - - crop->x = column * TILE_SIZE; - crop->y = row * TILE_SIZE; -} - - - - -// render() paints everything onto the screen when called -void render(SDL_Renderer* renderer, struct level_struct *currentlevel) -{ - /* The picture on-screen is made in three steps: - * 1) Draw the level onto the screen surface - * 2) Draw the player and echidnas ontop of this - * 3) 'flip' the screen, so the changes can be seen - */ - - SDL_Rect destination; // where the image is going to be painted to on-screen - SDL_Rect pixmap_crop; // where the tile image is on the source image - destination.w = TILE_SIZE; - destination.h = TILE_SIZE; - pixmap_crop.w = TILE_SIZE; - pixmap_crop.h = TILE_SIZE; - - /// Level drawing - - // ieterate through each square of the level, drawing the appropriate - // pictures for each onto the screen - - int tilex, tiley; // Where we are up to - - for ( tiley=0; tiley < SCREEN_WIDTH_SQUARES; tiley++ ) - { - for ( tilex=0; tilex < SCREEN_WIDTH_SQUARES; tilex++ ) - { - /// Determine which section of the entities image we are going to use - get_pixmap_level( currentlevel->squares[tilex][tiley] , &pixmap_crop ); - // remember that the currentlevel structure contains the array of - // level squares/tiles - - /// Determine where we are going to draw this image on-screen - destination.x = tilex * TILE_SIZE; - destination.y = tiley * TILE_SIZE; - - /// Render this tile onto the screen - int ret = SDL_RenderCopy(renderer, currentlevel->tiles, - &pixmap_crop, - &destination); - if (ret < 0 ) { - printf("Error (SDL): %s: %s\n", "could not render copy tiles", SDL_GetError()); - } - } - } - - /// Entity drawing - // First of all the character - get_pixmap_lemon( currentlevel->player_face, &pixmap_crop ); - destination.x = currentlevel->px * TILE_SIZE; - destination.y = currentlevel->py * TILE_SIZE; - - // Render onto screen - SDL_RenderCopy(renderer, currentlevel->entities, - &pixmap_crop, - &destination); - - // Now draw all of the echidnas - int ech_no; // Echidna number - for ( ech_no = 0; ech_no < 10; ech_no++ ) - { - struct echidna current_echidna = currentlevel->echidnas[ech_no]; - - if ( current_echidna.this_echidna_exists == 1 ) - { - // This echidnas exists and is alive - - // Get its location and face - get_pixmap_echidna( current_echidna.face, &pixmap_crop ); - destination.x = current_echidna.xpos * TILE_SIZE; - destination.y = current_echidna.ypos * TILE_SIZE; - - // Render it onscreen - SDL_RenderCopy(renderer, currentlevel->entities, - &pixmap_crop, - &destination); - } - } - - // Flip screen, so everything we have 'blitted' can now be seen - SDL_RenderPresent(renderer); -} - -void render_a_losingscreen(SDL_Renderer* renderer, struct level_struct *currentlevel) -{ - // Render a losing screen - SDL_RenderClear(renderer); - SDL_RenderCopy(renderer, currentlevel->winning_screen, NULL, NULL); - SDL_RenderPresent(renderer); - - // Wait a second so the user can see the losing screen - SDL_Delay(2000); -} + + +/* Different areas of the entities.png image contain different things. + * For example the first row are player pictures whilst the second are + * of echidnas. + * + * 'SDL_Rect's are simple structures containing the location and size + * of these individual pictures inside of the larger 'entities.png' + * + * The same thing is done for the different level graphics + */ + +// Sets the SDL_Rect * crop to the location of specified echinida +// ( there are four echidna pictures, so which_echidna can be 0-3) +void get_pixmap_echidna(int which_echidna, SDL_Rect * crop ) +{ + crop->x = which_echidna * TILE_SIZE; + crop->y = 1 * TILE_SIZE; // all echidnas are in the second row +} + +// Same as above, but for the player (lemon) +void get_pixmap_lemon(int which_lemon, SDL_Rect * crop ) +{ + crop->x = which_lemon * TILE_SIZE; + crop->y = 0; // all lemons are in the first row +} + +// Getting the rectangle for level tiles is a little harder, as they are spread +// across multiple rows. Otherwise this does similarly to above modules +void get_pixmap_level( char number, SDL_Rect * crop ) +{ + int row, column; + + row = 0; + column = (int)number; + + // Compensate for the fact these pictures span multiple rows + while ( column >= 4 ) + { + column = column - 4; + row++; // add one to row's value + } + + crop->x = column * TILE_SIZE; + crop->y = row * TILE_SIZE; +} + + + + +// render() paints everything onto the screen when called +void render(SDL_Renderer* renderer, struct level_struct *currentlevel) +{ + /* The picture on-screen is made in three steps: + * 1) Draw the level onto the screen surface + * 2) Draw the player and echidnas ontop of this + * 3) 'flip' the screen, so the changes can be seen + */ + + SDL_Rect destination; // where the image is going to be painted to on-screen + SDL_Rect pixmap_crop; // where the tile image is on the source image + destination.w = TILE_SIZE; + destination.h = TILE_SIZE; + pixmap_crop.w = TILE_SIZE; + pixmap_crop.h = TILE_SIZE; + + /// Level drawing + + // ieterate through each square of the level, drawing the appropriate + // pictures for each onto the screen + + int tilex, tiley; // Where we are up to + + for ( tiley=0; tiley < SCREEN_WIDTH_SQUARES; tiley++ ) + { + for ( tilex=0; tilex < SCREEN_WIDTH_SQUARES; tilex++ ) + { + /// Determine which section of the entities image we are going to use + get_pixmap_level( currentlevel->squares[tilex][tiley] , &pixmap_crop ); + // remember that the currentlevel structure contains the array of + // level squares/tiles + + /// Determine where we are going to draw this image on-screen + destination.x = tilex * TILE_SIZE; + destination.y = tiley * TILE_SIZE; + + /// Render this tile onto the screen + int ret = SDL_RenderCopy(renderer, currentlevel->tiles, + &pixmap_crop, + &destination); + if (ret < 0 ) { + printf("Error (SDL): %s: %s\n", "could not render copy tiles", SDL_GetError()); + } + } + } + + /// Entity drawing + // First of all the character + get_pixmap_lemon( currentlevel->player_face, &pixmap_crop ); + destination.x = currentlevel->px * TILE_SIZE; + destination.y = currentlevel->py * TILE_SIZE; + + // Render onto screen + SDL_RenderCopy(renderer, currentlevel->entities, + &pixmap_crop, + &destination); + + // Now draw all of the echidnas + int ech_no; // Echidna number + for ( ech_no = 0; ech_no < 10; ech_no++ ) + { + struct echidna current_echidna = currentlevel->echidnas[ech_no]; + + if ( current_echidna.this_echidna_exists == 1 ) + { + // This echidnas exists and is alive + + // Get its location and face + get_pixmap_echidna( current_echidna.face, &pixmap_crop ); + destination.x = current_echidna.xpos * TILE_SIZE; + destination.y = current_echidna.ypos * TILE_SIZE; + + // Render it onscreen + SDL_RenderCopy(renderer, currentlevel->entities, + &pixmap_crop, + &destination); + } + } + + // Flip screen, so everything we have 'blitted' can now be seen + SDL_RenderPresent(renderer); +} + +void render_a_losingscreen(SDL_Renderer* renderer, struct level_struct *currentlevel) +{ + // Render a losing screen + SDL_RenderClear(renderer); + SDL_RenderCopy(renderer, currentlevel->winning_screen, NULL, NULL); + SDL_RenderPresent(renderer); + + // Wait a second so the user can see the losing screen + SDL_Delay(2000); +} -- cgit v1.2.1-18-gbd029