aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/contribution/system-requirements.md7
-rw-r--r--docs/simulation/calculations.yaml60
m---------extension/deps/openvic-simulation0
-rw-r--r--extension/src/openvic-extension/classes/GFXPieChartTexture.hpp73
-rw-r--r--extension/src/openvic-extension/singletons/LoadLocalisation.cpp2
-rw-r--r--extension/src/openvic-extension/singletons/MenuSingleton.cpp27
-rw-r--r--extension/src/openvic-extension/singletons/PopulationMenu.cpp21
-rw-r--r--extension/src/openvic-extension/singletons/SoundSingleton.cpp53
-rw-r--r--extension/src/openvic-extension/singletons/SoundSingleton.hpp15
-rw-r--r--game/src/Game/Autoload/SoundManager.gd3
-rw-r--r--game/src/Game/GameSession/NationManagementScreen/BudgetMenu.gd2
-rw-r--r--game/src/Game/GameSession/NationManagementScreen/PopulationMenu.gd2
-rw-r--r--game/src/Game/GameSession/ProvinceIndexSampler.gdshaderinc2
-rw-r--r--game/src/Game/GameSession/ProvinceOverviewPanel.gd17
-rw-r--r--game/src/Game/GameSession/Topbar.gd2
-rw-r--r--game/src/Game/Model/XACLoader.gd4
-rw-r--r--game/src/Game/Model/XSMLoader.gd4
-rw-r--r--game/src/Game/MusicConductor/MusicConductor.gd18
-rw-r--r--game/src/Game/MusicConductor/MusicPlayer.gd2
19 files changed, 191 insertions, 123 deletions
diff --git a/docs/contribution/system-requirements.md b/docs/contribution/system-requirements.md
index 9fd7719..9e1f16b 100644
--- a/docs/contribution/system-requirements.md
+++ b/docs/contribution/system-requirements.md
@@ -27,6 +27,7 @@ If you're installing Visual Studio 2015, ensure you choose custom and pick C++ a
### Linux Requirements
* GCC 12.3+
+ * For Ubuntu 22.04, you need to manually install g++-12 and set that version as active
#### Debian/Ubuntu Linux Install
```sh
@@ -43,7 +44,11 @@ apt-get install \
libpulse-dev \
libudev-dev \
libxi-dev \
- libxrandr-dev
+ libxrandr-dev \
+ g++-12
+
+update-alternatives --install /usr/bin/g++ g++ /usr/bin/g++-12 12
+update-alternatives --set g++ /usr/bin/g++-12
```
#### Arch Linux Requirements
diff --git a/docs/simulation/calculations.yaml b/docs/simulation/calculations.yaml
index e9de17a..96c045a 100644
--- a/docs/simulation/calculations.yaml
+++ b/docs/simulation/calculations.yaml
@@ -51,7 +51,44 @@ Economy:
formula: MGQ * GOLD_TO_CASH_RATE
MGQ: Money good quanity = total production of goods with money = yes
GOLD_TO_CASH_RATE: defines.country.GOLD_TO_CASH_RATE
+ Overseas maintenance:
+ See: https://github.com/schombert/Project-Alice/blob/b7889a9c302feed851c039f1a98d73b75be3215a/docs/rules.md#overseas-penalty
Production:
+ Resource gathering operation:
+ base workforce: defined in common/production_types.txt as workforce
+ rgo size:
+ formula: floor(1.5 * ceil(n_workers_in_state / (base workforce * (1 + terrain rgo size modifier)))
+ n_workers_in_state: Total size of POPs in state where POP type is in employees (common/production_types.txt).
+ note: Calculated when starting a new game.
+ Output:
+ formula: base output * rgo size * (1 + overseas penalty) * throughput from workers * (1 + throughput modifier) * (1 + output from workers) * (1 + output modifier)
+ base output: defined in common/production_types.txt
+ overseas penalty:
+ if province is 'overseas': overseas penalty (see economy > budget > overseas maintenance)
+ else: 0
+ workforce:
+ formula: base workforce * rgo size * (1 + rgo size modifier)
+ base workforce: Defined in common/production_types.txt as workforce.
+ rgo size modifier: sum of rgo size modifiers + sum of farm size modifiers (if farm=yes) + sum of mine size modifiers (if mine=yes)
+ job portion of workforce:
+ formula: employees with job / workforce
+ employees with job: Number of POPs employed with the job. Job being an entry in employees in common/production_types.txt.
+ effect from job:
+ formula:
+ if effect_multiplier == 1: effect_multiplier * employees with job / workforce
+ else: effect_multiplier * min(employees with job / workforce, amount)
+ effect_multiplier: defined in common/production_types.txt as part of employees (default 1)
+ amount: defined in common/production_types.txt (default 1)
+ throughput from workers: sum of effect from job for each job with `effect = throughput`
+ output from workers: sum of effect from job for each job with `effect = output`
+ throughput modifier: sum of throughput modifiers (including from owner job)
+ output modifier: sum of output modifiers (including from owner job)
+ modifier from owner job:
+ formula: effect_multiplier * n_owners_in_state / n_pops_in_state
+ effect_multiplier: defined in common/production_types.txt as part of owner (default 1)
+ n_owners_in_state: total size of owner POPs in state
+ n_pops_in_state: total size of all POPs in state
+ note: included in throughput or output modifier
Human resource management:
Vacancies: max employees - employees count
Maximum employees hired per day:
@@ -68,6 +105,17 @@ Economy:
absolute minimum: 50 (hardcoded value)
relative minimum: floor(defines.economy.EMPLOYMENT_HIRE_LOWEST * employees count)
Military:
+ Mobilisation:
+ Mobilised regiment limit:
+ formula: max(MIN_MOBILIZE_LIMIT, regular regiment count) * (1 + mobilization_impact)
+ MIN_MOBILIZE_LIMIT: defines.country.MIN_MOBILIZE_LIMIT
+ regular regiment count: number of deployed non-mobilised regiments
+ mobilization_impact: sum of mobilization_impact modifiers
+ Mobilisation throughput effect:
+ formula: -1 * mobilisation_size * mobilisation_economy_impact
+ -1: mobilisation reduces throughput for RGOs and factories
+ mobilisation_size: sum of mobilisation_size modifiers
+ mobilisation_economy_impact: sum of mobilisation_economy_impact modifiers
Maximum regiments per soldier POP:
if POP size < defines.military.POP_MIN_SIZE_FOR_REGIMENT: 0
else:
@@ -109,7 +157,7 @@ Military:
else:
formula: x^(1+i)
x: received supply
- i: 0 based index of regiment in the army
+ i: 0-based index of regiment in the army
trunc(... * 1000): Truncated to 1/1000th.
min(..., 1000): Capped at max strength.
Regiment reinforcements:
@@ -165,10 +213,10 @@ POPs:
RGO wages:
Owners:
if minimum worker wages > normal worker wage:
- formula: (RGO income - total worker income) * owner POP size / n_owners * (1 - effective tax)
+ formula: (RGO income - total worker income) * owner POP size / n_owners_in_state * (1 - effective tax)
else:
- formula: RGO income * min(0.5, 2 * n_owners / n_workers) * owner POP size / n_owners * (1 - effective tax)
- n_owners: total size of owner POPs in state
+ formula: RGO income * min(0.5, 2 * n_owners_in_state / n_workers) * owner POP size / n_owners_in_state * (1 - effective tax)
+ n_owners_in_state: total size of owner POPs in state
n_workers: number of employed workers in RGO
min(0.5: Hardcoded maximum of half the RGO income.
2 *: Hardcoded value.
@@ -178,8 +226,8 @@ POPs:
if minimum wage > normal wage:
formula: min(minimum wage, RGO income * employed workers in POP / total non-slave employed workers) * (1 - effective tax)
else:
- formula: RGO income * max(0.5, 1 - 2 * n_owners / n_workers) * employed workers in POP / total non-slave employed workers * (1 - effective tax)
- n_owners: total size of owner POPs in state
+ formula: RGO income * max(0.5, 1 - 2 * n_owners_in_state / n_workers) * employed workers in POP / total non-slave employed workers * (1 - effective tax)
+ n_owners_in_state: total size of owner POPs in state
n_workers: number of employed workers in RGO
max(0.5: Hardcoded minimum of half the RGO income.
2 *: Hardcoded value.
diff --git a/extension/deps/openvic-simulation b/extension/deps/openvic-simulation
-Subproject 7fe8ba2b3bd3bafad374a691280bbf5102b5867
+Subproject 8defcd5daa1acd2c61aa1cd0a26478d472fed9b
diff --git a/extension/src/openvic-extension/classes/GFXPieChartTexture.hpp b/extension/src/openvic-extension/classes/GFXPieChartTexture.hpp
index 69330a4..50616d9 100644
--- a/extension/src/openvic-extension/classes/GFXPieChartTexture.hpp
+++ b/extension/src/openvic-extension/classes/GFXPieChartTexture.hpp
@@ -11,6 +11,7 @@ namespace OpenVic {
GDCLASS(GFXPieChartTexture, godot::ImageTexture)
public:
+ using godot_pie_chart_data_t = godot::TypedArray<godot::Dictionary>;
struct slice_t {
godot::String name;
godot::Color colour;
@@ -29,56 +30,44 @@ namespace OpenVic {
godot::Error _generate_pie_chart_image();
- protected:
- static void _bind_methods();
-
public:
- GFXPieChartTexture();
-
- // Position must be centred and normalised so that coords are in [-1, 1].
- slice_t const* get_slice(godot::Vector2 const& position) const;
-
- using godot_pie_chart_data_t = godot::TypedArray<godot::Dictionary>;
-
- /* Set slices given an Array of Dictionaries, each with the following key-value entries:
- * - colour: Color
- * - weight: float */
- godot::Error set_slices_array(godot_pie_chart_data_t const& new_slices);
-
/* Generate slice data from a distribution of objects satisfying HasGetIdentifierAndGetColour, sorted by their weight.
* The resulting Array of Dictionaries can be used as an argument for set_slices_array. */
template<typename Container>
static godot_pie_chart_data_t distribution_to_slices_array(Container const& dist)
requires(
(
- /* fixed_point_map_t<T const*>, T derived from HasIdentifierAndColour */
- utility::is_specialization_of_v<Container, tsl::ordered_map> &&
- HasGetIdentifierAndGetColour<std::remove_pointer_t<typename Container::key_type>> &&
- std::is_same_v<typename Container::mapped_type, fixed_point_t>
- ) || (
- /* IndexedMap<T, fixed_point_t>, T derived from HasIdentifierAndColour */
- utility::is_specialization_of_v<Container, IndexedMap> &&
- HasGetIdentifierAndGetColour<typename Container::key_type> &&
- std::is_same_v<typename Container::value_t, fixed_point_t>
+ /* ordered_map<T const*, mapped_type>, T derived from HasIdentifierAndColour */
+ utility::is_specialization_of_v<Container, tsl::ordered_map>
+ /* IndexedMap<T, mapped_type>, T derived from HasIdentifierAndColour */
+ || utility::is_specialization_of_v<Container, IndexedMap>
)
+ && HasGetIdentifierAndGetColour<std::remove_pointer_t<typename Container::key_type>>
+ && std::convertible_to<typename Container::mapped_type, float>
) {
using namespace godot;
+
using key_type = std::remove_pointer_t<typename Container::key_type>;
- using entry_t = std::pair<key_type const*, fixed_point_t>;
+ using entry_t = std::pair<key_type const*, float>;
+
std::vector<entry_t> sorted_dist;
+ sorted_dist.reserve(dist.size());
if constexpr (utility::is_specialization_of_v<Container, tsl::ordered_map>) {
- sorted_dist.reserve(dist.size());
- for (entry_t const& entry : dist) {
- ERR_CONTINUE_MSG(
- entry.first == nullptr, vformat("Null distribution key with value %f", entry.second.to_float())
- );
- sorted_dist.push_back(entry);
+ for (auto const& [key, non_float_value] : dist) {
+ const float value = static_cast<float>(non_float_value);
+
+ ERR_CONTINUE_MSG(key == nullptr, vformat("Null distribution key with value %f", value));
+
+ if (value != 0.0f) {
+ sorted_dist.emplace_back(key, value);
+ }
}
} else {
for (size_t index = 0; index < dist.size(); ++index) {
- fixed_point_t const& value = dist[index];
- if (value != 0) {
+ const float value = static_cast<float>(dist[index]);
+
+ if (value != 0.0f) {
key_type const* key = &dist(index);
sorted_dist.emplace_back(key, value);
}
@@ -93,16 +82,30 @@ namespace OpenVic {
ERR_FAIL_COND_V(array.resize(sorted_dist.size()) != OK, {});
for (size_t idx = 0; idx < array.size(); ++idx) {
- auto const& [key, val] = sorted_dist[idx];
+ auto const& [key, value] = sorted_dist[idx];
Dictionary sub_dict;
sub_dict[_slice_identifier_key()] = Utilities::std_to_godot_string(key->get_identifier());
sub_dict[_slice_colour_key()] = Utilities::to_godot_color(key->get_colour());
- sub_dict[_slice_weight_key()] = val.to_float();
+ sub_dict[_slice_weight_key()] = value;
array[idx] = std::move(sub_dict);
}
return array;
}
+ protected:
+ static void _bind_methods();
+
+ public:
+ GFXPieChartTexture();
+
+ // Position must be centred and normalised so that coords are in [-1, 1].
+ slice_t const* get_slice(godot::Vector2 const& position) const;
+
+ /* Set slices given an Array of Dictionaries, each with the following key-value entries:
+ * - colour: Color
+ * - weight: float */
+ godot::Error set_slices_array(godot_pie_chart_data_t const& new_slices);
+
/* Create a GFXPieChartTexture using the specified GFX::PieChart. Returns nullptr if gfx_pie_chart fails. */
static godot::Ref<GFXPieChartTexture> make_gfx_pie_chart_texture(GFX::PieChart const* gfx_pie_chart);
diff --git a/extension/src/openvic-extension/singletons/LoadLocalisation.cpp b/extension/src/openvic-extension/singletons/LoadLocalisation.cpp
index 55073d6..fbd618c 100644
--- a/extension/src/openvic-extension/singletons/LoadLocalisation.cpp
+++ b/extension/src/openvic-extension/singletons/LoadLocalisation.cpp
@@ -34,7 +34,7 @@ Error LoadLocalisation::_load_file(String const& file_path, Ref<Translation> con
const Ref<FileAccess> file = FileAccess::open(file_path, FileAccess::ModeFlags::READ);
Error err = FileAccess::get_open_error();
ERR_FAIL_COND_V_MSG(
- err != OK || file.is_null(), err == OK ? FAILED : err, vformat("Failed to open localisation file: %s", file_path)
+ err != OK || file.is_null(), err == OK ? FAILED : err, vformat("Failed to open localisation file: %s", file_path)
);
int line_number = 0;
while (!file->eof_reached()) {
diff --git a/extension/src/openvic-extension/singletons/MenuSingleton.cpp b/extension/src/openvic-extension/singletons/MenuSingleton.cpp
index c4b704d..81582c2 100644
--- a/extension/src/openvic-extension/singletons/MenuSingleton.cpp
+++ b/extension/src/openvic-extension/singletons/MenuSingleton.cpp
@@ -2,8 +2,10 @@
#include <godot_cpp/variant/utility_functions.hpp>
+#include <openvic-simulation/economy/GoodDefinition.hpp>
#include <openvic-simulation/GameManager.hpp>
-#include <openvic-simulation/misc/Modifier.hpp>
+#include <openvic-simulation/modifier/Modifier.hpp>
+#include <openvic-simulation/types/fixed_point/FixedPoint.hpp>
#include "openvic-extension/classes/GFXPieChartTexture.hpp"
#include "openvic-extension/classes/GUINode.hpp"
@@ -434,6 +436,10 @@ Dictionary MenuSingleton::get_province_info_from_index(int32_t index) const {
static const StringName province_info_controller_key = "controller";
static const StringName province_info_rgo_name_key = "rgo_name";
static const StringName province_info_rgo_icon_key = "rgo_icon";
+ static const StringName province_info_rgo_total_employees_key = "rgo_total_employees";
+ static const StringName province_info_rgo_employment_percentage_key = "rgo_employment_percentage";
+ static const StringName province_info_rgo_output_quantity_yesterday_key = "rgo_output_quantity_yesterday";
+ static const StringName province_info_rgo_revenue_yesterday_key = "rgo_revenue_yesterday";
static const StringName province_info_crime_name_key = "crime_name";
static const StringName province_info_crime_icon_key = "crime_icon";
static const StringName province_info_total_population_key = "total_population";
@@ -472,10 +478,21 @@ Dictionary MenuSingleton::get_province_info_from_index(int32_t index) const {
ret[province_info_controller_key] = Utilities::std_to_godot_string(controller->get_identifier());
}
- GoodDefinition const* rgo = province->get_rgo();
- if (rgo != nullptr) {
- ret[province_info_rgo_name_key] = Utilities::std_to_godot_string(rgo->get_identifier());
- ret[province_info_rgo_icon_key] = static_cast<int32_t>(rgo->get_index());
+ ResourceGatheringOperation const& rgo = province->get_rgo();
+ ret[province_info_rgo_output_quantity_yesterday_key] = rgo.get_output_quantity_yesterday().to_float();
+ ret[province_info_rgo_revenue_yesterday_key] = rgo.get_revenue_yesterday().to_float();
+ ret[province_info_rgo_total_employees_key] = rgo.get_total_employees_count_cache();
+ const Pop::pop_size_t max_employee_count = rgo.get_max_employee_count_cache();
+ if (max_employee_count == 0) {
+ ret[province_info_rgo_employment_percentage_key] = 100.0f;
+ } else {
+ ret[province_info_rgo_employment_percentage_key] = (rgo.get_total_employees_count_cache() * fixed_point_t::_100() / max_employee_count).to_float_rounded();
+ }
+
+ GoodDefinition const* const rgo_good = province->get_rgo_good();
+ if (rgo_good != nullptr) {
+ ret[province_info_rgo_name_key] = Utilities::std_to_godot_string(rgo_good->get_identifier());
+ ret[province_info_rgo_icon_key] = static_cast<int32_t>(rgo_good->get_index());
}
Crime const* crime = province->get_crime();
diff --git a/extension/src/openvic-extension/singletons/PopulationMenu.cpp b/extension/src/openvic-extension/singletons/PopulationMenu.cpp
index 1bef050..700c57f 100644
--- a/extension/src/openvic-extension/singletons/PopulationMenu.cpp
+++ b/extension/src/openvic-extension/singletons/PopulationMenu.cpp
@@ -4,6 +4,7 @@
#include <openvic-simulation/DefinitionManager.hpp>
#include <openvic-simulation/InstanceManager.hpp>
+#include <openvic-simulation/types/fixed_point/FixedPoint.hpp>
#include "openvic-extension/classes/GFXPieChartTexture.hpp"
#include "openvic-extension/classes/GUINode.hpp"
@@ -160,7 +161,7 @@ TypedArray<Dictionary> MenuSingleton::get_population_menu_province_list_rows(int
return --count_counter > 0;
}
- return true;
+ return true;
}
} entry_visitor { *this, start, count };
@@ -366,7 +367,7 @@ Error MenuSingleton::_population_menu_update_pops() {
if (province_entry != nullptr && province_entry->selected) {
for (Pop const& pop : province_entry->province.get_pops()) {
population_menu.pops.push_back(&pop);
- population_menu_t::pop_filter_t& filter = population_menu.pop_filters[&pop.get_type()];
+ population_menu_t::pop_filter_t& filter = population_menu.pop_filters[pop.get_type()];
filter.count += pop.get_size();
// TODO - set filter.promotion_demotion_change
}
@@ -387,7 +388,7 @@ Error MenuSingleton::_population_menu_update_filtered_pops() {
population_menu.vote_distribution.clear();
for (Pop const* pop : population_menu.pops) {
- if (population_menu.pop_filters[&pop->get_type()].selected) {
+ if (population_menu.pop_filters[pop->get_type()].selected) {
population_menu.filtered_pops.push_back(pop);
}
}
@@ -395,12 +396,12 @@ Error MenuSingleton::_population_menu_update_filtered_pops() {
for (Pop const* pop : population_menu.filtered_pops) {
const fixed_point_t pop_size = fixed_point_t::parse(pop->get_size());
- population_menu.workforce_distribution[&pop->get_type()] += pop->get_size();
- population_menu.religion_distribution[&pop->get_religion()] += pop->get_size();
- population_menu.ideology_distribution += pop->get_ideologies() * fixed_point_t::parse(pop->get_size());
- population_menu.culture_distribution[&pop->get_culture()] += pop->get_size();
- population_menu.issue_distribution += pop->get_issues() * fixed_point_t::parse(pop->get_size());
- population_menu.vote_distribution += pop->get_votes() * fixed_point_t::parse(pop->get_size());
+ population_menu.workforce_distribution[pop->get_type()] += pop_size;
+ population_menu.religion_distribution[&pop->get_religion()] += pop_size;
+ population_menu.ideology_distribution += pop->get_ideologies() * pop_size;
+ population_menu.culture_distribution[&pop->get_culture()] += pop_size;
+ population_menu.issue_distribution += pop->get_issues() * pop_size;
+ population_menu.vote_distribution += pop->get_votes() * pop_size;
}
normalise_fixed_point_map(population_menu.workforce_distribution);
@@ -678,7 +679,7 @@ TypedArray<Dictionary> MenuSingleton::get_population_menu_pop_rows(int32_t start
Dictionary pop_dict;
pop_dict[pop_size_key] = pop->get_size();
- pop_dict[pop_type_icon_key] = pop->get_type().get_sprite();
+ pop_dict[pop_type_icon_key] = pop->get_type()->get_sprite();
pop_dict[pop_culture_key] = Utilities::std_to_godot_string(pop->get_culture().get_identifier());
pop_dict[pop_religion_icon_key] = pop->get_religion().get_icon();
if (pop->get_location() != nullptr) {
diff --git a/extension/src/openvic-extension/singletons/SoundSingleton.cpp b/extension/src/openvic-extension/singletons/SoundSingleton.cpp
index b9a2ee3..4ba62f2 100644
--- a/extension/src/openvic-extension/singletons/SoundSingleton.cpp
+++ b/extension/src/openvic-extension/singletons/SoundSingleton.cpp
@@ -28,32 +28,32 @@ void SoundSingleton::_bind_methods() {
OV_BIND_METHOD(SoundSingleton::load_music);
OV_BIND_METHOD(SoundSingleton::get_song, {"song_name"});
OV_BIND_METHOD(SoundSingleton::get_song_list);
-
+
ADD_PROPERTY(PropertyInfo(
Variant::ARRAY,
- "song_list", PROPERTY_HINT_ARRAY_TYPE,
- "AudioStreamMP3"),
+ "song_list", PROPERTY_HINT_ARRAY_TYPE,
+ "AudioStreamMP3"),
"", "get_song_list");
OV_BIND_METHOD(SoundSingleton::load_sounds);
OV_BIND_METHOD(SoundSingleton::get_sound_stream, {"sound_name"});
OV_BIND_METHOD(SoundSingleton::get_sound_base_volume, {"sound_name"});
OV_BIND_METHOD(SoundSingleton::get_sound_list);
-
+
ADD_PROPERTY(PropertyInfo(
Variant::ARRAY,
- "sound_list",
+ "sound_list",
PROPERTY_HINT_ARRAY_TYPE,
"AudioStreamWAV"),
"", "get_sound_list");
-
+
OV_BIND_METHOD(SoundSingleton::load_title_theme);
OV_BIND_METHOD(SoundSingleton::get_title_theme);
-
+
ADD_PROPERTY(PropertyInfo(
Variant::STRING,
"title_theme"),
- "", "get_title_theme");
+ "", "get_title_theme");
}
@@ -71,15 +71,14 @@ SoundSingleton::~SoundSingleton() {
_singleton = nullptr;
}
-
-//Load a sound from the path
+//Load a sound from the path
Ref<AudioStreamMP3> SoundSingleton::_load_godot_mp3(String const& path) const {
const Ref<FileAccess> file = FileAccess::open(path, FileAccess::ModeFlags::READ);
-
+
Error err = FileAccess::get_open_error();
ERR_FAIL_COND_V_MSG(
err != OK || file.is_null(), nullptr,
- vformat("Failed to open mp3 file %s", path) //named %s, path,
+ vformat("Failed to open mp3 file %s", path) //named %s, path,
);
const PackedByteArray data = file->get_buffer(file->get_length());
@@ -92,7 +91,7 @@ Ref<AudioStreamMP3> SoundSingleton::_load_godot_mp3(String const& path) const {
}
//slices a path down to after the base_folder, keeps the extension
-//this is because the defines refer to audio files using this format,
+//this is because the defines refer to audio files using this format,
//so we might as well use this form as the key for the "name"->audiostream map
String SoundSingleton::to_define_file_name(String const& path, std::string_view const& base_folder) const {
String name = path.replace("\\","/");
@@ -132,7 +131,7 @@ bool SoundSingleton::load_title_theme(){
Dataloader::path_vector_t music_files = game_singleton->get_dataloader()
.lookup_files_in_dir_recursive(music_directory, ".mp3");
-
+
if(music_files.size() < 1){
Logger::error("failed to load title theme: no files in music directory");
}
@@ -159,7 +158,7 @@ bool SoundSingleton::load_title_theme(){
}
if(!ret) Logger::error("Failed to load title theme!");
-
+
return ret;
}
@@ -167,13 +166,13 @@ bool SoundSingleton::load_music() {
GameSingleton const* game_singleton = GameSingleton::get_singleton();
ERR_FAIL_NULL_V_MSG(game_singleton, false, vformat("Error retrieving GameSingleton"));
-
+
static constexpr std::string_view music_directory = "music";
bool ret = true;
Dataloader::path_vector_t music_files = game_singleton->get_dataloader()
.lookup_files_in_dir_recursive(music_directory, ".mp3");
-
+
if(music_files.size() < 1){
Logger::error("failed to load music: no files in music directory");
ret = false;
@@ -190,7 +189,6 @@ bool SoundSingleton::load_music() {
continue; //don't try to append a null pointer to the list
}
song_list.append(name);
-
}
return ret;
@@ -209,7 +207,7 @@ Ref<AudioStreamWAV> SoundSingleton::get_sound(String const& path){
ERR_FAIL_NULL_V_MSG(
sound, nullptr,
- vformat("Failed to load sound file %s", path) //named %s, path,
+ vformat("Failed to load sound file %s", path) //named %s, path
);
sfx.emplace(std::move(name), sound);
@@ -256,14 +254,14 @@ bool SoundSingleton::load_sounds() {
for(SoundEffect const& sound_inst : sound_manager.get_sound_effects()){
std::string folder_path = StringUtils::append_string_views(sound_directory, "/", sound_inst.get_file());
fs::path full_path = game_singleton->get_dataloader().lookup_file(folder_path, false);
-
+
//UI_Cavalry_Selected.wav doesn't exist (paradox mistake, UI_Cavalry_Select.wav does), just keep going
//the define its associated with also isn't used in game
if(full_path.empty()){
Logger::warning("The sound define ",sound_inst.get_identifier()," points to an non-existing file ", folder_path);
continue;
}
-
+
Ref<AudioStreamWAV> stream = get_sound(std_to_godot_string(full_path.string()));
if(stream.is_null()){
Logger::error("failed to load sound ",sound_inst.get_identifier()," at path ",full_path);
@@ -286,14 +284,13 @@ bool SoundSingleton::load_sounds() {
Ref<AudioStreamWAV> SoundSingleton::_load_godot_wav(String const& path) const {
const Ref<FileAccess> file = FileAccess::open(path, FileAccess::ModeFlags::READ);
-
+
Error err = FileAccess::get_open_error();
ERR_FAIL_COND_V_MSG(
err != OK || file.is_null(), nullptr,
vformat("Failed to open wav file %s", path)
);
-
Ref<AudioStreamWAV> sound = Ref<AudioStreamWAV>();
sound.instantiate();
@@ -302,7 +299,6 @@ Ref<AudioStreamWAV> SoundSingleton::_load_godot_wav(String const& path) const {
int riff_size = std::min(static_cast<uint64_t>(file->get_32()), file->get_length());
String form_type = read_riff_str(file); //WAVE
-
//ie. 16, 24, 32 bit audio
int bits_per_sample = 0;
@@ -328,13 +324,13 @@ Ref<AudioStreamWAV> SoundSingleton::_load_godot_wav(String const& path) const {
int samplesPerSec = file->get_32();
int avgBytesPerSec = file->get_32();
int blockAlign = file->get_16();
-
+
bits_per_sample = file->get_16();
ERR_FAIL_COND_V_MSG(
bits_per_sample == 24 || bits_per_sample == 32, nullptr,
vformat("Unsupported wav file sample rate %s", bits_per_sample)
);
-
+
if(size > 16){
int extensionSize = file->get_16();
}
@@ -369,13 +365,12 @@ Ref<AudioStreamWAV> SoundSingleton::_load_godot_wav(String const& path) const {
break;
}
}
-
+
sound->set_mix_rate(samplesPerSec);
-
}
else if(id=="data"){
PackedByteArray audio_data = file->get_buffer(size);
-
+
if(bits_per_sample == 24 || bits_per_sample == 32){
//sound->set_data(to_16bit_wav_data(audio_data,bits_per_sample));
Logger::error("WAV file ",godot_to_std_string(path), " uses an unsupported sample rate ", bits_per_sample);
diff --git a/extension/src/openvic-extension/singletons/SoundSingleton.hpp b/extension/src/openvic-extension/singletons/SoundSingleton.hpp
index bfa03ea..67305a9 100644
--- a/extension/src/openvic-extension/singletons/SoundSingleton.hpp
+++ b/extension/src/openvic-extension/singletons/SoundSingleton.hpp
@@ -37,7 +37,7 @@ namespace OpenVic {
std::optional<godot::Ref<godot::AudioStreamWAV>> audioStream;
std::optional<fixed_point_t> volume;
};
- using sfx_define_map_t = deque_ordered_map<godot::StringName,sound_asset_t>;
+ using sfx_define_map_t = deque_ordered_map<godot::StringName,sound_asset_t>;
sfx_define_map_t sfx_define;
static constexpr std::string_view title_theme_name = "thecoronation_titletheme.mp3";
@@ -47,7 +47,7 @@ namespace OpenVic {
//property for gd scripts to access song names
godot::Array PROPERTY(song_list);
godot::String PROPERTY(title_theme);
-
+
//property for gd scripts to access sound names
godot::Array PROPERTY(sound_list);
@@ -61,26 +61,25 @@ namespace OpenVic {
godot::String to_define_file_name(godot::String const& path, std::string_view const& base_folder) const;
godot::String read_riff_str(godot::Ref<godot::FileAccess> const& file, int size=4) const;
-
+
private:
/* Loads AudioStreams (.mp3 or .wav) at runtime using godot's functions*/
godot::Ref<godot::AudioStreamMP3> _load_godot_mp3(godot::String const& path) const;
godot::Ref<godot::AudioStreamWAV> _load_godot_wav(godot::String const& path) const;
-
+
public:
//gets a song from the cache ('tracks' variable), or if not, then from the files using _load_godot_mp3
godot::Ref<godot::AudioStreamMP3> get_song(godot::String const& name);
godot::Ref<godot::AudioStreamWAV> get_sound(godot::String const& path);
-
+
//load the files into memory
bool load_music();
bool load_sounds();
bool load_title_theme();
-
+
//for sound effects, get the stream and relative volume it should play at from the sfx map
godot::Ref<godot::AudioStreamWAV> get_sound_stream(godot::String const& path);
float get_sound_base_volume(godot::String const& path);
};
-
-} \ No newline at end of file
+}
diff --git a/game/src/Game/Autoload/SoundManager.gd b/game/src/Game/Autoload/SoundManager.gd
index 01562f8..520afb1 100644
--- a/game/src/Game/Autoload/SoundManager.gd
+++ b/game/src/Game/Autoload/SoundManager.gd
@@ -41,7 +41,7 @@ func play_effect_stream(sound : AudioStream, volume : float = 1.0) -> void:
func play_effect(sound : String) -> void:
play(sound, "SFX")
-
+
func play_effect_compat(sfx : String, fallback : AudioStream=null) -> void:
var sound:AudioStreamWAV = SoundSingleton.get_sound_stream(sfx)
var volume:float = SoundSingleton.get_sound_base_volume(sfx)
@@ -53,4 +53,3 @@ func play_effect_compat(sfx : String, fallback : AudioStream=null) -> void:
play_effect_stream(fallback)
else:
push_warning("Failed to find sound %s" % sfx)
-
diff --git a/game/src/Game/GameSession/NationManagementScreen/BudgetMenu.gd b/game/src/Game/GameSession/NationManagementScreen/BudgetMenu.gd
index 021c9f2..45e91d3 100644
--- a/game/src/Game/GameSession/NationManagementScreen/BudgetMenu.gd
+++ b/game/src/Game/GameSession/NationManagementScreen/BudgetMenu.gd
@@ -203,7 +203,7 @@ func _update_info() -> void:
if _active:
if _gold_label:
- _gold_label.text = "%s¤" % GUINode.float_to_string_dp(_incVal - (_incVal % 7), 1)
+ _gold_label.text = "%s¤" % GUINode.float_to_string_dp(_incVal - (_incVal % 7), 1)
if _total_inc_label:
_total_inc_label.text = "%s¤" % GUINode.float_to_string_dp_dynamic(_incVal)
diff --git a/game/src/Game/GameSession/NationManagementScreen/PopulationMenu.gd b/game/src/Game/GameSession/NationManagementScreen/PopulationMenu.gd
index eb57387..b55d227 100644
--- a/game/src/Game/GameSession/NationManagementScreen/PopulationMenu.gd
+++ b/game/src/Game/GameSession/NationManagementScreen/PopulationMenu.gd
@@ -160,7 +160,7 @@ func _generate_province_list_row(index : int, type : MenuSingleton.ProvinceListE
return OK
func _setup_province_list() -> void:
- if not _province_listbox:
+ if not _province_listbox:
_province_listbox = get_gui_listbox_from_nodepath(^"./country_pop/pop_province_list")
if not _province_listbox:
diff --git a/game/src/Game/GameSession/ProvinceIndexSampler.gdshaderinc b/game/src/Game/GameSession/ProvinceIndexSampler.gdshaderinc
index 1adcd95..acebdc6 100644
--- a/game/src/Game/GameSession/ProvinceIndexSampler.gdshaderinc
+++ b/game/src/Game/GameSession/ProvinceIndexSampler.gdshaderinc
@@ -13,7 +13,7 @@ uvec3 vec3_to_uvec3(vec3 v) {
// (u, v) -> (province index bottom byte, province index top byte, terrain index byte)
uvec3 read_uvec3(vec2 uv) {
uv *= province_shape_subdivisions;
- vec2 subdivision_coords = mod(floor(uv), province_shape_subdivisions);
+ vec2 subdivision_coords = mod(floor(uv), province_shape_subdivisions);
float idx = subdivision_coords.x + subdivision_coords.y * province_shape_subdivisions.x;
return vec3_to_uvec3(texture(province_shape_tex, vec3(uv, idx)).rgb);
}
diff --git a/game/src/Game/GameSession/ProvinceOverviewPanel.gd b/game/src/Game/GameSession/ProvinceOverviewPanel.gd
index 66fd463..a9f0ae2 100644
--- a/game/src/Game/GameSession/ProvinceOverviewPanel.gd
+++ b/game/src/Game/GameSession/ProvinceOverviewPanel.gd
@@ -240,6 +240,10 @@ func _update_info() -> void:
const _province_info_controller_key : StringName = &"controller"
const _province_info_rgo_name_key : StringName = &"rgo_name"
const _province_info_rgo_icon_key : StringName = &"rgo_icon"
+ const _province_info_rgo_total_employees_key : StringName = &"rgo_total_employees"
+ const _province_info_rgo_employment_percentage_key : StringName = &"rgo_employment_percentage"
+ const _province_info_rgo_output_quantity_yesterday_key : StringName = &"rgo_output_quantity_yesterday"
+ const _province_info_rgo_revenue_yesterday_key : StringName = &"rgo_revenue_yesterday"
const _province_info_crime_name_key : StringName = &"crime_name"
const _province_info_crime_icon_key : StringName = &"crime_icon"
const _province_info_total_population_key : StringName = &"total_population"
@@ -305,22 +309,19 @@ func _update_info() -> void:
_rgo_icon.set_icon_index(_province_info.get(_province_info_rgo_icon_key, -1) + 2)
if _rgo_produced_label:
- # TODO - replace name with amount produced
- _rgo_produced_label.text = _province_info.get(_province_info_rgo_name_key, _province_info_rgo_name_key + _missing_suffix)
+ _rgo_produced_label.text = GUINode.float_to_string_dp(_province_info.get(_province_info_rgo_output_quantity_yesterday_key, 0), 3)
if _rgo_income_label:
- # TODO - add £ sign and replace placeholder with actual value
- _rgo_income_label.text = "%s¤" % GUINode.float_to_string_dp(12.34567, 3)
+ _rgo_income_label.text = "%s¤" % GUINode.float_to_string_dp(_province_info.get(_province_info_rgo_revenue_yesterday_key, 0), 3)
if _rgo_employment_percentage_icon:
- pass
+ _rgo_employment_percentage_icon.set_icon_index(int(_province_info.get(_province_info_rgo_employment_percentage_key, 0) / 10) + 1)
if _rgo_employment_population_label:
- # TODO - replace placeholder with actual value
- _rgo_employment_population_label.text = GUINode.int_to_string_suffixed(_province_info.get(_province_info_total_population_key, 0) / 10)
+ _rgo_employment_population_label.text = GUINode.int_to_string_suffixed(_province_info.get(_province_info_rgo_total_employees_key, 0))
if _rgo_employment_percentage_label:
- pass
+ _rgo_employment_percentage_label.text = "%d%%" % _province_info.get(_province_info_rgo_employment_percentage_key, 0)
if _crime_name_label:
_crime_name_label.text = _province_info.get(_province_info_crime_name_key, "")
diff --git a/game/src/Game/GameSession/Topbar.gd b/game/src/Game/GameSession/Topbar.gd
index 2f1fdbf..aecdced 100644
--- a/game/src/Game/GameSession/Topbar.gd
+++ b/game/src/Game/GameSession/Topbar.gd
@@ -570,7 +570,7 @@ func _update_speed_controls() -> void:
var paused : bool = MenuSingleton.is_paused()
var speed : int = MenuSingleton.get_speed()
- # TODO - decide whether to disable these or not
+ # TODO - decide whether to disable these or not
# (they don't appear to get disabled in the base game)
#if _speed_up_button:
# _speed_up_button.disabled = not MenuSingleton.can_increase_speed()
diff --git a/game/src/Game/Model/XACLoader.gd b/game/src/Game/Model/XACLoader.gd
index 6b12cf2..c16d1cd 100644
--- a/game/src/Game/Model/XACLoader.gd
+++ b/game/src/Game/Model/XACLoader.gd
@@ -197,13 +197,13 @@ static func _load_xac_model(source_file : String, is_unit : bool) -> Node3D:
break
skinning_chunk_ind += 1
if skinning_chunk_ind >= len(skinningChunks):
- skinning_chunk_ind = 1
+ skinning_chunk_ind = 1
applyVertexWeights = false
var meshInstance : MeshInstance3D = MeshInstance3D.new()
node.add_child(meshInstance)
meshInstance.owner = node
-
+
#stop the culling of units near the tops of screens
meshInstance.extra_cull_margin = EXTRA_CULL_MARGIN
diff --git a/game/src/Game/Model/XSMLoader.gd b/game/src/Game/Model/XSMLoader.gd
index bc85f9b..e1706ae 100644
--- a/game/src/Game/Model/XSMLoader.gd
+++ b/game/src/Game/Model/XSMLoader.gd
@@ -122,8 +122,8 @@ class MetadataChunk:
var unused : float
var fMaxAcceptableError : float
var fps : int # int32
- var exporterMajorVersion : int # byte
- var exporterMinorVersion : int # byte
+ var exporterMajorVersion : int # byte
+ var exporterMinorVersion : int # byte
var pad : int # 2x byte
var sourceApp : String
var origFileName : String
diff --git a/game/src/Game/MusicConductor/MusicConductor.gd b/game/src/Game/MusicConductor/MusicConductor.gd
index f7e3b2a..ade8fb4 100644
--- a/game/src/Game/MusicConductor/MusicConductor.gd
+++ b/game/src/Game/MusicConductor/MusicConductor.gd
@@ -34,7 +34,7 @@ func get_all_song_names() -> PackedStringArray:
for si : SongInfo in _available_songs:
songNames.append(si.song_name)
return songNames
-
+
func get_all_song_paths() -> PackedStringArray:
var songPaths : PackedStringArray = []
for si : SongInfo in _available_songs:
@@ -103,13 +103,13 @@ func setup_compat_song(file_name) -> void:
var metadata = MusicMetadata.new()
metadata.set_from_stream(stream)
var title = metadata.title
-
+
if title == "":
#use the file name without the extension if there's no metadata
title = file_name.split(".")[0]
song.init_stream(file_name,title,stream)
_available_songs.append(song)
-
+
func add_compat_songs() -> void:
for file_name : String in SoundSingleton.song_list:
setup_compat_song(file_name)
@@ -128,13 +128,13 @@ func add_ootb_music() -> void:
func generate_playlist() -> void:
var song_names = MusicConductor.get_all_song_paths()
var possible_indices = range(len(song_names)-1)
-
+
var title_index = song_names.find(SoundSingleton.title_theme)
possible_indices.remove_at(title_index)
-
+
var actual_playlist_len = min(preferred_playlist_len,len(possible_indices))
-
- #if the playlist size is too large or small, make it the same size as what we
+
+ #if the playlist size is too large or small, make it the same size as what we
#need to support
if len(playlist) != actual_playlist_len:
playlist.resize(actual_playlist_len)
@@ -143,14 +143,14 @@ func generate_playlist() -> void:
#The song we just played can be in the playlist, just not the first one
if last_played != -1:
possible_indices.remove_at(last_played)
-
+
#essentially shuffle-bag randomness, picking from a list of song indices
for i in range(actual_playlist_len):
var ind = randi_range(0,len(possible_indices)-1)
#add back the last song we just played as an option
if i==2:
possible_indices.append(last_played)
-
+
playlist[i] = possible_indices[ind]
possible_indices.remove_at(ind)
diff --git a/game/src/Game/MusicConductor/MusicPlayer.gd b/game/src/Game/MusicConductor/MusicPlayer.gd
index 17285dc..0db44ca 100644
--- a/game/src/Game/MusicConductor/MusicPlayer.gd
+++ b/game/src/Game/MusicConductor/MusicPlayer.gd
@@ -27,7 +27,7 @@ func _process(_delta : float) -> void:
_progress_slider.value = MusicConductor.get_current_song_progress_percentage()
func _update_play_pause_button(_arg1 : Variant = null, _arg2 : Variant = null) -> void:
- _play_pause_button.text = "◼" if MusicConductor.is_paused() else "▶"
+ _play_pause_button.text = "▶️" if MusicConductor.is_paused() else "❚❚"
func _on_play_pause_button_pressed() -> void:
MusicConductor.toggle_play_pause()