summaryrefslogtreecommitdiff
path: root/src/openvic
diff options
context:
space:
mode:
Diffstat (limited to 'src/openvic')
-rw-r--r--src/openvic/Date.cpp19
-rw-r--r--src/openvic/Date.hpp6
-rw-r--r--src/openvic/GameAdvancementHook.cpp22
-rw-r--r--src/openvic/GameAdvancementHook.hpp18
-rw-r--r--src/openvic/GameManager.cpp5
-rw-r--r--src/openvic/GameManager.hpp4
-rw-r--r--src/openvic/Logger.hpp83
-rw-r--r--src/openvic/Types.cpp4
-rw-r--r--src/openvic/Types.hpp11
-rw-r--r--src/openvic/economy/Good.cpp14
-rw-r--r--src/openvic/economy/Good.hpp5
-rw-r--r--src/openvic/map/Building.cpp14
-rw-r--r--src/openvic/map/Building.hpp14
-rw-r--r--src/openvic/map/Map.cpp35
-rw-r--r--src/openvic/map/Map.hpp11
-rw-r--r--src/openvic/map/Province.cpp10
-rw-r--r--src/openvic/map/Province.hpp1
-rw-r--r--src/openvic/map/Region.cpp2
-rw-r--r--src/openvic/map/Region.hpp3
-rw-r--r--src/openvic/utility/BMP.cpp162
-rw-r--r--src/openvic/utility/BMP.hpp48
-rw-r--r--src/openvic/utility/Logger.cpp (renamed from src/openvic/Logger.cpp)8
-rw-r--r--src/openvic/utility/Logger.hpp80
23 files changed, 421 insertions, 158 deletions
diff --git a/src/openvic/Date.cpp b/src/openvic/Date.cpp
index d25e99f..f7bf9a3 100644
--- a/src/openvic/Date.cpp
+++ b/src/openvic/Date.cpp
@@ -1,13 +1,13 @@
#include "Date.hpp"
-#include <cctype>
#include <algorithm>
+#include <cctype>
-#include "Logger.hpp"
+#include "utility/Logger.hpp"
using namespace OpenVic;
-Timespan::Timespan(day_t value) : days{value} {}
+Timespan::Timespan(day_t value) : days { value } {}
bool Timespan::operator<(Timespan other) const { return days < other.days; };
bool Timespan::operator>(Timespan other) const { return days > other.days; };
@@ -67,14 +67,14 @@ Timespan Date::_dateToTimespan(year_t year, month_t month, day_t day) {
return year * DAYS_IN_YEAR + DAYS_UP_TO_MONTH[month - 1] + day - 1;
}
-Date::Date(Timespan total_days) : timespan{ total_days } {
+Date::Date(Timespan total_days) : timespan { total_days } {
if (timespan < 0) {
Logger::error("Invalid timespan for date: ", timespan, " (cannot be negative)");
timespan = 0;
}
}
-Date::Date(year_t year, month_t month, day_t day) : timespan{ _dateToTimespan(year, month, day) } {}
+Date::Date(year_t year, month_t month, day_t day) : timespan { _dateToTimespan(year, month, day) } {}
Date::year_t Date::getYear() const {
return static_cast<Timespan::day_t>(timespan) / DAYS_IN_YEAR;
@@ -89,7 +89,6 @@ Date::day_t Date::getDay() const {
return days_in_year - DAYS_UP_TO_MONTH[days_in_year / 32] + 1;
}
-
bool Date::operator<(Date other) const { return timespan < other.timespan; };
bool Date::operator>(Date other) const { return timespan > other.timespan; };
bool Date::operator<=(Date other) const { return timespan <= other.timespan; };
@@ -129,7 +128,7 @@ Date::operator std::string() const {
}
std::ostream& OpenVic::operator<<(std::ostream& out, Date const& date) {
- return out << (int) date.getYear() << '.' << (int) date.getMonth() << '.' << (int) date.getDay();
+ return out << (int)date.getYear() << '.' << (int)date.getMonth() << '.' << (int)date.getDay();
}
// Parsed from string of the form YYYY.MM.DD
@@ -140,17 +139,17 @@ Date Date::from_string(std::string const& date) {
size_t first_pos = 0;
while (first_pos < date.length() && std::isdigit(date[first_pos++]));
- year = atoi(date.substr(0, first_pos).c_str());
+ year = stoi(date.substr(0, first_pos));
if (first_pos < date.length()) {
if (date[first_pos] == '.') {
size_t second_pos = first_pos + 1;
while (second_pos < date.length() && std::isdigit(date[second_pos++]));
- month = atoi(date.substr(first_pos, second_pos - first_pos).c_str());
+ month = stoi(date.substr(first_pos, second_pos - first_pos));
if (second_pos < date.length()) {
if (date[second_pos] == '.') {
size_t third_pos = second_pos + 1;
while (third_pos < date.length() && std::isdigit(date[third_pos++]));
- day = atoi(date.substr(second_pos, third_pos - second_pos).c_str());
+ day = stoi(date.substr(second_pos, third_pos - second_pos));
if (third_pos < date.length())
Logger::error("Unexpected string \"", date.substr(third_pos), "\" at the end of date ", date);
} else Logger::error("Unexpected character \"", date[second_pos], "\" in date ", date);
diff --git a/src/openvic/Date.hpp b/src/openvic/Date.hpp
index 61de9ba..151b4e5 100644
--- a/src/openvic/Date.hpp
+++ b/src/openvic/Date.hpp
@@ -1,15 +1,17 @@
#pragma once
#include <cstdint>
-#include <string>
#include <ostream>
+#include <string>
namespace OpenVic {
// A relative period between points in time, measured in days
struct Timespan {
using day_t = int64_t;
+
private:
day_t days;
+
public:
Timespan(day_t value = 0);
@@ -46,11 +48,13 @@ namespace OpenVic {
static constexpr Timespan::day_t DAYS_IN_YEAR = 365;
static constexpr Timespan::day_t DAYS_IN_MONTH[MONTHS_IN_YEAR] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
static constexpr Timespan::day_t DAYS_UP_TO_MONTH[MONTHS_IN_YEAR] = { 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334 };
+
private:
// Number of days since Jan 1st, Year 0
Timespan timespan;
static Timespan _dateToTimespan(year_t year, month_t month, day_t day);
+
public:
// The Timespan is considered to be the number of days since Jan 1st, Year 0
Date(Timespan total_days);
diff --git a/src/openvic/GameAdvancementHook.cpp b/src/openvic/GameAdvancementHook.cpp
index 0fcdd03..ac16158 100644
--- a/src/openvic/GameAdvancementHook.cpp
+++ b/src/openvic/GameAdvancementHook.cpp
@@ -3,15 +3,19 @@
using namespace OpenVic;
const std::vector<std::chrono::milliseconds> GameAdvancementHook::GAME_SPEEDS = {
- std::chrono::milliseconds{ 4000 },
- std::chrono::milliseconds{ 3000 },
- std::chrono::milliseconds{ 2000 },
- std::chrono::milliseconds{ 1000 },
- std::chrono::milliseconds{ 100 },
- std::chrono::milliseconds{ 1 } };
+ std::chrono::milliseconds { 4000 },
+ std::chrono::milliseconds { 3000 },
+ std::chrono::milliseconds { 2000 },
+ std::chrono::milliseconds { 1000 },
+ std::chrono::milliseconds { 100 },
+ std::chrono::milliseconds { 1 }
+};
-GameAdvancementHook::GameAdvancementHook(AdvancementFunction tickFunction, RefreshFunction updateFunction, bool startPaused, speed_t startingSpeed)
- : triggerFunction{ tickFunction }, refreshFunction{ updateFunction }, isPaused{ startPaused } {
+GameAdvancementHook::GameAdvancementHook(AdvancementFunction tickFunction,
+ RefreshFunction updateFunction, bool startPaused, speed_t startingSpeed)
+ : triggerFunction { tickFunction },
+ refreshFunction { updateFunction },
+ isPaused { startPaused } {
lastPolledTime = std::chrono::high_resolution_clock::now();
setSimulationSpeed(startingSpeed);
}
@@ -57,7 +61,7 @@ GameAdvancementHook& GameAdvancementHook::operator--() {
void GameAdvancementHook::conditionallyAdvanceGame() {
if (!isPaused) {
- std::chrono::time_point<std::chrono::high_resolution_clock> currentTime = std::chrono::high_resolution_clock::now();
+ time_point_t currentTime = std::chrono::high_resolution_clock::now();
if (std::chrono::duration_cast<std::chrono::milliseconds>(currentTime - lastPolledTime) >= GAME_SPEEDS[currentSpeed]) {
lastPolledTime = currentTime;
if (triggerFunction) triggerFunction();
diff --git a/src/openvic/GameAdvancementHook.hpp b/src/openvic/GameAdvancementHook.hpp
index 5d904bc..59e43a4 100644
--- a/src/openvic/GameAdvancementHook.hpp
+++ b/src/openvic/GameAdvancementHook.hpp
@@ -5,25 +5,27 @@
#include <vector>
namespace OpenVic {
- //Conditionally advances game with provided behaviour
- //Class governs game speed and pause state
+ // Conditionally advances game with provided behaviour
+ // Class governs game speed and pause state
class GameAdvancementHook {
- public:
+ public:
using AdvancementFunction = std::function<void()>;
using RefreshFunction = std::function<void()>;
using speed_t = int8_t;
- //Minimum number of miliseconds before the simulation advances
+ // Minimum number of miliseconds before the simulation advances
static const std::vector<std::chrono::milliseconds> GAME_SPEEDS;
- private:
- std::chrono::time_point<std::chrono::high_resolution_clock> lastPolledTime;
- //A function pointer that advances the simulation, intended to be a capturing lambda or something similar. May need to be reworked later
+ private:
+ using time_point_t = std::chrono::time_point<std::chrono::high_resolution_clock>;
+
+ time_point_t lastPolledTime;
+ // A function pointer that advances the simulation, intended to be a capturing lambda or something similar. May need to be reworked later
AdvancementFunction triggerFunction;
RefreshFunction refreshFunction;
speed_t currentSpeed;
- public:
+ public:
bool isPaused;
GameAdvancementHook(AdvancementFunction tickFunction, RefreshFunction updateFunction, bool startPaused = true, speed_t startingSpeed = 0);
diff --git a/src/openvic/GameManager.cpp b/src/openvic/GameManager.cpp
index 50d7358..b2a9c27 100644
--- a/src/openvic/GameManager.cpp
+++ b/src/openvic/GameManager.cpp
@@ -1,11 +1,12 @@
#include "GameManager.hpp"
-#include "Logger.hpp"
+#include "utility/Logger.hpp"
using namespace OpenVic;
GameManager::GameManager(state_updated_func_t state_updated_callback)
- : clock{ [this]() { tick(); }, [this]() { update_state(); } }, state_updated{ state_updated_callback } {}
+ : clock { [this]() { tick(); }, [this]() { update_state(); } },
+ state_updated { state_updated_callback } {}
void GameManager::set_needs_update() {
needs_update = true;
diff --git a/src/openvic/GameManager.hpp b/src/openvic/GameManager.hpp
index e1a9e9b..b8dfbf6 100644
--- a/src/openvic/GameManager.hpp
+++ b/src/openvic/GameManager.hpp
@@ -1,8 +1,8 @@
#pragma once
#include "GameAdvancementHook.hpp"
-#include "map/Map.hpp"
#include "economy/Good.hpp"
+#include "map/Map.hpp"
namespace OpenVic {
struct GameManager {
@@ -12,6 +12,7 @@ namespace OpenVic {
BuildingManager building_manager;
GoodManager good_manager;
GameAdvancementHook clock;
+
private:
time_t session_start; /* SS-54, as well as allowing time-tracking */
Date today;
@@ -21,6 +22,7 @@ namespace OpenVic {
void set_needs_update();
void update_state();
void tick();
+
public:
GameManager(state_updated_func_t state_updated_callback);
diff --git a/src/openvic/Logger.hpp b/src/openvic/Logger.hpp
deleted file mode 100644
index 2c603e2..0000000
--- a/src/openvic/Logger.hpp
+++ /dev/null
@@ -1,83 +0,0 @@
-#pragma once
-
-#include <functional>
-#include <sstream>
-#ifdef __cpp_lib_source_location
-#include <source_location>
-#endif
-
-namespace OpenVic {
-
- #ifndef __cpp_lib_source_location
- #include <string>
- //Implementation of std::source_location for compilers that do not support it
- //Note: uses non-standard extensions that are supported by Clang, GCC, and MSVC
- //https://clang.llvm.org/docs/LanguageExtensions.html#source-location-builtins
- //https://stackoverflow.com/a/67970107
- class source_location {
- std::string _file;
- int _line;
- std::string _function;
-
- public:
- source_location(std::string f, int l, std::string n) : _file(f), _line(l), _function(n) {}
- static source_location current(std::string f = __builtin_FILE(), int l = __builtin_LINE(), std::string n = __builtin_FUNCTION()) {
- return source_location(f, l, n);
- }
-
- inline char const* file_name() const { return _file.c_str(); }
- inline int line() const {return _line; }
- inline char const* function_name() const { return _function.c_str(); }
- };
- #endif
-
- class Logger {
- using log_func_t = std::function<void(std::string&&)>;
-
- #ifdef __cpp_lib_source_location
- using source_location = std::source_location;
- #else
- using source_location = OpenVic::source_location;
- #endif
-
- static log_func_t info_func, error_func;
-
- static char const* get_filename(char const* filepath);
-
- template <typename... Ts>
- struct log {
- log(log_func_t log_func, Ts&&... ts, const source_location& location) {
- if (log_func) {
- std::stringstream stream;
- stream << std::endl << get_filename(location.file_name()) << "(" << location.line() << ") `" << location.function_name() << "`: ";
- ((stream << std::forward<Ts>(ts)), ...);
- stream << std::endl;
- log_func(stream.str());
- }
- }
- };
- public:
- static void set_info_func(log_func_t log_func);
- static void set_error_func(log_func_t log_func);
-
- template <typename... Ts>
- struct info {
- info(Ts&&... ts, const source_location& location = source_location::current()) {
- log<Ts...>{ info_func, std::forward<Ts>(ts)..., location };
- }
- };
-
- template <typename... Ts>
- info(Ts&&...) -> info<Ts...>;
-
- template <typename... Ts>
- struct error {
- error(Ts&&... ts, const source_location& location = source_location::current()) {
- log<Ts...>{ error_func, std::forward<Ts>(ts)..., location };
- }
- };
-
- template <typename... Ts>
- error(Ts&&...) -> error<Ts...>;
- };
-}
diff --git a/src/openvic/Types.cpp b/src/openvic/Types.cpp
index 829770b..ab5d12a 100644
--- a/src/openvic/Types.cpp
+++ b/src/openvic/Types.cpp
@@ -1,12 +1,12 @@
#include "Types.hpp"
#include <cassert>
-#include <sstream>
#include <iomanip>
+#include <sstream>
using namespace OpenVic;
-HasIdentifier::HasIdentifier(std::string const& new_identifier) : identifier{ new_identifier } {
+HasIdentifier::HasIdentifier(std::string const& new_identifier) : identifier { new_identifier } {
assert(!identifier.empty());
}
diff --git a/src/openvic/Types.hpp b/src/openvic/Types.hpp
index c528ac0..fe22dc9 100644
--- a/src/openvic/Types.hpp
+++ b/src/openvic/Types.hpp
@@ -1,11 +1,11 @@
#pragma once
-#include <vector>
-#include <cstdint>
#include <algorithm>
+#include <cstdint>
#include <map>
+#include <vector>
-#include "Logger.hpp"
+#include "utility/Logger.hpp"
namespace OpenVic {
// Represents a 24-bit RGB integer OR a 32-bit ARGB integer
@@ -44,8 +44,10 @@ namespace OpenVic {
*/
class HasIdentifier {
const std::string identifier;
+
protected:
HasIdentifier(std::string const& new_identifier);
+
public:
HasIdentifier(HasIdentifier const&) = delete;
HasIdentifier(HasIdentifier&&) = default;
@@ -60,8 +62,10 @@ namespace OpenVic {
*/
class HasColour {
const colour_t colour;
+
protected:
HasColour(colour_t const new_colour, bool can_be_null = false);
+
public:
HasColour(HasColour const&) = delete;
HasColour(HasColour&&) = default;
@@ -87,6 +91,7 @@ namespace OpenVic {
std::vector<T> items;
bool locked = false;
identifier_index_map_t identifier_index_map;
+
public:
IdentifierRegistry(std::string const& new_name) : name(new_name) {}
return_t add_item(T&& item) {
diff --git a/src/openvic/economy/Good.cpp b/src/openvic/economy/Good.cpp
index b4fa060..6d515d5 100644
--- a/src/openvic/economy/Good.cpp
+++ b/src/openvic/economy/Good.cpp
@@ -6,9 +6,14 @@ using namespace OpenVic;
Good::Good(std::string const& new_identifier, std::string const& new_category, colour_t new_colour, price_t new_base_price,
bool new_default_available, bool new_tradeable, bool new_currency, bool new_overseas_maintenance)
- : HasIdentifier{ new_identifier }, HasColour{ new_colour, true }, category{ new_category }, base_price{ new_base_price },
- default_available{ new_default_available }, tradeable{ new_tradeable }, currency{ new_currency },
- overseas_maintenance{ new_overseas_maintenance } {
+ : HasIdentifier { new_identifier },
+ HasColour { new_colour, true },
+ category { new_category },
+ base_price { new_base_price },
+ default_available { new_default_available },
+ tradeable { new_tradeable },
+ currency { new_currency },
+ overseas_maintenance { new_overseas_maintenance } {
assert(base_price > NULL_PRICE);
}
@@ -37,7 +42,7 @@ void Good::reset_to_defaults() {
price = base_price;
}
-GoodManager::GoodManager() : goods{ "goods" } {}
+GoodManager::GoodManager() : goods { "goods" } {}
return_t GoodManager::add_good(std::string const& identifier, std::string const& category, colour_t colour,
price_t base_price, bool default_available, bool tradeable, bool currency, bool overseas_maintenance) {
@@ -76,4 +81,3 @@ size_t GoodManager::get_good_count() const {
std::vector<Good> const& GoodManager::get_goods() const {
return goods.get_items();
}
-
diff --git a/src/openvic/economy/Good.hpp b/src/openvic/economy/Good.hpp
index 75f5a73..577280d 100644
--- a/src/openvic/economy/Good.hpp
+++ b/src/openvic/economy/Good.hpp
@@ -9,7 +9,7 @@ namespace OpenVic {
* ECON-15, ECON-16, ECON-17, ECON-18, ECON-19, ECON-20, ECON-21, ECON-22, ECON-23, ECON-24, ECON-25, ECON-26,
* ECON-27, ECON-28, ECON-29, ECON-30, ECON-31, ECON-32, ECON-33, ECON-34, ECON-35, ECON-36, ECON-37, ECON-38,
* ECON-39, ECON-40, ECON-41, ECON-42, ECON-43, ECON-44, ECON-45, ECON-46, ECON-47, ECON-48, ECON-49, ECON-50
- *
+ *
* ECON-123, ECON-124, ECON-125, ECON-126, ECON-127, ECON-128, ECON-129, ECON-130, ECON-131, ECON-132, ECON-133, ECON-134,
* ECON-135, ECON-136, ECON-137, ECON-138, ECON-139, ECON-140, ECON-141, ECON-142, ECON-234, ECON-235, ECON-236, ECON-237,
* ECON-238, ECON-239, ECON-240, ECON-241, ECON-242, ECON-243, ECON-244, ECON-245, ECON-246, ECON-247, ECON-248, ECON-249,
@@ -17,6 +17,7 @@ namespace OpenVic {
*/
struct Good : HasIdentifier, HasColour {
friend struct GoodManager;
+
private:
const std::string category;
const price_t base_price;
@@ -26,6 +27,7 @@ namespace OpenVic {
Good(std::string const& new_identifier, std::string const& new_category, colour_t new_colour, price_t new_base_price,
bool new_default_available, bool new_tradeable, bool new_currency, bool new_overseas_maintenance);
+
public:
Good(Good&&) = default;
@@ -40,6 +42,7 @@ namespace OpenVic {
struct GoodManager {
private:
IdentifierRegistry<Good> goods;
+
public:
GoodManager();
diff --git a/src/openvic/map/Building.cpp b/src/openvic/map/Building.cpp
index ccd5ad7..317ccdf 100644
--- a/src/openvic/map/Building.cpp
+++ b/src/openvic/map/Building.cpp
@@ -2,12 +2,14 @@
#include <cassert>
-#include "../Logger.hpp"
+#include "../utility/Logger.hpp"
#include "Province.hpp"
using namespace OpenVic;
-Building::Building(BuildingType const& new_type) : HasIdentifier{ new_type.get_identifier() }, type{ new_type } {}
+Building::Building(BuildingType const& new_type)
+ : HasIdentifier { new_type.get_identifier() },
+ type { new_type } {}
bool Building::_can_expand() const {
return level < type.get_max_level();
@@ -74,8 +76,10 @@ void Building::tick(Date const& today) {
}
}
-BuildingType::BuildingType(std::string const& new_identifier, Building::level_t new_max_level, Timespan new_build_time) :
- HasIdentifier{ new_identifier }, max_level{ new_max_level }, build_time{ new_build_time } {
+BuildingType::BuildingType(std::string const& new_identifier, Building::level_t new_max_level, Timespan new_build_time)
+ : HasIdentifier { new_identifier },
+ max_level { new_max_level },
+ build_time { new_build_time } {
assert(max_level >= 0);
assert(build_time >= 0);
}
@@ -88,7 +92,7 @@ Timespan BuildingType::get_build_time() const {
return build_time;
}
-BuildingManager::BuildingManager() : building_types{ "building types" } {}
+BuildingManager::BuildingManager() : building_types { "building types" } {}
return_t BuildingManager::add_building_type(std::string const& identifier, Building::level_t max_level, Timespan build_time) {
if (identifier.empty()) {
diff --git a/src/openvic/map/Building.hpp b/src/openvic/map/Building.hpp
index ca5c196..98c3991 100644
--- a/src/openvic/map/Building.hpp
+++ b/src/openvic/map/Building.hpp
@@ -2,8 +2,8 @@
#include <vector>
-#include "../Types.hpp"
#include "../Date.hpp"
+#include "../Types.hpp"
namespace OpenVic {
struct Province;
@@ -19,7 +19,13 @@ namespace OpenVic {
using level_t = int8_t;
- enum class ExpansionState { CannotExpand, CanExpand, Preparing, Expanding };
+ enum class ExpansionState {
+ CannotExpand,
+ CanExpand,
+ Preparing,
+ Expanding
+ };
+
private:
BuildingType const& type;
level_t level = 0;
@@ -30,6 +36,7 @@ namespace OpenVic {
Building(BuildingType const& new_type);
bool _can_expand() const;
+
public:
Building(Building&&) = default;
@@ -49,11 +56,13 @@ namespace OpenVic {
struct BuildingType : HasIdentifier {
friend struct BuildingManager;
+
private:
const Building::level_t max_level;
const Timespan build_time;
BuildingType(std::string const& new_identifier, Building::level_t new_max_level, Timespan new_build_time);
+
public:
BuildingType(BuildingType&&) = default;
@@ -64,6 +73,7 @@ namespace OpenVic {
struct BuildingManager {
private:
IdentifierRegistry<BuildingType> building_types;
+
public:
BuildingManager();
diff --git a/src/openvic/map/Map.cpp b/src/openvic/map/Map.cpp
index af1ea8a..2efee32 100644
--- a/src/openvic/map/Map.cpp
+++ b/src/openvic/map/Map.cpp
@@ -3,16 +3,20 @@
#include <cassert>
#include <unordered_set>
-#include "../Logger.hpp"
#include "../economy/Good.hpp"
+#include "../utility/Logger.hpp"
using namespace OpenVic;
Mapmode::Mapmode(index_t new_index, std::string const& new_identifier, colour_func_t new_colour_func)
- : HasIdentifier{ new_identifier }, index{ new_index }, colour_func{ new_colour_func } {
+ : HasIdentifier { new_identifier },
+ index { new_index },
+ colour_func { new_colour_func } {
assert(colour_func != nullptr);
}
+const Mapmode Mapmode::ERROR_MAPMODE { 0, "mapmode_error", [](Map const& map, Province const& province) -> colour_t { return 0xFFFF0000; } };
+
Mapmode::index_t Mapmode::get_index() const {
return index;
}
@@ -21,7 +25,9 @@ colour_t Mapmode::get_colour(Map const& map, Province const& province) const {
return colour_func ? colour_func(map, province) : NULL_COLOUR;
}
-Map::Map() : provinces{ "provinces" }, regions{ "regions" }, mapmodes{ "mapmodes" } {}
+Map::Map() : provinces { "provinces" },
+ regions { "regions" },
+ mapmodes { "mapmodes" } {}
return_t Map::add_province(std::string const& identifier, colour_t colour) {
if (provinces.get_item_count() >= MAX_INDEX) {
@@ -36,7 +42,7 @@ return_t Map::add_province(std::string const& identifier, colour_t colour) {
Logger::error("Invalid province colour: ", Province::colour_to_hex_string(colour));
return FAILURE;
}
- Province new_province{ static_cast<index_t>(provinces.get_item_count() + 1), identifier, colour };
+ Province new_province { static_cast<index_t>(provinces.get_item_count() + 1), identifier, colour };
const index_t index = get_index_from_colour(colour);
if (index != NULL_INDEX) {
Logger::error("Duplicate province colours: ", get_province_by_index(index)->to_string(), " and ", new_province.to_string());
@@ -79,7 +85,7 @@ return_t Map::add_region(std::string const& identifier, std::vector<std::string>
Logger::error("Invalid region identifier - empty!");
return FAILURE;
}
- Region new_region{ identifier };
+ Region new_region { identifier };
return_t ret = SUCCESS;
for (std::string const& province_identifier : province_identifiers) {
Province* province = get_province_by_identifier(province_identifier);
@@ -176,7 +182,8 @@ Region const* Map::get_region_by_identifier(std::string const& identifier) const
}
static colour_t colour_at(uint8_t const* colour_data, int32_t idx) {
- return (colour_data[idx * 3] << 16) | (colour_data[idx * 3 + 1] << 8) | colour_data[idx * 3 + 2];
+ idx *= 3;
+ return (colour_data[idx] << 16) | (colour_data[idx + 1] << 8) | colour_data[idx + 2];
}
return_t Map::generate_province_shape_image(size_t new_width, size_t new_height, uint8_t const* colour_data,
@@ -309,10 +316,17 @@ return_t Map::generate_mapmode_colours(Mapmode::index_t index, uint8_t* target)
Logger::error("Mapmode colour target pointer is null!");
return FAILURE;
}
+ return_t ret = SUCCESS;
Mapmode const* mapmode = mapmodes.get_item_by_index(index);
if (mapmode == nullptr) {
- Logger::error("Invalid mapmode index: ", index);
- return FAILURE;
+ // Not an error if mapmodes haven't yet been loaded,
+ // e.g. if we want to allocate the province colour
+ // texture before mapmodes are loaded.
+ if (!(mapmodes.get_item_count() == 0 && index == 0)) {
+ Logger::error("Invalid mapmode index: ", index);
+ ret = FAILURE;
+ }
+ mapmode = &Mapmode::ERROR_MAPMODE;
}
// Skip past Province::NULL_INDEX
for (size_t i = 0; i < MAPMODE_COLOUR_SIZE; ++i)
@@ -324,13 +338,14 @@ return_t Map::generate_mapmode_colours(Mapmode::index_t index, uint8_t* target)
*target++ = colour & FULL_COLOUR;
*target++ = (colour >> 24) & FULL_COLOUR;
}
- return SUCCESS;
+ return ret;
}
return_t Map::setup(GoodManager const& good_manager, BuildingManager const& building_manager) {
return_t ret = SUCCESS;
for (Province& province : provinces.get_items()) {
- if (!province.is_water()) // Set all land provinces to have an RGO based on their index to test them
+ // Set all land provinces to have an RGO based on their index to test them
+ if (!province.is_water() && good_manager.get_good_count() > 0)
province.rgo = good_manager.get_good_by_index(province.get_index() % good_manager.get_good_count());
if (building_manager.generate_province_buildings(province) != SUCCESS) ret = FAILURE;
}
diff --git a/src/openvic/map/Map.hpp b/src/openvic/map/Map.hpp
index 39c37c0..3533a14 100644
--- a/src/openvic/map/Map.hpp
+++ b/src/openvic/map/Map.hpp
@@ -9,14 +9,18 @@ namespace OpenVic {
struct Mapmode : HasIdentifier {
friend struct Map;
- using colour_func_t = std::function<colour_t (Map const&, Province const&)>;
+ using colour_func_t = std::function<colour_t(Map const&, Province const&)>;
using index_t = size_t;
+
private:
const index_t index;
const colour_func_t colour_func;
Mapmode(index_t new_index, std::string const& new_identifier, colour_func_t new_colour_func);
+
public:
+ static const Mapmode ERROR_MAPMODE;
+
index_t get_index() const;
colour_t get_colour(Map const& map, Province const& province) const;
};
@@ -30,12 +34,12 @@ namespace OpenVic {
using terrain_t = uint8_t;
using terrain_variant_map_t = std::map<colour_t, terrain_t>;
- #pragma pack(push, 1)
+#pragma pack(push, 1)
struct shape_pixel_t {
index_t index;
terrain_t terrain;
};
- #pragma pack(pop)
+#pragma pack(pop)
private:
using colour_index_map_t = std::map<colour_t, index_t>;
@@ -51,6 +55,7 @@ namespace OpenVic {
index_t selected_province = NULL_INDEX;
index_t get_index_from_colour(colour_t colour) const;
+
public:
Map();
diff --git a/src/openvic/map/Province.cpp b/src/openvic/map/Province.cpp
index b169021..d2a5ecf 100644
--- a/src/openvic/map/Province.cpp
+++ b/src/openvic/map/Province.cpp
@@ -1,13 +1,16 @@
#include "Province.hpp"
#include <cassert>
-#include <sstream>
#include <iomanip>
+#include <sstream>
using namespace OpenVic;
-Province::Province(index_t new_index, std::string const& new_identifier, colour_t new_colour) :
- HasIdentifier{ new_identifier }, HasColour{ new_colour }, index{ new_index }, buildings{ "buildings" } {
+Province::Province(index_t new_index, std::string const& new_identifier, colour_t new_colour)
+ : HasIdentifier { new_identifier },
+ HasColour { new_colour },
+ index { new_index },
+ buildings { "buildings" } {
assert(index != NULL_INDEX);
}
@@ -66,7 +69,6 @@ std::string Province::to_string() const {
void Province::update_state(Date const& today) {
for (Building& building : buildings.get_items())
building.update_state(today);
-
}
void Province::tick(Date const& today) {
diff --git a/src/openvic/map/Province.hpp b/src/openvic/map/Province.hpp
index cc11046..f1f87a2 100644
--- a/src/openvic/map/Province.hpp
+++ b/src/openvic/map/Province.hpp
@@ -25,6 +25,7 @@ namespace OpenVic {
Good const* rgo = nullptr;
Province(index_t new_index, std::string const& new_identifier, colour_t new_colour);
+
public:
Province(Province&&) = default;
diff --git a/src/openvic/map/Region.cpp b/src/openvic/map/Region.cpp
index d546ff9..b83f556 100644
--- a/src/openvic/map/Region.cpp
+++ b/src/openvic/map/Region.cpp
@@ -16,7 +16,7 @@ std::vector<Province*> const& ProvinceSet::get_provinces() const {
return provinces;
}
-Region::Region(std::string const& new_identifier) : HasIdentifier{ new_identifier } {}
+Region::Region(std::string const& new_identifier) : HasIdentifier { new_identifier } {}
colour_t Region::get_colour() const {
if (provinces.empty()) return FULL_COLOUR << 16;
diff --git a/src/openvic/map/Region.hpp b/src/openvic/map/Region.hpp
index 953a3cc..331d883 100644
--- a/src/openvic/map/Region.hpp
+++ b/src/openvic/map/Region.hpp
@@ -7,6 +7,7 @@ namespace OpenVic {
struct ProvinceSet {
protected:
std::vector<Province*> provinces;
+
public:
size_t get_province_count() const;
bool contains_province(Province const* province) const;
@@ -18,8 +19,10 @@ namespace OpenVic {
*/
struct Region : HasIdentifier, ProvinceSet {
friend struct Map;
+
private:
Region(std::string const& new_identifier);
+
public:
Region(Region&&) = default;
diff --git a/src/openvic/utility/BMP.cpp b/src/openvic/utility/BMP.cpp
new file mode 100644
index 0000000..eb3e2b1
--- /dev/null
+++ b/src/openvic/utility/BMP.cpp
@@ -0,0 +1,162 @@
+#include "BMP.hpp"
+
+#include <set>
+
+#include "Logger.hpp"
+
+using namespace OpenVic;
+
+BMP::~BMP() {
+ close();
+}
+
+return_t BMP::open(char const* filepath) {
+ reset();
+ errno = 0;
+ file = fopen(filepath, "rb");
+ if (file == nullptr || errno != 0) {
+ Logger::error("Failed to open BMP file \"", filepath, "\" (errno = ", errno, ")");
+ file = nullptr;
+ return FAILURE;
+ }
+ return SUCCESS;
+}
+
+return_t BMP::read_header() {
+ if (header_validated) {
+ Logger::error("BMP header already validated!");
+ return FAILURE;
+ }
+ if (file == nullptr) {
+ Logger::error("Cannot read BMP header before opening a file");
+ return FAILURE;
+ }
+ if (fseek(file, 0, SEEK_SET) != 0) {
+ Logger::error("Failed to move to the beginning of the BMP file!");
+ return FAILURE;
+ }
+ if (fread(&header, sizeof(header), 1, file) != 1) {
+ Logger::error("Failed to read BMP header!");
+ return FAILURE;
+ }
+ return_t ret = SUCCESS;
+
+ // Validate constants
+ static constexpr uint16_t BMP_SIGNATURE = 0x4d42;
+ if (header.signature != BMP_SIGNATURE) {
+ Logger::error("Invalid BMP signature: ", header.signature, " (must be ", BMP_SIGNATURE, ")");
+ ret = FAILURE;
+ }
+ static constexpr uint32_t DIB_HEADER_SIZE = 40;
+ if (header.dib_header_size != DIB_HEADER_SIZE) {
+ Logger::error("Invalid BMP DIB header size: ", header.dib_header_size, " (must be ", DIB_HEADER_SIZE, ")");
+ ret = FAILURE;
+ }
+ static constexpr uint16_t NUM_PLANES = 1;
+ if (header.num_planes != NUM_PLANES) {
+ Logger::error("Invalid BMP plane count: ", header.num_planes, " (must be ", NUM_PLANES, ")");
+ ret = FAILURE;
+ }
+ static constexpr uint16_t COMPRESSION = 0; // Only support uncompressed BMPs
+ if (header.compression != COMPRESSION) {
+ Logger::error("Invalid BMP compression method: ", header.compression, " (must be ", COMPRESSION, ")");
+ ret = FAILURE;
+ }
+
+ // Validate sizes and dimensions
+ // TODO - image_size_bytes can be 0 for non-compressed BMPs
+ if (header.file_size != header.offset + header.image_size_bytes) {
+ Logger::error("Invalid BMP memory sizes: file size = ", header.file_size, " != ", header.offset + header.image_size_bytes,
+ " = ", header.offset, " + ", header.image_size_bytes, " = image data offset + image data size");
+ ret = FAILURE;
+ }
+ // TODO - support negative widths (i.e. horizontal flip)
+ if (header.width_px <= 0) {
+ Logger::error("Invalid BMP width: ", header.width_px, " (must be positive)");
+ ret = FAILURE;
+ }
+ // TODO - support negative heights (i.e. vertical flip)
+ if (header.height_px <= 0) {
+ Logger::error("Invalid BMP height: ", header.height_px, " (must be positive)");
+ ret = FAILURE;
+ }
+ // TODO - validate x_resolution_ppm
+ // TODO - validate y_resolution_ppm
+
+ // Validate colours
+#define VALID_BITS_PER_PIXEL 1, 2, 4, 8, 16, 24, 32
+#define STR(x) #x
+ static const std::set<uint16_t> BITS_PER_PIXEL { VALID_BITS_PER_PIXEL };
+ if (!BITS_PER_PIXEL.contains(header.bits_per_pixel)) {
+ Logger::error("Invalid BMP bits per pixel: ", header.bits_per_pixel, " (must be one of " STR(VALID_BITS_PER_PIXEL) ")");
+ ret = FAILURE;
+ }
+#undef VALID_BITS_PER_PIXEL
+#undef STR
+ static constexpr uint16_t PALETTE_BITS_PER_PIXEL_LIMIT = 8;
+ if (header.num_colours != 0 && header.bits_per_pixel > PALETTE_BITS_PER_PIXEL_LIMIT) {
+ Logger::error("Invalid BMP palette size: ", header.num_colours, " (should be 0 as bits per pixel is ", header.bits_per_pixel, " > 8)");
+ ret = FAILURE;
+ }
+ // TODO - validate important_colours
+
+ palette_size = header.bits_per_pixel > PALETTE_BITS_PER_PIXEL_LIMIT ? 0
+ // Use header.num_colours if it's greater than 0 and at most 1 << header.bits_per_pixel
+ : 0 < header.num_colours && header.num_colours - 1 >> header.bits_per_pixel == 0 ? header.num_colours
+ : 1 << header.bits_per_pixel;
+
+ const uint32_t expected_offset = palette_size * PALETTE_COLOUR_SIZE + sizeof(header);
+ if (header.offset != expected_offset) {
+ Logger::error("Invalid BMP image data offset: ", header.offset, " (should be ", expected_offset, ")");
+ ret = FAILURE;
+ }
+
+ header_validated = ret == SUCCESS;
+ return ret;
+}
+
+return_t BMP::read_palette() {
+ if (file == nullptr) {
+ Logger::error("Cannot read BMP palette before opening a file");
+ return FAILURE;
+ }
+ if (!header_validated) {
+ Logger::error("Cannot read palette before BMP header is validated!");
+ return FAILURE;
+ }
+ if (palette_size == 0) {
+ Logger::error("Cannot read BMP palette - header indicates this file doesn't have one");
+ return FAILURE;
+ }
+ if (fseek(file, sizeof(header), SEEK_SET) != 0) {
+ Logger::error("Failed to move to the palette in the BMP file!");
+ return FAILURE;
+ }
+ palette.resize(palette_size);
+ if (fread(palette.data(), palette_size * PALETTE_COLOUR_SIZE, 1, file) != 1) {
+ Logger::error("Failed to read BMP header!");
+ palette.clear();
+ return FAILURE;
+ }
+ return SUCCESS;
+}
+
+void BMP::close() {
+ if (file != nullptr) {
+ if (fclose(file) != 0)
+ Logger::error("Failed to close BMP!");
+ file = nullptr;
+ }
+}
+
+void BMP::reset() {
+ close();
+ memset(&header, 0, sizeof(header));
+ header_validated = false;
+ palette_size = 0;
+ palette.clear();
+}
+
+std::vector<colour_t> const& BMP::get_palette() const {
+ return palette;
+}
diff --git a/src/openvic/utility/BMP.hpp b/src/openvic/utility/BMP.hpp
new file mode 100644
index 0000000..c6f5815
--- /dev/null
+++ b/src/openvic/utility/BMP.hpp
@@ -0,0 +1,48 @@
+#pragma once
+
+#include "../Types.hpp"
+
+namespace OpenVic {
+ class BMP {
+#pragma pack(push)
+#pragma pack(1)
+ struct header_t {
+ uint16_t signature; // Signature: 0x4d42 (or 'B' 'M')
+ uint32_t file_size; // File size in bytes
+ uint16_t reserved1; // Not used
+ uint16_t reserved2; // Not used
+ uint32_t offset; // Offset to image data in bytes from beginning of file
+ uint32_t dib_header_size; // DIB header size in bytes
+ int32_t width_px; // Width of the image
+ int32_t height_px; // Height of image
+ uint16_t num_planes; // Number of colour planes
+ uint16_t bits_per_pixel; // Bits per pixel
+ uint32_t compression; // Compression type
+ uint32_t image_size_bytes; // Image size in bytes
+ int32_t x_resolution_ppm; // Pixels per meter
+ int32_t y_resolution_ppm; // Pixels per meter
+ uint32_t num_colours; // Number of colours
+ uint32_t important_colours; // Important colours
+ } header;
+#pragma pack(pop)
+
+ FILE* file = nullptr;
+ bool header_validated = false;
+ uint32_t palette_size = 0;
+ std::vector<colour_t> palette;
+
+ public:
+ static constexpr uint32_t PALETTE_COLOUR_SIZE = sizeof(colour_t);
+
+ BMP() = default;
+ ~BMP();
+
+ return_t open(char const* filepath);
+ return_t read_header();
+ return_t read_palette();
+ void close();
+ void reset();
+
+ std::vector<colour_t> const& get_palette() const;
+ };
+}
diff --git a/src/openvic/Logger.cpp b/src/openvic/utility/Logger.cpp
index f93b8f1..fe97473 100644
--- a/src/openvic/Logger.cpp
+++ b/src/openvic/utility/Logger.cpp
@@ -16,11 +16,3 @@ char const* Logger::get_filename(char const* filepath) {
}
return last_slash;
}
-
-void Logger::set_info_func(log_func_t log_func) {
- info_func = log_func;
-}
-
-void Logger::set_error_func(log_func_t log_func) {
- error_func = log_func;
-}
diff --git a/src/openvic/utility/Logger.hpp b/src/openvic/utility/Logger.hpp
new file mode 100644
index 0000000..ac82445
--- /dev/null
+++ b/src/openvic/utility/Logger.hpp
@@ -0,0 +1,80 @@
+#pragma once
+
+#include <functional>
+#include <sstream>
+#ifdef __cpp_lib_source_location
+#include <source_location>
+#endif
+
+namespace OpenVic {
+
+#ifndef __cpp_lib_source_location
+#include <string>
+ // Implementation of std::source_location for compilers that do not support it
+ // Note: uses non-standard extensions that are supported by Clang, GCC, and MSVC
+ // https://clang.llvm.org/docs/LanguageExtensions.html#source-location-builtins
+ // https://stackoverflow.com/a/67970107
+ class source_location {
+ std::string _file;
+ int _line;
+ std::string _function;
+
+ public:
+ source_location(std::string f, int l, std::string n) : _file(f), _line(l), _function(n) {}
+ static source_location current(std::string f = __builtin_FILE(), int l = __builtin_LINE(), std::string n = __builtin_FUNCTION()) {
+ return source_location(f, l, n);
+ }
+
+ inline char const* file_name() const { return _file.c_str(); }
+ inline int line() const { return _line; }
+ inline char const* function_name() const { return _function.c_str(); }
+ };
+#endif
+
+ class Logger {
+ using log_func_t = std::function<void(std::string&&)>;
+
+#ifdef __cpp_lib_source_location
+ using source_location = std::source_location;
+#else
+ using source_location = OpenVic::source_location;
+#endif
+
+ static char const* get_filename(char const* filepath);
+
+ template<typename... Ts>
+ struct log {
+ log(log_func_t log_func, Ts&&... ts, source_location const& location) {
+ if (log_func) {
+ std::stringstream stream;
+ stream << "\n" << get_filename(location.file_name()) << "("
+ << location.line() << ") `" << location.function_name() << "`: ";
+ ((stream << std::forward<Ts>(ts)), ...);
+ stream << std::endl;
+ log_func(stream.str());
+ }
+ }
+ };
+
+#define LOG_FUNC(name) \
+ private: \
+ static log_func_t name##_func; \
+ public: \
+ static void set_##name##_func(log_func_t log_func) { \
+ name##_func = log_func; \
+ } \
+ template <typename... Ts> \
+ struct name { \
+ name(Ts&&... ts, source_location const& location = source_location::current()) { \
+ log<Ts...>{ name##_func, std::forward<Ts>(ts)..., location }; \
+ } \
+ }; \
+ template <typename... Ts> \
+ name(Ts&&...) -> name<Ts...>;
+
+ LOG_FUNC(info)
+ LOG_FUNC(error)
+
+#undef LOG_FUNC
+ };
+}