| andrewm@10 | 1 /* | 
| andrewm@10 | 2  * game.cpp | 
| andrewm@10 | 3  * | 
| andrewm@10 | 4  *  Created on: Nov 10, 2014 | 
| andrewm@10 | 5  *      Author: parallels | 
| andrewm@10 | 6  */ | 
| andrewm@10 | 7 | 
| andrewm@10 | 8 #include <cmath> | 
| andrewm@10 | 9 #include <cstdlib> | 
| andrewm@10 | 10 #include "vector_graphics.h" | 
| andrewm@10 | 11 #include "../../include/Utilities.h" | 
| andrewm@10 | 12 | 
| andrewm@10 | 13 // Virtual screen size | 
| andrewm@10 | 14 int screenWidth, screenHeight; | 
| andrewm@10 | 15 | 
| andrewm@10 | 16 // Basic information on the terrain and the tanks | 
| andrewm@10 | 17 float *groundLevel;  // Y coordinate of the ground for each X coordinate | 
| andrewm@10 | 18 float tank1X, tank1Y, tank2X, tank2Y; // Positions of the two tanks | 
| andrewm@10 | 19 float tankRadius = 20;  // Radius of the tanks | 
| andrewm@10 | 20 float cannonLength = 40;   // How long the cannon on each tank extends | 
| andrewm@10 | 21 float gravity = 0.05;      // Strength of gravity | 
| andrewm@10 | 22 | 
| andrewm@10 | 23 // Current state of the game | 
| andrewm@10 | 24 int playerHasWon = 0; // 1 if player 1 wins, 2 if player 2 wins, 0 if game in progress | 
| andrewm@10 | 25 bool player1Turn = true;  // true if it's player 1's turn; false otherwise | 
| andrewm@10 | 26 float tank1CannonAngle = M_PI/2; | 
| andrewm@10 | 27 float tank2CannonAngle = M_PI/2; // Direction the tank cannons are pointing | 
| andrewm@10 | 28 float tank1CannonStrength = 3; | 
| andrewm@10 | 29 float tank2CannonStrength = 3; // Strength of intended projectile launch | 
| andrewm@10 | 30 | 
| andrewm@10 | 31 // Location of the projectile | 
| andrewm@10 | 32 bool projectileInMotion = false; | 
| andrewm@10 | 33 float projectilePositionX, projectilePositionY; | 
| andrewm@10 | 34 float projectileVelocityX, projectileVelocityY; | 
| andrewm@10 | 35 | 
| andrewm@22 | 36 // Infor needed for sound rendering | 
| andrewm@22 | 37 bool collisionJustOccurred = false; | 
| andrewm@22 | 38 | 
| andrewm@10 | 39 // Useful utility function for generating random floating-point values | 
| andrewm@10 | 40 float randomFloat(float low, float hi) | 
| andrewm@10 | 41 { | 
| andrewm@10 | 42 	float r = (float)random() / (float)RAND_MAX; | 
| andrewm@10 | 43 	return map(r, 0, 1, low, hi); | 
| andrewm@10 | 44 } | 
| andrewm@10 | 45 | 
| andrewm@10 | 46 // Restart the game, without reallocating memory | 
| andrewm@10 | 47 void restartGame() | 
| andrewm@10 | 48 { | 
| andrewm@10 | 49 	float player1Height = randomFloat(screenHeight/2, screenHeight-5); | 
| andrewm@10 | 50 	float player2Height = randomFloat(screenHeight/2, screenHeight-5); | 
| andrewm@10 | 51 	for(int i = 0; i < screenWidth * 0.2; i++) { | 
| andrewm@10 | 52 		groundLevel[i] = player1Height; | 
| andrewm@10 | 53 	} | 
| andrewm@10 | 54 	for(int i = screenWidth * 0.2; i < screenWidth * 0.8; i++) { | 
| andrewm@10 | 55 		groundLevel[i] = player1Height + (player2Height - player1Height) * (i - screenWidth*0.2)/(screenWidth*0.6); | 
| andrewm@10 | 56 	} | 
| andrewm@10 | 57 	for(int i = screenWidth * 0.8; i < screenWidth; i++) { | 
| andrewm@10 | 58 		groundLevel[i] = player2Height; | 
| andrewm@10 | 59 	} | 
| andrewm@10 | 60 | 
| andrewm@10 | 61 	// Set the location of the two tanks so they rest on the ground at opposite sides | 
| andrewm@10 | 62 	tank1X = screenWidth * 0.1; | 
| andrewm@10 | 63 	tank1Y = player1Height; | 
| andrewm@10 | 64 	tank2X = screenWidth * 0.9; | 
| andrewm@10 | 65 	tank2Y = player2Height; | 
| andrewm@10 | 66 | 
| andrewm@10 | 67 	playerHasWon = 0; | 
| andrewm@10 | 68 	projectileInMotion = false; | 
| andrewm@10 | 69 } | 
| andrewm@10 | 70 | 
| andrewm@10 | 71 // Initialise the game | 
| andrewm@10 | 72 void setupGame(int width, int height) | 
| andrewm@10 | 73 { | 
| andrewm@10 | 74 	// Set the screen size | 
| andrewm@10 | 75 	screenWidth = width; | 
| andrewm@10 | 76 	screenHeight = height; | 
| andrewm@10 | 77 | 
| andrewm@10 | 78 	// Initialize the ground level | 
| andrewm@10 | 79 	groundLevel = new float[screenWidth]; | 
| andrewm@10 | 80 | 
| andrewm@10 | 81 	restartGame(); | 
| andrewm@10 | 82 } | 
| andrewm@10 | 83 | 
| andrewm@10 | 84 // Advance the turn to the next player | 
| andrewm@10 | 85 void nextPlayersTurn() { | 
| andrewm@10 | 86 	player1Turn = !player1Turn; | 
| andrewm@10 | 87 } | 
| andrewm@10 | 88 | 
| andrewm@10 | 89 | 
| andrewm@10 | 90 // Move forward one frame on the game physics | 
| andrewm@10 | 91 void nextGameFrame() | 
| andrewm@10 | 92 { | 
| andrewm@10 | 93 	if(!projectileInMotion) | 
| andrewm@10 | 94 		return; | 
| andrewm@10 | 95 | 
| andrewm@10 | 96 	// Update position of projectile | 
| andrewm@10 | 97 	projectilePositionX += projectileVelocityX; | 
| andrewm@10 | 98 	projectilePositionY += projectileVelocityY; | 
| andrewm@10 | 99 	projectileVelocityY += gravity; | 
| andrewm@10 | 100 | 
| andrewm@10 | 101 	// Check collision with tanks first: a collision with tank 1 means player 2 wins and vice-versa | 
| andrewm@10 | 102 	if((tank1X - projectilePositionX)*(tank1X - projectilePositionX) + | 
| andrewm@10 | 103 		(tank1Y - projectilePositionY)*(tank1Y - projectilePositionY) | 
| andrewm@10 | 104 		<= tankRadius * tankRadius) | 
| andrewm@10 | 105 	{ | 
| andrewm@10 | 106 		projectileInMotion = false; | 
| andrewm@22 | 107 		collisionJustOccurred = true; | 
| andrewm@10 | 108 		playerHasWon = 2; | 
| andrewm@10 | 109 	} | 
| andrewm@10 | 110 	else if((tank2X - projectilePositionX)*(tank2X - projectilePositionX) + | 
| andrewm@10 | 111 		(tank2Y - projectilePositionY)*(tank2Y - projectilePositionY) | 
| andrewm@10 | 112 		<= tankRadius * tankRadius) | 
| andrewm@10 | 113 	{ | 
| andrewm@10 | 114 		projectileInMotion = false; | 
| andrewm@22 | 115 		collisionJustOccurred = true; | 
| andrewm@10 | 116 		playerHasWon = 1; | 
| andrewm@10 | 117 	} | 
| andrewm@10 | 118 	else if(projectilePositionX < 0 || projectilePositionX >= screenWidth) { | 
| andrewm@10 | 119 		// Check collision whether projectile has exited the screen to the left or right | 
| andrewm@10 | 120 		projectileInMotion = false; | 
| andrewm@22 | 121 		collisionJustOccurred = true; | 
| andrewm@10 | 122 		nextPlayersTurn(); | 
| andrewm@10 | 123 	} | 
| andrewm@10 | 124 	else if(projectilePositionY >= groundLevel[(int)floorf(projectilePositionX)]) { | 
| andrewm@10 | 125 		// Check for projectile collision with ground | 
| andrewm@10 | 126 		projectileInMotion = false; | 
| andrewm@22 | 127 		collisionJustOccurred = true; | 
| andrewm@10 | 128 		nextPlayersTurn(); | 
| andrewm@10 | 129 	} | 
| andrewm@10 | 130 } | 
| andrewm@10 | 131 | 
| andrewm@10 | 132 // Updates for game state | 
| andrewm@10 | 133 void setTank1CannonAngle(float angle) | 
| andrewm@10 | 134 { | 
| andrewm@10 | 135 	tank1CannonAngle = angle; | 
| andrewm@10 | 136 } | 
| andrewm@10 | 137 | 
| andrewm@10 | 138 void setTank2CannonAngle(float angle) | 
| andrewm@10 | 139 { | 
| andrewm@10 | 140 	tank2CannonAngle = angle; | 
| andrewm@10 | 141 } | 
| andrewm@10 | 142 | 
| andrewm@10 | 143 void setTank1CannonStrength(float strength) | 
| andrewm@10 | 144 { | 
| andrewm@10 | 145 	tank1CannonStrength = strength; | 
| andrewm@10 | 146 } | 
| andrewm@10 | 147 | 
| andrewm@10 | 148 void setTank2CannonStrength(float strength) | 
| andrewm@10 | 149 { | 
| andrewm@10 | 150 	tank2CannonStrength = strength; | 
| andrewm@10 | 151 } | 
| andrewm@10 | 152 | 
| andrewm@10 | 153 // FIRE! | 
| andrewm@10 | 154 void fireProjectile() | 
| andrewm@10 | 155 { | 
| andrewm@10 | 156 	// Can't fire while projectile is already moving, or if someone has won | 
| andrewm@10 | 157 	if(projectileInMotion) | 
| andrewm@10 | 158 		return; | 
| andrewm@10 | 159 	if(playerHasWon != 0) | 
| andrewm@10 | 160 		return; | 
| andrewm@10 | 161 | 
| andrewm@10 | 162     if(player1Turn) { | 
| andrewm@10 | 163 		projectilePositionX = tank1X + cannonLength * cosf(tank1CannonAngle); | 
| andrewm@10 | 164 		projectilePositionY = tank1Y - cannonLength * sinf(tank1CannonAngle); | 
| andrewm@10 | 165 		projectileVelocityX = tank1CannonStrength * cosf(tank1CannonAngle); | 
| andrewm@10 | 166 		projectileVelocityY = -tank1CannonStrength * sinf(tank1CannonAngle); | 
| andrewm@10 | 167     } | 
| andrewm@10 | 168     else { | 
| andrewm@10 | 169 		projectilePositionX = tank2X + cannonLength * cosf(tank2CannonAngle); | 
| andrewm@10 | 170 		projectilePositionY = tank2Y - cannonLength * sinf(tank2CannonAngle); | 
| andrewm@10 | 171 		projectileVelocityX = tank2CannonStrength * cosf(tank2CannonAngle); | 
| andrewm@10 | 172 		projectileVelocityY = -tank2CannonStrength * sinf(tank2CannonAngle); | 
| andrewm@10 | 173     } | 
| andrewm@10 | 174 | 
| andrewm@10 | 175     // GO! | 
| andrewm@10 | 176     projectileInMotion = true; | 
| andrewm@10 | 177 } | 
| andrewm@10 | 178 | 
| andrewm@10 | 179 // Game state queries | 
| andrewm@10 | 180 bool gameStatusPlayer1Turn() | 
| andrewm@10 | 181 { | 
| andrewm@10 | 182 	return player1Turn; | 
| andrewm@10 | 183 } | 
| andrewm@10 | 184 | 
| andrewm@10 | 185 bool gameStatusProjectileInMotion() | 
| andrewm@10 | 186 { | 
| andrewm@10 | 187 	return projectileInMotion; | 
| andrewm@10 | 188 } | 
| andrewm@10 | 189 | 
| andrewm@10 | 190 int gameStatusWinner() | 
| andrewm@10 | 191 { | 
| andrewm@10 | 192 	return playerHasWon; | 
| andrewm@10 | 193 } | 
| andrewm@10 | 194 | 
| andrewm@22 | 195 bool gameStatusCollisionOccurred() | 
| andrewm@22 | 196 { | 
| andrewm@22 | 197 	if(collisionJustOccurred) { | 
| andrewm@22 | 198 		collisionJustOccurred = false; | 
| andrewm@22 | 199 		return true; | 
| andrewm@22 | 200 	} | 
| andrewm@22 | 201 	return false; | 
| andrewm@22 | 202 } | 
| andrewm@22 | 203 | 
| andrewm@22 | 204 float gameStatusProjectileHeight() | 
| andrewm@22 | 205 { | 
| andrewm@22 | 206 	return projectilePositionY / (float)screenHeight; | 
| andrewm@22 | 207 } | 
| andrewm@22 | 208 | 
| andrewm@10 | 209 // Clean up any allocated memory for the game | 
| andrewm@10 | 210 void cleanupGame() | 
| andrewm@10 | 211 { | 
| andrewm@10 | 212 	delete groundLevel; | 
| andrewm@10 | 213 } | 
| andrewm@10 | 214 | 
| andrewm@10 | 215 // Drawing routines. Arguments are (interleaved) buffer to render | 
| andrewm@10 | 216 // into, the available size, and the target for how many samples | 
| andrewm@10 | 217 // to use (actual usage might vary slightly). Regardless of | 
| andrewm@10 | 218 // lengthTarget, never use more than bufferSize samples. | 
| andrewm@10 | 219 | 
| andrewm@10 | 220 int drawGround(float *buffer, int bufferSize, int framesTarget) | 
| andrewm@10 | 221 { | 
| andrewm@10 | 222 	int length; | 
| andrewm@10 | 223 | 
| andrewm@10 | 224 	// Calculate total length of ground line, to arrive at a speed calculation | 
| andrewm@10 | 225 	float totalLineLength = 0.4f*screenWidth | 
| andrewm@10 | 226 							+ sqrtf(0.36f*screenWidth*screenWidth | 
| andrewm@10 | 227 									+ (tank2Y-tank1Y)*(tank2Y-tank1Y)); | 
| andrewm@10 | 228 | 
| andrewm@10 | 229 	// Speed is calculated in pixels per frame | 
| andrewm@10 | 230 	float speed = totalLineLength / (float)framesTarget; | 
| andrewm@10 | 231 | 
| andrewm@10 | 232 	// Draw three lines: platforms for tanks and the connecting line. | 
| andrewm@10 | 233 	// Eventually, render a more complex ground from the array. | 
| andrewm@10 | 234 	length = renderLine(0, tank1Y, screenWidth * 0.2, tank1Y, | 
| andrewm@10 | 235 						speed, buffer, bufferSize); | 
| andrewm@10 | 236 	length += renderLine(screenWidth * 0.2, tank1Y, screenWidth * 0.8, tank2Y, | 
| andrewm@10 | 237 						speed, &buffer[length], bufferSize - length); | 
| andrewm@10 | 238 	length += renderLine(screenWidth * 0.8, tank2Y, screenWidth, tank2Y, | 
| andrewm@10 | 239 						speed, &buffer[length], bufferSize - length); | 
| andrewm@10 | 240 | 
| andrewm@10 | 241 	return length; | 
| andrewm@10 | 242 } | 
| andrewm@10 | 243 | 
| andrewm@10 | 244 int drawTanks(float *buffer, int bufferSize, int framesTarget) | 
| andrewm@10 | 245 { | 
| andrewm@10 | 246 	int length = 0; | 
| andrewm@10 | 247 | 
| andrewm@10 | 248 	// Calculate total length of tank lines, to arrive at a speed calculation | 
| andrewm@10 | 249 	float totalLineLength = 2.0*M_PI*tankRadius + 2.0*(cannonLength - tankRadius); | 
| andrewm@10 | 250 | 
| andrewm@10 | 251 	// Speed is calculated in pixels per frame | 
| andrewm@10 | 252 	float speed = totalLineLength / (float)framesTarget; | 
| andrewm@10 | 253 | 
| andrewm@10 | 254 	if(playerHasWon != 2) { | 
| andrewm@10 | 255 		// Tank 1 body = semicircle + line | 
| andrewm@10 | 256 		length += renderArc(tank1X, tank1Y, tankRadius, M_PI, 2.0 * M_PI, | 
| andrewm@10 | 257 							speed, buffer, bufferSize); | 
| andrewm@10 | 258 		length += renderLine(tank1X + tankRadius, tank1Y, | 
| andrewm@10 | 259 							 tank1X - tankRadius, tank1Y, | 
| andrewm@10 | 260 							speed, &buffer[length], bufferSize - length); | 
| andrewm@10 | 261 		// Tank 1 cannon (line depending on angle) | 
| andrewm@10 | 262 		length += renderLine(tank1X + tankRadius * cosf(tank1CannonAngle), | 
| andrewm@10 | 263 			 tank1Y - tankRadius * sinf(tank1CannonAngle), | 
| andrewm@10 | 264 			 tank1X + cannonLength * cosf(tank1CannonAngle), | 
| andrewm@10 | 265 			 tank1Y - cannonLength * sinf(tank1CannonAngle), | 
| andrewm@10 | 266 			 speed, &buffer[length], bufferSize - length); | 
| andrewm@10 | 267 	} | 
| andrewm@10 | 268 | 
| andrewm@10 | 269 	if(playerHasWon != 1) { | 
| andrewm@10 | 270 		// Same idea for tank 2 | 
| andrewm@10 | 271 		length += renderArc(tank2X, tank2Y, tankRadius, M_PI, 2.0 * M_PI, | 
| andrewm@10 | 272 							speed, &buffer[length], bufferSize - length); | 
| andrewm@10 | 273 		length += renderLine(tank2X + tankRadius, tank2Y, | 
| andrewm@10 | 274 							 tank2X - tankRadius, tank2Y, | 
| andrewm@10 | 275 							 speed, &buffer[length], bufferSize - length); | 
| andrewm@10 | 276 		length += renderLine(tank2X + tankRadius * cosf(tank2CannonAngle), | 
| andrewm@10 | 277 			 tank2Y - tankRadius * sinf(tank2CannonAngle), | 
| andrewm@10 | 278 			 tank2X + cannonLength * cosf(tank2CannonAngle), | 
| andrewm@10 | 279 			 tank2Y - cannonLength * sinf(tank2CannonAngle), | 
| andrewm@10 | 280 			 speed, &buffer[length], bufferSize - length); | 
| andrewm@10 | 281 	} | 
| andrewm@10 | 282 | 
| andrewm@10 | 283 	return length; | 
| andrewm@10 | 284 } | 
| andrewm@10 | 285 | 
| andrewm@10 | 286 int drawProjectile(float *buffer, int bufferSize, int framesTarget) | 
| andrewm@10 | 287 { | 
| andrewm@10 | 288 	if(!projectileInMotion) | 
| andrewm@10 | 289 		return 0; | 
| andrewm@10 | 290 | 
| andrewm@10 | 291 	// Draw a point for a specified number of frames (each containing X and Y) | 
| andrewm@10 | 292 	// Return the number of items used in the buffer, which will be twice | 
| andrewm@10 | 293 	// the number of frames unless the buffer is full | 
| andrewm@10 | 294 | 
| andrewm@10 | 295 	if(bufferSize/2 < framesTarget) { | 
| andrewm@10 | 296 		renderPoint(projectilePositionX, projectilePositionY, buffer, bufferSize/2); | 
| andrewm@10 | 297 		return bufferSize; | 
| andrewm@10 | 298 	} | 
| andrewm@10 | 299 	else { | 
| andrewm@10 | 300 		renderPoint(projectilePositionX, projectilePositionY, buffer, framesTarget); | 
| andrewm@10 | 301 		return framesTarget*2; | 
| andrewm@10 | 302 	} | 
| andrewm@10 | 303 } | 
| andrewm@10 | 304 | 
| andrewm@10 | 305 // Main drawing routine entry point | 
| andrewm@10 | 306 int drawGame(float *buffer, int bufferSize) | 
| andrewm@10 | 307 { | 
| andrewm@10 | 308 	int length; | 
| andrewm@10 | 309 | 
| andrewm@10 | 310 	// Based on buffer size, come up with speeds for each of the elements | 
| andrewm@10 | 311 	// 50% of time to ground; 30% to the tanks and 20% to the projectile | 
| andrewm@10 | 312 	// Give a margin of 25% beyond so we don't run out of buffer space | 
| andrewm@10 | 313 	// if things take longer to draw than we guess they will | 
| andrewm@10 | 314 	const float amountToUse = 0.375; // 0.75/2 because two samples per frame | 
| andrewm@10 | 315 	const float groundFraction = 0.5 * amountToUse; | 
| andrewm@10 | 316 	const float tankFraction = 0.3 * amountToUse; | 
| andrewm@10 | 317 	const float projectileFraction = 0.2 * amountToUse; | 
| andrewm@10 | 318 | 
| andrewm@10 | 319 	length = drawGround(buffer, bufferSize, bufferSize * groundFraction); | 
| andrewm@10 | 320 	length += drawTanks(&buffer[length], bufferSize - length, | 
| andrewm@10 | 321 						bufferSize * tankFraction); | 
| andrewm@10 | 322 	length += drawProjectile(&buffer[length], bufferSize - length, | 
| andrewm@10 | 323 						bufferSize * projectileFraction); | 
| andrewm@10 | 324 | 
| andrewm@10 | 325 	return length; | 
| andrewm@10 | 326 } |