Compare commits
1 commit
master
...
feature-de
Author | SHA1 | Date | |
---|---|---|---|
075cb5977e |
1
.gitignore
vendored
|
@ -1,3 +1,2 @@
|
|||
/build/
|
||||
/build-release/
|
||||
/.idea/
|
129
CMakeLists.txt
|
@ -11,9 +11,9 @@ include_directories(${SFML_INCLUDE_DIR})
|
|||
find_package(Eigen3 3.3 REQUIRED NO_MODULE)
|
||||
include_directories(${EIGEN3_INCLUDE_DIR})
|
||||
|
||||
# Include Box2D
|
||||
find_package(box2d REQUIRED)
|
||||
include_directories(${BOX2D_INCLUDE_DIR})
|
||||
# Find and include Box2D
|
||||
#find_package(Box2D REQUIRED)
|
||||
|
||||
|
||||
# Set up your project's source files
|
||||
set(SOURCES
|
||||
|
@ -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
|
||||
|
@ -42,6 +44,7 @@ set(SOURCES
|
|||
src/game/input/direction.cpp
|
||||
src/game/player/player.cpp
|
||||
src/game/player/player.hpp
|
||||
src/game/input/key_features.hpp
|
||||
src/game/camera/tracking_view.cpp
|
||||
src/game/camera/tracking_view.h
|
||||
src/utilities/smart_list.cpp
|
||||
|
@ -50,95 +53,12 @@ set(SOURCES
|
|||
src/game/camera/ITrackable.h
|
||||
src/game/input/input_identity.h
|
||||
src/utilities/magic_enum.hpp
|
||||
src/game/player/player_spawner.cpp
|
||||
src/game/player/player_spawner.hpp
|
||||
src/game/camera/tracking_area.h
|
||||
src/game/camera/tracking_view_options.hpp
|
||||
src/game/collectables/collectable.cpp
|
||||
src/game/collectables/collectable.hpp
|
||||
src/sprites/texture_manager.cpp
|
||||
src/sprites/texture_manager.hpp
|
||||
src/sprites/sprite_sheet.cpp
|
||||
src/sprites/sprite_sheet.hpp
|
||||
src/sprites/animated_sprite.cpp
|
||||
src/sprites/animated_sprite.hpp
|
||||
src/sprites/single_sprite.cpp
|
||||
src/sprites/single_sprite.hpp
|
||||
src/texture_config.h
|
||||
src/sprites/configs/sprite_config.hpp
|
||||
src/sprites/configs/sheet_config.hpp
|
||||
src/sprites/configs/animation_config.hpp
|
||||
src/sprites/versatile_sprite.cpp
|
||||
src/sprites/versatile_sprite.hpp
|
||||
src/sprites/sprite.hpp
|
||||
src/sprites/sprite_factory.cpp
|
||||
src/sprites/sprite_factory.hpp
|
||||
src/input_config.h
|
||||
src/game/input/button_config.hpp
|
||||
src/game/input/game_action.hpp
|
||||
src/game/input/button_config_factory.cpp
|
||||
src/game/input/button_config_factory.hpp
|
||||
src/game/input/game_action_config.hpp
|
||||
src/game/input/gamepad_buttons.hpp
|
||||
src/game/physics/map/map_simulation.cpp
|
||||
src/game/physics/map/map_simulation.hpp
|
||||
src/game/physics/map/map_player.hpp
|
||||
src/game/physics/map/map_player.cpp
|
||||
src/game/level/level_config.hpp
|
||||
src/game/level/level_loader.cpp
|
||||
src/game/level/level_loader.hpp
|
||||
src/levels.hpp src/collectables.hpp
|
||||
src/game/collectables/collectable_config.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/depth_renderer.cpp
|
||||
src/game/depth_renderer.hpp)
|
||||
|
||||
set(PHYSICS_00_SOURCES
|
||||
src/prototypes/physics_00.cpp)
|
||||
|
@ -146,42 +66,17 @@ set(PHYSICS_00_SOURCES
|
|||
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)
|
||||
target_link_libraries(Holesome box2d::box2d)
|
||||
#target_link_libraries(Holesome Box2D::Box2D)
|
||||
|
||||
target_link_libraries(Physics_00 box2d::box2d)
|
||||
#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
|
||||
)
|
||||
add_dependencies(Holesome copy_assets)
|
||||
|
|
13
README.md
|
@ -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
|
@ -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
|
||||
- [ ] Link box2d???
|
BIN
assets/64.png
Before Width: | Height: | Size: 2.1 KiB |
Before Width: | Height: | Size: 849 B |
Before Width: | Height: | Size: 630 B |
Before Width: | Height: | Size: 517 B |
Before Width: | Height: | Size: 578 B |
Before Width: | Height: | Size: 835 B |
Before Width: | Height: | Size: 617 B |
Before Width: | Height: | Size: 967 B |
Before Width: | Height: | Size: 3.6 KiB |
Before Width: | Height: | Size: 18 KiB |
BIN
assets/hole.png
Before Width: | Height: | Size: 313 KiB |
Before Width: | Height: | Size: 4 KiB |
Before Width: | Height: | Size: 6.6 KiB |
Before Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 2.7 KiB |
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 2.8 KiB |
Before Width: | Height: | Size: 1.4 KiB |
BIN
assets/ring.png
Before Width: | Height: | Size: 2.4 KiB |
|
@ -1,18 +0,0 @@
|
|||
#ifndef HOLESOME_COLLECTABLES_HPP
|
||||
#define HOLESOME_COLLECTABLES_HPP
|
||||
|
||||
#include <string>
|
||||
#include <map>
|
||||
#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)}
|
||||
};
|
||||
|
||||
#endif //HOLESOME_COLLECTABLES_HPP
|
104
src/config.h
|
@ -2,91 +2,75 @@
|
|||
#define HOLESOME_CONFIG_H
|
||||
|
||||
#include <SFML/Graphics.hpp>
|
||||
#include <box2d/box2d.h>
|
||||
#include <map>
|
||||
#include "game/input/input_device_group.h"
|
||||
|
||||
#define DEVELOPER_MODE false
|
||||
|
||||
#define GAME_NAME "Holesome"
|
||||
#define CLOSE_GAME_BTN sf::Keyboard::Escape
|
||||
|
||||
// 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
|
||||
|
||||
// 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
|
||||
|
||||
// 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
|
||||
#define WORLD_TO_ISO_SCALE 10.0f
|
||||
|
||||
// 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_MIN_VIEW_SIZE sf::Vector2f(300, 300)
|
||||
#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)
|
||||
|
||||
// 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
|
||||
// Inputs
|
||||
#define JOYSTICK_DEADZONE 0.1f
|
||||
// Key groups
|
||||
const std::map<InputDeviceGroup, std::set<sf::Keyboard::Key>> KEY_GROUPS = {
|
||||
{InputDeviceGroup::KEYBOARD_WASD,
|
||||
{
|
||||
sf::Keyboard::W,
|
||||
sf::Keyboard::A,
|
||||
sf::Keyboard::S,
|
||||
sf::Keyboard::D,
|
||||
sf::Keyboard::Q,
|
||||
sf::Keyboard::E
|
||||
}
|
||||
},
|
||||
{InputDeviceGroup::KEYBOARD_ARROWS,
|
||||
{
|
||||
sf::Keyboard::Up,
|
||||
sf::Keyboard::Left,
|
||||
sf::Keyboard::Down,
|
||||
sf::Keyboard::Right,
|
||||
sf::Keyboard::RShift,
|
||||
sf::Keyboard::PageDown,
|
||||
sf::Keyboard::PageUp,
|
||||
sf::Keyboard::RControl,
|
||||
}
|
||||
},
|
||||
{InputDeviceGroup::KEYBOARD_IJKL,
|
||||
{
|
||||
sf::Keyboard::I,
|
||||
sf::Keyboard::J,
|
||||
sf::Keyboard::K,
|
||||
sf::Keyboard::L,
|
||||
sf::Keyboard::U,
|
||||
sf::Keyboard::O
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// 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"}
|
||||
};
|
||||
#define DB_ISOPLANE_CORNER_RADIUS 2
|
||||
|
||||
#endif //HOLESOME_CONFIG_H
|
||||
|
|
|
@ -1,23 +1,11 @@
|
|||
#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();
|
||||
|
||||
|
@ -26,66 +14,21 @@ IsometricCoordinates CoordinateTransformer::worldToIsometric(WorldCoordinates wo
|
|||
Eigen::Vector3f worldCoordinatesVector;
|
||||
worldCoordinatesVector << worldCoordinates.x, worldCoordinates.y, worldCoordinates.z;
|
||||
|
||||
Eigen::Vector3f isoCoordinatesVector = worldToIsometricMatrix * worldCoordinatesVector;
|
||||
Eigen::Vector3f isoCoordinatesVector = worldToIsometricMatrix * worldCoordinatesVector * WORLD_TO_ISO_SCALE;
|
||||
|
||||
return {
|
||||
return IsometricCoordinates(
|
||||
isoCoordinatesVector.x(), // x
|
||||
isoCoordinatesVector.y(), // 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
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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)
|
||||
{}
|
||||
};
|
||||
|
||||
|
@ -101,14 +80,6 @@ struct GridCoordinates
|
|||
{
|
||||
float x;
|
||||
float y;
|
||||
|
||||
GridCoordinates() = default;
|
||||
|
||||
GridCoordinates(float x, float y) : x(x), y(y)
|
||||
{}
|
||||
|
||||
GridCoordinates(int x, int y) : x(static_cast<float>(x)), y(static_cast<float>(y))
|
||||
{}
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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
|
||||
{
|
||||
return CoordinateTransformer::worldToIsometric(world());
|
||||
IsometricCoordinates TranslatedCoordinates::isometric() const {
|
||||
return coordTransformer->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};
|
||||
return {referenceWordCoordinates.x * worldToGridFactor, referenceWordCoordinates.y * worldToGridFactor};
|
||||
}
|
||||
|
||||
void TranslatedCoordinates::setWorld(WorldCoordinates newWorldCoordinates)
|
||||
{
|
||||
void TranslatedCoordinates::set(WorldCoordinates newWorldCoordinates) {
|
||||
this->worldCoordinates = newWorldCoordinates;
|
||||
}
|
||||
|
||||
void TranslatedCoordinates::setIsometric(IsometricCoordinates newIsometricCoordinates)
|
||||
{
|
||||
this->worldCoordinates = CoordinateTransformer::isometricToWorld(newIsometricCoordinates);
|
||||
void TranslatedCoordinates::set(IsometricCoordinates newIsometricCoordinates) {
|
||||
this->worldCoordinates = coordTransformer->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)
|
||||
|
@ -66,51 +53,3 @@ void TranslatedCoordinates::setParent(std::shared_ptr<TranslatedCoordinates> par
|
|||
this->parent = std::move(parent);
|
||||
this->worldCoordinates = offset;
|
||||
}
|
||||
|
||||
void TranslatedCoordinates::setGrid(GridCoordinates newGridCoordinates)
|
||||
{
|
||||
this->worldCoordinates = {newGridCoordinates.x + 0.5f, newGridCoordinates.y + 0.5f, 0};
|
||||
}
|
||||
|
||||
void TranslatedCoordinates::setWorldOffset(WorldCoordinates offset)
|
||||
{
|
||||
this->worldCoordinates = offset;
|
||||
}
|
||||
|
||||
void TranslatedCoordinates::setScreenOffset(IsometricCoordinates offset)
|
||||
{
|
||||
setWorldOffset(CoordinateTransformer::isometricToWorld(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);
|
||||
}
|
||||
|
|
|
@ -1,3 +1,7 @@
|
|||
//
|
||||
// Created by max on 27.04.23.
|
||||
//
|
||||
|
||||
#ifndef HOLESOME_TRANSLATED_COORDINATES_H
|
||||
#define HOLESOME_TRANSLATED_COORDINATES_H
|
||||
|
||||
|
@ -5,31 +9,24 @@
|
|||
#include "coordinates.h"
|
||||
#include "coordinate_transformer.h"
|
||||
|
||||
#define INITIAL_WORLD_TO_GRID_FACTOR 0.25f
|
||||
|
||||
class TranslatedCoordinates
|
||||
{
|
||||
public:
|
||||
explicit TranslatedCoordinates(WorldCoordinates worldCoordinates);
|
||||
explicit TranslatedCoordinates(DiagonalWorldCoordinates diagonalWorldCoordinates);
|
||||
explicit TranslatedCoordinates(IsometricCoordinates isometricCoordinates);
|
||||
explicit TranslatedCoordinates(GridCoordinates gridCoordinates);
|
||||
|
||||
[[nodiscard]] WorldCoordinates world() const;
|
||||
WorldCoordinates world() const;
|
||||
|
||||
[[nodiscard]] IsometricCoordinates isometric() const;
|
||||
IsometricCoordinates isometric() const;
|
||||
|
||||
[[nodiscard]] GridCoordinates grid() const;
|
||||
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 setIsometric(IsometricCoordinates newIsometricCoordinates);
|
||||
|
||||
void setDiagonal(DiagonalWorldCoordinates newDiagonalWorldCoordinates);
|
||||
|
||||
void setGrid(GridCoordinates newGridCoordinates);
|
||||
void set(IsometricCoordinates newIsometricCoordinates);
|
||||
|
||||
void move(WorldCoordinates deltaWorldCoordinates);
|
||||
|
||||
|
@ -37,19 +34,12 @@ 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{};
|
||||
WorldCoordinates worldCoordinates;
|
||||
const float worldToGridFactor = INITIAL_WORLD_TO_GRID_FACTOR;
|
||||
const std::shared_ptr<CoordinateTransformer> coordTransformer = std::make_shared<CoordinateTransformer>();
|
||||
|
||||
std::shared_ptr<TranslatedCoordinates> parent = nullptr;
|
||||
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -22,13 +22,13 @@ GridDebugLayer::GridDebugLayer(int minX, int maxX, int minY, int maxY)
|
|||
color = sf::Color::Blue;
|
||||
}
|
||||
|
||||
auto gameObject = std::make_shared<CircleObject>(DB_CIRCLE_RADIUS, color);
|
||||
addChildWorldOffset(gameObject, WorldCoordinates(x, y));
|
||||
auto gameObject = std::make_shared<CircleObject>(DB_ISOPLANE_CORNER_RADIUS, color);
|
||||
addChild(gameObject, WorldCoordinates(x, y));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GridDebugLayer::draw(sf::RenderWindow *window)
|
||||
void GridDebugLayer::draw(sf::RenderWindow *window) const
|
||||
{
|
||||
for (auto &gameObject: getChildren())
|
||||
{
|
||||
|
|
|
@ -9,7 +9,7 @@ class GridDebugLayer : public GameObject
|
|||
public:
|
||||
GridDebugLayer(int minX, int maxX, int minY, int maxY);
|
||||
|
||||
void draw(sf::RenderWindow *window) override;
|
||||
void draw(sf::RenderWindow *window) const override;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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();
|
||||
}
|
|
@ -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
|
|
@ -1,13 +1,13 @@
|
|||
#include "tracking_view.h"
|
||||
#include "../../utilities/vector_utils.hpp"
|
||||
#include "../player/player_collection.hpp"
|
||||
|
||||
TrackingView::TrackingView(TrackingViewOptions options) : options(options),
|
||||
view(new sf::View(options.initialCenter,
|
||||
options.minViewSize)),
|
||||
trackables({})
|
||||
view(nullptr),
|
||||
hasViewChanged(false)
|
||||
{
|
||||
marker = new CircleObject(DB_CIRCLE_RADIUS, sf::Color::Yellow);
|
||||
trackables = std::vector<ITrackable *>();
|
||||
marker = new CircleObject(2, sf::Color::Yellow);
|
||||
Game::getInstance()->registerView(this);
|
||||
}
|
||||
|
||||
TrackingView::~TrackingView()
|
||||
|
@ -18,13 +18,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)
|
||||
|
@ -45,13 +65,13 @@ void TrackingView::setSize(sf::Vector2f newSize)
|
|||
didAspectRationChange = true;
|
||||
}
|
||||
|
||||
if (options.softResizeSpeed != 0 && !didAspectRationChange)
|
||||
{
|
||||
if (options.softResizeSpeed != 0 && !didAspectRationChange) {
|
||||
// Smooth out transition to new size
|
||||
newSize = size + (newSize - size) * options.softResizeSpeed * FRAME_TIME.asSeconds();
|
||||
}
|
||||
|
||||
view->setSize(newSize);
|
||||
hasViewChanged = true;
|
||||
}
|
||||
|
||||
sf::Vector2f TrackingView::getSize() const
|
||||
|
@ -59,11 +79,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 +93,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 +103,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 +115,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 +129,12 @@ void TrackingView::moveCenter(sf::Vector2<float> delta)
|
|||
}
|
||||
|
||||
view->move(delta);
|
||||
hasViewChanged = true;
|
||||
}
|
||||
|
||||
void TrackingView::draw(sf::RenderWindow *window)
|
||||
void TrackingView::draw(sf::RenderWindow *window) const
|
||||
{
|
||||
if (!DB_TRACKING_VIEW_CENTER)
|
||||
if (!DEVELOPER_MODE)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
@ -122,21 +142,15 @@ void TrackingView::draw(sf::RenderWindow *window)
|
|||
marker->draw(window);
|
||||
}
|
||||
|
||||
void TrackingView::addTrackable(const std::shared_ptr<ITrackable> &trackable)
|
||||
void TrackingView::addTrackable(ITrackable *trackable)
|
||||
{
|
||||
trackables.push_back(trackable);
|
||||
|
||||
// Set initial values if first trackable
|
||||
if (trackables.size() == 1)
|
||||
{
|
||||
view->setCenter(trackable->getTrackablePosition());
|
||||
}
|
||||
}
|
||||
|
||||
void TrackingView::processTrackableStates()
|
||||
{
|
||||
// Remove trackables that have ended tracking
|
||||
std::remove_if(trackables.begin(), trackables.end(), [](std::shared_ptr<ITrackable> trackable)
|
||||
std::remove_if(trackables.begin(), trackables.end(), [](ITrackable *trackable)
|
||||
{
|
||||
return trackable->getTrackableState() == TrackableState::END_TRACKING;
|
||||
});
|
||||
|
@ -144,11 +158,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 +203,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,49 +242,32 @@ 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;
|
||||
}
|
||||
|
||||
void TrackingView::removeTrackable(const std::shared_ptr<ITrackable> &trackable)
|
||||
{
|
||||
trackables.erase(
|
||||
std::remove_if(trackables.begin(), trackables.end(), [&trackable](const std::shared_ptr<ITrackable> &t)
|
||||
{
|
||||
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);
|
||||
return smallestSide / 2.f * threshold;
|
||||
}
|
||||
|
|
|
@ -17,17 +17,12 @@ public:
|
|||
|
||||
~TrackingView();
|
||||
|
||||
void draw(sf::RenderWindow *window) override;
|
||||
|
||||
void draw(sf::RenderWindow *window) const override;
|
||||
|
||||
void lateUpdate() override;
|
||||
|
||||
void addTrackable(const std::shared_ptr<ITrackable>& trackable);
|
||||
|
||||
void removeTrackable(const std::shared_ptr<ITrackable> &trackable);
|
||||
|
||||
void setViewport(sf::Vector2f center, sf::Vector2f size);
|
||||
|
||||
void setViewForWindow();
|
||||
void addTrackable(ITrackable *trackable);
|
||||
|
||||
sf::Vector2f getSize() const;
|
||||
|
||||
|
@ -35,11 +30,14 @@ public:
|
|||
|
||||
private:
|
||||
sf::View *view{};
|
||||
bool hasViewChanged{};
|
||||
TrackingViewOptions options;
|
||||
std::vector<std::shared_ptr<ITrackable>> trackables;
|
||||
std::vector<ITrackable *> trackables;
|
||||
|
||||
CircleObject *marker;
|
||||
|
||||
void initializeView();
|
||||
|
||||
void followTrackables();
|
||||
|
||||
void moveCenter(sf::Vector2<float> delta);
|
||||
|
@ -56,9 +54,9 @@ private:
|
|||
|
||||
sf::Vector2f restrainToBounds(sf::Vector2f viewSize) const;
|
||||
|
||||
float getFreeMovementRadius() const;
|
||||
float getRadius(float threshold) const;
|
||||
|
||||
sf::Vector2f getSizeInWindow() const;
|
||||
sf::Vector2f getWindowSize() const;
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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,12 @@ 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;
|
||||
};
|
||||
|
||||
#endif //HOLESOME_TRACKING_VIEW_OPTIONS_HPP
|
||||
|
|
|
@ -1,58 +0,0 @@
|
|||
#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)
|
||||
{
|
||||
collectableId = collectableCount;
|
||||
collectableCount++;
|
||||
}
|
||||
|
||||
void Collectable::setRotation(float angle)
|
||||
{
|
||||
// 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;
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
|
||||
sf::Vector2f Collectable::getSize() const
|
||||
{
|
||||
return size / WORLD_TO_ISO_SCALE;
|
||||
}
|
||||
|
||||
void Collectable::preRenderUpdate()
|
||||
{
|
||||
if (!sprite->isVisible())
|
||||
{
|
||||
auto closestPlayer = PlayerCollection::getInstance()->getClosestPlayer(*coordinates);
|
||||
closestPlayer->consume(points);
|
||||
setActive(false);
|
||||
return;
|
||||
}
|
||||
|
||||
GameObject::preRenderUpdate();
|
||||
}
|
|
@ -1,41 +0,0 @@
|
|||
#ifndef HOLESOME_COLLECTABLE_HPP
|
||||
#define HOLESOME_COLLECTABLE_HPP
|
||||
|
||||
|
||||
#include "../game_object.h"
|
||||
#include "../player/player.hpp"
|
||||
#include "../../sprites/masked_sprite.hpp"
|
||||
|
||||
class Collectable : public GameObject
|
||||
{
|
||||
public:
|
||||
Collectable(int points);
|
||||
|
||||
void setSprite(const std::string &spriteName);
|
||||
|
||||
void setRotation(float angle);
|
||||
|
||||
sf::Vector2f getSize() const;
|
||||
|
||||
float getDepth() const;
|
||||
|
||||
int getId() const
|
||||
{
|
||||
return collectableId;
|
||||
}
|
||||
|
||||
void preRenderUpdate() override;
|
||||
|
||||
private:
|
||||
int collectableId = 0;
|
||||
static inline int collectableCount = 0;
|
||||
std::shared_ptr<Sprite> sprite;
|
||||
|
||||
sf::Vector2f size;
|
||||
int points = 0;
|
||||
|
||||
std::shared_ptr<Player> consumedBy = nullptr;
|
||||
};
|
||||
|
||||
|
||||
#endif //HOLESOME_COLLECTABLE_HPP
|
|
@ -1,23 +0,0 @@
|
|||
#ifndef HOLESOME_COLLECTABLE_CONFIG_HPP
|
||||
#define HOLESOME_COLLECTABLE_CONFIG_HPP
|
||||
|
||||
#include <string>
|
||||
|
||||
struct CollectableConfig
|
||||
{
|
||||
std::string spriteName;
|
||||
int points = 0;
|
||||
|
||||
explicit CollectableConfig(std::string spriteName, int points)
|
||||
: spriteName(std::move(spriteName)), points(points)
|
||||
{}
|
||||
|
||||
CollectableConfig() = default;
|
||||
|
||||
bool isValid() const
|
||||
{
|
||||
return !spriteName.empty();
|
||||
}
|
||||
};
|
||||
|
||||
#endif //HOLESOME_COLLECTABLE_CONFIG_HPP
|
|
@ -1,12 +0,0 @@
|
|||
#include "collectable_factory.hpp"
|
||||
|
||||
std::shared_ptr<Collectable> CollectableFactory::createFromInLevelConfig(const CollectableInLevel &config)
|
||||
{
|
||||
auto collectableConfig = config.collectableConfig;
|
||||
auto collectable = std::make_shared<Collectable>(config.collectableConfig.points);
|
||||
|
||||
collectable->coordinates->setGrid(config.position);
|
||||
collectable->setSprite(collectableConfig.spriteName);
|
||||
|
||||
return collectable;
|
||||
}
|
|
@ -1,15 +0,0 @@
|
|||
#ifndef HOLESOME_COLLECTABLE_FACTORY_HPP
|
||||
#define HOLESOME_COLLECTABLE_FACTORY_HPP
|
||||
|
||||
|
||||
#include "collectable_in_level.hpp"
|
||||
#include "collectable.hpp"
|
||||
|
||||
class CollectableFactory
|
||||
{
|
||||
public:
|
||||
static std::shared_ptr<Collectable> createFromInLevelConfig(const CollectableInLevel &config);
|
||||
};
|
||||
|
||||
|
||||
#endif //HOLESOME_COLLECTABLE_FACTORY_HPP
|
|
@ -1,35 +0,0 @@
|
|||
#ifndef HOLESOME_COLLECTABLE_IN_LEVEL_HPP
|
||||
#define HOLESOME_COLLECTABLE_IN_LEVEL_HPP
|
||||
|
||||
#include "../../coordinates/coordinates.h"
|
||||
#include "collectable_config.hpp"
|
||||
#include "../../logging/easylogging++.h"
|
||||
#include "../../collectables.hpp"
|
||||
|
||||
struct CollectableInLevel
|
||||
{
|
||||
std::string name;
|
||||
GridCoordinates position;
|
||||
CollectableConfig collectableConfig;
|
||||
|
||||
CollectableInLevel(const std::string &name, GridCoordinates position)
|
||||
: name(name),
|
||||
position(position)
|
||||
{
|
||||
if (all_collectables.find(name) == all_collectables.end())
|
||||
{
|
||||
LOG(ERROR) << "Collectable config " << name << " not found. Skipping collectable.";
|
||||
this->name = ""; // Make this invalid
|
||||
return;
|
||||
}
|
||||
|
||||
this->collectableConfig = all_collectables.at(name);
|
||||
}
|
||||
|
||||
bool isValid() const
|
||||
{
|
||||
return !name.empty();
|
||||
}
|
||||
};
|
||||
|
||||
#endif //HOLESOME_COLLECTABLE_IN_LEVEL_HPP
|
|
@ -1,132 +0,0 @@
|
|||
#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()
|
||||
{
|
||||
if (singletonInstance == nullptr)
|
||||
{
|
||||
singletonInstance = std::make_shared<CollectablesCollection>();
|
||||
}
|
||||
return singletonInstance;
|
||||
}
|
||||
|
||||
void CollectablesCollection::createEmpty(int maxDepth)
|
||||
{
|
||||
// Remove previous collections
|
||||
depthCollections.clear();
|
||||
|
||||
LOG(INFO) << "Creating empty collectables collection with a max depth of " << maxDepth << " ...";
|
||||
// Create new collections
|
||||
for (int d = 0; d < maxDepth; d++)
|
||||
{
|
||||
auto depthCollection = std::make_shared<CollectablesDepthCollection>(d);
|
||||
depthCollections[d] = depthCollection;
|
||||
}
|
||||
}
|
||||
|
||||
void CollectablesCollection::remove(int collectableId)
|
||||
{
|
||||
HolesSimulation::getInstance()->removeCollectable(collectableId);
|
||||
|
||||
std::shared_ptr<Collectable> collectable = nullptr;
|
||||
for (auto &child: getChildren())
|
||||
{
|
||||
collectable = std::dynamic_pointer_cast<Collectable>(child);
|
||||
if (collectable->getId() == collectableId)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (collectable == nullptr)
|
||||
{
|
||||
LOG(ERROR) << "No collectables left to remove.";
|
||||
return;
|
||||
}
|
||||
|
||||
depthCollections[collectable->getDepth()]->remove(collectable);
|
||||
removeChild(collectable);
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// Move collectables to new depth collections if necessary
|
||||
|
||||
// First, clear history of all depth collections
|
||||
for (auto &[depth, depthCollection]: depthCollections)
|
||||
{
|
||||
depthCollection->clearHistory();
|
||||
}
|
||||
|
||||
// Then, move collectables to new depth collections
|
||||
for (auto &[depth, depthCollection]: depthCollections)
|
||||
{
|
||||
auto collectables = depthCollection->collectables;
|
||||
for (auto &collectable: collectables)
|
||||
{
|
||||
int newDepth = std::floor(collectable->getDepth());
|
||||
if (newDepth == depth)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
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
|
||||
int maxDepth = (int) depthCollections.size();
|
||||
for (int depth = maxDepth - 1; depth >= 0; depth--)
|
||||
{
|
||||
auto depthCollection = depthCollections.at(depth);
|
||||
for (auto &collectable: depthCollection->collectables)
|
||||
{
|
||||
collectable->draw(window);
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,35 +0,0 @@
|
|||
#ifndef HOLESOME_COLLECTABLES_COLLECTION_HPP
|
||||
#define HOLESOME_COLLECTABLES_COLLECTION_HPP
|
||||
|
||||
|
||||
#include <memory>
|
||||
#include <map>
|
||||
#include "collectables_depth_collection.hpp"
|
||||
|
||||
/**
|
||||
* @brief Collection of all collections for each depth.
|
||||
*/
|
||||
class CollectablesCollection : public GameObject
|
||||
{
|
||||
public:
|
||||
static std::shared_ptr<CollectablesCollection> getInstance();
|
||||
|
||||
void createEmpty(int maxDepth);
|
||||
|
||||
void update() override;
|
||||
|
||||
void draw(sf::RenderWindow *window) override;
|
||||
|
||||
void add(const std::shared_ptr<Collectable>& collectable);
|
||||
void remove(int collectableId);
|
||||
|
||||
private:
|
||||
static inline std::shared_ptr<CollectablesCollection> singletonInstance = nullptr;
|
||||
|
||||
std::map<int, std::shared_ptr<CollectablesDepthCollection>> depthCollections = {};
|
||||
|
||||
bool isValidDepth(int desiredDepth) const;
|
||||
};
|
||||
|
||||
|
||||
#endif //HOLESOME_COLLECTABLES_COLLECTION_HPP
|
|
@ -1,23 +0,0 @@
|
|||
#include "collectables_depth_collection.hpp"
|
||||
|
||||
CollectablesDepthCollection::CollectablesDepthCollection(int depth)
|
||||
: depth(depth)
|
||||
{}
|
||||
|
||||
void CollectablesDepthCollection::clearHistory()
|
||||
{
|
||||
recentlyAdded.clear();
|
||||
recentlyRemoved.clear();
|
||||
}
|
||||
|
||||
void CollectablesDepthCollection::add(const std::shared_ptr<Collectable> &collectable)
|
||||
{
|
||||
collectables.insert(collectable);
|
||||
recentlyAdded.insert(collectable);
|
||||
}
|
||||
|
||||
void CollectablesDepthCollection::remove(const std::shared_ptr<Collectable> &collectable)
|
||||
{
|
||||
recentlyRemoved.insert(collectable);
|
||||
collectables.erase(collectable);
|
||||
}
|
|
@ -1,28 +0,0 @@
|
|||
#ifndef HOLESOME_COLLECTABLES_DEPTH_COLLECTION_HPP
|
||||
#define HOLESOME_COLLECTABLES_DEPTH_COLLECTION_HPP
|
||||
|
||||
|
||||
#include <set>
|
||||
#include <memory>
|
||||
#include "../collectable.hpp"
|
||||
|
||||
/**
|
||||
* @brief Collection of collectables for a specific depth.
|
||||
*/
|
||||
struct CollectablesDepthCollection
|
||||
{
|
||||
int depth;
|
||||
std::set<std::shared_ptr<Collectable>> collectables = {};
|
||||
std::set<std::shared_ptr<Collectable>> recentlyRemoved = {};
|
||||
std::set<std::shared_ptr<Collectable>> recentlyAdded = {};
|
||||
|
||||
explicit CollectablesDepthCollection(int depth);
|
||||
|
||||
void clearHistory();
|
||||
|
||||
void add(const std::shared_ptr<Collectable>& collectable);
|
||||
void remove(const std::shared_ptr<Collectable>& collectable);
|
||||
};
|
||||
|
||||
|
||||
#endif //HOLESOME_COLLECTABLES_DEPTH_COLLECTION_HPP
|
14
src/game/depth_renderer.cpp
Normal file
|
@ -0,0 +1,14 @@
|
|||
#include "depth_renderer.hpp"
|
||||
|
||||
void DepthRenderer::addGameObject(const std::shared_ptr<GameObject>& gameObject)
|
||||
{
|
||||
// TODO: Subscribe to depth changes!
|
||||
gameObjects.insert(gameObject);
|
||||
}
|
||||
|
||||
void DepthRenderer::render(const std::shared_ptr<sf::RenderWindow> &window) const
|
||||
{
|
||||
for (const auto& gameObject : gameObjects) {
|
||||
gameObject->draw(window.get());
|
||||
}
|
||||
}
|
24
src/game/depth_renderer.hpp
Normal file
|
@ -0,0 +1,24 @@
|
|||
#ifndef HOLESOME_DEPTH_RENDERER_HPP
|
||||
#define HOLESOME_DEPTH_RENDERER_HPP
|
||||
|
||||
|
||||
#include <set>
|
||||
#include "game_object.h"
|
||||
|
||||
auto compareGameObjectDepth = [](const std::shared_ptr<GameObject>& a, const std::shared_ptr<GameObject>& b) { return a->coordinates->isometric().depth < b->coordinates->isometric().depth; };
|
||||
|
||||
class DepthRenderer
|
||||
{
|
||||
public:
|
||||
DepthRenderer() = default;
|
||||
|
||||
void addGameObject(const std::shared_ptr<GameObject>& gameObject);
|
||||
|
||||
void render(const std::shared_ptr<sf::RenderWindow>& window) const;
|
||||
|
||||
private:
|
||||
std::multiset<std::shared_ptr<GameObject>, decltype(compareGameObjectDepth)> gameObjects;
|
||||
};
|
||||
|
||||
|
||||
#endif //HOLESOME_DEPTH_RENDERER_HPP
|
|
@ -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;
|
||||
|
||||
}
|
|
@ -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
|
|
@ -3,29 +3,29 @@
|
|||
#include <SFML/Graphics/RenderWindow.hpp>
|
||||
|
||||
#include "game.h"
|
||||
#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))
|
||||
Game::Game(std::shared_ptr<sf::RenderWindow> window) : window(std::move(window)),
|
||||
gameObjects(),
|
||||
views()
|
||||
{
|
||||
}
|
||||
|
||||
Game::~Game()
|
||||
{
|
||||
for (auto &gameObject: gameObjects)
|
||||
{
|
||||
delete gameObject;
|
||||
}
|
||||
}
|
||||
|
||||
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,11 +35,8 @@ void Game::run()
|
|||
update();
|
||||
InputMapper::getInstance()->processEvents();
|
||||
}
|
||||
|
||||
drawFrame();
|
||||
}
|
||||
|
||||
LOG(INFO) << "Game closing ...";
|
||||
}
|
||||
|
||||
void Game::exit()
|
||||
|
@ -60,27 +57,17 @@ void Game::drawFrame()
|
|||
}
|
||||
|
||||
window->display();
|
||||
frameCounter->addFrame();
|
||||
}
|
||||
|
||||
void Game::addGameObject(const std::shared_ptr<GameObject> &gameObject)
|
||||
void Game::addGameObject(GameObject *gameObject)
|
||||
{
|
||||
gameObjectsBuffer.push_back(gameObject);
|
||||
gameObjects.push_back(gameObject);
|
||||
}
|
||||
|
||||
void Game::update()
|
||||
{
|
||||
handleWinning();
|
||||
|
||||
// Add new game objects
|
||||
for (const auto &gameObject: gameObjectsBuffer)
|
||||
{
|
||||
gameObjects.push_back(gameObject);
|
||||
}
|
||||
gameObjectsBuffer.clear();
|
||||
|
||||
// Basic Updates
|
||||
for (const auto &gameObject: gameObjects)
|
||||
for (auto &gameObject: gameObjects)
|
||||
{
|
||||
if (gameObject->getActive())
|
||||
{
|
||||
|
@ -89,7 +76,7 @@ void Game::update()
|
|||
}
|
||||
|
||||
// Late updates
|
||||
for (const auto &gameObject: gameObjects)
|
||||
for (auto &gameObject: gameObjects)
|
||||
{
|
||||
if (gameObject->getActive())
|
||||
{
|
||||
|
@ -97,24 +84,7 @@ void Game::update()
|
|||
}
|
||||
}
|
||||
|
||||
// Physics updates
|
||||
for (const auto &gameObject: gameObjects)
|
||||
{
|
||||
if (gameObject->getActive())
|
||||
{
|
||||
gameObject->physicsUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
InputMapper::getInstance()->updateIdentities();
|
||||
|
||||
for (const auto &gameObject: gameObjects)
|
||||
{
|
||||
if (gameObject->getActive())
|
||||
{
|
||||
gameObject->preRenderUpdate();
|
||||
}
|
||||
}
|
||||
InputMapper::getInstance()->updateIdentityEvents();
|
||||
}
|
||||
|
||||
std::shared_ptr<Game> Game::getInstance()
|
||||
|
@ -139,113 +109,7 @@ std::shared_ptr<Game> Game::constructInstance(const std::shared_ptr<sf::RenderWi
|
|||
return singletonInstance;
|
||||
}
|
||||
|
||||
void Game::clearGameObjects()
|
||||
void Game::registerView(TrackingView *view)
|
||||
{
|
||||
gameObjects.clear();
|
||||
}
|
||||
|
||||
void Game::setLevel(LevelConfig levelConfig)
|
||||
{
|
||||
loadedLevelConfig = std::move(levelConfig);
|
||||
}
|
||||
|
||||
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();
|
||||
views.push_back(view);
|
||||
}
|
||||
|
|
|
@ -7,9 +7,6 @@
|
|||
#include "game_object.h"
|
||||
#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,51 +14,29 @@ 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);
|
||||
|
||||
~Game();
|
||||
|
||||
void run();
|
||||
|
||||
void exit();
|
||||
|
||||
void clearGameObjects();
|
||||
void addGameObject(GameObject *gameObject);
|
||||
|
||||
void setLevel(LevelConfig levelConfig);
|
||||
|
||||
void startCountdown(int durationInSeconds);
|
||||
|
||||
[[nodiscard]] bool isLevelLoaded() const;
|
||||
|
||||
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 = {};
|
||||
std::vector<std::shared_ptr<GameObject>> gameObjectsBuffer = {};
|
||||
|
||||
LevelConfig loadedLevelConfig = {};
|
||||
|
||||
std::shared_ptr<FrameCounter> frameCounter = std::make_shared<FrameCounter>();
|
||||
std::shared_ptr<Countdown> countdown = std::make_shared<Countdown>();
|
||||
std::vector<GameObject *> gameObjects;
|
||||
|
||||
void drawFrame();
|
||||
|
||||
void update();
|
||||
|
||||
std::shared_ptr<Player> checkForWinner();
|
||||
|
||||
void handleWinning();
|
||||
|
||||
void showLoadingScreen(const std::string &message = "Loading...");
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -6,6 +6,12 @@ GameObject::GameObject()
|
|||
children = std::vector<std::shared_ptr<GameObject>>();
|
||||
}
|
||||
|
||||
void GameObject::addChild(const std::shared_ptr<GameObject>& child, WorldCoordinates offset)
|
||||
{
|
||||
children.push_back(child);
|
||||
child->coordinates->setParent(coordinates, offset);
|
||||
}
|
||||
|
||||
void GameObject::setActive(bool active)
|
||||
{
|
||||
// Careful: potential infinite loop!
|
||||
|
@ -16,80 +22,3 @@ void GameObject::setActive(bool active)
|
|||
child->setActive(active);
|
||||
}
|
||||
}
|
||||
|
||||
void GameObject::draw(sf::RenderWindow *window)
|
||||
{
|
||||
for (auto &child: children)
|
||||
{
|
||||
child->draw(window);
|
||||
}
|
||||
}
|
||||
|
||||
void GameObject::update()
|
||||
{
|
||||
for (auto &child: children)
|
||||
{
|
||||
child->update();
|
||||
}
|
||||
}
|
||||
|
||||
void GameObject::lateUpdate()
|
||||
{
|
||||
for (auto &child: children)
|
||||
{
|
||||
child->lateUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
void GameObject::addChildScreenOffset(const std::shared_ptr<GameObject> &child, IsometricCoordinates offset)
|
||||
{
|
||||
auto worldOffset = CoordinateTransformer::isometricToWorld(offset);
|
||||
addChildWorldOffset(child, worldOffset);
|
||||
}
|
||||
|
||||
void GameObject::addChildWorldOffset(const std::shared_ptr<GameObject> &child, WorldCoordinates offset)
|
||||
{
|
||||
children.push_back(child);
|
||||
child->coordinates->setParent(coordinates, offset);
|
||||
}
|
||||
|
||||
void GameObject::addChild(const std::shared_ptr<GameObject> &child)
|
||||
{
|
||||
addChildWorldOffset(child, {0, 0});
|
||||
}
|
||||
|
||||
void GameObject::removeChild(const std::shared_ptr<GameObject> &child)
|
||||
{
|
||||
auto it = std::find(children.begin(), children.end(), child);
|
||||
if (it != children.end())
|
||||
{
|
||||
children.erase(it);
|
||||
child->coordinates->removeParent();
|
||||
}
|
||||
}
|
||||
|
||||
void GameObject::clearChildren()
|
||||
{
|
||||
children.clear();
|
||||
}
|
||||
|
||||
void GameObject::physicsUpdate()
|
||||
{
|
||||
for (auto &child: children)
|
||||
{
|
||||
child->physicsUpdate();
|
||||
}
|
||||
}
|
||||
|
||||
void GameObject::addDetachedChild(const std::shared_ptr<GameObject> &child)
|
||||
{
|
||||
children.push_back(child);
|
||||
}
|
||||
|
||||
void GameObject::preRenderUpdate()
|
||||
{
|
||||
for (auto &child: children)
|
||||
{
|
||||
child->preRenderUpdate();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,26 +12,20 @@ class GameObject
|
|||
public:
|
||||
GameObject();
|
||||
|
||||
virtual void draw(sf::RenderWindow *window);
|
||||
virtual void draw(sf::RenderWindow *window) const
|
||||
{}
|
||||
|
||||
virtual void update();
|
||||
virtual void update()
|
||||
{}
|
||||
|
||||
virtual void lateUpdate();
|
||||
|
||||
virtual void physicsUpdate();
|
||||
|
||||
virtual void preRenderUpdate();
|
||||
virtual void lateUpdate()
|
||||
{}
|
||||
|
||||
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; }
|
||||
void addChild(const std::shared_ptr<GameObject> &child, WorldCoordinates offset = {0, 0});
|
||||
std::vector<std::shared_ptr<GameObject>> getChildren() const { return children; }
|
||||
|
||||
std::shared_ptr<TranslatedCoordinates> coordinates;
|
||||
|
||||
|
|
|
@ -1,37 +0,0 @@
|
|||
#ifndef HOLESOME_BUTTON_CONFIG_HPP
|
||||
#define HOLESOME_BUTTON_CONFIG_HPP
|
||||
|
||||
#include <vector>
|
||||
#include "game_action.hpp"
|
||||
#include "input_device_group.h"
|
||||
#include "direction.h"
|
||||
|
||||
struct ButtonConfig {
|
||||
std::vector<GameAction> actions;
|
||||
InputDeviceGroup deviceGroup;
|
||||
HardDirection direction;
|
||||
bool isValid;
|
||||
|
||||
ButtonConfig(InputDeviceGroup group, std::vector<GameAction> actions, HardDirection direction = HardDirection::NONE) :
|
||||
actions(std::move(actions)),
|
||||
deviceGroup(group),
|
||||
direction(direction),
|
||||
isValid(true)
|
||||
{}
|
||||
|
||||
ButtonConfig(InputDeviceGroup group, HardDirection direction) :
|
||||
actions({}),
|
||||
deviceGroup(group),
|
||||
direction(direction),
|
||||
isValid(true)
|
||||
{}
|
||||
|
||||
ButtonConfig() :
|
||||
actions({}),
|
||||
deviceGroup(InputDeviceGroup::UNKNOWN),
|
||||
direction(HardDirection::NONE),
|
||||
isValid(false)
|
||||
{}
|
||||
};
|
||||
|
||||
#endif //HOLESOME_BUTTON_CONFIG_HPP
|
|
@ -1,22 +0,0 @@
|
|||
#include "button_config_factory.hpp"
|
||||
#include "../../input_config.h"
|
||||
|
||||
ButtonConfig ButtonConfigFactory::fromKey(sf::Keyboard::Key key)
|
||||
{
|
||||
if (KEY_CONFIGS.find(key) != KEY_CONFIGS.end())
|
||||
{
|
||||
return KEY_CONFIGS.at(key);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
ButtonConfig ButtonConfigFactory::fromGamepadButton(unsigned int button)
|
||||
{
|
||||
if (GAMEPAD_BUTTON_CONFIGS.find(button) != GAMEPAD_BUTTON_CONFIGS.end())
|
||||
{
|
||||
return GAMEPAD_BUTTON_CONFIGS.at(button);
|
||||
}
|
||||
|
||||
return {};
|
||||
}
|
|
@ -1,17 +0,0 @@
|
|||
#ifndef HOLESOME_BUTTON_CONFIG_FACTORY_HPP
|
||||
#define HOLESOME_BUTTON_CONFIG_FACTORY_HPP
|
||||
|
||||
|
||||
#include <SFML/Window/Keyboard.hpp>
|
||||
#include "button_config.hpp"
|
||||
|
||||
class ButtonConfigFactory
|
||||
{
|
||||
public:
|
||||
static ButtonConfig fromKey(sf::Keyboard::Key key);
|
||||
|
||||
static ButtonConfig fromGamepadButton(unsigned int button);
|
||||
};
|
||||
|
||||
|
||||
#endif //HOLESOME_BUTTON_CONFIG_FACTORY_HPP
|
|
@ -3,6 +3,28 @@
|
|||
#include "../../utilities/vector_utils.hpp"
|
||||
#include "../../config.h"
|
||||
|
||||
HardDirection Direction::getKeyDirection(sf::Keyboard::Key key)
|
||||
{
|
||||
auto map = std::map<sf::Keyboard::Key, HardDirection>();
|
||||
map[sf::Keyboard::W] = HardDirection::UP;
|
||||
map[sf::Keyboard::S] = HardDirection::DOWN;
|
||||
map[sf::Keyboard::A] = HardDirection::LEFT;
|
||||
map[sf::Keyboard::D] = HardDirection::RIGHT;
|
||||
map[sf::Keyboard::Up] = HardDirection::UP;
|
||||
map[sf::Keyboard::Down] = HardDirection::DOWN;
|
||||
map[sf::Keyboard::Left] = HardDirection::LEFT;
|
||||
map[sf::Keyboard::Right] = HardDirection::RIGHT;
|
||||
map[sf::Keyboard::I] = HardDirection::UP;
|
||||
map[sf::Keyboard::K] = HardDirection::DOWN;
|
||||
map[sf::Keyboard::J] = HardDirection::LEFT;
|
||||
map[sf::Keyboard::L] = HardDirection::RIGHT;
|
||||
|
||||
if (map.find(key) == map.end())
|
||||
return HardDirection::NONE;
|
||||
|
||||
return map[key];
|
||||
}
|
||||
|
||||
sf::Vector2f Direction::getScreenVector(HardDirection direction)
|
||||
{
|
||||
auto vector = getVector(direction);
|
||||
|
|
|
@ -22,6 +22,8 @@ public:
|
|||
|
||||
explicit Direction(HardDirection direction);
|
||||
|
||||
static HardDirection getKeyDirection(sf::Keyboard::Key key);
|
||||
|
||||
static sf::Vector2f getVector(HardDirection direction);
|
||||
static sf::Vector2f getScreenVector(HardDirection direction);
|
||||
|
||||
|
|
|
@ -1,10 +0,0 @@
|
|||
#ifndef HOLESOME_GAME_ACTION_HPP
|
||||
#define HOLESOME_GAME_ACTION_HPP
|
||||
|
||||
enum GameAction
|
||||
{
|
||||
CONFIRM,
|
||||
RUN
|
||||
};
|
||||
|
||||
#endif //HOLESOME_GAME_ACTION_HPP
|
|
@ -1,19 +0,0 @@
|
|||
#ifndef HOLESOME_GAME_ACTION_CONFIG_HPP
|
||||
#define HOLESOME_GAME_ACTION_CONFIG_HPP
|
||||
|
||||
enum InteractionMode
|
||||
{
|
||||
PRESS,
|
||||
HOLD
|
||||
};
|
||||
|
||||
struct GameActionConfig
|
||||
{
|
||||
InteractionMode mode;
|
||||
|
||||
GameActionConfig(InteractionMode mode) :
|
||||
mode(mode)
|
||||
{}
|
||||
};
|
||||
|
||||
#endif //HOLESOME_GAME_ACTION_CONFIG_HPP
|
|
@ -1,23 +0,0 @@
|
|||
#ifndef HOLESOME_GAMEPAD_BUTTONS_HPP
|
||||
#define HOLESOME_GAMEPAD_BUTTONS_HPP
|
||||
|
||||
enum GamepadButton {
|
||||
SOUTH = 0,
|
||||
EAST = 1,
|
||||
NORTH = 2,
|
||||
WEST = 3,
|
||||
|
||||
LEFT_SHOULDER = 4,
|
||||
RIGHT_SHOULDER = 5,
|
||||
LEFT_TRIGGER = 6,
|
||||
RIGHT_TRIGGER = 7,
|
||||
|
||||
SELECT = 8,
|
||||
START = 9,
|
||||
HOME = 10,
|
||||
|
||||
LEFT_STICK = 11,
|
||||
RIGHT_STICK = 12
|
||||
};
|
||||
|
||||
#endif //HOLESOME_GAMEPAD_BUTTONS_HPP
|
|
@ -1,6 +1,7 @@
|
|||
#ifndef HOLESOME_INPUT_DEVICE_GROUP_H
|
||||
#define HOLESOME_INPUT_DEVICE_GROUP_H
|
||||
|
||||
#include "direction.h"
|
||||
#include <set>
|
||||
|
||||
enum InputDeviceGroup
|
||||
|
|
|
@ -4,18 +4,13 @@
|
|||
#include <set>
|
||||
#include "direction.h"
|
||||
#include "input_device_group.h"
|
||||
#include "key_features.hpp"
|
||||
#include "../../utilities/magic_enum.hpp"
|
||||
#include "../../logging/easylogging++.h"
|
||||
#include "../../input_config.h"
|
||||
#include <SFML/Window/Joystick.hpp>
|
||||
|
||||
|
||||
struct InputIdentity
|
||||
{
|
||||
std::set<GameAction> current_actions = {};
|
||||
std::set<GameAction> previous_actions = {};
|
||||
Direction direction = Direction();
|
||||
|
||||
InputDeviceGroup deviceGroup = InputDeviceGroup::UNKNOWN;
|
||||
unsigned int gamepadId = 0;
|
||||
bool isActive = true;
|
||||
|
@ -26,60 +21,25 @@ struct InputIdentity
|
|||
gamepadId = gamepad;
|
||||
|
||||
LOG(INFO) << "Created input identity [" << magic_enum::enum_name(type) << ", gamepad " << gamepadId << "]";
|
||||
|
||||
if (type == InputDeviceGroup::GAMEPAD)
|
||||
{
|
||||
auto numberOfButtons = sf::Joystick::getButtonCount(gamepadId);
|
||||
LOG(INFO) << "Gamepad has " << numberOfButtons << " buttons";
|
||||
}
|
||||
};
|
||||
|
||||
bool isPerformingAction(GameAction action)
|
||||
static InputDeviceGroup getDeviceTypeFromEvent(sf::Event event)
|
||||
{
|
||||
bool currently_performing = current_actions.find(action) != current_actions.end();
|
||||
bool previously_performing = previous_actions.find(action) != previous_actions.end();
|
||||
|
||||
auto action_config = GAME_ACTION_CONFIGS.at(action);
|
||||
|
||||
if (action_config.mode == InteractionMode::PRESS)
|
||||
switch (event.type)
|
||||
{
|
||||
return currently_performing && !previously_performing;
|
||||
} else if (action_config.mode == InteractionMode::HOLD)
|
||||
{
|
||||
return currently_performing;
|
||||
} else
|
||||
{
|
||||
LOG(ERROR) << "Unknown interaction mode [" << magic_enum::enum_name(action_config.mode) << "]";
|
||||
return false;
|
||||
case sf::Event::KeyPressed:
|
||||
case sf::Event::KeyReleased:
|
||||
return KeyFeatures(event.key.code).deviceGroup;
|
||||
case sf::Event::JoystickButtonPressed:
|
||||
case sf::Event::JoystickButtonReleased:
|
||||
case sf::Event::JoystickConnected:
|
||||
case sf::Event::JoystickDisconnected:
|
||||
case sf::Event::JoystickMoved:
|
||||
return InputDeviceGroup::GAMEPAD;
|
||||
default:
|
||||
return InputDeviceGroup::UNKNOWN;
|
||||
}
|
||||
}
|
||||
|
||||
void press(const ButtonConfig &button)
|
||||
{
|
||||
for (auto &action: button.actions)
|
||||
{
|
||||
current_actions.insert(action);
|
||||
}
|
||||
if (button.direction != HardDirection::NONE) {
|
||||
direction.add(button.direction);
|
||||
}
|
||||
}
|
||||
|
||||
void release(const ButtonConfig &button)
|
||||
{
|
||||
for (auto &action: button.actions)
|
||||
{
|
||||
current_actions.erase(action);
|
||||
}
|
||||
if (button.direction != HardDirection::NONE) {
|
||||
direction.remove(button.direction);
|
||||
}
|
||||
}
|
||||
|
||||
void update()
|
||||
{
|
||||
previous_actions = current_actions;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
#include "input_mapper.h"
|
||||
#include "input_device_group.h"
|
||||
#include "button_config_factory.hpp"
|
||||
|
||||
|
||||
InputMapper::InputMapper()
|
||||
|
@ -27,13 +26,7 @@ void InputMapper::processEvents()
|
|||
case sf::Event::Closed:
|
||||
Game::getInstance()->exit();
|
||||
break;
|
||||
case sf::Event::JoystickButtonPressed:
|
||||
getInputIdentity(InputDeviceGroup::GAMEPAD, event.joystickButton.joystickId)->press(
|
||||
ButtonConfigFactory::fromGamepadButton(event.joystickButton.button));
|
||||
break;
|
||||
case sf::Event::JoystickButtonReleased:
|
||||
getInputIdentity(InputDeviceGroup::GAMEPAD, event.joystickButton.joystickId)->release(
|
||||
ButtonConfigFactory::fromGamepadButton(event.joystickButton.button));
|
||||
case sf::Event::Resized:
|
||||
break;
|
||||
case sf::Event::JoystickMoved:
|
||||
handleJoystickMovement(event.joystickMove);
|
||||
|
@ -52,28 +45,28 @@ 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 || event.code == sf::Keyboard::Q))
|
||||
{
|
||||
Game::getInstance()->exit();
|
||||
return;
|
||||
}
|
||||
|
||||
// Handle directionVector
|
||||
auto button = ButtonConfigFactory::fromKey(event.code);
|
||||
if (button.deviceGroup != InputDeviceGroup::UNKNOWN)
|
||||
auto feature = KeyFeatures(event.code);
|
||||
if (feature.deviceGroup != InputDeviceGroup::UNKNOWN)
|
||||
{
|
||||
getInputIdentity(button.deviceGroup)->press(button);
|
||||
getInputIdentity(feature.deviceGroup)->direction.add(feature.hardDirection);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void InputMapper::handleKeyRelease(sf::Event::KeyEvent event)
|
||||
{
|
||||
auto button = ButtonConfigFactory::fromKey(event.code);
|
||||
if (button.deviceGroup != InputDeviceGroup::UNKNOWN)
|
||||
auto feature = KeyFeatures(event.code);
|
||||
if (feature.deviceGroup != InputDeviceGroup::UNKNOWN)
|
||||
{
|
||||
getInputIdentity(button.deviceGroup)->release(button);
|
||||
getInputIdentity(feature.deviceGroup)->direction.remove(feature.hardDirection);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -82,7 +75,7 @@ void InputMapper::handleJoystickMovement(sf::Event::JoystickMoveEvent event)
|
|||
{
|
||||
auto gamepadIdentity = getInputIdentity(InputDeviceGroup::GAMEPAD, event.joystickId);
|
||||
|
||||
auto value = event.position / 100.f; // Normalize to -1 to 1
|
||||
auto value = event.position / 100.f;
|
||||
auto axis = event.axis;
|
||||
|
||||
// Handle deadzone and joystick drift
|
||||
|
@ -121,13 +114,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;
|
||||
}
|
||||
|
||||
|
@ -157,13 +145,8 @@ std::set<std::shared_ptr<InputIdentity>> InputMapper::getInputIdentities()
|
|||
return inputIdentities;
|
||||
}
|
||||
|
||||
void InputMapper::updateIdentities()
|
||||
void InputMapper::updateIdentityEvents()
|
||||
{
|
||||
for (auto &identity: inputIdentities)
|
||||
{
|
||||
identity->update();
|
||||
}
|
||||
|
||||
newInputIdentities.clear();
|
||||
deprecatedInputIdentities.clear();
|
||||
}
|
||||
|
|
|
@ -19,14 +19,12 @@ public:
|
|||
static std::shared_ptr<InputMapper> getInstance();
|
||||
|
||||
void processEvents();
|
||||
void updateIdentities();
|
||||
void updateIdentityEvents();
|
||||
|
||||
std::shared_ptr<InputIdentity> getInputIdentity(InputDeviceGroup deviceGroup, unsigned int gamepadId = 0);
|
||||
|
||||
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;
|
||||
|
|
34
src/game/input/key_features.hpp
Normal file
|
@ -0,0 +1,34 @@
|
|||
#ifndef HOLESOME_KEY_FEATURES_HPP
|
||||
#define HOLESOME_KEY_FEATURES_HPP
|
||||
|
||||
|
||||
#include <memory>
|
||||
#include "direction.h"
|
||||
#include "input_identity.h"
|
||||
#include "../../config.h"
|
||||
#include "input_device_group.h"
|
||||
|
||||
struct KeyFeatures
|
||||
{
|
||||
sf::Keyboard::Key key;
|
||||
HardDirection hardDirection = HardDirection::NONE;
|
||||
InputDeviceGroup deviceGroup = InputDeviceGroup::UNKNOWN;
|
||||
|
||||
explicit KeyFeatures(sf::Keyboard::Key key)
|
||||
{
|
||||
this->key = key;
|
||||
hardDirection = Direction::getKeyDirection(key);
|
||||
|
||||
// Determine device group
|
||||
for (const auto &groupKeys: KEY_GROUPS)
|
||||
{
|
||||
if (groupKeys.second.contains(key))
|
||||
{
|
||||
deviceGroup = groupKeys.first;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
#endif //HOLESOME_KEY_FEATURES_HPP
|
5
src/game/input_handler.cpp
Normal file
|
@ -0,0 +1,5 @@
|
|||
//
|
||||
// Created by max on 27.04.23.
|
||||
//
|
||||
|
||||
#include "input_handler.h"
|
14
src/game/input_handler.h
Normal 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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -1,56 +0,0 @@
|
|||
#ifndef HOLESOME_LEVEL_CONFIG_HPP
|
||||
#define HOLESOME_LEVEL_CONFIG_HPP
|
||||
|
||||
|
||||
#include <SFML/System/Vector2.hpp>
|
||||
#include "../../coordinates/coordinates.h"
|
||||
#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 = {};
|
||||
std::vector<GridCoordinates> playerSpawnPoints = {};
|
||||
std::vector<CollectableInLevel> collectables = {};
|
||||
std::vector<sf::Color> skyColors = {};
|
||||
|
||||
LevelConfig(std::string name,
|
||||
int durationInSeconds,
|
||||
const std::vector<GridCoordinates> &playerSpawnPoints,
|
||||
const std::vector<CollectableInLevel> &collectables,
|
||||
std::vector<sf::Color> skyColors,
|
||||
const TileMapConfig &tileMapConfig)
|
||||
: name(std::move(name)),
|
||||
durationInSeconds(durationInSeconds),
|
||||
playerSpawnPoints(playerSpawnPoints),
|
||||
tileMapConfig(tileMapConfig),
|
||||
skyColors(std::move(skyColors))
|
||||
{
|
||||
worldMapSize = tileMapConfig.getSize();
|
||||
|
||||
// Remove invalid collectables
|
||||
for (auto &collectable: collectables)
|
||||
{
|
||||
if (collectable.isValid())
|
||||
{
|
||||
this->collectables.push_back(collectable);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
LevelConfig() = default;
|
||||
|
||||
bool isValid() const
|
||||
{
|
||||
return !name.empty();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
#endif //HOLESOME_LEVEL_CONFIG_HPP
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -1,110 +0,0 @@
|
|||
#include "level_loader.hpp"
|
||||
#include "../game.h"
|
||||
#include "../physics/map/map_simulation.hpp"
|
||||
#include "../../debug/grid_debug_layer.h"
|
||||
#include "../../levels.hpp"
|
||||
#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"
|
||||
|
||||
void LevelLoader::loadLevel(const LevelConfig &levelConfig)
|
||||
{
|
||||
auto game = Game::getInstance();
|
||||
game->clearGameObjects();
|
||||
|
||||
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();
|
||||
|
||||
|
||||
// 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)
|
||||
{
|
||||
levelRenderer->addChild(std::make_shared<GridDebugLayer>(0, 50, 0, 50));
|
||||
}
|
||||
|
||||
levelRenderer->addChild(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());
|
||||
|
||||
// Spawn collectibles
|
||||
for (auto const &collectableInfo: levelConfig.collectables)
|
||||
{
|
||||
spawnCollectable(collectableInfo);
|
||||
}
|
||||
|
||||
game->startCountdown(levelConfig.durationInSeconds);
|
||||
|
||||
// Must be last
|
||||
game->addGameObject(GlobalLayer::getInstance());
|
||||
|
||||
LOG(INFO) << "Finished loading level '" << levelConfig.name << "'.";
|
||||
}
|
||||
|
||||
void LevelLoader::loadLevel(const std::string &levelName)
|
||||
{
|
||||
// Does level exist?
|
||||
if (!all_levels.contains(levelName))
|
||||
{
|
||||
LOG(ERROR) << "Level '" << levelName << "' not found. Could not load it.";
|
||||
throw std::invalid_argument("Could not load level.");
|
||||
}
|
||||
|
||||
LevelLoader::loadLevel(all_levels.at(levelName));
|
||||
}
|
||||
|
||||
void LevelLoader::spawnCollectable(const CollectableInLevel &collectableInfo)
|
||||
{
|
||||
LOG(INFO) << "Spawning collectable '" << collectableInfo.name << "' ...";
|
||||
auto collectable = CollectableFactory::createFromInLevelConfig(collectableInfo);
|
||||
LOG(INFO) << "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.";
|
||||
}
|
|
@ -1,21 +0,0 @@
|
|||
#ifndef HOLESOME_LEVEL_LOADER_HPP
|
||||
#define HOLESOME_LEVEL_LOADER_HPP
|
||||
|
||||
|
||||
#include <memory>
|
||||
#include "level_config.hpp"
|
||||
|
||||
class LevelLoader
|
||||
{
|
||||
public:
|
||||
static void loadLevel(const LevelConfig& levelConfig);
|
||||
|
||||
static void loadLevel(const std::string &levelName);
|
||||
|
||||
static void cleanUp();
|
||||
|
||||
static void spawnCollectable(const CollectableInLevel &collectableInfo);
|
||||
};
|
||||
|
||||
|
||||
#endif //HOLESOME_LEVEL_LOADER_HPP
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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);
|
||||
}
|
|
@ -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
|
|
@ -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;
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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};
|
||||
}
|
|
@ -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
|
|
@ -1,100 +0,0 @@
|
|||
#include "map_player.hpp"
|
||||
#include "map_simulation.hpp"
|
||||
|
||||
void MapPlayer::updateSimulationPosition()
|
||||
{
|
||||
auto coordinates = player->coordinates->world();
|
||||
b2Vec2 playerPosition = b2Vec2(coordinates.x, coordinates.y);
|
||||
|
||||
// Calculate velocity that theoretically needs to be applied to the body, to get to the same position
|
||||
b2Vec2 delta = playerPosition - body->GetPosition();
|
||||
b2Vec2 velocity = {delta.x * FRAME_RATE, delta.y * FRAME_RATE};
|
||||
|
||||
body->SetLinearVelocity(velocity);
|
||||
|
||||
if (player->getWorldRadius() != shapeRadius)
|
||||
{
|
||||
updateShape();
|
||||
}
|
||||
}
|
||||
|
||||
void MapPlayer::updatePlayerPosition() const
|
||||
{
|
||||
b2Vec2 playerPosition = body->GetPosition();
|
||||
player->coordinates->setWorld({playerPosition.x, playerPosition.y});
|
||||
}
|
||||
|
||||
void MapPlayer::updateShape()
|
||||
{
|
||||
shapeRadius = player->getWorldRadius();
|
||||
|
||||
b2Fixture *oldFixture = body->GetFixtureList();
|
||||
if (oldFixture != nullptr)
|
||||
{
|
||||
body->DestroyFixture(oldFixture);
|
||||
}
|
||||
|
||||
b2CircleShape shape;
|
||||
shape.m_radius = shapeRadius;
|
||||
shape.m_p.Set(0, 0);
|
||||
|
||||
b2FixtureDef fixtureDef;
|
||||
fixtureDef.shape = &shape;
|
||||
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();
|
||||
}
|
|
@ -1,37 +0,0 @@
|
|||
#ifndef HOLESOME_MAP_PLAYER_HPP
|
||||
#define HOLESOME_MAP_PLAYER_HPP
|
||||
|
||||
|
||||
#include <box2d/box2d.h>
|
||||
|
||||
#include <utility>
|
||||
#include "../../player/player.hpp"
|
||||
#include "../../../config.h"
|
||||
|
||||
class MapPlayer
|
||||
{
|
||||
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)
|
||||
{
|
||||
updateShape();
|
||||
}
|
||||
|
||||
void updateSimulationPosition();
|
||||
void updatePlayer();
|
||||
|
||||
void updateShape();
|
||||
|
||||
private:
|
||||
|
||||
void updatePlayerPosition() const;
|
||||
|
||||
void updateCollidingWithPlayers();
|
||||
};
|
||||
|
||||
|
||||
#endif //HOLESOME_MAP_PLAYER_HPP
|
|
@ -1,131 +0,0 @@
|
|||
#include "map_simulation.hpp"
|
||||
#include "../../../config.h"
|
||||
#include "../../player/player_collection.hpp"
|
||||
|
||||
MapSimulation::MapSimulation()
|
||||
{
|
||||
mapPlayersById = std::map<int, std::shared_ptr<MapPlayer>>();
|
||||
}
|
||||
|
||||
std::shared_ptr<MapSimulation> MapSimulation::getInstance()
|
||||
{
|
||||
if (singletonInstance == nullptr)
|
||||
{
|
||||
singletonInstance = std::make_shared<MapSimulation>();
|
||||
}
|
||||
return singletonInstance;
|
||||
}
|
||||
|
||||
void MapSimulation::physicsUpdate()
|
||||
{
|
||||
// Update simulation positions
|
||||
for (auto &mapPlayer: mapPlayersById)
|
||||
{
|
||||
mapPlayer.second->updateSimulationPosition();
|
||||
}
|
||||
|
||||
world->Step(FRAME_TIME.asSeconds(), MAPSIM_VELOCITY_ITERATIONS, MAPSIM_POSITION_ITERATIONS);
|
||||
|
||||
// Update players
|
||||
for (auto &mapPlayer: mapPlayersById)
|
||||
{
|
||||
mapPlayer.second->updatePlayer();
|
||||
}
|
||||
}
|
||||
|
||||
void MapSimulation::resetMap(sf::Vector2<int> worldMapSize)
|
||||
{
|
||||
// Clear all players
|
||||
for (auto &[id, _]: mapPlayersById)
|
||||
{
|
||||
world->DestroyBody(mapPlayersById[id]->body);
|
||||
}
|
||||
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
|
||||
}
|
||||
|
||||
void MapSimulation::constructSquareObstacle(float minX, float minY, float maxX, float maxY)
|
||||
{
|
||||
b2BodyDef bodyDef;
|
||||
bodyDef.type = b2_staticBody;
|
||||
bodyDef.position.Set((maxX + minX) / 2.f, (maxY + minY) / 2.f);
|
||||
b2Body *body = world->CreateBody(&bodyDef);
|
||||
|
||||
b2PolygonShape shape;
|
||||
shape.SetAsBox((maxX - minX) / 2.f, (maxY - minY) / 2.f);
|
||||
|
||||
b2FixtureDef fixtureDef;
|
||||
fixtureDef.shape = &shape;
|
||||
fixtureDef.density = 1.0f;
|
||||
|
||||
body->CreateFixture(&fixtureDef);
|
||||
}
|
||||
|
||||
void MapSimulation::addPlayer(const std::shared_ptr<Player> &player)
|
||||
{
|
||||
b2BodyDef bodyDef;
|
||||
bodyDef.type = b2_dynamicBody;
|
||||
bodyDef.position.Set(player->spawnPosition.world().x, player->spawnPosition.world().y);
|
||||
b2Body *body = world->CreateBody(&bodyDef);
|
||||
mapPlayersById[player->getPlayerId()] = std::make_shared<MapPlayer>(player, body);
|
||||
}
|
||||
|
||||
void MapSimulation::update()
|
||||
{
|
||||
// Update players from player collection
|
||||
// New player
|
||||
for (auto &player: PlayerCollection::getInstance()->getNewPlayers())
|
||||
{
|
||||
addPlayer(player);
|
||||
}
|
||||
// Removed players
|
||||
for (auto &player: PlayerCollection::getInstance()->getRemovedPlayers())
|
||||
{
|
||||
removePlayer(player);
|
||||
}
|
||||
}
|
||||
|
||||
void MapSimulation::removePlayer(std::shared_ptr<Player> &player)
|
||||
{
|
||||
// Remove body from simulation
|
||||
int playerId = player->getPlayerId();
|
||||
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);
|
||||
}
|