init
8
.gitignore
vendored
|
|
@ -30,3 +30,11 @@
|
|||
*.exe
|
||||
*.out
|
||||
*.app
|
||||
|
||||
# Notepad Backups
|
||||
*.bak
|
||||
|
||||
# Clion
|
||||
.idea
|
||||
cmake-build-release
|
||||
CMakeLists.txt
|
||||
33
Button.cpp
Normal 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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
BIN
assets/textures/character/character.png
Normal file
|
After Width: | Height: | Size: 869 B |
BIN
assets/textures/environments/obstacles/log1-1.png
Normal file
|
After Width: | Height: | Size: 1,008 B |
BIN
assets/textures/environments/obstacles/puddle1-1.png
Normal file
|
After Width: | Height: | Size: 730 B |
BIN
assets/textures/environments/obstacles/rock1-1.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/textures/environments/obstacles/rock1-2.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/textures/environments/obstacles/rock1-3.png
Normal file
|
After Width: | Height: | Size: 746 B |
BIN
assets/textures/environments/obstacles/stump1-1_bottom.png
Normal file
|
After Width: | Height: | Size: 764 B |
BIN
assets/textures/environments/obstacles/stump1-1_top.png
Normal file
|
After Width: | Height: | Size: 1,012 B |
BIN
assets/textures/environments/obstacles/stump1-2_bottom.png
Normal file
|
After Width: | Height: | Size: 764 B |
BIN
assets/textures/environments/obstacles/stump1-2_top.png
Normal file
|
After Width: | Height: | Size: 1,013 B |
BIN
assets/textures/environments/obstacles/stump1-3.png
Normal file
|
After Width: | Height: | Size: 764 B |
BIN
assets/textures/environments/paths/path1-1.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
assets/textures/environments/paths/path1-2.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/textures/environments/paths/path1-3.png
Normal file
|
After Width: | Height: | Size: 1.4 KiB |
BIN
assets/textures/environments/paths/path1-4.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
assets/textures/environments/paths/path1-5.png
Normal file
|
After Width: | Height: | Size: 1.5 KiB |
BIN
assets/textures/environments/terrain/terrain1-1.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
assets/textures/environments/terrain/terrain1-2.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
assets/textures/environments/terrain/terrain1-3.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
assets/textures/environments/terrain/terrain1-4.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/textures/environments/terrain/terrain1-5.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/textures/gui/attack_speed_icon.png
Normal file
|
After Width: | Height: | Size: 991 B |
BIN
assets/textures/gui/backdrop.png
Normal file
|
After Width: | Height: | Size: 11 KiB |
BIN
assets/textures/gui/damage_icon.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
assets/textures/gui/energy.png
Normal file
|
After Width: | Height: | Size: 840 B |
BIN
assets/textures/gui/gears.png
Normal file
|
After Width: | Height: | Size: 1 KiB |
BIN
assets/textures/gui/health_bar.png
Normal file
|
After Width: | Height: | Size: 1,021 B |
BIN
assets/textures/gui/hover_tower.png
Normal file
|
After Width: | Height: | Size: 965 B |
BIN
assets/textures/gui/lives_box.png
Normal file
|
After Width: | Height: | Size: 1,002 B |
BIN
assets/textures/gui/melee_icon.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/textures/gui/money_box.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
assets/textures/gui/pierce_icon.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
assets/textures/gui/placeable_disk.png
Normal file
|
After Width: | Height: | Size: 23 KiB |
BIN
assets/textures/gui/proj_speed_icon.png
Normal file
|
After Width: | Height: | Size: 1 KiB |
BIN
assets/textures/gui/range_icon.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/textures/gui/ranged_icon.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
assets/textures/gui/select_arrow_left.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
BIN
assets/textures/gui/select_arrow_right.png
Normal file
|
After Width: | Height: | Size: 6.2 KiB |
BIN
assets/textures/gui/splash_damage_icon.png
Normal file
|
After Width: | Height: | Size: 1.2 KiB |
BIN
assets/textures/gui/splash_radius.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/textures/gui/tower_box.png
Normal file
|
After Width: | Height: | Size: 5.4 KiB |
BIN
assets/textures/menu/quit_button.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
assets/textures/menu/start_button.png
Normal file
|
After Width: | Height: | Size: 1.3 KiB |
BIN
assets/textures/placeables/bait_trap.png
Normal file
|
After Width: | Height: | Size: 1 KiB |
BIN
assets/textures/towers/1_caveman.png
Normal file
|
After Width: | Height: | Size: 866 B |
BIN
assets/textures/towers/1_rock_thrower.png
Normal file
|
After Width: | Height: | Size: 1 KiB |
BIN
assets/textures/towers/1_spear_thrower.png
Normal file
|
After Width: | Height: | Size: 904 B |
BIN
assets/textures/towers/2_archer.png
Normal file
|
After Width: | Height: | Size: 1.1 KiB |
BIN
assets/textures/towers/2_swordsman.png
Normal file
|
After Width: | Height: | Size: 878 B |
7
main.cpp
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
#include "Game.h"
|
||||
|
||||
int main() {
|
||||
Game g;
|
||||
|
||||
return EXIT_SUCCESS; // report our program exited successfully
|
||||
}
|
||||