Add multi-select batch fish selling
This commit is contained in:
parent
9277326164
commit
e560cc0b3e
9 changed files with 747 additions and 272 deletions
|
|
@ -8,12 +8,15 @@ enum Status {
|
|||
INVALID_VALUE,
|
||||
INVALID_BUYER,
|
||||
INVALID_OFFER,
|
||||
INVALID_SELECTION,
|
||||
TRANSACTION_FAILED,
|
||||
}
|
||||
|
||||
var success: bool = false
|
||||
var status: Status = Status.TRANSACTION_FAILED
|
||||
var catch_id: StringName
|
||||
var catch_ids: Array[StringName] = []
|
||||
var fish_count: int = 0
|
||||
var fish_name: String = ""
|
||||
var buyer_id: StringName
|
||||
var buyer_display_name: String = ""
|
||||
|
|
@ -32,14 +35,16 @@ func get_message() -> String:
|
|||
Status.SUCCESS:
|
||||
return sale_message
|
||||
Status.NOT_FOUND:
|
||||
return "Fish no longer exists."
|
||||
return "fish no longer exists."
|
||||
Status.FAVORITED:
|
||||
return "Favorited fish cannot be sold."
|
||||
return "favorited fish cannot be sold."
|
||||
Status.INVALID_VALUE:
|
||||
return "Invalid sale value."
|
||||
return "invalid sale value."
|
||||
Status.INVALID_BUYER:
|
||||
return "Buyer is unavailable."
|
||||
return "buyer is unavailable."
|
||||
Status.INVALID_OFFER:
|
||||
return "Invalid buyer offer."
|
||||
return "invalid buyer offer."
|
||||
Status.INVALID_SELECTION:
|
||||
return "the fish selection is invalid."
|
||||
_:
|
||||
return "Transaction failed."
|
||||
return "transaction failed."
|
||||
|
|
|
|||
|
|
@ -25,24 +25,53 @@ func can_sell(
|
|||
catch_id: StringName,
|
||||
buyer: FishBuyerProfileType,
|
||||
) -> bool:
|
||||
return _validate_sale(catch_id, buyer).is_success()
|
||||
return preview_batch([catch_id], buyer).is_success()
|
||||
|
||||
|
||||
func can_sell_batch(
|
||||
catch_ids: Array[StringName],
|
||||
buyer: FishBuyerProfileType,
|
||||
) -> bool:
|
||||
return preview_batch(catch_ids, buyer).is_success()
|
||||
|
||||
|
||||
func preview_batch(
|
||||
catch_ids: Array[StringName],
|
||||
buyer: FishBuyerProfileType,
|
||||
) -> FishSaleResultType:
|
||||
return _validate_batch(catch_ids, buyer)
|
||||
|
||||
|
||||
func sell(
|
||||
catch_id: StringName,
|
||||
buyer: FishBuyerProfileType,
|
||||
) -> FishSaleResultType:
|
||||
var result: FishSaleResultType = _validate_sale(catch_id, buyer)
|
||||
return sell_batch([catch_id], buyer)
|
||||
|
||||
|
||||
func sell_batch(
|
||||
catch_ids: Array[StringName],
|
||||
buyer: FishBuyerProfileType,
|
||||
) -> FishSaleResultType:
|
||||
var result: FishSaleResultType = _validate_batch(catch_ids, buyer)
|
||||
if not result.is_success():
|
||||
return result
|
||||
|
||||
var removed_catch: FishCatchType = _inventory.remove_catch_by_id(catch_id)
|
||||
if removed_catch == null:
|
||||
var inventory_snapshot: Array[FishCatchType] = _inventory.get_all_catches()
|
||||
var next_sequence_snapshot: int = _inventory.get_next_catch_sequence()
|
||||
var removed_catches: Array[FishCatchType] = (
|
||||
_inventory.remove_catches_by_ids(result.catch_ids)
|
||||
)
|
||||
if removed_catches.size() != result.catch_ids.size():
|
||||
result.success = false
|
||||
result.status = FishSaleResultType.Status.NOT_FOUND
|
||||
result.status = FishSaleResultType.Status.TRANSACTION_FAILED
|
||||
return result
|
||||
if not _wallet.credit(result.payout):
|
||||
_inventory.add_catch(removed_catch)
|
||||
if not _inventory.replace_all_catches(
|
||||
inventory_snapshot,
|
||||
next_sequence_snapshot
|
||||
):
|
||||
push_error("Unable to roll back a failed fish batch sale.")
|
||||
result.success = false
|
||||
result.status = FishSaleResultType.Status.TRANSACTION_FAILED
|
||||
return result
|
||||
|
|
@ -50,27 +79,22 @@ func sell(
|
|||
return result
|
||||
|
||||
|
||||
func _validate_sale(
|
||||
catch_id: StringName,
|
||||
func _validate_batch(
|
||||
catch_ids: Array[StringName],
|
||||
buyer: FishBuyerProfileType,
|
||||
) -> FishSaleResultType:
|
||||
var result := FishSaleResultType.new()
|
||||
result.catch_id = catch_id
|
||||
if (
|
||||
catch_id.is_empty()
|
||||
or _inventory == null
|
||||
or _wallet == null
|
||||
):
|
||||
result.status = FishSaleResultType.Status.NOT_FOUND
|
||||
result.catch_ids = catch_ids.duplicate()
|
||||
result.fish_count = catch_ids.size()
|
||||
if _inventory == null or _wallet == null:
|
||||
result.status = FishSaleResultType.Status.TRANSACTION_FAILED
|
||||
return result
|
||||
if catch_ids.is_empty():
|
||||
result.status = FishSaleResultType.Status.INVALID_SELECTION
|
||||
return result
|
||||
if buyer == null or not buyer.is_valid():
|
||||
result.status = FishSaleResultType.Status.INVALID_BUYER
|
||||
return result
|
||||
var fish_catch: FishCatchType = _inventory.get_catch_by_id(catch_id)
|
||||
if fish_catch == null:
|
||||
result.status = FishSaleResultType.Status.NOT_FOUND
|
||||
return result
|
||||
result.fish_name = fish_catch.fish.display_name
|
||||
result.buyer_id = buyer.id
|
||||
result.buyer_display_name = buyer.display_name
|
||||
result.buyer_animal_name = (
|
||||
|
|
@ -78,21 +102,55 @@ func _validate_sale(
|
|||
if not buyer.animal_name_plural.is_empty()
|
||||
else buyer.display_name
|
||||
)
|
||||
result.base_value = fish_catch.sale_value
|
||||
result.payout = buyer.get_offer(result.base_value)
|
||||
if fish_catch.is_favorited:
|
||||
var seen_ids: Dictionary[StringName, bool] = {}
|
||||
var contains_favorite: bool = false
|
||||
const MAX_SAFE_TOTAL: int = 9223372036854775807
|
||||
for catch_id: StringName in catch_ids:
|
||||
if catch_id.is_empty() or seen_ids.has(catch_id):
|
||||
result.status = FishSaleResultType.Status.INVALID_SELECTION
|
||||
return result
|
||||
seen_ids[catch_id] = true
|
||||
var fish_catch: FishCatchType = _inventory.get_catch_by_id(catch_id)
|
||||
if fish_catch == null:
|
||||
result.status = FishSaleResultType.Status.NOT_FOUND
|
||||
return result
|
||||
if fish_catch.is_favorited:
|
||||
contains_favorite = true
|
||||
if fish_catch.sale_value < 0:
|
||||
result.status = FishSaleResultType.Status.INVALID_VALUE
|
||||
return result
|
||||
var offer: int = buyer.get_offer(fish_catch.sale_value)
|
||||
if offer < 0:
|
||||
result.status = FishSaleResultType.Status.INVALID_OFFER
|
||||
return result
|
||||
if (
|
||||
result.base_value > MAX_SAFE_TOTAL - fish_catch.sale_value
|
||||
or result.payout > MAX_SAFE_TOTAL - offer
|
||||
):
|
||||
result.status = FishSaleResultType.Status.TRANSACTION_FAILED
|
||||
return result
|
||||
result.base_value += fish_catch.sale_value
|
||||
result.payout += offer
|
||||
if result.fish_count == 1:
|
||||
result.catch_id = fish_catch.catch_id
|
||||
result.fish_name = fish_catch.fish.display_name
|
||||
if contains_favorite:
|
||||
result.status = FishSaleResultType.Status.FAVORITED
|
||||
elif fish_catch.sale_value < 0:
|
||||
result.status = FishSaleResultType.Status.INVALID_VALUE
|
||||
elif result.payout < 0:
|
||||
result.status = FishSaleResultType.Status.INVALID_OFFER
|
||||
elif not _wallet.can_credit(result.payout):
|
||||
return result
|
||||
if not _wallet.can_credit(result.payout):
|
||||
result.status = FishSaleResultType.Status.TRANSACTION_FAILED
|
||||
else:
|
||||
result.status = FishSaleResultType.Status.SUCCESS
|
||||
result.success = true
|
||||
return result
|
||||
result.status = FishSaleResultType.Status.SUCCESS
|
||||
result.success = true
|
||||
if result.fish_count == 1:
|
||||
result.sale_message = buyer.get_sale_message(
|
||||
result.fish_name,
|
||||
result.payout
|
||||
)
|
||||
else:
|
||||
result.sale_message = "you sold %d fish to the %s for $%d." % [
|
||||
result.fish_count,
|
||||
result.buyer_animal_name,
|
||||
result.payout,
|
||||
]
|
||||
return result
|
||||
|
|
|
|||
|
|
@ -95,18 +95,40 @@ func contains_catch_id(catch_id: StringName) -> bool:
|
|||
func remove_catch_by_id(catch_id: StringName) -> FishCatchType:
|
||||
if catch_id.is_empty():
|
||||
return null
|
||||
for index: int in range(_catches.size()):
|
||||
var fish_catch: FishCatchType = _catches[index]
|
||||
if fish_catch == null or fish_catch.catch_id != catch_id:
|
||||
continue
|
||||
_catches.remove_at(index)
|
||||
contents_changed.emit(
|
||||
fish_catch.fish_id,
|
||||
get_count(fish_catch.fish_id)
|
||||
)
|
||||
catches_changed.emit()
|
||||
return fish_catch
|
||||
return null
|
||||
var removed: Array[FishCatchType] = remove_catches_by_ids([catch_id])
|
||||
return removed.front() if removed.size() == 1 else null
|
||||
|
||||
|
||||
func remove_catches_by_ids(
|
||||
catch_ids: Array[StringName],
|
||||
) -> Array[FishCatchType]:
|
||||
var removed: Array[FishCatchType] = []
|
||||
if catch_ids.is_empty():
|
||||
return removed
|
||||
var requested_ids: Dictionary[StringName, bool] = {}
|
||||
for catch_id: StringName in catch_ids:
|
||||
if catch_id.is_empty() or requested_ids.has(catch_id):
|
||||
return removed
|
||||
requested_ids[catch_id] = true
|
||||
for catch_id: StringName in catch_ids:
|
||||
var fish_catch: FishCatchType = get_catch_by_id(catch_id)
|
||||
if fish_catch == null:
|
||||
removed.clear()
|
||||
return removed
|
||||
removed.append(fish_catch)
|
||||
|
||||
var remaining: Array[FishCatchType] = []
|
||||
var affected_species: Dictionary[StringName, bool] = {}
|
||||
for fish_catch: FishCatchType in _catches:
|
||||
if requested_ids.has(fish_catch.catch_id):
|
||||
affected_species[fish_catch.fish_id] = true
|
||||
else:
|
||||
remaining.append(fish_catch)
|
||||
_catches = remaining
|
||||
for fish_id: StringName in affected_species:
|
||||
contents_changed.emit(fish_id, get_count(fish_id))
|
||||
catches_changed.emit()
|
||||
return removed
|
||||
|
||||
|
||||
func set_catch_favorited(
|
||||
|
|
|
|||
106
ui/fish_batch_selection.gd
Normal file
106
ui/fish_batch_selection.gd
Normal file
|
|
@ -0,0 +1,106 @@
|
|||
class_name FishBatchSelection
|
||||
extends RefCounted
|
||||
|
||||
var _selected_ids: Dictionary[StringName, bool] = {}
|
||||
var _visible_ids: Array[StringName] = []
|
||||
var _focused_id: StringName
|
||||
var _anchor_id: StringName
|
||||
|
||||
|
||||
func set_visible_order(visible_ids: Array[StringName]) -> void:
|
||||
_visible_ids = visible_ids.duplicate()
|
||||
var visible_set: Dictionary[StringName, bool] = {}
|
||||
for catch_id: StringName in _visible_ids:
|
||||
if not catch_id.is_empty():
|
||||
visible_set[catch_id] = true
|
||||
for catch_id: StringName in _selected_ids.keys():
|
||||
if not visible_set.has(catch_id):
|
||||
_selected_ids.erase(catch_id)
|
||||
if not visible_set.has(_focused_id):
|
||||
_focused_id = StringName()
|
||||
if not visible_set.has(_anchor_id):
|
||||
_anchor_id = StringName()
|
||||
|
||||
|
||||
func apply_click(
|
||||
catch_id: StringName,
|
||||
ctrl_pressed: bool,
|
||||
shift_pressed: bool,
|
||||
) -> void:
|
||||
if catch_id.is_empty() or not _visible_ids.has(catch_id):
|
||||
return
|
||||
if not shift_pressed or _anchor_id.is_empty():
|
||||
if ctrl_pressed:
|
||||
if _selected_ids.has(catch_id):
|
||||
_selected_ids.erase(catch_id)
|
||||
else:
|
||||
_selected_ids[catch_id] = true
|
||||
else:
|
||||
_selected_ids.clear()
|
||||
_selected_ids[catch_id] = true
|
||||
_focused_id = catch_id
|
||||
_anchor_id = catch_id
|
||||
return
|
||||
|
||||
var anchor_index: int = _visible_ids.find(_anchor_id)
|
||||
var target_index: int = _visible_ids.find(catch_id)
|
||||
if anchor_index < 0 or target_index < 0:
|
||||
select_only(catch_id)
|
||||
return
|
||||
if not ctrl_pressed:
|
||||
_selected_ids.clear()
|
||||
var first_index: int = mini(anchor_index, target_index)
|
||||
var last_index: int = maxi(anchor_index, target_index)
|
||||
for index: int in range(first_index, last_index + 1):
|
||||
_selected_ids[_visible_ids[index]] = true
|
||||
_focused_id = catch_id
|
||||
|
||||
|
||||
func select_only(catch_id: StringName) -> void:
|
||||
_selected_ids.clear()
|
||||
if catch_id.is_empty() or not _visible_ids.has(catch_id):
|
||||
_focused_id = StringName()
|
||||
_anchor_id = StringName()
|
||||
return
|
||||
_selected_ids[catch_id] = true
|
||||
_focused_id = catch_id
|
||||
_anchor_id = catch_id
|
||||
|
||||
|
||||
func clear() -> void:
|
||||
_selected_ids.clear()
|
||||
_focused_id = StringName()
|
||||
_anchor_id = StringName()
|
||||
|
||||
|
||||
func remove_ids(catch_ids: Array[StringName]) -> void:
|
||||
for catch_id: StringName in catch_ids:
|
||||
_selected_ids.erase(catch_id)
|
||||
if _focused_id == catch_id:
|
||||
_focused_id = StringName()
|
||||
if _anchor_id == catch_id:
|
||||
_anchor_id = StringName()
|
||||
|
||||
|
||||
func get_selected_ids() -> Array[StringName]:
|
||||
var selected_in_order: Array[StringName] = []
|
||||
for catch_id: StringName in _visible_ids:
|
||||
if _selected_ids.has(catch_id):
|
||||
selected_in_order.append(catch_id)
|
||||
return selected_in_order
|
||||
|
||||
|
||||
func is_selected(catch_id: StringName) -> bool:
|
||||
return _selected_ids.has(catch_id)
|
||||
|
||||
|
||||
func get_selected_count() -> int:
|
||||
return _selected_ids.size()
|
||||
|
||||
|
||||
func get_focused_id() -> StringName:
|
||||
return _focused_id
|
||||
|
||||
|
||||
func get_anchor_id() -> StringName:
|
||||
return _anchor_id
|
||||
1
ui/fish_batch_selection.gd.uid
Normal file
1
ui/fish_batch_selection.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
|||
uid://ct2140qw4pdug
|
||||
|
|
@ -24,6 +24,9 @@ const PlayerCoolerCapacityType = preload(
|
|||
const ShopInteractionType = preload(
|
||||
"res://world/fishing_shop_interaction.gd"
|
||||
)
|
||||
const FishBatchSelectionType = preload(
|
||||
"res://ui/fish_batch_selection.gd"
|
||||
)
|
||||
|
||||
signal menu_visibility_changed(is_open: bool)
|
||||
|
||||
|
|
@ -42,6 +45,7 @@ enum CloseReason {
|
|||
@onready var _fish_texture: TextureRect = %FishTexture
|
||||
@onready var _fish_name: Label = %FishName
|
||||
@onready var _fish_details: Label = %FishDetails
|
||||
@onready var _selection_summary: Label = %SelectionSummary
|
||||
@onready var _sell_button: Button = %SellButton
|
||||
@onready var _feedback: Label = %Feedback
|
||||
@onready var _reel_level: Label = %ReelLevel
|
||||
|
|
@ -73,14 +77,16 @@ var _interaction: ShopInteractionType
|
|||
var _bag: PlayerBagType
|
||||
var _item_catalog: ItemCatalogType
|
||||
var _cooler_capacity: PlayerCoolerCapacityType
|
||||
var _selected_catch_id: StringName
|
||||
var _confirmation_catch_id: StringName
|
||||
var _fish_selection := FishBatchSelectionType.new()
|
||||
var _confirmation_catch_ids: Array[StringName] = []
|
||||
var _confirmation_generation: int = -1
|
||||
var _prior_movement_enabled: bool = true
|
||||
var _prior_camera_enabled: bool = true
|
||||
var _prior_mouse_mode: Input.MouseMode = Input.MOUSE_MODE_VISIBLE
|
||||
var _snapshot_stored: bool = false
|
||||
var _mouse_snapshot_stored: bool = false
|
||||
var _generation: int = 0
|
||||
var _selection_input_generation: int = 0
|
||||
var _transaction_in_progress: bool = false
|
||||
var _closing: bool = false
|
||||
|
||||
|
|
@ -88,6 +94,7 @@ var _closing: bool = false
|
|||
func _ready() -> void:
|
||||
%CloseButton.pressed.connect(close_shop)
|
||||
_fish_list.item_selected.connect(_on_fish_selected)
|
||||
_fish_list.item_clicked.connect(_on_fish_clicked)
|
||||
_sell_button.pressed.connect(_open_sale_confirmation)
|
||||
_confirm_sale.pressed.connect(_on_confirm_sale)
|
||||
_cancel_sale.pressed.connect(_close_sale_confirmation)
|
||||
|
|
@ -120,6 +127,7 @@ func setup(
|
|||
_bag = bag
|
||||
_item_catalog = item_catalog
|
||||
_cooler_capacity = cooler_capacity
|
||||
_fish_selection.clear()
|
||||
if not _inventory.catches_changed.is_connected(_on_inventory_changed):
|
||||
_inventory.catches_changed.connect(_on_inventory_changed)
|
||||
if not _wallet.balance_changed.is_connected(_on_wallet_changed):
|
||||
|
|
@ -189,7 +197,7 @@ func close_shop(
|
|||
_generation += 1
|
||||
_transaction_in_progress = false
|
||||
_close_sale_confirmation()
|
||||
_selected_catch_id = StringName()
|
||||
_fish_selection.clear()
|
||||
hide()
|
||||
get_viewport().gui_release_focus()
|
||||
if _fishing_spot != null and is_instance_valid(_fishing_spot):
|
||||
|
|
@ -242,9 +250,9 @@ func _refresh_all() -> void:
|
|||
|
||||
func _refresh_wallet() -> void:
|
||||
_wallet_label.text = (
|
||||
"Wallet: $%d" % _wallet.get_balance()
|
||||
"wallet: $%d" % _wallet.get_balance()
|
||||
if _wallet != null
|
||||
else "Wallet: $0"
|
||||
else "wallet: $0"
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -253,7 +261,7 @@ func _refresh_fish_list() -> void:
|
|||
var catches: Array[FishCatchType] = (
|
||||
_inventory.get_all_catches() if _inventory != null else []
|
||||
)
|
||||
_sales_title.text = "Cooler %d / %d • Full base value" % [
|
||||
_sales_title.text = "cooler %d / %d • full base value" % [
|
||||
catches.size(),
|
||||
_cooler_capacity.get_capacity() if _cooler_capacity != null else 0,
|
||||
]
|
||||
|
|
@ -261,11 +269,21 @@ func _refresh_fish_list() -> void:
|
|||
func(a: FishCatchType, b: FishCatchType) -> bool:
|
||||
return a.catch_sequence > b.catch_sequence
|
||||
)
|
||||
var selected_index: int = -1
|
||||
var visible_ids: Array[StringName] = []
|
||||
for fish_catch: FishCatchType in catches:
|
||||
if fish_catch != null and fish_catch.is_valid():
|
||||
visible_ids.append(fish_catch.catch_id)
|
||||
_fish_selection.set_visible_order(visible_ids)
|
||||
for fish_catch: FishCatchType in catches:
|
||||
if fish_catch == null or not fish_catch.is_valid():
|
||||
continue
|
||||
var marker: String = "★ " if fish_catch.is_favorited else ""
|
||||
var marker: String = ""
|
||||
if _fish_selection.is_selected(fish_catch.catch_id):
|
||||
marker += "✓ "
|
||||
if fish_catch.catch_id == _fish_selection.get_focused_id():
|
||||
marker += "◆ "
|
||||
if fish_catch.is_favorited:
|
||||
marker += "★ "
|
||||
var index: int = _fish_list.add_item(
|
||||
"%s%s — %.2f lb"
|
||||
% [marker, fish_catch.fish.display_name, fish_catch.weight_lb],
|
||||
|
|
@ -277,16 +295,15 @@ func _refresh_fish_list() -> void:
|
|||
"%s • %s"
|
||||
% [
|
||||
fish_catch.fish.get_rarity_name(),
|
||||
"Favorited" if fish_catch.is_favorited else "Available",
|
||||
"favorited" if fish_catch.is_favorited else "available",
|
||||
]
|
||||
)
|
||||
if fish_catch.catch_id == _selected_catch_id:
|
||||
selected_index = index
|
||||
_fish_empty.visible = _fish_list.item_count == 0
|
||||
if selected_index >= 0:
|
||||
_fish_list.select(selected_index)
|
||||
elif not _selected_catch_id.is_empty():
|
||||
_selected_catch_id = StringName()
|
||||
_fish_list.deselect_all()
|
||||
for index: int in range(_fish_list.item_count):
|
||||
var catch_id := StringName(str(_fish_list.get_item_metadata(index)))
|
||||
if _fish_selection.is_selected(catch_id):
|
||||
_fish_list.select(index, false)
|
||||
|
||||
|
||||
func _refresh_selected_fish() -> void:
|
||||
|
|
@ -295,27 +312,72 @@ func _refresh_selected_fish() -> void:
|
|||
fish_catch.fish.display_texture if fish_catch != null else null
|
||||
)
|
||||
_fish_name.text = (
|
||||
fish_catch.fish.display_name if fish_catch != null else "Select a fish"
|
||||
fish_catch.fish.display_name if fish_catch != null else "select a fish"
|
||||
)
|
||||
if fish_catch == null:
|
||||
_fish_details.text = "Choose one individual fish from your Cooler."
|
||||
_sell_button.disabled = true
|
||||
_fish_details.text = "choose one individual fish from your cooler."
|
||||
_update_sale_summary()
|
||||
return
|
||||
var offer: int = _buyer.get_offer(fish_catch.sale_value)
|
||||
var individual_preview: FishSaleResultType = (
|
||||
_sale_service.preview_batch([fish_catch.catch_id], _buyer)
|
||||
if _sale_service != null
|
||||
else null
|
||||
)
|
||||
var offer: int = (
|
||||
individual_preview.payout if individual_preview != null else -1
|
||||
)
|
||||
_fish_details.text = (
|
||||
"%.2f lb • %s\nBase value: $%d\nMain-shop offer: $%d%s"
|
||||
"%.2f lb • %s\nbase value: $%d\nmain-shop offer: $%d%s"
|
||||
% [
|
||||
fish_catch.weight_lb,
|
||||
fish_catch.fish.get_rarity_name(),
|
||||
fish_catch.sale_value,
|
||||
offer,
|
||||
"\nFavorited fish cannot be sold."
|
||||
"\nfavorited fish cannot be sold."
|
||||
if fish_catch.is_favorited
|
||||
else "",
|
||||
]
|
||||
)
|
||||
_update_sale_summary()
|
||||
|
||||
|
||||
func _update_sale_summary() -> void:
|
||||
var selected_ids: Array[StringName] = _fish_selection.get_selected_ids()
|
||||
var selected_count: int = selected_ids.size()
|
||||
_sell_button.text = (
|
||||
"sell fish"
|
||||
if selected_count <= 1
|
||||
else "sell %d fish" % selected_count
|
||||
)
|
||||
if selected_count == 0:
|
||||
_selection_summary.text = "no fish selected"
|
||||
_sell_button.disabled = true
|
||||
return
|
||||
var preview: FishSaleResultType = (
|
||||
_sale_service.preview_batch(selected_ids, _buyer)
|
||||
if _sale_service != null
|
||||
else null
|
||||
)
|
||||
_selection_summary.text = (
|
||||
"1 fish selected"
|
||||
if selected_count == 1
|
||||
else "%d fish selected" % selected_count
|
||||
)
|
||||
if (
|
||||
preview != null
|
||||
and (
|
||||
preview.is_success()
|
||||
or preview.status == FishSaleResultType.Status.FAVORITED
|
||||
)
|
||||
):
|
||||
_selection_summary.text += "\ntotal offer: $%d" % preview.payout
|
||||
if preview != null and preview.status == FishSaleResultType.Status.FAVORITED:
|
||||
_selection_summary.text += (
|
||||
"\nfavorited fish must be removed from the selection."
|
||||
)
|
||||
_sell_button.disabled = (
|
||||
fish_catch.is_favorited
|
||||
preview == null
|
||||
or not preview.is_success()
|
||||
or _transaction_in_progress
|
||||
or _closing
|
||||
)
|
||||
|
|
@ -326,7 +388,7 @@ func _refresh_upgrades() -> void:
|
|||
return
|
||||
var reel_level: int = _upgrades.get_reel_speed_level()
|
||||
var reel_cost: int = _upgrades.get_next_reel_speed_cost()
|
||||
_reel_level.text = "Level %d" % reel_level
|
||||
_reel_level.text = "level %d" % reel_level
|
||||
_reel_purchase.disabled = (
|
||||
reel_cost < 0
|
||||
or _transaction_in_progress
|
||||
|
|
@ -335,7 +397,7 @@ func _refresh_upgrades() -> void:
|
|||
)
|
||||
if reel_cost < 0:
|
||||
_reel_effect.text = "%.2f×" % _upgrades.get_reel_speed_multiplier()
|
||||
_reel_cost.text = "MAX"
|
||||
_reel_cost.text = "max"
|
||||
else:
|
||||
_reel_effect.text = (
|
||||
"%.2f× → %.2f×"
|
||||
|
|
@ -345,10 +407,10 @@ func _refresh_upgrades() -> void:
|
|||
]
|
||||
)
|
||||
_reel_cost.text = "$%d" % reel_cost
|
||||
_reel_purchase.text = "MAX" if reel_cost < 0 else "Purchase"
|
||||
_reel_purchase.text = "max" if reel_cost < 0 else "purchase"
|
||||
var barrier_level: int = _upgrades.get_barrier_power_level()
|
||||
var barrier_cost: int = _upgrades.get_next_barrier_power_cost()
|
||||
_barrier_level.text = "Level %d" % barrier_level
|
||||
_barrier_level.text = "level %d" % barrier_level
|
||||
_barrier_purchase.disabled = (
|
||||
barrier_cost < 0
|
||||
or _transaction_in_progress
|
||||
|
|
@ -357,7 +419,7 @@ func _refresh_upgrades() -> void:
|
|||
)
|
||||
if barrier_cost < 0:
|
||||
_barrier_effect.text = "%d damage" % _upgrades.get_barrier_damage()
|
||||
_barrier_cost.text = "MAX"
|
||||
_barrier_cost.text = "max"
|
||||
else:
|
||||
_barrier_effect.text = (
|
||||
"%d damage → %d damage"
|
||||
|
|
@ -367,7 +429,7 @@ func _refresh_upgrades() -> void:
|
|||
]
|
||||
)
|
||||
_barrier_cost.text = "$%d" % barrier_cost
|
||||
_barrier_purchase.text = "MAX" if barrier_cost < 0 else "Purchase"
|
||||
_barrier_purchase.text = "max" if barrier_cost < 0 else "purchase"
|
||||
|
||||
|
||||
func _refresh_supplies() -> void:
|
||||
|
|
@ -385,7 +447,7 @@ func _refresh_supplies() -> void:
|
|||
button.icon = item.icon
|
||||
button.expand_icon = true
|
||||
button.alignment = HORIZONTAL_ALIGNMENT_LEFT
|
||||
button.text = "%s\n$%d • Owned %d" % [
|
||||
button.text = "%s\n$%d • owned %d" % [
|
||||
item.display_name,
|
||||
FishingShopStockType.get_price(item_id),
|
||||
_bag.get_quantity(item_id),
|
||||
|
|
@ -409,7 +471,7 @@ func _refresh_cooler_capacity() -> void:
|
|||
var level: int = _cooler_capacity.get_level()
|
||||
var cost: int = _cooler_capacity.get_next_cost()
|
||||
var next_capacity: int = _cooler_capacity.get_next_capacity()
|
||||
_cooler_level.text = "Level %d" % level
|
||||
_cooler_level.text = "level %d" % level
|
||||
_cooler_purchase.disabled = (
|
||||
cost < 0
|
||||
or _transaction_in_progress
|
||||
|
|
@ -418,46 +480,99 @@ func _refresh_cooler_capacity() -> void:
|
|||
)
|
||||
if cost < 0:
|
||||
_cooler_effect.text = "%d fish" % _cooler_capacity.get_capacity()
|
||||
_cooler_cost.text = "MAX"
|
||||
_cooler_purchase.text = "MAX"
|
||||
_cooler_cost.text = "max"
|
||||
_cooler_purchase.text = "max"
|
||||
else:
|
||||
_cooler_effect.text = "%d → %d fish" % [
|
||||
_cooler_capacity.get_capacity(),
|
||||
next_capacity,
|
||||
]
|
||||
_cooler_cost.text = "$%d" % cost
|
||||
_cooler_purchase.text = "Purchase"
|
||||
_cooler_purchase.text = "purchase"
|
||||
|
||||
|
||||
func _on_fish_selected(index: int) -> void:
|
||||
if index < 0 or index >= _fish_list.item_count:
|
||||
return
|
||||
_selected_catch_id = StringName(str(_fish_list.get_item_metadata(index)))
|
||||
var input_generation: int = _selection_input_generation
|
||||
call_deferred(
|
||||
"_apply_keyboard_fish_selection",
|
||||
index,
|
||||
input_generation
|
||||
)
|
||||
|
||||
|
||||
func _apply_keyboard_fish_selection(
|
||||
index: int,
|
||||
input_generation: int,
|
||||
) -> void:
|
||||
if (
|
||||
input_generation != _selection_input_generation
|
||||
or index < 0
|
||||
or index >= _fish_list.item_count
|
||||
):
|
||||
return
|
||||
var catch_id := StringName(str(_fish_list.get_item_metadata(index)))
|
||||
_fish_selection.select_only(catch_id)
|
||||
_feedback.text = ""
|
||||
_refresh_fish_list()
|
||||
_refresh_selected_fish()
|
||||
|
||||
|
||||
func _on_fish_clicked(
|
||||
index: int,
|
||||
_position: Vector2,
|
||||
mouse_button_index: int,
|
||||
) -> void:
|
||||
if (
|
||||
mouse_button_index != MOUSE_BUTTON_LEFT
|
||||
or index < 0
|
||||
or index >= _fish_list.item_count
|
||||
):
|
||||
return
|
||||
_selection_input_generation += 1
|
||||
var catch_id := StringName(str(_fish_list.get_item_metadata(index)))
|
||||
_fish_selection.apply_click(
|
||||
catch_id,
|
||||
Input.is_key_pressed(KEY_CTRL),
|
||||
Input.is_key_pressed(KEY_SHIFT)
|
||||
)
|
||||
_feedback.text = ""
|
||||
_refresh_fish_list()
|
||||
_refresh_selected_fish()
|
||||
|
||||
|
||||
func _open_sale_confirmation() -> void:
|
||||
var fish_catch: FishCatchType = _get_selected_catch()
|
||||
if not _is_transaction_context_valid() or fish_catch == null:
|
||||
_feedback.text = "This fish is no longer available."
|
||||
var selected_ids: Array[StringName] = _fish_selection.get_selected_ids()
|
||||
if not _is_transaction_context_valid() or selected_ids.is_empty():
|
||||
_feedback.text = "the fish selection is no longer available."
|
||||
_refresh_all()
|
||||
return
|
||||
if fish_catch.is_favorited:
|
||||
_feedback.text = "Favorited fish cannot be sold."
|
||||
var preview: FishSaleResultType = _sale_service.preview_batch(
|
||||
selected_ids,
|
||||
_buyer
|
||||
)
|
||||
if not preview.is_success():
|
||||
_feedback.text = (
|
||||
"favorited fish cannot be sold. "
|
||||
+ "remove them from the selection first."
|
||||
if preview.status == FishSaleResultType.Status.FAVORITED
|
||||
else preview.get_message()
|
||||
)
|
||||
return
|
||||
_confirmation_catch_id = fish_catch.catch_id
|
||||
var offer: int = _buyer.get_offer(fish_catch.sale_value)
|
||||
_confirmation_catch_ids = selected_ids.duplicate()
|
||||
_confirmation_generation = _generation
|
||||
_confirmation_text.text = (
|
||||
"Sell this %.2f lb %s to the Fishing Shop for $%d?"
|
||||
% [fish_catch.weight_lb, fish_catch.fish.display_name, offer]
|
||||
"sell %d fish to the fishing shop for $%d?"
|
||||
% [preview.fish_count, preview.payout]
|
||||
)
|
||||
_confirmation.show()
|
||||
_cancel_sale.grab_focus()
|
||||
|
||||
|
||||
func _close_sale_confirmation() -> void:
|
||||
_confirmation_catch_id = StringName()
|
||||
_confirmation_catch_ids.clear()
|
||||
_confirmation_generation = -1
|
||||
_confirmation.hide()
|
||||
|
||||
|
||||
|
|
@ -465,18 +580,23 @@ func _on_confirm_sale() -> void:
|
|||
if _transaction_in_progress:
|
||||
return
|
||||
var transaction_generation: int = _generation
|
||||
var catch_id: StringName = _confirmation_catch_id
|
||||
var catch_ids: Array[StringName] = _confirmation_catch_ids.duplicate()
|
||||
var confirmation_generation: int = _confirmation_generation
|
||||
_close_sale_confirmation()
|
||||
if (
|
||||
catch_id.is_empty()
|
||||
catch_ids.is_empty()
|
||||
or not _is_transaction_context_valid()
|
||||
or transaction_generation != _generation
|
||||
or confirmation_generation != transaction_generation
|
||||
or _buyer.id != MAIN_SHOP_BUYER_ID
|
||||
):
|
||||
_feedback.text = "Unable to complete sale."
|
||||
_feedback.text = "unable to complete sale."
|
||||
return
|
||||
_transaction_in_progress = true
|
||||
var result: FishSaleResultType = _sale_service.sell(catch_id, _buyer)
|
||||
var result: FishSaleResultType = _sale_service.sell_batch(
|
||||
catch_ids,
|
||||
_buyer
|
||||
)
|
||||
_transaction_in_progress = false
|
||||
if (
|
||||
transaction_generation != _generation
|
||||
|
|
@ -484,8 +604,15 @@ func _on_confirm_sale() -> void:
|
|||
):
|
||||
return
|
||||
if result.is_success():
|
||||
_feedback.text = "Fish sold for $%d." % result.payout
|
||||
_selected_catch_id = StringName()
|
||||
_feedback.text = (
|
||||
"fish sold for $%d."
|
||||
if result.fish_count == 1
|
||||
else "%d fish sold for $%d." % [
|
||||
result.fish_count,
|
||||
result.payout,
|
||||
]
|
||||
)
|
||||
_fish_selection.remove_ids(catch_ids)
|
||||
else:
|
||||
_feedback.text = result.get_message()
|
||||
_refresh_all()
|
||||
|
|
@ -501,14 +628,14 @@ func _purchase_barrier_power() -> void:
|
|||
|
||||
func _purchase_supply(item_id: StringName) -> void:
|
||||
if _transaction_in_progress or not _is_transaction_context_valid():
|
||||
_feedback.text = "Unable to complete purchase."
|
||||
_feedback.text = "unable to complete purchase."
|
||||
return
|
||||
var price: int = FishingShopStockType.get_price(item_id)
|
||||
if not _bag.can_add_item(item_id, 1):
|
||||
_feedback.text = "Bag cannot accept this item."
|
||||
_feedback.text = "bag cannot accept this item."
|
||||
return
|
||||
if not _wallet.can_afford(price):
|
||||
_feedback.text = "Not enough money."
|
||||
_feedback.text = "not enough money."
|
||||
return
|
||||
var transaction_generation: int = _generation
|
||||
_transaction_in_progress = true
|
||||
|
|
@ -522,21 +649,21 @@ func _purchase_supply(item_id: StringName) -> void:
|
|||
if transaction_generation != _generation or not visible:
|
||||
return
|
||||
_feedback.text = (
|
||||
"Item purchased." if purchased else "Unable to complete purchase."
|
||||
"item purchased." if purchased else "unable to complete purchase."
|
||||
)
|
||||
_refresh_all()
|
||||
|
||||
|
||||
func _purchase_cooler_capacity() -> void:
|
||||
if _transaction_in_progress or not _is_transaction_context_valid():
|
||||
_feedback.text = "Unable to complete purchase."
|
||||
_feedback.text = "unable to complete purchase."
|
||||
return
|
||||
var cost: int = _cooler_capacity.get_next_cost()
|
||||
if cost < 0:
|
||||
_feedback.text = "Maximum level reached."
|
||||
_feedback.text = "maximum level reached."
|
||||
return
|
||||
if not _wallet.can_afford(cost):
|
||||
_feedback.text = "Not enough money."
|
||||
_feedback.text = "not enough money."
|
||||
return
|
||||
var transaction_generation: int = _generation
|
||||
_transaction_in_progress = true
|
||||
|
|
@ -545,16 +672,16 @@ func _purchase_cooler_capacity() -> void:
|
|||
if transaction_generation != _generation or not visible:
|
||||
return
|
||||
_feedback.text = (
|
||||
"Upgrade purchased."
|
||||
"upgrade purchased."
|
||||
if purchased
|
||||
else "Unable to complete purchase."
|
||||
else "unable to complete purchase."
|
||||
)
|
||||
_refresh_all()
|
||||
|
||||
|
||||
func _purchase_upgrade(is_reel_speed: bool) -> void:
|
||||
if _transaction_in_progress or not _is_transaction_context_valid():
|
||||
_feedback.text = "Unable to complete purchase."
|
||||
_feedback.text = "unable to complete purchase."
|
||||
return
|
||||
var cost: int = (
|
||||
_upgrades.get_next_reel_speed_cost()
|
||||
|
|
@ -562,10 +689,10 @@ func _purchase_upgrade(is_reel_speed: bool) -> void:
|
|||
else _upgrades.get_next_barrier_power_cost()
|
||||
)
|
||||
if cost < 0:
|
||||
_feedback.text = "Maximum level reached."
|
||||
_feedback.text = "maximum level reached."
|
||||
return
|
||||
if not _wallet.can_afford(cost):
|
||||
_feedback.text = "Not enough money."
|
||||
_feedback.text = "not enough money."
|
||||
return
|
||||
var transaction_generation: int = _generation
|
||||
_transaction_in_progress = true
|
||||
|
|
@ -578,17 +705,18 @@ func _purchase_upgrade(is_reel_speed: bool) -> void:
|
|||
if transaction_generation != _generation or not visible:
|
||||
return
|
||||
_feedback.text = (
|
||||
"Upgrade purchased."
|
||||
"upgrade purchased."
|
||||
if purchased
|
||||
else "Unable to complete purchase."
|
||||
else "unable to complete purchase."
|
||||
)
|
||||
_refresh_all()
|
||||
|
||||
|
||||
func _get_selected_catch() -> FishCatchType:
|
||||
if _inventory == null or _selected_catch_id.is_empty():
|
||||
var focused_id: StringName = _fish_selection.get_focused_id()
|
||||
if _inventory == null or focused_id.is_empty():
|
||||
return null
|
||||
return _inventory.get_catch_by_id(_selected_catch_id)
|
||||
return _inventory.get_catch_by_id(focused_id)
|
||||
|
||||
|
||||
func _is_transaction_context_valid() -> bool:
|
||||
|
|
@ -607,12 +735,22 @@ func _is_transaction_context_valid() -> bool:
|
|||
|
||||
func _on_inventory_changed() -> void:
|
||||
_refresh_fish_list()
|
||||
if (
|
||||
not _confirmation_catch_id.is_empty()
|
||||
and _inventory.get_catch_by_id(_confirmation_catch_id) == null
|
||||
):
|
||||
_close_sale_confirmation()
|
||||
_feedback.text = "This fish is no longer available."
|
||||
if not _confirmation_catch_ids.is_empty():
|
||||
var preview: FishSaleResultType = _sale_service.preview_batch(
|
||||
_confirmation_catch_ids,
|
||||
_buyer
|
||||
)
|
||||
if (
|
||||
_confirmation_generation != _generation
|
||||
or not preview.is_success()
|
||||
):
|
||||
_close_sale_confirmation()
|
||||
_feedback.text = (
|
||||
"favorited fish cannot be sold. "
|
||||
+ "remove them from the selection first."
|
||||
if preview.status == FishSaleResultType.Status.FAVORITED
|
||||
else preview.get_message()
|
||||
)
|
||||
_refresh_selected_fish()
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ theme_override_constants/separation = 8
|
|||
[node name="Title" type="Label" parent="ShopPanel/Margin/Layout/Header"]
|
||||
layout_mode = 2
|
||||
size_flags_horizontal = 3
|
||||
text = "Fishing Shop"
|
||||
text = "fishing shop"
|
||||
theme_override_font_sizes/font_size = 24
|
||||
theme_override_colors/font_color = Color(0.208, 0.725, 0.78, 1)
|
||||
|
||||
|
|
@ -78,12 +78,12 @@ texture_filter = 1
|
|||
[node name="WalletLabel" type="Label" parent="ShopPanel/Margin/Layout/Header"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Wallet: $0"
|
||||
text = "wallet: $0"
|
||||
|
||||
[node name="CloseButton" type="Button" parent="ShopPanel/Margin/Layout/Header"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Close"
|
||||
text = "close"
|
||||
custom_minimum_size = Vector2(72, 30)
|
||||
|
||||
[node name="Feedback" type="Label" parent="ShopPanel/Margin/Layout"]
|
||||
|
|
@ -107,7 +107,7 @@ theme_override_constants/separation = 6
|
|||
[node name="SalesTitle" type="Label" parent="ShopPanel/Margin/Layout/Body/FishSales"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Sell individual Cooler fish • Full base value"
|
||||
text = "sell individual cooler fish • full base value"
|
||||
theme_override_font_sizes/font_size = 17
|
||||
|
||||
[node name="SalesBody" type="HSplitContainer" parent="ShopPanel/Margin/Layout/Body/FishSales"]
|
||||
|
|
@ -133,7 +133,7 @@ layout_mode = 2
|
|||
[node name="FishEmpty" type="Label" parent="ShopPanel/Margin/Layout/Body/FishSales/SalesBody/ListPanel/ListMargin/ListStack"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Your Cooler is empty."
|
||||
text = "your cooler is empty."
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="FishList" type="ItemList" parent="ShopPanel/Margin/Layout/Body/FishSales/SalesBody/ListPanel/ListMargin/ListStack"]
|
||||
|
|
@ -142,6 +142,7 @@ layout_mode = 2
|
|||
size_flags_vertical = 3
|
||||
fixed_icon_size = Vector2i(48, 36)
|
||||
icon_mode = 1
|
||||
select_mode = 1
|
||||
same_column_width = true
|
||||
|
||||
[node name="FishDetail" type="PanelContainer" parent="ShopPanel/Margin/Layout/Body/FishSales/SalesBody"]
|
||||
|
|
@ -170,22 +171,30 @@ texture_filter = 1
|
|||
[node name="FishName" type="Label" parent="ShopPanel/Margin/Layout/Body/FishSales/SalesBody/FishDetail/DetailMargin/DetailStack"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Select a fish"
|
||||
text = "select a fish"
|
||||
horizontal_alignment = 1
|
||||
theme_override_font_sizes/font_size = 19
|
||||
|
||||
[node name="FishDetails" type="Label" parent="ShopPanel/Margin/Layout/Body/FishSales/SalesBody/FishDetail/DetailMargin/DetailStack"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Choose one individual fish from your Cooler."
|
||||
text = "choose one individual fish from your cooler."
|
||||
horizontal_alignment = 1
|
||||
autowrap_mode = 2
|
||||
|
||||
[node name="SelectionSummary" type="Label" parent="ShopPanel/Margin/Layout/Body/FishSales/SalesBody/FishDetail/DetailMargin/DetailStack"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "no fish selected"
|
||||
horizontal_alignment = 1
|
||||
autowrap_mode = 2
|
||||
theme_override_colors/font_color = Color(0.208, 0.725, 0.78, 1)
|
||||
|
||||
[node name="SellButton" type="Button" parent="ShopPanel/Margin/Layout/Body/FishSales/SalesBody/FishDetail/DetailMargin/DetailStack"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
disabled = true
|
||||
text = "Sell Selected"
|
||||
text = "sell selected"
|
||||
|
||||
[node name="Upgrades" type="VBoxContainer" parent="ShopPanel/Margin/Layout/Body"]
|
||||
custom_minimum_size = Vector2(270, 0)
|
||||
|
|
@ -194,7 +203,7 @@ theme_override_constants/separation = 8
|
|||
|
||||
[node name="UpgradeTitle" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades"]
|
||||
layout_mode = 2
|
||||
text = "Fishing Upgrades"
|
||||
text = "fishing upgrades"
|
||||
theme_override_font_sizes/font_size = 17
|
||||
|
||||
[node name="Supplies" type="VBoxContainer" parent="ShopPanel/Margin/Layout/Body"]
|
||||
|
|
@ -204,7 +213,7 @@ theme_override_constants/separation = 6
|
|||
|
||||
[node name="Title" type="Label" parent="ShopPanel/Margin/Layout/Body/Supplies"]
|
||||
layout_mode = 2
|
||||
text = "Supplies"
|
||||
text = "supplies"
|
||||
theme_override_font_sizes/font_size = 17
|
||||
|
||||
[node name="Scroll" type="ScrollContainer" parent="ShopPanel/Margin/Layout/Body/Supplies"]
|
||||
|
|
@ -246,13 +255,13 @@ size_flags_horizontal = 3
|
|||
|
||||
[node name="Name" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard/Margin/Row/Data"]
|
||||
layout_mode = 2
|
||||
text = "Reel Speed"
|
||||
text = "reel speed"
|
||||
theme_override_font_sizes/font_size = 17
|
||||
|
||||
[node name="ReelLevel" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard/Margin/Row/Data"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Level 0"
|
||||
text = "level 0"
|
||||
|
||||
[node name="ReelEffect" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard/Margin/Row/Data"]
|
||||
unique_name_in_owner = true
|
||||
|
|
@ -267,7 +276,7 @@ text = "$50"
|
|||
[node name="ReelPurchase" type="Button" parent="ShopPanel/Margin/Layout/Body/Upgrades/ReelCard/Margin/Row"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Purchase"
|
||||
text = "purchase"
|
||||
|
||||
[node name="BarrierCard" type="PanelContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades"]
|
||||
layout_mode = 2
|
||||
|
|
@ -297,13 +306,13 @@ size_flags_horizontal = 3
|
|||
|
||||
[node name="Name" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard/Margin/Row/Data"]
|
||||
layout_mode = 2
|
||||
text = "Barrier Power"
|
||||
text = "barrier power"
|
||||
theme_override_font_sizes/font_size = 17
|
||||
|
||||
[node name="BarrierLevel" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard/Margin/Row/Data"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Level 0"
|
||||
text = "level 0"
|
||||
|
||||
[node name="BarrierEffect" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard/Margin/Row/Data"]
|
||||
unique_name_in_owner = true
|
||||
|
|
@ -318,7 +327,7 @@ text = "$100"
|
|||
[node name="BarrierPurchase" type="Button" parent="ShopPanel/Margin/Layout/Body/Upgrades/BarrierCard/Margin/Row"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Purchase"
|
||||
text = "purchase"
|
||||
|
||||
[node name="CoolerCard" type="PanelContainer" parent="ShopPanel/Margin/Layout/Body/Upgrades"]
|
||||
layout_mode = 2
|
||||
|
|
@ -348,13 +357,13 @@ size_flags_horizontal = 3
|
|||
|
||||
[node name="Name" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin/Row/Data"]
|
||||
layout_mode = 2
|
||||
text = "Cooler Capacity"
|
||||
text = "cooler capacity"
|
||||
theme_override_font_sizes/font_size = 17
|
||||
|
||||
[node name="CoolerLevel" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin/Row/Data"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Level 0"
|
||||
text = "level 0"
|
||||
|
||||
[node name="CoolerEffect" type="Label" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin/Row/Data"]
|
||||
unique_name_in_owner = true
|
||||
|
|
@ -369,7 +378,7 @@ text = "$75"
|
|||
[node name="CoolerPurchase" type="Button" parent="ShopPanel/Margin/Layout/Body/Upgrades/CoolerCard/Margin/Row"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Purchase"
|
||||
text = "purchase"
|
||||
|
||||
[node name="SaleConfirmation" type="PanelContainer" parent="."]
|
||||
unique_name_in_owner = true
|
||||
|
|
@ -413,10 +422,10 @@ theme_override_constants/separation = 8
|
|||
[node name="ConfirmSale" type="Button" parent="SaleConfirmation/Margin/Stack/Buttons"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Sell Fish"
|
||||
text = "sell fish"
|
||||
theme_type_variation = &"DangerButton"
|
||||
|
||||
[node name="CancelSale" type="Button" parent="SaleConfirmation/Margin/Stack/Buttons"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Cancel"
|
||||
text = "cancel"
|
||||
|
|
|
|||
|
|
@ -22,6 +22,9 @@ const ItemDragSourceType = preload("res://ui/item_drag_source.gd")
|
|||
const PlayerCoolerCapacityType = preload(
|
||||
"res://progression/player_cooler_capacity.gd"
|
||||
)
|
||||
const FishBatchSelectionType = preload(
|
||||
"res://ui/fish_batch_selection.gd"
|
||||
)
|
||||
|
||||
signal menu_visibility_changed(is_open: bool)
|
||||
|
||||
|
|
@ -68,6 +71,7 @@ enum CloseReason {
|
|||
@onready var _detail_texture: TextureRect = %DetailTexture
|
||||
@onready var _detail_name: Label = %DetailName
|
||||
@onready var _detail_data: Label = %DetailData
|
||||
@onready var _selection_summary: Label = %SelectionSummary
|
||||
@onready var _favorite_button: Button = %FavoriteButton
|
||||
@onready var _sell_button: Button = %SellButton
|
||||
@onready var _sale_unavailable: Label = %SaleUnavailable
|
||||
|
|
@ -94,7 +98,7 @@ var _cooler_capacity: PlayerCoolerCapacityType
|
|||
var _current_section: Section = Section.COOLER
|
||||
var _sort_mode: SortMode = SortMode.CATCH_ORDER
|
||||
var _sort_descending: bool = true
|
||||
var _selected_catch_id: StringName
|
||||
var _fish_selection := FishBatchSelectionType.new()
|
||||
var _selected_bag_item_id: StringName
|
||||
var _prior_movement_enabled: bool = true
|
||||
var _prior_camera_input_enabled: bool = true
|
||||
|
|
@ -102,9 +106,10 @@ var _prior_mouse_mode: Input.MouseMode = Input.MOUSE_MODE_VISIBLE
|
|||
var _control_snapshot_stored: bool = false
|
||||
var _mouse_snapshot_stored: bool = false
|
||||
var _menu_generation: int = 0
|
||||
var _confirmation_catch_id: StringName
|
||||
var _confirmation_catch_ids: Array[StringName] = []
|
||||
var _confirmation_buyer: FishBuyerProfileType
|
||||
var _confirmation_buyer_id: StringName
|
||||
var _confirmation_generation: int = -1
|
||||
var _sale_in_progress: bool = false
|
||||
|
||||
|
||||
|
|
@ -123,9 +128,9 @@ func _ready() -> void:
|
|||
_sell_button.pressed.connect(_on_sell_pressed)
|
||||
_confirm_sale_button.pressed.connect(_on_confirm_sale_pressed)
|
||||
_cancel_sale_button.pressed.connect(_close_sale_confirmation)
|
||||
_sort_option.add_item("Catch order", SortMode.CATCH_ORDER)
|
||||
_sort_option.add_item("Name", SortMode.NAME)
|
||||
_sort_option.add_item("Rarity", SortMode.RARITY)
|
||||
_sort_option.add_item("catch order", SortMode.CATCH_ORDER)
|
||||
_sort_option.add_item("name", SortMode.NAME)
|
||||
_sort_option.add_item("rarity", SortMode.RARITY)
|
||||
_sort_option.select(SortMode.CATCH_ORDER)
|
||||
_update_sort_direction_text()
|
||||
_show_section(_current_section)
|
||||
|
|
@ -157,6 +162,7 @@ func setup(
|
|||
_hotbar = hotbar
|
||||
_item_catalog = item_catalog
|
||||
_cooler_capacity = cooler_capacity
|
||||
_fish_selection.clear()
|
||||
if not _inventory.catches_changed.is_connected(_on_inventory_changed):
|
||||
_inventory.catches_changed.connect(_on_inventory_changed)
|
||||
if not _collection_log.fish_discovered.is_connected(_on_fish_discovered):
|
||||
|
|
@ -234,6 +240,13 @@ func close_menu(
|
|||
return
|
||||
get_viewport().gui_cancel_drag()
|
||||
_close_sale_confirmation()
|
||||
if reason in [
|
||||
CloseReason.BITE_STARTED,
|
||||
CloseReason.WATER_RECOVERY,
|
||||
CloseReason.SESSION_END,
|
||||
CloseReason.TEARDOWN,
|
||||
]:
|
||||
_fish_selection.clear()
|
||||
var closing_generation: int = _menu_generation
|
||||
visible = false
|
||||
get_viewport().gui_release_focus()
|
||||
|
|
@ -249,6 +262,7 @@ func close_menu(
|
|||
|
||||
|
||||
func close_for_water_recovery() -> void:
|
||||
_fish_selection.clear()
|
||||
close_menu(CloseReason.WATER_RECOVERY, false)
|
||||
|
||||
|
||||
|
|
@ -257,13 +271,14 @@ func close_for_game_menu() -> void:
|
|||
|
||||
|
||||
func close_for_session_end() -> void:
|
||||
_fish_selection.clear()
|
||||
close_menu(CloseReason.SESSION_END, false)
|
||||
|
||||
|
||||
func _exit_tree() -> void:
|
||||
if visible:
|
||||
close_menu(CloseReason.TEARDOWN, false)
|
||||
_selected_catch_id = StringName()
|
||||
_fish_selection.clear()
|
||||
_menu_generation += 1
|
||||
|
||||
|
||||
|
|
@ -340,11 +355,11 @@ func _on_sort_direction_pressed() -> void:
|
|||
func _update_sort_direction_text() -> void:
|
||||
match _sort_mode:
|
||||
SortMode.CATCH_ORDER:
|
||||
_sort_direction.text = "Newest first" if _sort_descending else "Oldest first"
|
||||
_sort_direction.text = "newest first" if _sort_descending else "oldest first"
|
||||
SortMode.NAME:
|
||||
_sort_direction.text = "Z–A" if _sort_descending else "A–Z"
|
||||
_sort_direction.text = "z–a" if _sort_descending else "a–z"
|
||||
SortMode.RARITY:
|
||||
_sort_direction.text = "High to low" if _sort_descending else "Low to high"
|
||||
_sort_direction.text = "high to low" if _sort_descending else "low to high"
|
||||
|
||||
|
||||
func _refresh_all() -> void:
|
||||
|
|
@ -386,9 +401,9 @@ func _refresh_bag() -> void:
|
|||
item.get_category_name(),
|
||||
]
|
||||
card.tooltip_text = (
|
||||
"Drag to a hotbar slot."
|
||||
"drag to a hotbar slot."
|
||||
if item.hotbar_allowed
|
||||
else "This item cannot be assigned to the hotbar."
|
||||
else "this item cannot be assigned to the hotbar."
|
||||
)
|
||||
card.setup(item.item_id, item.display_name, item.icon)
|
||||
card.pressed.connect(_select_bag_item.bind(item.item_id))
|
||||
|
|
@ -427,19 +442,19 @@ func _update_bag_detail() -> void:
|
|||
_bag_detail_texture.texture = item.icon if item != null else null
|
||||
_bag_detail_name.text = item.display_name if item != null else ""
|
||||
_bag_detail_data.text = (
|
||||
"%s\nQuantity: %d\n%s\n%s"
|
||||
"%s\nquantity: %d\n%s\n%s"
|
||||
% [
|
||||
item.get_category_name(),
|
||||
quantity,
|
||||
item.description,
|
||||
(
|
||||
"Can be assigned to the hotbar."
|
||||
"can be assigned to the hotbar."
|
||||
if item.hotbar_allowed
|
||||
else "Cannot be assigned to the hotbar."
|
||||
else "cannot be assigned to the hotbar."
|
||||
),
|
||||
]
|
||||
if item != null
|
||||
else "Select a Bag item for details."
|
||||
else "select a bag item for details."
|
||||
)
|
||||
|
||||
|
||||
|
|
@ -470,8 +485,8 @@ func _refresh_economy_summary() -> void:
|
|||
if _inventory != null
|
||||
else 0
|
||||
)
|
||||
_wallet_balance.text = "Wallet: $%d" % balance
|
||||
_held_value.text = "Held fish base value: $%d" % held_total
|
||||
_wallet_balance.text = "wallet: $%d" % balance
|
||||
_held_value.text = "held fish base value: $%d" % held_total
|
||||
_cooler_count.text = "%d / %d" % [
|
||||
_inventory.get_all_catches().size() if _inventory != null else 0,
|
||||
_cooler_capacity.get_capacity() if _cooler_capacity != null else 0,
|
||||
|
|
@ -493,16 +508,18 @@ func _refresh_inventory() -> void:
|
|||
catches.append(fish_catch)
|
||||
catches.sort_custom(_compare_catches)
|
||||
_inventory_empty.visible = catches.is_empty()
|
||||
var visible_ids: Array[StringName] = []
|
||||
for fish_catch: FishCatchType in catches:
|
||||
visible_ids.append(fish_catch.catch_id)
|
||||
_fish_selection.set_visible_order(visible_ids)
|
||||
|
||||
var selected: FishCatchType
|
||||
if _inventory != null:
|
||||
selected = _inventory.get_catch(_selected_catch_id)
|
||||
if selected == null and not catches.is_empty():
|
||||
selected = catches.front()
|
||||
_selected_catch_id = selected.catch_id
|
||||
selected = _inventory.get_catch(_fish_selection.get_focused_id())
|
||||
for fish_catch: FishCatchType in catches:
|
||||
_inventory_grid.add_child(_create_inventory_card(fish_catch))
|
||||
_update_inventory_detail(selected)
|
||||
_update_sale_summary()
|
||||
_update_sort_direction_text()
|
||||
|
||||
|
||||
|
|
@ -527,11 +544,17 @@ func _create_inventory_card(fish_catch: FishCatchType) -> Button:
|
|||
card.theme_type_variation = &"CardButton"
|
||||
card.custom_minimum_size = Vector2(132.0, 118.0)
|
||||
card.toggle_mode = true
|
||||
card.button_pressed = fish_catch.catch_id == _selected_catch_id
|
||||
card.pressed.connect(_select_catch.bind(fish_catch.catch_id))
|
||||
var is_selected: bool = _fish_selection.is_selected(fish_catch.catch_id)
|
||||
var is_focused: bool = (
|
||||
fish_catch.catch_id == _fish_selection.get_focused_id()
|
||||
)
|
||||
card.button_pressed = is_selected
|
||||
card.pressed.connect(_on_catch_card_pressed.bind(fish_catch.catch_id))
|
||||
_apply_inventory_card_styles(
|
||||
card,
|
||||
UIPalette.get_rarity_color(fish_catch.fish.rarity)
|
||||
UIPalette.get_rarity_color(fish_catch.fish.rarity),
|
||||
is_selected,
|
||||
is_focused
|
||||
)
|
||||
|
||||
var content := VBoxContainer.new()
|
||||
|
|
@ -573,34 +596,83 @@ func _create_inventory_card(fish_catch: FishCatchType) -> Button:
|
|||
favorite_marker.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
favorite_marker.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
card.add_child(favorite_marker)
|
||||
if is_selected:
|
||||
var selected_marker := Label.new()
|
||||
selected_marker.text = "✓"
|
||||
selected_marker.add_theme_color_override(
|
||||
"font_color",
|
||||
UIPalette.SUCCESS
|
||||
)
|
||||
selected_marker.add_theme_font_size_override("font_size", 18)
|
||||
selected_marker.set_anchors_preset(Control.PRESET_TOP_LEFT)
|
||||
selected_marker.offset_left = 8.0
|
||||
selected_marker.offset_top = 5.0
|
||||
selected_marker.offset_right = 30.0
|
||||
selected_marker.offset_bottom = 29.0
|
||||
selected_marker.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
card.add_child(selected_marker)
|
||||
if is_focused:
|
||||
var focus_marker := Label.new()
|
||||
focus_marker.text = "◆"
|
||||
focus_marker.add_theme_color_override(
|
||||
"font_color",
|
||||
UIPalette.PRIMARY
|
||||
)
|
||||
focus_marker.set_anchors_preset(Control.PRESET_BOTTOM_RIGHT)
|
||||
focus_marker.offset_left = -26.0
|
||||
focus_marker.offset_top = -25.0
|
||||
focus_marker.offset_right = -8.0
|
||||
focus_marker.offset_bottom = -7.0
|
||||
focus_marker.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
focus_marker.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
card.add_child(focus_marker)
|
||||
return card
|
||||
|
||||
|
||||
func _apply_inventory_card_styles(
|
||||
card: Button,
|
||||
rarity_color: Color,
|
||||
is_selected: bool,
|
||||
is_focused: bool,
|
||||
) -> void:
|
||||
card.add_theme_stylebox_override(
|
||||
"normal",
|
||||
_create_inventory_card_style(rarity_color, 0)
|
||||
_create_inventory_card_style(
|
||||
rarity_color,
|
||||
0,
|
||||
is_selected,
|
||||
is_focused
|
||||
)
|
||||
)
|
||||
card.add_theme_stylebox_override(
|
||||
"hover",
|
||||
_create_inventory_card_style(rarity_color, 1)
|
||||
_create_inventory_card_style(
|
||||
rarity_color,
|
||||
1,
|
||||
is_selected,
|
||||
is_focused
|
||||
)
|
||||
)
|
||||
card.add_theme_stylebox_override(
|
||||
"focus",
|
||||
_create_inventory_card_style(rarity_color, 1)
|
||||
_create_inventory_card_style(
|
||||
rarity_color,
|
||||
1,
|
||||
is_selected,
|
||||
true
|
||||
)
|
||||
)
|
||||
card.add_theme_stylebox_override(
|
||||
"pressed",
|
||||
_create_inventory_card_style(rarity_color, 2)
|
||||
_create_inventory_card_style(rarity_color, 2, true, is_focused)
|
||||
)
|
||||
|
||||
|
||||
func _create_inventory_card_style(
|
||||
rarity_color: Color,
|
||||
state_index: int,
|
||||
is_selected: bool,
|
||||
is_focused: bool,
|
||||
) -> StyleBoxFlat:
|
||||
var style := StyleBoxFlat.new()
|
||||
match state_index:
|
||||
|
|
@ -609,9 +681,13 @@ func _create_inventory_card_style(
|
|||
2:
|
||||
style.bg_color = UIPalette.PRIMARY.darkened(0.48)
|
||||
_:
|
||||
style.bg_color = UIPalette.ELEVATED_PANEL
|
||||
style.bg_color = (
|
||||
UIPalette.PRIMARY.darkened(0.55)
|
||||
if is_selected
|
||||
else UIPalette.ELEVATED_PANEL
|
||||
)
|
||||
style.border_color = rarity_color
|
||||
var border_width: int = 4 if state_index == 2 else 2
|
||||
var border_width: int = 4 if state_index == 2 or is_focused else 2
|
||||
style.set_border_width_all(border_width)
|
||||
style.set_corner_radius_all(8)
|
||||
style.content_margin_left = 7.0
|
||||
|
|
@ -621,13 +697,18 @@ func _create_inventory_card_style(
|
|||
return style
|
||||
|
||||
|
||||
func _select_catch(catch_id: StringName) -> void:
|
||||
func _on_catch_card_pressed(catch_id: StringName) -> void:
|
||||
if _inventory == null:
|
||||
return
|
||||
var selected: FishCatchType = _inventory.get_catch(catch_id)
|
||||
if selected == null:
|
||||
return
|
||||
_selected_catch_id = catch_id
|
||||
_fish_selection.apply_click(
|
||||
catch_id,
|
||||
Input.is_key_pressed(KEY_CTRL),
|
||||
Input.is_key_pressed(KEY_SHIFT)
|
||||
)
|
||||
_transaction_feedback.text = ""
|
||||
_refresh_inventory()
|
||||
|
||||
|
||||
|
|
@ -638,26 +719,28 @@ func _update_inventory_detail(fish_catch: FishCatchType) -> void:
|
|||
_detail_name.text = ""
|
||||
_detail_data.text = ""
|
||||
_favorite_button.disabled = true
|
||||
_favorite_button.text = "Favorite"
|
||||
_sell_button.disabled = true
|
||||
_sale_unavailable.text = ""
|
||||
_sale_unavailable.visible = false
|
||||
_favorite_button.text = "favorite"
|
||||
return
|
||||
_detail_texture.texture = fish_catch.fish.display_texture
|
||||
_detail_texture.visible = fish_catch.fish.display_texture != null
|
||||
_detail_name.text = fish_catch.fish.display_name
|
||||
var individual_preview: FishSaleResultType = (
|
||||
_sale_service.preview_batch([fish_catch.catch_id], _default_buyer)
|
||||
if _sale_service != null
|
||||
else null
|
||||
)
|
||||
var buyer_offer: int = (
|
||||
_default_buyer.get_offer(fish_catch.sale_value)
|
||||
if _default_buyer != null
|
||||
individual_preview.payout
|
||||
if individual_preview != null
|
||||
else -1
|
||||
)
|
||||
var buyer_offer_text: String = "Buyer unavailable"
|
||||
if buyer_offer >= 0:
|
||||
var buyer_offer_text: String = "buyer unavailable"
|
||||
if buyer_offer >= 0 and _default_buyer != null:
|
||||
buyer_offer_text = "%s offer: $%d" % [
|
||||
_default_buyer.display_name,
|
||||
buyer_offer,
|
||||
]
|
||||
_detail_data.text = "%.2f lb\n%s\nBase value: $%d\n%s" % [
|
||||
_detail_data.text = "%.2f lb\n%s\nbase value: $%d\n%s" % [
|
||||
fish_catch.weight_lb,
|
||||
fish_catch.fish.get_rarity_name(),
|
||||
fish_catch.sale_value,
|
||||
|
|
@ -665,35 +748,68 @@ func _update_inventory_detail(fish_catch: FishCatchType) -> void:
|
|||
]
|
||||
_favorite_button.disabled = false
|
||||
_favorite_button.text = (
|
||||
"Unfavorite" if fish_catch.is_favorited else "Favorite"
|
||||
"unfavorite" if fish_catch.is_favorited else "favorite"
|
||||
)
|
||||
var valid_sale_value: bool = fish_catch.sale_value >= 0
|
||||
var valid_buyer: bool = (
|
||||
_default_buyer != null
|
||||
and _default_buyer.is_valid()
|
||||
and buyer_offer >= 0
|
||||
|
||||
|
||||
func _update_sale_summary() -> void:
|
||||
var selected_ids: Array[StringName] = _fish_selection.get_selected_ids()
|
||||
var selected_count: int = selected_ids.size()
|
||||
_sell_button.text = (
|
||||
"sell fish"
|
||||
if selected_count == 1
|
||||
else "sell %d fish" % selected_count
|
||||
)
|
||||
_sell_button.disabled = (
|
||||
fish_catch.is_favorited
|
||||
or not valid_sale_value
|
||||
or not valid_buyer
|
||||
if selected_count == 0:
|
||||
_selection_summary.text = "no fish selected"
|
||||
_sell_button.text = "sell fish"
|
||||
_sell_button.disabled = true
|
||||
_sale_unavailable.text = ""
|
||||
_sale_unavailable.visible = false
|
||||
return
|
||||
var preview: FishSaleResultType = (
|
||||
_sale_service.preview_batch(selected_ids, _default_buyer)
|
||||
if _sale_service != null
|
||||
else null
|
||||
)
|
||||
if fish_catch.is_favorited:
|
||||
_sale_unavailable.text = "Favorited fish cannot be sold."
|
||||
elif not valid_sale_value:
|
||||
_sale_unavailable.text = "Invalid sale value."
|
||||
elif not valid_buyer:
|
||||
_sale_unavailable.text = "Buyer is unavailable."
|
||||
var count_text: String = (
|
||||
"1 fish selected"
|
||||
if selected_count == 1
|
||||
else "%d fish selected" % selected_count
|
||||
)
|
||||
_selection_summary.text = count_text
|
||||
if (
|
||||
preview != null
|
||||
and preview.payout >= 0
|
||||
and _default_buyer != null
|
||||
and (
|
||||
preview.is_success()
|
||||
or preview.status == FishSaleResultType.Status.FAVORITED
|
||||
)
|
||||
):
|
||||
_selection_summary.text += "\n%s total offer: $%d" % [
|
||||
_default_buyer.display_name,
|
||||
preview.payout,
|
||||
]
|
||||
_sell_button.disabled = preview == null or not preview.is_success()
|
||||
if preview != null and preview.status == FishSaleResultType.Status.FAVORITED:
|
||||
_sale_unavailable.text = (
|
||||
"favorited fish cannot be sold. "
|
||||
+ "remove them from the selection first."
|
||||
)
|
||||
elif preview != null and not preview.is_success():
|
||||
_sale_unavailable.text = preview.get_message()
|
||||
else:
|
||||
_sale_unavailable.text = ""
|
||||
_sale_unavailable.visible = not _sale_unavailable.text.is_empty()
|
||||
|
||||
|
||||
func _on_favorite_pressed() -> void:
|
||||
if _inventory == null or _selected_catch_id.is_empty():
|
||||
var focused_id: StringName = _fish_selection.get_focused_id()
|
||||
if _inventory == null or focused_id.is_empty():
|
||||
return
|
||||
var fish_catch: FishCatchType = _inventory.get_catch_by_id(
|
||||
_selected_catch_id
|
||||
focused_id
|
||||
)
|
||||
if fish_catch == null:
|
||||
_refresh_inventory()
|
||||
|
|
@ -712,46 +828,40 @@ func _on_favorite_pressed() -> void:
|
|||
|
||||
|
||||
func _on_sell_pressed() -> void:
|
||||
var selected_ids: Array[StringName] = _fish_selection.get_selected_ids()
|
||||
if (
|
||||
_inventory == null
|
||||
or _sale_service == null
|
||||
or _default_buyer == null
|
||||
or _selected_catch_id.is_empty()
|
||||
or selected_ids.is_empty()
|
||||
):
|
||||
return
|
||||
var fish_catch: FishCatchType = _inventory.get_catch_by_id(
|
||||
_selected_catch_id
|
||||
var preview: FishSaleResultType = _sale_service.preview_batch(
|
||||
selected_ids,
|
||||
_default_buyer
|
||||
)
|
||||
if fish_catch == null:
|
||||
_transaction_feedback.text = "Fish no longer exists."
|
||||
if not preview.is_success():
|
||||
_transaction_feedback.text = (
|
||||
"favorited fish cannot be sold. "
|
||||
+ "remove them from the selection first."
|
||||
if preview.status == FishSaleResultType.Status.FAVORITED
|
||||
else preview.get_message()
|
||||
)
|
||||
_refresh_inventory()
|
||||
return
|
||||
if fish_catch.is_favorited:
|
||||
_transaction_feedback.text = "Favorited fish cannot be sold."
|
||||
return
|
||||
if fish_catch.sale_value < 0:
|
||||
_transaction_feedback.text = "Invalid sale value."
|
||||
return
|
||||
if not _default_buyer.is_valid():
|
||||
_transaction_feedback.text = "Buyer is unavailable."
|
||||
return
|
||||
var buyer_offer: int = _default_buyer.get_offer(
|
||||
fish_catch.sale_value
|
||||
)
|
||||
if buyer_offer < 0:
|
||||
_transaction_feedback.text = "Invalid buyer offer."
|
||||
return
|
||||
_confirmation_catch_id = fish_catch.catch_id
|
||||
_confirmation_catch_ids = selected_ids.duplicate()
|
||||
_confirmation_buyer = _default_buyer
|
||||
_confirmation_buyer_id = _default_buyer.id
|
||||
_confirmation_generation = _menu_generation
|
||||
var fish_label: String = "fish"
|
||||
_confirmation_message.text = (
|
||||
"Sell this %.2f lb %s to the %s for $%d?\nBase value: $%d"
|
||||
"sell %d %s to the %s for $%d?\ncombined base value: $%d"
|
||||
% [
|
||||
fish_catch.weight_lb,
|
||||
fish_catch.fish.display_name,
|
||||
preview.fish_count,
|
||||
fish_label,
|
||||
_get_buyer_display_group(_default_buyer),
|
||||
buyer_offer,
|
||||
fish_catch.sale_value,
|
||||
preview.payout,
|
||||
preview.base_value,
|
||||
]
|
||||
)
|
||||
_sale_confirmation.visible = true
|
||||
|
|
@ -763,31 +873,39 @@ func _on_confirm_sale_pressed() -> void:
|
|||
if (
|
||||
_sale_in_progress
|
||||
or _sale_service == null
|
||||
or _confirmation_catch_id.is_empty()
|
||||
or _confirmation_catch_ids.is_empty()
|
||||
or _confirmation_buyer == null
|
||||
or _confirmation_buyer.id != _confirmation_buyer_id
|
||||
or _confirmation_generation != _menu_generation
|
||||
):
|
||||
return
|
||||
_sale_in_progress = true
|
||||
var requested_catch_id: StringName = _confirmation_catch_id
|
||||
var requested_catch_ids: Array[StringName] = (
|
||||
_confirmation_catch_ids.duplicate()
|
||||
)
|
||||
var transaction_generation: int = _menu_generation
|
||||
var transaction_buyer: FishBuyerProfileType = _confirmation_buyer
|
||||
_confirm_sale_button.disabled = true
|
||||
var result: FishSaleResultType = _sale_service.sell(
|
||||
requested_catch_id,
|
||||
_confirmation_buyer
|
||||
var result: FishSaleResultType = _sale_service.sell_batch(
|
||||
requested_catch_ids,
|
||||
transaction_buyer
|
||||
)
|
||||
_close_sale_confirmation()
|
||||
_transaction_feedback.text = result.get_message()
|
||||
_sale_in_progress = false
|
||||
if result.is_success() and _selected_catch_id == requested_catch_id:
|
||||
_selected_catch_id = StringName()
|
||||
if transaction_generation != _menu_generation or not visible:
|
||||
return
|
||||
_transaction_feedback.text = result.get_message()
|
||||
if result.is_success():
|
||||
_fish_selection.remove_ids(requested_catch_ids)
|
||||
_refresh_all()
|
||||
_inventory_tab.grab_focus()
|
||||
|
||||
|
||||
func _close_sale_confirmation() -> void:
|
||||
_confirmation_catch_id = StringName()
|
||||
_confirmation_catch_ids.clear()
|
||||
_confirmation_buyer = null
|
||||
_confirmation_buyer_id = StringName()
|
||||
_confirmation_generation = -1
|
||||
_sale_confirmation.visible = false
|
||||
_confirmation_message.text = ""
|
||||
_confirm_sale_button.disabled = false
|
||||
|
|
@ -796,25 +914,35 @@ func _close_sale_confirmation() -> void:
|
|||
func _revalidate_confirmation() -> void:
|
||||
if not _sale_confirmation.visible:
|
||||
return
|
||||
var fish_catch: FishCatchType
|
||||
if _inventory != null:
|
||||
fish_catch = _inventory.get_catch_by_id(_confirmation_catch_id)
|
||||
if fish_catch == null:
|
||||
if (
|
||||
_confirmation_generation != _menu_generation
|
||||
or _confirmation_catch_ids.is_empty()
|
||||
or _sale_service == null
|
||||
or _confirmation_buyer == null
|
||||
or _confirmation_buyer.id != _confirmation_buyer_id
|
||||
):
|
||||
_close_sale_confirmation()
|
||||
_transaction_feedback.text = "Fish no longer exists."
|
||||
elif fish_catch.is_favorited:
|
||||
_close_sale_confirmation()
|
||||
_transaction_feedback.text = "Favorited fish cannot be sold."
|
||||
elif fish_catch.sale_value < 0:
|
||||
_close_sale_confirmation()
|
||||
_transaction_feedback.text = "Invalid sale value."
|
||||
elif (
|
||||
_transaction_feedback.text = "sale selection is no longer available."
|
||||
return
|
||||
var preview: FishSaleResultType = _sale_service.preview_batch(
|
||||
_confirmation_catch_ids,
|
||||
_confirmation_buyer
|
||||
)
|
||||
if (
|
||||
not preview.is_success()
|
||||
or (
|
||||
_confirmation_buyer == null
|
||||
or _confirmation_buyer.id != _confirmation_buyer_id
|
||||
or not _confirmation_buyer.is_valid()
|
||||
)
|
||||
):
|
||||
_close_sale_confirmation()
|
||||
_transaction_feedback.text = "Buyer is unavailable."
|
||||
_transaction_feedback.text = (
|
||||
"favorited fish cannot be sold. "
|
||||
+ "remove them from the selection first."
|
||||
if preview.status == FishSaleResultType.Status.FAVORITED
|
||||
else preview.get_message()
|
||||
)
|
||||
|
||||
|
||||
func _get_buyer_display_group(
|
||||
|
|
@ -840,9 +968,9 @@ func _refresh_logbook() -> void:
|
|||
valid_species.append(fish)
|
||||
_logbook_empty.visible = valid_species.is_empty()
|
||||
_logbook_empty.text = (
|
||||
"No fish catalog configured."
|
||||
"no fish catalog configured."
|
||||
if valid_species.is_empty()
|
||||
else "No species discovered yet."
|
||||
else "no species discovered yet."
|
||||
)
|
||||
if not valid_species.is_empty() and _collection_log != null:
|
||||
var any_discovered: bool = false
|
||||
|
|
@ -881,12 +1009,12 @@ func _create_logbook_card(fish: FishDataType) -> Control:
|
|||
var owned_count: int = (
|
||||
_inventory.get_count(fish.id) if _inventory != null else 0
|
||||
)
|
||||
details.text = "%s\nOwned: %d" % [
|
||||
details.text = "%s\nowned: %d" % [
|
||||
fish.get_rarity_name(),
|
||||
owned_count,
|
||||
]
|
||||
else:
|
||||
details.text = "Undiscovered"
|
||||
details.text = "undiscovered"
|
||||
details.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||
content.add_child(details)
|
||||
return card
|
||||
|
|
@ -904,7 +1032,7 @@ func _create_texture_frame(
|
|||
texture_frame.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
|
||||
texture_frame.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||
if texture == null:
|
||||
texture_frame.tooltip_text = "Display texture unavailable"
|
||||
texture_frame.tooltip_text = "display texture unavailable"
|
||||
return texture_frame
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -56,39 +56,39 @@ layout_mode = 2
|
|||
size_flags_horizontal = 3
|
||||
theme_override_colors/font_color = Color(0.208, 0.725, 0.78, 1)
|
||||
theme_override_font_sizes/font_size = 22
|
||||
text = "Player Menu"
|
||||
text = "player menu"
|
||||
|
||||
[node name="WalletBalance" type="Label" parent="MenuPanel/Margin/Layout/Header"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Wallet: $0"
|
||||
text = "wallet: $0"
|
||||
theme_override_colors/font_color = Color(1, 0.82, 0.4, 1)
|
||||
|
||||
[node name="InventoryTab" type="Button" parent="MenuPanel/Margin/Layout/Header"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
toggle_mode = true
|
||||
text = "Cooler"
|
||||
text = "cooler"
|
||||
custom_minimum_size = Vector2(88, 30)
|
||||
|
||||
[node name="BagTab" type="Button" parent="MenuPanel/Margin/Layout/Header"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
toggle_mode = true
|
||||
text = "Bag"
|
||||
text = "bag"
|
||||
custom_minimum_size = Vector2(76, 30)
|
||||
|
||||
[node name="LogbookTab" type="Button" parent="MenuPanel/Margin/Layout/Header"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
toggle_mode = true
|
||||
text = "Logbook"
|
||||
text = "logbook"
|
||||
custom_minimum_size = Vector2(88, 30)
|
||||
|
||||
[node name="CloseButton" type="Button" parent="MenuPanel/Margin/Layout/Header"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Close"
|
||||
text = "close"
|
||||
custom_minimum_size = Vector2(68, 30)
|
||||
|
||||
[node name="Separator" type="HSeparator" parent="MenuPanel/Margin/Layout"]
|
||||
|
|
@ -123,7 +123,7 @@ theme_override_constants/separation = 6
|
|||
|
||||
[node name="SortLabel" type="Label" parent="MenuPanel/Margin/Layout/Content/InventorySection/SortBar"]
|
||||
layout_mode = 2
|
||||
text = "Sort:"
|
||||
text = "sort:"
|
||||
|
||||
[node name="SortOption" type="OptionButton" parent="MenuPanel/Margin/Layout/Content/InventorySection/SortBar"]
|
||||
unique_name_in_owner = true
|
||||
|
|
@ -134,7 +134,7 @@ custom_minimum_size = Vector2(130, 30)
|
|||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
custom_minimum_size = Vector2(112, 30)
|
||||
text = "Newest first"
|
||||
text = "newest first"
|
||||
|
||||
[node name="SortSpacer" type="Control" parent="MenuPanel/Margin/Layout/Content/InventorySection/SortBar"]
|
||||
layout_mode = 2
|
||||
|
|
@ -149,7 +149,7 @@ theme_override_colors/font_color = Color(0.208, 0.725, 0.78, 1)
|
|||
[node name="HeldValue" type="Label" parent="MenuPanel/Margin/Layout/Content/InventorySection/SortBar"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Held fish base value: $0"
|
||||
text = "held fish base value: $0"
|
||||
theme_override_colors/font_color = Color(0.682, 0.733, 0.761, 1)
|
||||
|
||||
[node name="InventoryBody" type="HSplitContainer" parent="MenuPanel/Margin/Layout/Content/InventorySection"]
|
||||
|
|
@ -176,7 +176,7 @@ theme_override_constants/separation = 6
|
|||
[node name="InventoryEmpty" type="Label" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody/InventoryList/InventoryListMargin/InventoryStack"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Your Cooler is empty."
|
||||
text = "your cooler is empty."
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="InventoryScroll" type="ScrollContainer" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody/InventoryList/InventoryListMargin/InventoryStack"]
|
||||
|
|
@ -228,17 +228,25 @@ layout_mode = 2
|
|||
autowrap_mode = 2
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="SelectionSummary" type="Label" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody/DetailPanel/DetailMargin/DetailStack"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "no fish selected"
|
||||
autowrap_mode = 2
|
||||
horizontal_alignment = 1
|
||||
theme_override_colors/font_color = Color(0.208, 0.725, 0.78, 1)
|
||||
|
||||
[node name="FavoriteButton" type="Button" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody/DetailPanel/DetailMargin/DetailStack"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
disabled = true
|
||||
text = "Favorite"
|
||||
text = "favorite"
|
||||
|
||||
[node name="SellButton" type="Button" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody/DetailPanel/DetailMargin/DetailStack"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
disabled = true
|
||||
text = "Sell"
|
||||
text = "sell"
|
||||
theme_type_variation = &"DangerButton"
|
||||
|
||||
[node name="SaleUnavailable" type="Label" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody/DetailPanel/DetailMargin/DetailStack"]
|
||||
|
|
@ -262,7 +270,7 @@ theme_override_constants/separation = 7
|
|||
|
||||
[node name="BagHint" type="Label" parent="MenuPanel/Margin/Layout/Content/BagSection"]
|
||||
layout_mode = 2
|
||||
text = "Drag hotbar-compatible items onto a slot below. Right-click a slot to clear it."
|
||||
text = "drag hotbar-compatible items onto a slot below. right-click a slot to clear it."
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="BagBody" type="HSplitContainer" parent="MenuPanel/Margin/Layout/Content/BagSection"]
|
||||
|
|
@ -289,7 +297,7 @@ theme_override_constants/separation = 6
|
|||
[node name="BagEmpty" type="Label" parent="MenuPanel/Margin/Layout/Content/BagSection/BagBody/BagList/Margin/Stack"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Your Bag is empty."
|
||||
text = "your bag is empty."
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="BagScroll" type="ScrollContainer" parent="MenuPanel/Margin/Layout/Content/BagSection/BagBody/BagList/Margin/Stack"]
|
||||
|
|
@ -337,7 +345,7 @@ horizontal_alignment = 1
|
|||
[node name="BagDetailData" type="Label" parent="MenuPanel/Margin/Layout/Content/BagSection/BagBody/BagDetail/Margin/Stack"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Select a Bag item for details."
|
||||
text = "select a bag item for details."
|
||||
horizontal_alignment = 1
|
||||
autowrap_mode = 2
|
||||
|
||||
|
|
@ -355,7 +363,7 @@ theme_override_constants/separation = 7
|
|||
[node name="LogbookEmpty" type="Label" parent="MenuPanel/Margin/Layout/Content/LogbookSection"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "No species discovered yet."
|
||||
text = "no species discovered yet."
|
||||
horizontal_alignment = 1
|
||||
|
||||
[node name="LogbookScroll" type="ScrollContainer" parent="MenuPanel/Margin/Layout/Content/LogbookSection"]
|
||||
|
|
@ -412,12 +420,12 @@ alignment = 1
|
|||
[node name="ConfirmSaleButton" type="Button" parent="MenuPanel/Margin/Layout/Content/SaleConfirmation/ConfirmationMargin/ConfirmationLayout/ConfirmationButtons"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Confirm"
|
||||
text = "confirm"
|
||||
theme_type_variation = &"DangerButton"
|
||||
custom_minimum_size = Vector2(120, 38)
|
||||
|
||||
[node name="CancelSaleButton" type="Button" parent="MenuPanel/Margin/Layout/Content/SaleConfirmation/ConfirmationMargin/ConfirmationLayout/ConfirmationButtons"]
|
||||
unique_name_in_owner = true
|
||||
layout_mode = 2
|
||||
text = "Cancel"
|
||||
text = "cancel"
|
||||
custom_minimum_size = Vector2(120, 38)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue