Compare commits

..

1 commit

Author SHA1 Message Date
9dc26a6b06 Yikes 2023-06-14 01:06:11 +02:00
136 changed files with 685 additions and 4432 deletions

1
.gitignore vendored
View file

@ -1,3 +1,2 @@
/build/
/build-release/
/.idea/

View file

@ -29,6 +29,8 @@ set(SOURCES
src/coordinates/translated_coordinates.cpp
src/coordinates/translated_coordinates.h
src/coordinates/coordinates.h
src/game/input_handler.cpp
src/game/input_handler.h
src/primitives/circle_object.cpp
src/primitives/circle_object.h
src/game/game_factory.cpp
@ -87,86 +89,25 @@ set(SOURCES
src/game/level/level_loader.hpp
src/levels.hpp src/collectables.hpp
src/game/collectables/collectable_config.hpp
src/game/physics/hole/hole_depth_simulation.cpp
src/game/physics/hole/hole_depth_simulation.hpp
src/game/collectables/collection/collectables_collection.cpp
src/game/collectables/collection/collectables_collection.hpp
src/game/collectables/collection/collectables_depth_collection.cpp
src/game/collectables/collection/collectables_depth_collection.hpp
src/game/collectables/collectable_in_level.hpp
src/game/collectables/collectable_factory.cpp
src/game/collectables/collectable_factory.hpp
src/game/player/player_collection.cpp
src/game/player/player_collection.hpp
src/levels.hpp
src/sprites/tiling/tilemap.cpp
src/sprites/tiling/tilemap.hpp
src/sprites/tiling/tilemap_config.hpp
src/sprites/tiling/tileset_config.hpp
src/sprites/tiling/tileset.cpp
src/sprites/tiling/tileset.hpp
src/game/frame_counter.cpp
src/game/frame_counter.hpp
src/game/level/level_renderer.cpp
src/game/level/level_renderer.hpp
src/sprites/skymap/skymap.cpp
src/sprites/skymap/skymap.hpp
src/game/camera/multiplayer_view.cpp
src/game/camera/multiplayer_view.hpp
src/game/camera/multiplayer_borders.cpp
src/game/camera/multiplayer_borders.hpp
src/game/physics/holes/collectable_simulation.cpp
src/game/physics/holes/collectable_simulation.hpp
src/game/physics/holes/holes_simulation.cpp
src/game/physics/holes/holes_simulation.hpp
src/game/physics/holes/layouts/hole_description.hpp
src/game/physics/holes/layouts/hole_layout.cpp
src/game/physics/holes/layouts/hole_layout.hpp
src/game/physics/holes/layouts/depth_hole_layout.hpp
src/game/physics/holes/layouts/depth_hole_description.hpp
src/game/physics/holes/ground/collectable_sim_ground_segment.hpp
src/game/physics/holes/ground/collectable_sim_ground.cpp
src/game/physics/holes/ground/collectable_sim_ground.hpp
src/game/physics/body_adapter.cpp
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/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
src/screens/winner_screen.cpp
src/screens/winner_screen.hpp src/screens/screen.cpp src/screens/screen.hpp src/screens/join_screen.cpp src/screens/join_screen.hpp src/game/level/level_generator.cpp src/game/level/level_generator.hpp src/screens/loading_screen.cpp src/screens/loading_screen.hpp)
src/game/collectables/collection/collectables_depth_collection.hpp src/game/collectables/collectable_in_level.hpp src/game/collectables/collectable_factory.cpp src/game/collectables/collectable_factory.hpp src/game/player/player_collection.cpp src/game/player/player_collection.hpp src/game/physics/hole/hole_sim_player.cpp src/game/physics/hole/hole_sim_player.hpp src/game/collectables/collectable_sim_parameters.hpp src/game/collectables/sim_collectable.hpp)
set(PHYSICS_00_SOURCES
src/prototypes/physics_00.cpp)
src/prototypes/physics_00.cpp)
set(MATH_00_SOURCES
src/prototypes/math_00.cpp)
set(COORDINATES_00_SOURCES
src/prototypes/coordinates_00.cpp
src/coordinates/coordinates.h
src/coordinates/coordinate_transformer.h
src/coordinates/coordinate_transformer.cpp
src/coordinates/translated_coordinates.h
src/coordinates/translated_coordinates.cpp)
set(MINIMAP_00_SOURCES
src/prototypes/minimap_00.cpp)
# Add an executable target
add_executable(Holesome ${SOURCES})
add_executable(Physics_00 ${PHYSICS_00_SOURCES})
add_executable(Math_00 ${MATH_00_SOURCES})
add_executable(Coordinates_00 ${COORDINATES_00_SOURCES})
add_executable(Minimap_00 ${MINIMAP_00_SOURCES})
# Link SFML and other libraries to your executable target
target_link_libraries(Holesome sfml-graphics sfml-audio)
target_link_libraries(Holesome Eigen3::Eigen)
@ -176,10 +117,6 @@ target_link_libraries(Physics_00 box2d::box2d)
target_link_libraries(Math_00 Eigen3::Eigen)
target_link_libraries(Coordinates_00 Eigen3::Eigen)
target_link_libraries(Minimap_00 sfml-graphics sfml-audio)
# Assets
add_custom_target(copy_assets
COMMAND ${CMAKE_COMMAND} -E copy_directory ${CMAKE_CURRENT_LIST_DIR}/assets ${CMAKE_CURRENT_BINARY_DIR}/assets

View file

@ -8,7 +8,7 @@ Maximilian Giller,
## What is this Game about?
Holesome is about currentLayoutHoles! But not the kind you have seen before ...
Holesome is about holes! But not the kind you have seen before ...
Gameplay:
- Navigate the environment as a hole
@ -33,7 +33,7 @@ The player controls a hole and has to consume as many objects as possible to gro
## Which components are included?
- **Physics**: Core gameplay element to make the objects fall into the currentLayoutHoles in a fun way
- **Physics**: Core gameplay element to make the objects fall into the holes in a fun way
- **Controller Support**: Use a controller to control the hole
- **Local Multiplayer**: Play with up to 4 players on one device using split screen
- **Level Files**: Levels are stored using a simple file format
@ -43,7 +43,7 @@ The player controls a hole and has to consume as many objects as possible to gro
Potential expansions:
- **Multithreading**: Improving performance by running the phsics in a separat thread and similar concepts
- **AI**: Some currentLayoutHoles could be controlled by AI, which makes singleplayer games more exciting
- **AI**: Some holes could be controlled by AI, which makes singleplayer games more exciting
- **Online Multiplayer**: Play with friends online
## Project Setup
@ -68,10 +68,3 @@ In case of "RandR headers were not found": `sudo apt install xorg-dev`.
cmake --build .
cmake --build . --target INSTALL
```
## Project structure
- Physics Simulations
- **Map Simulation**: World-bounds collider
- **Holes Simulation**: Creates hole physics by managing all the individual Collectable Simulations
- **Collectable Simulation**: Simulates physics for a single collectable

27
TODO.md
View file

@ -1,28 +1,3 @@
# Holesome - ToDos
## Next
- Physics
- Damage other players on contact
## Bugs
- Player disconnect in multiplayer sometimes targeting disconnected player or something like that
## High priority
- Players-join-screen before the game starts
- Short input delay when a new identity connects, to avoid accidental inputs at beginning
- Procedural points generation
- Game over screen
- Proper player graphics
- Collectables with graphics
- In-View-Detection for rendering more efficiently
## Low priority
- Score
- Player actions like shrinking hole or throwing bomb
- Menu
- Sound
- [ ] Optimize hole depth simulations by not simulating when nothing happens

Binary file not shown.

Before

Width:  |  Height:  |  Size: 849 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 630 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 517 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 578 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 835 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 617 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 967 B

BIN
assets/edge.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 19 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 18 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.4 KiB

View file

@ -6,13 +6,7 @@
#include "game/collectables/collectable_config.hpp"
std::map<std::string, CollectableConfig> const all_collectables = {
{"rose", CollectableConfig("rose", 1)},
{"rosebush", CollectableConfig("rosebush", 3)},
{"stone", CollectableConfig("stone", 5)},
{"bike", CollectableConfig("bike", 15)},
{"small-tree", CollectableConfig("small-tree", 20)},
{"tram", CollectableConfig("tram", 50)},
{"lantern", CollectableConfig("lantern", 10)}
{"box", CollectableConfig("numbers")}
};
#endif //HOLESOME_COLLECTABLES_HPP

View file

@ -4,89 +4,56 @@
#include <SFML/Graphics.hpp>
#include <box2d/box2d.h>
#define DEVELOPER_MODE false
#define GAME_NAME "Holesome"
#define CLOSE_GAME_BTN sf::Keyboard::Escape
#define DEVELOPER_MODE true
// Player
#define DEFAULT_PLAYER_SPEED 5.f // World units per second
#define DEFAULT_PLAYER_POINTS 0
#define PLAYER_MIN_RADIUS 0.5f // World units
#define PLAYER_RADIUS_PER_LEVEL 0.25f
#define DEFAULT_MAX_PLAYER_NUMBER 4
#define MIN_PLAYER_COUNT 2
#define POINT_DELTA_FOR_ATTACK 20
#define ATTACK_PER_SECOND 1.f
#define PLAYER_ALIVE_THRESHOLD (-10)
#define PLAYER_RUN_SPEED (DEFAULT_PLAYER_SPEED * 2.f)
#define PLAYER_RUN_SPEED_COST_PER_SECOND 10.f
#define DEFAULT_PLAYER_RADIUS .5f // In World units
#define PLAYER_PROPORTIONAL_SIZE_CHANGE_SPEED 0.4f
// World
#define WORLD_GRAVITY b2Vec2(0.f, -9.8f)
#define SKY_HEIGHT_SCALE 5.f
#define CONSIDER_COLLECTABLE_DEPTH_MOVEMENT false // Might cost performance and is currently not in use
#define WORLD_GRAVITY b2Vec2(0.f, 9.8f)
// FPS
#define FRAME_RATE 60
#define FRAME_TIME sf::Time(sf::seconds(1.0f / FRAME_RATE))
#define FRAME_LIMIT_ENABLED true
// Window settings
#define ANTIALIASINGLEVEL 8
#define KEY_REPEAT_ENABLED false
#define REFERENCE_SIZE sf::Vector2f(1920, 1080)
// Graphic settings
#define ISOMETRIC_SKEW (16.f/32.f)
#define ISOMETRIC_SKEW 0.3f
#define MOVEMENT_SKEW sf::Vector2f(1.f, 1/ISOMETRIC_SKEW/2.f)
#define WORLD_TO_ISO_SCALE 50.0f // 50.f, don't change. Rather adjust the zoom of the camera
#define MASKED_HOLE_BORDER_TRANSITION_SIZE 0.2f
#define MASKED_HOLE_DARKNESS_LIMIT 0.5f
#define COLLECTABLE_SCALE 5.f
// Tracking view defaults
#define DEF_TV_IS_ABSOLUTE_FREE_MOVE_THRESHOLD false
#define DEF_TV_FREE_MOVE_THRESHOLD 0.f
#define DEF_TV_SOFT_FOLLOW_SPEED 2.5f
#define DEF_TV_SOFT_RESIZE_SPEED 5.f
#define DEF_TV_MIN_VIEW_SIZE sf::Vector2f(6, 6) * WORLD_TO_ISO_SCALE
#define DEF_TV_MAX_VIEW_SIZE sf::Vector2f(0, 0)
#define DEF_TV_IS_ABSOLUTE_VIEW_SIZE_PADDING false
#define DEF_TV_VIEW_SIZE_PADDING sf::Vector2f(2.f, 2.f)
#define MP_VIEW_ADD_NEW_PLAYERS true
#define MP_VIEW_BORDER_COLOR sf::Color::Black
#define DEF_TV_VIEW_SIZE_PADDING sf::Vector2f(0.5f, 0.5f)
#define DEF_TV_ADD_PLAYERS_DYNAMICALLY true
// Simulations
#define MAPSIM_WALL_THICKNESS 3.f
#define MAPSIM_VELOCITY_ITERATIONS 6
#define MAPSIM_POSITION_ITERATIONS 2
#define COLLECTABLES_SIM_VELOCITY_ITERATIONS 8
#define COLLECTABLES_SIM_POSITION_ITERATIONS 3
#define COLLECTABLES_SIM_LINEAR_DAMPING 0.5f
#define COLLECTABLES_SIM_ANGULAR_DAMPING 0.5f
#define COLLECTABLES_SIM_DENSITY 3.f
#define COLLECTABLES_SIM_FRICTION 0.0f
#define COLLECTABLES_SIM_RESTITUTION 0.5f
#define COLLECTABLES_SIM_SLEEPING true
#define COLLECTABLES_SIM_GROUND_THICKNESS 0.1f
#define HOLESIM_VELOCITY_ITERATIONS 8
#define HOLESIM_POSITION_ITERATIONS 3
#define HOLESIM_COLLECTABLE_LINEAR_DAMPING 0.5f
#define HOLESIM_COLLECTABLE_ANGULAR_DAMPING 0.5f
#define HOLESIM_COLLECTABLE_DENSITY 1.f
#define HOLESIM_COLLECTABLE_FRICTION 0.3f
#define HOLESIM_COLLECTABLE_RESTITUTION 0.5f
#define HOLESIM_FLOOR_THICKNESS 0.5f
#define HOLESIM_WALL_HEIGHT 5.f
// Directions
#define DIRECTION_HARD_ACTIVATION_THRESHOLD 0.1f
// DEBUG
#define DB_CIRCLE_RADIUS 1
#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

View file

@ -1,23 +1,12 @@
#include "coordinate_transformer.h"
#include "../config.h"
#include "../logging/easylogging++.h"
#include "../utilities/vector_utils.hpp"
#include <cmath>
// Initialize matrix
const Eigen::Matrix<float, 3, 3> CoordinateTransformer::worldToIsometricMatrix =
(Eigen::Matrix<float, 3, 3>() <<
WORLD_TO_ISO_SCALE, -WORLD_TO_ISO_SCALE, 0,
-ISOMETRIC_SKEW * WORLD_TO_ISO_SCALE, -ISOMETRIC_SKEW * WORLD_TO_ISO_SCALE, -WORLD_TO_ISO_SCALE,
1, 1, 0
).finished();
// Same as above, but with SKEW = 0 and height positive
const auto rotateFactor = cos(M_PI / 4);
const Eigen::Matrix<float, 3, 3> CoordinateTransformer::worldToDiagonalMatrix =
(Eigen::Matrix<float, 3, 3>() <<
rotateFactor, -rotateFactor, 0,
0, 0, 1,
1, -1, 0,
-ISOMETRIC_SKEW, -ISOMETRIC_SKEW, -1,
1, 1, 0
).finished();
@ -28,64 +17,19 @@ IsometricCoordinates CoordinateTransformer::worldToIsometric(WorldCoordinates wo
Eigen::Vector3f isoCoordinatesVector = worldToIsometricMatrix * worldCoordinatesVector;
return {
isoCoordinatesVector.x(), // x
isoCoordinatesVector.y(), // y
return IsometricCoordinates(
isoCoordinatesVector.x() * WORLD_TO_ISO_SCALE, // x
isoCoordinatesVector.y() * WORLD_TO_ISO_SCALE, // y
isoCoordinatesVector.z() // depth
};
);
}
WorldCoordinates CoordinateTransformer::isometricToWorld(IsometricCoordinates isometricCoordinates)
{
Eigen::Vector3f isoCoordinatesVector;
isoCoordinatesVector << isometricCoordinates.x,
isometricCoordinates.y,
isometricCoordinates.depth;
Eigen::Vector3f isoCoordinatesVector;
isoCoordinatesVector << isometricCoordinates.x, isometricCoordinates.y, isometricCoordinates.depth;
Eigen::Vector3f worldCoordinatesVector = worldToIsometricMatrix.inverse() * isoCoordinatesVector;
return {
worldCoordinatesVector.x(), // x
worldCoordinatesVector.y(), // y
worldCoordinatesVector.z() // z
};
}
float CoordinateTransformer::worldToDepth(sf::Vector2f worldGroundCoordinates)
{
return CoordinateTransformer::worldToIsometric({worldGroundCoordinates.x, worldGroundCoordinates.y, 0}).depth;
}
sf::Vector2f CoordinateTransformer::closestWorldPointAtDepth(float depth, sf::Vector2f referenceWorldPoint)
{
float depthAtReferencePoint = worldToDepth(referenceWorldPoint);
float depthDifference = depth - depthAtReferencePoint;
float stepSizePerAxis = depthDifference / 2.f;
return referenceWorldPoint + sf::Vector2f(stepSizePerAxis, stepSizePerAxis);
}
DiagonalWorldCoordinates CoordinateTransformer::worldToDiagonal(WorldCoordinates worldCoordinates)
{
Eigen::Vector3f worldCoordinatesVector;
worldCoordinatesVector << worldCoordinates.x, worldCoordinates.y, worldCoordinates.z;
Eigen::Vector3f diagonalCoordinateVector = worldToDiagonalMatrix * worldCoordinatesVector;
return {
diagonalCoordinateVector.x(), // horizontal
diagonalCoordinateVector.y(), // vertical
diagonalCoordinateVector.z() // depth
};
}
WorldCoordinates CoordinateTransformer::diagonalToWorld(DiagonalWorldCoordinates diagonalWorldCoordinates)
{
Eigen::Vector3f diagonalCoordinateVector;
diagonalCoordinateVector << diagonalWorldCoordinates.horizontal,
diagonalWorldCoordinates.vertical,
diagonalWorldCoordinates.depth;
Eigen::Vector3f worldCoordinatesVector = worldToDiagonalMatrix.inverse() * diagonalCoordinateVector;
Eigen::Vector3f worldCoordinatesVector = worldToIsometricMatrix.inverse() * isoCoordinatesVector / WORLD_TO_ISO_SCALE;
return {
worldCoordinatesVector.x(), // x

View file

@ -8,15 +8,10 @@ class CoordinateTransformer
{
private:
static const Eigen::Matrix<float, 3, 3> worldToIsometricMatrix;
static const Eigen::Matrix<float, 3, 3> worldToDiagonalMatrix;
public:
static IsometricCoordinates worldToIsometric(WorldCoordinates worldCoordinates);
static WorldCoordinates isometricToWorld(IsometricCoordinates isometricCoordinates);
static DiagonalWorldCoordinates worldToDiagonal(WorldCoordinates worldCoordinates);
static WorldCoordinates diagonalToWorld(DiagonalWorldCoordinates diagonalWorldCoordinates);
static float worldToDepth(sf::Vector2f worldGroundCoordinates);
static sf::Vector2f closestWorldPointAtDepth(float depth, sf::Vector2f referenceWorldPoint);
};

View file

@ -2,9 +2,6 @@
#define HOLESOME_COORDINATES_H
#include <SFML/System/Vector2.hpp>
#include "../utilities/vector_utils.hpp"
class DiagonalWorldCoordinates;
struct WorldCoordinates
{
@ -13,7 +10,7 @@ struct WorldCoordinates
WorldCoordinates(float x, float y, float z = 0) : x(x), y(y), z(z)
{}
explicit WorldCoordinates(sf::Vector2f vector) : x(vector.x), y(vector.y), z(0)
WorldCoordinates(sf::Vector2f vector) : x(vector.x), y(vector.y), z(0)
{}
float x;
@ -57,11 +54,6 @@ struct WorldCoordinates
{
return !(*this == other);
}
[[nodiscard]] sf::Vector2f toGroundCoordinates() const
{
return {x, y};
}
};
struct IsometricCoordinates
@ -70,7 +62,7 @@ struct IsometricCoordinates
float y;
float depth; // Bigger means further back. Can be used for accurate rendering order.
[[nodiscard]] sf::Vector2f toScreen() const
sf::Vector2f toScreen() const
{
return {x, y};
}
@ -80,20 +72,7 @@ struct IsometricCoordinates
IsometricCoordinates(float x, float y, float depth = 0) : x(x), y(y), depth(depth)
{}
explicit IsometricCoordinates(sf::Vector2f vector) : x(vector.x), y(vector.y), depth(0)
{}
};
struct DiagonalWorldCoordinates
{
float horizontal;
float vertical;
float depth; // Bigger means further back. Can be used for accurate rendering order.
const sf::Vector2f horizontalAxis = {1, -1};
DiagonalWorldCoordinates(float horizontal, float vertical, float depth) : horizontal(horizontal),
vertical(vertical), depth(depth)
explicit IsometricCoordinates (sf::Vector2f vector) : x(vector.x), y(vector.y), depth(0)
{}
};
@ -106,9 +85,6 @@ struct GridCoordinates
GridCoordinates(float x, float y) : x(x), y(y)
{}
GridCoordinates(int x, int y) : x(static_cast<float>(x)), y(static_cast<float>(y))
{}
};

View file

@ -2,8 +2,7 @@
#include <utility>
WorldCoordinates TranslatedCoordinates::world() const
{
WorldCoordinates TranslatedCoordinates::world() const {
auto coordinates = worldCoordinates;
if (parent != nullptr)
{
@ -13,52 +12,40 @@ WorldCoordinates TranslatedCoordinates::world() const
return coordinates;
}
IsometricCoordinates TranslatedCoordinates::isometric() const
{
IsometricCoordinates TranslatedCoordinates::isometric() const {
return CoordinateTransformer::worldToIsometric(world());
}
GridCoordinates TranslatedCoordinates::grid() const
{
GridCoordinates TranslatedCoordinates::grid() const {
auto referenceWordCoordinates = world();
// Grid coords are just camera coords without height, and scaled differently
return {referenceWordCoordinates.x - 0.5f, referenceWordCoordinates.y - 0.5f};
}
void TranslatedCoordinates::setWorld(WorldCoordinates newWorldCoordinates)
{
void TranslatedCoordinates::set(WorldCoordinates newWorldCoordinates) {
this->worldCoordinates = newWorldCoordinates;
}
void TranslatedCoordinates::setIsometric(IsometricCoordinates newIsometricCoordinates)
{
void TranslatedCoordinates::set(IsometricCoordinates newIsometricCoordinates) {
this->worldCoordinates = CoordinateTransformer::isometricToWorld(newIsometricCoordinates);
}
void TranslatedCoordinates::setTranslated(const TranslatedCoordinates &newCoordinates)
{
void TranslatedCoordinates::set(const TranslatedCoordinates& newCoordinates) {
this->worldCoordinates = newCoordinates.world();
}
void TranslatedCoordinates::move(WorldCoordinates deltaWorldCoordinates)
{
void TranslatedCoordinates::move(WorldCoordinates deltaWorldCoordinates) {
this->worldCoordinates = this->worldCoordinates + deltaWorldCoordinates;
}
void TranslatedCoordinates::move(IsometricCoordinates deltaIsometricCoordinates)
{
move(CoordinateTransformer::isometricToWorld(deltaIsometricCoordinates));
}
TranslatedCoordinates::TranslatedCoordinates(WorldCoordinates worldCoordinates)
: worldCoordinates(worldCoordinates)
{
: worldCoordinates(worldCoordinates) {
}
void TranslatedCoordinates::move(sf::Vector2f deltaWorldCoordinates)
{
move(WorldCoordinates {deltaWorldCoordinates.x, deltaWorldCoordinates.y, 0});
move({deltaWorldCoordinates.x, deltaWorldCoordinates.y, 0});
}
void TranslatedCoordinates::setParent(std::shared_ptr<TranslatedCoordinates> parent, WorldCoordinates offset)
@ -67,7 +54,7 @@ void TranslatedCoordinates::setParent(std::shared_ptr<TranslatedCoordinates> par
this->worldCoordinates = offset;
}
void TranslatedCoordinates::setGrid(GridCoordinates newGridCoordinates)
void TranslatedCoordinates::set(GridCoordinates newGridCoordinates)
{
this->worldCoordinates = {newGridCoordinates.x + 0.5f, newGridCoordinates.y + 0.5f, 0};
}
@ -84,33 +71,5 @@ void TranslatedCoordinates::setScreenOffset(IsometricCoordinates offset)
TranslatedCoordinates::TranslatedCoordinates(GridCoordinates gridCoordinates)
{
setGrid(gridCoordinates);
}
DiagonalWorldCoordinates TranslatedCoordinates::diagonalWorld() const
{
return CoordinateTransformer::worldToDiagonal(world());
}
void TranslatedCoordinates::setDiagonal(DiagonalWorldCoordinates newDiagonalWorldCoordinates)
{
this->worldCoordinates = CoordinateTransformer::diagonalToWorld(newDiagonalWorldCoordinates);
}
TranslatedCoordinates::TranslatedCoordinates(DiagonalWorldCoordinates diagonalWorldCoordinates)
{
setDiagonal(diagonalWorldCoordinates);
}
void TranslatedCoordinates::removeParent()
{
// Uncomment, if global position should be preserved
// auto globalWorldCoordinates = this->world();
// this->worldCoordinates = globalWorldCoordinates;
this->parent = nullptr;
}
TranslatedCoordinates::TranslatedCoordinates(IsometricCoordinates isometricCoordinates)
{
setIsometric(isometricCoordinates);
set(gridCoordinates);
}

View file

@ -9,8 +9,6 @@ class TranslatedCoordinates
{
public:
explicit TranslatedCoordinates(WorldCoordinates worldCoordinates);
explicit TranslatedCoordinates(DiagonalWorldCoordinates diagonalWorldCoordinates);
explicit TranslatedCoordinates(IsometricCoordinates isometricCoordinates);
explicit TranslatedCoordinates(GridCoordinates gridCoordinates);
[[nodiscard]] WorldCoordinates world() const;
@ -19,17 +17,13 @@ public:
[[nodiscard]] GridCoordinates grid() const;
[[nodiscard]] DiagonalWorldCoordinates diagonalWorld() const;
void set(WorldCoordinates newWorldCoordinates);
void setWorld(WorldCoordinates newWorldCoordinates);
void set(const TranslatedCoordinates& newCoordinates);
void setTranslated(const TranslatedCoordinates& newCoordinates);
void set(IsometricCoordinates newIsometricCoordinates);
void setIsometric(IsometricCoordinates newIsometricCoordinates);
void setDiagonal(DiagonalWorldCoordinates newDiagonalWorldCoordinates);
void setGrid(GridCoordinates newGridCoordinates);
void set(GridCoordinates newGridCoordinates);
void move(WorldCoordinates deltaWorldCoordinates);
@ -37,19 +31,14 @@ public:
void setParent(std::shared_ptr<TranslatedCoordinates> parent, WorldCoordinates offset = {0, 0});
void removeParent();
void setWorldOffset(WorldCoordinates offset);
void setScreenOffset(IsometricCoordinates offset);
void move(IsometricCoordinates deltaIsometricCoordinates);
private:
WorldCoordinates worldCoordinates{};
std::shared_ptr<TranslatedCoordinates> parent = nullptr;
};

View file

@ -1,65 +0,0 @@
#include <SFML/Graphics/VertexArray.hpp>
#include "multiplayer_borders.hpp"
#include "../../config.h"
MultiplayerBorders::MultiplayerBorders(int firstRowViewCount, int secondRowViewCount)
: view(new sf::View(sf::Vector2f(0.5f, 0.5f), sf::Vector2f(1, 1)))
{
createBorders(firstRowViewCount, secondRowViewCount);
}
void MultiplayerBorders::createBorders(int firstRowViewCount, int secondRowViewCount)
{
int lineCount = firstRowViewCount + secondRowViewCount - 1;
if (lineCount <= 0)
{
return;
}
lines = sf::VertexArray(sf::Lines, lineCount * 2);
float lineHeight = secondRowViewCount > 0 ? 0.5f : 1.f;
// First row
int firstRowLineCount = firstRowViewCount - 1;
createRowOfLines(0, firstRowLineCount, 0, lineHeight);
if (secondRowViewCount <= 0)
{
return;
}
// Horizontal separator
createLine(firstRowLineCount,
{0, lineHeight},
{1, lineHeight});
// Second row
int secondRowLineCount = secondRowViewCount - 1;
createRowOfLines(firstRowLineCount + 1, secondRowLineCount, lineHeight, lineHeight);
}
void MultiplayerBorders::draw(sf::RenderWindow *window)
{
window->setView(*view);
window->draw(lines);
}
void MultiplayerBorders::createRowOfLines(int startIndex, int count, float topOffset, float height)
{
for (int i = 0; i < count; i++)
{
float horizontalProgress = (float) (i + 1) / (float) (count + 1);
createLine(startIndex + i,
{horizontalProgress, topOffset},
{horizontalProgress, topOffset + height});
}
}
void MultiplayerBorders::createLine(int lineIndex, sf::Vector2f firstPoint, sf::Vector2f secondPoint)
{
lines[lineIndex * 2].position = firstPoint;
lines[lineIndex * 2 + 1].position = secondPoint;
lines[lineIndex * 2].color = MP_VIEW_BORDER_COLOR;
lines[lineIndex * 2 + 1].color = MP_VIEW_BORDER_COLOR;
}

View file

@ -1,26 +0,0 @@
#ifndef HOLESOME_MULTIPLAYER_BORDERS_HPP
#define HOLESOME_MULTIPLAYER_BORDERS_HPP
#include "../game_object.h"
class MultiplayerBorders : public GameObject
{
public:
explicit MultiplayerBorders(int firstRowViewCount = 0, int secondRowViewCount = 0);
void draw(sf::RenderWindow *window) override;
void createBorders(int firstRowViewCount, int secondRowViewCount = 0);
private:
sf::VertexArray lines{};
sf::View* view = nullptr;
void createRowOfLines(int startIndex, int count, float topOffset, float height);
void createLine(int lineIndex, sf::Vector2f firstPoint, sf::Vector2f secondPoint);
};
#endif //HOLESOME_MULTIPLAYER_BORDERS_HPP

View file

@ -1,150 +0,0 @@
#include "multiplayer_view.hpp"
#include "../player/player_collection.hpp"
#include "ITrackable.h"
std::shared_ptr<MultiplayerView> MultiplayerView::getInstance()
{
if (singletonInstance == nullptr)
{
singletonInstance = std::make_shared<MultiplayerView>();
}
return singletonInstance;
}
void MultiplayerView::update()
{
// Update based on PlayerCollection
// Add new
if (MP_VIEW_ADD_NEW_PLAYERS)
{
for (const auto &player: PlayerCollection::getInstance()->getNewPlayers())
{
if (player->getTrackableState() == TrackableState::TRACKING)
{
addPlayer(player);
}
}
}
GameObject::update();
}
void MultiplayerView::addPlayer(const std::shared_ptr<Player> &player)
{
// Player already added?
if (viewsForPlayers.find(player->getPlayerId()) != viewsForPlayers.end())
{
LOG(WARNING) << "Player " << player->getPlayerId() << " already added to MultiplayerView.";
return;
}
// Create new view
auto view = std::make_shared<TrackingView>();
view->addTrackable(player);
viewsForPlayers[player->getPlayerId()] = view;
addChild(view);
updateLayout();
}
void MultiplayerView::removePlayer(const std::shared_ptr<Player> &player)
{
// Player already removed?
if (viewsForPlayers.find(player->getPlayerId()) == viewsForPlayers.end())
{
LOG(WARNING) << "Player " << player->getPlayerId() << " already removed from MultiplayerView.";
return;
}
// Make view show all remaining players
auto view = viewsForPlayers[player->getPlayerId()];
view->removeTrackable(player);
for (const auto &remainingPlayers: PlayerCollection::getInstance()->getPlayers())
{
view->addTrackable(remainingPlayers);
}
}
void MultiplayerView::updateLayout() const
{
if (viewsForPlayers.empty())
{
return;
}
int viewCount = viewsForPlayers.size();
// Handle simple cases
if (viewCount == 1)
{
viewsForPlayers.begin()->second->setViewport({0, 0}, {1, 1});
return;
}
if (viewCount == 2)
{
auto it = viewsForPlayers.begin();
it->second->setViewport({0, 0}, {0.5, 1});
it++;
it->second->setViewport({0.5, 0}, {0.5, 1});
borders->createBorders(2);
return;
}
// Calculate layout
int firstRowViewCount = ceil(viewCount / 2.f);
int secondRowViewCount = viewCount - firstRowViewCount;
borders->createBorders(firstRowViewCount, secondRowViewCount);
int index = 0;
for (auto &view: getViews())
{
bool secondRow = index >= viewCount / 2.f;
int rowViewCount = secondRow ? secondRowViewCount : firstRowViewCount;
int rowIndexOfView = index - (secondRow ? firstRowViewCount : 0);
view->setViewport(
{
rowIndexOfView / (float) rowViewCount,
secondRow ? 0.5f : 0
}, {
1.f / rowViewCount,
0.5f
}
);
index++;
}
}
std::vector<std::shared_ptr<TrackingView>> MultiplayerView::getViews() const
{
std::vector<std::shared_ptr<TrackingView>> views;
for (const auto &[_, view]: viewsForPlayers)
{
views.push_back(view);
}
return views;
}
void MultiplayerView::draw(sf::RenderWindow *window)
{
if (borders != nullptr)
{
borders->draw(window);
}
}
MultiplayerView::MultiplayerView()
: borders(std::make_shared<MultiplayerBorders>()), viewsForPlayers({})
{
}
void MultiplayerView::clear()
{
viewsForPlayers.clear();
}

View file

@ -1,40 +0,0 @@
#ifndef HOLESOME_MULTIPLAYER_VIEW_HPP
#define HOLESOME_MULTIPLAYER_VIEW_HPP
#include <map>
#include "../game_object.h"
#include "tracking_view.h"
#include "../player/player.hpp"
#include "multiplayer_borders.hpp"
class MultiplayerView : public GameObject
{
public:
static std::shared_ptr<MultiplayerView> getInstance();
MultiplayerView();
void draw(sf::RenderWindow *window) override;
void update() override;
void clear();
void addPlayer(const std::shared_ptr<Player>& player);
void removePlayer(const std::shared_ptr<Player>& player);
std::vector<std::shared_ptr<TrackingView>> getViews() const;
private:
static inline std::shared_ptr<MultiplayerView> singletonInstance = nullptr;
std::map<int, std::shared_ptr<TrackingView>> viewsForPlayers; // Player ID => View
void updateLayout() const;
std::shared_ptr<MultiplayerBorders> borders;
};
#endif //HOLESOME_MULTIPLAYER_VIEW_HPP

View file

@ -3,11 +3,13 @@
#include "../player/player_collection.hpp"
TrackingView::TrackingView(TrackingViewOptions options) : options(options),
view(new sf::View(options.initialCenter,
options.minViewSize)),
view(nullptr),
hasViewChanged(false),
trackables({})
{
;
marker = new CircleObject(DB_CIRCLE_RADIUS, sf::Color::Yellow);
Game::getInstance()->registerView(this);
}
TrackingView::~TrackingView()
@ -18,13 +20,33 @@ TrackingView::~TrackingView()
void TrackingView::lateUpdate()
{
// Initialize if necessary
if (view == nullptr)
{
initializeView();
}
processTrackableStates();
if (!trackables.empty())
{
followTrackables();
adjustSizeToTrackables();
}
adjustSizeToTrackables();
// Update window if necessary
if (hasViewChanged)
{
Game::getInstance()->window->setView(*this->view);
hasViewChanged = false;
}
}
void TrackingView::initializeView()
{
auto size = Game::getInstance()->window->getView().getSize();
view = new sf::View({0, 0}, size);
hasViewChanged = true;
}
void TrackingView::setSize(sf::Vector2f newSize)
@ -52,6 +74,7 @@ void TrackingView::setSize(sf::Vector2f newSize)
}
view->setSize(newSize);
hasViewChanged = true;
}
sf::Vector2f TrackingView::getSize() const
@ -59,11 +82,10 @@ sf::Vector2f TrackingView::getSize() const
return view->getSize();
}
sf::Vector2f TrackingView::getSizeInWindow() const
sf::Vector2f TrackingView::getWindowSize() const
{
auto size = Game::getInstance()->window->getSize();
auto viewPort = view->getViewport();
return {static_cast<float>(size.x * viewPort.width), static_cast<float>(size.y * viewPort.height)};
return {static_cast<float>(size.x), static_cast<float>(size.y)};
}
sf::Vector2f TrackingView::getCenter() const
@ -74,9 +96,9 @@ sf::Vector2f TrackingView::getCenter() const
void TrackingView::followTrackables()
{
auto trackingPoint = getTrackingArea().getCenter();
if (DB_TRACKING_VIEW_CENTER)
if (DEVELOPER_MODE)
{
marker->coordinates->setIsometric(IsometricCoordinates(trackingPoint));
marker->coordinates->set(IsometricCoordinates(trackingPoint));
}
// Calculate distance to target to check how to handle it
@ -84,7 +106,7 @@ void TrackingView::followTrackables()
auto vectorToTarget = trackingPoint - currentCenter;
auto distanceToTarget = length(vectorToTarget);
if (distanceToTarget <= getFreeMovementRadius())
if (distanceToTarget <= getRadius(options.freeMoveThreshold))
{
// Nothing to do
return;
@ -96,7 +118,7 @@ void TrackingView::followTrackables()
auto deltaToDesiredView = normalize(vectorToTarget);
// Reduce delta to edge of free-move area to make it less jaring
deltaToDesiredView *= distanceToTarget - getFreeMovementRadius();
deltaToDesiredView *= distanceToTarget - getRadius(options.freeMoveThreshold);
moveCenter(deltaToDesiredView);
}
@ -110,11 +132,12 @@ void TrackingView::moveCenter(sf::Vector2<float> delta)
}
view->move(delta);
hasViewChanged = true;
}
void TrackingView::draw(sf::RenderWindow *window)
{
if (!DB_TRACKING_VIEW_CENTER)
if (!DEVELOPER_MODE)
{
return;
}
@ -125,12 +148,6 @@ void TrackingView::draw(sf::RenderWindow *window)
void TrackingView::addTrackable(const std::shared_ptr<ITrackable> &trackable)
{
trackables.push_back(trackable);
// Set initial values if first trackable
if (trackables.size() == 1)
{
view->setCenter(trackable->getTrackablePosition());
}
}
void TrackingView::processTrackableStates()
@ -144,11 +161,6 @@ void TrackingView::processTrackableStates()
TrackingArea TrackingView::getTrackingArea() const
{
if (trackables.empty())
{
return TrackingArea();
}
auto initialTrackable = trackables[0];
TrackingArea area = {
initialTrackable->getTrackablePosition() - initialTrackable->getTrackableSize() / 2.f,
@ -194,7 +206,7 @@ void TrackingView::adjustSizeToTrackables()
newViewSize = restrainToBounds(newViewSize);
// Extend view to match aspect ratio
auto windowSize = getSizeInWindow();
auto windowSize = getWindowSize();
auto aspectRatio = windowSize.x / windowSize.y;
if (newViewSize.x / newViewSize.y > aspectRatio)
{
@ -233,31 +245,61 @@ sf::Vector2f TrackingView::restrainToBounds(sf::Vector2f viewSize) const
sf::Vector2f TrackingView::applyPadding(sf::Vector2f viewSize) const
{
auto padding = options.viewSizePadding;
if (!options.isAbsoluteViewSizePadding)
if (padding.x <= 1)
{
padding.x *= viewSize.x;
}
if (padding.y <= 1)
{
padding.y *= viewSize.y;
}
return viewSize + padding;
}
float TrackingView::getFreeMovementRadius() const
float TrackingView::getRadius(float threshold) const
{
if (options.freeMoveThreshold <= 0)
if (threshold <= 0)
{
return 0;
}
if (options.isAbsoluteFreeMoveThreshold)
if (threshold > 1)
{
return options.freeMoveThreshold;
return threshold;
}
auto windowSize = getSizeInWindow();
auto windowSize = getWindowSize();
float smallestSide = std::min(windowSize.x, windowSize.y);
// Only half of the side, since we are calculating radius
return smallestSide / 2.f * options.freeMoveThreshold;
return smallestSide / 2.f * threshold;
}
void TrackingView::update()
{
if (options.addPlayersDynamically)
{
addPlayersDynamically();
}
}
void TrackingView::addPlayersDynamically()
{
// Update based on PlayerCollection
// Add new
for (const auto &player: PlayerCollection::getInstance()->getNewPlayers())
{
if (player->getTrackableState() == TrackableState::TRACKING)
{
addTrackable(player);
}
}
// Remove old
for (const auto &player: PlayerCollection::getInstance()->getRemovedPlayers())
{
removeTrackable(player);
}
}
void TrackingView::removeTrackable(const std::shared_ptr<ITrackable> &trackable)
@ -268,14 +310,3 @@ void TrackingView::removeTrackable(const std::shared_ptr<ITrackable> &trackable)
return t == trackable;
}), trackables.end());
}
void TrackingView::setViewport(sf::Vector2f center, sf::Vector2f size)
{
// TODO: Resize content for viewport
view->setViewport(sf::FloatRect(center.x, center.y, size.x, size.y));
}
void TrackingView::setViewForWindow()
{
Game::getInstance()->window->setView(*this->view);
}

View file

@ -17,6 +17,8 @@ public:
~TrackingView();
void update() override;
void draw(sf::RenderWindow *window) override;
void lateUpdate() override;
@ -25,21 +27,20 @@ public:
void removeTrackable(const std::shared_ptr<ITrackable> &trackable);
void setViewport(sf::Vector2f center, sf::Vector2f size);
void setViewForWindow();
sf::Vector2f getSize() const;
sf::Vector2f getCenter() const;
private:
sf::View *view{};
bool hasViewChanged{};
TrackingViewOptions options;
std::vector<std::shared_ptr<ITrackable>> trackables;
CircleObject *marker;
void initializeView();
void followTrackables();
void moveCenter(sf::Vector2<float> delta);
@ -56,9 +57,11 @@ private:
sf::Vector2f restrainToBounds(sf::Vector2f viewSize) const;
float getFreeMovementRadius() const;
float getRadius(float threshold) const;
sf::Vector2f getSizeInWindow() const;
sf::Vector2f getWindowSize() const;
void addPlayersDynamically();
};

View file

@ -7,9 +7,10 @@
struct TrackingViewOptions
{
/**
* Value >1 to set pixel radius.
* Value between 0 and 1 to set relative radius based on smallest half-axis-size.
*/
float freeMoveThreshold = DEF_TV_FREE_MOVE_THRESHOLD;
bool isAbsoluteFreeMoveThreshold = DEF_TV_IS_ABSOLUTE_FREE_MOVE_THRESHOLD;
/**
* 0 for instant follow.
@ -30,14 +31,17 @@ struct TrackingViewOptions
*/
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 set to true, padding will be added to view size, is multiplied with tracking size and added.
* Value >1 to set pixel padding.
* Value between 0 and 1 to set relative padding.
*/
sf::Vector2f viewSizePadding = DEF_TV_VIEW_SIZE_PADDING;
bool isAbsoluteViewSizePadding = DEF_TV_IS_ABSOLUTE_VIEW_SIZE_PADDING;
/**
* If set to true, view will add all new players automatically and remove them accordingly, based on PlayerCollection.
*/
bool addPlayersDynamically = DEF_TV_ADD_PLAYERS_DYNAMICALLY;
};
#endif //HOLESOME_TRACKING_VIEW_OPTIONS_HPP

View file

@ -1,58 +1,33 @@
#include "collectable.hpp"
#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(int points)
: points(points)
Collectable::Collectable()
{
collectableId = collectableCount;
collectableCount++;
}
void Collectable::setRotation(float angle)
int Collectable::getDepth() const
{
// Assume that the sprite is the first child
auto sprite = std::dynamic_pointer_cast<VersatileSprite>(getChildren()[0]);
sprite->setRotation(angle);
}
float Collectable::getDepth() const
{
return coordinates->isometric().depth;
return static_cast<int>(coordinates->isometric().depth);
}
void Collectable::setSprite(const std::string &spriteName)
{
// Create versatile sprite
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));
float sizeWidth = 1.f * WORLD_TO_ISO_SCALE;
auto size = sf::Vector2f{sizeWidth, sizeWidth};
auto sprite = std::make_shared<VersatileSprite>(spriteName, size);
addChildScreenOffset(sprite, IsometricCoordinates(-sizeWidth / 2.f, -sizeWidth));
}
sf::Vector2f Collectable::getSize() const
int Collectable::getId() const
{
return size / WORLD_TO_ISO_SCALE;
return collectableId;
}
void Collectable::preRenderUpdate()
void Collectable::setSpriteAngle(float angle)
{
if (!sprite->isVisible())
{
auto closestPlayer = PlayerCollection::getInstance()->getClosestPlayer(*coordinates);
closestPlayer->consume(points);
setActive(false);
return;
}
GameObject::preRenderUpdate();
// TODO
}

View file

@ -1,40 +1,28 @@
#ifndef HOLESOME_COLLECTABLE_HPP
#define HOLESOME_COLLECTABLE_HPP
#include <SFML/System/Vector2.hpp>
#include "../game_object.h"
#include "../player/player.hpp"
#include "../../sprites/masked_sprite.hpp"
#include "collectable_sim_parameters.hpp"
class Collectable : public GameObject
{
public:
Collectable(int points);
Collectable();
void setSprite(const std::string &spriteName);
void setRotation(float angle);
void setSpriteAngle(float angle);
sf::Vector2f getSize() const;
[[nodiscard]] int getDepth() const;
float getDepth() const;
[[nodiscard]] int getId() const;
int getId() const
{
return collectableId;
}
void preRenderUpdate() override;
CollectableSimParameters simParameters = {};
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;
};

View file

@ -6,10 +6,9 @@
struct CollectableConfig
{
std::string spriteName;
int points = 0;
explicit CollectableConfig(std::string spriteName, int points)
: spriteName(std::move(spriteName)), points(points)
explicit CollectableConfig(std::string spriteName)
: spriteName(std::move(spriteName))
{}
CollectableConfig() = default;

View file

@ -3,9 +3,9 @@
std::shared_ptr<Collectable> CollectableFactory::createFromInLevelConfig(const CollectableInLevel &config)
{
auto collectableConfig = config.collectableConfig;
auto collectable = std::make_shared<Collectable>(config.collectableConfig.points);
auto collectable = std::make_shared<Collectable>();
collectable->coordinates->setGrid(config.position);
collectable->coordinates->set(config.position);
collectable->setSprite(collectableConfig.spriteName);
return collectable;

View file

@ -0,0 +1,15 @@
#ifndef HOLESOME_COLLECTABLE_SIM_PARAMETERS_HPP
#define HOLESOME_COLLECTABLE_SIM_PARAMETERS_HPP
#include <box2d/box2d.h>
struct CollectableSimParameters
{
float angle = 0;
float angularVelocity = 0;
b2Vec2 linearVelocity = {0, 0};
CollectableSimParameters() = default;
};
#endif //HOLESOME_COLLECTABLE_SIM_PARAMETERS_HPP

View file

@ -1,10 +1,6 @@
#include "collectables_collection.hpp"
#include <cmath>
#include "collectables_depth_collection.hpp"
#include "../../../logging/easylogging++.h"
#include "../../../config.h"
#include "../../physics/holes/holes_simulation.hpp"
std::shared_ptr<CollectablesCollection> CollectablesCollection::getInstance()
{
@ -29,57 +25,20 @@ void CollectablesCollection::createEmpty(int maxDepth)
}
}
void CollectablesCollection::remove(int collectableId)
void CollectablesCollection::remove(const std::shared_ptr<Collectable> &collectable)
{
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);
}
void CollectablesCollection::add(const std::shared_ptr<Collectable> &collectable)
{
depthCollections[collectable->getDepth()]->add(collectable);
addDetachedChild(collectable);
}
void CollectablesCollection::update()
{
GameObject::update();
// 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;
}
updateCollectables();
// Move collectables to new depth collections if necessary
@ -92,31 +51,18 @@ void CollectablesCollection::update()
// Then, move collectables to new depth collections
for (auto &[depth, depthCollection]: depthCollections)
{
auto collectables = depthCollection->collectables;
for (auto &collectable: collectables)
for (auto &collectable: depthCollection->collectables)
{
int newDepth = std::floor(collectable->getDepth());
if (newDepth == depth)
auto newDepth = collectable->getDepth();
if (newDepth != depth)
{
continue;
depthCollection->remove(collectable);
depthCollections[newDepth]->add(collectable);
}
if (!isValidDepth(newDepth))
{
LOG(ERROR) << "Collectable " << collectable->getId() << " has invalid depth " << newDepth
<< "! Keeping it at previous depth " << depth << " ...";
continue;
}
depthCollection->remove(collectable);
depthCollections[newDepth]->add(collectable);
}
}
}
bool CollectablesCollection::isValidDepth(int desiredDepth) const
{ return desiredDepth >= 0 && desiredDepth < (int) depthCollections.size(); }
void CollectablesCollection::draw(sf::RenderWindow *window)
{
// Render collectables in reverse order of depth
@ -130,3 +76,25 @@ void CollectablesCollection::draw(sf::RenderWindow *window)
}
}
}
void CollectablesCollection::updateCollectables()
{
for (auto &[depth, depthCollection]: depthCollections)
{
for (auto &collectable: depthCollection->collectables)
{
collectable->update();
}
}
}
std::shared_ptr<CollectablesDepthCollection> CollectablesCollection::getDepthCollection(int depth)
{
if (depthCollections.find(depth) == depthCollections.end())
{
LOG(ERROR) << "Depth collection for depth " << depth << " does not exist! Returning empty collection ...";
return std::make_shared<CollectablesDepthCollection>(depth);
}
return depthCollections[depth];
}

View file

@ -21,14 +21,16 @@ public:
void draw(sf::RenderWindow *window) override;
void add(const std::shared_ptr<Collectable>& collectable);
void remove(int collectableId);
void remove(const std::shared_ptr<Collectable>& collectable);
std::shared_ptr<CollectablesDepthCollection> getDepthCollection(int depth);
private:
static inline std::shared_ptr<CollectablesCollection> singletonInstance = nullptr;
std::map<int, std::shared_ptr<CollectablesDepthCollection>> depthCollections = {};
bool isValidDepth(int desiredDepth) const;
void updateCollectables();
};

View file

@ -0,0 +1,21 @@
#ifndef HOLESOME_SIM_COLLECTABLE_HPP
#define HOLESOME_SIM_COLLECTABLE_HPP
#include "collectable.hpp"
#include <memory>
struct SimCollectable
{
std::shared_ptr<Collectable> collectable = nullptr;
b2Body *body = nullptr;
SimCollectable(std::shared_ptr<Collectable> collectable, b2Body *body)
{
this->collectable = std::move(collectable);
this->body = body;
}
SimCollectable() = default;
};
#endif //HOLESOME_SIM_COLLECTABLE_HPP

View file

@ -1,36 +0,0 @@
#include "frame_counter.hpp"
#include "../logging/easylogging++.h"
unsigned int FrameCounter::getFps() const
{
return lastFps;
}
void FrameCounter::addFrame()
{
liveFrameCount++;
timeSinceLastFpsUpdate += clock.restart();
auto const second = sf::seconds(1);
if (timeSinceLastFpsUpdate < second)
{
return;
}
lastFps = liveFrameCount;
liveFrameCount = 0;
timeSinceLastFpsUpdate -= second;
if (printFpsInConsole)
{
LOG(INFO) << "FPS: " << lastFps;
}
}
void FrameCounter::reset()
{
clock.restart();
timeSinceLastFpsUpdate = sf::Time::Zero;
liveFrameCount = 0;
lastFps = 0;
}

View file

@ -1,25 +0,0 @@
#ifndef HOLESOME_FRAME_COUNTER_HPP
#define HOLESOME_FRAME_COUNTER_HPP
#include <SFML/System/Clock.hpp>
class FrameCounter
{
public:
void reset();
void addFrame();
unsigned int getFps() const;
bool printFpsInConsole = true;
private:
sf::Clock clock;
sf::Time timeSinceLastFpsUpdate = sf::Time::Zero;
unsigned int liveFrameCount = 0;
unsigned int lastFps = 0;
};
#endif //HOLESOME_FRAME_COUNTER_HPP

View file

@ -6,12 +6,6 @@
#include "level/level_loader.hpp"
#include "physics/map/map_simulation.hpp"
#include "../logging/easylogging++.h"
#include "layer/global_layer.hpp"
#include "player/player_collection.hpp"
#include "../screens/winner_screen.hpp"
#include "../screens/join_screen.hpp"
#include "level/level_generator.hpp"
#include "../screens/loading_screen.hpp"
Game::Game(std::shared_ptr<sf::RenderWindow> window) : window(std::move(window))
{
@ -21,11 +15,11 @@ void Game::run()
{
sf::Clock clock;
sf::Time TimeSinceLastUpdate = sf::seconds(0);
frameCounter->reset();
LOG(INFO) << "Game loop started ...";
while (window->isOpen())
{
InputMapper::getInstance()->processEvents();
TimeSinceLastUpdate += clock.restart();
while (TimeSinceLastUpdate >= FRAME_TIME)
@ -35,7 +29,6 @@ void Game::run()
update();
InputMapper::getInstance()->processEvents();
}
drawFrame();
}
@ -60,27 +53,24 @@ void Game::drawFrame()
}
window->display();
frameCounter->addFrame();
}
void Game::addGameObject(const std::shared_ptr<GameObject> &gameObject)
void Game::addGameObject(const std::shared_ptr<GameObject>& gameObject)
{
gameObjectsBuffer.push_back(gameObject);
}
void Game::update()
{
handleWinning();
// Add new game objects
for (const auto &gameObject: gameObjectsBuffer)
for (const auto& gameObject: gameObjectsBuffer)
{
gameObjects.push_back(gameObject);
}
gameObjectsBuffer.clear();
// Basic Updates
for (const auto &gameObject: gameObjects)
for (const auto& gameObject: gameObjects)
{
if (gameObject->getActive())
{
@ -107,14 +97,6 @@ void Game::update()
}
InputMapper::getInstance()->updateIdentities();
for (const auto &gameObject: gameObjects)
{
if (gameObject->getActive())
{
gameObject->preRenderUpdate();
}
}
}
std::shared_ptr<Game> Game::getInstance()
@ -139,6 +121,11 @@ std::shared_ptr<Game> Game::constructInstance(const std::shared_ptr<sf::RenderWi
return singletonInstance;
}
void Game::registerView(TrackingView *view)
{
views.push_back(view);
}
void Game::clearGameObjects()
{
gameObjects.clear();
@ -153,99 +140,3 @@ bool Game::isLevelLoaded() const
{
return loadedLevelConfig.isValid();
}
void Game::startCountdown(int durationInSeconds)
{
countdown = std::make_shared<Countdown>(durationInSeconds);
GlobalLayer::getInstance()->add(countdown);
}
std::shared_ptr<Player> Game::checkForWinner()
{
std::vector<std::shared_ptr<Player>> remainingPlayers = PlayerCollection::getInstance()->getRemainingPlayers();
// Has timer run out or is only one player left?
if (!countdown->isFinished() && remainingPlayers.size() > 1)
{
return nullptr;
}
// Return player with highest score as winner
std::shared_ptr<Player> winner = nullptr;
for (const auto &player: remainingPlayers)
{
if (winner == nullptr || player->getPoints() > winner->getPoints())
{
winner = player;
}
}
return winner;
}
void Game::handleWinning()
{
if (!isLevelLoaded())
{
return;
}
// Any players in game?
if (PlayerCollection::getInstance()->getPlayers().empty())
{
return;
}
std::shared_ptr<Player> winner = checkForWinner();
if (winner == nullptr)
{
return;
}
LOG(INFO) << "Player " << winner->getPlayerId() << " won the game with " << winner->getPoints() << " points.";
// Stop game and show winner
auto players = PlayerCollection::getInstance()->getPlayers();
LevelLoader::cleanUp();
auto winnerScreen = std::make_shared<WinnerScreen>(players);
GlobalLayer::getInstance()->add(winnerScreen);
addGameObject(GlobalLayer::getInstance());
}
void Game::showJoinScreen()
{
GlobalLayer::getInstance()->clear();
clearGameObjects();
InputMapper::getInstance()->allowNewInputIdentities = true;
PlayerCollection::getInstance()->deactivatePlayers();
auto joinScreen = std::make_shared<JoinScreen>();
GlobalLayer::getInstance()->add(joinScreen);
addGameObject(GlobalLayer::getInstance());
addGameObject(PlayerCollection::getInstance());
}
void Game::generateNewLevel()
{
showLoadingScreen("Generating new level ...");
LevelConfig newLevel = LevelGenerator::generateLevel("Procedural Level");
LevelLoader::loadLevel(newLevel);
}
void Game::showLoadingScreen(const std::string &message)
{
GlobalLayer::getInstance()->clear();
clearGameObjects();
auto loadingScreen = std::make_shared<LoadingScreen>(message);
GlobalLayer::getInstance()->add(loadingScreen);
addGameObject(GlobalLayer::getInstance());
// Mini loop
update();
drawFrame();
}

View file

@ -8,8 +8,6 @@
#include "input/input_mapper.h"
#include "camera/tracking_view.h"
#include "level/level_config.hpp"
#include "frame_counter.hpp"
#include "time/countdown.hpp"
class TrackingView;
@ -17,10 +15,8 @@ class TrackingView;
class Game
{
public:
static std::shared_ptr<Game> constructInstance(const std::shared_ptr<sf::RenderWindow> &window);
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();
@ -31,18 +27,14 @@ public:
void setLevel(LevelConfig levelConfig);
void startCountdown(int durationInSeconds);
[[nodiscard]] bool isLevelLoaded() const;
void addGameObject(const std::shared_ptr<GameObject> &gameObject);
void addGameObject(const std::shared_ptr<GameObject>& gameObject);
void registerView(TrackingView *view);
std::shared_ptr<sf::RenderWindow> window;
void showJoinScreen();
void generateNewLevel();
std::vector<TrackingView*> views = {};
private:
static inline std::shared_ptr<Game> singletonInstance = nullptr;
std::vector<std::shared_ptr<GameObject>> gameObjects = {};
@ -50,18 +42,9 @@ private:
LevelConfig loadedLevelConfig = {};
std::shared_ptr<FrameCounter> frameCounter = std::make_shared<FrameCounter>();
std::shared_ptr<Countdown> countdown = std::make_shared<Countdown>();
void drawFrame();
void update();
std::shared_ptr<Player> checkForWinner();
void handleWinning();
void showLoadingScreen(const std::string &message = "Loading...");
};

View file

@ -3,8 +3,7 @@
#include "../config.h"
std::shared_ptr<Game> GameFactory::createWindowed(const std::string &title, int width, int height)
{
std::shared_ptr<Game> GameFactory::createWindowed(const std::string &title, int width, int height) {
auto window = std::make_shared<sf::RenderWindow>(sf::VideoMode(width, height), title,
sf::Style::Default,
getAdditionalSettings());
@ -12,16 +11,13 @@ std::shared_ptr<Game> GameFactory::createWindowed(const std::string &title, int
return Game::constructInstance(window);
}
std::shared_ptr<Game> GameFactory::createFullscreen(const std::string &title)
{
std::shared_ptr<Game> GameFactory::createFullscreen(const std::string &title) {
sf::VideoMode fullScreenMode;
auto availableModes = sf::VideoMode::getFullscreenModes();
if (availableModes.empty())
{
if (availableModes.empty()) {
LOG(INFO) << "No fullscreen modes available, falling back to Desktop Mode.";
fullScreenMode = sf::VideoMode::getDesktopMode();
} else
{
} else {
fullScreenMode = availableModes[0];
fullScreenMode.bitsPerPixel = sf::VideoMode::getDesktopMode().bitsPerPixel;
}
@ -34,8 +30,7 @@ std::shared_ptr<Game> GameFactory::createFullscreen(const std::string &title)
return Game::constructInstance(window);
}
sf::ContextSettings GameFactory::getAdditionalSettings()
{
sf::ContextSettings GameFactory::getAdditionalSettings() {
sf::ContextSettings settings = sf::ContextSettings();
settings.antialiasingLevel = ANTIALIASINGLEVEL;
@ -43,11 +38,7 @@ sf::ContextSettings GameFactory::getAdditionalSettings()
return settings;
}
void GameFactory::applyAdditionalWindowConfig(sf::RenderWindow *window)
{
if (FRAME_LIMIT_ENABLED)
{
window->setFramerateLimit(FRAME_RATE);
}
void GameFactory::applyAdditionalWindowConfig(sf::RenderWindow *window) {
window->setFramerateLimit(FRAME_RATE);
window->setKeyRepeatEnabled(KEY_REPEAT_ENABLED);
}

View file

@ -64,7 +64,6 @@ void GameObject::removeChild(const std::shared_ptr<GameObject> &child)
if (it != children.end())
{
children.erase(it);
child->coordinates->removeParent();
}
}
@ -80,16 +79,3 @@ void GameObject::physicsUpdate()
child->physicsUpdate();
}
}
void GameObject::addDetachedChild(const std::shared_ptr<GameObject> &child)
{
children.push_back(child);
}
void GameObject::preRenderUpdate()
{
for (auto &child: children)
{
child->preRenderUpdate();
}
}

View file

@ -20,15 +20,12 @@ public:
virtual void physicsUpdate();
virtual void preRenderUpdate();
void setActive(bool active);
bool getActive() const { return isActive; }
void addChildScreenOffset(const std::shared_ptr<GameObject> &child, IsometricCoordinates offset = {0, 0});
void addChildWorldOffset(const std::shared_ptr<GameObject> &child, WorldCoordinates offset);
void addChild(const std::shared_ptr<GameObject> &child);
void addDetachedChild(const std::shared_ptr<GameObject> &child);
void removeChild(const std::shared_ptr<GameObject> &child);
void clearChildren();
[[nodiscard]] std::vector<std::shared_ptr<GameObject>> getChildren() const { return children; }

View file

@ -4,7 +4,11 @@
enum GameAction
{
CONFIRM,
RUN
CANCEL,
MENU,
GROW,
SHRINK
};
#endif //HOLESOME_GAME_ACTION_HPP

View file

@ -52,8 +52,8 @@ void InputMapper::processEvents()
void InputMapper::handleKeyPress(sf::Event::KeyEvent event)
{
// Close game on Special button
if (event.code == CLOSE_GAME_BTN)
// Close game on Escape or Q in DEV Mode
if (DEVELOPER_MODE && event.code == sf::Keyboard::Escape)
{
Game::getInstance()->exit();
return;
@ -121,13 +121,8 @@ std::shared_ptr<InputIdentity> InputMapper::getInputIdentity(InputDeviceGroup de
// No identity found, create new
auto newIdentity = std::make_shared<InputIdentity>(deviceGroup, gamepadId);
// Only add to newInputIdentities if it is allowed, otherwise it will be discarded when possible anyway
if (allowNewInputIdentities) {
inputIdentities.insert(newIdentity);
newInputIdentities.insert(newIdentity);
}
inputIdentities.insert(newIdentity);
newInputIdentities.insert(newIdentity);
return newIdentity;
}

View file

@ -25,8 +25,6 @@ public:
std::set<std::shared_ptr<InputIdentity>> getInputIdentities();
bool allowNewInputIdentities = true;
public:
std::set<std::shared_ptr<InputIdentity>> newInputIdentities;
std::set<std::shared_ptr<InputIdentity>> deprecatedInputIdentities;

View file

@ -0,0 +1,5 @@
//
// Created by max on 27.04.23.
//
#include "input_handler.h"

14
src/game/input_handler.h Normal file
View file

@ -0,0 +1,14 @@
//
// Created by max on 27.04.23.
//
#ifndef HOLESOME_INPUT_HANDLER_H
#define HOLESOME_INPUT_HANDLER_H
class InputHandler {
};
#endif //HOLESOME_INPUT_HANDLER_H

View file

@ -1,39 +0,0 @@
#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, REFERENCE_SIZE.y},
.initialCenter = REFERENCE_SIZE / 2.0f,
};
view = std::make_shared<TrackingView>(options);
addDetachedChild(view);
}
void GlobalLayer::add(const std::shared_ptr<GameObject>& gameObject)
{
addDetachedChild(gameObject);
}

View file

@ -1,27 +0,0 @@
#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

View file

@ -7,35 +7,24 @@
#include "../../logging/easylogging++.h"
#include "../collectables/collectable_in_level.hpp"
#include <vector>
#include "../../sprites/tiling/tilemap_config.hpp"
#include "../../sprites/sprite_factory.hpp"
struct LevelConfig
{
std::string name;
int durationInSeconds = 0;
sf::Vector2i worldMapSize = {};
TileMapConfig tileMapConfig = {};
sf::Vector2f worldMapSize = {};
std::vector<GridCoordinates> playerSpawnPoints = {};
std::vector<CollectableInLevel> collectables = {};
std::vector<sf::Color> skyColors = {};
LevelConfig(std::string name,
int durationInSeconds,
const sf::Vector2f &worldMapSize,
const std::vector<GridCoordinates> &playerSpawnPoints,
const std::vector<CollectableInLevel> &collectables,
std::vector<sf::Color> skyColors,
const TileMapConfig &tileMapConfig)
const std::vector<CollectableInLevel> &collectables = {})
: name(std::move(name)),
durationInSeconds(durationInSeconds),
playerSpawnPoints(playerSpawnPoints),
tileMapConfig(tileMapConfig),
skyColors(std::move(skyColors))
worldMapSize(worldMapSize),
playerSpawnPoints(playerSpawnPoints)
{
worldMapSize = tileMapConfig.getSize();
// Remove invalid collectables
for (auto &collectable: collectables)
for (auto &collectable : collectables)
{
if (collectable.isValid())
{

View file

@ -1,134 +0,0 @@
#include "level_generator.hpp"
LevelConfig LevelGenerator::generateLevel(const std::string &name)
{
int levelSize = 50;
TileMapConfig map = generateRandomMap(levelSize, levelSize);
std::vector<CollectableInLevel> collectables = generateRandomCollectables(levelSize, levelSize);
return LevelConfig(name,
90,
{
{1, 2},
{2, 1},
{0, 3},
{3, 0}
},
collectables,
{
// Blues
sf::Color(2, 100, 234),
sf::Color(2, 100, 234),
sf::Color(2, 100, 234),
sf::Color(2, 195, 234),
// Neutral
sf::Color::White,
// Browns
sf::Color(163, 128, 68),
sf::Color(100, 80, 40),
sf::Color(20, 18, 11),
sf::Color::Black
},
map
);
}
TileMapConfig LevelGenerator::generateRandomMap(int width, int height)
{
int tileVariants = 6 * 2;
std::vector<std::vector<int>> tiles;
for (int h = 0; h < height; h++)
{
std::vector<int> row;
for (int w = 0; w < width; w++)
{
row.push_back(rand() % tileVariants);
}
tiles.push_back(row);
}
return TileMapConfig("iso-tiles", tiles);
}
std::vector<CollectableInLevel> LevelGenerator::generateRandomCollectables(int width, int height)
{
std::vector<CollectableInLevel> collectables;
// Add rose fields
for (int i = 0; i < 20; i++)
{
// Random coordinates for center of rose field with enough distance from edges
int x = rand() % int(width - 2 * roseFieldRadius) + roseFieldRadius;
int y = rand() % int(height - 2 * roseFieldRadius) + roseFieldRadius;
GridCoordinates fieldCenter = GridCoordinates(x, y);
// Add rose field by creating a dense field of roses around the center
int roseCount = rand() % 15 + 5;
for (int j = 0; j < roseCount; j++)
{
// Use cosine distribution to get a more dense field around the center
float angle = rand() % 360;
float radius = rand() % 1000 / 1000.f;
// Skew radius to avoid too dense at center
radius = pow(radius, 0.3f) * roseFieldRadius;
float xOffset = radius * cos(angle);
float yOffset = radius * sin(angle);
GridCoordinates roseCoordinates = GridCoordinates(fieldCenter.x + xOffset, fieldCenter.y + yOffset);
collectables.push_back(CollectableInLevel("rose", roseCoordinates));
}
}
// Place rows of lanterns
int rowCount = rand() % 3 + 2;
int heightDelta = height / (rowCount + 1);
int stepSize = 7;
for (int i = 0; i < rowCount; i++)
{
int y = (i + 1) * heightDelta;
for (int j = stepSize; j < width; j += stepSize)
{
collectables.push_back(CollectableInLevel("lantern", GridCoordinates(j, y)));
}
}
// Place random bikes
int bikeCount = rand() % 5 + 5;
for (int i = 0; i < bikeCount; i++)
{
int x = rand() % width;
int y = rand() % height;
collectables.push_back(CollectableInLevel("bike", GridCoordinates(x, y)));
}
// Place random rose bushes
int roseBushCount = rand() % 40 + 40;
for (int i = 0; i < roseBushCount; i++)
{
int x = rand() % width;
int y = rand() % height;
collectables.push_back(CollectableInLevel("rose-bush", GridCoordinates(x, y)));
}
// Place random stones
int stoneCount = rand() % 20 + 20;
for (int i = 0; i < stoneCount; i++)
{
int x = rand() % width;
int y = rand() % height;
collectables.push_back(CollectableInLevel("stone", GridCoordinates(x, y)));
}
// Place a single tree
int x = rand() % width;
int y = rand() % height;
collectables.push_back(CollectableInLevel("small-tree", GridCoordinates(x, y)));
return collectables;
}

View file

@ -1,21 +0,0 @@
#ifndef HOLESOME_LEVEL_GENERATOR_HPP
#define HOLESOME_LEVEL_GENERATOR_HPP
#include "level_config.hpp"
class LevelGenerator
{
public:
static LevelConfig generateLevel(const std::string& name);
static TileMapConfig generateRandomMap(int width, int height);
static std::vector<CollectableInLevel> generateRandomCollectables(int width, int height);
private:
static const inline float roseFieldRadius = 3.f;
};
#endif //HOLESOME_LEVEL_GENERATOR_HPP

View file

@ -6,12 +6,7 @@
#include "../collectables/collection/collectables_collection.hpp"
#include "../collectables/collectable_factory.hpp"
#include "../player/player_collection.hpp"
#include "level_renderer.hpp"
#include "../../sprites/skymap/skymap.hpp"
#include "../camera/multiplayer_view.hpp"
#include "../physics/holes/holes_simulation.hpp"
#include "../physics/holes/layouts/hole_layout.hpp"
#include "../layer/global_layer.hpp"
#include "../physics/hole/hole_depth_simulation.hpp"
void LevelLoader::loadLevel(const LevelConfig &levelConfig)
{
@ -21,45 +16,24 @@ void LevelLoader::loadLevel(const LevelConfig &levelConfig)
LOG(INFO) << "Loading level '" << levelConfig.name << "' ...";
game->setLevel(levelConfig);
InputMapper::getInstance()->allowNewInputIdentities = false;
MapSimulation::getInstance()->resetMap(levelConfig.worldMapSize);
HolesSimulation::getInstance()->clear();
GlobalLayer::getInstance()->clear();
HoleLayout::getInstance()->clear();
MultiplayerView::getInstance()->clear();
PlayerCollection::getInstance()->clear();
// Add views
game->addGameObject(MultiplayerView::getInstance());
// Add rendered level objects
std::shared_ptr<LevelRenderer> levelRenderer = std::make_shared<LevelRenderer>();
game->addGameObject(levelRenderer);
levelRenderer->addChild(std::make_shared<Skymap>(levelConfig.skyColors, levelConfig.worldMapSize));
levelRenderer->addChild(SpriteFactory::createTileMap(levelConfig.tileMapConfig));
if (DB_WORLD_GRID_RENDER)
// Add basic game objects
if (DEVELOPER_MODE)
{
levelRenderer->addChild(std::make_shared<GridDebugLayer>(0, 50, 0, 50));
game->addGameObject(std::make_shared<GridDebugLayer>(0, 50, 0, 50));
}
levelRenderer->addChild(PlayerCollection::getInstance());
game->addGameObject(std::make_shared<TrackingView>());
game->addGameObject(PlayerCollection::getInstance());
PlayerCollection::getInstance()->setSpawnPoints(levelConfig.playerSpawnPoints);
PlayerCollection::getInstance()->resetPlayers();
PlayerCollection::getInstance()->activatePlayers();
// Prepare collectables framework
auto maxDepth = (int) levelConfig.worldMapSize.x * 2;
auto collectablesCollection = CollectablesCollection::getInstance();
collectablesCollection->createEmpty(maxDepth);
levelRenderer->addChild(collectablesCollection);
// Add physics simulations
game->addGameObject(MapSimulation::getInstance());
game->addGameObject(HolesSimulation::getInstance());
game->addGameObject(HoleLayout::getInstance());
game->addGameObject(collectablesCollection);
// Spawn collectibles
for (auto const &collectableInfo: levelConfig.collectables)
@ -67,10 +41,14 @@ void LevelLoader::loadLevel(const LevelConfig &levelConfig)
spawnCollectable(collectableInfo);
}
game->startCountdown(levelConfig.durationInSeconds);
// Must be last
game->addGameObject(GlobalLayer::getInstance());
// Add physics simulations
game->addGameObject(MapSimulation::getInstance());
// For every depth, add a hole depth simulation
for (int depth = 0; depth < maxDepth; depth++)
{
auto depthCollection = collectablesCollection->getDepthCollection(depth);
game->addGameObject(std::make_shared<HoleDepthSimulation>(depthCollection));
}
LOG(INFO) << "Finished loading level '" << levelConfig.name << "'.";
}
@ -91,20 +69,6 @@ void LevelLoader::spawnCollectable(const CollectableInLevel &collectableInfo)
{
LOG(INFO) << "Spawning collectable '" << collectableInfo.name << "' ...";
auto collectable = CollectableFactory::createFromInLevelConfig(collectableInfo);
LOG(INFO) << "Has depth " << collectable->getDepth() << ".";
LOG(DEBUG) << "Has depth " << collectable->getDepth() << ".";
CollectablesCollection::getInstance()->add(collectable);
HolesSimulation::getInstance()->addCollectable(collectable);
}
void LevelLoader::cleanUp()
{
auto game = Game::getInstance();
game->clearGameObjects();
game->setLevel(LevelConfig());
HolesSimulation::getInstance()->clear();
PlayerCollection::getInstance()->resetPlayers();
GlobalLayer::getInstance()->clear();
HoleLayout::getInstance()->clear();
LOG(INFO) << "Cleaned up level.";
}

View file

@ -12,8 +12,6 @@ public:
static void loadLevel(const std::string &levelName);
static void cleanUp();
static void spawnCollectable(const CollectableInLevel &collectableInfo);
};

View file

@ -1,15 +0,0 @@
#include "level_renderer.hpp"
#include "../camera/multiplayer_view.hpp"
#include "../../logging/easylogging++.h"
void LevelRenderer::draw(sf::RenderWindow *window)
{
for(auto &view : MultiplayerView::getInstance()->getViews())
{
view->setViewForWindow();
GameObject::draw(window);
}
// Render borders between views
MultiplayerView::getInstance()->draw(window);
}

View file

@ -1,14 +0,0 @@
#ifndef HOLESOME_LEVEL_RENDERER_HPP
#define HOLESOME_LEVEL_RENDERER_HPP
#include "../game_object.h"
class LevelRenderer : public GameObject
{
public:
void draw(sf::RenderWindow *window) override;
};
#endif //HOLESOME_LEVEL_RENDERER_HPP

View file

@ -1,78 +0,0 @@
#include "body_adapter.hpp"
#include "../../config.h"
BodyAdapter::BodyAdapter(std::shared_ptr<b2World> world)
: world(std::move(world)), bodyObject(nullptr)
{}
BodyAdapter::~BodyAdapter()
{
destroyBody();
}
void BodyAdapter::destroyBody()
{
if (bodyObject != nullptr)
{
world->DestroyBody(bodyObject);
}
}
void BodyAdapter::createSquare(b2BodyType type, sf::Vector2f center, sf::Vector2f size)
{
destroyBody();
b2BodyDef bodyDef;
bodyDef.type = type;
// bodyDef.angularDamping = COLLECTABLES_SIM_ANGULAR_DAMPING;
// bodyDef.linearDamping = COLLECTABLES_SIM_LINEAR_DAMPING;
bodyDef.position.Set(center.x, center.y);
bodyObject = world->CreateBody(&bodyDef);
setBoxSize(size);
}
b2Body *BodyAdapter::body() const
{
return bodyObject;
}
sf::Vector2f BodyAdapter::getCenter() const
{
auto position = bodyObject->GetPosition();
return {position.x, position.y};
}
sf::Vector2f BodyAdapter::getSize() const
{
return boundingBox;
}
void BodyAdapter::setCenter(sf::Vector2f center)
{
bodyObject->SetTransform({center.x, center.y}, bodyObject->GetAngle());
}
void BodyAdapter::setBoxSize(sf::Vector2f size)
{
// Remove old fixture
if (bodyObject->GetFixtureList() != nullptr)
{
bodyObject->DestroyFixture(bodyObject->GetFixtureList());
}
// Recreates a square fixture
b2PolygonShape shape;
shape.SetAsBox(size.x / 2.f, size.y / 2.f);
boundingBox = size;
b2FixtureDef fixtureDef;
fixtureDef.shape = &shape;
// fixtureDef.friction = COLLECTABLES_SIM_FRICTION;
// fixtureDef.restitution = COLLECTABLES_SIM_RESTITUTION;
fixtureDef.density = COLLECTABLES_SIM_DENSITY;
bodyObject->CreateFixture(&fixtureDef);
}

View file

@ -1,37 +0,0 @@
#ifndef HOLESOME_BODY_ADAPTER_HPP
#define HOLESOME_BODY_ADAPTER_HPP
#include <box2d/box2d.h>
#include <memory>
#include <SFML/System/Vector2.hpp>
class BodyAdapter
{
public:
explicit BodyAdapter(std::shared_ptr<b2World> world);
~BodyAdapter();
[[nodiscard]] b2Body *body() const;
void createSquare(b2BodyType type, sf::Vector2f center, sf::Vector2f size);
[[nodiscard]] sf::Vector2f getCenter() const;
void setCenter(sf::Vector2f center);
[[nodiscard]] sf::Vector2f getSize() const;
void setBoxSize(sf::Vector2f size);
private:
std::shared_ptr<b2World> world;
b2Body *bodyObject;
sf::Vector2f boundingBox;
void destroyBody();
};
#endif //HOLESOME_BODY_ADAPTER_HPP

View file

@ -0,0 +1,136 @@
#include "hole_depth_simulation.hpp"
#include "../../../config.h"
#include "../../player/player_collection.hpp"
HoleDepthSimulation::HoleDepthSimulation(std::shared_ptr<CollectablesDepthCollection> collectables) : collectables(
std::move(collectables))
{
simWorld = std::make_shared<b2World>(WORLD_GRAVITY);
}
void HoleDepthSimulation::physicsUpdate()
{
updateBodiesFromCollectionHistory();
preparePlayers();
updateGroundBodies();
simWorld->Step(FRAME_TIME.asSeconds(), HOLESIM_VELOCITY_ITERATIONS, HOLESIM_POSITION_ITERATIONS);
updateCollectables();
}
void HoleDepthSimulation::updateBodiesFromCollectionHistory()
{
// Remove bodies that are no longer in the collection
for (auto &collectable: collectables->recentlyRemoved)
{
auto simCollectable = simCollectables.at(collectable->getId());
simWorld->DestroyBody(simCollectable.body);
simCollectables.erase(collectable->getId());
}
// Create bodies for new collectables
for (auto &collectable: collectables->recentlyAdded)
{
simCollectables[collectable->getId()] = SimCollectable(collectable, createBody(collectable));
}
}
void HoleDepthSimulation::updateCollectables()
{
for (auto &[id, simCollectable]: simCollectables)
{
auto collectable = simCollectable.collectable;
auto body = simCollectable.body;
auto position = body->GetPosition();
collectable->coordinates->set(IsometricCoordinates{
position.x,
position.y,
collectable->coordinates->isometric().depth
});
collectable->simParameters.angle = body->GetAngle();
collectable->simParameters.linearVelocity = body->GetLinearVelocity();
collectable->simParameters.angularVelocity = body->GetAngularVelocity();
collectable->setSpriteAngle(collectable->simParameters.angle);
}
}
b2Body *HoleDepthSimulation::createBody(const std::shared_ptr<Collectable> &collectable)
{
b2BodyDef bodyDef;
bodyDef.type = b2_dynamicBody;
bodyDef.position.Set(collectable->coordinates->isometric().x, collectable->coordinates->isometric().y);
bodyDef.linearDamping = HOLESIM_COLLECTABLE_LINEAR_DAMPING;
bodyDef.angularDamping = HOLESIM_COLLECTABLE_ANGULAR_DAMPING;
bodyDef.allowSleep = false;
bodyDef.awake = true;
auto body = simWorld->CreateBody(&bodyDef);
b2PolygonShape shape;
shape.SetAsBox(1, 1);
b2FixtureDef fixtureDef;
fixtureDef.shape = &shape;
fixtureDef.density = HOLESIM_COLLECTABLE_DENSITY;
fixtureDef.friction = HOLESIM_COLLECTABLE_FRICTION;
fixtureDef.restitution = HOLESIM_COLLECTABLE_RESTITUTION;
body->CreateFixture(&fixtureDef);
// Update simulation parameters from existing collectable
body->SetTransform(
b2Vec2{
collectable->coordinates->isometric().x,
collectable->coordinates->isometric().y
},
collectable->simParameters.angle);
body->SetLinearVelocity(collectable->simParameters.linearVelocity);
body->SetAngularVelocity(collectable->simParameters.angularVelocity);
return body;
}
int HoleDepthSimulation::getDepth() const
{
return collectables->depth;
}
void HoleDepthSimulation::preparePlayers()
{
for (const auto &player: PlayerCollection::getInstance()->getPlayers())
{
if (player->getDepth() == getDepth())
{
addToSim(player);
} else
{
removeFromSim(player);
}
}
}
void HoleDepthSimulation::addToSim(const std::shared_ptr<Player> &player)
{
int playerId = player->getPlayerId();
if (simPlayers.find(playerId) == simPlayers.end())
{
simPlayers[playerId] = HoleSimPlayer(player);
}
}
void HoleDepthSimulation::removeFromSim(const std::shared_ptr<Player> &player)
{
int playerId = player->getPlayerId();
if (simPlayers.find(playerId) != simPlayers.end())
{
simPlayers.erase(playerId);
}
}
void HoleDepthSimulation::updateGroundBodies()
{
}

View file

@ -0,0 +1,46 @@
#ifndef HOLESOME_HOLE_DEPTH_SIMULATION_HPP
#define HOLESOME_HOLE_DEPTH_SIMULATION_HPP
#include <memory>
#include <box2d/box2d.h>
#include <map>
#include "../../collectables/collection/collectables_depth_collection.hpp"
#include "hole_sim_player.hpp"
#include "../../collectables/sim_collectable.hpp"
/**
* @brief Simulates the hole(s) for the specific depth
*/
class HoleDepthSimulation : public GameObject
{
public:
explicit HoleDepthSimulation(std::shared_ptr<CollectablesDepthCollection> collectables);
void physicsUpdate() override;
[[nodiscard]] int getDepth() const;
private:
std::shared_ptr<CollectablesDepthCollection> collectables;
std::shared_ptr<b2World> simWorld;
std::map<int, HoleSimPlayer> simPlayers = {}; // Player ID -> HoleSimPlayer
void updateBodiesFromCollectionHistory();
void updateCollectables();
std::map<int, SimCollectable> simCollectables = {}; // Collectable ID -> Body
b2Body *createBody(const std::shared_ptr<Collectable> &collectable);
void preparePlayers();
void addToSim(const std::shared_ptr<Player> &player);
void removeFromSim(const std::shared_ptr<Player> &player);
void updateGroundBodies();
};
#endif //HOLESOME_HOLE_DEPTH_SIMULATION_HPP

View file

@ -0,0 +1,18 @@
#include "hole_sim_player.hpp"
void HoleSimPlayer::updateSimulationPosition()
{
xPosition = player->coordinates->isometric().x;
width = player->getIsoHoleWidth();
}
HoleSimPlayer::HoleSimPlayer(std::shared_ptr<Player> player)
{
this->player = std::move(player);
updateSimulationPosition();
}
HoleSimPlayer::HoleSimPlayer()
{
player = nullptr;
}

View file

@ -0,0 +1,23 @@
#ifndef HOLESOME_HOLE_SIM_PLAYER_HPP
#define HOLESOME_HOLE_SIM_PLAYER_HPP
#include <memory>
#include "../../player/player.hpp"
#include <box2d/box2d.h>
struct HoleSimPlayer
{
HoleSimPlayer();
std::shared_ptr<Player> player;
float xPosition = 0;
float width = 0;
explicit HoleSimPlayer(std::shared_ptr<Player> player);
void updateSimulationPosition();
};
#endif //HOLESOME_HOLE_SIM_PLAYER_HPP

View file

@ -1,52 +0,0 @@
#include "collectable_simulation.hpp"
#include "../../../logging/easylogging++.h"
#include "../../../config.h"
#include "layouts/hole_layout.hpp"
CollectableSimulation::CollectableSimulation(const std::shared_ptr<Collectable> &collectable)
: collectable(collectable), simulationDepth(collectable->getDepth())
{
// Create simulation
world = std::make_shared<b2World>(WORLD_GRAVITY);
world->SetAllowSleeping(COLLECTABLES_SIM_SLEEPING);
// Create ground
simGround = std::make_shared<CollectableSimGround>(world);
// Todo: Set proper ground width
// Create body
collectableBody = std::make_shared<BodyAdapter>(world);
auto coordinates = collectable->coordinates->diagonalWorld();
collectableBody->createSquare(b2_dynamicBody, {coordinates.horizontal, coordinates.vertical}, collectable->getSize());
}
void CollectableSimulation::physicsUpdate()
{
updateGroundHole();
world->Step(FRAME_TIME.asSeconds(), COLLECTABLES_SIM_VELOCITY_ITERATIONS, COLLECTABLES_SIM_POSITION_ITERATIONS);
updateCollectable();
}
std::shared_ptr<Collectable> CollectableSimulation::getCollectable() const
{
return collectable;
}
void CollectableSimulation::updateGroundHole()
{
auto holeLayout = HoleLayout::getInstance()->atDepth(simulationDepth);
simGround->createLayout(holeLayout);
}
void CollectableSimulation::updateCollectable()
{
auto bodyPosition = collectableBody->body()->GetPosition();
collectable->coordinates->setDiagonal({bodyPosition.x, bodyPosition.y, simulationDepth});
// Convert radians to degrees
float angle = -collectableBody->body()->GetAngle() * 180 / M_PI;
collectable->setRotation(angle);
}

View file

@ -1,37 +0,0 @@
#ifndef HOLESOME_COLLECTABLE_SIMULATION_HPP
#define HOLESOME_COLLECTABLE_SIMULATION_HPP
#include <box2d/box2d.h>
#include <map>
#include "../../game_object.h"
#include "../../collectables/collectable.hpp"
#include "ground/collectable_sim_ground_segment.hpp"
#include "ground/collectable_sim_ground.hpp"
#include "../body_adapter.hpp"
class CollectableSimulation : public GameObject
{
public:
explicit CollectableSimulation(const std::shared_ptr<Collectable> &collectable);
void physicsUpdate() override;
[[nodiscard]] std::shared_ptr<Collectable> getCollectable() const;
private:
std::shared_ptr<b2World> world;
std::shared_ptr<BodyAdapter> collectableBody;
float simulationDepth;
std::shared_ptr<Collectable> collectable;
std::shared_ptr<CollectableSimGround> simGround;
void updateGroundHole();
void updateCollectable();
};
#endif //HOLESOME_COLLECTABLE_SIMULATION_HPP

View file

@ -1,193 +0,0 @@
#include "collectable_sim_ground.hpp"
#include <utility>
#include "../../../../config.h"
void CollectableSimGround::closeAllHoles()
{
// Create one segment for the ground
createSegment(-groundWidth / 2.f, groundWidth / 2.f);
}
void CollectableSimGround::createLayout(DepthHoleLayout &layout)
{
if (!hasLayoutChanged(layout))
{
return;
}
currentLayoutHoles = layout.holes;
segments.clear();
if (currentLayoutHoles.empty())
{
closeAllHoles();
return;
}
// Sort holes from left to right
std::sort(currentLayoutHoles.begin(), currentLayoutHoles.end(),
[](DepthHoleDescription a, DepthHoleDescription b)
{
return a.x < b.x;
});
// Create segments for holes
float leftCorner = -groundWidth / 2.f;
float leftHoleId = -1;
for (auto &hole: currentLayoutHoles)
{
auto rightCorner = hole.x - hole.width / 2.f;
auto segment = createSegment(leftCorner, rightCorner);
if (segment != nullptr)
{
segment->rightHoleId = hole.playerId;
segment->leftHoleId = leftHoleId;
}
leftHoleId = hole.playerId;
leftCorner = hole.x + hole.width / 2.f;
}
// Create segment for the right side
auto segment = createSegment(leftCorner, groundWidth / 2.f);
if (segment != nullptr)
{
segment->leftHoleId = leftHoleId;
}
}
CollectableSimGround::CollectableSimGround(std::shared_ptr<b2World> world, float groundWidth)
: world(std::move(world)), groundWidth(groundWidth)
{
closeAllHoles();
}
void CollectableSimGround::setGroundWidth(float width)
{
if (width <= 0)
{
throw std::runtime_error("Ground width must be greater than 0");
}
if (width == groundWidth)
{
return;
}
groundWidth = width;
updateOuterSegmentsToWidth();
}
void CollectableSimGround::updateOuterSegmentsToWidth()
{
auto outerSegments = getOuterSegments();
auto borderPoints = groundWidth / 2.f;
// Left segment
if (outerSegments.left != nullptr)
{
auto leftSegment = outerSegments.left->body;
auto leftCenter = leftSegment->getCenter();
auto leftSize = leftSegment->getSize();
auto rightPoint = leftCenter.x + leftSize.x / 2.f;
createSegment(-borderPoints, rightPoint, outerSegments.left);
}
// Right segment
if (outerSegments.right != nullptr)
{
auto rightSegment = outerSegments.right->body;
auto rightCenter = rightSegment->getCenter();
auto rightSize = rightSegment->getSize();
auto leftPoint = rightCenter.x - rightSize.x / 2.f;
createSegment(leftPoint, borderPoints, outerSegments.right);
}
}
std::shared_ptr<CollectableSimGroundSegment> CollectableSimGround::createSegment(float leftCorner, float rightCorner,
std::shared_ptr<CollectableSimGroundSegment> segment)
{
if (leftCorner > rightCorner)
{
// Segment would have negative width, don't create it and leave empty instead
return segment;
}
if (segment == nullptr)
{
segment = std::make_shared<CollectableSimGroundSegment>(world);
segments.push_back(segment);
}
auto center = sf::Vector2f((leftCorner + rightCorner) / 2.f, -COLLECTABLES_SIM_GROUND_THICKNESS / 2.f);
auto size = sf::Vector2f(rightCorner - leftCorner, COLLECTABLES_SIM_GROUND_THICKNESS);
segment->body->createSquare(b2_kinematicBody, center, size);
return segment;
}
CollectableSimGround::SideSegments CollectableSimGround::getOuterSegments()
{
SideSegments sideSegments;
for (const auto &segment: segments)
{
if (segment->rightHoleId < 0)
{
sideSegments.right = segment;
}
if (segment->leftHoleId < 0)
{
sideSegments.left = segment;
}
if (sideSegments.left != nullptr && sideSegments.right != nullptr)
{
break;
}
}
return sideSegments;
}
bool CollectableSimGround::hasLayoutChanged(DepthHoleLayout &layout)
{
if (layout.holes.size() != currentLayoutHoles.size())
{
// Number of holes changed
return true;
}
// Below here: Number of holes is the same
if (currentLayoutHoles.empty())
{
// Both are empty, so no change
return false;
}
// Sort holes from left to right
std::sort(layout.holes.begin(), layout.holes.end(),
[](DepthHoleDescription a, DepthHoleDescription b)
{
return a.x < b.x;
});
// Compare holes
for (int i = 0; i < layout.holes.size(); i++)
{
auto currentHole = currentLayoutHoles[i];
auto newHole = layout.holes[i];
if (newHole.x != currentHole.x ||
newHole.width != currentHole.width)
{
// Hole changed
return true;
}
}
return false;
}

View file

@ -1,43 +0,0 @@
#ifndef HOLESOME_COLLECTABLE_SIM_GROUND_HPP
#define HOLESOME_COLLECTABLE_SIM_GROUND_HPP
#include <vector>
#include <memory>
#include "collectable_sim_ground_segment.hpp"
#include "../layouts/depth_hole_layout.hpp"
class CollectableSimGround
{
public:
explicit CollectableSimGround(std::shared_ptr<b2World> world, float groundWidth = 1000);
void setGroundWidth(float width);
void createLayout(DepthHoleLayout &layout);
private:
std::vector<std::shared_ptr<CollectableSimGroundSegment>> segments;
std::shared_ptr<b2World> world;
std::vector<DepthHoleDescription> currentLayoutHoles;
float groundWidth;
struct SideSegments {
std::shared_ptr<CollectableSimGroundSegment> left;
std::shared_ptr<CollectableSimGroundSegment> right;
};
CollectableSimGround::SideSegments getOuterSegments();
void updateOuterSegmentsToWidth();
std::shared_ptr<CollectableSimGroundSegment> createSegment(float leftCorner, float rightCorner, std::shared_ptr<CollectableSimGroundSegment> segment = nullptr);
void closeAllHoles();
bool hasLayoutChanged(DepthHoleLayout &layout);
};
#endif //HOLESOME_COLLECTABLE_SIM_GROUND_HPP

View file

@ -1,22 +0,0 @@
#ifndef HOLESOME_COLLECTABLE_SIM_GROUND_SEGMENT_HPP
#define HOLESOME_COLLECTABLE_SIM_GROUND_SEGMENT_HPP
#include <box2d/box2d.h>
#include <memory>
#include "../layouts/depth_hole_description.hpp"
#include "../../body_adapter.hpp"
class CollectableSimGroundSegment
{
public:
explicit CollectableSimGroundSegment(const std::shared_ptr<b2World>& world)
{
body = std::make_shared<BodyAdapter>(world);
}
int leftHoleId = -1;
int rightHoleId = -1;
std::shared_ptr<BodyAdapter> body;
};
#endif //HOLESOME_COLLECTABLE_SIM_GROUND_SEGMENT_HPP

View file

@ -1,63 +0,0 @@
#include "holes_simulation.hpp"
std::shared_ptr<HolesSimulation> HolesSimulation::getInstance()
{
if (singletonInstance == nullptr)
{
singletonInstance = std::make_shared<HolesSimulation>();
}
return singletonInstance;
}
std::vector<std::shared_ptr<CollectableSimulation>> HolesSimulation::getCollectableSimulations() const
{
std::vector<std::shared_ptr<CollectableSimulation>> collectableSimulations{};
for (auto &child: getChildren())
{
auto sim = std::dynamic_pointer_cast<CollectableSimulation>(child);
collectableSimulations.push_back(sim);
}
return collectableSimulations;
}
void HolesSimulation::clear()
{
clearChildren();
}
void HolesSimulation::addCollectable(const std::shared_ptr<Collectable> &collectable)
{
// Create new collectable simulation
auto collectableSim = std::make_shared<CollectableSimulation>(collectable);
addDetachedChild(collectableSim);
}
void HolesSimulation::lateUpdate()
{
// Remove disabled collectables
for (const auto &sim: getCollectableSimulations())
{
if (sim->getCollectable()->getActive())
{
continue;
}
removeChild(sim);
}
GameObject::lateUpdate();
}
void HolesSimulation::removeCollectable(int collectableId)
{
for (const auto &sim: getCollectableSimulations())
{
if (sim->getCollectable()->getId() == collectableId)
{
removeChild(sim);
return;
}
}
}

View file

@ -1,29 +0,0 @@
#ifndef HOLESOME_HOLES_SIMULATION_HPP
#define HOLESOME_HOLES_SIMULATION_HPP
#include <memory>
#include "../../game_object.h"
#include "collectable_simulation.hpp"
class HolesSimulation : public GameObject
{
public:
static std::shared_ptr<HolesSimulation> getInstance();
void addCollectable(const std::shared_ptr<Collectable> &collectable);
void removeCollectable(int collectableId);
void lateUpdate() override;
void clear();
std::vector<std::shared_ptr<CollectableSimulation>> getCollectableSimulations() const;
private:
static inline std::shared_ptr<HolesSimulation> singletonInstance = nullptr;
};
#endif //HOLESOME_HOLES_SIMULATION_HPP

View file

@ -1,15 +0,0 @@
#ifndef HOLESOME_DEPTH_HOLE_DESCRIPTION_HPP
#define HOLESOME_DEPTH_HOLE_DESCRIPTION_HPP
struct DepthHoleDescription
{
int playerId;
float x;
float width;
DepthHoleDescription(int playerId, float x, float width)
: playerId(playerId), x(x), width(width)
{}
};
#endif //HOLESOME_DEPTH_HOLE_DESCRIPTION_HPP

View file

@ -1,38 +0,0 @@
#ifndef HOLESOME_DEPTH_HOLE_LAYOUT_HPP
#define HOLESOME_DEPTH_HOLE_LAYOUT_HPP
#include <vector>
#include "depth_hole_description.hpp"
#include "../../../../logging/easylogging++.h"
struct DepthHoleLayout
{
float depth;
std::vector<DepthHoleDescription> holes;
DepthHoleLayout(float depth, std::vector<DepthHoleDescription> holes)
: depth(depth), holes(std::move(holes))
{}
[[nodiscard]] DepthHoleDescription getHole(int playerId) const
{
for (const auto &hole: holes)
{
if (hole.playerId == playerId)
{
return hole;
}
}
throw std::runtime_error(
"No hole for player " + std::to_string(playerId) + " at depth " + std::to_string(depth) +
" in layout.");
}
[[nodiscard]] bool hasHole(int playerId) const
{
return std::ranges::any_of(holes, [playerId](const auto &hole) { return hole.playerId == playerId; });
}
};
#endif //HOLESOME_DEPTH_HOLE_LAYOUT_HPP

View file

@ -1,44 +0,0 @@
#ifndef HOLESOME_HOLE_DESCRIPTION_HPP
#define HOLESOME_HOLE_DESCRIPTION_HPP
#include <SFML/System/Vector2.hpp>
#include "../../../player/player.hpp"
#include "../../../../utilities/vector_utils.hpp"
#include "depth_hole_description.hpp"
struct HoleDescription
{
int playerId;
sf::Vector2f worldPosition;
float radius;
explicit HoleDescription(const std::shared_ptr<Player> &player)
{
playerId = player->getPlayerId();
radius = player->getWorldRadius();
auto world3d = player->coordinates->world();
worldPosition = sf::Vector2f(world3d.x, world3d.y);
}
[[nodiscard]] DepthHoleDescription atDepth(float depth) const
{
auto closestPointAtDepth = CoordinateTransformer::closestWorldPointAtDepth(depth, worldPosition);
auto distance = length(worldPosition - closestPointAtDepth);
if (distance > radius)
{
// No hole at this depth
return {playerId, 0.f, 0.f};
}
float chordLength = 2.f * std::sqrt(radius * radius - distance * distance);
auto diagonalWorldCoords = CoordinateTransformer::worldToDiagonal(WorldCoordinates{worldPosition});
return {playerId, diagonalWorldCoords.horizontal, chordLength};
}
};
#endif //HOLESOME_HOLE_DESCRIPTION_HPP

View file

@ -1,50 +0,0 @@
#include "hole_layout.hpp"
#include "../../../player/player_collection.hpp"
std::shared_ptr<HoleLayout> HoleLayout::getInstance()
{
if (singletonInstance == nullptr)
{
singletonInstance = std::make_shared<HoleLayout>();
}
return singletonInstance;
}
void HoleLayout::clear()
{
currentHoles.clear();
}
void HoleLayout::lateUpdate()
{
currentHoles.clear();
// Collect hole descriptions of active players
for (const auto &player: PlayerCollection::getInstance()->getPlayers())
{
if (player->getActive())
{
currentHoles.emplace_back(player);
}
}
}
HoleLayout::HoleLayout()
: currentHoles()
{}
DepthHoleLayout HoleLayout::atDepth(float depth) const
{
std::vector<DepthHoleDescription> depthHoles{};
for (const auto &hole: currentHoles)
{
auto holeAtDepth = hole.atDepth(depth);
if (holeAtDepth.width <= 0)
{
continue;
}
depthHoles.push_back(holeAtDepth);
}
return {depth, depthHoles};
}

View file

@ -1,30 +0,0 @@
#ifndef HOLESOME_HOLE_LAYOUT_HPP
#define HOLESOME_HOLE_LAYOUT_HPP
#include <memory>
#include "../../../game_object.h"
#include "hole_description.hpp"
#include "depth_hole_layout.hpp"
class HoleLayout : public GameObject
{
public:
HoleLayout();
static std::shared_ptr<HoleLayout> getInstance();
void lateUpdate() override;
void clear();
[[nodiscard]] DepthHoleLayout atDepth(float depth) const;
private:
static inline std::shared_ptr<HoleLayout> singletonInstance = nullptr;
std::vector<HoleDescription> currentHoles;
};
#endif //HOLESOME_HOLE_LAYOUT_HPP

View file

@ -1,5 +1,4 @@
#include "map_player.hpp"
#include "map_simulation.hpp"
void MapPlayer::updateSimulationPosition()
{
@ -21,7 +20,7 @@ void MapPlayer::updateSimulationPosition()
void MapPlayer::updatePlayerPosition() const
{
b2Vec2 playerPosition = body->GetPosition();
player->coordinates->setWorld({playerPosition.x, playerPosition.y});
player->coordinates->set(sf::Vector2f(playerPosition.x, playerPosition.y));
}
void MapPlayer::updateShape()
@ -29,8 +28,7 @@ void MapPlayer::updateShape()
shapeRadius = player->getWorldRadius();
b2Fixture *oldFixture = body->GetFixtureList();
if (oldFixture != nullptr)
{
if (oldFixture != nullptr) {
body->DestroyFixture(oldFixture);
}
@ -43,58 +41,4 @@ void MapPlayer::updateShape()
fixtureDef.density = 1.0f;
body->CreateFixture(&fixtureDef);
}
void MapPlayer::updateCollidingWithPlayers()
{
collidingWithPlayers.clear();
for (b2ContactEdge *contactEdge = body->GetContactList(); contactEdge != nullptr; contactEdge = contactEdge->next)
{
b2Contact *contact = contactEdge->contact;
if (!contact->IsTouching())
{
continue;
}
b2Fixture *fixtureA = contact->GetFixtureA();
b2Fixture *fixtureB = contact->GetFixtureB();
if (fixtureA->IsSensor() || fixtureB->IsSensor())
{
continue;
}
b2Body *bodyA = fixtureA->GetBody();
b2Body *bodyB = fixtureB->GetBody();
if (bodyA->GetType() != b2_dynamicBody || bodyB->GetType() != b2_dynamicBody)
{
continue;
}
std::shared_ptr<MapPlayer> mapPlayerA = MapSimulation::getInstance()->getMapPlayerByBody(bodyA);
std::shared_ptr<MapPlayer> mapPlayerB = MapSimulation::getInstance()->getMapPlayerByBody(bodyB);
if (mapPlayerA == nullptr || mapPlayerB == nullptr)
{
continue;
}
if (mapPlayerA->player->getPlayerId() == player->getPlayerId())
{
collidingWithPlayers.push_back(mapPlayerB->player->getPlayerId());
} else
{
collidingWithPlayers.push_back(mapPlayerA->player->getPlayerId());
}
}
player->setCollidingPlayers(collidingWithPlayers);
}
void MapPlayer::updatePlayer()
{
updatePlayerPosition();
updateCollidingWithPlayers();
}
}

View file

@ -14,7 +14,6 @@ public:
std::shared_ptr<Player> player;
b2Body *body;
float shapeRadius = 0;
std::vector<int> collidingWithPlayers = {};
MapPlayer(std::shared_ptr<Player> player, b2Body *body) : player(std::move(player)), body(body)
{
@ -22,15 +21,10 @@ public:
}
void updateSimulationPosition();
void updatePlayer();
void updateShape();
private:
void updatePlayerPosition() const;
void updateCollidingWithPlayers();
void updateShape();
};

View file

@ -26,35 +26,29 @@ void MapSimulation::physicsUpdate()
world->Step(FRAME_TIME.asSeconds(), MAPSIM_VELOCITY_ITERATIONS, MAPSIM_POSITION_ITERATIONS);
// Update players
// Update player positions
for (auto &mapPlayer: mapPlayersById)
{
mapPlayer.second->updatePlayer();
mapPlayer.second->updatePlayerPosition();
}
}
void MapSimulation::resetMap(sf::Vector2<int> worldMapSize)
void MapSimulation::resetMap(sf::Vector2f worldMapSize)
{
// Clear all players
for (auto &[id, _]: mapPlayersById)
for (auto &mapPlayer: mapPlayersById)
{
world->DestroyBody(mapPlayersById[id]->body);
removePlayer(mapPlayer.second->player);
}
mapPlayersById.clear();
// No gravity, since this a top-down view of the map
world = std::make_shared<b2World>(b2Vec2(0.0f, 0.0f));
// Create map borders
constructSquareObstacle(-MAPSIM_WALL_THICKNESS, -MAPSIM_WALL_THICKNESS,
0, worldMapSize.y + MAPSIM_WALL_THICKNESS); // Bottom left
constructSquareObstacle(0, -MAPSIM_WALL_THICKNESS,
worldMapSize.x, 0); // Bottom right
constructSquareObstacle(worldMapSize.x, -MAPSIM_WALL_THICKNESS,
worldMapSize.x + MAPSIM_WALL_THICKNESS,
worldMapSize.y + MAPSIM_WALL_THICKNESS); // Top right
constructSquareObstacle(0, worldMapSize.y,
worldMapSize.x, worldMapSize.y + MAPSIM_WALL_THICKNESS); // Top left
constructSquareObstacle(-MAPSIM_WALL_THICKNESS, -MAPSIM_WALL_THICKNESS, 0, worldMapSize.y + MAPSIM_WALL_THICKNESS); // Bottom left
constructSquareObstacle(0, -MAPSIM_WALL_THICKNESS, worldMapSize.x, 0); // Bottom right
constructSquareObstacle(worldMapSize.x, -MAPSIM_WALL_THICKNESS, worldMapSize.x + MAPSIM_WALL_THICKNESS, worldMapSize.y + MAPSIM_WALL_THICKNESS); // Top right
constructSquareObstacle(0, worldMapSize.y, worldMapSize.x, worldMapSize.y + MAPSIM_WALL_THICKNESS); // Top left
}
void MapSimulation::constructSquareObstacle(float minX, float minY, float maxX, float maxY)
@ -105,27 +99,3 @@ void MapSimulation::removePlayer(std::shared_ptr<Player> &player)
world->DestroyBody(mapPlayersById[playerId]->body);
mapPlayersById.erase(playerId);
}
std::shared_ptr<MapPlayer> MapSimulation::getMapPlayerByBody(b2Body *body) const
{
for (auto &mapPlayer: mapPlayersById)
{
if (mapPlayer.second->body == body)
{
return mapPlayer.second;
}
}
return nullptr;
}
void MapSimulation::removePlayer(int playerId)
{
// Still in simulation?
if (mapPlayersById.find(playerId) == mapPlayersById.end())
{
return;
}
world->DestroyBody(mapPlayersById[playerId]->body);
mapPlayersById.erase(playerId);
}

View file

@ -16,15 +16,12 @@ public:
void physicsUpdate() override;
void resetMap(sf::Vector2<int> worldMapSize);
void resetMap(sf::Vector2f worldMapSize);
static std::shared_ptr<MapSimulation> getInstance();
void addPlayer(const std::shared_ptr<Player>& player);
void removePlayer(int playerId);
[[nodiscard]] std::shared_ptr<MapPlayer> getMapPlayerByBody(b2Body* body) const;
private:
static inline std::shared_ptr<MapSimulation> singletonInstance = nullptr;

View file

@ -1,24 +1,20 @@
#include "player.hpp"
#include "../../logging/easylogging++.h"
#include "player_collection.hpp"
#include "../../typography/font_manager.hpp"
#include "../physics/map/map_simulation.hpp"
#include <utility>
Player::Player(std::shared_ptr<InputIdentity> assignedInput, const std::string &skinRessourceName,
GridCoordinates initCoordinates)
: spawnPosition(initCoordinates), skinName(skinRessourceName)
: spawnPosition(initCoordinates)
{
playerId = playerCreationCounter++;
coordinates->setTranslated(spawnPosition);
coordinates->set(spawnPosition);
input = std::move(assignedInput);
skinSprite = std::make_shared<VersatileSprite>(skinRessourceName, getIsoSize());
addChildScreenOffset(skinSprite, IsometricCoordinates(-getIsoSize() / 2.f));
updateRadiusBasedOnPoints();
LOG(INFO) << "Player " << playerId << " created.";
}
@ -40,12 +36,22 @@ void Player::update()
return;
}
if (!passiveMode)
{
performInteractiveUpdates();
}
auto moveDirection = input->direction.asIsometricVector();
auto moveDelta = moveDirection * speed * FRAME_TIME.asSeconds();
coordinates->move(moveDelta);
handlePlayerCollisions();
if (input->isPerformingAction(GameAction::GROW))
{
setWorldRadius(radiusInWorld * (1 + PLAYER_PROPORTIONAL_SIZE_CHANGE_SPEED * FRAME_TIME.asSeconds()));
} else if (input->isPerformingAction(GameAction::SHRINK))
{
auto newRadius = radiusInWorld * (1 - PLAYER_PROPORTIONAL_SIZE_CHANGE_SPEED * FRAME_TIME.asSeconds());
if (newRadius <= DEFAULT_PLAYER_RADIUS)
{
newRadius = DEFAULT_PLAYER_RADIUS;
}
setWorldRadius(newRadius);
}
GameObject::update();
}
@ -74,7 +80,7 @@ float Player::getWorldRadius() const
sf::Vector2f Player::getIsoSize() const
{
// TODO: For some reason, the player is a little to narrow. This fixes it.
const float fixFactor = sqrt(2);
const float fixFactor = 1.4f;
float width = radiusInWorld * 2.f * WORLD_TO_ISO_SCALE * fixFactor;
return {width, width * ISOMETRIC_SKEW};
}
@ -89,152 +95,19 @@ void Player::setWorldRadius(float newWorldRadius)
skinSprite->coordinates->setScreenOffset(IsometricCoordinates(-newSize / 2.f));
}
int Player::getPoints() const
Player::~Player()
{
return points;
LOG(INFO) << "Player " << playerId << " destroyed.";
}
void Player::updateRadiusBasedOnPoints()
float Player::getIsoHoleWidth() const
{
int points = getPoints();
float newWorldRadius = PLAYER_MIN_RADIUS + PLAYER_RADIUS_PER_LEVEL * points / 10.f;
setWorldRadius(newWorldRadius);
// Assuming 10% of the player's size is border
float holeFactor = .9f;
return getIsoSize().x * holeFactor;
}
void Player::consume(int consumedPoints)
int Player::getDepth() const
{
setPoints(getPoints() + consumedPoints);
LOG(INFO) << "Player " << playerId << " consumed " << consumedPoints << " points. Total: " << this->points;
}
std::string Player::getSkinName() const
{
return skinName;
}
std::shared_ptr<InputIdentity> Player::getInput() const
{
return input;
}
void Player::setPassiveMode(bool newPassiveMode)
{
passiveMode = newPassiveMode;
}
void Player::performInteractiveUpdates()
{
if (input->isPerformingAction(GameAction::RUN))
{
speed = PLAYER_RUN_SPEED;
// Subtract cost
setPoints(points - PLAYER_RUN_SPEED_COST_PER_SECOND * FRAME_TIME.asSeconds());
} else
{
speed = DEFAULT_PLAYER_SPEED;
}
auto moveDirection = input->direction.asIsometricVector();
auto moveDelta = moveDirection * speed * FRAME_TIME.asSeconds();
coordinates->move(moveDelta);
}
void Player::setCollidingPlayers(const std::vector<int> &collidingPlayerIds)
{
collidingPlayers.clear();
for (auto collidingPlayerId: collidingPlayerIds)
{
collidingPlayers.push_back(PlayerCollection::getInstance()->getPlayerById(collidingPlayerId));
}
}
void Player::handlePlayerCollisions()
{
if (collidingPlayers.empty() || !isAlive)
{
return;
}
// Count number of players that are bigger than this player by the required margin
float numAttackingPlayers = 0;
for (const auto &collidingPlayer: collidingPlayers)
{
if (collidingPlayer->getPoints() - getPoints() >= POINT_DELTA_FOR_ATTACK)
{
numAttackingPlayers++;
}
}
// Reduce points if necessary
float damage = numAttackingPlayers * ATTACK_PER_SECOND * FRAME_TIME.asSeconds() *
(points + 1.f + fabs(PLAYER_ALIVE_THRESHOLD));
if (numAttackingPlayers > 0)
{
LOG(INFO) << "Player " << playerId << " is getting attacked by " << numAttackingPlayers << "players and lost "
<< damage << " points.";
}
setPoints(points - damage);
}
bool Player::getAlive() const
{
return isAlive;
}
void Player::setAlive(bool newAlive)
{
if (isAlive == newAlive)
{
return;
}
if (!newAlive)
{
setPassiveMode(true);
MapSimulation::getInstance()->removePlayer(getPlayerId());
}
isAlive = newAlive;
}
void Player::draw(sf::RenderWindow *window)
{
if (isAlive)
{
GameObject::draw(window);
return;
}
// Print "You were consumed" message
sf::Text text;
text.setFont(*FontManager::getInstance()->getDefaultFont());
text.setString("x-x");
text.setCharacterSize(15);
text.setFillColor(sf::Color::Red);
text.setOutlineColor(sf::Color::Black);
text.setOutlineThickness(2);
text.setPosition(coordinates->isometric().x - text.getLocalBounds().width / 2.f,
coordinates->isometric().y - text.getLocalBounds().height / 2.f);
window->draw(text);
}
void Player::setPoints(float newPoints)
{
points = newPoints;
updateRadiusBasedOnPoints();
if (points > highestPoints)
{
highestPoints = points;
} else if (points <= PLAYER_ALIVE_THRESHOLD)
{
setAlive(false);
}
}
int Player::getMaxPoints() const
{
return highestPoints;
return ceil(coordinates->isometric().depth);
}

View file

@ -13,6 +13,8 @@ public:
Player(std::shared_ptr<InputIdentity> assignedInput, const std::string &skinRessourceName,
GridCoordinates initCoordinates);
~Player();
void update() override;
[[nodiscard]] sf::Vector2f getTrackablePosition() const override;
@ -29,53 +31,20 @@ public:
[[nodiscard]] float getWorldRadius() const;
[[nodiscard]] int getPoints() const;
[[nodiscard]] float getIsoHoleWidth() const;
int getMaxPoints() const;
void consume(int consumedPoints);
std::string getSkinName() const;
void setAlive(bool newAlive);
[[nodiscard]] bool getAlive() const;
std::shared_ptr<InputIdentity> getInput() const;
void setPassiveMode(bool newPassiveMode);
void setCollidingPlayers(const std::vector<int>& collidingPlayerIds);
void draw(sf::RenderWindow *window) override;
[[nodiscard]] int getDepth() const;
TranslatedCoordinates spawnPosition;
private:
std::shared_ptr<InputIdentity> input;
float radiusInWorld = 0.5f; // In world units
float radiusInWorld = DEFAULT_PLAYER_RADIUS;
std::shared_ptr<VersatileSprite> skinSprite;
std::vector<std::shared_ptr<Player>> collidingPlayers;
void setPoints(float newPoints);
bool passiveMode = true;
bool isAlive = true;
std::string skinName;
float points = DEFAULT_PLAYER_POINTS;
float highestPoints = DEFAULT_PLAYER_POINTS;
int playerId;
static inline int playerCreationCounter = 0;
void setWorldRadius(float newWorldRadius);
void updateRadiusBasedOnPoints();
void performInteractiveUpdates();
void handlePlayerCollisions();
};

View file

@ -4,8 +4,7 @@
#include "../input/input_mapper.h"
#include "../../texture_config.h"
PlayerCollection::PlayerCollection(int maxPlayerCount)
: maxPlayerCount(maxPlayerCount)
PlayerCollection::PlayerCollection()
{
// Set default spawn point
setSpawnPoints({{0, 0}});
@ -13,7 +12,7 @@ PlayerCollection::PlayerCollection(int maxPlayerCount)
// Create player for existing input identities
for (auto &inputIdentity: InputMapper::getInstance()->getInputIdentities())
{
spawnPlayer(inputIdentity, nullptr);
spawnPlayer(inputIdentity);
}
}
@ -29,7 +28,8 @@ std::shared_ptr<PlayerCollection> PlayerCollection::getInstance()
void PlayerCollection::addPlayer(const std::shared_ptr<Player> &player)
{
newPlayerBuffer.push_back(player);
addDetachedChild(player);
players.push_back(player);
addChild(player);
}
void PlayerCollection::clear()
@ -37,18 +37,16 @@ void PlayerCollection::clear()
newPlayerBuffer.clear();
removedPlayerBuffer.clear();
nextSpawnPointIndex = 0;
// Fill in removed players
for (auto &child: getChildren())
for (auto &player: players)
{
auto player = std::dynamic_pointer_cast<Player>(child);
if (player != nullptr)
{
removedPlayerBuffer.push_back(player);
}
}
players.clear();
clearChildren();
}
@ -70,8 +68,7 @@ void PlayerCollection::lateUpdate()
{
if (child != nullptr && !child->getActive())
{
removedPlayerBuffer.push_back(std::dynamic_pointer_cast<Player>(child));
removeChild(child);
removePlayer(std::dynamic_pointer_cast<Player>(child));
}
}
}
@ -79,6 +76,7 @@ void PlayerCollection::lateUpdate()
void PlayerCollection::removePlayer(const std::shared_ptr<Player> &player)
{
removedPlayerBuffer.push_back(player);
players.erase(std::remove(players.begin(), players.end(), player), players.end());
removeChild(player);
}
@ -98,143 +96,17 @@ void PlayerCollection::setSpawnPoints(std::vector<GridCoordinates> newSpawnPoint
nextSpawnPointIndex = 0;
}
void PlayerCollection::spawnPlayer(const std::shared_ptr<InputIdentity> &inputIdentity, std::string skin)
void PlayerCollection::spawnPlayer(const std::shared_ptr<InputIdentity> &inputIdentity)
{
// Get proper Spawn point, if available
auto spawn = spawnPoints[nextSpawnPointIndex];
nextSpawnPointIndex = static_cast<int>((nextSpawnPointIndex + 1) % spawnPoints.size());
if (skin.empty())
{
skin = getNextSkin();
}
auto player = std::make_shared<Player>(inputIdentity, skin, spawn);
addPlayer(player);
auto player = std::make_shared<Player>(inputIdentity, PLAYER_SKIN, spawn);
PlayerCollection::getInstance()->addPlayer(player);
}
std::vector<std::shared_ptr<Player>> PlayerCollection::getPlayers() const
{
std::vector<std::shared_ptr<Player>> players = {};
for (auto &child: getChildren())
{
auto player = std::dynamic_pointer_cast<Player>(child);
if (player != nullptr)
{
players.push_back(player);
}
}
return players;
}
std::shared_ptr<Player> PlayerCollection::getPlayerById(int playerId) const
{
for (auto &player: getPlayers())
{
if (player->getPlayerId() == playerId)
{
return player;
}
}
throw std::runtime_error("Player with id " + std::to_string(playerId) + " not found");
}
int PlayerCollection::getMaxPlayerCount() const
{
return 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;
}
std::string PlayerCollection::getNextSkin()
{
int numberOfSkins = static_cast<int>(PLAYER_SKINS.size());
// Count how often each skin index is taken
std::map<int, int> skinIndexCount;
for (int i = 0; i < numberOfSkins; i++)
{
skinIndexCount[i] = 0;
for (auto &takenSkinIndex: takenSkinIndices)
{
if (takenSkinIndex == i)
{
skinIndexCount[i]++;
}
}
}
// Find skin index with lowest count
int skinIndex = takenSkinIndices.size() % numberOfSkins;
int lowestCount = takenSkinIndices.size();
for (auto &pair: skinIndexCount)
{
if (pair.second < lowestCount)
{
lowestCount = pair.second;
skinIndex = pair.first;
}
}
takenSkinIndices.push_back(skinIndex);
return PLAYER_SKINS[skinIndex];
}
void PlayerCollection::activatePlayers()
{
for (auto player: getPlayers())
{
player->setPassiveMode(false);
}
}
void PlayerCollection::deactivatePlayers()
{
for (auto player: getPlayers())
{
player->setPassiveMode(true);
}
}
void PlayerCollection::resetPlayers()
{
// Recreate players
auto oldPlayers = getPlayers();
clear();
removedPlayerBuffer.clear();
for (auto &player: oldPlayers)
{
spawnPlayer(player->getInput(), player->getSkinName());
}
}
std::vector<std::shared_ptr<Player>> PlayerCollection::getRemainingPlayers() const
{
std::vector<std::shared_ptr<Player>> remainingPlayers = {};
for (auto &player: getPlayers())
{
if (player->getAlive())
{
remainingPlayers.push_back(player);
}
}
return remainingPlayers;
}

View file

@ -8,7 +8,7 @@
class PlayerCollection : public GameObject
{
public:
explicit PlayerCollection(int maxPlayerCount = DEFAULT_MAX_PLAYER_NUMBER);
PlayerCollection();
static std::shared_ptr<PlayerCollection> getInstance();
@ -18,45 +18,27 @@ 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;
[[nodiscard]] std::vector<std::shared_ptr<Player>> getNewPlayers() const;
[[nodiscard]] std::vector<std::shared_ptr<Player>> getRemovedPlayers() const;
[[nodiscard]] std::vector<std::shared_ptr<Player>> getRemainingPlayers() const;
[[nodiscard]] int getMaxPlayerCount() const;
void lateUpdate() override;
void clear();
void resetPlayers();
void activatePlayers();
void deactivatePlayers();
private:
static inline std::shared_ptr<PlayerCollection> singletonInstance = nullptr;
std::vector<std::shared_ptr<Player>> players = {};
std::vector<std::shared_ptr<Player>> newPlayerBuffer = {};
std::vector<std::shared_ptr<Player>> removedPlayerBuffer = {};
std::vector<GridCoordinates> spawnPoints;
int nextSpawnPointIndex = 0;
void spawnPlayer(const std::shared_ptr<InputIdentity> &inputIdentity, std::string skin = "");
std::vector<int> takenSkinIndices = {};
std::string getNextSkin();
int maxPlayerCount;
void spawnPlayer(const std::shared_ptr<InputIdentity> &inputIdentity);
};

View file

@ -1,70 +0,0 @@
#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);
}

View file

@ -1,31 +0,0 @@
#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

View file

@ -3,7 +3,6 @@
#include <map>
#include <string>
#include "game/input/input_device_group.h"
#include "game/input/button_config.hpp"
#include "game/input/game_action_config.hpp"
@ -18,68 +17,36 @@ const std::map<sf::Keyboard::Key, ButtonConfig> KEY_CONFIGS = {
{sf::Keyboard::Down, ButtonConfig(InputDeviceGroup::KEYBOARD_ARROWS, HardDirection::DOWN)},
{sf::Keyboard::Left, ButtonConfig(InputDeviceGroup::KEYBOARD_ARROWS, HardDirection::LEFT)},
{sf::Keyboard::Right, ButtonConfig(InputDeviceGroup::KEYBOARD_ARROWS, HardDirection::RIGHT)},
{sf::Keyboard::RShift, ButtonConfig(InputDeviceGroup::KEYBOARD_ARROWS, {GameAction::RUN})},
{sf::Keyboard::RControl, ButtonConfig(InputDeviceGroup::KEYBOARD_ARROWS, {GameAction::RUN})},
{sf::Keyboard::Numpad0, ButtonConfig(InputDeviceGroup::KEYBOARD_ARROWS, {GameAction::RUN})},
{sf::Keyboard::Space, ButtonConfig(InputDeviceGroup::KEYBOARD_ARROWS, {GameAction::CONFIRM})},
{sf::Keyboard::RControl, ButtonConfig(InputDeviceGroup::KEYBOARD_ARROWS, {GameAction::GROW})},
{sf::Keyboard::Numpad0, ButtonConfig(InputDeviceGroup::KEYBOARD_ARROWS, {GameAction::SHRINK})},
{sf::Keyboard::W, ButtonConfig(InputDeviceGroup::KEYBOARD_WASD, HardDirection::UP)},
{sf::Keyboard::S, ButtonConfig(InputDeviceGroup::KEYBOARD_WASD, HardDirection::DOWN)},
{sf::Keyboard::A, ButtonConfig(InputDeviceGroup::KEYBOARD_WASD, HardDirection::LEFT)},
{sf::Keyboard::D, ButtonConfig(InputDeviceGroup::KEYBOARD_WASD, HardDirection::RIGHT)},
{sf::Keyboard::Q, ButtonConfig(InputDeviceGroup::KEYBOARD_WASD, {GameAction::CONFIRM})},
{sf::Keyboard::E, ButtonConfig(InputDeviceGroup::KEYBOARD_WASD, {GameAction::CONFIRM})},
{sf::Keyboard::LShift, ButtonConfig(InputDeviceGroup::KEYBOARD_WASD, {GameAction::RUN})},
{sf::Keyboard::LControl, ButtonConfig(InputDeviceGroup::KEYBOARD_WASD, {GameAction::RUN})},
{sf::Keyboard::Space, ButtonConfig(InputDeviceGroup::KEYBOARD_WASD, {GameAction::GROW})},
{sf::Keyboard::Q, ButtonConfig(InputDeviceGroup::KEYBOARD_WASD, {GameAction::SHRINK})},
{sf::Keyboard::I, ButtonConfig(InputDeviceGroup::KEYBOARD_IJKL, HardDirection::UP)},
{sf::Keyboard::K, ButtonConfig(InputDeviceGroup::KEYBOARD_IJKL, HardDirection::DOWN)},
{sf::Keyboard::J, ButtonConfig(InputDeviceGroup::KEYBOARD_IJKL, HardDirection::LEFT)},
{sf::Keyboard::L, ButtonConfig(InputDeviceGroup::KEYBOARD_IJKL, HardDirection::RIGHT)},
{sf::Keyboard::U, ButtonConfig(InputDeviceGroup::KEYBOARD_IJKL, {GameAction::CONFIRM})},
{sf::Keyboard::O, ButtonConfig(InputDeviceGroup::KEYBOARD_IJKL, {GameAction::CONFIRM})},
{sf::Keyboard::RAlt, ButtonConfig(InputDeviceGroup::KEYBOARD_IJKL, {GameAction::RUN})},
{sf::Keyboard::B, ButtonConfig(InputDeviceGroup::KEYBOARD_IJKL, {GameAction::RUN})}
{sf::Keyboard::U, ButtonConfig(InputDeviceGroup::KEYBOARD_IJKL, {GameAction::GROW})},
{sf::Keyboard::O, ButtonConfig(InputDeviceGroup::KEYBOARD_IJKL, {GameAction::SHRINK})}
};
// Gamepad buttons
const std::map<int, ButtonConfig> GAMEPAD_BUTTON_CONFIGS = {
{GamepadButton::EAST, ButtonConfig(InputDeviceGroup::GAMEPAD, {GameAction::CONFIRM})},
{GamepadButton::SOUTH, ButtonConfig(InputDeviceGroup::GAMEPAD, {GameAction::CONFIRM})},
{GamepadButton::RIGHT_SHOULDER, ButtonConfig(InputDeviceGroup::GAMEPAD, {GameAction::RUN})},
{GamepadButton::LEFT_SHOULDER, ButtonConfig(InputDeviceGroup::GAMEPAD, {GameAction::RUN})},
{GamepadButton::RIGHT_TRIGGER, ButtonConfig(InputDeviceGroup::GAMEPAD, {GameAction::RUN})},
{GamepadButton::LEFT_TRIGGER, ButtonConfig(InputDeviceGroup::GAMEPAD, {GameAction::RUN})},
{GamepadButton::NORTH, ButtonConfig(InputDeviceGroup::GAMEPAD, {GameAction::RUN})},
{GamepadButton::WEST, ButtonConfig(InputDeviceGroup::GAMEPAD, {GameAction::RUN})}
{GamepadButton::EAST, ButtonConfig(InputDeviceGroup::GAMEPAD, {GameAction::GROW})},
{GamepadButton::SOUTH, ButtonConfig(InputDeviceGroup::GAMEPAD, {GameAction::SHRINK})}
};
// Actions
const std::map<GameAction, GameActionConfig> GAME_ACTION_CONFIGS = {
{GameAction::CONFIRM, GameActionConfig(InteractionMode::PRESS)},
{GameAction::RUN, GameActionConfig(InteractionMode::HOLD)}
};
const std::map<InputDeviceGroup, std::string> DEVICE_GROUP_NAMES = {
{InputDeviceGroup::KEYBOARD_WASD, "WASD"},
{InputDeviceGroup::KEYBOARD_IJKL, "IJKL"},
{InputDeviceGroup::KEYBOARD_ARROWS, "Arrow keys"},
{InputDeviceGroup::GAMEPAD, "Gamepad"}
};
const std::map<InputDeviceGroup, std::string> DEVICE_GROUP_CONFIRM = {
{InputDeviceGroup::KEYBOARD_WASD, "Q or E"},
{InputDeviceGroup::KEYBOARD_IJKL, "U or O"},
{InputDeviceGroup::KEYBOARD_ARROWS, "Space"},
{InputDeviceGroup::GAMEPAD, "A"}
};
const std::map<InputDeviceGroup, std::string> DEVICE_GROUP_RUN = {
{InputDeviceGroup::KEYBOARD_WASD, "L-Shift or -Ctrl"},
{InputDeviceGroup::KEYBOARD_IJKL, "B or R-Alt"},
{InputDeviceGroup::KEYBOARD_ARROWS, "R-Shift, -Ctrl or Pad-0"},
{InputDeviceGroup::GAMEPAD, "Trigger or Shoulder"}
{GameAction::GROW, GameActionConfig(InteractionMode::HOLD)},
{GameAction::SHRINK, GameActionConfig(InteractionMode::HOLD)}
};
#endif //HOLESOME_INPUT_CONFIG_H

View file

@ -8,64 +8,9 @@
#define INITIAL_LEVEL "default"
std::map<std::string, LevelConfig> const all_levels = {
{"default", LevelConfig("Default",
120,
{
{0, 0},
{18, 18},
{0, 18},
{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("lantern", {6, 7}),
CollectableInLevel("rose", {5, 5}),
CollectableInLevel("tram", {9, 5}),
CollectableInLevel("rose", {0, 1})
},
{
// Blues
sf::Color(2, 100, 234),
sf::Color(2, 100, 234),
sf::Color(2, 100, 234),
sf::Color(2, 195, 234),
// Neutral
sf::Color::White,
// Browns
sf::Color(163, 128, 68),
sf::Color(100, 80, 40),
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},
{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},
{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},
{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},
{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, 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}
})
)}
{"default", LevelConfig("Default", {25, 25}, {{0, 0}}, {
CollectableInLevel("box", {5, 5})
})}
};
#endif //HOLESOME_LEVELS_HPP

View file

@ -694,7 +694,7 @@ class ConfigurationTypeHelper : base::StaticClass {
/// This bool represent whether or not to exit iterating through configurations.
static inline void forEachConfigType(base::type::EnumType* startIndex, const std::function<bool(void)>& fn);
};
/// @brief Flags used while writing logs. This flags are setWorld by user
/// @brief Flags used while writing logs. This flags are set by user
enum class LoggingFlag : base::type::EnumType {
/// @brief Makes sure we have new line for each container log entry
NewLineForContainer = 1,
@ -1701,7 +1701,7 @@ class Configuration : public Loggable {
}
/// @brief Set string based configuration value
/// @param value Value to setWorld. Values have to be std::string; For boolean values use "true", "false", for any integral values
/// @param value Value to set. Values have to be std::string; For boolean values use "true", "false", for any integral values
/// use them in quotes. They will be parsed when configuring
inline void setValue(const std::string& value) {
m_value = value;
@ -1735,9 +1735,9 @@ class Configurations : public base::utils::RegistryWithPred<Configuration, Confi
/// @brief Default constructor with empty repository
Configurations(void);
/// @brief Constructor used to setWorld configurations using configuration file.
/// @brief Constructor used to set configurations using configuration file.
/// @param configurationFile Full path to configuration file
/// @param useDefaultsForRemaining Lets you setWorld the remaining configurations to default.
/// @param useDefaultsForRemaining Lets you set the remaining configurations to default.
/// @param base If provided, this configuration will be based off existing repository that this argument is pointing to.
/// @see parseFromFile(const std::string&, Configurations* base)
/// @see setRemainingToDefault()
@ -1750,7 +1750,7 @@ class Configurations : public base::utils::RegistryWithPred<Configuration, Confi
/// @brief Parses configuration from file.
/// @param configurationFile Full path to configuration file
/// @param base Configurations to base new configuration repository off. This value is used when you want to use
/// existing Configurations to base all the values and then setWorld rest of configuration via configuration file.
/// existing Configurations to base all the values and then set rest of configuration via configuration file.
/// @return True if successfully parsed, false otherwise. You may define 'ELPP_DEBUG_ASSERT_FAILURE' to make sure you
/// do not proceed without successful parse.
bool parseFromFile(const std::string& configurationFile, Configurations* base = nullptr);
@ -1760,7 +1760,7 @@ class Configurations : public base::utils::RegistryWithPred<Configuration, Confi
/// @detail This configuration string has same syntax as configuration file contents. Make sure all the necessary
/// new line characters are provided.
/// @param base Configurations to base new configuration repository off. This value is used when you want to use
/// existing Configurations to base all the values and then setWorld rest of configuration via configuration text.
/// existing Configurations to base all the values and then set rest of configuration via configuration text.
/// @return True if successfully parsed, false otherwise. You may define 'ELPP_DEBUG_ASSERT_FAILURE' to make sure you
/// do not proceed without successful parse.
bool parseFromText(const std::string& configurationsString, Configurations* base = nullptr);
@ -1783,9 +1783,9 @@ class Configurations : public base::utils::RegistryWithPred<Configuration, Confi
/// @brief Sets value of configuration for specified level.
///
/// @detail Any existing configuration for specified level will be replaced. Also note that configuration types
/// ConfigurationType::SubsecondPrecision and ConfigurationType::PerformanceTracking will be ignored if not setWorld for
/// ConfigurationType::SubsecondPrecision and ConfigurationType::PerformanceTracking will be ignored if not set for
/// Level::Global because these configurations are not dependant on level.
/// @param level Level to setWorld configuration for (el::Level).
/// @param level Level to set configuration for (el::Level).
/// @param configurationType Type of configuration (el::ConfigurationType)
/// @param value A string based value. Regardless of what the data type of configuration is, it will always be string
/// from users' point of view. This is then parsed later to be used internally.
@ -1795,7 +1795,7 @@ class Configurations : public base::utils::RegistryWithPred<Configuration, Confi
void set(Level level, ConfigurationType configurationType, const std::string& value);
/// @brief Sets single configuration based on other single configuration.
/// @see setWorld(Level level, ConfigurationType configurationType, const std::string& value)
/// @see set(Level level, ConfigurationType configurationType, const std::string& value)
void set(Configuration* conf);
inline Configuration* get(Level level, ConfigurationType configurationType) {
@ -1806,7 +1806,7 @@ class Configurations : public base::utils::RegistryWithPred<Configuration, Confi
/// @brief Sets configuration for all levels.
/// @param configurationType Type of configuration
/// @param value String based value
/// @see Configurations::setWorld(Level level, ConfigurationType configurationType, const std::string& value)
/// @see Configurations::set(Level level, ConfigurationType configurationType, const std::string& value)
inline void setGlobally(ConfigurationType configurationType, const std::string& value) {
setGlobally(configurationType, value, false);
}
@ -1819,7 +1819,7 @@ class Configurations : public base::utils::RegistryWithPred<Configuration, Confi
/// @brief Gets configuration file used in parsing this configurations.
///
/// @detail If this repository was setWorld manually or by text this returns empty string.
/// @detail If this repository was set manually or by text this returns empty string.
inline const std::string& configurationFile(void) const {
return m_configurationFile;
}
@ -1827,11 +1827,11 @@ class Configurations : public base::utils::RegistryWithPred<Configuration, Confi
/// @brief Sets configurations to "factory based" configurations.
void setToDefault(void);
/// @brief Lets you setWorld the remaining configurations to default.
/// @brief Lets you set the remaining configurations to default.
///
/// @detail By remaining, it means that the level/type a configuration does not exist for.
/// This function is useful when you want to minimize chances of failures, e.g, if you have a configuration file that sets
/// configuration for all the configurations except for Enabled or not, we use this so that ENABLED is setWorld to default i.e,
/// configuration for all the configurations except for Enabled or not, we use this so that ENABLED is set to default i.e,
/// true. If you dont do this explicitly (either by calling this function or by using second param in Constructor
/// and try to access a value, an error is thrown
void setRemainingToDefault(void);
@ -1846,7 +1846,7 @@ class Configurations : public base::utils::RegistryWithPred<Configuration, Confi
/// @param configurationFile Full path to configuration file
/// @param sender Sender configurations pointer. Usually 'this' is used from calling class
/// @param base Configurations to base new configuration repository off. This value is used when you want to use
/// existing Configurations to base all the values and then setWorld rest of configuration via configuration file.
/// existing Configurations to base all the values and then set rest of configuration via configuration file.
/// @return True if successfully parsed, false otherwise. You may define '_STOP_ON_FIRSTELPP_ASSERTION' to make sure you
/// do not proceed without successful parse.
static bool parseFromFile(const std::string& configurationFile, Configurations* sender,
@ -1860,7 +1860,7 @@ class Configurations : public base::utils::RegistryWithPred<Configuration, Confi
/// @param configurationsString the configuration in plain text format
/// @param sender Sender configurations pointer. Usually 'this' is used from calling class
/// @param base Configurations to base new configuration repository off. This value is used when you want to use
/// existing Configurations to base all the values and then setWorld rest of configuration via configuration text.
/// existing Configurations to base all the values and then set rest of configuration via configuration text.
/// @return True if successfully parsed, false otherwise.
static bool parseFromText(const std::string& configurationsString, Configurations* sender,
Configurations* base = nullptr);
@ -1883,7 +1883,7 @@ class Configurations : public base::utils::RegistryWithPred<Configuration, Confi
/// @brief Unsafely sets configuration if does not already exist
void unsafeSetIfNotExist(Level level, ConfigurationType configurationType, const std::string& value);
/// @brief Thread unsafe setWorld
/// @brief Thread unsafe set
void unsafeSet(Level level, ConfigurationType configurationType, const std::string& value);
/// @brief Sets configurations for all levels including Level::Global if includeGlobalLevel is true
@ -3839,7 +3839,7 @@ class Loggers : base::StaticClass {
static std::vector<std::string>* populateAllLoggerIds(std::vector<std::string>* targetList);
/// @brief Sets configurations from global configuration file.
static void configureFromGlobal(const char* globalConfigurationFilePath);
/// @brief Configures loggers using command line arg. Ensure you have already setWorld command line args,
/// @brief Configures loggers using command line arg. Ensure you have already set command line args,
/// @return False if invalid argument or argument with no value provided, true if attempted to configure logger.
/// If true is returned that does not mean it has been configured successfully, it only means that it
/// has attempted to configure logger using configuration file provided in argument

View file

@ -4,14 +4,11 @@
#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[])
@ -19,26 +16,17 @@ 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 ...";
auto game = GameFactory::createFullscreen(GAME_NAME);
auto game = GameFactory::createWindowed("Holesome");
// Load initial level
LevelLoader::loadLevel(INITIAL_LEVEL);
game->showJoinScreen();
game->run();
InputMapper::getInstance().reset();

View file

@ -1,121 +0,0 @@
#include <iostream>
#include <Eigen/Dense>
#include "../coordinates/translated_coordinates.h"
enum CoordinateSystem
{
NONE,
WORLD,
DIAGONAL,
ISOMETRIC
};
CoordinateSystem inputCoordinateSystem()
{
while (true)
{
try
{
std::cout << "Enter coordinate system (w/d/i) or x to close: ";
char coordinateSystemIndicator;
std::cin >> coordinateSystemIndicator;
switch (coordinateSystemIndicator)
{
case 'x':
std::cout << "Closing ..." << std::endl;
return NONE;
case 'w':
std::cout << "World coordinates" << std::endl;
return WORLD;
case 'd':
std::cout << "Diagonal world coordinates" << std::endl;
return DIAGONAL;
case 'i':
std::cout << "Isometric coordinates" << std::endl;
return ISOMETRIC;
default:
std::cout << "Invalid coordinate system. Try again ..." << std::endl;
continue;
}
} catch (...)
{
}
}
}
std::vector<float> input3values(std::string prompt = "Enter 3 values: ")
{
while (true)
{
try
{
std::vector<float> values(3);
std::cout << prompt;
std::cin >> values[0] >> values[1] >> values[2];
return values;
} catch (...)
{
}
}
}
TranslatedCoordinates worldToTranslated(std::vector<float> values)
{
return TranslatedCoordinates(WorldCoordinates{values[0], values[1], values[2]});
}
TranslatedCoordinates diagonalToTranslated(std::vector<float> values)
{
return TranslatedCoordinates(DiagonalWorldCoordinates{values[0], values[1], values[2]});
}
TranslatedCoordinates isometricToTranslated(std::vector<float> values)
{
auto world = CoordinateTransformer::isometricToWorld(IsometricCoordinates{values[0], values[1], values[2]});
return TranslatedCoordinates(world);
}
void printCoordinates(TranslatedCoordinates coordinates)
{
std::cout << "World coordinates (x, y, z): " << coordinates.world().x << ", " << coordinates.world().y << ", "
<< coordinates.world().z << std::endl;
std::cout << "Diagonal world coordinates (horizontal, vertical, depth): " << coordinates.diagonalWorld().horizontal << ", "
<< coordinates.diagonalWorld().vertical << ", " << coordinates.diagonalWorld().depth
<< std::endl;
std::cout << "Isometric coordinates (x, y, depth): " << coordinates.isometric().x << ", "
<< coordinates.isometric().y << ", " << coordinates.isometric().depth << std::endl;
}
int main()
{
while (true)
{
CoordinateSystem coordinateSystem = inputCoordinateSystem();
TranslatedCoordinates translatedCoordinates = TranslatedCoordinates(WorldCoordinates{0, 0, 0});
switch (coordinateSystem)
{
case NONE:
return 0;
case WORLD:
translatedCoordinates = worldToTranslated(input3values("Enter world coordinates (x, y, z): "));
break;
case DIAGONAL:
translatedCoordinates = diagonalToTranslated(
input3values("Enter diagonal world coordinates (horizontal, vertical, depth): "));
break;
case ISOMETRIC:
translatedCoordinates = isometricToTranslated(
input3values("Enter isometric coordinates (x, y, depth): "));
break;
}
printCoordinates(translatedCoordinates);
// Wait for enter, then clear screen
std::cin.ignore();
std::cin.get();
std::cout << "\033[2J\033[1;1H";
}
}

View file

@ -1,72 +0,0 @@
#include <SFML/Graphics/RenderWindow.hpp>
#include <SFML/Window/Event.hpp>
#include <SFML/Graphics/CircleShape.hpp>
#include <SFML/Graphics/RectangleShape.hpp>
void drawMainContent(sf::RenderWindow *window)
{
sf::RectangleShape rectangle(sf::Vector2f(800, 600));
rectangle.setFillColor(sf::Color::Green);
rectangle.setPosition(0, 0);
window->draw(rectangle);
sf::CircleShape circle(50);
circle.setFillColor(sf::Color::Red);
circle.setPosition(150, 150);
window->draw(circle);
}
void drawMiniMapContent(sf::RenderWindow *window)
{
sf::RectangleShape rectangle(sf::Vector2f(196, 196));
rectangle.setFillColor(sf::Color::White);
rectangle.setPosition(2, 2);
rectangle.setOutlineColor(sf::Color::Black);
rectangle.setOutlineThickness(2);
window->draw(rectangle);
sf::CircleShape circle(20);
circle.setFillColor(sf::Color::Blue);
circle.setPosition(80, 80);
window->draw(circle);
}
int main(int argc, char *argv[])
{
// Create simple window
sf::RenderWindow window(sf::VideoMode(800, 600), "Minimap example");
// Create minimap view
sf::View minimapView(sf::FloatRect(0, 0, 200, 200));
// Set viewport to the top right corner
minimapView.setViewport(sf::FloatRect(0.75f, 0, 0.25f, 0.25f));
while (window.isOpen()) {
window.clear(sf::Color::Black);
// Draw main content
auto fullView = window.getDefaultView();
window.setView(fullView);
drawMainContent(&window);
// Draw minimap
window.setView(minimapView);
drawMiniMapContent(&window);
// Finished
window.display();
// Handle close event
sf::Event event{};
while (window.pollEvent(event)) {
if (event.type == sf::Event::Closed) {
window.close();
break;
}
}
}
}

View file

@ -1,179 +0,0 @@
#include "join_screen.hpp"
#include "../config.h"
#include "../typography/font_manager.hpp"
#include "../game/player/player_collection.hpp"
#include "../levels.hpp"
#include "../game/level/level_loader.hpp"
#include "../game/game.h"
JoinScreen::JoinScreen()
{
// Title text
auto titleText = std::make_shared<sf::Text>();
titleText->setString(GAME_NAME);
titleText->setFont(*FontManager::getInstance()->getDefaultFont());
titleText->setCharacterSize(150);
titleText->setStyle(sf::Text::Bold);
titleText->setFillColor(sf::Color::White);
titleText->setPosition(REFERENCE_SIZE.x / 2.f - titleText->getGlobalBounds().width / 2.f, 20);
add(titleText);
// Create join-player text
auto joinText = std::make_shared<sf::Text>();
joinText->setString("Press button to join");
joinText->setFont(*FontManager::getInstance()->getDefaultFont());
joinText->setCharacterSize(24);
joinText->setFillColor(sf::Color::White);
joinText->setPosition(REFERENCE_SIZE.x / 2.f - joinText->getGlobalBounds().width / 2.f, 200);
add(joinText);
}
void JoinScreen::update()
{
Screen::update();
// Did any player start game?
auto players = PlayerCollection::getInstance()->getPlayers();
for (auto &player: players)
{
auto input = player->getInput();
if (input->isPerformingAction(GameAction::CONFIRM))
{
// Start game
Game::getInstance()->generateNewLevel();
return;
}
}
// Update player list items
if (players.size() == lastPlayerCount)
{
return;
}
lastPlayerCount = players.size();
playerList.clear();
playerListObjects.clear();
int index = 0;
for (auto &player: players)
{
addPlayerAtIndex(player, index);
index++;
}
}
void JoinScreen::draw(sf::RenderWindow *window)
{
Screen::draw(window);
// Draw player list items
for (auto &item: playerList)
{
window->draw(*item);
}
for (auto &item: playerListObjects)
{
item->draw(window);
}
}
void JoinScreen::addPlayerAtIndex(std::shared_ptr<Player> &player, int index)
{
int numberOfMaxPlayers = PlayerCollection::getInstance()->getMaxPlayerCount();
int paddingLeftRight = 100;
int paddingTop = 400;
int relativePaddingPerColumn = 0.1;
int columnWidth = (REFERENCE_SIZE.x - 2 * paddingLeftRight) / numberOfMaxPlayers;
int paddingPerColumn = columnWidth * relativePaddingPerColumn;
int contentWidth = columnWidth - paddingPerColumn;
auto topLeft = sf::Vector2f(paddingLeftRight + columnWidth * index,
paddingTop);
// Create player character
auto skinSprite = std::make_shared<VersatileSprite>(player->getSkinName(),
sf::Vector2f(contentWidth, contentWidth * ISOMETRIC_SKEW) *
0.9f);
skinSprite->coordinates->setIsometric(IsometricCoordinates(topLeft));
playerListObjects.push_back(skinSprite);
skinSprite->update();
skinSprite->lateUpdate();
skinSprite->preRenderUpdate();
// Show device group
auto deviceGroupText = std::make_shared<sf::Text>();
InputDeviceGroup deviceGroup = player->getInput()->deviceGroup;
deviceGroupText->setString(DEVICE_GROUP_NAMES.at(deviceGroup));
deviceGroupText->setFont(*FontManager::getInstance()->getDefaultFont());
deviceGroupText->setCharacterSize(24);
deviceGroupText->setFillColor(sf::Color::White);
deviceGroupText->setPosition(topLeft.x + contentWidth / 2.f * 0.9f - deviceGroupText->getGlobalBounds().width / 2.f,
topLeft.y + skinSprite->getSize().y + 20);
playerList.push_back(deviceGroupText);
// Show button to run
auto runText = std::make_shared<sf::Text>();
runText->setString("Press");
runText->setFont(*FontManager::getInstance()->getDefaultFont());
runText->setCharacterSize(15);
runText->setFillColor(sf::Color::White);
runText->setPosition(topLeft.x + contentWidth / 2.f * 0.9f - runText->getGlobalBounds().width / 2.f,
topLeft.y + skinSprite->getSize().y + 60);
playerList.push_back(runText);
std::string buttonToRun = DEVICE_GROUP_RUN.at(deviceGroup);
auto runButton = std::make_shared<sf::Text>();
runButton->setString(buttonToRun);
runButton->setFont(*FontManager::getInstance()->getDefaultFont());
runButton->setCharacterSize(15);
runButton->setFillColor(sf::Color::White);
runButton->setPosition(topLeft.x + contentWidth / 2.f * 0.9f - runButton->getGlobalBounds().width / 2.f,
topLeft.y + skinSprite->getSize().y + 80);
playerList.push_back(runButton);
auto confirmText = std::make_shared<sf::Text>();
confirmText->setString("to run");
confirmText->setFont(*FontManager::getInstance()->getDefaultFont());
confirmText->setCharacterSize(15);
confirmText->setFillColor(sf::Color::White);
confirmText->setPosition(topLeft.x + contentWidth / 2.f * 0.9f - confirmText->getGlobalBounds().width / 2.f,
topLeft.y + skinSprite->getSize().y + 100);
playerList.push_back(confirmText);
if (lastPlayerCount < MIN_PLAYER_COUNT)
{
return;
}
// Show button to press to start
auto pressText = std::make_shared<sf::Text>();
pressText->setString("Press");
pressText->setFont(*FontManager::getInstance()->getDefaultFont());
pressText->setCharacterSize(24);
pressText->setFillColor(sf::Color::Yellow);
pressText->setPosition(topLeft.x + contentWidth / 2.f * 0.9f - pressText->getGlobalBounds().width / 2.f,
topLeft.y + skinSprite->getSize().y + 170);
playerList.push_back(pressText);
std::string buttonToStart = DEVICE_GROUP_CONFIRM.at(deviceGroup);
auto buttonText = std::make_shared<sf::Text>();
buttonText->setString(buttonToStart);
buttonText->setFont(*FontManager::getInstance()->getDefaultFont());
buttonText->setCharacterSize(24);
buttonText->setFillColor(sf::Color::Yellow);
buttonText->setPosition(topLeft.x + contentWidth / 2.f * 0.9f - buttonText->getGlobalBounds().width / 2.f,
topLeft.y + skinSprite->getSize().y + 200);
playerList.push_back(buttonText);
auto toStartText = std::make_shared<sf::Text>();
toStartText->setString("to start");
toStartText->setFont(*FontManager::getInstance()->getDefaultFont());
toStartText->setCharacterSize(24);
toStartText->setFillColor(sf::Color::Yellow);
toStartText->setPosition(topLeft.x + contentWidth / 2.f * 0.9f - toStartText->getGlobalBounds().width / 2.f,
topLeft.y + skinSprite->getSize().y + 230);
playerList.push_back(toStartText);
}

View file

@ -1,27 +0,0 @@
#ifndef HOLESOME_JOIN_SCREEN_HPP
#define HOLESOME_JOIN_SCREEN_HPP
#include "screen.hpp"
#include "../game/player/player.hpp"
#include <SFML/Graphics/Text.hpp>
class JoinScreen : public Screen
{
public:
JoinScreen();
void update() override;
void draw(sf::RenderWindow *window) override;
private:
int lastPlayerCount = -1;
std::vector<std::shared_ptr<sf::Drawable>> playerList = {};
std::vector<std::shared_ptr<GameObject>> playerListObjects = {};
void addPlayerAtIndex(std::shared_ptr<Player> &player, int index);
};
#endif //HOLESOME_JOIN_SCREEN_HPP

Some files were not shown because too many files have changed in this diff Show more