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