diff --git a/extension/doc_classes/GUIScrollbar.xml b/extension/doc_classes/GUIScrollbar.xml
index 83fdf1d4..5e6b538c 100644
--- a/extension/doc_classes/GUIScrollbar.xml
+++ b/extension/doc_classes/GUIScrollbar.xml
@@ -73,6 +73,11 @@
+
+
+
+
+
@@ -118,6 +123,15 @@
+
+
+
+
+
+
+
+
+
diff --git a/extension/doc_classes/MenuSingleton.xml b/extension/doc_classes/MenuSingleton.xml
index b96d465d..b0288df4 100644
--- a/extension/doc_classes/MenuSingleton.xml
+++ b/extension/doc_classes/MenuSingleton.xml
@@ -7,6 +7,12 @@
+
+
+
+
+
+
@@ -183,6 +189,7 @@
+
diff --git a/extension/src/openvic-extension/classes/GUIScrollbar.cpp b/extension/src/openvic-extension/classes/GUIScrollbar.cpp
index f8d2e7b1..493babf5 100644
--- a/extension/src/openvic-extension/classes/GUIScrollbar.cpp
+++ b/extension/src/openvic-extension/classes/GUIScrollbar.cpp
@@ -4,6 +4,8 @@
#include
#include
+#include
+
#include "openvic-extension/utility/ClassBindings.hpp"
#include "openvic-extension/utility/UITools.hpp"
#include "openvic-extension/utility/Utilities.hpp"
@@ -40,11 +42,15 @@ void GUIScrollbar::_bind_methods() {
OV_BIND_METHOD(GUIScrollbar::increment_value, { "signal" }, DEFVAL(true));
OV_BIND_METHOD(GUIScrollbar::decrement_value, { "signal" }, DEFVAL(true));
OV_BIND_METHOD(GUIScrollbar::set_value_as_ratio, { "new_ratio", "signal" }, DEFVAL(true));
+ OV_BIND_METHOD(GUIScrollbar::get_value_scaled);
OV_BIND_METHOD(GUIScrollbar::is_range_limited);
OV_BIND_METHOD(GUIScrollbar::get_range_limit_min);
OV_BIND_METHOD(GUIScrollbar::get_range_limit_max);
OV_BIND_METHOD(GUIScrollbar::set_range_limits, { "new_range_limit_min", "new_range_limit_max", "signal" }, DEFVAL(true));
+ OV_BIND_METHOD(GUIScrollbar::set_range_limits_and_value, {
+ "new_range_limit_min", "new_range_limit_max", "new_value", "signal"
+ }, DEFVAL(true));
OV_BIND_METHOD(GUIScrollbar::set_limits, { "new_min_value", "new_max_value", "signal" }, DEFVAL(true));
OV_BIND_METHOD(GUIScrollbar::get_length_override);
@@ -426,20 +432,9 @@ Error GUIScrollbar::set_gui_scrollbar(GUI::Scrollbar const* new_gui_scrollbar) {
_calculate_rects();
- fixed_point_t step_size = gui_scrollbar->get_step_size();
- if (step_size <= 0) {
- UtilityFunctions::push_error(
- "Invalid step size ", Utilities::fixed_point_to_string_dp(step_size, -1), " for GUIScrollbar ",
- gui_scrollbar_name, " - not positive! Defaulting to 1."
- );
- step_size = 1;
- ret = false;
- }
- min_value = gui_scrollbar->get_min_value() / step_size;
- max_value = gui_scrollbar->get_max_value() / step_size;
-
- ret &= _constrain_limits() == OK;
- ret &= reset() == OK;
+ ret &= set_step_size_and_limits_fp(
+ gui_scrollbar->get_step_size(), gui_scrollbar->get_min_value(), gui_scrollbar->get_max_value()
+ ) == OK;
return ERR(ret);
}
@@ -472,6 +467,14 @@ void GUIScrollbar::set_value(int32_t new_value, bool signal) {
}
}
+void GUIScrollbar::set_value_fp(fixed_point_t new_value, bool signal) {
+ return set_value(new_value / step_size, signal);
+}
+
+void GUIScrollbar::set_value_from_slider_value(SliderValue const& slider_value, int32_t scale, bool signal) {
+ set_value_fp(slider_value.get_value() * scale, signal);
+}
+
void GUIScrollbar::increment_value(bool signal) {
set_value(value + 1, signal);
}
@@ -488,12 +491,26 @@ void GUIScrollbar::set_value_as_ratio(float new_ratio, bool signal) {
set_value(min_value + (max_value - min_value) * new_ratio, signal);
}
+fixed_point_t GUIScrollbar::get_value_scaled_fp() const {
+ return value * step_size;
+}
+
+float GUIScrollbar::get_value_scaled() const {
+ return get_value_scaled_fp().to_float();
+}
+
Error GUIScrollbar::set_range_limits(int32_t new_range_limit_min, int32_t new_range_limit_max, bool signal) {
+ return set_range_limits_and_value(new_range_limit_min, new_range_limit_max, value, signal);
+}
+
+Error GUIScrollbar::set_range_limits_and_value(
+ int32_t new_range_limit_min, int32_t new_range_limit_max, int32_t new_value, bool signal
+) {
ERR_FAIL_COND_V_MSG(!range_limited, FAILED, "Cannot set range limits of non-range-limited GUIScrollbar!");
range_limit_min = new_range_limit_min;
range_limit_max = new_range_limit_max;
const Error err = _constrain_range_limits();
- set_value(value, signal);
+ set_value(new_value, signal);
return err;
}
@@ -506,6 +523,50 @@ Error GUIScrollbar::set_limits(int32_t new_min_value, int32_t new_max_value, boo
return ERR(ret);
}
+Error GUIScrollbar::set_range_limits_and_value_fp(
+ fixed_point_t new_range_limit_min, fixed_point_t new_range_limit_max, fixed_point_t new_value, bool signal
+) {
+ return set_range_limits_and_value(
+ new_range_limit_min / step_size,
+ new_range_limit_max / step_size,
+ new_value / step_size,
+ signal
+ );
+}
+
+Error GUIScrollbar::set_range_limits_and_value_from_slider_value(
+ SliderValue const& slider_value, int32_t scale, bool signal
+) {
+ return set_range_limits_and_value_fp(
+ slider_value.get_min() * scale,
+ slider_value.get_max() * scale,
+ slider_value.get_value() * scale,
+ signal
+ );
+}
+
+Error GUIScrollbar::set_step_size_and_limits_fp(fixed_point_t new_step_size, int32_t new_min_value, int32_t new_max_value) {
+ bool ret = true;
+
+ step_size = new_step_size;
+ if (step_size <= 0) {
+ UtilityFunctions::push_error(
+ "Invalid step size ", Utilities::fixed_point_to_string_dp(step_size, -1), " for GUIScrollbar ",
+ get_name(), " - not positive! Defaulting to 1."
+ );
+ step_size = fixed_point_t::_1();
+ ret = false;
+ }
+
+ min_value = new_min_value / step_size;
+ max_value = new_max_value / step_size;
+
+ ret &= _constrain_limits() == OK;
+ ret &= reset() == OK;
+
+ return ERR(ret);
+}
+
void GUIScrollbar::set_length_override(real_t new_length_override) {
ERR_FAIL_COND_MSG(
length_override < 0, vformat("Invalid GUIScrollbar length override: %f - cannot be negative!", length_override)
diff --git a/extension/src/openvic-extension/classes/GUIScrollbar.hpp b/extension/src/openvic-extension/classes/GUIScrollbar.hpp
index beba50b5..3760e966 100644
--- a/extension/src/openvic-extension/classes/GUIScrollbar.hpp
+++ b/extension/src/openvic-extension/classes/GUIScrollbar.hpp
@@ -9,6 +9,8 @@
#include "openvic-extension/classes/GUIHasTooltip.hpp"
namespace OpenVic {
+ struct SliderValue;
+
class GUIScrollbar : public godot::Control {
GDCLASS(GUIScrollbar, godot::Control)
@@ -36,11 +38,12 @@ namespace OpenVic {
godot::Orientation PROPERTY(orientation, godot::HORIZONTAL);
real_t PROPERTY(length_override, 0.0);
+ fixed_point_t PROPERTY(step_size, fixed_point_t::_1());
int32_t PROPERTY(value, 0);
int32_t PROPERTY(min_value, 0);
int32_t PROPERTY(max_value, 0);
- bool PROPERTY_CUSTOM_PREFIX(range_limited, is);
+ bool PROPERTY_CUSTOM_PREFIX(range_limited, is, false);
int32_t PROPERTY(range_limit_min, 0);
int32_t PROPERTY(range_limit_max, 0);
@@ -97,15 +100,33 @@ namespace OpenVic {
godot::String get_gui_scrollbar_name() const;
void set_value(int32_t new_value, bool signal = true);
+ void set_value_fp(fixed_point_t new_value, bool signal = true);
+ void set_value_from_slider_value(SliderValue const& slider_value, int32_t scale, bool signal = true);
void increment_value(bool signal = true);
void decrement_value(bool signal = true);
float get_value_as_ratio() const;
void set_value_as_ratio(float new_ratio, bool signal = true);
- godot::Error set_range_limits(int32_t new_range_limit_min, int32_t new_range_limit_max, bool signal = true);
+ fixed_point_t get_value_scaled_fp() const;
+ float get_value_scaled() const;
+
+ godot::Error set_range_limits(
+ int32_t new_range_limit_min, int32_t new_range_limit_max, bool signal = true
+ );
+ godot::Error set_range_limits_and_value(
+ int32_t new_range_limit_min, int32_t new_range_limit_max, int32_t new_value, bool signal = true
+ );
godot::Error set_limits(int32_t new_min_value, int32_t new_max_value, bool signal = true);
+ godot::Error set_range_limits_and_value_fp(
+ fixed_point_t new_range_limit_min, fixed_point_t new_range_limit_max, fixed_point_t new_value, bool signal = true
+ );
+ godot::Error set_range_limits_and_value_from_slider_value(
+ SliderValue const& slider_value, int32_t scale, bool signal = true
+ );
+ godot::Error set_step_size_and_limits_fp(fixed_point_t new_step_size, int32_t new_min_value, int32_t new_max_value);
+
/* Override the main dimension of gui_scollbar's size with the specified length. */
void set_length_override(real_t new_length_override);
};
diff --git a/extension/src/openvic-extension/singletons/MenuSingleton.cpp b/extension/src/openvic-extension/singletons/MenuSingleton.cpp
index 9e62641f..faf28f19 100644
--- a/extension/src/openvic-extension/singletons/MenuSingleton.cpp
+++ b/extension/src/openvic-extension/singletons/MenuSingleton.cpp
@@ -405,8 +405,9 @@ void MenuSingleton::_bind_methods() {
/* TRADE MENU */
OV_BIND_METHOD(MenuSingleton::get_trade_menu_good_categories_info);
- OV_BIND_METHOD(MenuSingleton::get_trade_menu_trade_details_info, { "trade_detail_good_index" });
+ OV_BIND_METHOD(MenuSingleton::get_trade_menu_trade_details_info, { "trade_detail_good_index", "stockpile_cutoff_slider" });
OV_BIND_METHOD(MenuSingleton::get_trade_menu_tables_info);
+ OV_BIND_SMETHOD(calculate_trade_menu_stockpile_cutoff_amount, { "slider" });
BIND_ENUM_CONSTANT(TRADE_SETTING_NONE);
BIND_ENUM_CONSTANT(TRADE_SETTING_AUTOMATED);
diff --git a/extension/src/openvic-extension/singletons/MenuSingleton.hpp b/extension/src/openvic-extension/singletons/MenuSingleton.hpp
index 072327e6..9aee2131 100644
--- a/extension/src/openvic-extension/singletons/MenuSingleton.hpp
+++ b/extension/src/openvic-extension/singletons/MenuSingleton.hpp
@@ -26,6 +26,7 @@ namespace OpenVic {
struct ModifierSum;
struct RuleSet;
struct LeaderInstance;
+ struct GUIScrollbar;
class MenuSingleton : public godot::Object {
GDCLASS(MenuSingleton, godot::Object)
@@ -239,8 +240,15 @@ namespace OpenVic {
/* TRADE MENU */
godot::Dictionary get_trade_menu_good_categories_info() const;
- godot::Dictionary get_trade_menu_trade_details_info(int32_t trade_detail_good_index) const;
+ godot::Dictionary get_trade_menu_trade_details_info(
+ int32_t trade_detail_good_index, GUIScrollbar* stockpile_cutoff_slider
+ ) const;
godot::Dictionary get_trade_menu_tables_info() const;
+ static constexpr fixed_point_t calculate_trade_menu_stockpile_cutoff_amount_fp(fixed_point_t value) {
+ // TODO - replace this with: pow(2001, value / 2000) - 1
+ return value;
+ }
+ static float calculate_trade_menu_stockpile_cutoff_amount(GUIScrollbar const* slider);
/* MILITARY MENU */
godot::Dictionary make_leader_dict(LeaderInstance const& leader);
diff --git a/extension/src/openvic-extension/singletons/TradeMenu.cpp b/extension/src/openvic-extension/singletons/TradeMenu.cpp
index e8bc8f6d..1618b633 100644
--- a/extension/src/openvic-extension/singletons/TradeMenu.cpp
+++ b/extension/src/openvic-extension/singletons/TradeMenu.cpp
@@ -3,6 +3,7 @@
#include
#include "openvic-extension/classes/GUILabel.hpp"
+#include "openvic-extension/classes/GUIScrollbar.hpp"
#include "openvic-extension/singletons/GameSingleton.hpp"
#include "openvic-extension/singletons/PlayerSingleton.hpp"
#include "openvic-extension/utility/Utilities.hpp"
@@ -105,14 +106,15 @@ Dictionary MenuSingleton::get_trade_menu_good_categories_info() const {
return ret;
}
-Dictionary MenuSingleton::get_trade_menu_trade_details_info(int32_t trade_detail_good_index) const {
+Dictionary MenuSingleton::get_trade_menu_trade_details_info(
+ int32_t trade_detail_good_index, GUIScrollbar* stockpile_cutoff_slider
+) const {
static const StringName trade_detail_good_name_key = "trade_detail_good_name";
static const StringName trade_detail_good_price_key = "trade_detail_good_price";
static const StringName trade_detail_good_base_price_key = "trade_detail_good_base_price";
static const StringName trade_detail_price_history_key = "trade_detail_price_history";
static const StringName trade_detail_is_automated_key = "trade_detail_is_automated";
static const StringName trade_detail_is_selling_key = "trade_detail_is_selling"; // or buying (false)
- static const StringName trade_detail_slider_value_key = "trade_detail_slider_value"; // linear slider value
static const StringName trade_detail_slider_amount_key = "trade_detail_slider_amount"; // exponential good amount
static const StringName trade_detail_government_needs_key = "trade_detail_government_needs";
static const StringName trade_detail_army_needs_key = "trade_detail_army_needs";
@@ -163,8 +165,19 @@ Dictionary MenuSingleton::get_trade_menu_trade_details_info(int32_t trade_detail
ret[trade_detail_is_automated_key] = good_data.is_automated;
ret[trade_detail_is_selling_key] = good_data.is_selling;
- // TODO - use exponential formula!
- ret[trade_detail_slider_value_key] = (good_data.stockpile_cutoff / 2000).to_int32_t();
+ if (stockpile_cutoff_slider != nullptr) {
+ int32_t index = 0;
+
+ while (index < stockpile_cutoff_slider->get_max_value() && calculate_trade_menu_stockpile_cutoff_amount_fp(
+ index * stockpile_cutoff_slider->get_step_size()
+ ) < good_data.stockpile_cutoff) {
+ ++index;
+ }
+
+ // TODO - use a more efficient algorithm, e.g. some kind of binary search
+
+ stockpile_cutoff_slider->set_value(index, false);
+ }
ret[trade_detail_slider_amount_key] = good_data.stockpile_cutoff.to_float();
ret[trade_detail_government_needs_key] = good_data.government_needs.to_float();
ret[trade_detail_army_needs_key] = good_data.army_needs.to_float();
@@ -317,3 +330,9 @@ Dictionary MenuSingleton::get_trade_menu_tables_info() const {
return ret;
}
+
+float MenuSingleton::calculate_trade_menu_stockpile_cutoff_amount(GUIScrollbar const* slider) {
+ ERR_FAIL_NULL_V(slider, 0.0f);
+
+ return calculate_trade_menu_stockpile_cutoff_amount_fp(slider->get_value_scaled_fp());
+}
diff --git a/game/src/Game/GameSession/NationManagementScreen/TradeMenu.gd b/game/src/Game/GameSession/NationManagementScreen/TradeMenu.gd
index 544da242..d12ab21d 100644
--- a/game/src/Game/GameSession/NationManagementScreen/TradeMenu.gd
+++ b/game/src/Game/GameSession/NationManagementScreen/TradeMenu.gd
@@ -173,12 +173,19 @@ func _ready() -> void:
if _trade_detail_automate_checkbox:
_trade_detail_automate_checkbox.set_tooltip_string("AUTOMATE_TRADE_CHECK")
_trade_detail_buy_sell_stockpile_checkbox = get_gui_icon_button_from_nodepath(^"./country_trade/trade_details/sell_stockpile")
+ if _trade_detail_buy_sell_stockpile_checkbox:
+ _trade_detail_buy_sell_stockpile_checkbox.toggled.connect(_update_trade_order_buy_sell)
_trade_detail_buy_sell_stockpile_label = get_gui_label_from_nodepath(^"./country_trade/trade_details/sell_stockpile_label")
_trade_detail_stockpile_slider_description_label = get_gui_label_from_nodepath(^"./country_trade/trade_details/sell_slidier_desc")
_trade_detail_stockpile_slider_scrollbar = get_gui_scrollbar_from_nodepath(^"./country_trade/trade_details/sell_slider")
_trade_detail_stockpile_slider_amount_label = get_gui_label_from_nodepath(^"./country_trade/trade_details/slider_value")
if _trade_detail_stockpile_slider_amount_label:
_trade_detail_stockpile_slider_amount_label.set_auto_translate(false)
+ if _trade_detail_stockpile_slider_scrollbar:
+ _trade_detail_stockpile_slider_scrollbar.value_changed.connect(
+ func(value : int) -> void:
+ _update_stockpile_slider_amount_label(MenuSingleton.calculate_trade_menu_stockpile_cutoff_amount(_trade_detail_stockpile_slider_scrollbar))
+ )
_trade_detail_confirm_trade_button = get_gui_icon_button_from_nodepath(^"./country_trade/trade_details/confirm_trade")
if _trade_detail_confirm_trade_button:
_trade_detail_confirm_trade_button.pressed.connect(
@@ -256,12 +263,11 @@ func _update_info() -> void:
hide()
func _update_trade_details(new_trade_detail_good_index : int = -1) -> void:
- # If the desired good is already selected, do nothing (current index will never be negative, so -1 forces a refresh)
- if _trade_detail_good_index == new_trade_detail_good_index:
- return
-
- # If the new index isn't negative, update the current index to match it (newly selected good)
- if new_trade_detail_good_index >= 0:
+ # If the new index isn't negative, update the current index to match it
+ # Even if the new index is the same as the current index, it indicates a forced refresh (including the trade order
+ # buy/sell checkbox and stockpile cutoff slider, which otherwise wouldn't be refreshed)
+ var force_refresh : bool = new_trade_detail_good_index >= 0
+ if force_refresh:
_trade_detail_good_index = new_trade_detail_good_index
# Trade details
@@ -271,7 +277,6 @@ func _update_trade_details(new_trade_detail_good_index : int = -1) -> void:
const trade_detail_price_history_key : StringName = &"trade_detail_price_history"
const trade_detail_is_automated_key : StringName = &"trade_detail_is_automated"
const trade_detail_is_selling_key : StringName = &"trade_detail_is_selling" # or buying (false)
- const trade_detail_slider_value_key : StringName = &"trade_detail_slider_value" # linear slider value
const trade_detail_slider_amount_key : StringName = &"trade_detail_slider_amount" # exponential good amount
const trade_detail_government_needs_key : StringName = &"trade_detail_government_needs"
const trade_detail_army_needs_key : StringName = &"trade_detail_army_needs"
@@ -281,7 +286,9 @@ func _update_trade_details(new_trade_detail_good_index : int = -1) -> void:
const trade_detail_pop_needs_key : StringName = &"trade_detail_pop_needs"
const trade_detail_available_key : StringName = &"trade_detail_available"
- var trade_info : Dictionary = MenuSingleton.get_trade_menu_trade_details_info(_trade_detail_good_index)
+ var trade_info : Dictionary = MenuSingleton.get_trade_menu_trade_details_info(
+ _trade_detail_good_index, _trade_detail_stockpile_slider_scrollbar if force_refresh else null
+ )
var trade_detail_good_name : String = trade_info.get(trade_detail_good_name_key, "")
@@ -322,28 +329,16 @@ func _update_trade_details(new_trade_detail_good_index : int = -1) -> void:
_trade_detail_good_chart_time_label.add_substitution("MONTHS", str(price_history.size()))
var is_automated : bool = trade_info.get(trade_detail_is_automated_key, false)
- var is_selling : bool = trade_info.get(trade_detail_is_selling_key, false)
if _trade_detail_automate_checkbox:
# Investigate whether set_pressed_no_signal can/should be used here
_trade_detail_automate_checkbox.set_pressed(is_automated)
- if _trade_detail_buy_sell_stockpile_checkbox:
- # Investigate whether set_pressed_no_signal can/should be used here
- _trade_detail_buy_sell_stockpile_checkbox.set_pressed(is_selling)
-
- if _trade_detail_buy_sell_stockpile_label:
- _trade_detail_buy_sell_stockpile_label.set_text("SELL" if is_selling else "BUY")
-
- if _trade_detail_stockpile_slider_description_label:
- _trade_detail_stockpile_slider_description_label.set_text("MINIMUM_STOCKPILE_TARGET" if is_selling else "MAXIMUM_STOCKPILE_TARGET")
+ if force_refresh:
+ _update_trade_order_buy_sell(trade_info.get(trade_detail_is_selling_key, false))
- if _trade_detail_stockpile_slider_scrollbar:
- _trade_detail_stockpile_slider_scrollbar.set_value(trade_info.get(trade_detail_slider_value_key, 0), false)
-
- if _trade_detail_stockpile_slider_amount_label:
- var slider_amount : float = trade_info.get(trade_detail_slider_amount_key, 0)
- _trade_detail_stockpile_slider_amount_label.set_text(GUINode.float_to_string_dp(slider_amount, 3 if slider_amount < 10.0 else 2))
+ if _trade_detail_stockpile_slider_amount_label:
+ _update_stockpile_slider_amount_label(trade_info.get(trade_detail_slider_amount_key, 0))
if _trade_detail_confirm_trade_button:
_trade_detail_confirm_trade_button.set_disabled(is_automated)
@@ -379,6 +374,20 @@ func _update_trade_details(new_trade_detail_good_index : int = -1) -> void:
if _trade_detail_good_available_label:
_trade_detail_good_available_label.add_substitution("VAL", GUINode.float_to_string_dp(trade_info.get(trade_detail_available_key, 0), 2))
+func _update_trade_order_buy_sell(is_selling : bool) -> void:
+ if _trade_detail_buy_sell_stockpile_checkbox:
+ # Investigate whether set_pressed_no_signal can/should be used here
+ _trade_detail_buy_sell_stockpile_checkbox.set_pressed(is_selling)
+
+ if _trade_detail_buy_sell_stockpile_label:
+ _trade_detail_buy_sell_stockpile_label.set_text("SELL" if is_selling else "BUY")
+
+ if _trade_detail_stockpile_slider_description_label:
+ _trade_detail_stockpile_slider_description_label.set_text("MINIMUM_STOCKPILE_TARGET" if is_selling else "MAXIMUM_STOCKPILE_TARGET")
+
+func _update_stockpile_slider_amount_label(slider_amount : float) -> void:
+ _trade_detail_stockpile_slider_amount_label.set_text(GUINode.float_to_string_dp(slider_amount, 3 if slider_amount < 10.0 else 2))
+
func _change_table_sorting(table : Table, column : int) -> void:
if _table_sort_columns[table] != column:
_table_sort_columns[table] = column