-
Notifications
You must be signed in to change notification settings - Fork 0
/
Game.cpp
484 lines (417 loc) · 15 KB
/
Game.cpp
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
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
#include "Game.h"
#include <iostream>
#include <fstream>
#include <random>
#include <cmath>
const int PLAYER_SPEED = 5;
Game::Game(const std::string& config)
{
init(config);
}
void Game::init(const std::string& path)
{
// TODO: read in config file here
// use the premade PlayerConfig, EnemyConfig, BulletConfig variables
std::ifstream fin(path);
std::string configType;
int window_width, window_height, frameLimit, isFullScreen;
std::cout << "Loading configuration...\n";
while (fin >> configType)
{
if (configType == "Window")
{
fin >> window_width >> window_height >> frameLimit >> isFullScreen;
}
if (configType == "Font")
{
std::string type, fontFile;
int size, r, g, b;
fin >> fontFile >> size >> r >> g >> b;
if (!m_font.loadFromFile(fontFile))
{
std::cerr << "Could not load font\n";
exit(-1);
}
m_text.setCharacterSize(size);
std::cout << "Font loaded!\n";
}
if (configType == "Player")
{
// struct PlayerConfig { int SR, CR, FR, FG, FB, OR, OG, OB, OT, V; float S; };
fin >> m_playerConfig.SR >> m_playerConfig.CR >> m_playerConfig.S >> m_playerConfig.FR >> m_playerConfig.FG >> m_playerConfig.FB >> m_playerConfig.OR >> m_playerConfig.OG >> m_playerConfig.OB >> m_playerConfig.OT >> m_playerConfig.V;
std::cout << "Player configuration loaded!\n";
}
if (configType == "Enemy")
{
// struct EnemyConfig { int SR, CR, OR, OG, OB, OT, VMIN, VMAX, L, SI; float SMIN, SMAX; };
fin >> m_enemyConfig.SR >> m_enemyConfig.CR >> m_enemyConfig.SMIN >> m_enemyConfig.SMAX >> m_enemyConfig.OR >> m_enemyConfig.OG >> m_enemyConfig.OB >> m_enemyConfig.OT >> m_enemyConfig.VMIN >> m_enemyConfig.VMAX >> m_enemyConfig.L >> m_enemyConfig.SI;
std::cout << "Enemy configuration loaded!\n";
}
if (configType == "Bullet")
{
// struct BulletConfig { int SR, CR, FR, FG, FB, OR, OG, OB, OT, V, L; float S; };
fin >> m_bulletConfig.SR >> m_bulletConfig.CR >> m_bulletConfig.S >> m_bulletConfig.FR >> m_bulletConfig.FG >> m_bulletConfig.FB >> m_bulletConfig.OR >> m_bulletConfig.OG >> m_bulletConfig.OB >> m_bulletConfig.OT >> m_bulletConfig.V >> m_bulletConfig.L;
std::cout << "Bullet configuration loaded!\n";
}
}
// set up default window parameters
m_window.create(sf::VideoMode(window_width, window_height), "Assignment 2");
m_window.setFramerateLimit(frameLimit);
m_window.setVerticalSyncEnabled(true);
spawnPlayer();
}
void Game::run()
{
// TODO: add pause functionality in here
// some systems should function while paused (rendering)
// some systems shouldn't (movement / input)
while (m_running)
{
m_entities.update();
sUserInput();
sRender();
if (!m_paused)
{
sCollision();
sMovement();
sEnemySpawner();
sLifespan();
sScore();
m_currentFrame++;
}
// increment the current frame
// may need to be moved when pause implemented
}
}
void Game::setPaused()
{
m_paused = !m_paused;
}
void Game::sScore()
{
auto scoreText = "Score: " + std::to_string(m_score);
m_text = sf::Text(scoreText, m_font);
m_text.setPosition(10, 10);
}
// respawn the player in the middle of the screen
void Game::spawnPlayer()
{
// TODO: Finish adding all properties of the player with the correct values from the config
// we create every entity by calling EntityManger.addEntity(tag)
// This returns a std::shared_ptr<Entity>, so we use 'auto' to save typing
auto entity = m_entities.addEntity("player");
// Give this entity a Transform so it spawns at (200, 200) with velocity (1,1) and angle 0
entity->cTransform = std::make_shared<CTransform>(Vec2(200.0f, 200.0f), Vec2(1.0f, 1.0f), 0.0f);
// The entity's shape will have radius 32, 8 sides, dark grey fill, and red outline of thickness 4
entity->cShape = std::make_shared<CShape>(m_playerConfig.SR, m_playerConfig.V, sf::Color(m_playerConfig.FR, m_playerConfig.FG, m_playerConfig.FB), sf::Color(m_playerConfig.OR, m_playerConfig.OG, m_playerConfig.OB), m_playerConfig.OT);
// Add an input component to the plaer so that we can use inputs
entity->cInput = std::make_shared<CInput>();
// Since we want this Entity to be our player, set our Game's player variable to be this Entioty
// This goes slightly against the EntityManager paradim, but we use the player so much it's worth it
m_player = entity;
}
// spawn an enemy at a random position
void Game::spawnEnemy()
{
// TODO: make sure the enemy is spawned properly with te m_enemyConfig variables
// the enemy must be spawned completely within the bounds of the window
// exemplo
auto entity = m_entities.addEntity("enemy");
float ex = rand() % m_window.getSize().x;
float ey = rand() % m_window.getSize().y;
float targetX = rand() % m_window.getSize().x;
float targetY = rand() % m_window.getSize().y;
int red = 50 + rand() % 255;
int green = 50 + rand() % 255;
int blue = 50 + rand() % 255;
int vertices = 3 + rand() % 7;
int enemySpeed = m_enemyConfig.VMIN + rand() % (m_enemyConfig.VMAX - m_enemyConfig.VMIN);
Vec2 difference{targetX - ex, targetY - ey};
difference.normalize();
Vec2 velocity{enemySpeed * difference.x, enemySpeed * difference.y};
entity->cTransform = std::make_shared<CTransform>(Vec2(ex, ey), velocity, 0.0f);
entity->cShape = std::make_shared<CShape>(m_enemyConfig.SR, vertices, sf::Color(red, green, blue), sf::Color(255, 255, 255), m_enemyConfig.OT);
entity->cCollision = std::make_shared<CCollision>(m_enemyConfig.CR);
// record when most recent enemy was spawned
m_lastEnemySpawnTime = m_currentFrame;
}
// spawns te small enemies when a big one (input entity e) explodes
void Game::spawnSmallEnemies(std::shared_ptr<Entity> e)
{
// TODO: spawn small enemies at the location of the input enemy e
int vertices = e->cShape->circle.getPointCount();
float speed = e->cTransform->velocity.dist(e->cTransform->pos);
for(int i = 0; i < vertices; i++)
{
float angle = 360 / vertices * i;
float angleInRadians = 3.14f * angle / 180;
Vec2 velocity = Vec2(2*(float)cos(angleInRadians), 2*(float)sin(angleInRadians));
auto entity = m_entities.addEntity("smallEnemy");
entity->cTransform = std::make_shared<CTransform>(e->cTransform->pos, velocity, angle);
entity->cShape = std::make_shared<CShape>(e->cShape->circle.getRadius() / 2, vertices, e->cShape->circle.getFillColor(), e->cShape->circle.getOutlineColor(), 4.0f);
entity->cCollision = std::make_shared<CCollision>(e->cCollision->radius / 2);
entity->cLifeSpan = std::make_shared<CLifespan>(m_enemyConfig.L);
}
// when we create the smaller enemy, we have to read the values of the original enemy
// - spawn a number of small enemies equal to the verticles of the original enemy
// - set each small enemy to the same color as the original, half the size
// - small enemies are worth double points of the original enemy
}
// spawns a bullet from a given entity to a target location
void Game::spawnBullet(std::shared_ptr<Entity> entity, const Vec2& target)
{
auto bullet = m_entities.addEntity("bullet");
// The entity's shape will have radius 32, 8 sides, dark grey fill, and red outline of thickness 4
bullet->cShape = std::make_shared<CShape>(
m_bulletConfig.SR,
m_bulletConfig.V,
sf::Color(m_bulletConfig.FR, m_bulletConfig.FG, m_bulletConfig.FB),
sf::Color(m_bulletConfig.OR, m_bulletConfig.OG, m_bulletConfig.OB),
m_playerConfig.OT);
Vec2 difference{target.x - entity->cTransform->pos.x, target.y - entity->cTransform->pos.y};
difference.normalize();
Vec2 velocity{m_bulletConfig.S * difference.x, m_bulletConfig.S * difference.y};
bullet->cTransform = std::make_shared<CTransform>(entity->cTransform->pos, velocity, 0);
bullet->cCollision = std::make_shared<CCollision>(m_bulletConfig.CR);
bullet->cLifeSpan = std::make_shared<CLifespan>(m_bulletConfig.L);
}
void Game::spawnSpecialWeapon(std::shared_ptr<Entity> entity)
{
for(int i = 0; i <= 360; i++) {
spawnBullet(entity, Vec2(entity->cTransform->pos.x + 10 * cos(i), entity->cTransform->pos.y + 10 * sin(i)));
}
m_specialWeaponCharge = 0;
}
void Game::sMovement()
{
// TODO: implement all entity movement in this function
// you should read the m_player->cInput component to determine if the player is moving
m_player->cTransform->velocity = {0, 0};
// implement player movement
float playerRadius = m_player->cShape->circle.getRadius();
if (m_player->cInput->up && (m_player->cTransform->pos.y - playerRadius) > PLAYER_SPEED)
{
m_player->cTransform->velocity.y = -PLAYER_SPEED;
}
if (m_player->cInput->down && (m_player->cTransform->pos.y + playerRadius) < m_window.getSize().y - PLAYER_SPEED)
{
m_player->cTransform->velocity.y = PLAYER_SPEED;
}
if (m_player->cInput->left && (m_player->cTransform->pos.x - playerRadius) > PLAYER_SPEED)
{
m_player->cTransform->velocity.x = -PLAYER_SPEED;
}
if (m_player->cInput->right && (m_player->cTransform->pos.x + playerRadius) < m_window.getSize().x - PLAYER_SPEED)
{
m_player->cTransform->velocity.x = PLAYER_SPEED;
}
for (auto e : m_entities.getEntities())
{
if (e->tag() == "player")
{
m_player->cTransform->pos += m_player->cTransform->velocity;
e->cTransform->angle += 2.0f;
e->cShape->circle.setRotation(e->cTransform->angle);
}
else if (e->cTransform)
{
e->cTransform->pos += e->cTransform->velocity;
e->cTransform->angle += 2.0f;
e->cShape->circle.setRotation(e->cTransform->angle);
}
}
}
void Game::sLifespan()
{
// TODO: implement all lifespan functionality
//
// for all entityes
// if entity has no lifespan component, skip it
// if entity has > 0 remaining lifespan, subtract 1
// if it has lifespan and is alive
// scale its alpha channel properly
// if it has lifespan and its time is up
// destroy the entity
for(auto e : m_entities.getEntities())
{
if(e->cLifeSpan)
{
if(e->cLifeSpan->remaining > 0)
{
e->cLifeSpan->remaining--;
}
if(e->cLifeSpan->remaining > 0)
{
auto color = sf::Color(e->cShape->circle.getFillColor().r, e->cShape->circle.getFillColor().g, e->cShape->circle.getFillColor().b, 255 * e->cLifeSpan->remaining / m_enemyConfig.L);
e->cShape->circle.setFillColor(color);
e->cShape->circle.setOutlineColor(color);
}
if(e->cLifeSpan->remaining == 0)
{
e->destroy();
}
}
}
}
void Game::sCollision()
{
for (auto b : m_entities.getEntities("bullet"))
{
for (auto e : m_entities.getEntities("enemy"))
{
float dist;
dist = b->cTransform->pos.dist(e->cTransform->pos);
if (dist < e->cCollision->radius + b->cCollision->radius)
{
b->destroy();
e->destroy();
m_score ++;
if(m_specialWeaponCharge < 100)
{
m_specialWeaponCharge ++;
}
spawnSmallEnemies(e);
}
}
for (auto e : m_entities.getEntities("smallEnemy"))
{
float dist;
dist = b->cTransform->pos.dist(e->cTransform->pos);
if (dist < e->cCollision->radius + b->cCollision->radius)
{
b->destroy();
e->destroy();
m_score += 2;
m_specialWeaponCharge += 2;
}
}
if(b->cTransform->pos.x < 0 || b->cTransform->pos.x > m_window.getSize().x || b->cTransform->pos.y < 0 || b->cTransform->pos.y > m_window.getSize().y)
{
b->destroy();
}
}
}
void Game::sEnemySpawner()
{
if (m_currentFrame - m_lastEnemySpawnTime > 90 && m_entities.getEntities("enemy").size() < 10)
{
spawnEnemy();
}
}
void Game::sRender()
{
m_window.clear();
// set the position of the shape based on the entity's transform->pos
m_player->cShape->circle.setPosition(m_player->cTransform->pos.x, m_player->cTransform->pos.y);
// set the rotation of the shape based on the entity's transform->angle
m_player->cTransform->angle += 1.0f;
m_player->cShape->circle.setRotation(m_player->cTransform->angle);
// draw the entity's sf::CircleShape
m_window.draw(m_player->cShape->circle);
m_window.draw(m_text);
for (auto e : m_entities.getEntities())
{
e->cShape->circle.setRotation(e->cTransform->angle);
float xPos, yPos;
xPos = e->cTransform->pos.x + e->cTransform->velocity.x;
yPos = e->cTransform->pos.y + e->cTransform->velocity.y;
if(e->tag() == "enemy" || e->tag() == "smallEnemy")
{
if (xPos < 0 || xPos > m_window.getSize().x)
{
e->cTransform->velocity.x *= -1;
}
if (yPos < 0 || yPos > m_window.getSize().y)
{
e->cTransform->velocity.y *= -1;
}
}
e->cShape->circle.setPosition(xPos, yPos);
m_window.draw(e->cShape->circle);
}
for (auto e : m_entities.getEntities())
{
e->cShape->circle.setPosition(e->cTransform->pos.x, e->cTransform->pos.y);
e->cTransform->angle += 1.0f;
e->cShape->circle.setRotation(e->cTransform->angle);
m_window.draw(e->cShape->circle);
}
m_window.display();
}
void Game::sUserInput()
{
// TODO: handle user input here
// note that you should only be setting the player's input component variables here
// you should not implement the player's movement logic here
// the movement system will read the variables you set in this function
sf::Event event;
while (m_window.pollEvent(event))
{
// this event triggers when te window is closed
if (event.type == sf::Event::Closed)
{
m_running = false;
}
// this event is triggered when a key is pressed
if (event.type == sf::Event::KeyPressed)
{
switch (event.key.code)
{
case sf::Keyboard::W:
m_player->cInput->up = true;
break;
case sf::Keyboard::S:
m_player->cInput->down = true;
break;
case sf::Keyboard::D:
m_player->cInput->right = true;
break;
case sf::Keyboard::A:
m_player->cInput->left = true;
break;
case sf::Keyboard::P:
setPaused();
break;
case sf::Keyboard::Space:
spawnBullet(m_player, Vec2(sf::Mouse::getPosition(m_window).x, sf::Mouse::getPosition(m_window).y));
default:
break;
}
}
// this event is triggered when a key is released
if (event.type == sf::Event::KeyReleased)
{
switch (event.key.code)
{
case sf::Keyboard::W:
m_player->cInput->up = false;
break;
case sf::Keyboard::S:
m_player->cInput->down = false;
break;
case sf::Keyboard::D:
m_player->cInput->right = false;
break;
case sf::Keyboard::A:
m_player->cInput->left = false;
break;
case sf::Keyboard::X:
if(m_specialWeaponCharge >= 100) {
spawnSpecialWeapon(m_player);
}
break;
case sf::Keyboard::Space:
spawnBullet(m_player, Vec2(sf::Mouse::getPosition(m_window).x, sf::Mouse::getPosition(m_window).y));
default:
break;
}
}
if (event.type == sf::Event::MouseButtonPressed)
{
if (event.mouseButton.button == sf::Mouse::Left)
{
spawnBullet(m_player, Vec2(event.mouseButton.x, event.mouseButton.y));
}
}
}
}