diff options
author | Silvan Jegen <silvan@homearch> | 2019-04-19 16:20:44 +0200 |
---|---|---|
committer | Silvan Jegen <silvan@homearch> | 2019-04-19 16:20:44 +0200 |
commit | e168efe028dec4073d85d988de49cc8c642c6b39 (patch) | |
tree | 782ec7d8d759ac9d313d284c390f1186430eb0be | |
parent | 54591b7d51269554ba672906fb2f9a569fa38287 (diff) |
Use Unix line endings
-rw-r--r-- | echidna_ai.c | 489 | ||||
-rw-r--r-- | game.c | 404 | ||||
-rw-r--r-- | helpscreen.c | 60 | ||||
-rw-r--r-- | level.c | 466 | ||||
-rw-r--r-- | main.c | 218 | ||||
-rw-r--r-- | 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 + } + } + } +} + + @@ -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; + } + } + } +} @@ -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; + } + } +} @@ -1,109 +1,109 @@ -// Standard libraries ( not written by me )
-#include <stdio.h>
-#include <string.h>
-
-// 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 <stdio.h> +#include <string.h> + +// 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; +} @@ -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); +} |