diff options
| -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); +}  | 
