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