Added consumption and countdown
This commit is contained in:
parent
b0a83a90ef
commit
241d8119ce
39 changed files with 538 additions and 81 deletions
|
@ -129,7 +129,14 @@ set(SOURCES
|
|||
src/game/physics/body_adapter.hpp
|
||||
src/sprites/masked_sprite.cpp
|
||||
src/sprites/masked_sprite.hpp
|
||||
src/sprites/configs/masked_sprite_config.hpp src/sprites/masked_sprite_hole.hpp)
|
||||
src/sprites/configs/masked_sprite_config.hpp
|
||||
src/sprites/masked_sprite_hole.hpp
|
||||
src/game/time/countdown.cpp
|
||||
src/game/time/countdown.hpp
|
||||
src/game/layer/global_layer.cpp
|
||||
src/game/layer/global_layer.hpp
|
||||
src/typography/font_manager.cpp
|
||||
src/typography/font_manager.hpp)
|
||||
|
||||
set(PHYSICS_00_SOURCES
|
||||
src/prototypes/physics_00.cpp)
|
||||
|
|
BIN
assets/fonts/pixel.ttf
Normal file
BIN
assets/fonts/pixel.ttf
Normal file
Binary file not shown.
|
@ -6,7 +6,12 @@
|
|||
#include "game/collectables/collectable_config.hpp"
|
||||
|
||||
std::map<std::string, CollectableConfig> const all_collectables = {
|
||||
{"box", CollectableConfig("rosebush")}
|
||||
{"rose", CollectableConfig("rose", 1)},
|
||||
{"rosebush", CollectableConfig("rosebush", 3)},
|
||||
{"stone", CollectableConfig("stone", 5)},
|
||||
{"bike", CollectableConfig("bike", 10)},
|
||||
{"small-tree", CollectableConfig("small-tree", 20)},
|
||||
{"tram", CollectableConfig("tram", 50)}
|
||||
};
|
||||
|
||||
#endif //HOLESOME_COLLECTABLES_HPP
|
||||
|
|
11
src/config.h
11
src/config.h
|
@ -34,6 +34,7 @@
|
|||
#define MASKED_HOLE_BORDER_TRANSITION_SIZE 0.2f
|
||||
#define MASKED_HOLE_DARKNESS_LIMIT 0.5f
|
||||
#define COLLECTABLE_SCALE 5.f
|
||||
#define MASKED_SPRITE_LOWER_BOUND -3.f
|
||||
|
||||
// Tracking view defaults
|
||||
#define DEF_TV_IS_ABSOLUTE_FREE_MOVE_THRESHOLD false
|
||||
|
@ -70,4 +71,14 @@
|
|||
#define DB_WORLD_GRID_RENDER false
|
||||
#define DB_TRACKING_VIEW_CENTER false
|
||||
|
||||
// Fonts
|
||||
#define FONT_BASE_PATH "assets/fonts/"
|
||||
#define DEFAULT_FONT "pixel"
|
||||
#define COUNTDOWN_FONT_SIZE 50
|
||||
|
||||
// Name => Path
|
||||
const std::map<std::string, std::string> all_fonts = {
|
||||
{"pixel", "pixel.ttf"}
|
||||
};
|
||||
|
||||
#endif //HOLESOME_CONFIG_H
|
||||
|
|
|
@ -3,7 +3,8 @@
|
|||
#include "../player/player_collection.hpp"
|
||||
|
||||
TrackingView::TrackingView(TrackingViewOptions options) : options(options),
|
||||
view(new sf::View()),
|
||||
view(new sf::View(options.initialCenter,
|
||||
options.minViewSize)),
|
||||
trackables({})
|
||||
{
|
||||
marker = new CircleObject(DB_CIRCLE_RADIUS, sf::Color::Yellow);
|
||||
|
@ -22,8 +23,8 @@ void TrackingView::lateUpdate()
|
|||
if (!trackables.empty())
|
||||
{
|
||||
followTrackables();
|
||||
adjustSizeToTrackables();
|
||||
}
|
||||
adjustSizeToTrackables();
|
||||
}
|
||||
|
||||
void TrackingView::setSize(sf::Vector2f newSize)
|
||||
|
@ -143,6 +144,11 @@ void TrackingView::processTrackableStates()
|
|||
|
||||
TrackingArea TrackingView::getTrackingArea() const
|
||||
{
|
||||
if (trackables.empty())
|
||||
{
|
||||
return TrackingArea();
|
||||
}
|
||||
|
||||
auto initialTrackable = trackables[0];
|
||||
TrackingArea area = {
|
||||
initialTrackable->getTrackablePosition() - initialTrackable->getTrackableSize() / 2.f,
|
||||
|
|
|
@ -21,18 +21,20 @@ struct TrackingViewOptions
|
|||
float softResizeSpeed = DEF_TV_SOFT_RESIZE_SPEED;
|
||||
|
||||
/**
|
||||
* If setWorld to 0, view will not be limited.
|
||||
* If set to 0, view will not be limited.
|
||||
*/
|
||||
sf::Vector2f minViewSize = DEF_TV_MIN_VIEW_SIZE;
|
||||
|
||||
/**
|
||||
* If setWorld to 0, view will not be limited.
|
||||
* If set to 0, view will not be limited.
|
||||
*/
|
||||
sf::Vector2f maxViewSize = DEF_TV_MAX_VIEW_SIZE;
|
||||
|
||||
sf::Vector2f initialCenter = {0, 0};
|
||||
|
||||
/**
|
||||
* Will be added to tracked area size twice, as padding for each side.
|
||||
* If isAbsoluteViewSizePadding is setWorld to true, padding will be added to view size, is multiplied with tracking size and added.
|
||||
* If isAbsoluteViewSizePadding is set to true, padding will be added to view size, is multiplied with tracking size and added.
|
||||
*/
|
||||
sf::Vector2f viewSizePadding = DEF_TV_VIEW_SIZE_PADDING;
|
||||
bool isAbsoluteViewSizePadding = DEF_TV_IS_ABSOLUTE_VIEW_SIZE_PADDING;
|
||||
|
|
|
@ -2,8 +2,12 @@
|
|||
#include "../../sprites/versatile_sprite.hpp"
|
||||
#include "../../config.h"
|
||||
#include "../input/input_mapper.h"
|
||||
#include "../physics/holes/holes_simulation.hpp"
|
||||
#include "collection/collectables_collection.hpp"
|
||||
#include "../player/player_collection.hpp"
|
||||
|
||||
Collectable::Collectable()
|
||||
Collectable::Collectable(int points)
|
||||
: points(points)
|
||||
{
|
||||
collectableId = collectableCount;
|
||||
collectableCount++;
|
||||
|
@ -24,10 +28,12 @@ float Collectable::getDepth() const
|
|||
void Collectable::setSprite(const std::string &spriteName)
|
||||
{
|
||||
// Create versatile sprite
|
||||
auto sprite = std::make_shared<VersatileSprite>(spriteName);
|
||||
size = sprite->getSize() * COLLECTABLE_SCALE;
|
||||
sprite->setSize(size);
|
||||
addChildScreenOffset(sprite, IsometricCoordinates(-size / 2.f));
|
||||
auto spriteObject = std::make_shared<VersatileSprite>(spriteName);
|
||||
size = spriteObject->getSize() * COLLECTABLE_SCALE;
|
||||
spriteObject->setSize(size);
|
||||
addChildScreenOffset(spriteObject, IsometricCoordinates(-size / 2.f));
|
||||
|
||||
sprite = spriteObject;
|
||||
|
||||
// Set half size offset of coordinates
|
||||
coordinates->move(IsometricCoordinates(0, -size.x / 2.f, 0));
|
||||
|
@ -37,3 +43,16 @@ sf::Vector2f Collectable::getSize() const
|
|||
{
|
||||
return size / WORLD_TO_ISO_SCALE;
|
||||
}
|
||||
|
||||
void Collectable::preRenderUpdate()
|
||||
{
|
||||
if (!sprite->isVisible())
|
||||
{
|
||||
auto closestPlayer = PlayerCollection::getInstance()->getClosestPlayer(*coordinates);
|
||||
closestPlayer->consume(points);
|
||||
setActive(false);
|
||||
return;
|
||||
}
|
||||
|
||||
GameObject::preRenderUpdate();
|
||||
}
|
||||
|
|
|
@ -4,11 +4,12 @@
|
|||
|
||||
#include "../game_object.h"
|
||||
#include "../player/player.hpp"
|
||||
#include "../../sprites/masked_sprite.hpp"
|
||||
|
||||
class Collectable : public GameObject
|
||||
{
|
||||
public:
|
||||
Collectable();
|
||||
Collectable(int points);
|
||||
|
||||
void setSprite(const std::string &spriteName);
|
||||
|
||||
|
@ -23,11 +24,15 @@ public:
|
|||
return collectableId;
|
||||
}
|
||||
|
||||
void preRenderUpdate() override;
|
||||
|
||||
private:
|
||||
int collectableId = 0;
|
||||
static inline int collectableCount = 0;
|
||||
std::shared_ptr<Sprite> sprite;
|
||||
|
||||
sf::Vector2f size;
|
||||
int points = 0;
|
||||
|
||||
std::shared_ptr<Player> consumedBy = nullptr;
|
||||
};
|
||||
|
|
|
@ -6,9 +6,10 @@
|
|||
struct CollectableConfig
|
||||
{
|
||||
std::string spriteName;
|
||||
int points = 0;
|
||||
|
||||
explicit CollectableConfig(std::string spriteName)
|
||||
: spriteName(std::move(spriteName))
|
||||
explicit CollectableConfig(std::string spriteName, int points)
|
||||
: spriteName(std::move(spriteName)), points(points)
|
||||
{}
|
||||
|
||||
CollectableConfig() = default;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
std::shared_ptr<Collectable> CollectableFactory::createFromInLevelConfig(const CollectableInLevel &config)
|
||||
{
|
||||
auto collectableConfig = config.collectableConfig;
|
||||
auto collectable = std::make_shared<Collectable>();
|
||||
auto collectable = std::make_shared<Collectable>(config.collectableConfig.points);
|
||||
|
||||
collectable->coordinates->setGrid(config.position);
|
||||
collectable->setSprite(collectableConfig.spriteName);
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
#include "collectables_depth_collection.hpp"
|
||||
#include "../../../logging/easylogging++.h"
|
||||
#include "../../../config.h"
|
||||
#include "../../physics/holes/holes_simulation.hpp"
|
||||
|
||||
std::shared_ptr<CollectablesCollection> CollectablesCollection::getInstance()
|
||||
{
|
||||
|
@ -28,8 +29,26 @@ void CollectablesCollection::createEmpty(int maxDepth)
|
|||
}
|
||||
}
|
||||
|
||||
void CollectablesCollection::remove(const std::shared_ptr<Collectable> &collectable)
|
||||
void CollectablesCollection::remove(int collectableId)
|
||||
{
|
||||
HolesSimulation::getInstance()->removeCollectable(collectableId);
|
||||
|
||||
std::shared_ptr<Collectable> collectable = nullptr;
|
||||
for (auto &child: getChildren())
|
||||
{
|
||||
collectable = std::dynamic_pointer_cast<Collectable>(child);
|
||||
if (collectable->getId() == collectableId)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (collectable == nullptr)
|
||||
{
|
||||
LOG(ERROR) << "No collectables left to remove.";
|
||||
return;
|
||||
}
|
||||
|
||||
depthCollections[collectable->getDepth()]->remove(collectable);
|
||||
removeChild(collectable);
|
||||
}
|
||||
|
@ -44,7 +63,21 @@ void CollectablesCollection::update()
|
|||
{
|
||||
GameObject::update();
|
||||
|
||||
if (!CONSIDER_COLLECTABLE_DEPTH_MOVEMENT) {
|
||||
// Remove inactive collectables
|
||||
auto collectables = getChildren();
|
||||
for (auto &child: collectables)
|
||||
{
|
||||
auto collectable = std::dynamic_pointer_cast<Collectable>(child);
|
||||
if (collectable->getActive())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
remove(collectable->getId());
|
||||
}
|
||||
|
||||
if (!CONSIDER_COLLECTABLE_DEPTH_MOVEMENT)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ public:
|
|||
void draw(sf::RenderWindow *window) override;
|
||||
|
||||
void add(const std::shared_ptr<Collectable>& collectable);
|
||||
void remove(const std::shared_ptr<Collectable>& collectable);
|
||||
void remove(int collectableId);
|
||||
|
||||
private:
|
||||
static inline std::shared_ptr<CollectablesCollection> singletonInstance = nullptr;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include "level/level_loader.hpp"
|
||||
#include "physics/map/map_simulation.hpp"
|
||||
#include "../logging/easylogging++.h"
|
||||
#include "layer/global_layer.hpp"
|
||||
|
||||
Game::Game(std::shared_ptr<sf::RenderWindow> window) : window(std::move(window))
|
||||
{
|
||||
|
@ -145,3 +146,9 @@ bool Game::isLevelLoaded() const
|
|||
{
|
||||
return loadedLevelConfig.isValid();
|
||||
}
|
||||
|
||||
void Game::startCountdown(int durationInSeconds)
|
||||
{
|
||||
countdown = std::make_shared<Countdown>(durationInSeconds);
|
||||
GlobalLayer::getInstance()->add(countdown);
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
#include "camera/tracking_view.h"
|
||||
#include "level/level_config.hpp"
|
||||
#include "frame_counter.hpp"
|
||||
#include "time/countdown.hpp"
|
||||
|
||||
class TrackingView;
|
||||
|
||||
|
@ -17,7 +18,9 @@ class Game
|
|||
{
|
||||
public:
|
||||
static std::shared_ptr<Game> constructInstance(const std::shared_ptr<sf::RenderWindow> &window);
|
||||
|
||||
static std::shared_ptr<Game> getInstance();
|
||||
|
||||
explicit Game(std::shared_ptr<sf::RenderWindow> window);
|
||||
|
||||
void run();
|
||||
|
@ -28,6 +31,8 @@ public:
|
|||
|
||||
void setLevel(LevelConfig levelConfig);
|
||||
|
||||
void startCountdown(int durationInSeconds);
|
||||
|
||||
[[nodiscard]] bool isLevelLoaded() const;
|
||||
|
||||
void addGameObject(const std::shared_ptr<GameObject> &gameObject);
|
||||
|
@ -41,6 +46,7 @@ private:
|
|||
LevelConfig loadedLevelConfig = {};
|
||||
|
||||
std::shared_ptr<FrameCounter> frameCounter = std::make_shared<FrameCounter>();
|
||||
std::shared_ptr<Countdown> countdown = std::make_shared<Countdown>();
|
||||
|
||||
void drawFrame();
|
||||
|
||||
|
|
39
src/game/layer/global_layer.cpp
Normal file
39
src/game/layer/global_layer.cpp
Normal file
|
@ -0,0 +1,39 @@
|
|||
#include "global_layer.hpp"
|
||||
|
||||
void GlobalLayer::clear()
|
||||
{
|
||||
clearChildren();
|
||||
addDetachedChild(view);
|
||||
}
|
||||
|
||||
std::shared_ptr<GlobalLayer> GlobalLayer::getInstance()
|
||||
{
|
||||
if (singletonInstance == nullptr)
|
||||
{
|
||||
singletonInstance = std::make_shared<GlobalLayer>();
|
||||
}
|
||||
return singletonInstance;
|
||||
}
|
||||
|
||||
void GlobalLayer::draw(sf::RenderWindow *window)
|
||||
{
|
||||
view->setViewForWindow();
|
||||
GameObject::draw(window);
|
||||
}
|
||||
|
||||
GlobalLayer::GlobalLayer()
|
||||
{
|
||||
// Reference screen size of 1920x1080
|
||||
TrackingViewOptions options = {
|
||||
.minViewSize = sf::Vector2f{10, 1080},
|
||||
.initialCenter = sf::Vector2f{1920, 1080} / 2.0f,
|
||||
};
|
||||
|
||||
view = std::make_shared<TrackingView>(options);
|
||||
addDetachedChild(view);
|
||||
}
|
||||
|
||||
void GlobalLayer::add(const std::shared_ptr<GameObject>& gameObject)
|
||||
{
|
||||
addDetachedChild(gameObject);
|
||||
}
|
27
src/game/layer/global_layer.hpp
Normal file
27
src/game/layer/global_layer.hpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
#ifndef HOLESOME_GLOBAL_LAYER_HPP
|
||||
#define HOLESOME_GLOBAL_LAYER_HPP
|
||||
|
||||
|
||||
#include "../game_object.h"
|
||||
#include "../camera/tracking_view.h"
|
||||
|
||||
class GlobalLayer : public GameObject
|
||||
{
|
||||
public:
|
||||
GlobalLayer();
|
||||
|
||||
static std::shared_ptr<GlobalLayer> getInstance();
|
||||
|
||||
void clear();
|
||||
|
||||
void draw(sf::RenderWindow *window) override;
|
||||
|
||||
void add(const std::shared_ptr<GameObject>& gameObject);
|
||||
|
||||
private:
|
||||
static inline std::shared_ptr<GlobalLayer> singletonInstance = nullptr;
|
||||
std::shared_ptr<TrackingView> view;
|
||||
};
|
||||
|
||||
|
||||
#endif //HOLESOME_GLOBAL_LAYER_HPP
|
|
@ -13,6 +13,7 @@
|
|||
struct LevelConfig
|
||||
{
|
||||
std::string name;
|
||||
int durationInSeconds = 0;
|
||||
sf::Vector2i worldMapSize = {};
|
||||
TileMapConfig tileMapConfig = {};
|
||||
std::vector<GridCoordinates> playerSpawnPoints = {};
|
||||
|
@ -20,11 +21,13 @@ struct LevelConfig
|
|||
std::vector<sf::Color> skyColors = {};
|
||||
|
||||
LevelConfig(std::string name,
|
||||
int durationInSeconds,
|
||||
const std::vector<GridCoordinates> &playerSpawnPoints,
|
||||
const std::vector<CollectableInLevel> &collectables,
|
||||
std::vector<sf::Color> skyColors,
|
||||
const TileMapConfig &tileMapConfig)
|
||||
: name(std::move(name)),
|
||||
durationInSeconds(durationInSeconds),
|
||||
playerSpawnPoints(playerSpawnPoints),
|
||||
tileMapConfig(tileMapConfig),
|
||||
skyColors(std::move(skyColors))
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "../camera/multiplayer_view.hpp"
|
||||
#include "../physics/holes/holes_simulation.hpp"
|
||||
#include "../physics/holes/layouts/hole_layout.hpp"
|
||||
#include "../layer/global_layer.hpp"
|
||||
|
||||
void LevelLoader::loadLevel(const LevelConfig &levelConfig)
|
||||
{
|
||||
|
@ -23,6 +24,7 @@ void LevelLoader::loadLevel(const LevelConfig &levelConfig)
|
|||
MapSimulation::getInstance()->resetMap(levelConfig.worldMapSize);
|
||||
HolesSimulation::getInstance()->clear();
|
||||
PlayerCollection::getInstance()->clear();
|
||||
GlobalLayer::getInstance()->clear();
|
||||
HoleLayout::getInstance()->clear();
|
||||
|
||||
// Add views
|
||||
|
@ -60,6 +62,10 @@ void LevelLoader::loadLevel(const LevelConfig &levelConfig)
|
|||
spawnCollectable(collectableInfo);
|
||||
}
|
||||
|
||||
game->startCountdown(levelConfig.durationInSeconds);
|
||||
|
||||
// Must be last
|
||||
game->addGameObject(GlobalLayer::getInstance());
|
||||
|
||||
LOG(INFO) << "Finished loading level '" << levelConfig.name << "'.";
|
||||
}
|
||||
|
|
|
@ -2,7 +2,8 @@
|
|||
|
||||
std::shared_ptr<HolesSimulation> HolesSimulation::getInstance()
|
||||
{
|
||||
if (singletonInstance == nullptr) {
|
||||
if (singletonInstance == nullptr)
|
||||
{
|
||||
singletonInstance = std::make_shared<HolesSimulation>();
|
||||
}
|
||||
|
||||
|
@ -13,7 +14,8 @@ std::vector<std::shared_ptr<CollectableSimulation>> HolesSimulation::getCollecta
|
|||
{
|
||||
std::vector<std::shared_ptr<CollectableSimulation>> collectableSimulations{};
|
||||
|
||||
for (auto &child : getChildren()) {
|
||||
for (auto &child: getChildren())
|
||||
{
|
||||
auto sim = std::dynamic_pointer_cast<CollectableSimulation>(child);
|
||||
collectableSimulations.push_back(sim);
|
||||
}
|
||||
|
@ -35,8 +37,10 @@ void HolesSimulation::addCollectable(const std::shared_ptr<Collectable> &collect
|
|||
void HolesSimulation::lateUpdate()
|
||||
{
|
||||
// Remove disabled collectables
|
||||
for (const auto& sim : getCollectableSimulations()) {
|
||||
if (sim->getCollectable()->getActive()) {
|
||||
for (const auto &sim: getCollectableSimulations())
|
||||
{
|
||||
if (sim->getCollectable()->getActive())
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -45,3 +49,15 @@ void HolesSimulation::lateUpdate()
|
|||
|
||||
GameObject::lateUpdate();
|
||||
}
|
||||
|
||||
void HolesSimulation::removeCollectable(int collectableId)
|
||||
{
|
||||
for (const auto &sim: getCollectableSimulations())
|
||||
{
|
||||
if (sim->getCollectable()->getId() == collectableId)
|
||||
{
|
||||
removeChild(sim);
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -13,6 +13,8 @@ public:
|
|||
|
||||
void addCollectable(const std::shared_ptr<Collectable> &collectable);
|
||||
|
||||
void removeCollectable(int collectableId);
|
||||
|
||||
void lateUpdate() override;
|
||||
|
||||
void clear();
|
||||
|
|
|
@ -14,7 +14,7 @@ Player::Player(std::shared_ptr<InputIdentity> assignedInput, const std::string &
|
|||
skinSprite = std::make_shared<VersatileSprite>(skinRessourceName, getIsoSize());
|
||||
addChildScreenOffset(skinSprite, IsometricCoordinates(-getIsoSize() / 2.f));
|
||||
|
||||
updateRadiusBasedOnLevel();
|
||||
updateRadiusBasedOnPoints();
|
||||
|
||||
LOG(INFO) << "Player " << playerId << " created.";
|
||||
}
|
||||
|
@ -41,19 +41,6 @@ void Player::update()
|
|||
auto moveDelta = moveDirection * speed * FRAME_TIME.asSeconds();
|
||||
coordinates->move(moveDelta);
|
||||
|
||||
if (input->isPerformingAction(GameAction::GROW))
|
||||
{
|
||||
points = (points + 1) * (1 + 1 * FRAME_TIME.asSeconds());
|
||||
} else if (input->isPerformingAction(GameAction::SHRINK))
|
||||
{
|
||||
points *= 1 - 1 * FRAME_TIME.asSeconds();
|
||||
if (points < 0) {
|
||||
points = 0;
|
||||
}
|
||||
}
|
||||
|
||||
updateRadiusBasedOnLevel();
|
||||
|
||||
GameObject::update();
|
||||
}
|
||||
|
||||
|
@ -101,10 +88,18 @@ long Player::getPoints() const
|
|||
return points;
|
||||
}
|
||||
|
||||
void Player::updateRadiusBasedOnLevel()
|
||||
void Player::updateRadiusBasedOnPoints()
|
||||
{
|
||||
long points = getPoints();
|
||||
float newWorldRadius = PLAYER_MIN_RADIUS + PLAYER_RADIUS_PER_LEVEL * pow(points / 100.f, 2);
|
||||
float newWorldRadius = PLAYER_MIN_RADIUS + PLAYER_RADIUS_PER_LEVEL * points / 10.f;
|
||||
|
||||
setWorldRadius(newWorldRadius);
|
||||
}
|
||||
|
||||
void Player::consume(int points)
|
||||
{
|
||||
this->points += points;
|
||||
LOG(INFO) << "Player " << playerId << " consumed " << points << " points. Total: " << this->points;
|
||||
|
||||
updateRadiusBasedOnPoints();
|
||||
}
|
||||
|
|
|
@ -31,6 +31,8 @@ public:
|
|||
|
||||
[[nodiscard]] long getPoints() const;
|
||||
|
||||
void consume(int points);
|
||||
|
||||
TranslatedCoordinates spawnPosition;
|
||||
private:
|
||||
std::shared_ptr<InputIdentity> input;
|
||||
|
@ -44,7 +46,7 @@ private:
|
|||
|
||||
void setWorldRadius(float newWorldRadius);
|
||||
|
||||
void updateRadiusBasedOnLevel();
|
||||
void updateRadiusBasedOnPoints();
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -144,3 +144,22 @@ void PlayerCollection::updateInputIdentityAllowance() const
|
|||
{
|
||||
InputMapper::getInstance()->allowNewInputIdentities = getPlayers().size() < maxPlayerCount;
|
||||
}
|
||||
|
||||
std::shared_ptr<Player> PlayerCollection::getClosestPlayer(const TranslatedCoordinates& point) const
|
||||
{
|
||||
std::shared_ptr<Player> closestPlayer = nullptr;
|
||||
float closestDistance = INFINITY;
|
||||
for (auto &player: getPlayers())
|
||||
{
|
||||
auto playerCenterGround = player->coordinates->world().toGroundCoordinates();
|
||||
auto pointCenterGround = point.world().toGroundCoordinates();
|
||||
// Normalize distance by player radius to get a value below 1 for something inside the player
|
||||
float distance = length(playerCenterGround - pointCenterGround) / player->getWorldRadius();
|
||||
if (distance < closestDistance)
|
||||
{
|
||||
closestPlayer = player;
|
||||
closestDistance = distance;
|
||||
}
|
||||
}
|
||||
return closestPlayer;
|
||||
}
|
||||
|
|
|
@ -18,6 +18,8 @@ public:
|
|||
|
||||
void removePlayer(const std::shared_ptr<Player>& player);
|
||||
|
||||
std::shared_ptr<Player> getClosestPlayer(const TranslatedCoordinates& point) const;
|
||||
|
||||
[[nodiscard]] std::vector<std::shared_ptr<Player>> getPlayers() const;
|
||||
|
||||
[[nodiscard]] std::shared_ptr<Player> getPlayerById(int playerId) const;
|
||||
|
|
70
src/game/time/countdown.cpp
Normal file
70
src/game/time/countdown.cpp
Normal file
|
@ -0,0 +1,70 @@
|
|||
#include <SFML/Graphics/Text.hpp>
|
||||
#include <iomanip>
|
||||
#include "countdown.hpp"
|
||||
#include "../../typography/font_manager.hpp"
|
||||
#include "../../config.h"
|
||||
|
||||
void Countdown::restart(int durationInSeconds)
|
||||
{
|
||||
this->durationInSeconds = durationInSeconds;
|
||||
timeElapsed = sf::Time::Zero;
|
||||
clock.restart();
|
||||
stopped = false;
|
||||
}
|
||||
|
||||
Countdown::Countdown(int durationInSeconds)
|
||||
{
|
||||
restart(durationInSeconds);
|
||||
}
|
||||
|
||||
void Countdown::stop()
|
||||
{
|
||||
stopped = true;
|
||||
}
|
||||
|
||||
bool Countdown::isFinished() const
|
||||
{
|
||||
return timeElapsed.asSeconds() >= durationInSeconds;
|
||||
}
|
||||
|
||||
void Countdown::update()
|
||||
{
|
||||
GameObject::update();
|
||||
|
||||
if (stopped)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
timeElapsed += clock.restart();
|
||||
}
|
||||
|
||||
void Countdown::draw(sf::RenderWindow *window)
|
||||
{
|
||||
GameObject::draw(window);
|
||||
|
||||
// Draw the countdown
|
||||
int timeLeft = durationInSeconds - timeElapsed.asSeconds();
|
||||
if (timeLeft <= 0)
|
||||
{
|
||||
timeLeft = 0;
|
||||
}
|
||||
|
||||
int minutes = timeLeft / 60;
|
||||
int seconds = timeLeft % 60;
|
||||
std::stringstream ss;
|
||||
ss << std::setfill('0') << std::setw(2) << minutes << ":" << std::setw(2) << seconds;
|
||||
auto timeLeftString = ss.str();
|
||||
|
||||
|
||||
auto font = FontManager::getInstance()->getDefaultFont();
|
||||
auto text = sf::Text(timeLeftString, *font, COUNTDOWN_FONT_SIZE);
|
||||
text.setFillColor(sf::Color::White);
|
||||
text.setPosition(1920 / 2.f, 5);
|
||||
|
||||
// Align top center
|
||||
sf::FloatRect textRect = text.getLocalBounds();
|
||||
text.setOrigin(textRect.left + textRect.width / 2.0f,
|
||||
textRect.top);
|
||||
window->draw(text);
|
||||
}
|
31
src/game/time/countdown.hpp
Normal file
31
src/game/time/countdown.hpp
Normal file
|
@ -0,0 +1,31 @@
|
|||
#ifndef HOLESOME_COUNTDOWN_HPP
|
||||
#define HOLESOME_COUNTDOWN_HPP
|
||||
|
||||
|
||||
#include <SFML/System/Clock.hpp>
|
||||
#include "../game_object.h"
|
||||
|
||||
class Countdown : public GameObject
|
||||
{
|
||||
public:
|
||||
explicit Countdown(int durationInSeconds = 0);
|
||||
|
||||
void update() override;
|
||||
|
||||
void restart(int durationInSeconds);
|
||||
|
||||
void stop();
|
||||
|
||||
[[nodiscard]] bool isFinished() const;
|
||||
|
||||
void draw(sf::RenderWindow *window) override;
|
||||
|
||||
private:
|
||||
sf::Clock clock;
|
||||
int durationInSeconds = 0;
|
||||
sf::Time timeElapsed = sf::Time::Zero;
|
||||
bool stopped = false;
|
||||
};
|
||||
|
||||
|
||||
#endif //HOLESOME_COUNTDOWN_HPP
|
|
@ -8,21 +8,25 @@
|
|||
#define INITIAL_LEVEL "default"
|
||||
|
||||
std::map<std::string, LevelConfig> const all_levels = {
|
||||
{"default", LevelConfig("Default", {{0, 0},
|
||||
{"default", LevelConfig("Default",
|
||||
30,
|
||||
{
|
||||
{0, 0},
|
||||
{18, 18},
|
||||
{0, 18},
|
||||
{18, 0}}, {
|
||||
CollectableInLevel("box", {3, 5}),
|
||||
CollectableInLevel("box", {4, 5}),
|
||||
CollectableInLevel("box", {10, 6}),
|
||||
CollectableInLevel("box", {2, 8}),
|
||||
CollectableInLevel("box", {1, 2}),
|
||||
CollectableInLevel("box", {4, 3}),
|
||||
CollectableInLevel("box", {8, 3}),
|
||||
CollectableInLevel("box", {6, 7}),
|
||||
CollectableInLevel("box", {5, 5}),
|
||||
CollectableInLevel("box", {9, 5}),
|
||||
CollectableInLevel("box", {0, 1})
|
||||
{18, 0}
|
||||
}, {
|
||||
CollectableInLevel("rose", {3, 5}),
|
||||
CollectableInLevel("rosebush", {4, 5}),
|
||||
CollectableInLevel("stone", {10, 6}),
|
||||
CollectableInLevel("bike", {2, 8}),
|
||||
CollectableInLevel("rose", {1, 2}),
|
||||
CollectableInLevel("small-tree", {4, 3}),
|
||||
CollectableInLevel("rose", {8, 3}),
|
||||
CollectableInLevel("rose", {6, 7}),
|
||||
CollectableInLevel("rose", {5, 5}),
|
||||
CollectableInLevel("tram", {9, 5}),
|
||||
CollectableInLevel("rose", {0, 1})
|
||||
},
|
||||
{
|
||||
// Blues
|
||||
|
@ -40,7 +44,8 @@ std::map<std::string, LevelConfig> const all_levels = {
|
|||
sf::Color(20, 18, 11),
|
||||
sf::Color::Black
|
||||
},
|
||||
TileMapConfig("iso-tiles", {{4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4},
|
||||
TileMapConfig("iso-tiles", {
|
||||
{4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4},
|
||||
{4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4},
|
||||
{4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4},
|
||||
{4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4},
|
||||
|
@ -58,7 +63,8 @@ std::map<std::string, LevelConfig> const all_levels = {
|
|||
{4, 0, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4},
|
||||
{4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4},
|
||||
{4, 1, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4},
|
||||
{4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}})
|
||||
{4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4, 4}
|
||||
})
|
||||
)}
|
||||
};
|
||||
|
||||
|
|
14
src/main.cpp
14
src/main.cpp
|
@ -4,11 +4,14 @@
|
|||
#include "texture_config.h"
|
||||
#include "game/level/level_loader.hpp"
|
||||
#include "levels.hpp"
|
||||
#include "typography/font_manager.hpp"
|
||||
|
||||
void loadAllTextures();
|
||||
|
||||
void runGame();
|
||||
|
||||
void loadAllFonts();
|
||||
|
||||
INITIALIZE_EASYLOGGINGPP
|
||||
|
||||
int main(int argc, char *argv[])
|
||||
|
@ -16,9 +19,20 @@ int main(int argc, char *argv[])
|
|||
START_EASYLOGGINGPP(argc, argv);
|
||||
|
||||
loadAllTextures();
|
||||
loadAllFonts();
|
||||
runGame();
|
||||
}
|
||||
|
||||
void loadAllFonts()
|
||||
{
|
||||
LOG(INFO) << "Loading fonts...";
|
||||
for (auto const &[key, path]: all_fonts)
|
||||
{
|
||||
FontManager::getInstance()->loadFont(key, path);
|
||||
}
|
||||
LOG(INFO) << "Finished loading fonts.";
|
||||
}
|
||||
|
||||
void runGame()
|
||||
{
|
||||
LOG(INFO) << "Starting game ...";
|
||||
|
|
|
@ -77,3 +77,8 @@ void AnimatedSprite::preRenderUpdate()
|
|||
|
||||
renderPosition = calculateRotatedCornerPosition(position, angle);
|
||||
}
|
||||
|
||||
bool AnimatedSprite::isVisible() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,8 @@ public:
|
|||
|
||||
void preRenderUpdate() override;
|
||||
|
||||
bool isVisible() const override;
|
||||
|
||||
void setRotation(float angle) override;
|
||||
|
||||
private:
|
||||
|
|
|
@ -24,7 +24,15 @@ void MaskedSprite::preRenderUpdate()
|
|||
{
|
||||
GameObject::preRenderUpdate();
|
||||
|
||||
// TODO: Did anything change? Is update of masked sprite necessary?
|
||||
if (isHidden)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (previousAngle == angle && previousRenderPosition == renderPosition)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (angle == 0)
|
||||
{
|
||||
|
@ -38,6 +46,14 @@ void MaskedSprite::preRenderUpdate()
|
|||
sprite->setRotation(angle);
|
||||
|
||||
updateFreshMaskedSprite();
|
||||
|
||||
previousAngle = angle;
|
||||
previousRenderPosition = renderPosition;
|
||||
|
||||
if (isHidden)
|
||||
{
|
||||
LOG(INFO) << "Masked sprite has been permanently hidden";
|
||||
}
|
||||
}
|
||||
|
||||
void MaskedSprite::draw(sf::RenderWindow *window)
|
||||
|
@ -49,14 +65,12 @@ void MaskedSprite::draw(sf::RenderWindow *window)
|
|||
|
||||
void MaskedSprite::updateFreshMaskedSprite()
|
||||
{
|
||||
// TODO: Calculate depth per pixel
|
||||
auto maskedImage = std::make_shared<sf::Image>();
|
||||
maskedImage->create(textureRect.width, textureRect.height, sf::Color::Transparent);
|
||||
|
||||
// todo: or use sf::RenderTexture?
|
||||
|
||||
maskedImage->copy(*image, 0, 0);
|
||||
|
||||
isHidden = true;
|
||||
|
||||
// Calculate world coordinates for each pixel
|
||||
auto coordinatesTopLeftCorner = TranslatedCoordinates(
|
||||
IsometricCoordinates(renderPosition.x, renderPosition.y, coordinates->isometric().depth));
|
||||
|
@ -77,6 +91,7 @@ void MaskedSprite::updateFreshMaskedSprite()
|
|||
topLeftCorner + xAxis * (xOffset * xFactorPerPixel) + yAxis * (yOffset * yFactorPerPixel);
|
||||
if (pixelPosition.y >= 0)
|
||||
{
|
||||
isHidden = false;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -124,6 +139,7 @@ sf::Color MaskedSprite::calculateNewPixelColor(sf::Color currentColor, sf::Vecto
|
|||
if (position.y >= 0)
|
||||
{
|
||||
// Pixel is above ground
|
||||
isHidden = false;
|
||||
return currentColor;
|
||||
}
|
||||
|
||||
|
@ -138,6 +154,7 @@ sf::Color MaskedSprite::calculateNewPixelColor(sf::Color currentColor, sf::Vecto
|
|||
auto players = PlayerCollection::getInstance()->getPlayers();
|
||||
if (players.empty())
|
||||
{
|
||||
isHidden = false;
|
||||
return currentColor;
|
||||
}
|
||||
|
||||
|
@ -153,6 +170,16 @@ sf::Color MaskedSprite::calculateNewPixelColor(sf::Color currentColor, sf::Vecto
|
|||
}
|
||||
|
||||
float biggestHoleAlphaFactor = *std::max_element(holeAlphaFactors.begin(), holeAlphaFactors.end());
|
||||
if (biggestHoleAlphaFactor > 0)
|
||||
{
|
||||
isHidden = false;
|
||||
}
|
||||
|
||||
currentColor.a *= biggestHoleAlphaFactor;
|
||||
return currentColor;
|
||||
}
|
||||
|
||||
bool MaskedSprite::isVisible() const
|
||||
{
|
||||
return !isHidden;
|
||||
}
|
||||
|
|
|
@ -27,6 +27,8 @@ public:
|
|||
|
||||
void setRotation(float angle) override;
|
||||
|
||||
bool isVisible() const override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<sf::Sprite> sprite;
|
||||
std::shared_ptr<sf::Texture> maskedTexture;
|
||||
|
@ -34,7 +36,11 @@ private:
|
|||
std::shared_ptr<sf::Image> image;
|
||||
sf::IntRect textureRect;
|
||||
float angle = 0;
|
||||
float previousAngle = -10;
|
||||
sf::Vector2f renderPosition;
|
||||
sf::Vector2f previousRenderPosition = sf::Vector2f(-10, 0);
|
||||
|
||||
bool isHidden = false;
|
||||
|
||||
void updateFreshMaskedSprite();
|
||||
|
||||
|
|
|
@ -61,3 +61,8 @@ void SingleSprite::preRenderUpdate()
|
|||
|
||||
renderPosition = calculateRotatedCornerPosition(position, angle);
|
||||
}
|
||||
|
||||
bool SingleSprite::isVisible() const
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
|
|
@ -25,6 +25,8 @@ public:
|
|||
|
||||
void preRenderUpdate() override;
|
||||
|
||||
bool isVisible() const override;
|
||||
|
||||
private:
|
||||
sf::Sprite sprite;
|
||||
float angle = 0;
|
||||
|
|
|
@ -25,6 +25,11 @@ public:
|
|||
throw std::runtime_error("Sprite::getSprite() not implemented");
|
||||
}
|
||||
|
||||
[[nodiscard]] virtual bool isVisible() const
|
||||
{
|
||||
throw std::runtime_error("Sprite::isVisible() not implemented");
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates a sprite around its center.
|
||||
* @param angle [0, 360]
|
||||
|
|
|
@ -46,3 +46,8 @@ void VersatileSprite::setRotation(float angle)
|
|||
{
|
||||
sprite->setRotation(angle);
|
||||
}
|
||||
|
||||
bool VersatileSprite::isVisible() const
|
||||
{
|
||||
return sprite->isVisible();
|
||||
}
|
||||
|
|
|
@ -22,6 +22,8 @@ public:
|
|||
|
||||
void setRotation(float angle) override;
|
||||
|
||||
bool isVisible() const override;
|
||||
|
||||
private:
|
||||
std::shared_ptr<Sprite> sprite = nullptr;
|
||||
};
|
||||
|
|
37
src/typography/font_manager.cpp
Normal file
37
src/typography/font_manager.cpp
Normal file
|
@ -0,0 +1,37 @@
|
|||
#include "font_manager.hpp"
|
||||
#include "../config.h"
|
||||
|
||||
std::shared_ptr<FontManager> FontManager::getInstance()
|
||||
{
|
||||
if (singletonInstance == nullptr)
|
||||
{
|
||||
singletonInstance = std::make_shared<FontManager>();
|
||||
}
|
||||
return singletonInstance;
|
||||
}
|
||||
|
||||
void FontManager::loadFont(const std::string &name, const std::string &fontPath)
|
||||
{
|
||||
auto font = std::make_shared<sf::Font>();
|
||||
auto fullPath = FONT_BASE_PATH + fontPath;
|
||||
if (!font->loadFromFile(fullPath))
|
||||
{
|
||||
throw std::runtime_error("Failed to load font: " + fullPath);
|
||||
}
|
||||
|
||||
fonts[name] = font;
|
||||
}
|
||||
|
||||
std::shared_ptr<sf::Font> FontManager::getFont(const std::string &name)
|
||||
{
|
||||
if (fonts.find(name) == fonts.end())
|
||||
{
|
||||
throw std::runtime_error("Font not found: " + name);
|
||||
}
|
||||
return fonts[name];
|
||||
}
|
||||
|
||||
std::shared_ptr<sf::Font> FontManager::getDefaultFont()
|
||||
{
|
||||
return getFont(DEFAULT_FONT);
|
||||
}
|
27
src/typography/font_manager.hpp
Normal file
27
src/typography/font_manager.hpp
Normal file
|
@ -0,0 +1,27 @@
|
|||
#ifndef HOLESOME_FONT_MANAGER_HPP
|
||||
#define HOLESOME_FONT_MANAGER_HPP
|
||||
|
||||
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include <SFML/Graphics/Font.hpp>
|
||||
|
||||
class FontManager
|
||||
{
|
||||
public:
|
||||
static std::shared_ptr<FontManager> getInstance();
|
||||
|
||||
void loadFont(const std::string &name, const std::string &fontPath);
|
||||
|
||||
std::shared_ptr<sf::Font> getFont(const std::string &name);
|
||||
|
||||
std::shared_ptr<sf::Font> getDefaultFont();
|
||||
|
||||
private:
|
||||
static inline std::shared_ptr<FontManager> singletonInstance = nullptr;
|
||||
|
||||
std::map<std::string, std::shared_ptr<sf::Font>> fonts;
|
||||
};
|
||||
|
||||
|
||||
#endif //HOLESOME_FONT_MANAGER_HPP
|
Loading…
Reference in a new issue