summaryrefslogtreecommitdiff
path: root/level.c
blob: 0db6d8816c78511275a2b533b4bd7b4a3f8b39ba (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
/* 
 * 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 * surf_entities; // Player and echidna pictures
	SDL_Texture * surf_tiles;    // Level tiles/squares
	
	// A various selection of losing screens
	SDL_Texture * surf_losingscreens[5];
	
	// The screen to display when the game ends
	SDL_Texture * surf_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->surf_entities = SDL_CreateTextureFromSurface(renderer, surf_entities);

	SDL_Surface* surf_tiles = IMG_Load( "tiles.png" );
  level->surf_tiles = SDL_CreateTextureFromSurface(renderer, surf_tiles);
	
	// Do the same for the losing screens
	SDL_Surface* surf_losingscreen = IMG_Load("failscreen_01.png");
  level->surf_losingscreens[0] = SDL_CreateTextureFromSurface(renderer, surf_losingscreen);

	SDL_Surface* surf_winning_screen = IMG_Load("winningscreen.png");
  level->surf_winning_screen = SDL_CreateTextureFromSurface(renderer, surf_winning_screen);
}



// 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;
		}
	}
}