aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--extension/src/MapSingleton.cpp117
-rw-r--r--extension/src/MapSingleton.hpp6
-rw-r--r--extension/src/openvic2/Date.cpp181
-rw-r--r--extension/src/openvic2/Date.hpp3
-rw-r--r--extension/src/openvic2/Map.cpp132
-rw-r--r--extension/src/openvic2/Map.hpp64
-rw-r--r--extension/src/openvic2/Province.cpp19
-rw-r--r--extension/src/openvic2/Province.hpp39
-rw-r--r--extension/src/openvic2/Region.cpp24
-rw-r--r--extension/src/openvic2/Region.hpp29
-rw-r--r--extension/src/openvic2/Types.cpp13
-rw-r--r--extension/src/openvic2/Types.hpp11
-rw-r--r--game/art/terrain/farmlands.pngbin0 -> 265377 bytes
-rw-r--r--game/art/terrain/farmlands.png.import (renamed from game/art/terrain/terrain.png.import)10
-rw-r--r--game/art/terrain/terrain.pngbin27542091 -> 0 bytes
-rw-r--r--game/common/map/provinces.json216
-rw-r--r--game/common/map/provinces.pngbin9464 -> 1083451 bytes
-rw-r--r--game/common/map/regions.json15
-rw-r--r--game/common/map/water.json6
-rw-r--r--game/localisation/en_GB/mapmodes.csv1
-rw-r--r--game/localisation/en_GB/menus.csv14
-rw-r--r--game/localisation/en_GB/provinces.csv223
-rw-r--r--game/localisation/en_GB/regions.csv8
-rw-r--r--game/localisation/en_GB/regions.csv.import3
-rw-r--r--game/localisation/en_US/menus.csv7
-rw-r--r--game/localisation/fr_FR/menus.csv7
-rw-r--r--game/src/Autoload/Events.gd5
-rw-r--r--game/src/GameSession/GameSession.gd9
-rw-r--r--game/src/GameSession/GameSession.tscn32
-rw-r--r--game/src/GameSession/GameSessionMenu.gd21
-rw-r--r--game/src/GameSession/GameSessionMenu.tscn59
-rw-r--r--game/src/GameSession/MapControlPanel.gd13
-rw-r--r--game/src/GameSession/MapControlPanel.tscn68
-rw-r--r--game/src/GameSession/MapView.gd30
-rw-r--r--game/src/GameSession/MapView.tscn6
-rw-r--r--game/src/GameSession/ProvinceOverviewPanel.gd27
-rw-r--r--game/src/GameSession/ProvinceOverviewPanel.tscn13
-rw-r--r--game/src/GameSession/TerrainMap.gdshader50
38 files changed, 1146 insertions, 335 deletions
diff --git a/extension/src/MapSingleton.cpp b/extension/src/MapSingleton.cpp
index 73cf522..6f90dee 100644
--- a/extension/src/MapSingleton.cpp
+++ b/extension/src/MapSingleton.cpp
@@ -11,11 +11,13 @@ MapSingleton* MapSingleton::singleton = nullptr;
void MapSingleton::_bind_methods() {
ClassDB::bind_method(D_METHOD("load_province_identifier_file", "file_path"), &MapSingleton::load_province_identifier_file);
+ ClassDB::bind_method(D_METHOD("load_water_province_file", "file_path"), &MapSingleton::load_water_province_file);
ClassDB::bind_method(D_METHOD("load_region_file", "file_path"), &MapSingleton::load_region_file);
ClassDB::bind_method(D_METHOD("load_province_shape_file", "file_path"), &MapSingleton::load_province_shape_file);
ClassDB::bind_method(D_METHOD("get_province_index_from_uv_coords", "coords"), &MapSingleton::get_province_index_from_uv_coords);
ClassDB::bind_method(D_METHOD("get_province_identifier_from_uv_coords", "coords"), &MapSingleton::get_province_identifier_from_uv_coords);
+ ClassDB::bind_method(D_METHOD("get_region_identifier_from_province_identifier", "identifier"), &MapSingleton::get_region_identifier_from_province_identifier);
ClassDB::bind_method(D_METHOD("get_width"), &MapSingleton::get_width);
ClassDB::bind_method(D_METHOD("get_height"), &MapSingleton::get_height);
ClassDB::bind_method(D_METHOD("get_province_index_image"), &MapSingleton::get_province_index_image);
@@ -43,16 +45,19 @@ MapSingleton::MapSingleton() {
{ "mapmode_province", [](Map const&, Province const& province) -> Province::colour_t { return province.get_colour(); } },
{ "mapmode_region", [](Map const&, Province const& province) -> Province::colour_t {
Region const* region = province.get_region();
- if (region != nullptr) return region->get_provinces().front()->get_colour();
+ if (region != nullptr) return region->get_colour();
return province.get_colour();
} },
+ { "mapmode_terrain", [](Map const&, Province const& province) -> Province::colour_t {
+ return province.is_water() ? 0x4287F5 : 0x0D7017;
+ } },
{ "mapmode_index", [](Map const& map, Province const& province) -> Province::colour_t {
const uint8_t f = float(province.get_index()) / float(map.get_province_count()) * 255.0f;
return (f << 16) | (f << 8) | f;
} }
};
- std::string error_message = "";
- for (mapmode_t mapmode : mapmodes)
+ std::string error_message;
+ for (mapmode_t const& mapmode : mapmodes)
if (map.add_mapmode(mapmode.first, mapmode.second, error_message) != SUCCESS)
UtilityFunctions::push_error(error_message.c_str());
}
@@ -62,34 +67,43 @@ MapSingleton::~MapSingleton() {
singleton = nullptr;
}
-Error MapSingleton::parse_json_dictionary_file(String const& file_description, String const& file_path,
- String const& identifier_prefix, parse_json_entry_func_t parse_entry) const {
+static Error load_json_file(String const& file_description, String const& file_path, Variant& result) {
+ result.clear();
UtilityFunctions::print("Loading ", file_description, " file: ", file_path);
- Ref<FileAccess> file = FileAccess::open(file_path, FileAccess::ModeFlags::READ);
+ const Ref<FileAccess> file = FileAccess::open(file_path, FileAccess::ModeFlags::READ);
Error err = FileAccess::get_open_error();
if (err != OK || file.is_null()) {
UtilityFunctions::push_error("Failed to load ", file_description, " file: ", file_path);
return err == OK ? FAILED : err;
}
const String json_string = file->get_as_text();
- Ref<JSON> json;
- json.instantiate();
- err = json->parse(json_string);
+ JSON json;
+ err = json.parse(json_string);
if (err != OK) {
UtilityFunctions::push_error("Failed to parse ", file_description, " file as JSON: ", file_path,
- "\nError at line ", json->get_error_line(), ": ", json->get_error_message());
+ "\nError at line ", json.get_error_line(), ": ", json.get_error_message());
return err;
}
- const Variant json_var = json->get_data();
+ result = json.get_data();
+ return err;
+}
+
+using parse_json_entry_func_t = std::function<godot::Error (godot::String const&, godot::Variant const&)>;
+
+static Error parse_json_dictionary_file(String const& file_description, String const& file_path,
+ String const& identifier_prefix, parse_json_entry_func_t parse_entry) {
+ Variant json_var;
+ Error err = load_json_file(file_description, file_path, json_var);
+ if (err != OK) return err;
const Variant::Type type = json_var.get_type();
if (type != Variant::DICTIONARY) {
UtilityFunctions::push_error("Invalid ", file_description, " JSON: root has type ",
Variant::get_type_name(type), " (expected Dictionary)");
return FAILED;
}
- const Dictionary dict = json_var;
+ Dictionary const& dict = json_var;
const Array identifiers = dict.keys();
- for (int idx = 0; idx < identifiers.size(); ++idx) {
+ for (int64_t idx = 0; idx < identifiers.size(); ++idx) {
String const& identifier = identifiers[idx];
Variant const& entry = dict[identifier];
if (identifier.is_empty()) {
@@ -108,20 +122,20 @@ Error MapSingleton::_parse_province_identifier_entry(String const& identifier, V
const Variant::Type type = entry.get_type();
Province::colour_t colour = Province::NULL_COLOUR;
if (type == Variant::ARRAY) {
- const Array colour_array = entry;
+ Array const& colour_array = entry;
if (colour_array.size() == 3) {
for (int jdx = 0; jdx < 3; ++jdx) {
- const Variant var = colour_array[jdx];
+ Variant const& var = colour_array[jdx];
if (var.get_type() != Variant::FLOAT) {
colour = Province::NULL_COLOUR;
break;
}
- double colour_double = var;
+ const double colour_double = var;
if (std::trunc(colour_double) != colour_double) {
colour = Province::NULL_COLOUR;
break;
}
- int64_t colour_int = static_cast<int64_t>(colour_double);
+ const int64_t colour_int = static_cast<int64_t>(colour_double);
if (colour_int < 0 || colour_int > 255) {
colour = Province::NULL_COLOUR;
break;
@@ -130,17 +144,18 @@ Error MapSingleton::_parse_province_identifier_entry(String const& identifier, V
}
}
} else if (type == Variant::STRING) {
- String colour_string = entry;
+ String const& colour_string = entry;
if (colour_string.is_valid_hex_number()) {
- int64_t colour_int = colour_string.hex_to_int();
+ const int64_t colour_int = colour_string.hex_to_int();
if (0 <= colour_int && colour_int <= 0xFFFFFF)
colour = colour_int;
}
- } else {
+ }
+ if (colour == Province::NULL_COLOUR) {
UtilityFunctions::push_error("Invalid colour for province identifier \"", identifier, "\": ", entry);
return FAILED;
}
- std::string error_message = "";
+ std::string error_message;
if (map.add_province(identifier.utf8().get_data(), colour, error_message) != SUCCESS) {
UtilityFunctions::push_error(error_message.c_str());
return FAILED;
@@ -162,12 +177,12 @@ Error MapSingleton::_parse_region_entry(String const& identifier, Variant const&
Variant::Type type = entry.get_type();
std::vector<std::string> province_identifiers;
if (type == Variant::ARRAY) {
- const Array province_array = entry;
+ Array const& province_array = entry;
for (int64_t idx = 0; idx < province_array.size(); ++idx) {
- const Variant province_var = province_array[idx];
+ Variant const& province_var = province_array[idx];
type = province_var.get_type();
if (type == Variant::STRING) {
- String province_string = province_var;
+ String const& province_string = province_var;
province_identifiers.push_back(province_string.utf8().get_data());
} else {
UtilityFunctions::push_error("Invalid province identifier for region \"", identifier, "\": ", entry);
@@ -175,7 +190,11 @@ Error MapSingleton::_parse_region_entry(String const& identifier, Variant const&
}
}
}
- std::string error_message = "";
+ if (province_identifiers.empty()) {
+ UtilityFunctions::push_error("Invalid province list for region \"", identifier, "\": ", entry);
+ return FAILED;
+ }
+ std::string error_message;
if (map.add_region(identifier.utf8().get_data(), province_identifiers, error_message) != SUCCESS) {
UtilityFunctions::push_error(error_message.c_str());
return FAILED;
@@ -218,7 +237,7 @@ Error MapSingleton::load_province_shape_file(String const& file_path) {
}
if (err != OK) return err;
- std::string error_message = "";
+ std::string error_message;
if (map.generate_province_index_image(width, height, province_shape_image->get_data().ptr(), error_message) != SUCCESS) {
UtilityFunctions::push_error(error_message.c_str());
err = FAILED;
@@ -239,6 +258,37 @@ Error MapSingleton::load_province_shape_file(String const& file_path) {
return err;
}
+Error MapSingleton::load_water_province_file(String const& file_path) {
+ Variant json_var;
+ Error err = load_json_file("water province", file_path, json_var);
+ if (err != OK) return err;
+ Variant::Type type = json_var.get_type();
+ if (type != Variant::ARRAY) {
+ UtilityFunctions::push_error("Invalid water province JSON: root has type ",
+ Variant::get_type_name(type), " (expected Array)");
+ err = FAILED;
+ } else {
+ Array const& array = json_var;
+ std::string error_message;
+ for (int64_t idx = 0; idx < array.size(); ++idx) {
+ Variant const& entry = array[idx];
+ type = entry.get_type();
+ if (type != Variant::STRING) {
+ UtilityFunctions::push_error("Invalid water province identifier: ", entry);
+ err = FAILED;
+ continue;
+ }
+ String const& identifier = entry;
+ if (map.set_water_province(identifier.utf8().get_data(), error_message) != SUCCESS) {
+ UtilityFunctions::push_error(error_message.c_str());
+ err = FAILED;
+ }
+ }
+ }
+ map.lock_water_provinces();
+ return err;
+}
+
Province* MapSingleton::get_province_from_uv_coords(godot::Vector2 const& coords) {
if (province_index_image.is_valid()) {
const PackedByteArray index_data_array = province_index_image->get_data();
@@ -273,6 +323,15 @@ String MapSingleton::get_province_identifier_from_uv_coords(Vector2 const& coord
return String{};
}
+String MapSingleton::get_region_identifier_from_province_identifier(String const& identifier) const {
+ Province const* province = map.get_province_by_identifier(identifier.utf8().get_data());
+ if (province != nullptr) {
+ Region const* region = province->get_region();
+ if (region != nullptr) return region->get_identifier().c_str();
+ }
+ return String{};
+}
+
int32_t MapSingleton::get_width() const {
return map.get_width();
}
@@ -291,11 +350,11 @@ Ref<Image> MapSingleton::get_province_colour_image() const {
Error MapSingleton::update_colour_image() {
static PackedByteArray colour_data_array;
- static const int64_t colour_data_array_size = (Province::MAX_INDEX + 1) * 3;
+ static const int64_t colour_data_array_size = (Province::MAX_INDEX + 1) * 4;
colour_data_array.resize(colour_data_array_size);
Error err = OK;
- std::string error_message = "";
+ std::string error_message;
if (map.generate_mapmode_colours(mapmode_index, colour_data_array.ptrw(), error_message) != SUCCESS) {
UtilityFunctions::push_error(error_message.c_str());
err = FAILED;
@@ -305,7 +364,7 @@ Error MapSingleton::update_colour_image() {
if (province_colour_image.is_null())
province_colour_image.instantiate();
province_colour_image->set_data(PROVINCE_INDEX_SQRT, PROVINCE_INDEX_SQRT,
- false, Image::FORMAT_RGB8, colour_data_array);
+ false, Image::FORMAT_RGBA8, colour_data_array);
if (province_colour_image.is_null()) {
UtilityFunctions::push_error("Failed to update province colour image");
return FAILED;
diff --git a/extension/src/MapSingleton.hpp b/extension/src/MapSingleton.hpp
index 9205e92..6ec2ea4 100644
--- a/extension/src/MapSingleton.hpp
+++ b/extension/src/MapSingleton.hpp
@@ -8,8 +8,6 @@
namespace OpenVic2 {
class MapSingleton : public godot::Object {
- using parse_json_entry_func_t = std::function<godot::Error (godot::String const&, godot::Variant const&)>;
-
GDCLASS(MapSingleton, godot::Object)
static MapSingleton* singleton;
@@ -18,8 +16,6 @@ namespace OpenVic2 {
Map map;
Mapmode::index_t mapmode_index = 0;
- godot::Error parse_json_dictionary_file(godot::String const& file_description, godot::String const& file_path,
- godot::String const& identifier_prefix, parse_json_entry_func_t parse_entry) const;
godot::Error _parse_province_identifier_entry(godot::String const& identifier, godot::Variant const& entry);
godot::Error _parse_region_entry(godot::String const& identifier, godot::Variant const& entry);
protected:
@@ -32,6 +28,7 @@ namespace OpenVic2 {
~MapSingleton();
godot::Error load_province_identifier_file(godot::String const& file_path);
+ godot::Error load_water_province_file(godot::String const& file_path);
godot::Error load_region_file(godot::String const& file_path);
godot::Error load_province_shape_file(godot::String const& file_path);
@@ -39,6 +36,7 @@ namespace OpenVic2 {
Province const* get_province_from_uv_coords(godot::Vector2 const& coords) const;
int32_t get_province_index_from_uv_coords(godot::Vector2 const& coords) const;
godot::String get_province_identifier_from_uv_coords(godot::Vector2 const& coords) const;
+ godot::String get_region_identifier_from_province_identifier(godot::String const& identifier) const;
int32_t get_width() const;
int32_t get_height() const;
godot::Ref<godot::Image> get_province_index_image() const;
diff --git a/extension/src/openvic2/Date.cpp b/extension/src/openvic2/Date.cpp
index 3c5b8d8..e1cef72 100644
--- a/extension/src/openvic2/Date.cpp
+++ b/extension/src/openvic2/Date.cpp
@@ -1,108 +1,115 @@
#include <sstream>
#include "Date.hpp"
-namespace OpenVic2 {
- bool Timespan::operator< (Timespan const& other) const { return days < other.days; }
- bool Timespan::operator> (Timespan const& other) const { return days > other.days; }
- bool Timespan::operator<= (Timespan const& other) const { return days <= other.days; }
- bool Timespan::operator>= (Timespan const& other) const { return days >= other.days; }
- bool Timespan::operator== (Timespan const& other) const { return days == other.days; }
- bool Timespan::operator!= (Timespan const& other) const { return days != other.days; }
-
- Timespan Timespan::operator+ (Timespan const& other) const { return Timespan(days + other.days); }
- Timespan Timespan::operator- (Timespan const& other) const { return Timespan(days - other.days); }
- Timespan Timespan::operator* (int64_t const& factor) const { return Timespan(days * factor); }
- Timespan Timespan::operator/ (int64_t const& factor) const { return Timespan(days / factor); }
-
- Timespan& Timespan::operator+= (Timespan const& other) {
- days += other.days;
- return *this;
- }
- Timespan& Timespan::operator-= (Timespan const& other) {
- days -= other.days;
- return *this;
- }
+using namespace OpenVic2;
+
+bool Timespan::operator< (Timespan const& other) const { return days < other.days; }
+bool Timespan::operator> (Timespan const& other) const { return days > other.days; }
+bool Timespan::operator<= (Timespan const& other) const { return days <= other.days; }
+bool Timespan::operator>= (Timespan const& other) const { return days >= other.days; }
+bool Timespan::operator== (Timespan const& other) const { return days == other.days; }
+bool Timespan::operator!= (Timespan const& other) const { return days != other.days; }
+
+Timespan Timespan::operator+ (Timespan const& other) const { return Timespan(days + other.days); }
+Timespan Timespan::operator- (Timespan const& other) const { return Timespan(days - other.days); }
+Timespan Timespan::operator* (int64_t const& factor) const { return Timespan(days * factor); }
+Timespan Timespan::operator/ (int64_t const& factor) const { return Timespan(days / factor); }
+
+Timespan& Timespan::operator+= (Timespan const& other) {
+ days += other.days;
+ return *this;
+}
+Timespan& Timespan::operator-= (Timespan const& other) {
+ days -= other.days;
+ return *this;
+}
- Timespan fromYearZero(year_t year, month_t month, date_t day) {
- int64_t daysElapsed = year * DAYS_IN_YEAR;
- size_t daysSinceMonthStart = (day == 0) ? 0 : day - 1; //Underflow protection
- for (size_t x = 0; x < month && x < MONTHS_IN_YEAR; x++) {
- daysElapsed += DAYS_IN_MONTH[x];
- }
- daysElapsed += daysSinceMonthStart;
- return Timespan(daysElapsed);
- }
+Timespan::operator std::string() const {
+ return std::to_string(days);
+}
+std::ostream &operator<<(std::ostream &out, Timespan const& timespan) {
+ return out << static_cast<std::string>(timespan);
+}
- //This function is not set up to handle dates before Year 0
- YearMonthDayBundle toGregorianDate(Timespan const& timespan) {
- year_t year = 0;
- month_t month = 0;
- date_t day = 0;
+Timespan fromYearZero(year_t year, month_t month, date_t day) {
+ int64_t daysElapsed = year * DAYS_IN_YEAR;
+ size_t daysSinceMonthStart = (day == 0) ? 0 : day - 1; //Underflow protection
+ for (size_t x = 0; x < month && x < MONTHS_IN_YEAR; x++) {
+ daysElapsed += DAYS_IN_MONTH[x];
+ }
+ daysElapsed += daysSinceMonthStart;
+ return Timespan(daysElapsed);
+}
- if (timespan >= 0) {
- year = timespan.days / DAYS_IN_YEAR;
- int64_t remainingDays = timespan.days % DAYS_IN_YEAR;
+//This function is not set up to handle dates before Year 0
+YearMonthDayBundle toGregorianDate(Timespan const& timespan) {
+ year_t year = 0;
+ month_t month = 0;
+ date_t day = 0;
- for (size_t x = 0; x < MONTHS_IN_YEAR && remainingDays >= DAYS_IN_MONTH[x]; x++) {
- remainingDays -= DAYS_IN_MONTH[x];
- month++;
- }
+ if (timespan >= 0) {
+ year = timespan.days / DAYS_IN_YEAR;
+ int64_t remainingDays = timespan.days % DAYS_IN_YEAR;
- //Corrects month and day to be 1-indexed
+ for (size_t x = 0; x < MONTHS_IN_YEAR && remainingDays >= DAYS_IN_MONTH[x]; x++) {
+ remainingDays -= DAYS_IN_MONTH[x];
month++;
- day++;
}
- return std::make_tuple(year, month, day);
+
+ //Corrects month and day to be 1-indexed
+ month++;
+ day++;
}
+ return std::make_tuple(year, month, day);
+}
- Date::Date(Timespan const& timespan) : ts(timespan) { updateDate(ts); }
+Date::Date(Timespan const& timespan) : ts(timespan) { updateDate(ts); }
- Date::Date(year_t year, month_t month, date_t day) {
- ts = fromYearZero(year, month, day);
- updateDate(ts);
- }
+Date::Date(year_t year, month_t month, date_t day) {
+ ts = fromYearZero(year, month, day);
+ updateDate(ts);
+}
- void Date::updateDate(Timespan const& timespan) {
- gregorianDate = toGregorianDate(timespan);
- }
+void Date::updateDate(Timespan const& timespan) {
+ gregorianDate = toGregorianDate(timespan);
+}
- size_t Date::getDay() const { return std::get<2>(gregorianDate); }
- size_t Date::getMonth() const { return std::get<1>(gregorianDate); }
- size_t Date::getYear() const { return std::get<0>(gregorianDate); }
+size_t Date::getDay() const { return std::get<2>(gregorianDate); }
+size_t Date::getMonth() const { return std::get<1>(gregorianDate); }
+size_t Date::getYear() const { return std::get<0>(gregorianDate); }
- bool Date::operator< (Date const& other) const { return ts < other.ts; }
- bool Date::operator> (Date const& other) const { return ts > other.ts; }
- bool Date::operator<= (Date const& other) const { return ts <= other.ts; }
- bool Date::operator>= (Date const& other) const { return ts >= other.ts; }
- bool Date::operator== (Date const& other) const { return ts == other.ts; }
- bool Date::operator!= (Date const& other) const { return ts != other.ts; }
+bool Date::operator< (Date const& other) const { return ts < other.ts; }
+bool Date::operator> (Date const& other) const { return ts > other.ts; }
+bool Date::operator<= (Date const& other) const { return ts <= other.ts; }
+bool Date::operator>= (Date const& other) const { return ts >= other.ts; }
+bool Date::operator== (Date const& other) const { return ts == other.ts; }
+bool Date::operator!= (Date const& other) const { return ts != other.ts; }
- Date Date::operator+ (Timespan timespan) const { return Date(ts + timespan); }
- Timespan Date::operator- (Date const& other) const { return ts - other.ts; }
+Date Date::operator+ (Timespan timespan) const { return Date(ts + timespan); }
+Timespan Date::operator- (Date const& other) const { return ts - other.ts; }
- Date& Date::operator+= (Timespan const& timespan) {
- ts += timespan;
- updateDate(ts);
- return *this;
- }
- Date& Date::operator-= (Timespan const& timespan) {
- ts -= timespan;
- updateDate(ts);
- return *this;
- }
- Date Date::operator++ (int) {
- Date oldCopy = *this;
- (*this) += 1;
- return oldCopy;
- }
+Date& Date::operator+= (Timespan const& timespan) {
+ ts += timespan;
+ updateDate(ts);
+ return *this;
+}
+Date& Date::operator-= (Timespan const& timespan) {
+ ts -= timespan;
+ updateDate(ts);
+ return *this;
+}
+Date Date::operator++ (int) {
+ Date oldCopy = *this;
+ (*this) += 1;
+ return oldCopy;
+}
- Date::operator std::string() const {
- std::stringstream ss;
- ss << getYear() << '.' << getMonth() << '.' << getDay();
- return ss.str();
- }
- std::ostream &operator<<(std::ostream &out, Date const& date) {
- return out << static_cast<std::string>(date);
- }
+Date::operator std::string() const {
+ std::stringstream ss;
+ ss << getYear() << '.' << getMonth() << '.' << getDay();
+ return ss.str();
+}
+std::ostream &operator<<(std::ostream &out, Date const& date) {
+ return out << static_cast<std::string>(date);
}
diff --git a/extension/src/openvic2/Date.hpp b/extension/src/openvic2/Date.hpp
index 841b118..9ed5963 100644
--- a/extension/src/openvic2/Date.hpp
+++ b/extension/src/openvic2/Date.hpp
@@ -28,6 +28,9 @@ namespace OpenVic2 {
Timespan& operator+= (Timespan const& other);
Timespan& operator-= (Timespan const& other);
+
+ explicit operator std::string() const;
+ friend std::ostream& operator<< (std::ostream& out, Timespan const& timespan);
};
static constexpr size_t MONTHS_IN_YEAR = 12;
diff --git a/extension/src/openvic2/Map.cpp b/extension/src/openvic2/Map.cpp
index 1654189..0ac8091 100644
--- a/extension/src/openvic2/Map.cpp
+++ b/extension/src/openvic2/Map.cpp
@@ -1,14 +1,14 @@
#include "Map.hpp"
#include <cassert>
+#include <unordered_set>
using namespace OpenVic2;
static const std::string SEPARATOR = "\n - ";
Mapmode::Mapmode(index_t new_index, std::string const& new_identifier, colour_func_t new_colour_func)
- : index(new_index), identifier(new_identifier), colour_func(new_colour_func) {
- assert(!identifier.empty());
+ : HasIdentifier(new_identifier), index(new_index), colour_func(new_colour_func) {
assert(colour_func != nullptr);
}
@@ -16,10 +16,6 @@ Mapmode::index_t Mapmode::get_index() const {
return index;
}
-std::string const& Mapmode::get_identifier() const {
- return identifier;
-}
-
Mapmode::colour_func_t Mapmode::get_colour_func() const {
return colour_func;
}
@@ -42,15 +38,15 @@ return_t Map::add_province(std::string const& identifier, Province::colour_t col
return FAILURE;
}
Province new_province{ static_cast<Province::index_t>(provinces.size() + 1), identifier, colour };
- for (Province const& province : provinces) {
- if (province.get_identifier() == identifier) {
- error_message = "Duplicate province identifiers: " + province.to_string() + " and " + new_province.to_string();
- return FAILURE;
- }
- if (province.get_colour() == colour) {
- error_message = "Duplicate province colours: " + province.to_string() + " and " + new_province.to_string();
- return FAILURE;
- }
+ Province const* old_province = get_province_by_identifier(identifier);
+ if (old_province != nullptr) {
+ error_message = "Duplicate province identifiers: " + old_province->to_string() + " and " + new_province.to_string();
+ return FAILURE;
+ }
+ old_province = get_province_by_colour(colour);
+ if (old_province != nullptr) {
+ error_message = "Duplicate province colours: " + old_province->to_string() + " and " + new_province.to_string();
+ return FAILURE;
}
provinces.push_back(new_province);
error_message = "Added province: " + new_province.to_string();
@@ -61,6 +57,29 @@ void Map::lock_provinces() {
provinces_locked = true;
}
+return_t Map::set_water_province(std::string const& identifier, std::string& error_message) {
+ if (water_provinces_locked) {
+ error_message = "The map's water provinces have already been locked!";
+ return FAILURE;
+ }
+ Province* province = get_province_by_identifier(identifier);
+ if (province == nullptr) {
+ error_message = "Unrecognised water province identifier: " + identifier;
+ return FAILURE;
+ }
+ if (province->is_water()) {
+ error_message = "Province " + identifier + " is already a water province!";
+ return FAILURE;
+ }
+ province->water = true;
+ error_message = "Set province " + identifier + " as a water province";
+ return SUCCESS;
+}
+
+void Map::lock_water_provinces() {
+ water_provinces_locked = true;
+}
+
return_t Map::add_region(std::string const& identifier, std::vector<std::string> const& province_identifiers, std::string& error_message) {
if (regions_locked) {
error_message = "The map's region list has already been locked!";
@@ -75,7 +94,7 @@ return_t Map::add_region(std::string const& identifier, std::vector<std::string>
return FAILURE;
}
Region new_region{ identifier };
- error_message = "Error message for region: " + identifier;
+ error_message.clear();
for (std::string const& province_identifier : province_identifiers) {
Province* province = get_province_by_identifier(province_identifier);
if (province != nullptr) {
@@ -90,28 +109,36 @@ return_t Map::add_region(std::string const& identifier, std::vector<std::string>
error_message += regions[other_region_index].get_identifier();
else
error_message += "an unknown region with index " + std::to_string(other_region_index);
- } else new_region.provinces.push_back(province);
+ } else new_region.provinces.insert(province);
}
} else error_message += SEPARATOR + "Invalid province identifier " + province_identifier;
}
+ return_t ret = SUCCESS;
if (!new_region.get_province_count()) {
error_message += SEPARATOR + "No valid provinces in region's list";
- return FAILURE;
+ ret = FAILURE;
}
- for (Region const& region : regions) {
- if (region.get_identifier() == identifier) {
- error_message += SEPARATOR + "Duplicate region identifiers: " + region.get_identifier() + " and " + identifier;
- return FAILURE;
+ Region const* old_region = get_region_by_identifier(identifier);
+ if (old_region != nullptr) {
+ error_message += SEPARATOR + "Duplicate region identifiers: " + old_region->get_identifier() + " and " + identifier;
+ ret = FAILURE;
+ }
+
+ if (ret == SUCCESS) {
+ regions.push_back(new_region);
+ // Used to detect provinces listed in multiple regions, will
+ // be corrected once regions is stable (i.e. lock_regions).
+ Region* tmp_region_index = reinterpret_cast<Region*>(regions.size());
+ for (Province* province : new_region.get_provinces())
+ province->region = tmp_region_index;
+ if (!error_message.empty()) {
+ error_message += SEPARATOR;
+ ret = FAILURE;
}
+ error_message += "Added region: " + identifier;
}
- regions.push_back(new_region);
- error_message += SEPARATOR + "Added region: " + identifier;
- // Used to detect provinces listed in multiple regions, will
- // be corrected once regions is stable (i.e. lock_regions).
- Region* tmp_region_index = reinterpret_cast<Region*>(regions.size());
- for (Province* province : new_region.get_provinces())
- province->region = tmp_region_index;
- return SUCCESS;
+ if (ret != SUCCESS) error_message = "Error message for region: " + identifier + error_message;
+ return ret;
}
void Map::lock_regions() {
@@ -161,6 +188,20 @@ Province const* Map::get_province_by_colour(Province::colour_t colour) const {
return nullptr;
}
+Region* Map::get_region_by_identifier(std::string const& identifier) {
+ if (!identifier.empty())
+ for (Region& region : regions)
+ if (region.get_identifier() == identifier) return &region;
+ return nullptr;
+}
+
+Region const* Map::get_region_by_identifier(std::string const& identifier) const {
+ if (!identifier.empty())
+ for (Region const& region : regions)
+ if (region.get_identifier() == identifier) return &region;
+ return nullptr;
+}
+
static Province::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];
}
@@ -188,17 +229,13 @@ return_t Map::generate_province_index_image(size_t new_width, size_t new_height,
std::vector<bool> province_checklist(provinces.size());
return_t ret = SUCCESS;
-
- error_message = "Error message for province index image generation:";
+ std::unordered_set<Province::colour_t> unrecognised_colours;
+ error_message.clear();
for (int32_t y = 0; y < height; ++y) {
for (int32_t x = 0; x < width; ++x) {
const int32_t idx = x + y * width;
const Province::colour_t colour = colour_at(colour_data, idx);
- if (colour == Province::NULL_COLOUR) {
- province_index_image[idx] = Province::NULL_INDEX;
- continue;
- }
if (x > 0) {
const int32_t jdx = idx - 1;
if (colour_at(colour_data, jdx) == colour) {
@@ -220,8 +257,11 @@ return_t Map::generate_province_index_image(size_t new_width, size_t new_height,
province_checklist[index - 1] = true;
continue;
}
- error_message += SEPARATOR + "Unrecognised province colour " + Province::colour_to_hex_string(colour) + " at (" + std::to_string(x) + ", " + std::to_string(x) + ")";
- ret = FAILURE;
+ if (unrecognised_colours.find(colour) == unrecognised_colours.end()) {
+ unrecognised_colours.insert(colour);
+ error_message += SEPARATOR + "Unrecognised province colour " + Province::colour_to_hex_string(colour) + " at (" + std::to_string(x) + ", " + std::to_string(y) + ")";
+ ret = FAILURE;
+ }
province_index_image[idx] = Province::NULL_INDEX;
}
}
@@ -233,7 +273,8 @@ return_t Map::generate_province_index_image(size_t new_width, size_t new_height,
}
}
- error_message += SEPARATOR + "Generated province index image";
+ if (!error_message.empty()) error_message = "Error message for province index image generation:" + error_message + SEPARATOR;
+ error_message += "Generated province index image";
return ret;
}
@@ -259,11 +300,10 @@ return_t Map::add_mapmode(std::string const& identifier, Mapmode::colour_func_t
return FAILURE;
}
Mapmode new_mapmode{ mapmodes.size(), identifier, colour_func };
- for (Mapmode const& mapmode : mapmodes) {
- if (mapmode.get_identifier() == identifier) {
- error_message = "Duplicate mapmode identifiers: " + mapmode.get_identifier() + " and " + identifier;
- return FAILURE;
- }
+ Mapmode const* old_mapmode = get_mapmode_by_identifier(identifier);
+ if (old_mapmode != nullptr) {
+ error_message = "Duplicate mapmode identifiers: " + old_mapmode->get_identifier() + " and " + identifier;
+ return FAILURE;
}
mapmodes.push_back(new_mapmode);
error_message = "Added mapmode: " + identifier;
@@ -295,12 +335,14 @@ return_t Map::generate_mapmode_colours(Mapmode::index_t index, uint8_t* target,
return FAILURE;
}
Mapmode const& mapmode = mapmodes[index];
- target += 3; // Skip past Province::NULL_INDEX
+ target += 4; // Skip past Province::NULL_INDEX
for (Province const& province : provinces) {
const Province::colour_t colour = mapmode.get_colour_func()(*this, province);
*target++ = (colour >> 16) & 0xFF;
*target++ = (colour >> 8) & 0xFF;
*target++ = colour & 0xFF;
+ *target++ = province.is_water() ? 0 : 255;
}
+ error_message = "Generated province colours for mapmode " + mapmode.get_identifier();
return SUCCESS;
}
diff --git a/extension/src/openvic2/Map.hpp b/extension/src/openvic2/Map.hpp
index 42963c9..73ab8fd 100644
--- a/extension/src/openvic2/Map.hpp
+++ b/extension/src/openvic2/Map.hpp
@@ -1,75 +1,24 @@
#pragma once
-#include <string>
-#include <cstdint>
#include <vector>
#include <functional>
-#include "Types.hpp"
+#include "Region.hpp"
namespace OpenVic2 {
- struct Region;
- struct Map;
- /* REQUIREMENTS:
- * MAP-43, MAP-47
- */
- struct Province {
- friend struct Map;
-
- using colour_t = uint32_t;
- using index_t = uint16_t;
-
- static const colour_t NULL_COLOUR = 0, MAX_COLOUR = 0xFFFFFF;
- static const index_t NULL_INDEX = 0, MAX_INDEX = 0xFFFF;
- private:
- index_t index;
- std::string identifier;
- colour_t colour;
- Region* region = nullptr;
-
- Province(index_t new_index, std::string const& new_identifier, colour_t new_colour);
- public:
- static std::string colour_to_hex_string(colour_t colour);
-
- index_t get_index() const;
- std::string const& get_identifier() const;
- colour_t get_colour() const;
- Region* get_region() const;
- std::string to_string() const;
- };
-
- /* REQUIREMENTS:
- * MAP-6, MAP-44, MAP-48
- */
- struct Region {
- friend struct Map;
- private:
- std::string identifier;
- std::vector<Province*> provinces;
-
- Region(std::string const& new_identifier);
- public:
- std::string const& get_identifier() const;
- size_t get_province_count() const;
- bool contains_province(Province const* province) const;
- std::vector<Province*> const& get_provinces() const;
- };
-
- struct Mapmode {
+ struct Mapmode : HasIdentifier {
friend struct Map;
using colour_func_t = std::function<Province::colour_t (Map const&, Province const&)>;
using index_t = size_t;
private:
index_t index;
- std::string identifier;
colour_func_t colour_func;
Mapmode(index_t new_index, std::string const& new_identifier, colour_func_t new_colour_func);
public:
index_t get_index() const;
- std::string const& get_identifier() const;
colour_func_t get_colour_func() const;
};
@@ -80,7 +29,7 @@ namespace OpenVic2 {
private:
std::vector<Province> provinces;
std::vector<Region> regions;
- bool provinces_locked = false, regions_locked = false;
+ bool provinces_locked = false, water_provinces_locked = false, regions_locked = false;
size_t width = 0, height = 0;
std::vector<Province::index_t> province_index_image;
@@ -88,10 +37,12 @@ namespace OpenVic2 {
public:
return_t add_province(std::string const& identifier, Province::colour_t colour, std::string& error_message);
void lock_provinces();
+ return_t set_water_province(std::string const& identifier, std::string& error_message);
+ void lock_water_provinces();
return_t add_region(std::string const& identifier, std::vector<std::string> const& province_identifiers, std::string& error_message);
void lock_regions();
- size_t get_province_count() const;
+ size_t get_province_count() const;
Province* get_province_by_index(Province::index_t index);
Province const* get_province_by_index(Province::index_t index) const;
Province* get_province_by_identifier(std::string const& identifier);
@@ -99,6 +50,9 @@ namespace OpenVic2 {
Province* get_province_by_colour(Province::colour_t colour);
Province const* get_province_by_colour(Province::colour_t colour) const;
+ Region* get_region_by_identifier(std::string const& identifier);
+ Region const* get_region_by_identifier(std::string const& identifier) const;
+
return_t generate_province_index_image(size_t new_width, size_t new_height, uint8_t const* colour_data, std::string& error_message);
size_t get_width() const;
size_t get_height() const;
diff --git a/extension/src/openvic2/Province.cpp b/extension/src/openvic2/Province.cpp
index 49f1b0e..7d55708 100644
--- a/extension/src/openvic2/Province.cpp
+++ b/extension/src/openvic2/Province.cpp
@@ -1,4 +1,4 @@
-#include "Map.hpp"
+#include "Province.hpp"
#include <cassert>
#include <sstream>
@@ -7,9 +7,8 @@
using namespace OpenVic2;
Province::Province(index_t new_index, std::string const& new_identifier, colour_t new_colour) :
- index(new_index), identifier(new_identifier), colour(new_colour) {
+ HasIdentifier(new_identifier), index(new_index), colour(new_colour) {
assert(index != NULL_INDEX);
- assert(!identifier.empty());
assert(colour != NULL_COLOUR);
}
@@ -23,10 +22,6 @@ Province::index_t Province::get_index() const {
return index;
}
-std::string const& Province::get_identifier() const {
- return identifier;
-}
-
Province::colour_t Province::get_colour() const {
return colour;
}
@@ -35,6 +30,14 @@ Region* Province::get_region() const {
return region;
}
+bool Province::is_water() const {
+ return water;
+}
+
+Province::life_rating_t Province::get_life_rating() const {
+ return life_rating;
+}
+
std::string Province::to_string() const {
- return "(#" + std::to_string(index) + ", " + identifier + ", 0x" + colour_to_hex_string(colour) + ")";
+ return "(#" + std::to_string(index) + ", " + get_identifier() + ", 0x" + colour_to_hex_string(colour) + ")";
}
diff --git a/extension/src/openvic2/Province.hpp b/extension/src/openvic2/Province.hpp
new file mode 100644
index 0000000..deebd8c
--- /dev/null
+++ b/extension/src/openvic2/Province.hpp
@@ -0,0 +1,39 @@
+#pragma once
+
+#include "Types.hpp"
+
+namespace OpenVic2 {
+ struct Region;
+ struct Map;
+
+ /* REQUIREMENTS:
+ * MAP-5, MAP-8, MAP-43, MAP-47
+ */
+ struct Province : HasIdentifier {
+ friend struct Map;
+
+ using colour_t = uint32_t;
+ using index_t = uint16_t;
+ using life_rating_t = int8_t;
+
+ static const colour_t NULL_COLOUR = 0, MAX_COLOUR = 0xFFFFFF;
+ static const index_t NULL_INDEX = 0, MAX_INDEX = 0xFFFF;
+ private:
+ index_t index;
+ colour_t colour;
+ Region* region = nullptr;
+ bool water = false;
+ life_rating_t life_rating = 0;
+
+ Province(index_t new_index, std::string const& new_identifier, colour_t new_colour);
+ public:
+ static std::string colour_to_hex_string(colour_t colour);
+
+ index_t get_index() const;
+ colour_t get_colour() const;
+ Region* get_region() const;
+ bool is_water() const;
+ life_rating_t get_life_rating() const;
+ std::string to_string() const;
+ };
+}
diff --git a/extension/src/openvic2/Region.cpp b/extension/src/openvic2/Region.cpp
index 6ee05f5..67a75a9 100644
--- a/extension/src/openvic2/Region.cpp
+++ b/extension/src/openvic2/Region.cpp
@@ -1,26 +1,26 @@
-#include "Map.hpp"
+#include "Region.hpp"
#include <cassert>
#include <algorithm>
using namespace OpenVic2;
-Region::Region(std::string const& new_identifier) : identifier(new_identifier) {
- assert(!identifier.empty());
-}
-
-std::string const& Region::get_identifier() const {
- return identifier;
-}
-
-size_t Region::get_province_count() const {
+size_t ProvinceSet::get_province_count() const {
return provinces.size();
}
-bool Region::contains_province(Province const* province) const {
+bool ProvinceSet::contains_province(Province const* province) const {
return province && std::find(provinces.begin(), provinces.end(), province) != provinces.end();
}
-std::vector<Province*> const& Region::get_provinces() const {
+std::set<Province*> const& ProvinceSet::get_provinces() const {
return provinces;
}
+
+Region::Region(std::string const& new_identifier) : HasIdentifier(new_identifier) {}
+
+Province::colour_t Region::get_colour() const {
+ if (provinces.empty()) return 0xFF0000;
+ Province const* province = *provinces.cbegin();
+ return province->get_colour();
+}
diff --git a/extension/src/openvic2/Region.hpp b/extension/src/openvic2/Region.hpp
new file mode 100644
index 0000000..4ebabd1
--- /dev/null
+++ b/extension/src/openvic2/Region.hpp
@@ -0,0 +1,29 @@
+#pragma once
+
+#include <set>
+
+#include "Province.hpp"
+
+namespace OpenVic2 {
+ struct Map;
+
+ struct ProvinceSet {
+ protected:
+ std::set<Province*> provinces;
+ public:
+ size_t get_province_count() const;
+ bool contains_province(Province const* province) const;
+ std::set<Province*> const& get_provinces() const;
+ };
+
+ /* REQUIREMENTS:
+ * MAP-6, MAP-44, MAP-48
+ */
+ struct Region : HasIdentifier, ProvinceSet {
+ friend struct Map;
+ private:
+ Region(std::string const& new_identifier);
+ public:
+ Province::colour_t get_colour() const;
+ };
+}
diff --git a/extension/src/openvic2/Types.cpp b/extension/src/openvic2/Types.cpp
new file mode 100644
index 0000000..67c7a21
--- /dev/null
+++ b/extension/src/openvic2/Types.cpp
@@ -0,0 +1,13 @@
+#include "Types.hpp"
+
+#include <cassert>
+
+using namespace OpenVic2;
+
+HasIdentifier::HasIdentifier(std::string const& new_identifier) : identifier(new_identifier) {
+ assert(!identifier.empty());
+}
+
+std::string const& HasIdentifier::get_identifier() const {
+ return identifier;
+}
diff --git a/extension/src/openvic2/Types.hpp b/extension/src/openvic2/Types.hpp
index 0fb1c8b..bf5ee27 100644
--- a/extension/src/openvic2/Types.hpp
+++ b/extension/src/openvic2/Types.hpp
@@ -1,7 +1,18 @@
#pragma once
+#include <string>
+
namespace OpenVic2 {
using return_t = bool;
// This mirrors godot::Error, where `OK = 0` and `FAILED = 1`.
static const return_t SUCCESS = false, FAILURE = true;
+
+ struct HasIdentifier {
+ private:
+ std::string identifier;
+ protected:
+ HasIdentifier(std::string const& new_identifier);
+ public:
+ std::string const& get_identifier() const;
+ };
}
diff --git a/game/art/terrain/farmlands.png b/game/art/terrain/farmlands.png
new file mode 100644
index 0000000..3702ad0
--- /dev/null
+++ b/game/art/terrain/farmlands.png
Binary files differ
diff --git a/game/art/terrain/terrain.png.import b/game/art/terrain/farmlands.png.import
index d7a078f..4a06b1f 100644
--- a/game/art/terrain/terrain.png.import
+++ b/game/art/terrain/farmlands.png.import
@@ -2,16 +2,16 @@
importer="texture"
type="CompressedTexture2D"
-uid="uid://cmw0pvjthnn8c"
-path="res://.godot/imported/terrain.png-06c63c2b87b3131a2067f668f87a9d67.ctex"
+uid="uid://ckf222w5usrsu"
+path="res://.godot/imported/farmlands.png-821213ab9dba19cea6f6c966b3a3760b.ctex"
metadata={
"vram_texture": false
}
[deps]
-source_file="res://art/terrain/terrain.png"
-dest_files=["res://.godot/imported/terrain.png-06c63c2b87b3131a2067f668f87a9d67.ctex"]
+source_file="res://art/terrain/farmlands.png"
+dest_files=["res://.godot/imported/farmlands.png-821213ab9dba19cea6f6c966b3a3760b.ctex"]
[params]
@@ -21,7 +21,7 @@ compress/lossy_quality=0.7
compress/hdr_compression=1
compress/normal_map=2
compress/channel_pack=0
-mipmaps/generate=true
+mipmaps/generate=false
mipmaps/limit=-1
roughness/mode=0
roughness/src_normal=""
diff --git a/game/art/terrain/terrain.png b/game/art/terrain/terrain.png
deleted file mode 100644
index 6d36dee..0000000
--- a/game/art/terrain/terrain.png
+++ /dev/null
Binary files differ
diff --git a/game/common/map/provinces.json b/game/common/map/provinces.json
index 66de29b..71be5cb 100644
--- a/game/common/map/provinces.json
+++ b/game/common/map/provinces.json
@@ -2,8 +2,222 @@
"prov_britain": [150, 0, 0],
"prov_ireland": [23, 147, 31],
"prov_iceland": "343D91",
+ "prov_corsica": "FFD800",
+ "prov_sardinia": "00FFFF",
+ "prov_sicily": "FFA13D",
+ "prov_malta": "96E6FF",
+ "prov_cyprus": "FFE682",
+ "prov_greenland": "7A66FF",
+ "prov_baleares": "FF9E21",
+ "prov_gotland": "8300FF",
+ "prov_crete": "FF3523",
+ "prov_jan_mayen": "003FFF",
+ "prov_faroes": "964B00",
+ "prov_mann": "935575",
+ "prov_shetlands": "916F7D",
+ "prov_orkney": "8E1C70",
+ "prov_channel_islands": "8C6267",
+ "prov_madiera": "87580C",
+ "prov_canarias": "843740",
+ "prov_sjaeland": "820B02",
+ "prov_bornholm": "7F512F",
+ "prov_bear_island": "AE2AD6",
+ "prov_pantelleria": "FFA575",
+ "prov_iberia": "FF425D",
+ "prov_scandinavia": "005F32",
+ "prov_jutland": "B7004B",
+ "prov_fyn": "B55C63",
+ "prov_hiiumaa": "52FFB2",
+ "prov_saaremaa": "B5E31D",
+ "prov_italy": "47872C",
+ "prov_elba": "333333",
+ "prov_france": "0B0BC6",
+ "prov_netherlands": "FF472C",
+ "prov_belgium": "8C6A16",
+ "prov_luxembourg": "8FB7C6",
+ "prov_switzerland": "A50000",
+ "prov_balkans": "00FF5D",
+ "prov_crimea": "752179",
+ "prov_germany": "303030",
+ "prov_poland": "C90043",
+ "prov_baltics": "6327C4",
+ "prov_finland": "E8E8E8",
+ "prov_aland": "8769B3",
+ "prov_ukraine": "C1CB00",
+ "prov_russia": "006311",
+
+ "prov_north_america": "A114FF",
"prov_cuba": "1E29FF",
+ "prov_nova_scotia": "1115FF",
+ "prov_bermuda": "FF2370",
+ "prov_jamaica": "329600",
+ "prov_hispaniola": "742B7C",
+ "prov_aleutians": "FF07AC",
+ "prov_bahamas": "824261",
+ "prov_turks_and_caicos": "7F4424",
+ "prov_puerto_rico": "0D7C57",
+ "prov_barbados": "747A48",
+ "prov_grenada": "32774B",
+ "prov_st_vincent": "535C75",
+ "prov_st_lucia": "E15CEE",
+ "prov_martinique": "EDB274",
+ "prov_dominica": "EAE77E",
+ "prov_guadeloupe": "E8279E",
+ "prov_montserrat": "80AAE5",
+ "prov_antigua": "E2AEB9",
+ "prov_barbuda": "E0966D",
+ "prov_st_kitts": "DD85DA",
+ "prov_west_virgin_islands": "304716",
+ "prov_east_virgin_islands": "44321E",
+ "prov_cayman_islands": "FF7900",
+ "prov_angulia": "FF3D57",
+ "prov_central_america": "005B5E",
+ "prov_mexico": "3BA533",
+
+ "prov_south_america": "2F7A22",
+ "prov_galapagos": "638293",
+ "prov_falklands": "91451F",
+ "prov_south_georgia": "8E8B8E",
+
+ "prov_africa": "7F3300",
"prov_madagascar": "790091",
+ "prov_socotra": "89801A",
+ "prov_mauritius": "1C8400",
+ "prov_reunion": "7D821F",
+ "prov_comoros": "7F5731",
+ "prov_sao_tome": "FFAFE8",
+ "prov_fernando_po": "FF665B",
+ "prov_cape_verde": "00FF48",
+ "prov_ascension": "7C3B3E",
+ "prov_st_helena": "7A6C48",
+ "prov_tristan_da_cunha": "77680E",
+ "prov_seychelles": "EF68FF",
+ "prov_prince_edward_islands": "826F68",
+ "prov_kerguelen_islands": "671F7F",
+ "prov_heard_island": "7C4244",
+ "prov_egypt": "A5815B",
+ "prov_morocco": "D44509",
+
+ "prov_middle_east": "615EA8",
"prov_ceylon": "FF6A00",
- "prov_formosa": "82B1FF"
+ "prov_formosa": "82B1FF",
+ "prov_sakhalin": "006421",
+ "prov_maldives": "DC0000",
+ "prov_hainan": "688785",
+ "prov_hokkaido": "BA0DB5",
+ "prov_diego_garcia": "752268",
+ "prov_philippines": "FFA5AD",
+ "prov_india": "720041",
+ "prov_andamans": "706054",
+ "prov_nicobar_islands": "30006B",
+ "prov_indochina": "007272",
+ "prov_korea": "1E2570",
+ "prov_okinawa": "216D38",
+ "prov_yaeyama": "6B120A",
+ "prov_kyushu": "9E2521",
+ "prov_shikoku": "6F9B61",
+ "prov_japan": "99581B",
+ "prov_kurils": "992B15",
+ "prov_manchuria": "B79900",
+ "prov_china": "B57A53",
+ "prov_central_asia": "00B2B2",
+ "prov_siberia": "000EAF",
+ "prov_iran": "703E1D",
+ "prov_anatolia": "A81000",
+
+ "prov_oceania": "FF4C00",
+ "prov_indonesia": "A56A9E",
+ "prov_north_island": "DB41AF",
+ "prov_south_island": "D86E47",
+ "prov_tasmania": "D6BF2C",
+ "prov_australia": "5BB1D3",
+ "prov_hawaii": "008C5D",
+
+ "prov_aral_sea": "AA61C6",
+ "prov_caspian_sea": "00C413",
+ "prov_lake_ladoga": "462A64",
+ "prov_lake_ontario": "46CD64",
+ "prov_lake_erie": "9F2663",
+ "prov_lake_huron": "9FC6EA",
+ "prov_lake_michigan": "9FC62A",
+ "prov_lake_superior": "9F352A",
+ "prov_lake_baikal": "B51453",
+ "prov_lake_woods": "9F892A",
+ "prov_lake_manitoba": "FF0071",
+ "prov_reindeer_lake": "FF7DFF",
+ "prov_lake_ronge": "D9FF51",
+ "prov_lake_athabasca": "FF7D3D",
+ "prov_great_slave_lake": "49FF6B",
+ "prov_great_bear_lake": "FFC6C9",
+
+ "prov_azov_sea": "3E33FF",
+ "prov_black_sea": "3EE1FF",
+ "prov_marmara_sea": "00009D",
+ "prov_agean_sea": "0019FF",
+ "prov_ionian_sea": "003A9D",
+ "prov_adriatic_sea": "00E3FF",
+ "prov_tyrrhenian_sea": "0935FF",
+ "prov_east_mediterranean": "00BBFF",
+ "prov_central_mediterranean": "3A5FDC",
+ "prov_west_mediterranean": "93C5FF",
+ "prov_ligurian_sea": "0000B0",
+ "prov_balearic_sea": "001BFF",
+ "prov_alboran_sea": "3E98FF",
+ "prov_gulf_bothnia": "52E39D",
+ "prov_gulf_finland": "0935C2",
+ "prov_gulf_riga": "004987",
+ "prov_baltic_sea": "5BC5CB",
+ "prov_danish_straits": "004DB0",
+ "prov_english_channel": "0079FF",
+ "prov_irish_sea": "544DFF",
+ "prov_biscay_bay": "93EEFF",
+ "prov_north_sea": "31BBFF",
+ "prov_red_sea": "0952FF",
+ "prov_arabian_sea": "00007E",
+ "prov_persian_gulf": "3EA3FF",
+ "prov_andaman_sea": "0092FF",
+ "prov_bay_bengal": "4075FF",
+ "prov_okhotsk_sea": "66C5FF",
+ "prov_japan_sea": "3A92DC",
+ "prov_east_china_sea": "0066CB",
+ "prov_south_china_sea": "3EEEFF",
+ "prov_philippine_sea": "210066",
+ "prov_celebes_sea": "0066BB",
+ "prov_java_sea": "3E33C9",
+ "prov_banda_sea": "73A3FF",
+ "prov_arafura_sea": "0000D4",
+ "prov_gulf_mexico": "00639D",
+ "prov_caribbean_sea": "00C2DA",
+ "prov_mozambique_channel": "098FFF",
+ "prov_zanj_sea": "93B5FF",
+ "prov_kara_sea": "49FFFF",
+ "prov_barents_sea": "3A40DC",
+ "prov_norwegian_sea": "000087",
+ "prov_greenland_sea": "33EEFF",
+ "prov_labrador_sea": "003787",
+ "prov_hudson_bay": "93F7FF",
+ "prov_gulf_st_lawrence": "4DF7FF",
+ "prov_gulf_alaska": "2C2F87",
+ "prov_gulf_california": "2C2FCF",
+ "prov_east_siberian_sea": "52E3FF",
+ "prov_sargasso_sea": "0019DA",
+ "prov_gulf_guinea": "008BDA",
+ "prov_celtic_sea": "0050B0",
+ "prov_argentine_sea": "0082B0",
+ "prov_chilean_sea": "47FFFF",
+ "prov_north_atlantic": "00B3FF",
+ "prov_central_atlantic": "68C5FF",
+ "prov_south_atlantic": "004DDA",
+ "prov_indian_ocean": "000066",
+ "prov_great_australian_bight": "3A75FF",
+ "prov_tasman_sea": "002CD4",
+ "prov_coral_sea": "8919FF",
+ "prov_melanesia": "3AA8FF",
+ "prov_micronesia": "0098CB",
+ "prov_polynesia": "003EDA",
+ "prov_north_pacific": "00EECF",
+ "prov_south_pacific": "03B3FF",
+ "prov_bering_sea": "0049DA",
+ "prov_chukchi_sea": "0087A5",
+ "prov_beaufort_sea": "93BEFF"
}
diff --git a/game/common/map/provinces.png b/game/common/map/provinces.png
index 68bf528..73cc33c 100644
--- a/game/common/map/provinces.png
+++ b/game/common/map/provinces.png
Binary files differ
diff --git a/game/common/map/regions.json b/game/common/map/regions.json
index bbeeb56..1684fb5 100644
--- a/game/common/map/regions.json
+++ b/game/common/map/regions.json
@@ -1,6 +1,13 @@
{
- "region_europe": ["prov_britain", "prov_ireland", "prov_iceland"],
- "region_america": ["prov_cuba"],
- "region_africa": ["prov_madagascar"],
- "region_asia": ["prov_ceylon", "prov_formosa"]
+ "region_europe": ["prov_britain", "prov_ireland", "prov_iceland", "prov_corsica", "prov_sardinia", "prov_sicily", "prov_malta", "prov_cyprus", "prov_greenland", "prov_baleares", "prov_gotland", "prov_crete", "prov_jan_mayen", "prov_faroes", "prov_mann", "prov_shetlands", "prov_orkney", "prov_channel_islands", "prov_madiera", "prov_canarias", "prov_sjaeland", "prov_bornholm", "prov_bear_island", "prov_pantelleria", "prov_iberia", "prov_scandinavia", "prov_jutland", "prov_fyn", "prov_hiiumaa", "prov_saaremaa", "prov_italy", "prov_elba", "prov_france", "prov_netherlands", "prov_belgium", "prov_luxembourg", "prov_switzerland", "prov_balkans", "prov_crimea", "prov_germany", "prov_poland", "prov_baltics", "prov_finland", "prov_aland", "prov_ukraine", "prov_russia"],
+
+ "region_north_america": ["prov_north_america", "prov_cuba", "prov_nova_scotia", "prov_bermuda", "prov_jamaica", "prov_hispaniola", "prov_aleutians", "prov_bahamas", "prov_turks_and_caicos", "prov_puerto_rico", "prov_barbados", "prov_grenada", "prov_st_vincent", "prov_st_lucia", "prov_martinique", "prov_dominica", "prov_guadeloupe", "prov_montserrat", "prov_antigua", "prov_barbuda", "prov_st_kitts", "prov_west_virgin_islands", "prov_east_virgin_islands", "prov_cayman_islands", "prov_angulia", "prov_central_america", "prov_mexico"],
+
+ "region_south_america": ["prov_south_america", "prov_galapagos", "prov_falklands", "prov_south_georgia"],
+
+ "region_africa": ["prov_africa", "prov_madagascar", "prov_socotra", "prov_mauritius", "prov_reunion", "prov_comoros", "prov_sao_tome", "prov_fernando_po", "prov_cape_verde", "prov_ascension", "prov_st_helena", "prov_tristan_da_cunha", "prov_seychelles", "prov_prince_edward_islands", "prov_kerguelen_islands", "prov_heard_island", "prov_egypt", "prov_morocco"],
+
+ "region_asia": ["prov_middle_east", "prov_ceylon", "prov_formosa", "prov_sakhalin", "prov_maldives", "prov_hainan", "prov_hokkaido", "prov_diego_garcia", "prov_philippines", "prov_india", "prov_andamans", "prov_nicobar_islands", "prov_indochina", "prov_korea", "prov_okinawa", "prov_yaeyama", "prov_kyushu", "prov_shikoku", "prov_japan", "prov_kurils", "prov_manchuria", "prov_china", "prov_central_asia", "prov_siberia", "prov_iran", "prov_anatolia"],
+
+ "region_oceania": ["prov_indonesia", "prov_north_island", "prov_south_island", "prov_tasmania", "prov_australia", "prov_hawaii", "prov_oceania"]
}
diff --git a/game/common/map/water.json b/game/common/map/water.json
new file mode 100644
index 0000000..fda53a9
--- /dev/null
+++ b/game/common/map/water.json
@@ -0,0 +1,6 @@
+
+[
+ "prov_aral_sea", "prov_caspian_sea", "prov_lake_ladoga", "prov_lake_ontario", "prov_lake_erie", "prov_lake_huron", "prov_lake_michigan", "prov_lake_superior", "prov_lake_baikal", "prov_lake_woods", "prov_lake_manitoba", "prov_reindeer_lake", "prov_lake_ronge", "prov_lake_athabasca", "prov_great_slave_lake", "prov_great_bear_lake",
+
+ "prov_azov_sea", "prov_black_sea", "prov_marmara_sea", "prov_agean_sea", "prov_ionian_sea", "prov_adriatic_sea", "prov_tyrrhenian_sea", "prov_east_mediterranean", "prov_central_mediterranean", "prov_west_mediterranean", "prov_ligurian_sea", "prov_balearic_sea", "prov_alboran_sea", "prov_gulf_bothnia", "prov_gulf_finland", "prov_gulf_riga", "prov_baltic_sea", "prov_danish_straits", "prov_english_channel", "prov_irish_sea", "prov_biscay_bay", "prov_north_sea", "prov_red_sea", "prov_arabian_sea", "prov_persian_gulf", "prov_andaman_sea", "prov_bay_bengal", "prov_okhotsk_sea", "prov_japan_sea", "prov_east_china_sea", "prov_south_china_sea", "prov_philippine_sea", "prov_celebes_sea", "prov_java_sea", "prov_banda_sea", "prov_arafura_sea", "prov_gulf_mexico", "prov_caribbean_sea", "prov_mozambique_channel", "prov_zanj_sea", "prov_kara_sea", "prov_barents_sea", "prov_norwegian_sea", "prov_greenland_sea", "prov_labrador_sea", "prov_hudson_bay", "prov_gulf_st_lawrence", "prov_gulf_alaska", "prov_gulf_california", "prov_east_siberian_sea", "prov_sargasso_sea", "prov_gulf_guinea", "prov_celtic_sea", "prov_argentine_sea", "prov_chilean_sea", "prov_north_atlantic", "prov_central_atlantic", "prov_south_atlantic", "prov_indian_ocean", "prov_great_australian_bight", "prov_tasman_sea", "prov_coral_sea", "prov_melanesia", "prov_micronesia", "prov_polynesia", "prov_north_pacific", "prov_south_pacific", "prov_bering_sea", "prov_chukchi_sea", "prov_beaufort_sea"
+]
diff --git a/game/localisation/en_GB/mapmodes.csv b/game/localisation/en_GB/mapmodes.csv
index c5b1bb0..440175f 100644
--- a/game/localisation/en_GB/mapmodes.csv
+++ b/game/localisation/en_GB/mapmodes.csv
@@ -2,4 +2,5 @@
,, Test Mapmodes
mapmode_province,Province Mapmode
mapmode_region,Region Mapmode
+mapmode_terrain,Terrain Mapmode
mapmode_index,Index Mapmode
diff --git a/game/localisation/en_GB/menus.csv b/game/localisation/en_GB/menus.csv
index b483662..33e5c42 100644
--- a/game/localisation/en_GB/menus.csv
+++ b/game/localisation/en_GB/menus.csv
@@ -49,5 +49,17 @@ GAMELOBBY_START,Start Game
GAMELOBBY_BACK,Back
,, Game Session Menu
-GAMESESSIONMENU_RESIGN,Resign
+GAMESESSIONMENU_SAVE,Save Game
+GAMESESSIONMENU_LOAD,Load Game
+GAMESESSIONMENU_OPTIONS,Options
+GAMESESSIONMENU_MAINMENU,Resign
+GAMESESSIONMENU_QUIT,Quit
GAMESESSIONMENU_CLOSE,Close
+
+GAMESESSIONMENU_MAINMENU_DIALOG_TITLE,Resign Game
+GAMESESSIONMENU_MAINMENU_DIALOG_TEXT,Are you sure you want to resign and return to the main menu?
+GAMESESSIONMENU_QUIT_DIALOG_TITLE,Quit Game
+GAMESESSIONMENU_QUIT_DIALOG_TEXT,Are you sure you want to quit and return to desktop?
+
+DIALOG_OK,OK
+DIALOG_CANCEL,Cancel
diff --git a/game/localisation/en_GB/provinces.csv b/game/localisation/en_GB/provinces.csv
index 3b47955..24248a0 100644
--- a/game/localisation/en_GB/provinces.csv
+++ b/game/localisation/en_GB/provinces.csv
@@ -1,9 +1,230 @@
-,, Test Provinces
+,, Europe
prov_britain_NAME,Britain
prov_ireland_NAME,Ireland
prov_iceland_NAME,Iceland
+prov_corsica_NAME,Corsica
+prov_sardinia_NAME,Sardinia
+prov_sicily_NAME,Sicily
+prov_malta_NAME,Malta
+prov_cyprus_NAME,Cyprus
+prov_greenland_NAME,Greenland
+prov_baleares_NAME,Balearic Islands
+prov_gotland_NAME,Gotland
+prov_crete_NAME,Crete
+prov_jan_mayen_NAME,Jan Mayen
+prov_faroes_NAME,Faroe Islands
+prov_mann_NAME,Isle of Mann
+prov_shetlands_NAME,Shetlands
+prov_orkney_NAME,Orkney
+prov_channel_islands_NAME,Channel Islands
+prov_madiera_NAME,Madiera
+prov_canarias_NAME,Canarias
+prov_sjaeland_NAME,Sjaeland
+prov_bornholm_NAME,Bornholm
+prov_bear_island_NAME,Bear Island
+prov_pantelleria_NAME,Pantelleria
+prov_iberia_NAME,Iberia
+prov_scandinavia_NAME,Scandinavia
+prov_jutland_NAME,Jutland
+prov_fyn_NAME,Fyn
+prov_hiiumaa_NAME,Hiiumaa
+prov_saaremaa_NAME,Saaremaa
+prov_italy_NAME,Italy
+prov_elba_NAME,Elba
+prov_france_NAME,France
+prov_netherlands_NAME,Netherlands
+prov_belgium_NAME,Belgium
+prov_luxembourg_NAME,Luxembourg
+prov_switzerland_NAME,Switzerland
+prov_balkans_NAME,Balkans
+prov_crimea_NAME,Crimea
+prov_germany_NAME,Germany
+prov_poland_NAME,Poland
+prov_baltics_NAME,Baltics
+prov_finland_NAME,Finland
+prov_aland_NAME,Åland
+prov_ukraine_NAME,Ukraine
+prov_russia_NAME,Russia
+
+,, North America
+prov_north_america_NAME,North America
prov_cuba_NAME,Cuba
+prov_nova_scotia_NAME,Nova Scotia
+prov_bermuda_NAME,Bermuda
+prov_jamaica_NAME,Jamaica
+prov_hispaniola_NAME,Hispaniola
+prov_aleutians_name,Aleutian Islands
+prov_bahamas_NAME,Bahamas
+prov_turks_and_caicos_NAME,Turks and Caicos
+prov_puerto_rico_NAME,Puerto Rico
+prov_barbados_NAME,Barbados
+prov_grenada_NAME,Grenada
+prov_st_vincent_NAME,St Vincent
+prov_st_lucia_NAME,St Lucia
+prov_martinique_NAME,Martinique
+prov_dominica_NAME,Dominica
+prov_guadeloupe_NAME,Guadeloupe
+prov_montserrat_NAME,Montserrat
+prov_antigua_NAME,Antigua
+prov_barbuda_NAME,Barbuda
+prov_st_kitts_NAME,St Kitts
+prov_west_virgin_islands_NAME,West Virgin Islands
+prov_east_virgin_islands_NAME,East Virgin Islands
+prov_cayman_islands_NAME,Cayman Islands
+prov_angulia_NAME,Angulia
+prov_central_america_NAME,Central America
+prov_mexico_NAME,Mexico
+
+,, South America
+prov_south_america_NAME,South America
+prov_galapagos_NAME,Galapagos
+prov_falklands_NAME,Falklands
+prov_south_georgia_NAME,South Georgia
+
+,, Africa
+prov_africa_NAME,Africa
prov_madagascar_NAME,Madagascar
+prov_socotra_NAME,Socotra
+prov_mauritius_NAME,Mauritius
+prov_reunion_NAME,Réunion
+prov_comoros_NAME,Comoros
+prov_sao_tome_NAME,Sao Tome
+prov_fernando_po_NAME,Fernando Po
+prov_cape_verde_NAME,Cape Verde
+prov_ascension_NAME,Ascension
+prov_st_helena_NAME,St Helena
+prov_tristan_da_cunha_NAME,Tristan da Cunha
+prov_seychelles_NAME,Seychelles
+prov_prince_edward_islands_NAME,Prince Edward Islands
+prov_kerguelen_islands_NAME,Kerguelen Islands
+prov_heard_island_NAME,Heard Island
+prov_egypt_NAME,Egypt
+prov_morocco_NAME,Morocco
+
+,, Asia
+prov_middle_east_NAME,Middle East
prov_ceylon_NAME,Ceylon
prov_formosa_NAME,Formosa
+prov_sakhalin_NAME,Sakhalin
+prov_maldives_NAME,Maldives
+prov_hainan_NAME,Hainan
+prov_hokkaido_NAME,Hokkaido
+prov_diego_garcia_NAME,Diego Garcia
+prov_philippines_NAME,Philippines
+prov_india_NAME,India
+prov_andamans_NAME,Andaman Islands
+prov_nicobar_islands_NAME,Nicobar Islands
+prov_indochina_NAME,Indochina
+prov_korea_NAME,Korea
+prov_okinawa_NAME,Okinawa
+prov_yaeyama_NAME,Yaeyama
+prov_kyushu_NAME,Kyushu
+prov_shikoku_NAME,Shikoku
+prov_japan_NAME,Japan
+prov_kurils_NAME,Kuril Islands
+prov_manchuria_NAME,Manchuria
+prov_china_NAME,China
+prov_central_asia_NAME,Central Asia
+prov_siberia_NAME,Siberia
+prov_iran_NAME,Iran
+prov_anatolia_NAME,Anatolia
+
+,, Oceania
+prov_oceania_NAME,Oceania
+prov_indonesia_NAME,Indonesia
+prov_north_island_NAME,North Island
+prov_south_island_NAME,South Island
+prov_tasmania_NAME,Tasmania
+prov_australia_NAME,Australia
+prov_hawaii_NAME,Hawaii
+
+,, Lakes
+prov_aral_sea_NAME,Aral Sea
+prov_caspian_sea_NAME,Caspian Sea
+prov_lake_ladoga_NAME,Lake Ladoga
+prov_lake_ontario_NAME,Lake Ontario
+prov_lake_erie_NAME,Lake Erie
+prov_lake_huron_NAME,Lake Huron
+prov_lake_michigan_NAME,Lake Michigan
+prov_lake_superior_NAME,Lake Superior
+prov_lake_baikal_NAME,Lake Baikal
+prov_lake_woods_NAME,Lake of the Woods
+prov_lake_manitoba_NAME,Lake Manitoba
+prov_reindeer_lake_NAME,Reindeer Lake
+prov_lake_ronge_NAME,Lac la Ronge
+prov_lake_athabasca_NAME,Lake Athabasca
+prov_great_slave_lake_NAME,Great Slave Lake
+prov_great_bear_lake_NAME,Great Bear Lake
+
+,, Seas and Oceans
+prov_azov_sea_NAME,Sea of Azov
+prov_black_sea_NAME,Black Sea
+prov_marmara_sea_NAME,Sea of Marmara
+prov_agean_sea_NAME,Agean Sea
+prov_ionian_sea_NAME,Ionian Sea
+prov_adriatic_sea_NAME,Adriatic Sea
+prov_tyrrhenian_sea_NAME,Tyrrhenian Sea
+prov_east_mediterranean_NAME,East Mediterranean Sea
+prov_central_mediterranean_NAME,Central Mediterranean Sea
+prov_west_mediterranean_NAME,West Mediterranean Sea
+prov_ligurian_sea_NAME,Ligurian Sea
+prov_balearic_sea_NAME,Balearic Sea
+prov_alboran_sea_NAME,Alboran Sea
+prov_gulf_bothnia_NAME,Gulf of Bothnia
+prov_gulf_finland_NAME,Gulf of Finland
+prov_gulf_riga_NAME,Gulf of Riga
+prov_baltic_sea_NAME,Baltic Sea
+prov_danish_straits_NAME,Danish Straits
+prov_english_channel_NAME,English Channel
+prov_irish_sea_NAME,Irish Sea
+prov_biscay_bay_NAME,Bay of Biscay
+prov_north_sea_NAME,North Sea
+prov_red_sea_NAME,Red Sea
+prov_arabian_sea_NAME,Arabian Sea
+prov_persian_gulf_NAME,Persian Gulf
+prov_andaman_sea_NAME,Andaman Sea
+prov_bay_bengal_NAME,Bay of Bengal
+prov_okhotsk_sea_NAME,Sea of Okhotsk
+prov_japan_sea_NAME,Sea of Japan
+prov_east_china_sea_NAME,East China Sea
+prov_south_china_sea_NAME,South China Sea
+prov_philippine_sea_NAME,Philippine Sea
+prov_celebes_sea_NAME,Celebes Sea
+prov_java_sea_NAME,Java Sea
+prov_banda_sea_NAME,Banda Sea
+prov_arafura_sea_NAME,Arafura Sea
+prov_gulf_mexico_NAME,Gulf of Mexico
+prov_caribbean_sea_NAME,Caribbean Sea
+prov_mozambique_channel_NAME,Mozambique Channel
+prov_zanj_sea_NAME,Sea of Zanj
+prov_kara_sea_NAME,Kara Sea
+prov_barents_sea_NAME,Barents Sea
+prov_norwegian_sea_NAME,Norwegian Sea
+prov_greenland_sea_NAME,Greenland Sea
+prov_labrador_sea_NAME,Labrador Sea
+prov_hudson_bay_NAME,Hudson Bay
+prov_gulf_st_lawrence_NAME,Gulf of St Lawrence
+prov_gulf_alaska_NAME,Gulf of Alaska
+prov_gulf_california_NAME,Gulf of California
+prov_east_siberian_sea_NAME,East Siberian Sea
+prov_sargasso_sea_NAME,Sargasso Sea
+prov_gulf_guinea_NAME,Gulf of Guinea
+prov_celtic_sea_NAME,Celtic Sea
+prov_argentine_sea_NAME,Argentine Sea
+prov_chilean_sea_NAME,Chilean Sea
+prov_north_atlantic_NAME,North Atlantic Ocean
+prov_central_atlantic_NAME,Central Atlantic Ocean
+prov_south_atlantic_NAME,South Atlantic Ocean
+prov_indian_ocean_NAME,Indian Ocean
+prov_great_australian_bight_NAME,Great Australian Bight
+prov_tasman_sea_NAME,Tasman Sea
+prov_coral_sea_NAME,Coral Sea
+prov_melanesia_NAME,Melanesia
+prov_micronesia_NAME,Micronesia
+prov_polynesia_NAME,Polynesia
+prov_north_pacific_NAME,North Pacific Ocean
+prov_south_pacific_NAME,South Pacific Ocean
+prov_bering_sea_NAME,Bering Sea
+prov_chukchi_sea_NAME,Chukchi Sea
+prov_beaufort_sea_NAME,Beaufort Sea
diff --git a/game/localisation/en_GB/regions.csv b/game/localisation/en_GB/regions.csv
new file mode 100644
index 0000000..8fe8943
--- /dev/null
+++ b/game/localisation/en_GB/regions.csv
@@ -0,0 +1,8 @@
+
+,, Regions
+region_europe_NAME,Europe
+region_north_america_NAME,North America
+region_south_america_NAME,South America
+region_africa_NAME,Africa
+region_asia_NAME,Asia
+region_oceania_NAME,Oceania
diff --git a/game/localisation/en_GB/regions.csv.import b/game/localisation/en_GB/regions.csv.import
new file mode 100644
index 0000000..8dd0c09
--- /dev/null
+++ b/game/localisation/en_GB/regions.csv.import
@@ -0,0 +1,3 @@
+[remap]
+
+importer="keep"
diff --git a/game/localisation/en_US/menus.csv b/game/localisation/en_US/menus.csv
index 27a0858..807d1cb 100644
--- a/game/localisation/en_US/menus.csv
+++ b/game/localisation/en_US/menus.csv
@@ -1,3 +1,4 @@
+
,, Main Menu
MAINMENU_TITLE,OpenVic2
MAINMENU_NEW_GAME,New Game
@@ -48,5 +49,9 @@ GAMELOBBY_START,Start Game
GAMELOBBY_BACK,Back
,, Game Session Menu
-GAMESESSIONMENU_RESIGN,Resign
+GAMESESSIONMENU_SAVE,Save Game
+GAMESESSIONMENU_LOAD,Load Game
+GAMESESSIONMENU_OPTIONS,Options
+GAMESESSIONMENU_MAINMENU,Resign
+GAMESESSIONMENU_QUIT,Quit
GAMESESSIONMENU_CLOSE,Close
diff --git a/game/localisation/fr_FR/menus.csv b/game/localisation/fr_FR/menus.csv
index 8e8a7ad..0d84b64 100644
--- a/game/localisation/fr_FR/menus.csv
+++ b/game/localisation/fr_FR/menus.csv
@@ -1,3 +1,4 @@
+
,, Main Menu
MAINMENU_TITLE,OpenVic2
MAINMENU_NEW_GAME,Nouveau Jeu
@@ -48,5 +49,9 @@ GAMELOBBY_START,Démarrer Jeu
GAMELOBBY_BACK,Retourner
,, Game Session Menu
-GAMESESSIONMENU_RESIGN,Démissionner
+GAMESESSIONMENU_SAVE,Sauvegarder la Partie
+GAMESESSIONMENU_LOAD,Charger la Partie
+GAMESESSIONMENU_OPTIONS,Options
+GAMESESSIONMENU_MAINMENU,Démissionner
+GAMESESSIONMENU_QUIT,Quitter
GAMESESSIONMENU_CLOSE,Fermer
diff --git a/game/src/Autoload/Events.gd b/game/src/Autoload/Events.gd
index f4aac70..dbd3f9f 100644
--- a/game/src/Autoload/Events.gd
+++ b/game/src/Autoload/Events.gd
@@ -4,14 +4,17 @@ var Options = preload("Events/Options.gd").new()
var Localisation = preload("Events/Localisation.gd").new()
const _province_identifier_file : String = "res://common/map/provinces.json"
+const _water_province_file : String = "res://common/map/water.json"
const _region_file : String = "res://common/map/regions.json"
const _province_shape_file : String = "res://common/map/provinces.png"
# REQUIREMENTS
-# * FS-333, FS-335, FS-341
+# * FS-333, FS-334, FS-335, FS-341
func _ready():
if MapSingleton.load_province_identifier_file(_province_identifier_file) != OK:
push_error("Failed to load province identifiers")
+ if MapSingleton.load_water_province_file(_water_province_file) != OK:
+ push_error("Failed to load water provinces")
if MapSingleton.load_region_file(_region_file) != OK:
push_error("Failed to load regions")
if MapSingleton.load_province_shape_file(_province_shape_file) != OK:
diff --git a/game/src/GameSession/GameSession.gd b/game/src/GameSession/GameSession.gd
index 38eaba1..fe83a8a 100644
--- a/game/src/GameSession/GameSession.gd
+++ b/game/src/GameSession/GameSession.gd
@@ -1,16 +1,11 @@
-extends Node
+extends Control
@export var _game_session_menu : Control
func _ready():
- print("GameSession ready")
+ Events.Options.load_settings_from_file()
# REQUIREMENTS:
# * SS-42
func _on_game_session_menu_button_pressed():
_game_session_menu.visible = !_game_session_menu.visible
-
-# REQUIREMENTS:
-# * SS-64
-func _on_game_session_menu_close_button_pressed():
- _game_session_menu.hide()
diff --git a/game/src/GameSession/GameSession.tscn b/game/src/GameSession/GameSession.tscn
index e270f8a..d23cb6c 100644
--- a/game/src/GameSession/GameSession.tscn
+++ b/game/src/GameSession/GameSession.tscn
@@ -1,13 +1,21 @@
-[gd_scene load_steps=6 format=3 uid="uid://bgnupcshe1m7r"]
+[gd_scene load_steps=7 format=3 uid="uid://bgnupcshe1m7r"]
[ext_resource type="Script" path="res://src/GameSession/GameSession.gd" id="1_eklvp"]
[ext_resource type="PackedScene" uid="uid://g524p8lr574w" path="res://src/GameSession/MapControlPanel.tscn" id="3_afh6d"]
[ext_resource type="PackedScene" uid="uid://dvdynl6eir40o" path="res://src/GameSession/GameSessionMenu.tscn" id="3_bvmqh"]
[ext_resource type="PackedScene" uid="uid://dkehmdnuxih2r" path="res://src/GameSession/MapView.tscn" id="4_xkg5j"]
[ext_resource type="PackedScene" uid="uid://byq323jbel48u" path="res://src/GameSession/ProvinceOverviewPanel.tscn" id="5_osjnn"]
+[ext_resource type="PackedScene" uid="uid://cnbfxjy1m6wja" path="res://src/OptionMenu/OptionsMenu.tscn" id="6_p5mnx"]
-[node name="GameSession" type="Node" node_paths=PackedStringArray("_game_session_menu")]
+[node name="GameSession" type="Control" node_paths=PackedStringArray("_game_session_menu")]
editor_description = "SS-102, UI-546"
+layout_mode = 3
+anchors_preset = 15
+anchor_right = 1.0
+anchor_bottom = 1.0
+grow_horizontal = 2
+grow_vertical = 2
+mouse_filter = 2
script = ExtResource("1_eklvp")
_game_session_menu = NodePath("GameSessionMenu")
@@ -15,6 +23,7 @@ _game_session_menu = NodePath("GameSessionMenu")
[node name="GameSessionMenu" parent="." instance=ExtResource("3_bvmqh")]
visible = false
+layout_mode = 1
anchors_preset = 8
anchor_left = 0.5
anchor_top = 0.5
@@ -24,28 +33,31 @@ grow_horizontal = 2
grow_vertical = 2
[node name="MapControlPanel" parent="." instance=ExtResource("3_afh6d")]
+layout_mode = 1
anchors_preset = 3
anchor_left = 1.0
anchor_top = 1.0
anchor_right = 1.0
anchor_bottom = 1.0
-offset_left = -350.0
-offset_top = -210.0
grow_horizontal = 0
grow_vertical = 0
[node name="ProvinceOverviewPanel" parent="." instance=ExtResource("5_osjnn")]
-anchors_preset = -1
-anchor_top = 0.583333
-anchor_right = 0.15625
-offset_top = 0.0
-offset_right = 0.0
+layout_mode = 1
+
+[node name="OptionsMenu" parent="." instance=ExtResource("6_p5mnx")]
+visible = false
+layout_mode = 1
[connection signal="map_view_camera_changed" from="MapView" to="MapControlPanel" method="_on_map_view_camera_changed"]
[connection signal="province_selected" from="MapView" to="ProvinceOverviewPanel" method="_on_province_selected"]
-[connection signal="close_button_pressed" from="GameSessionMenu" to="." method="_on_game_session_menu_close_button_pressed"]
+[connection signal="options_button_pressed" from="GameSessionMenu" to="OptionsMenu" method="show"]
[connection signal="game_session_menu_button_pressed" from="MapControlPanel" to="." method="_on_game_session_menu_button_pressed"]
[connection signal="mapmode_changed" from="MapControlPanel" to="MapView" method="_update_colour_texture"]
[connection signal="minimap_clicked" from="MapControlPanel" to="MapView" method="_on_minimap_clicked"]
[connection signal="mouse_entered" from="MapControlPanel" to="MapView" method="_on_mouse_exited_viewport"]
[connection signal="mouse_exited" from="MapControlPanel" to="MapView" method="_on_mouse_entered_viewport"]
+[connection signal="zoom_in_button_pressed" from="MapControlPanel" to="MapView" method="zoom_in"]
+[connection signal="zoom_out_button_pressed" from="MapControlPanel" to="MapView" method="zoom_out"]
+[connection signal="back_button_pressed" from="OptionsMenu" to="MapView" method="enable_processing"]
+[connection signal="back_button_pressed" from="OptionsMenu" to="OptionsMenu" method="hide"]
diff --git a/game/src/GameSession/GameSessionMenu.gd b/game/src/GameSession/GameSessionMenu.gd
index 3722b52..8a76c0f 100644
--- a/game/src/GameSession/GameSessionMenu.gd
+++ b/game/src/GameSession/GameSessionMenu.gd
@@ -1,19 +1,24 @@
extends PanelContainer
-signal close_button_pressed
-
@export var _main_menu_scene : PackedScene
-func _ready():
- print("GameSessionMenu ready")
+signal options_button_pressed
# REQUIREMENTS:
# * SS-47
# * UIFUN-69
-func _on_to_main_menu_pressed():
+func _on_main_menu_confirmed() -> void:
get_tree().change_scene_to_packed(_main_menu_scene)
# REQUIREMENTS:
-# * UIFUN-69
-func _on_close_button_pressed():
- close_button_pressed.emit()
+# * SS-48
+# * UIFUN-70
+func _on_quit_confirmed() -> void:
+ get_tree().quit()
+
+# REQUIREMENTS:
+# * SS-7, SS-46
+# * UIFUN-11
+func _on_options_button_pressed() -> void:
+ hide()
+ options_button_pressed.emit()
diff --git a/game/src/GameSession/GameSessionMenu.tscn b/game/src/GameSession/GameSessionMenu.tscn
index 45fff4b..a753184 100644
--- a/game/src/GameSession/GameSessionMenu.tscn
+++ b/game/src/GameSession/GameSessionMenu.tscn
@@ -8,21 +8,64 @@ editor_description = "UI-68"
script = ExtResource("1_usq6o")
_main_menu_scene = ExtResource("2_xi6a4")
-[node name="VBoxContainer" type="VBoxContainer" parent="."]
+[node name="MarginContainer" type="MarginContainer" parent="."]
layout_mode = 2
+theme_override_constants/margin_left = 10
+theme_override_constants/margin_top = 10
+theme_override_constants/margin_right = 10
+theme_override_constants/margin_bottom = 10
-[node name="MainMenuButton" type="Button" parent="VBoxContainer"]
+[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer"]
+layout_mode = 2
+
+[node name="SaveButton" type="Button" parent="MarginContainer/VBoxContainer"]
+editor_description = "UI-69"
+layout_mode = 2
+text = "GAMESESSIONMENU_SAVE"
+
+[node name="LoadButton" type="Button" parent="MarginContainer/VBoxContainer"]
+editor_description = "UI-70"
+layout_mode = 2
+text = "GAMESESSIONMENU_LOAD"
+
+[node name="OptionsButton" type="Button" parent="MarginContainer/VBoxContainer"]
+editor_description = "UI-10"
+layout_mode = 2
+text = "GAMESESSIONMENU_OPTIONS"
+
+[node name="MainMenuButton" type="Button" parent="MarginContainer/VBoxContainer"]
editor_description = "UI-71"
layout_mode = 2
-text = "GAMESESSIONMENU_RESIGN"
+text = "GAMESESSIONMENU_MAINMENU"
+
+[node name="QuitButton" type="Button" parent="MarginContainer/VBoxContainer"]
+editor_description = "UI-72"
+layout_mode = 2
+text = "GAMESESSIONMENU_QUIT"
-[node name="HSeparator" type="HSeparator" parent="VBoxContainer"]
+[node name="HSeparator" type="HSeparator" parent="MarginContainer/VBoxContainer"]
layout_mode = 2
-[node name="CloseButton" type="Button" parent="VBoxContainer"]
-editor_description = "UI-80"
+[node name="CloseButton" type="Button" parent="MarginContainer/VBoxContainer"]
+editor_description = "SS-64, UI-80, UIFUN-79"
layout_mode = 2
text = "GAMESESSIONMENU_CLOSE"
-[connection signal="pressed" from="VBoxContainer/MainMenuButton" to="." method="_on_to_main_menu_pressed"]
-[connection signal="pressed" from="VBoxContainer/CloseButton" to="." method="_on_close_button_pressed"]
+[node name="MainMenuDialog" type="ConfirmationDialog" parent="."]
+title = "GAMESESSIONMENU_MAINMENU_DIALOG_TITLE"
+ok_button_text = "DIALOG_OK"
+dialog_text = "GAMESESSIONMENU_MAINMENU_DIALOG_TEXT"
+cancel_button_text = "DIALOG_CANCEL"
+
+[node name="QuitDialog" type="ConfirmationDialog" parent="."]
+title = "GAMESESSIONMENU_QUIT_DIALOG_TITLE"
+ok_button_text = "DIALOG_OK"
+dialog_text = "GAMESESSIONMENU_QUIT_DIALOG_TEXT"
+cancel_button_text = "DIALOG_CANCEL"
+
+[connection signal="pressed" from="MarginContainer/VBoxContainer/OptionsButton" to="." method="_on_options_button_pressed"]
+[connection signal="pressed" from="MarginContainer/VBoxContainer/MainMenuButton" to="MainMenuDialog" method="popup_centered"]
+[connection signal="pressed" from="MarginContainer/VBoxContainer/QuitButton" to="QuitDialog" method="popup_centered"]
+[connection signal="pressed" from="MarginContainer/VBoxContainer/CloseButton" to="." method="hide"]
+[connection signal="confirmed" from="MainMenuDialog" to="." method="_on_main_menu_confirmed"]
+[connection signal="confirmed" from="QuitDialog" to="." method="_on_quit_confirmed"]
diff --git a/game/src/GameSession/MapControlPanel.gd b/game/src/GameSession/MapControlPanel.gd
index 693890f..d5810c5 100644
--- a/game/src/GameSession/MapControlPanel.gd
+++ b/game/src/GameSession/MapControlPanel.gd
@@ -4,6 +4,8 @@ signal game_session_menu_button_pressed
signal mapmode_changed
signal map_view_camera_changed(near_left : Vector2, far_left : Vector2, far_right : Vector2, near_right : Vector2)
signal minimap_clicked(pos_clicked : Vector2)
+signal zoom_in_button_pressed
+signal zoom_out_button_pressed
@export var _mapmodes_grid : GridContainer
@@ -17,6 +19,7 @@ func _add_mapmode_button(identifier : String) -> void:
button.tooltip_text = identifier
button.toggle_mode = true
button.button_group = _mapmode_button_group
+ button.mouse_filter = MOUSE_FILTER_PASS
_mapmodes_grid.add_child(button)
if _mapmode_button_group.get_pressed_button() == null:
button.button_pressed = true
@@ -44,3 +47,13 @@ func _on_map_view_camera_changed(near_left : Vector2, far_left : Vector2, far_ri
func _on_minimap_clicked(pos_clicked : Vector2) -> void:
minimap_clicked.emit(pos_clicked)
+
+# REQUIREMENTS:
+# * UIFUN-269
+func _on_zoom_in_button_pressed() -> void:
+ zoom_in_button_pressed.emit()
+
+# REQUIREMENTS:
+# * UIFUN-270
+func _on_zoom_out_button_pressed() -> void:
+ zoom_out_button_pressed.emit()
diff --git a/game/src/GameSession/MapControlPanel.tscn b/game/src/GameSession/MapControlPanel.tscn
index 70d7eab..27205e3 100644
--- a/game/src/GameSession/MapControlPanel.tscn
+++ b/game/src/GameSession/MapControlPanel.tscn
@@ -15,49 +15,89 @@ events = [SubResource("InputEventAction_5nck3")]
editor_description = "SS-103"
mouse_filter = 1
script = ExtResource("1_ign64")
-_mapmodes_grid = NodePath("HBoxContainer/VBoxContainer/MapmodesGrid")
+_mapmodes_grid = NodePath("MarginContainer/HBoxContainer/VBoxContainer/MapmodesGrid")
-[node name="HBoxContainer" type="HBoxContainer" parent="."]
+[node name="MarginContainer" type="MarginContainer" parent="."]
layout_mode = 2
-alignment = 2
+theme_override_constants/margin_left = 5
+theme_override_constants/margin_top = 5
+theme_override_constants/margin_right = 5
+theme_override_constants/margin_bottom = 5
-[node name="VBoxContainer" type="VBoxContainer" parent="HBoxContainer"]
+[node name="HBoxContainer" type="HBoxContainer" parent="MarginContainer"]
layout_mode = 2
+theme_override_constants/separation = 6
+alignment = 1
-[node name="MapmodesGrid" type="GridContainer" parent="HBoxContainer/VBoxContainer"]
+[node name="VBoxContainer" type="VBoxContainer" parent="MarginContainer/HBoxContainer"]
+layout_mode = 2
+alignment = 1
+
+[node name="MapmodesGrid" type="GridContainer" parent="MarginContainer/HBoxContainer/VBoxContainer"]
editor_description = "UI-750"
layout_mode = 2
columns = 11
-[node name="Minimap" type="PanelContainer" parent="HBoxContainer/VBoxContainer"]
+[node name="Minimap" type="PanelContainer" parent="MarginContainer/HBoxContainer/VBoxContainer"]
editor_description = "UI-549"
layout_mode = 2
mouse_filter = 1
-[node name="TextureRect" type="TextureRect" parent="HBoxContainer/VBoxContainer/Minimap"]
+[node name="TextureRect" type="TextureRect" parent="MarginContainer/HBoxContainer/VBoxContainer/Minimap"]
editor_description = "UI-751, FS-338"
layout_mode = 2
texture = ExtResource("2_r613r")
-[node name="ViewportQuad" type="Control" parent="HBoxContainer/VBoxContainer/Minimap"]
+[node name="ViewportQuad" type="Control" parent="MarginContainer/HBoxContainer/VBoxContainer/Minimap"]
layout_mode = 2
mouse_filter = 2
script = ExtResource("3_s4dml")
-[node name="Frame" type="NinePatchRect" parent="HBoxContainer/VBoxContainer/Minimap"]
+[node name="Frame" type="NinePatchRect" parent="MarginContainer/HBoxContainer/VBoxContainer/Minimap"]
layout_mode = 2
texture = ExtResource("4_f1exl")
-[node name="AuxiliaryPanel" type="VBoxContainer" parent="HBoxContainer"]
+[node name="AuxiliaryPanel" type="VBoxContainer" parent="MarginContainer/HBoxContainer"]
editor_description = "UI-761"
layout_mode = 2
-[node name="GameSessionMenuButton" type="Button" parent="HBoxContainer/AuxiliaryPanel"]
+[node name="GameSessionMenuButton" type="Button" parent="MarginContainer/HBoxContainer/AuxiliaryPanel"]
editor_description = "UI-9"
layout_mode = 2
+mouse_filter = 1
shortcut = SubResource("Shortcut_fc1tk")
text = "ESC"
-[connection signal="map_view_camera_changed" from="." to="HBoxContainer/VBoxContainer/Minimap/ViewportQuad" method="_on_map_view_camera_changed"]
-[connection signal="minimap_clicked" from="HBoxContainer/VBoxContainer/Minimap/ViewportQuad" to="." method="_on_minimap_clicked"]
-[connection signal="pressed" from="HBoxContainer/AuxiliaryPanel/GameSessionMenuButton" to="." method="_on_game_session_menu_button_pressed"]
+[node name="LedgerButton" type="Button" parent="MarginContainer/HBoxContainer/AuxiliaryPanel"]
+editor_description = "UI-860"
+layout_mode = 2
+mouse_filter = 1
+text = "L"
+
+[node name="FindButton" type="Button" parent="MarginContainer/HBoxContainer/AuxiliaryPanel"]
+editor_description = "UI-861"
+layout_mode = 2
+mouse_filter = 1
+text = "F"
+
+[node name="ZoomButtonsContainer" type="HBoxContainer" parent="MarginContainer/HBoxContainer/AuxiliaryPanel"]
+layout_mode = 2
+alignment = 1
+
+[node name="ZoomInButton" type="Button" parent="MarginContainer/HBoxContainer/AuxiliaryPanel/ZoomButtonsContainer"]
+editor_description = "UI-862"
+layout_mode = 2
+mouse_filter = 1
+text = "+"
+
+[node name="ZoomOutButton" type="Button" parent="MarginContainer/HBoxContainer/AuxiliaryPanel/ZoomButtonsContainer"]
+editor_description = "UI-863"
+layout_mode = 2
+mouse_filter = 1
+text = "-"
+
+[connection signal="map_view_camera_changed" from="." to="MarginContainer/HBoxContainer/VBoxContainer/Minimap/ViewportQuad" method="_on_map_view_camera_changed"]
+[connection signal="minimap_clicked" from="MarginContainer/HBoxContainer/VBoxContainer/Minimap/ViewportQuad" to="." method="_on_minimap_clicked"]
+[connection signal="pressed" from="MarginContainer/HBoxContainer/AuxiliaryPanel/GameSessionMenuButton" to="." method="_on_game_session_menu_button_pressed"]
+[connection signal="pressed" from="MarginContainer/HBoxContainer/AuxiliaryPanel/ZoomButtonsContainer/ZoomInButton" to="." method="_on_zoom_in_button_pressed"]
+[connection signal="pressed" from="MarginContainer/HBoxContainer/AuxiliaryPanel/ZoomButtonsContainer/ZoomOutButton" to="." method="_on_zoom_out_button_pressed"]
diff --git a/game/src/GameSession/MapView.gd b/game/src/GameSession/MapView.gd
index 54d8df8..ae49e82 100644
--- a/game/src/GameSession/MapView.gd
+++ b/game/src/GameSession/MapView.gd
@@ -16,6 +16,7 @@ const _shader_param_province_index : StringName = &"province_index_tex"
const _shader_param_province_colour : StringName = &"province_colour_tex"
const _shader_param_hover_index : StringName = &"hover_index"
const _shader_param_selected_index : StringName = &"selected_index"
+const _shader_param_terrain_tile_factor : StringName = &"terrain_tile_factor"
@export var _camera : Camera3D
@@ -25,9 +26,9 @@ const _shader_param_selected_index : StringName = &"selected_index"
var _drag_anchor : Vector2
var _drag_active : bool = false
-var _mouse_over_viewport : bool = false
+var _mouse_over_viewport : bool = true
-@export var _zoom_target_min : float = 0.2
+@export var _zoom_target_min : float = 0.05
@export var _zoom_target_max : float = 5.0
@export var _zoom_target_step : float = 0.1
@export var _zoom_epsilon : float = _zoom_target_step * 0.1
@@ -100,6 +101,7 @@ func _ready():
# Set map mesh size and get bounds
_map_image_size = Vector2(Vector2i(MapSingleton.get_width(), MapSingleton.get_height()))
_map_mesh.aspect_ratio = _map_image_size.x / _map_image_size.y
+ _map_shader_material.set_shader_parameter(_shader_param_terrain_tile_factor, _map_image_size.y / 64.0)
var map_mesh_aabb := _map_mesh.get_core_aabb() * _map_mesh_instance.transform
_map_mesh_corner = Vector2(
min(map_mesh_aabb.position.x, map_mesh_aabb.end.x),
@@ -137,10 +139,16 @@ func _viewport_to_map_coords(pos_viewport : Vector2) -> Vector2:
push_error("Invalid intersection: ", intersection)
return Vector2(0.5, 0.5)
+func zoom_in() -> void:
+ _zoom_target -= _zoom_target_step
+
+func zoom_out() -> void:
+ _zoom_target += _zoom_target_step
+
# REQUIREMENTS
# * SS-31
func _unhandled_input(event : InputEvent):
- if event.is_action_pressed(_action_click):
+ if _mouse_over_viewport and event.is_action_pressed(_action_click):
# Check if the mouse is outside of bounds
if _map_mesh.is_valid_uv_coord(_mouse_pos_map):
var selected_index := MapSingleton.get_province_index_from_uv_coords(_mouse_pos_map)
@@ -159,9 +167,9 @@ func _unhandled_input(event : InputEvent):
push_warning("Drag being deactivated while already not active!")
_drag_active = false
elif event.is_action_pressed(_action_zoomin, true):
- _zoom_target -= _zoom_target_step
+ zoom_in()
elif event.is_action_pressed(_action_zoomout, true):
- _zoom_target += _zoom_target_step
+ zoom_out()
func _physics_process(delta : float):
_mouse_pos_viewport = get_viewport().get_mouse_position()
@@ -228,7 +236,7 @@ func _zoom_process(delta : float) -> void:
_camera.position.y = height
func _update_orientation() -> void:
- var dir := Vector3(0, -1, -exp(-_camera.position.y * 2.0 + 0.5))
+ var dir := Vector3(0, -1, -exp(-_camera.position.y - 1))
_camera.look_at(_camera.position + dir)
func _update_minimap_viewport() -> void:
@@ -254,4 +262,12 @@ func _on_minimap_clicked(pos_clicked : Vector2):
pos_clicked *= _map_mesh_dims
_camera.position.x = pos_clicked.x
_camera.position.z = pos_clicked.y
- _clamp_over_map() \ No newline at end of file
+ _clamp_over_map()
+
+func enable_processing() -> void:
+ set_process_unhandled_input(true)
+ set_physics_process(true)
+
+func disable_processing() -> void:
+ set_process_unhandled_input(false)
+ set_physics_process(false)
diff --git a/game/src/GameSession/MapView.tscn b/game/src/GameSession/MapView.tscn
index 93fc162..c8934c5 100644
--- a/game/src/GameSession/MapView.tscn
+++ b/game/src/GameSession/MapView.tscn
@@ -2,14 +2,15 @@
[ext_resource type="Script" path="res://src/GameSession/MapView.gd" id="1_exccw"]
[ext_resource type="Shader" path="res://src/GameSession/TerrainMap.gdshader" id="1_upocn"]
-[ext_resource type="Texture2D" uid="uid://cmw0pvjthnn8c" path="res://art/terrain/terrain.png" id="3_l8pnf"]
+[ext_resource type="Texture2D" uid="uid://ckf222w5usrsu" path="res://art/terrain/farmlands.png" id="3_47mq1"]
[sub_resource type="ShaderMaterial" id="ShaderMaterial_tayeg"]
render_priority = 0
shader = ExtResource("1_upocn")
shader_parameter/hover_index = null
shader_parameter/selected_index = null
-shader_parameter/terrain_tex = ExtResource("3_l8pnf")
+shader_parameter/terrain_tile_factor = null
+shader_parameter/farmlands_tex = ExtResource("3_47mq1")
[sub_resource type="MapMesh" id="MapMesh_3gtsd"]
@@ -21,6 +22,7 @@ _map_mesh_instance = NodePath("MapMeshInstance")
[node name="MapCamera" type="Camera3D" parent="."]
transform = Transform3D(1, 0, 0, 0, 0.707107, 0.707107, 0, -0.707107, 0.707107, 0.25, 1.5, -2.75)
+near = 0.01
[node name="MapMeshInstance" type="MeshInstance3D" parent="."]
editor_description = "FS-343"
diff --git a/game/src/GameSession/ProvinceOverviewPanel.gd b/game/src/GameSession/ProvinceOverviewPanel.gd
index 434f6b1..4615df9 100644
--- a/game/src/GameSession/ProvinceOverviewPanel.gd
+++ b/game/src/GameSession/ProvinceOverviewPanel.gd
@@ -1,22 +1,33 @@
extends Panel
@export var _province_name_label : Label
+@export var _region_name_label : Label
-@export var province_identifier: String = "":
- get: return province_identifier
+var _province_identifier: String = "":
+ get: return _province_identifier
set(v):
- province_identifier = v
+ _province_identifier = v
update_info()
+const _name_suffix : String = "_NAME"
+
func _ready():
update_info()
func update_info() -> void:
- _province_name_label.text = province_identifier + "_NAME"
- visible = not province_identifier.is_empty()
+ if _province_identifier:
+ _province_name_label.text = _province_identifier + _name_suffix
+ var region_identifier := MapSingleton.get_region_identifier_from_province_identifier(_province_identifier)
+ if region_identifier:
+ _region_name_label.text = region_identifier + _name_suffix
+ else:
+ _region_name_label.text = "NO REGION"
+ show()
+ else:
+ hide()
func _on_province_selected(identifier : String) -> void:
- province_identifier = identifier
+ _province_identifier = identifier
-func _on_button_pressed() -> void:
- province_identifier = ""
+func _on_close_button_pressed() -> void:
+ _province_identifier = ""
diff --git a/game/src/GameSession/ProvinceOverviewPanel.tscn b/game/src/GameSession/ProvinceOverviewPanel.tscn
index 2df95fb..7d21edc 100644
--- a/game/src/GameSession/ProvinceOverviewPanel.tscn
+++ b/game/src/GameSession/ProvinceOverviewPanel.tscn
@@ -2,7 +2,7 @@
[ext_resource type="Script" path="res://src/GameSession/ProvinceOverviewPanel.gd" id="1_3n8k5"]
-[node name="ProvinceOverviewPanel" type="Panel" node_paths=PackedStringArray("_province_name_label")]
+[node name="ProvinceOverviewPanel" type="Panel" node_paths=PackedStringArray("_province_name_label", "_region_name_label")]
editor_description = "UI-56"
anchors_preset = 2
anchor_top = 1.0
@@ -12,6 +12,7 @@ offset_right = 200.0
grow_vertical = 0
script = ExtResource("1_3n8k5")
_province_name_label = NodePath("VBoxContainer/ProvinceName")
+_region_name_label = NodePath("VBoxContainer/RegionName")
[node name="VBoxContainer" type="VBoxContainer" parent="."]
layout_mode = 1
@@ -29,7 +30,13 @@ layout_mode = 2
text = "PROVINCE_NAME"
vertical_alignment = 1
-[node name="Button" type="Button" parent="."]
+[node name="RegionName" type="Label" parent="VBoxContainer"]
+editor_description = "UI-58"
+layout_mode = 2
+text = "REGION_NAME"
+vertical_alignment = 1
+
+[node name="CloseButton" type="Button" parent="."]
custom_minimum_size = Vector2(30, 30)
layout_mode = 1
anchors_preset = -1
@@ -39,4 +46,4 @@ anchor_bottom = 0.103333
grow_horizontal = 0
text = "X"
-[connection signal="pressed" from="Button" to="." method="_on_button_pressed"]
+[connection signal="pressed" from="CloseButton" to="." method="_on_close_button_pressed"]
diff --git a/game/src/GameSession/TerrainMap.gdshader b/game/src/GameSession/TerrainMap.gdshader
index 7aca0f9..9ce1a24 100644
--- a/game/src/GameSession/TerrainMap.gdshader
+++ b/game/src/GameSession/TerrainMap.gdshader
@@ -2,16 +2,18 @@ shader_type spatial;
render_mode unshaded;
-// Cosmetic terrain texture
-uniform sampler2D terrain_tex: source_color, repeat_enable, filter_linear;
+// Cosmetic farmlands terrain texture
+uniform sampler2D farmlands_tex: source_color, repeat_enable, filter_linear;
// Province index texture
-uniform sampler2D province_index_tex : repeat_enable, filter_nearest;
+uniform sampler2D province_index_tex : source_color, repeat_enable, filter_nearest;
// Province colour texture
uniform sampler2D province_colour_tex: source_color, repeat_enable, filter_nearest;
// Index of the mouse over the map mesh
uniform uint hover_index;
// Index of the currently selected province
uniform uint selected_index;
+// The number of times the terrain textures should tile vertically
+uniform float terrain_tile_factor;
uvec2 vec2_to_uvec2(vec2 v) {
return uvec2(v * 255.0);
@@ -29,17 +31,39 @@ uint read_uint16(sampler2D tex, vec2 uv) {
return uvec2_to_uint(read_uvec2(tex, uv));
}
-void fragment() {
- uvec2 prov_idx_split = read_uvec2(province_index_tex, UV);
- uint prov_index = uvec2_to_uint(prov_idx_split);
+const vec3 water_colour = vec3(0, 0, 1);
+
+vec3 get_terrain_colour(vec2 uv, vec2 corner, vec2 half_pixel_size, vec2 terrain_uv) {
+ uvec2 index_split = read_uvec2(province_index_tex, fma(corner, half_pixel_size, uv));
+ uint index = uvec2_to_uint(index_split);
+ vec4 province_data = texelFetch(province_colour_tex, ivec2(index_split), 0);
+ vec3 province_colour = province_data.rgb;
+ float is_land = province_data.a;
+ vec3 farmlands_colour = texture(farmlands_tex, terrain_uv).rgb;
+ vec3 terrain_colour = mix(water_colour, farmlands_colour, is_land);
+ float mix_val = 0.4 + float(index == hover_index) * 0.2 + float(index == selected_index) * 0.2;
+ vec3 mixed_colour = mix(terrain_colour, province_colour, mix_val);
+ return mixed_colour;
+}
- // Boost prov_colour's contribution if it matches hover_colour or selected_colour
- float mix_val = 0.3 + float(prov_index == hover_index) * 0.3 + float(prov_index == selected_index) * 0.3;
- // Don't mix if the province index is 0
- mix_val *= float(prov_index != 0u);
+vec3 mix_terrain_colour(vec2 uv) {
+ vec2 map_size = vec2(textureSize(province_index_tex, 0));
+ vec2 pixel_offset = mod(fma(uv, map_size, vec2(0.5)), 1.0);
+ vec2 half_pixel_size = 0.49 / map_size;
- vec3 terrain_colour = texture(terrain_tex, UV).rgb;
- vec3 province_colour = texelFetch(province_colour_tex, ivec2(prov_idx_split), 0).rgb;
+ vec2 terrain_uv = uv;
+ terrain_uv.x *= map_size.x / map_size.y;
+ terrain_uv *= terrain_tile_factor;
- ALBEDO = mix(terrain_colour, province_colour, mix_val);
+ return mix(
+ mix(get_terrain_colour(uv, vec2(-1, -1), half_pixel_size, terrain_uv),
+ get_terrain_colour(uv, vec2(+1, -1), half_pixel_size, terrain_uv), pixel_offset.x),
+ mix(get_terrain_colour(uv, vec2(-1, +1), half_pixel_size, terrain_uv),
+ get_terrain_colour(uv, vec2(+1, +1), half_pixel_size, terrain_uv), pixel_offset.x),
+ pixel_offset.y);
+}
+
+void fragment() {
+ vec3 terrain_colour = mix_terrain_colour(UV);
+ ALBEDO = terrain_colour;
}