This commit is contained in:
bMorgan01 2022-03-08 10:18:34 -07:00
parent 54f5313273
commit b49080f675
83 changed files with 2083 additions and 0 deletions

8
.gitignore vendored
View file

@ -30,3 +30,11 @@
*.exe
*.out
*.app
# Notepad Backups
*.bak
# Clion
.idea
cmake-build-release
CMakeLists.txt

33
Button.cpp Normal file
View file

@ -0,0 +1,33 @@
//
// Created by Benjamin on 1/25/2022.
//
#include "Button.h"
Button::Button(const sf::Texture &texture, const std::function<void()> &command) : Sprite(texture) {
this->command = command;
}
Button::Button(const sf::Texture &texture, float x, float y, float scale, int origin, const std::function<void()> &command) : Sprite(texture, x, y, scale, origin) {
this->command = command;
}
void Button::click() {
command();
}
void Button::hover() {
Sprite::hover();
sf::Vector2f pos = getPosition();
setScale(getScale().x + 0.1, getScale().x + 0.1);
setPosition(pos.x, pos.y);
}
void Button::unHover() {
Sprite::unHover();
sf::Vector2f pos = getPosition();
setScale(getScale().x - 0.1, getScale().x - 0.1);
setPosition(pos.x, pos.y);
}

24
Button.h Normal file
View file

@ -0,0 +1,24 @@
//
// Created by Benjamin on 1/25/2022.
//
#ifndef SFML_TEMPLATE_BUTTON_H
#define SFML_TEMPLATE_BUTTON_H
#include <functional>
#include "Sprite.h"
class Button : public Sprite {
public:
explicit Button(const sf::Texture &, const std::function<void()> &);
Button(const sf::Texture &, float, float, float, int, const std::function<void()> &);
void click() override;
void hover() override;
void unHover() override;
protected:
std::function<void()> command;
};
#endif //SFML_TEMPLATE_BUTTON_H

19
Canopy.cpp Normal file
View file

@ -0,0 +1,19 @@
//
// Created by Benjamin on 1/30/2022.
//
#include "Canopy.h"
Canopy::Canopy(const sf::Texture &texture, float x, float y, float angle, float scale, int origin) : Sprite(texture, x, y, angle, scale, origin) {}
void Canopy::hover() {
Sprite::hover();
setColor(sf::Color(255, 255, 255, 128));
}
void Canopy::unHover() {
Sprite::unHover();
setColor(sf::Color(255, 255, 255, 255));
}

20
Canopy.h Normal file
View file

@ -0,0 +1,20 @@
//
// Created by Benjamin on 1/30/2022.
//
#ifndef SFML_TEMPLATE_CANOPY_H
#define SFML_TEMPLATE_CANOPY_H
#include "Sprite.h"
class Canopy : public Sprite {
public:
Canopy(const sf::Texture &, float, float, float, float, int);
void hover() override;
void unHover() override;
};
#endif //SFML_TEMPLATE_CANOPY_H

190
Collision.cpp Normal file
View file

@ -0,0 +1,190 @@
/*
* File: collision.cpp
* Author: Nick (original version), ahnonay (SFML2 compatibility)
*/
#include <map>
#include "Collision.h"
namespace Collision
{
class BitmaskManager
{
public:
~BitmaskManager() {
std::map<const sf::Texture*, sf::Uint8*>::const_iterator end = Bitmasks.end();
for (std::map<const sf::Texture*, sf::Uint8*>::const_iterator iter = Bitmasks.begin(); iter!=end; iter++)
delete [] iter->second;
}
sf::Uint8 GetPixel (const sf::Uint8* mask, const sf::Texture* tex, unsigned int x, unsigned int y) {
if (x>tex->getSize().x||y>tex->getSize().y)
return 0;
return mask[x+y*tex->getSize().x];
}
sf::Uint8* GetMask (const sf::Texture* tex) {
sf::Uint8* mask;
std::map<const sf::Texture*, sf::Uint8*>::iterator pair = Bitmasks.find(tex);
if (pair==Bitmasks.end())
{
sf::Image img = tex->copyToImage();
mask = CreateMask (tex, img);
}
else
mask = pair->second;
return mask;
}
sf::Uint8* CreateMask (const sf::Texture* tex, const sf::Image& img) {
sf::Uint8* mask = new sf::Uint8[tex->getSize().y*tex->getSize().x];
for (unsigned int y = 0; y<tex->getSize().y; y++)
{
for (unsigned int x = 0; x<tex->getSize().x; x++)
mask[x+y*tex->getSize().x] = img.getPixel(x,y).a;
}
Bitmasks.insert(std::pair<const sf::Texture*, sf::Uint8*>(tex,mask));
return mask;
}
private:
std::map<const sf::Texture*, sf::Uint8*> Bitmasks;
};
BitmaskManager Bitmasks;
bool PixelPerfectTest(const sf::Sprite& Object1, const sf::Sprite& Object2, sf::Uint8 AlphaLimit) {
sf::FloatRect Intersection;
if (Object1.getGlobalBounds().intersects(Object2.getGlobalBounds(), Intersection)) {
sf::IntRect O1SubRect = Object1.getTextureRect();
sf::IntRect O2SubRect = Object2.getTextureRect();
sf::Uint8* mask1 = Bitmasks.GetMask(Object1.getTexture());
sf::Uint8* mask2 = Bitmasks.GetMask(Object2.getTexture());
// Loop through our pixels
for (int i = Intersection.left; i < Intersection.left+Intersection.width; i++) {
for (int j = Intersection.top; j < Intersection.top+Intersection.height; j++) {
sf::Vector2f o1v = Object1.getInverseTransform().transformPoint(i, j);
sf::Vector2f o2v = Object2.getInverseTransform().transformPoint(i, j);
// Make sure pixels fall within the sprite's subrect
if (o1v.x > 0 && o1v.y > 0 && o2v.x > 0 && o2v.y > 0 &&
o1v.x < O1SubRect.width && o1v.y < O1SubRect.height &&
o2v.x < O2SubRect.width && o2v.y < O2SubRect.height) {
if (Bitmasks.GetPixel(mask1, Object1.getTexture(), (int)(o1v.x)+O1SubRect.left, (int)(o1v.y)+O1SubRect.top) > AlphaLimit &&
Bitmasks.GetPixel(mask2, Object2.getTexture(), (int)(o2v.x)+O2SubRect.left, (int)(o2v.y)+O2SubRect.top) > AlphaLimit)
return true;
}
}
}
}
return false;
}
bool CreateTextureAndBitmask(sf::Texture &LoadInto, const std::string& Filename)
{
sf::Image img;
if (!img.loadFromFile(Filename))
return false;
if (!LoadInto.loadFromImage(img))
return false;
Bitmasks.CreateMask(&LoadInto, img);
return true;
}
sf::Vector2f GetSpriteCenter (const sf::Sprite& Object)
{
sf::FloatRect AABB = Object.getGlobalBounds();
return sf::Vector2f (AABB.left+AABB.width/2.f, AABB.top+AABB.height/2.f);
}
sf::Vector2f GetSpriteSize (const sf::Sprite& Object)
{
sf::IntRect OriginalSize = Object.getTextureRect();
sf::Vector2f Scale = Object.getScale();
return sf::Vector2f (OriginalSize.width*Scale.x, OriginalSize.height*Scale.y);
}
bool CircleTest(const sf::Sprite& Object1, const sf::Sprite& Object2) {
sf::Vector2f Obj1Size = GetSpriteSize(Object1);
sf::Vector2f Obj2Size = GetSpriteSize(Object2);
float Radius1 = (Obj1Size.x + Obj1Size.y) / 4;
float Radius2 = (Obj2Size.x + Obj2Size.y) / 4;
sf::Vector2f Distance = GetSpriteCenter(Object1)-GetSpriteCenter(Object2);
return (Distance.x * Distance.x + Distance.y * Distance.y <= (Radius1 + Radius2) * (Radius1 + Radius2));
}
class OrientedBoundingBox // Used in the BoundingBoxTest
{
public:
OrientedBoundingBox (const sf::Sprite& Object) // Calculate the four points of the OBB from a transformed (scaled, rotated...) sprite
{
sf::Transform trans = Object.getTransform();
sf::IntRect local = Object.getTextureRect();
Points[0] = trans.transformPoint(0.f, 0.f);
Points[1] = trans.transformPoint(local.width, 0.f);
Points[2] = trans.transformPoint(local.width, local.height);
Points[3] = trans.transformPoint(0.f, local.height);
}
sf::Vector2f Points[4];
void ProjectOntoAxis (const sf::Vector2f& Axis, float& Min, float& Max) // Project all four points of the OBB onto the given axis and return the dotproducts of the two outermost points
{
Min = (Points[0].x*Axis.x+Points[0].y*Axis.y);
Max = Min;
for (int j = 1; j<4; j++)
{
float Projection = (Points[j].x*Axis.x+Points[j].y*Axis.y);
if (Projection<Min)
Min=Projection;
if (Projection>Max)
Max=Projection;
}
}
};
bool BoundingBoxTest(const sf::Sprite& Object1, const sf::Sprite& Object2) {
OrientedBoundingBox OBB1 (Object1);
OrientedBoundingBox OBB2 (Object2);
// Create the four distinct axes that are perpendicular to the edges of the two rectangles
sf::Vector2f Axes[4] = {
sf::Vector2f (OBB1.Points[1].x-OBB1.Points[0].x,
OBB1.Points[1].y-OBB1.Points[0].y),
sf::Vector2f (OBB1.Points[1].x-OBB1.Points[2].x,
OBB1.Points[1].y-OBB1.Points[2].y),
sf::Vector2f (OBB2.Points[0].x-OBB2.Points[3].x,
OBB2.Points[0].y-OBB2.Points[3].y),
sf::Vector2f (OBB2.Points[0].x-OBB2.Points[1].x,
OBB2.Points[0].y-OBB2.Points[1].y)
};
for (int i = 0; i<4; i++) // For each axis...
{
float MinOBB1, MaxOBB1, MinOBB2, MaxOBB2;
// ... project the points of both OBBs onto the axis ...
OBB1.ProjectOntoAxis(Axes[i], MinOBB1, MaxOBB1);
OBB2.ProjectOntoAxis(Axes[i], MinOBB2, MaxOBB2);
// ... and check whether the outermost projected points of both OBBs overlap.
// If this is not the case, the Separating Axis Theorem states that there can be no collision between the rectangles
if (!((MinOBB2<=MaxOBB1)&&(MaxOBB2>=MinOBB1)))
return false;
}
return true;
}
}

77
Collision.h Normal file
View file

@ -0,0 +1,77 @@
/*
* File: collision.h
* Authors: Nick Koirala (original version), ahnonay (SFML2 compatibility)
*
* Collision Detection and handling class
* For SFML2.
Notice from the original version:
(c) 2009 - LittleMonkey Ltd
This software is provided 'as-is', without any express or
implied warranty. In no event will the authors be held
liable for any damages arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute
it freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented;
you must not claim that you wrote the original software.
If you use this software in a product, an acknowledgment
in the product documentation would be appreciated but
is not required.
2. Altered source versions must be plainly marked as such,
and must not be misrepresented as being the original software.
3. This notice may not be removed or altered from any
source distribution.
*
* Created on 30 January 2009, 11:02
*/
#ifndef SFML_TEMPLATE_COLLISION_H
#define SFML_TEMPLATE_COLLISION_H
#include <SFML\Graphics.hpp>
namespace Collision {
//////
/// Test for a collision between two sprites by comparing the alpha values of overlapping pixels
/// Supports scaling and rotation
/// AlphaLimit: The threshold at which a pixel becomes "solid". If AlphaLimit is 127, a pixel with
/// alpha value 128 will cause a collision and a pixel with alpha value 126 will not.
///
/// This functions creates bitmasks of the textures of the two sprites by
/// downloading the textures from the graphics card to memory -> SLOW!
/// You can avoid this by using the "CreateTextureAndBitmask" function
//////
bool PixelPerfectTest(const sf::Sprite& Object1 ,const sf::Sprite& Object2, sf::Uint8 AlphaLimit = 0);
//////
/// Replaces Texture::loadFromFile
/// Load an imagefile into the given texture and create a bitmask for it
/// This is much faster than creating the bitmask for a texture on the first run of "PixelPerfectTest"
///
/// The function returns false if the file could not be opened for some reason
//////
bool CreateTextureAndBitmask(sf::Texture &LoadInto, const std::string& Filename);
//////
/// Test for collision using circle collision dection
/// Radius is averaged from the dimensions of the sprite so
/// roughly circular objects will be much more accurate
//////
bool CircleTest(const sf::Sprite& Object1, const sf::Sprite& Object2);
//////
/// Test for bounding box collision using the Separating Axis Theorem
/// Supports scaling and rotation
//////
bool BoundingBoxTest(const sf::Sprite& Object1, const sf::Sprite& Object2);
}
#endif //SFML_TEMPLATE_COLLISION_H

696
Game.cpp Normal file
View file

@ -0,0 +1,696 @@
//
// Created by Benjamin on 1/25/2022.
//
#include <cmath>
#include <dirent.h>
#include <random>
#include <fstream>
#include "Button.h"
#include "Game.h"
#include "Path.h"
#include "Obstacle.h"
#include "Canopy.h"
#include "TowerGUI.h"
#include "Collision.h"
#include "TextBox.h"
#include "VariableLabel.h"
std::vector<std::string> open(const std::string &path) {
std::vector<std::string> files;
DIR *dir;
struct dirent *ent;
if ((dir = opendir(path.c_str())) != nullptr) {
/* print all the files and directories within directory */
while ((ent = readdir(dir)) != nullptr) {
files.emplace_back(ent->d_name);
}
closedir(dir);
} else {
/* could not open directory */
perror("");
}
return files;
}
int wrap(int n, int const nLowerBound, int const nUpperBound)
{
int range_size = nUpperBound - nLowerBound + 1;
if (n < nLowerBound)
n += range_size * ((nLowerBound - n) / range_size + 1);
return nLowerBound + (n - nLowerBound) % range_size;
}
Game::Game() {
// displayGroup "Hello, World!" -- this still appears in our Run terminal as before
std::cout << "Hello, World!" << std::endl;
// create a RenderWindow object
// specify the size to be 640x640
// set the title to be "SFML Example Window"
auto mode = sf::VideoMode::getDesktopMode();
mode.height += 1;
window = new sf::RenderWindow(mode, "Tower Defense", sf::Style::None);
screenRatio = sf::Vector2f(window->getSize().x/BASE_RESOLUTION.x, window->getSize().y/BASE_RESOLUTION.y);
float fps;
sf::Clock clock;
sf::Time previousTime = clock.getElapsedTime();
sf::Time currentTime;
pixelFont.loadFromFile("./assets/fonts/FreePixel.ttf");
Label fps_label = Label("0", pixelFont, 14, 10, 10, Label::TOP_LEFT);
menu = Group(window);
play = Group(window);
bool show_fps = false;
load_tower_data();
create_menu();
// while our window is open, keep it open
// this is our draw loop
while( window->isOpen() ) {
window->clear( sf::Color::Black ); // clear the contents of the old frame
// by setting the window to black
//****************************************
// ADD ALL OF OUR DRAWING BELOW HERE
//****************************************
if (displayGroup == &menu) {
menu.update();
menu.draw();
} else if (displayGroup == &play) {
play.update();
sf::Vector2f mousePosF(static_cast<float>( sf::Mouse::getPosition().x ), static_cast<float>( sf::Mouse::getPosition().y ));
if (!mouseLeftMenu) {
if (!sidebar->getGlobalBounds().contains(mousePosF)) {
mouseLeftMenu = true;
}
} else {
sf::FloatRect windowRect(window->getPosition().x, window->getPosition().y, window->getSize().x, window->getSize().y);
if (inHand && (sidebar->getGlobalBounds().contains(mousePosF) || !windowRect.contains(mousePosF))) {
play.kill(inHand);
inHand = nullptr;
unpick_tower();
}
}
play.draw();
}
if (show_fps) {
currentTime = clock.getElapsedTime();
fps = 1.0f / (currentTime.asSeconds() - previousTime.asSeconds()); // the asSeconds returns a float
previousTime = currentTime;
fps_label.setString(std::to_string((int)floor(fps)));
window->draw(fps_label);
}
//****************************************
// ADD ALL OF OUR DRAWING ABOVE HERE
//****************************************
window->display(); // displayGroup the window
//****************************************
// HANDLE EVENTS BELOW HERE
//****************************************
sf::Event event{};
while( window->pollEvent(event) ) { // ask the window if any events occurred
if( event.type == sf::Event::Closed ) { // if event type is a closed event
// i.e. they clicked the X on the window
window->close();
} else if ( event.type == sf::Event::MouseButtonReleased ) {
sf::Vector2i mousePos = sf::Mouse::getPosition(*window);
displayGroup->do_clicked(mousePos);
} else if ( event.type == sf::Event::KeyPressed ) {
if (event.key.code == sf::Keyboard::F1 ) {
show_fps = !show_fps;
} else if ( event.key.code == sf::Keyboard::Escape ) {
if (inHand) {
play.kill(inHand);
inHand = nullptr;
unpick_tower();
}
}
}
}
}
clean_up();
}
void Game::create_menu() {
startButtonTexture.loadFromFile("./assets/textures/menu/start_button.png");
quitButtonTexture.loadFromFile("./assets/textures/menu/quit_button.png");
menu.empty();
auto *playButton = new Button(startButtonTexture, window->getSize().x/2.0, window->getSize().y/2.0, ((window->getSize().x / 10.0)/startButtonTexture.getSize().x) * screenRatio.x, Sprite::CENTER, [this] { create_play(); });
menu.add(playButton);
menu.add(new Button(quitButtonTexture, playButton->getPosition().x, playButton->getPosition().y + 1.5 * playButton->getGlobalBounds().height, playButton->getScale().x, Sprite::CENTER, [this] { end_game(); }));
displayGroup = &menu;
}
void Game::create_play() {
load_textures();
play.empty();
while(generate_path() != 0);
generate_obstacles();
show_dash();
displayGroup = &play;
}
void Game::load_textures() {
placeableDiskTexture.loadFromFile("./assets/textures/gui/placeable_disk.png");
selectArrowTextureLeft.loadFromFile("./assets/textures/gui/select_arrow_left.png");
selectArrowTextureRight.loadFromFile("./assets/textures/gui/select_arrow_right.png");
damageTexture.loadFromFile("./assets/textures/gui/damage_icon.png");
rangeTexture.loadFromFile("./assets/textures/gui/range_icon.png");
speedTexture.loadFromFile("./assets/textures/gui/attack_speed_icon.png");
pierceTexture.loadFromFile("./assets/textures/gui/pierce_icon.png");
splashDamageTexture.loadFromFile("./assets/textures/gui/splash_damage_icon.png");
splashRangeTexture.loadFromFile("./assets/textures/gui/splash_radius.png");
terrainTextures.clear();
pathTextures.clear();
towerTextures.clear();
//Load terrain textures
for (const std::string &s : open((std::string) "./assets/textures/environments/terrain")) {
if (s.find(std::to_string(environment) + "-") == std::string::npos) continue;
terrainTextures.emplace_back();
terrainTextures[terrainTextures.size() - 1].loadFromFile("./assets/textures/environments/terrain/" + s);
}
//Load path textures
for (const std::string &s : open((std::string) "./assets/textures/environments/paths")) {
if (s.find(std::to_string(environment) + "-") == std::string::npos) continue;
pathTextures.emplace_back();
pathTextures[pathTextures.size() - 1].loadFromFile("./assets/textures/environments/paths/" + s);
}
//Load obstacle textures
for (std::string &s : open((std::string) "./assets/textures/environments/obstacles")) {
if (s.find(std::to_string(environment) + "-") == std::string::npos || s.find("_top") != std::string::npos) continue;
obstacleBases.emplace_back();
obstacleBases[obstacleBases.size() - 1].loadFromFile("./assets/textures/environments/obstacles/" + s);
if (s.find("_bottom") != std::string::npos) {
obstacleHasTops.push_back(true);
obstacleTops.emplace_back();
int index;
while((index = s.find("_bottom")) != std::string::npos) {
s.replace(index, 7, "_top"); //remove and replace from that position
}
obstacleTops[obstacleTops.size() - 1].loadFromFile("./assets/textures/environments/obstacles/" + s);
} else {
obstacleHasTops.push_back(false);
}
}
//Load tower textures
for (const std::string &s : open((std::string) "./assets/textures/towers")) {
if (s == "." || s == "..") continue;
towerTextures.emplace_back();
towerTextures[towerTextures.size() - 1].loadFromFile("./assets/textures/towers/" + s);
}
}
void Game::load_tower_data() {
std::ifstream dataFile("./assets/data/towers.dat");
towerData.emplace_back();
std::string line;
while (std::getline(dataFile, line)) {
if (line.empty()) {
towerData.emplace_back();
} else {
unsigned int ind = line.find_first_of(' ');
towerData[towerData.size() - 1].insert({line.substr(0, ind), line.substr(ind + 1, line.size() - ind)});
}
}
dataFile.close();
}
void Game::generate_obstacles() {
static std::mt19937 rng( time(nullptr) );
float obstaclePerc = DIFFICULTY_OBSTACLE_PERCS[pathDifficulty]/100.0;
float baseScale = 1.5;
float obstacleScale = screenRatio.x * baseScale;
float area = 0;
while (area/(BASE_RESOLUTION.y * 4*(BASE_RESOLUTION.y/3)) < obstaclePerc) {
int ind = std::uniform_int_distribution<int>(0, obstacleBases.size() - 1)(rng);
sf::Texture t = obstacleBases[ind];
area += (t.getSize().x * t.getSize().y * baseScale * baseScale);
auto* obstacle = new Obstacle(obstacleBases[ind]);
obstacle->setOrigin(Sprite::CENTER);
obstacle->setScale(obstacleScale, obstacleScale);
bool colliding = true;
int angle, x, y;
while (colliding) {
angle = std::uniform_int_distribution<int>(0, 359)(rng);
obstacle->setRotation(angle);
x = std::uniform_int_distribution<int>(mapCorner + obstacle->getGlobalBounds().width/2.0, window->getSize().x - obstacle->getGlobalBounds().width/2.0)(rng);
y = std::uniform_int_distribution<int>(obstacle->getGlobalBounds().height/2.0, mapBottom - obstacle->getGlobalBounds().height/2.0)(rng);
obstacle->setPosition(x, y);
colliding = false;
for (auto *s : *play.getLayerSprites(PATH_LAYER)) {
if (obstacle->getGlobalBounds().intersects(s->getGlobalBounds()) && Collision::PixelPerfectTest(*obstacle, *s)) {
colliding = true;
break;
}
}
if (colliding) {
continue;
}
//TODO fix this double usage
if (play.getLayerSprites(OBSTACLE_LAYER) != nullptr) {
for (auto *s : *play.getLayerSprites(OBSTACLE_LAYER)) {
if (obstacle->getGlobalBounds().intersects(s->getGlobalBounds()) && Collision::PixelPerfectTest(*obstacle, *s)) {
colliding = true;
break;
}
}
}
}
play.add(obstacle, OBSTACLE_LAYER);
if (obstacleHasTops[ind]) {
int canopyInd = 0;
for (int i = 0; i < ind; i++) {
if (obstacleHasTops[i]) {
canopyInd++;
}
}
play.add(new Canopy(obstacleTops[canopyInd], x, y, std::uniform_int_distribution<int>(0, 359)(rng), obstacleScale, Sprite::CENTER), CANOPY_LAYER);
}
}
}
int Game::generate_path() {
static std::mt19937 rng( time(nullptr) );
int startPoint[2];
int innerStartPoint[2];
int endPoint[2];
int innerEndPoint[2];
int numWaypoints = DIFFICULTY_WAYPOINTS[pathDifficulty][std::uniform_int_distribution<int>(0, 1)(rng)];
int startSide = std::uniform_int_distribution<int>(0, 1)(rng);
if (startSide == 0) {
startPoint[0] = 0;
startPoint[1] = std::uniform_int_distribution<int>(1, 28)(rng);
innerStartPoint[0] = 1;
innerStartPoint[1] = startPoint[1];
} else {
startPoint[0] = std::uniform_int_distribution<int>(1, 38)(rng);
startPoint[1] = 0;
innerStartPoint[0] = startPoint[0];
innerStartPoint[1] = 1;
}
int endSide = std::uniform_int_distribution<int>(0, 1)(rng);
if (endSide == 0) {
endPoint[0] = 39;
endPoint[1] = std::uniform_int_distribution<int>(1, 28)(rng);
innerEndPoint[0] = 38;
innerEndPoint[1] = endPoint[1];
} else {
endPoint[0] = std::uniform_int_distribution<int>(1, 38)(rng);
endPoint[1] = 29;
innerEndPoint[0] = endPoint[0];
innerEndPoint[1] = 28;
}
waypoints = {sf::Vector2i(startPoint[0], startPoint[1]), sf::Vector2i(innerStartPoint[0], innerStartPoint[1])};
int prevWaypoint[2] = { innerStartPoint[0], innerStartPoint[1] };
for (int i = 0; i < numWaypoints; i++) {
int newWaypoint[2] = { waypoints[waypoints.size() - 1].x, waypoints[waypoints.size() - 1].y };
while (newWaypoint[0] == prevWaypoint[0] || newWaypoint[1] == prevWaypoint[1] || (i == numWaypoints - 1 && (newWaypoint[0] == innerEndPoint[0] || newWaypoint[1] == innerEndPoint[1]))) {
newWaypoint[0] = std::uniform_int_distribution<int>(1, 38)(rng);
newWaypoint[1] = std::uniform_int_distribution<int>(1, 28)(rng);
}
int endOfLastPath[2] = { waypoints[waypoints.size() - 1].x, waypoints[waypoints.size() - 1].y };
draw_path(endOfLastPath, newWaypoint);
prevWaypoint[0] = newWaypoint[0];
prevWaypoint[1] = newWaypoint[1];
}
int endOfLastPath[2] = { waypoints[waypoints.size() - 1].x, waypoints[waypoints.size() - 1].y };
draw_path(endOfLastPath, innerEndPoint);
waypoints.emplace_back(endPoint[0], endPoint[1]);
if (!distances_ok()) {
return 1;
}
float tileWidth = window->getSize().y/30.0;
mapCorner = window->getSize().x - tileWidth * 40;
mapBottom = tileWidth * 30;
for (int i = 0; i < MAP_HEIGHT; i++) {
for (int j = 0; j < MAP_WIDTH; j++) {
float x = mapCorner + j * tileWidth;
float y = i * tileWidth;
bool found = false;
for (auto &waypoint : waypoints) {
if (waypoint.x == j && waypoint.y == i) {
found = true;
break;
}
}
if (found) {
play.add(new Path(pathTextures[std::uniform_int_distribution<int>(0, pathTextures.size() - 1)(rng)], x, y, tileWidth / ((float) pathTextures[0].getSize().y), Sprite::TOP_LEFT), PATH_LAYER);
} else {
play.add(new Terrain(terrainTextures[std::uniform_int_distribution<int>(0, terrainTextures.size() - 1)(rng)], x, y, tileWidth / ((float) terrainTextures[0].getSize().y), Sprite::TOP_LEFT), TERRAIN_LAYER);
}
}
}
return 0;
}
void Game::draw_path(int *a, int *b) {
static std::mt19937 rng(time(nullptr) );
int order = std::uniform_int_distribution<int>(0, 1)(rng);
if (order == 0) {
for (auto & waypoint : waypoints) {
if (waypoint.x == a[0] && waypoint.y == a[1] - abs(a[1] - b[1])/(a[1] - b[1])) {
order = 1;
break;
}
}
} else {
for (auto & waypoint : waypoints) {
if (waypoint.x == a[0] - abs(a[0] - b[0])/(a[0] - b[0]) && waypoint.y == a[1]) {
order = 0;
break;
}
}
}
if (order == 0) {
draw_line(a, b, 1); //vertical
int endOfLastPath[2] = { waypoints[waypoints.size() - 1].x, waypoints[waypoints.size() - 1].y };
draw_line(endOfLastPath, b, 0); //horz
} else {
draw_line(a, b, 0); //horz
int endOfLastPath[2] = { waypoints[waypoints.size() - 1].x, waypoints[waypoints.size() - 1].y };
draw_line(endOfLastPath, b, 1); //vertical
}
}
void Game::draw_line(int *a, const int *b, int dir) {
int diff = a[dir] - b[dir];
int paired = 1;
if (dir == 1) {
paired = 0;
}
if (diff != 0) {
int l = 1;
int h = abs(diff) + 1;
for (int i = l; i < h; i++) {
int tile = i * abs(diff)/diff;
int newWaypoint[2] = { a[paired] , a[dir] - tile};
if (dir == 0) {
newWaypoint[0] = a[dir] - tile;
newWaypoint[1] = a[paired];
}
waypoints.emplace_back(newWaypoint[0], newWaypoint[1]);
}
}
}
bool Game::distances_ok() {
int too_short = 0;
for (int i = 0; i < waypoints.size(); i++) {
for (int j = 0; j < waypoints.size(); j++) {
if (abs(i - j) > 1 && sqrt(pow(waypoints[i].x - waypoints[j].x, 2) + pow(waypoints[i].y - waypoints[j].y, 2)) == 1) {
too_short++;
}
}
}
return too_short <= 10;
}
void Game::show_dash() {
sidebarTexture.loadFromFile("./assets/textures/gui/backdrop.png");
energyTexture.loadFromFile("./assets/textures/gui/energy.png");
gearsTexture.loadFromFile("./assets/textures/gui/gears.png");
moneyBoxTexture.loadFromFile("./assets/textures/gui/money_box.png");
livesBoxTexture.loadFromFile("./assets/textures/gui/lives_box.png");
livesBarTexture.loadFromFile("./assets/textures/gui/health_bar.png");
towerBoxTexture.loadFromFile("./assets/textures/gui/tower_box.png");
sidebar = new Sprite(sidebarTexture, mapCorner, mapBottom, window->getSize().y/((float)sidebarTexture.getSize().y), Sprite::BOTTOM_RIGHT);
play.add(sidebar, GUI_LAYER);
float tileWidthRatio = (TILE_UNIT / 30.0) * screenRatio.y;
float tileWidth = TILE_UNIT * screenRatio.y;
float moneyBoxHeight = 70 * tileWidthRatio;
float livesBoxHeight = 40 * tileWidthRatio;
float towerBoxHeight = 540 * tileWidthRatio;
float spacing = window->getSize().y / 72;
float total_height = moneyBoxHeight + livesBoxHeight + towerBoxHeight + (2 * spacing);
float gui_start_y = (window->getSize().y - total_height) / 2.0;
float marginRight = 18 * window->getSize().y/((float)sidebarTexture.getSize().y);
auto moneyBox = new Sprite(moneyBoxTexture, mapCorner - (marginRight + mapCorner + moneyBoxTexture.getSize().x * tileWidthRatio)/2.0, gui_start_y, moneyBoxHeight/((float)moneyBoxTexture.getSize().y), Sprite::TOP_LEFT);
play.add(moneyBox, GUI_LAYER);
auto energyIcon = new Sprite(energyTexture, moneyBox->getPosition().x + tileWidth / 4.0, moneyBox->getPosition().y + tileWidth / 4.0, tileWidth/((float)energyTexture.getSize().y), Sprite::TOP_LEFT);
play.add(energyIcon, GUI_LAYER);
auto gearsIcon = new Sprite(gearsTexture, moneyBox->getPosition().x + tileWidth / 4.0, moneyBox->getPosition().y + tileWidth * 1.25, tileWidth/((float)gearsTexture.getSize().y), Sprite::TOP_LEFT);
play.add(gearsIcon, GUI_LAYER);
play.add(new VariableLabel(&energy, pixelFont, TILE_UNIT*screenRatio.y, energyIcon->getPosition().x + tileWidth * 1.25, energyIcon->getPosition().y + tileWidth/2.0, Label::LEFT_CENTER), GUI_LAYER);
play.add(new VariableLabel(&gears, pixelFont, TILE_UNIT*screenRatio.y, gearsIcon->getPosition().x + tileWidth * 1.25, gearsIcon->getPosition().y + tileWidth/2.0, Label::LEFT_CENTER), GUI_LAYER);
auto livesBox = new Sprite(livesBoxTexture, mapCorner - (marginRight + mapCorner + livesBoxTexture.getSize().x * tileWidthRatio)/2.0, gui_start_y + moneyBoxHeight + spacing, livesBoxHeight/((float)livesBoxTexture.getSize().y), Sprite::TOP_LEFT);
play.add(livesBox, GUI_LAYER);
play.add(new Sprite(livesBarTexture, livesBox->getPosition().x + livesBox->getGlobalBounds().width/2.0, livesBox->getPosition().y + livesBox->getGlobalBounds().height/2.0, tileWidth/32.0, Sprite::CENTER), GUI_LAYER);
play.add(new Label(std::to_string(health), pixelFont, TILE_UNIT*screenRatio.y, livesBox->getPosition().x + livesBox->getGlobalBounds().width/2.0, livesBox->getPosition().y + livesBox->getGlobalBounds().height/2.0, Label::CENTER), GUI_LAYER);
auto towerBox = new Sprite(towerBoxTexture, mapCorner - (marginRight + mapCorner + towerBoxTexture.getSize().x * tileWidthRatio)/2.0, gui_start_y + moneyBoxHeight + livesBoxHeight + 2*spacing, towerBoxHeight/((float)towerBoxTexture.getSize().y), Sprite::TOP_LEFT);
play.add(towerBox, GUI_LAYER);
fill_tower_box(towerBox->getGlobalBounds());
}
void Game::fill_tower_box(const sf::Rect<float>& bounds) {
sf::Vector2f pos(bounds.left, bounds.top);
float towerWidth = bounds.width / 4;
float startOffset = towerWidth / 4;
towerMenuHoverTexture.loadFromFile("./assets/textures/gui/hover_tower.png");
for (int i = 0; i < towerTextures.size(); i++) {
int r = i/3;
int c = i%3;
auto *s = new Sprite(towerMenuHoverTexture, pos.x + startOffset + towerWidth * 1.25 * c, pos.y + startOffset + towerWidth * 1.5 * r, screenRatio.x, Sprite::TOP_LEFT);
play.add(new TowerGUI(towerTextures[i], s->getPosition().x + s->getGlobalBounds().width/2, s->getPosition().y + s->getGlobalBounds().height/2, towerWidth/((float)towerTextures[i].getSize().x), Sprite::CENTER, [this, i, bounds] { pick_tower(i, bounds); }, s), TOWER_BUTTONS_LAYER);
}
}
void Game::pick_tower(int i, const sf::Rect<float>& towerBoxBounds) {
mouseLeftMenu = false;
std::vector<int> layers = {PATH_LAYER, OBSTACLE_LAYER, TOWER_LAYER, GUI_LAYER};
inHand = new Tower(towerTextures[i], sf::Mouse::getPosition().x, sf::Mouse::getPosition().y, screenRatio.x, Sprite::CENTER, std::stof(towerData[i].at("range")), TILE_UNIT, std::stoi(towerData[i].at("price")), &energy, &inHand, layers, &play, new Sprite(placeableDiskTexture, Sprite::CENTER), [this] { unpick_tower(); });
Placeable *p = inHand;
inHand->setCommand([this, i, p, towerBoxBounds] { select_tower(i, (Tower *)p, towerBoxBounds); });
play.add(inHand, TOWER_LAYER);
play.hide_layer(TOWER_BUTTONS_LAYER);
show_stats(i, towerBoxBounds);
}
void Game::select_tower(int i, Tower* tower, const sf::Rect<float>& towerBoxBounds) {
play.empty_layer(TOWER_DESC_LAYER);
play.hide_layer(TOWER_BUTTONS_LAYER);
show_stats(i, tower, towerBoxBounds);
}
void Game::show_stats(int i, const sf::Rect<float>& towerBoxBounds) {
float iconSpacing = (towerBoxBounds.width - (TILE_UNIT * screenRatio.y) / 2.0)/3.0;
float bottomOffsetMult = 3;
if (towerData[i].find("pierce") != towerData[i].end() || towerData[i].find("splash_radius") != towerData[i].end()) {
bottomOffsetMult = 4;
}
auto icon = new Sprite(towerTextures[i], towerBoxBounds.left + (TILE_UNIT * screenRatio.y) / 4.0, towerBoxBounds.top + (TILE_UNIT * screenRatio.y) / 2.0, (towerBoxBounds.width/6.0)/((float)towerTextures[i].getSize().x), Sprite::TOP_LEFT);
play.add(icon, TOWER_DESC_LAYER);
play.add(new Label(towerData[i]["name"], pixelFont, TILE_UNIT*screenRatio.y, icon->getPosition().x + icon->getGlobalBounds().width + (12 * screenRatio.y), icon->getPosition().y + icon->getGlobalBounds().height/2.0, Label::LEFT_CENTER), TOWER_DESC_LAYER);
play.add(new TextBox(towerData[i]["description"], pixelFont, 16 * screenRatio.y, icon->getPosition().x, icon->getPosition().y + icon->getGlobalBounds().height, towerBoxBounds.width - icon->getPosition().x, Label::TOP_LEFT), TOWER_DESC_LAYER);
float priceIconY = towerBoxBounds.top + towerBoxBounds.height - (TILE_UNIT * screenRatio.y) * bottomOffsetMult;
float priceIconHeight = TILE_UNIT * screenRatio.y;
auto energyIcon = new Sprite(energyTexture, towerBoxBounds.left + (TILE_UNIT * screenRatio.y) / 4.0, priceIconY, (TILE_UNIT * screenRatio.y) / ((float) energyTexture.getSize().y), Sprite::TOP_LEFT);
play.add(energyIcon, TOWER_DESC_LAYER);
play.add(new Label(towerData[i]["price"], pixelFont, TILE_UNIT * screenRatio.y, energyIcon->getPosition().x + energyIcon->getGlobalBounds().width + (12 * screenRatio.y), energyIcon->getPosition().y + energyIcon->getGlobalBounds().height / 2.0, Label::LEFT_CENTER), TOWER_DESC_LAYER);
auto damageIcon = new Sprite(damageTexture, towerBoxBounds.left + (TILE_UNIT * screenRatio.y) / 4.0, priceIconY + priceIconHeight + (12 * screenRatio.y), (TILE_UNIT * screenRatio.y)/((float)energyTexture.getSize().y), Sprite::TOP_LEFT);
play.add(damageIcon, TOWER_DESC_LAYER);
play.add(new Label(towerData[i]["damage"], pixelFont, TILE_UNIT*screenRatio.y, damageIcon->getPosition().x + damageIcon->getGlobalBounds().width + (12 * screenRatio.y), damageIcon->getPosition().y + damageIcon->getGlobalBounds().height/2.0, Label::LEFT_CENTER), TOWER_DESC_LAYER);
auto rangeIcon = new Sprite(rangeTexture, damageIcon->getPosition().x + iconSpacing, damageIcon->getPosition().y, (TILE_UNIT * screenRatio.y)/((float)energyTexture.getSize().y), Sprite::TOP_LEFT);
play.add(rangeIcon, TOWER_DESC_LAYER);
play.add(new Label(towerData[i]["range"], pixelFont, TILE_UNIT*screenRatio.y, rangeIcon->getPosition().x + rangeIcon->getGlobalBounds().width + (12 * screenRatio.y), rangeIcon->getPosition().y + rangeIcon->getGlobalBounds().height/2.0, Label::LEFT_CENTER), TOWER_DESC_LAYER);
auto speedIcon = new Sprite(speedTexture, rangeIcon->getPosition().x + iconSpacing, damageIcon->getPosition().y, (TILE_UNIT * screenRatio.y)/((float)energyTexture.getSize().y), Sprite::TOP_LEFT);
play.add(speedIcon, TOWER_DESC_LAYER);
play.add(new Label(towerData[i]["speed"], pixelFont, TILE_UNIT*screenRatio.y, speedIcon->getPosition().x + speedIcon->getGlobalBounds().width + (12 * screenRatio.y), speedIcon->getPosition().y + speedIcon->getGlobalBounds().height/2.0, Label::LEFT_CENTER), TOWER_DESC_LAYER);
float offset = 0;
if (towerData[i].find("pierce") != towerData[i].end()) {
auto pierceIcon = new Sprite(pierceTexture, damageIcon->getPosition().x, damageIcon->getPosition().y + damageIcon->getGlobalBounds().height + (12 * screenRatio.y), (TILE_UNIT * screenRatio.y)/((float)energyTexture.getSize().y), Sprite::TOP_LEFT);
play.add(pierceIcon, TOWER_DESC_LAYER);
play.add(new Label(towerData[i]["pierce"], pixelFont, TILE_UNIT*screenRatio.y, pierceIcon->getPosition().x + pierceIcon->getGlobalBounds().width + (12 * screenRatio.y), pierceIcon->getPosition().y + pierceIcon->getGlobalBounds().height/2.0, Label::LEFT_CENTER), TOWER_DESC_LAYER);
offset++;
}
if (towerData[i].find("splash_radius") != towerData[i].end()) {
auto splashRadiusIcon = new Sprite(splashRangeTexture, damageIcon->getPosition().x + iconSpacing*offset, damageIcon->getPosition().y + damageIcon->getGlobalBounds().height + (12 * screenRatio.y), (TILE_UNIT * screenRatio.y)/((float)energyTexture.getSize().y), Sprite::TOP_LEFT);
play.add(splashRadiusIcon, TOWER_DESC_LAYER);
play.add(new Label(towerData[i]["splash_radius"], pixelFont, TILE_UNIT*screenRatio.y, splashRadiusIcon->getPosition().x + splashRadiusIcon->getGlobalBounds().width + (12 * screenRatio.y), splashRadiusIcon->getPosition().y + splashRadiusIcon->getGlobalBounds().height/2.0, Label::LEFT_CENTER), TOWER_DESC_LAYER);
offset++;
auto splashDmgIcon = new Sprite(splashDamageTexture, damageIcon->getPosition().x + iconSpacing*offset, damageIcon->getPosition().y + damageIcon->getGlobalBounds().height + (12 * screenRatio.y), (TILE_UNIT * screenRatio.y)/((float)energyTexture.getSize().y), Sprite::TOP_LEFT);
play.add(splashDmgIcon, TOWER_DESC_LAYER);
play.add(new Label(towerData[i]["splash_damage"], pixelFont, TILE_UNIT*screenRatio.y, splashDmgIcon->getPosition().x + splashDmgIcon->getGlobalBounds().width + (12 * screenRatio.y), splashDmgIcon->getPosition().y + splashDmgIcon->getGlobalBounds().height/2.0, Label::LEFT_CENTER), TOWER_DESC_LAYER);
}
}
void Game::show_stats(int i, Tower* tower, const sf::Rect<float>& towerBoxBounds) {
float iconSpacing = (towerBoxBounds.width - (TILE_UNIT * screenRatio.y) / 2.0)/3.0;
float bottomOffsetMult = 3;
if (towerData[i].find("pierce") != towerData[i].end() || towerData[i].find("splash_radius") != towerData[i].end()) {
bottomOffsetMult = 4;
}
auto icon = new Sprite(towerTextures[i], towerBoxBounds.left + (TILE_UNIT * screenRatio.y) / 4.0, towerBoxBounds.top + (TILE_UNIT * screenRatio.y) / 2.0, (towerBoxBounds.width/6.0)/((float)towerTextures[i].getSize().x), Sprite::TOP_LEFT);
play.add(icon, TOWER_DESC_LAYER);
play.add(new Label(towerData[i]["name"], pixelFont, TILE_UNIT*screenRatio.y, icon->getPosition().x + icon->getGlobalBounds().width + (12 * screenRatio.y), icon->getPosition().y + icon->getGlobalBounds().height/2.0, Label::LEFT_CENTER), TOWER_DESC_LAYER);
float descY = icon->getPosition().y + icon->getGlobalBounds().height;
focus1Label = new Label(foci_strings[tower->getFocus1()], pixelFont, 20 * screenRatio.y, towerBoxBounds.left + towerBoxBounds.width/2.0, descY, Label::CENTER);
play.add(focus1Label, TOWER_DESC_LAYER);
play.add(new Button(selectArrowTextureLeft, focus1Label->getPosition().x - 1.5*TILE_UNIT*screenRatio.y, focus1Label->getPosition().y, (20*screenRatio.y)/((float)selectArrowTextureLeft.getSize().y), Button::CENTER, [this, tower] { prev_focus(1, tower); }), TOWER_DESC_LAYER);
play.add(new Button(selectArrowTextureRight, focus1Label->getPosition().x + 1.5*TILE_UNIT*screenRatio.y, focus1Label->getPosition().y, (20*screenRatio.y)/((float)selectArrowTextureLeft.getSize().y), Button::CENTER, [this, tower] { next_focus(1, tower); }), TOWER_DESC_LAYER);
focus2Label = new Label(foci_strings[tower->getFocus2()], pixelFont, 20 * screenRatio.y, towerBoxBounds.left + towerBoxBounds.width/2.0, focus1Label->getPosition().y + focus1Label->getCharacterSize() + 6*screenRatio.y, Label::CENTER);
play.add(focus2Label, TOWER_DESC_LAYER);
play.add(new Button(selectArrowTextureLeft, focus2Label->getPosition().x - 1.5*TILE_UNIT*screenRatio.y, focus2Label->getPosition().y, (20*screenRatio.y)/((float)selectArrowTextureLeft.getSize().y), Button::CENTER, [this, tower] { prev_focus(2, tower); }), TOWER_DESC_LAYER);
play.add(new Button(selectArrowTextureRight, focus2Label->getPosition().x + 1.5*TILE_UNIT*screenRatio.y, focus2Label->getPosition().y, (20*screenRatio.y)/((float)selectArrowTextureLeft.getSize().y), Button::CENTER, [this, tower] { next_focus(2, tower); }), TOWER_DESC_LAYER);
play.add(new Label(foci_strings[0], pixelFont, 20 * screenRatio.y, towerBoxBounds.left + towerBoxBounds.width/2.0, focus2Label->getPosition().y + focus2Label->getCharacterSize() + 6*screenRatio.y, Label::CENTER), TOWER_DESC_LAYER);
descY = descY + 2*TILE_UNIT * screenRatio.y;
play.add(new TextBox(towerData[i]["description"], pixelFont, 16 * screenRatio.y, icon->getPosition().x, descY, towerBoxBounds.width - icon->getPosition().x, Label::TOP_LEFT), TOWER_DESC_LAYER);
float priceIconY = towerBoxBounds.top + towerBoxBounds.height - (TILE_UNIT * screenRatio.y) * bottomOffsetMult;
float priceIconHeight = TILE_UNIT * screenRatio.y;
auto damageIcon = new Sprite(damageTexture, towerBoxBounds.left + (TILE_UNIT * screenRatio.y) / 4.0, priceIconY + priceIconHeight + (12 * screenRatio.y), (TILE_UNIT * screenRatio.y)/((float)energyTexture.getSize().y), Sprite::TOP_LEFT);
play.add(damageIcon, TOWER_DESC_LAYER);
play.add(new Label(towerData[i]["damage"], pixelFont, TILE_UNIT*screenRatio.y, damageIcon->getPosition().x + damageIcon->getGlobalBounds().width + (12 * screenRatio.y), damageIcon->getPosition().y + damageIcon->getGlobalBounds().height/2.0, Label::LEFT_CENTER), TOWER_DESC_LAYER);
auto rangeIcon = new Sprite(rangeTexture, damageIcon->getPosition().x + iconSpacing, damageIcon->getPosition().y, (TILE_UNIT * screenRatio.y)/((float)energyTexture.getSize().y), Sprite::TOP_LEFT);
play.add(rangeIcon, TOWER_DESC_LAYER);
play.add(new Label(towerData[i]["range"], pixelFont, TILE_UNIT*screenRatio.y, rangeIcon->getPosition().x + rangeIcon->getGlobalBounds().width + (12 * screenRatio.y), rangeIcon->getPosition().y + rangeIcon->getGlobalBounds().height/2.0, Label::LEFT_CENTER), TOWER_DESC_LAYER);
auto speedIcon = new Sprite(speedTexture, rangeIcon->getPosition().x + iconSpacing, damageIcon->getPosition().y, (TILE_UNIT * screenRatio.y)/((float)energyTexture.getSize().y), Sprite::TOP_LEFT);
play.add(speedIcon, TOWER_DESC_LAYER);
play.add(new Label(towerData[i]["speed"], pixelFont, TILE_UNIT*screenRatio.y, speedIcon->getPosition().x + speedIcon->getGlobalBounds().width + (12 * screenRatio.y), speedIcon->getPosition().y + speedIcon->getGlobalBounds().height/2.0, Label::LEFT_CENTER), TOWER_DESC_LAYER);
float offset = 0;
if (towerData[i].find("pierce") != towerData[i].end()) {
auto pierceIcon = new Sprite(pierceTexture, damageIcon->getPosition().x, damageIcon->getPosition().y + damageIcon->getGlobalBounds().height + (12 * screenRatio.y), (TILE_UNIT * screenRatio.y)/((float)energyTexture.getSize().y), Sprite::TOP_LEFT);
play.add(pierceIcon, TOWER_DESC_LAYER);
play.add(new Label(towerData[i]["pierce"], pixelFont, TILE_UNIT*screenRatio.y, pierceIcon->getPosition().x + pierceIcon->getGlobalBounds().width + (12 * screenRatio.y), pierceIcon->getPosition().y + pierceIcon->getGlobalBounds().height/2.0, Label::LEFT_CENTER), TOWER_DESC_LAYER);
offset++;
}
if (towerData[i].find("splash_radius") != towerData[i].end()) {
auto splashRadiusIcon = new Sprite(splashRangeTexture, damageIcon->getPosition().x + iconSpacing*offset, damageIcon->getPosition().y + damageIcon->getGlobalBounds().height + (12 * screenRatio.y), (TILE_UNIT * screenRatio.y)/((float)energyTexture.getSize().y), Sprite::TOP_LEFT);
play.add(splashRadiusIcon, TOWER_DESC_LAYER);
play.add(new Label(towerData[i]["splash_radius"], pixelFont, TILE_UNIT*screenRatio.y, splashRadiusIcon->getPosition().x + splashRadiusIcon->getGlobalBounds().width + (12 * screenRatio.y), splashRadiusIcon->getPosition().y + splashRadiusIcon->getGlobalBounds().height/2.0, Label::LEFT_CENTER), TOWER_DESC_LAYER);
offset++;
auto splashDmgIcon = new Sprite(splashDamageTexture, damageIcon->getPosition().x + iconSpacing*offset, damageIcon->getPosition().y + damageIcon->getGlobalBounds().height + (12 * screenRatio.y), (TILE_UNIT * screenRatio.y)/((float)energyTexture.getSize().y), Sprite::TOP_LEFT);
play.add(splashDmgIcon, TOWER_DESC_LAYER);
play.add(new Label(towerData[i]["splash_damage"], pixelFont, TILE_UNIT*screenRatio.y, splashDmgIcon->getPosition().x + splashDmgIcon->getGlobalBounds().width + (12 * screenRatio.y), splashDmgIcon->getPosition().y + splashDmgIcon->getGlobalBounds().height/2.0, Label::LEFT_CENTER), TOWER_DESC_LAYER);
}
}
void Game::prev_focus(int focus, Tower* tower) {
if (focus == 1) {
tower->setFocus1(wrap(tower->getFocus1() - 1, 0, 10));
focus1Label->setString(foci_strings[tower->getFocus1()]);
} else {
tower->setFocus2(wrap(tower->getFocus2() - 1, 0, 10));
focus2Label->setString(foci_strings[tower->getFocus2()]);
}
}
void Game::next_focus(int focus, Tower* tower) {
if (focus == 1) {
tower->setFocus1(wrap(tower->getFocus1() + 1, 0, 10));
focus1Label->setString(foci_strings[tower->getFocus1()]);
} else {
tower->setFocus2(wrap(tower->getFocus2() + 1, 0, 10));
focus2Label->setString(foci_strings[tower->getFocus2()]);
}
}
void Game::unpick_tower() {
play.empty_layer(TOWER_DESC_LAYER);
play.show_layer(TOWER_BUTTONS_LAYER);
}
void Game::end_game() {
window->close();
}
void Game::clean_up() {
menu.empty();
play.empty();
delete window;
}

128
Game.h Normal file
View file

@ -0,0 +1,128 @@
//
// Created by Benjamin on 1/25/2022.
//
#ifndef SFML_TEMPLATE_GAME_H
#define SFML_TEMPLATE_GAME_H
#include "Group.h"
#include "Terrain.h"
#include "Placeable.h"
#include "Tower.h"
#include <iostream>
#include <map>
class Game {
public:
const sf::Vector2f BASE_RESOLUTION = sf::Vector2f(1920, 1080);
Game();
private:
static const int MAP_HEIGHT = 30;
static const int MAP_WIDTH = 40;
static const int EASY = 1;
static const int MEDIUM = 2;
static const int HARD = 3;
static const int EXPERT = 4;
const int DIFFICULTY_WAYPOINTS[4][2] = {{5, 6}, {3, 4}, {1, 2}, {0, 1}};
const int DIFFICULTY_OBSTACLE_PERCS[4] = {5, 10, 15, 20};
static const int PLAINS = 1;
static const int TERRAIN_LAYER = 0;
static const int PATH_LAYER = 1;
static const int OBSTACLE_LAYER = 2;
static const int TOWER_LAYER = 3;
static const int CANOPY_LAYER = 4;
static const int GUI_LAYER = 5;
static const int TOWER_BUTTONS_LAYER = 6;
static const int TOWER_DESC_LAYER = 7;
static const int TILE_UNIT = 36;
const std::string foci_strings[10] = {"First", "Last", "Strong", "Weak", "Fast", "Slow", "In-file", "Clustered", "Healthy", "Unhealthy"};
int pathDifficulty = EASY;
int environment = PLAINS;
std::vector<sf::Vector2i> waypoints;
float mapCorner;
float mapBottom;
int energy = 100, gears = 100, health = 100;
Placeable* inHand = nullptr;
Sprite* sidebar;
bool mouseLeftMenu = false;
Label *focus1Label, *focus2Label;
sf::Vector2f screenRatio;
sf::RenderWindow *window;
Group *displayGroup;
Group menu;
Group play;
sf::Texture startButtonTexture;
sf::Texture quitButtonTexture;
sf::Texture selectArrowTextureLeft;
sf::Texture selectArrowTextureRight;
sf::Texture sidebarTexture;
sf::Texture energyTexture;
sf::Texture gearsTexture;
sf::Texture damageTexture;
sf::Texture rangeTexture;
sf::Texture speedTexture;
sf::Texture pierceTexture;
sf::Texture splashRangeTexture;
sf::Texture splashDamageTexture;
sf::Texture moneyBoxTexture;
sf::Texture livesBoxTexture;
sf::Texture livesBarTexture;
sf::Texture towerBoxTexture;
std::vector<sf::Texture> terrainTextures;
std::vector<sf::Texture> pathTextures;
std::vector<sf::Texture> obstacleBases;
std::vector<bool> obstacleHasTops;
std::vector<sf::Texture> obstacleTops;
std::vector<sf::Texture> towerTextures;
sf::Texture towerMenuHoverTexture;
sf::Texture placeableDiskTexture;
std::vector<std::map<std::string, std::string>> towerData;
sf::Font pixelFont;
void create_menu();
void create_play();
void load_textures();
void load_tower_data();
void generate_obstacles();
void show_dash();
void fill_tower_box(const sf::Rect<float>&);
int generate_path();
void draw_path(int *, int *);
void draw_line(int *, const int *, int dir);
bool distances_ok();
void pick_tower(int, const sf::Rect<float>&);
void select_tower(int, Tower*, const sf::Rect<float>&);
void show_stats(int, const sf::Rect<float>&);
void show_stats(int, Tower*, const sf::Rect<float>&);
void prev_focus(int, Tower*);
void next_focus(int, Tower*);
void unpick_tower();
void end_game();
void clean_up();
};
#endif //SFML_TEMPLATE_GAME_H

185
Group.cpp Normal file
View file

@ -0,0 +1,185 @@
//
// Created by Benjamin on 1/25/2022.
//
#include <iostream>
#include "Group.h"
Group::Group() {
window = nullptr;
}
Group::Group(sf::RenderWindow *window) {
this->window = window;
}
std::vector<Sprite *> * Group::getLayerSprites(unsigned int l) {
if (layers.size() > l)
return &sprites[l];
else
return nullptr;
}
std::vector<Label *> * Group::getLayerLabels(unsigned int l) {
if (layers.size() > l)
return &labels[l];
else
return nullptr;
}
void Group::add(Sprite *s, int layer) {
while (layers.size() < layer + 1) {
layers.emplace_back();
}
while(sprites.size() < layer + 1) {
sprites.emplace_back();
}
layers.at(layer).push_back(0);
sprites.at(layer).push_back(s);
}
void Group::add(Label *l, int layer) {
while (layers.size() < layer + 1) {
layers.emplace_back();
}
while(labels.size() < layer + 1) {
labels.emplace_back();
}
layers.at(layer).push_back(1);
labels.at(layer).push_back(l);
}
void Group::kill(Sprite *target) {
int sprite;
int label;
for (int i = 0; i < layers.size(); i++) {
sprite = 0;
label = 0;
for (int j = 0; j < layers[i].size(); j++) {
if (layers[i][j] == 0) {
if (sprites[i][sprite] == target) {
sprites[i][sprite]->kill();
layers[i].erase(layers[i].begin() + j);
sprites[i].erase(sprites[i].begin() + sprite);
}
sprite++;
} else {
label++;
}
}
}
}
void Group::empty() {
for (int i = 0; i < layers.size(); i++) {
empty_layer(i);
}
layers.clear();
}
void Group::empty_layer(unsigned int l) {
if (sprites.size() > l) {
for (auto s : sprites[l]) {
s->kill();
delete s;
}
sprites[l].clear();
}
if (labels.size() > l) {
for (auto s : labels[l]) {
delete s;
}
labels[l].clear();
}
layers[l].clear();
}
void Group::update() {
int sprite;
int label;
for (int i = 0; i < layers.size(); i++) {
sprite = 0;
label = 0;
for (int type : layers[i]) {
if (type == 0) {
if (sprites[i][sprite]->isVisible())
sprites[i][sprite]->update();
sprite++;
} else {
labels[i][label]->update();
label++;
}
}
}
}
void Group::draw() {
int sprite;
int label;
for (int i = 0; i < layers.size(); i++) {
sprite = 0;
label = 0;
for (int type : layers[i]) {
if (type == 0) {
sprites[i][sprite]->draw(window);
sprite++;
} else {
labels[i][label]->draw(window);
label++;
}
}
}
}
void Group::do_clicked(sf::Vector2i &pos) {
sf::Vector2f mousePosF(static_cast<float>( pos.x ), static_cast<float>( pos.y ));
for (auto &layer : sprites) {
for (Sprite *s : layer) {
if (s->isVisible()) {
if (s->getGlobalBounds().contains(mousePosF)) {
s->click();
}
else {
s->noClick();
}
}
}
}
}
void Group::hide_layer(unsigned int l) {
if (sprites.size() > l) {
for (auto *s : sprites[l]) {
s->setVisible(false);
}
}
if (labels.size() > l) {
for (auto *s : labels[l]) {
s->setVisible(false);
}
}
}
void Group::show_layer(unsigned int l) {
if (sprites.size() > l) {
for (auto *s : sprites[l]) {
s->setVisible(true);
}
}
if (labels.size() > l) {
for (auto *s : labels[l]) {
s->setVisible(true);
}
}
}

39
Group.h Normal file
View file

@ -0,0 +1,39 @@
//
// Created by Benjamin on 1/25/2022.
//
#ifndef SFML_TEMPLATE_GROUP_H
#define SFML_TEMPLATE_GROUP_H
#include <vector>
#include "Sprite.h"
#include "Label.h"
class Group {
public:
Group();
explicit Group(sf::RenderWindow *);
std::vector<Sprite *>* getLayerSprites(unsigned int);
std::vector<Label *>* getLayerLabels(unsigned int);
void add(Sprite *, int = 0);
void add(Label *, int = 0);
void kill(Sprite *);
void empty();
void empty_layer(unsigned int);
void update();
void draw();
void do_clicked(sf::Vector2i &);
void hide_layer(unsigned int);
void show_layer(unsigned int);
private:
sf::RenderWindow *window;
std::vector<std::vector<int>> layers;
std::vector<std::vector<Sprite *>> sprites;
std::vector<std::vector<Label *>> labels;
};
#endif //SFML_TEMPLATE_GROUP_H

31
Label.cpp Normal file
View file

@ -0,0 +1,31 @@
//
// Created by Benjamin on 1/26/2022.
//
#include <iostream>
#include "Label.h"
Label::Label(const std::string& text, const sf::Font& font, unsigned int size, float x, float y, int origin) : sf::Text(text, font, size) {
setOrigin(origin);
setPosition(x, y);
}
void Label::setOrigin(int origin) {
if (origin == TOP_LEFT) {
sf::Text::setOrigin(getLocalBounds().left, getLocalBounds().top);
} else if (origin == CENTER) {
sf::Text::setOrigin(getLocalBounds().left + getLocalBounds().width/2.0,getLocalBounds().top + getLocalBounds().height/2.0);
} else if (origin == LEFT_CENTER) {
sf::Text::setOrigin(getLocalBounds().left, getLocalBounds().top + getLocalBounds().height/2.0);
}
}
void Label::setVisible(bool visible) {
this->visible = visible;
}
void Label::draw(sf::RenderWindow *window) {
if (visible) {
window->draw(*this);
}
}

30
Label.h Normal file
View file

@ -0,0 +1,30 @@
//
// Created by Benjamin on 1/26/2022.
//
#ifndef SFML_TEMPLATE_LABEL_H
#define SFML_TEMPLATE_LABEL_H
#include <SFML/Graphics.hpp>
class Label : public sf::Text {
protected:
int origin = TOP_LEFT;
bool visible = true;
public:
static const int TOP_LEFT = 0;
static const int CENTER = 1;
static const int LEFT_CENTER = 3;
Label(const std::string&, const sf::Font&, unsigned int, float, float, int);
void setOrigin(int);
void setVisible(bool);
virtual void update() {};
void draw(sf::RenderWindow *window);
};
#endif //SFML_TEMPLATE_LABEL_H

7
Obstacle.cpp Normal file
View file

@ -0,0 +1,7 @@
//
// Created by Benjamin on 1/27/2022.
//
#include "Obstacle.h"
Obstacle::Obstacle(const sf::Texture &texture) : Sprite(texture) {}

17
Obstacle.h Normal file
View file

@ -0,0 +1,17 @@
//
// Created by Benjamin on 1/27/2022.
//
#ifndef SFML_TEMPLATE_OBSTACLE_H
#define SFML_TEMPLATE_OBSTACLE_H
#include "Sprite.h"
class Obstacle : public Sprite {
public:
explicit Obstacle(const sf::Texture &);
};
#endif //SFML_TEMPLATE_OBSTACLE_H

7
Path.cpp Normal file
View file

@ -0,0 +1,7 @@
//
// Created by Benjamin on 1/26/2022.
//
#include "Path.h"
Path::Path(sf::Texture &texture, float x, float y, float scale, int origin) : Terrain(texture, x, y, scale, origin) {}

18
Path.h Normal file
View file

@ -0,0 +1,18 @@
//
// Created by Benjamin on 1/26/2022.
//
#ifndef SFML_TEMPLATE_PATH_H
#define SFML_TEMPLATE_PATH_H
#include "Terrain.h"
class Path : public Terrain {
private:
Path *next_waypoint;
public:
Path(sf::Texture &, float, float, float, int);
};
#endif //SFML_TEMPLATE_PATH_H

100
Placeable.cpp Normal file
View file

@ -0,0 +1,100 @@
//
// Created by Benjamin on 1/30/2022.
//
#include "Placeable.h"
#include "Collision.h"
Placeable::Placeable(const sf::Texture &texture, float x, float y, float scale, int origin, float tileWidth, int price, int* energy, Placeable **inHand, std::vector<int> &collideLayers, Group *collideGroup, Sprite *disk, const std::function<void()> &uncommand) : Sprite(texture, x, y, scale, origin) {
riding = disk;
this->inHand = inHand;
this->collideLayers = collideLayers;
this->collideGroup = collideGroup;
this->uncommand = uncommand;
this->price = price;
this->energy = energy;
setReach(tileWidth);
}
void Placeable::setReach(float reach) {
this->reach = reach;
sf::Vector2u size = riding->getTexture()->getSize();
riding->setScale((reach*2)/((float)size.x), (reach*2)/((float)size.y));
}
void Placeable::setCommand(const std::function<void()> &command) {
this->command = command;
}
void Placeable::update() {
Sprite::update();
if (placing) {
float x = sf::Mouse::getPosition().x;
float y = sf::Mouse::getPosition().y;
setPosition(x, y);
riding->setPosition(x, y);
if (!isColliding()) {
riding->setColor(sf::Color(255, 255, 255, 128));
} else {
riding->setColor(sf::Color(255, 0, 0, 128));
}
}
}
void Placeable::click() {
if (placing && !isColliding() && (price == 0 || *energy >= price)) {
placing = !placing;
if (price != 0)
*energy -= price;
riding->setColor(sf::Color(0, 0, 0, 128));
}
if (!placing) {
if (*inHand == this)
*inHand = nullptr;
if (!*inHand) {
clicked = true;
command();
riding->setVisible(true);
}
}
}
void Placeable::noClick() {
riding->setVisible(false);
if (clicked)
uncommand();
clicked = false;
}
bool Placeable::isColliding() {
bool colliding = false;
for (int layer : collideLayers) {
for (Sprite *s : *collideGroup->getLayerSprites(layer)) {
if (s != this && s->getGlobalBounds().intersects(this->getGlobalBounds()) && Collision::PixelPerfectTest(*s, *this)) {
colliding = true;
break;
}
if (colliding) {
break;
}
}
}
return colliding;
}
bool Placeable::isPlacing() const {
return placing;
}

39
Placeable.h Normal file
View file

@ -0,0 +1,39 @@
//
// Created by Benjamin on 1/30/2022.
//
#ifndef SFML_TEMPLATE_PLACEABLE_H
#define SFML_TEMPLATE_PLACEABLE_H
#include <functional>
#include "Group.h"
class Placeable : public Sprite {
protected:
bool placing = true;
std::vector<int> collideLayers;
Group *collideGroup;
std::function<void()> uncommand;
Placeable **inHand;
float reach;
int price;
int* energy;
bool isColliding();
public:
Placeable(const sf::Texture &, float, float, float, int, float, int, int*, Placeable **, std::vector<int>&, Group *, Sprite *, const std::function<void()> &);
void setReach(float reach);
void setCommand(const std::function<void()> &);
bool isPlacing() const;
std::function<void()> command;
void update() override;
void click() override;
void noClick() override;
};
#endif //SFML_TEMPLATE_PLACEABLE_H

87
Sprite.cpp Normal file
View file

@ -0,0 +1,87 @@
//
// Created by Benjamin on 1/25/2022.
//
#include "Sprite.h"
Sprite::Sprite(const sf::Texture &texture) : sf::Sprite(texture) {
}
Sprite::Sprite(const sf::Texture &texture, int origin) : sf::Sprite(texture) {
setOrigin(origin);
}
Sprite::Sprite(const sf::Texture &texture, float x, float y, float scale, int origin) : Sprite(texture, origin) {
setScale(scale, scale);
setPosition(x, y);
}
Sprite::Sprite(const sf::Texture &texture, float x, float y, float angle, float scale, int origin) : Sprite(texture, x, y, scale, origin) {
setRotation(angle);
}
Sprite::~Sprite() noexcept {
removeMounts();
}
void Sprite::setOrigin(int origin) {
if (origin == TOP_LEFT) {
sf::Sprite::setOrigin(0, 0);
} else if (origin == CENTER) {
sf::Sprite::setOrigin(getLocalBounds().width/2.0,getLocalBounds().height/2.0);
} else if (origin == BOTTOM_RIGHT) {
sf::Sprite::setOrigin(getLocalBounds().width, getLocalBounds().height);
}
}
bool Sprite::isVisible() const {
return visible;
}
void Sprite::setVisible(bool visible) {
this->visible = visible;
if (hasMount()) {
riding->setVisible(visible);
}
}
void Sprite::setMount(Sprite *s) {
removeMounts();
riding = s;
}
void Sprite::removeMounts() {
if (hasMount()) {
riding->removeMounts();
delete riding;
riding = nullptr;
}
}
void Sprite::kill() {
removeMounts();
}
void Sprite::update() {
sf::Vector2f mousePosF(static_cast<float>( sf::Mouse::getPosition().x ), static_cast<float>( sf::Mouse::getPosition().y ));
if (getGlobalBounds().contains(mousePosF)) {
if (!isHovered()) {
hover();
}
} else if (isHovered()) {
unHover();
}
}
void Sprite::draw(sf::RenderWindow *window) {
if (hasMount()) {
riding->draw(window);
}
if (visible) {
window->draw(*this);
}
}

48
Sprite.h Normal file
View file

@ -0,0 +1,48 @@
//
// Created by Benjamin on 1/25/2022.
//
#ifndef SFML_TEMPLATE_SPRITE_H
#define SFML_TEMPLATE_SPRITE_H
#include <SFML/Graphics.hpp>
class Sprite : public sf::Sprite {
protected:
bool hovered = false;
bool clicked = false;
bool visible = true;
Sprite *riding = nullptr;
public:
static const int TOP_LEFT = 0;
static const int CENTER = 1;
static const int BOTTOM_RIGHT = 2;
explicit Sprite(const sf::Texture &);
Sprite(const sf::Texture &, int);
Sprite(const sf::Texture &, float, float, float, int);
Sprite(const sf::Texture &, float, float, float, float, int);
~Sprite() noexcept;
void setOrigin(int);
bool isHovered() const { return hovered; };
bool isVisible() const;
virtual void setVisible(bool visible);
void setMount(Sprite *);
bool hasMount() { return riding; };
void removeMounts();
void kill();
virtual void update();
virtual void click() {};
virtual void noClick() {};
virtual void hover() { hovered = true; };
virtual void unHover() { hovered = false; };
void draw(sf::RenderWindow *window);
};
#endif //SFML_TEMPLATE_SPRITE_H

7
Terrain.cpp Normal file
View file

@ -0,0 +1,7 @@
//
// Created by Benjamin on 1/26/2022.
//
#include "Terrain.h"
Terrain::Terrain(sf::Texture &texture, float x, float y, float scale, int origin) : Sprite(texture, x, y, scale, origin) {}

17
Terrain.h Normal file
View file

@ -0,0 +1,17 @@
//
// Created by Benjamin on 1/26/2022.
//
#ifndef SFML_TEMPLATE_TERRAIN_H
#define SFML_TEMPLATE_TERRAIN_H
#include "Sprite.h"
class Terrain : public Sprite {
public:
Terrain(sf::Texture &, float, float, float, int);
};
#endif //SFML_TEMPLATE_TERRAIN_H

31
TextBox.cpp Normal file
View file

@ -0,0 +1,31 @@
//
// Created by Benjamin on 2/27/2022.
//
#include <sstream>
#include "TextBox.h"
std::string wordWrap(const std::string &text, float length, const sf::Font &font, unsigned int charSize) {
std::istringstream iss(text);
std::vector<std::string> results((std::istream_iterator<std::string>(iss)), std::istream_iterator<std::string>());
sf::Text temp;
temp.setFont(font);
temp.setCharacterSize(charSize);
std::string tempStr;
std::string returnStr;
for (const std::string &s : results) {
tempStr += s + " ";
temp.setString(tempStr);
if (temp.getGlobalBounds().width < length) returnStr += s + " ";
else {
returnStr += "\n" + s + " ";
tempStr = s;
}
}
return returnStr;
}
TextBox::TextBox(const std::string &text, const sf::Font &font, unsigned int size, float x, float y, float w, int origin) : Label(wordWrap(text, w, font, size), font, size, x, y, origin) {}

16
TextBox.h Normal file
View file

@ -0,0 +1,16 @@
//
// Created by Benjamin on 2/27/2022.
//
#ifndef SFML_TEMPLATE_TEXTBOX_H
#define SFML_TEMPLATE_TEXTBOX_H
#include "Label.h"
class TextBox : public Label {
public:
TextBox(const std::string&, const sf::Font&, unsigned int, float, float, float, int);
};
#endif //SFML_TEMPLATE_TEXTBOX_H

25
Tower.cpp Normal file
View file

@ -0,0 +1,25 @@
//
// Created by Benjamin on 1/30/2022.
//
#include "Tower.h"
Tower::Tower(const sf::Texture &texture, float x, float y, float scale, int origin, float reach, float tileWidth, int price, int* energy, Placeable **inHand, std::vector<int> &collideLayers, Group *collideGroup, Sprite *disk, const std::function<void()> &uncommand) : Placeable(texture, x, y, scale, origin, tileWidth, price, energy, inHand, collideLayers, collideGroup, disk, uncommand) {
setReach(reach * tileWidth);
}
int Tower::getFocus1() const {
return focus1;
}
void Tower::setFocus1(int focus1) {
Tower::focus1 = focus1;
}
int Tower::getFocus2() const {
return focus2;
}
void Tower::setFocus2(int focus2) {
Tower::focus2 = focus2;
}

34
Tower.h Normal file
View file

@ -0,0 +1,34 @@
//
// Created by Benjamin on 1/30/2022.
//
#ifndef SFML_TEMPLATE_TOWER_H
#define SFML_TEMPLATE_TOWER_H
#include "Placeable.h"
class Tower : public Placeable {
private:
int focus1 = FIRST, focus2 = FIRST;
public:
static const int FIRST = 0;
static const int LAST = 1;
static const int STRONG = 2;
static const int WEAK = 3;
static const int HEALTHY = 4;
static const int UNHEALTHY = 5;
static const int FAST = 6;
static const int SLOW = 7;
static const int INFILE = 8;
static const int CLUSTERED = 9;
Tower(const sf::Texture &, float, float, float, int, float, float, int, int*, Placeable **, std::vector<int>&, Group *, Sprite *, const std::function<void()> &);
int getFocus2() const;
void setFocus2(int focus2);
int getFocus1() const;
void setFocus1(int focus1);
};
#endif //SFML_TEMPLATE_TOWER_H

30
TowerGUI.cpp Normal file
View file

@ -0,0 +1,30 @@
//
// Created by Benjamin on 1/30/2022.
//
#include "TowerGUI.h"
TowerGUI::TowerGUI(const sf::Texture &texture, float x, float y, float scale, int origin, const std::function<void()> &command, Sprite* hoverSprite) : Button(texture, x, y, scale, origin, command) {
riding = hoverSprite;
riding->setVisible(false);
}
void TowerGUI::hover() {
Sprite::hover();
riding->setVisible(true);
}
void TowerGUI::unHover() {
Sprite::unHover();
riding->setVisible(false);
}
void TowerGUI::setVisible(bool visible) {
Sprite::setVisible(visible);
if (visible) {
unHover();
}
}

20
TowerGUI.h Normal file
View file

@ -0,0 +1,20 @@
//
// Created by Benjamin on 1/30/2022.
//
#ifndef SFML_TEMPLATE_TOWERGUI_H
#define SFML_TEMPLATE_TOWERGUI_H
#include "Button.h"
class TowerGUI : public Button {
public:
TowerGUI(const sf::Texture &, float, float, float, int, const std::function<void()> &, Sprite*);
void hover() override;
void unHover() override;
void setVisible(bool visible);
};
#endif //SFML_TEMPLATE_TOWERGUI_H

13
VariableLabel.cpp Normal file
View file

@ -0,0 +1,13 @@
//
// Created by Benjamin on 2/28/2022.
//
#include "VariableLabel.h"
VariableLabel::VariableLabel(int *track, const sf::Font& font, unsigned int size, float x, float y, int origin) : Label(std::to_string(*track), font, size, x, y, origin) {
this->track = track;
}
void VariableLabel::update() {
setString(std::to_string(*track));
}

20
VariableLabel.h Normal file
View file

@ -0,0 +1,20 @@
//
// Created by Benjamin on 2/28/2022.
//
#ifndef SFML_TEMPLATE_VARIABLELABEL_H
#define SFML_TEMPLATE_VARIABLELABEL_H
#include "Label.h"
class VariableLabel : public Label {
private:
int* track;
public:
VariableLabel(int* track, const sf::Font&, unsigned int, float, float, int);
void update() override;
};
#endif //SFML_TEMPLATE_VARIABLELABEL_H

60
assets/data/towers.dat Normal file
View file

@ -0,0 +1,60 @@
age 1
name Caveman
price 50
type m
range 2.5
damage 4
speed 1
splash_radius 1
splash_damage 4
pierce 3
stun 0.5
projectile blunt
description Not very bright but wields a large club. Attacks and stuns nearby enemies.
age 1
name Rock Thrower
price 75
type r
range 6
damage 3
speed 0.5
projectile_speed 0.111
knock_away 0.5
projectile rock
description Slings hard stones at enemies, slightly knocking them back towards whatever time period they came from.
age 1
name Spear Thrower
price 75
type r
range 7
damage 4
speed 0.75
projectile_speed 0.111
pierce 3
projectile spear
description Pointy spears fly far and can pierce through multiple enemies.
age 2
name Archer
price 100
type r
range 8
damage 3
speed 0.5
projectile_speed 0.167
projectile arrow
description A talented hunter, can take aim at enemies from a great distance.
age 2
name Swordsman
price 100
type m
range 2.5
damage 4
speed 1
sweep_angle 120
pierce 5
projectile sword
description His bronze sword is capable of slicing through many enemies, but he doesn't have to be so stuck-up about it.

BIN
assets/fonts/FreePixel.ttf Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 869 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,008 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 730 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 746 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 764 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,012 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 764 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,013 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 764 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 991 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 840 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,021 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 965 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1,002 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 23 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 866 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 904 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 878 B

7
main.cpp Normal file
View file

@ -0,0 +1,7 @@
#include "Game.h"
int main() {
Game g;
return EXIT_SUCCESS; // report our program exited successfully
}