summaryrefslogtreecommitdiff
path: root/game.c
blob: 79502523a60333081d9911d95ec2a59ac8bb694f (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
242
243
244
245
246
#define PLAYER_IS_OK 1
#define PLAYER_IS_DEAD 2
#define PLAYER_HAS_WON 3

#include <time.h>
#include <unistd.h>


void update_player_animation(struct level_struct *cur_level_state) {
	// Cycle through the player's faces
	cur_level_state->player_face++;
	if (cur_level_state->player_face == 4) {
		cur_level_state->player_face = 0;
	}
}

// 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 )
{
	// no movent so we just update the animation
	if (xdiff == 0 && ydiff == 0) {
		update_player_animation(currentlevel);
		return PLAYER_IS_OK;
	}

	// Find the tile the player moved into/onto using the player's
	// current position
	int newx = currentlevel->px + xdiff;
	int newy = currentlevel->py + ydiff;
	
	update_player_animation(currentlevel);
	
	// 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)
{
	struct timespec last_render;
	last_render.tv_sec = 0;
	last_render.tv_nsec = 0;

	// Loop until the user completes the level
	int player_status = PLAYER_IS_OK;

	while ( player_status == PLAYER_IS_OK ) {
	  struct timespec start, end;
		clock_gettime(CLOCK_MONOTONIC_RAW, &start);

		double since_last = (start.tv_sec - last_render.tv_sec) * 1000000 + (double)(start.tv_nsec - last_render.tv_nsec) / 1000;
		double ms_since_last = (double)since_last/(double)1000;
		printf("ms since last %f\n", ms_since_last);
		if (last_render.tv_nsec == 0 || ms_since_last >= 16.5) {
			printf("fps: %f\n", 1000 / ms_since_last);
		  render(renderer, currentlevel);
		} else {
			float sleep_ms = 16.5 - ms_since_last;
			printf("not yet. Sleep for %f ms\n", sleep_ms);
			if (sleep_ms > 0) {
				usleep(sleep_ms * 1000);
			}
			continue;
		}

		clock_gettime(CLOCK_MONOTONIC_RAW, &end);

		uint64_t delta_us = (end.tv_sec - start.tv_sec) * 1000000 + (end.tv_nsec - start.tv_nsec) / 1000;
		double ms = (double)delta_us/(double)1000;
		printf("time taken %f\n", ms);

		last_render = end;
		
		SDL_Event userinput;
		int got_event = SDL_PollEvent(&userinput);

		// Player movement
		signed int x = 0;
		signed int y = 0;
		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 update the player state
		player_status = player_action(x, y, currentlevel);

		// there was no event so there is nothing else to do
		if (!got_event) {
			continue;
		}

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