summaryrefslogtreecommitdiff
path: root/game.c
blob: e0246b6b473bef5e0f916fa9485aa80830cbd8d8 (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
#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, &currentlevel ); // 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, &currentlevel );
			
			// Set the player's new position
			currentlevel.px = currentlevel.sx;
			currentlevel.py = currentlevel.sy;

			// Main game loop
			level_state = play_level(renderer, &currentlevel);
			
		}
		// 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
}