forked from woofmeow/straywild
366 lines
11 KiB
GDScript
366 lines
11 KiB
GDScript
class_name ShopSellInventory
|
|
extends Control
|
|
|
|
const MAIN_SHOP_BUYER_ID: StringName = &"main_fishing_shop"
|
|
|
|
var _layout: PlayerInventoryLayout
|
|
var _bag: PlayerBag
|
|
var _fish_inventory: FishInventory
|
|
var _item_catalog: ItemCatalog
|
|
var _buyer: FishBuyerProfile
|
|
var _reservations: PlayerAssetReservationService
|
|
var _network_sale: NetworkSaleService
|
|
var _inventory_grid: GeneralInventoryGrid
|
|
var _tray_grid: GridContainer
|
|
var _total_label: Label
|
|
var _feedback: Label
|
|
var _sell_button: Button
|
|
var _staged: Dictionary[String, Dictionary] = {}
|
|
|
|
|
|
func _ready() -> void:
|
|
UtilityPageStyle.apply_page(self)
|
|
_build_ui()
|
|
|
|
|
|
func setup(
|
|
layout: PlayerInventoryLayout,
|
|
bag: PlayerBag,
|
|
fish_inventory: FishInventory,
|
|
hotbar: PlayerHotbar,
|
|
item_catalog: ItemCatalog,
|
|
buyer: FishBuyerProfile,
|
|
reservations: PlayerAssetReservationService,
|
|
network_sale: NetworkSaleService,
|
|
) -> void:
|
|
_layout = layout
|
|
_bag = bag
|
|
_fish_inventory = fish_inventory
|
|
_item_catalog = item_catalog
|
|
_buyer = buyer
|
|
_reservations = reservations
|
|
_network_sale = network_sale
|
|
_inventory_grid.setup(
|
|
layout,
|
|
bag,
|
|
fish_inventory,
|
|
hotbar,
|
|
item_catalog,
|
|
PlayerInventoryLayout.InventoryContainer.INVENTORY,
|
|
)
|
|
_inventory_grid.entry_selected.connect(_on_inventory_entry_selected)
|
|
if _layout != null:
|
|
_layout.layout_changed.connect(_refresh)
|
|
if _network_sale != null:
|
|
_network_sale.local_sale_pending.connect(_on_sale_pending)
|
|
_network_sale.local_sale_finished.connect(_on_sale_finished)
|
|
_refresh()
|
|
|
|
|
|
func activate() -> void:
|
|
_feedback.text = "select or drag items into the sell tray"
|
|
_refresh()
|
|
var slot := _inventory_grid.get_first_occupied_slot()
|
|
if slot != null:
|
|
slot.grab_focus()
|
|
|
|
|
|
func clear_staged() -> void:
|
|
_staged.clear()
|
|
_refresh_tray()
|
|
|
|
|
|
func _on_inventory_entry_selected(kind: int, identity: StringName) -> void:
|
|
_stage(kind, identity)
|
|
|
|
|
|
func _stage(kind: int, identity: StringName) -> void:
|
|
if identity.is_empty():
|
|
return
|
|
var key := (
|
|
PlayerInventoryLayout.catch_key(identity)
|
|
if kind == PlayerInventoryLayout.EntryKind.CATCH
|
|
else PlayerInventoryLayout.item_key(identity)
|
|
)
|
|
if _staged.has(key):
|
|
_staged.erase(key)
|
|
_refresh_tray()
|
|
return
|
|
if kind == PlayerInventoryLayout.EntryKind.CATCH:
|
|
var fish_catch := _fish_inventory.get_catch_by_id(identity)
|
|
if fish_catch == null:
|
|
_feedback.text = "that catch is no longer available"
|
|
return
|
|
if fish_catch.is_favorited:
|
|
_feedback.text = "favorite catches cannot be sold"
|
|
return
|
|
if _reservations != null and _reservations.is_fish_reserved(identity):
|
|
_feedback.text = "reserved in a letter"
|
|
return
|
|
_staged[key] = {"kind": kind, "identity": identity, "quantity": 1}
|
|
else:
|
|
var item := _item_catalog.get_item_by_id(identity)
|
|
if not ItemResalePolicy.is_sellable(item):
|
|
_feedback.text = "equipment cannot be sold"
|
|
return
|
|
var available := _bag.get_quantity(identity)
|
|
if _reservations != null:
|
|
available = _reservations.get_available_item_quantity(identity)
|
|
if available < 1:
|
|
_feedback.text = "that item is reserved"
|
|
return
|
|
_staged[key] = {
|
|
"kind": kind,
|
|
"identity": identity,
|
|
"quantity": available,
|
|
}
|
|
_feedback.text = ""
|
|
_refresh_tray()
|
|
|
|
|
|
func _on_drop_payload(payload: Dictionary) -> void:
|
|
var payload_kind := str(payload.get("kind", ""))
|
|
_stage(
|
|
PlayerInventoryLayout.EntryKind.CATCH
|
|
if payload_kind == "cooler_fish"
|
|
else PlayerInventoryLayout.EntryKind.ITEM,
|
|
StringName(str(payload.get(
|
|
"catch_id" if payload_kind == "cooler_fish" else "item_id", ""
|
|
))),
|
|
)
|
|
|
|
|
|
func _on_remove_requested(key: String) -> void:
|
|
_staged.erase(key)
|
|
_refresh_tray()
|
|
|
|
|
|
func _submit_sale() -> void:
|
|
if _staged.is_empty() or _network_sale == null:
|
|
return
|
|
var catch_ids: Array[StringName] = []
|
|
var items: Dictionary[StringName, int] = {}
|
|
for record: Dictionary in _staged.values():
|
|
var identity := StringName(str(record.get("identity", "")))
|
|
if int(record.get("kind", -1)) == PlayerInventoryLayout.EntryKind.CATCH:
|
|
catch_ids.append(identity)
|
|
else:
|
|
items[identity] = int(record.get("quantity", 0))
|
|
if _network_sale.request_local_mixed_sale(
|
|
catch_ids, items, MAIN_SHOP_BUYER_ID
|
|
).is_empty():
|
|
_feedback.text = "sale could not be started"
|
|
|
|
|
|
func _on_sale_pending(_request_id: String) -> void:
|
|
_sell_button.disabled = true
|
|
_feedback.text = "selling…"
|
|
|
|
|
|
func _on_sale_finished(
|
|
_request_id: String,
|
|
accepted: bool,
|
|
message: String,
|
|
_catch_ids: Array[StringName],
|
|
_payout: int,
|
|
) -> void:
|
|
if accepted:
|
|
_staged.clear()
|
|
_feedback.text = message.to_lower()
|
|
_refresh()
|
|
|
|
|
|
func _refresh() -> void:
|
|
_validate_staged()
|
|
_inventory_grid.refresh()
|
|
_refresh_tray()
|
|
|
|
|
|
func _validate_staged() -> void:
|
|
for key: String in _staged.keys():
|
|
var record: Dictionary = _staged[key]
|
|
var identity := StringName(str(record.get("identity", "")))
|
|
if int(record.get("kind", -1)) == PlayerInventoryLayout.EntryKind.CATCH:
|
|
if (
|
|
_fish_inventory.get_catch_by_id(identity) == null
|
|
or not _layout.is_catch_in_inventory(identity)
|
|
):
|
|
_staged.erase(key)
|
|
elif (
|
|
_bag.get_quantity(identity) < int(record.get("quantity", 0))
|
|
or not _layout.is_item_in_inventory(identity)
|
|
):
|
|
_staged.erase(key)
|
|
|
|
|
|
func _refresh_tray() -> void:
|
|
_refresh_inventory_staged_highlights()
|
|
for child: Node in _tray_grid.get_children():
|
|
child.queue_free()
|
|
var keys: Array[String] = []
|
|
keys.assign(_staged.keys())
|
|
keys.sort()
|
|
for key: String in keys:
|
|
var record: Dictionary = _staged[key]
|
|
var identity := StringName(str(record["identity"]))
|
|
var icon: Texture2D
|
|
var label: String
|
|
if int(record["kind"]) == PlayerInventoryLayout.EntryKind.CATCH:
|
|
var fish_catch := _fish_inventory.get_catch_by_id(identity)
|
|
if fish_catch == null:
|
|
continue
|
|
icon = fish_catch.fish.display_texture
|
|
label = fish_catch.fish.display_name
|
|
else:
|
|
var item := _item_catalog.get_item_by_id(identity)
|
|
if item == null:
|
|
continue
|
|
icon = item.icon
|
|
label = item.display_name
|
|
var slot := ShopSaleTraySlot.new()
|
|
_tray_grid.add_child(slot)
|
|
slot.configure(key, icon, label, int(record.get("quantity", 1)))
|
|
slot.remove_requested.connect(_on_remove_requested)
|
|
slot.drop_requested.connect(_on_drop_payload)
|
|
var drop_slot := ShopSaleTraySlot.new()
|
|
_tray_grid.add_child(drop_slot)
|
|
drop_slot.configure("", null, "drop an item here")
|
|
drop_slot.disabled = false
|
|
drop_slot.drop_requested.connect(_on_drop_payload)
|
|
var total := _calculate_total()
|
|
_total_label.text = str(total)
|
|
_sell_button.disabled = (
|
|
_staged.is_empty()
|
|
or total < 0
|
|
or _network_sale == null
|
|
or _network_sale.is_local_sale_pending()
|
|
)
|
|
call_deferred("_configure_focus")
|
|
|
|
|
|
func _refresh_inventory_staged_highlights() -> void:
|
|
if _inventory_grid == null:
|
|
return
|
|
for slot: GeneralInventorySlot in _inventory_grid.get_slots():
|
|
var key: String = ""
|
|
if not slot.entry_identity.is_empty():
|
|
key = (
|
|
PlayerInventoryLayout.catch_key(slot.entry_identity)
|
|
if slot.entry_kind == PlayerInventoryLayout.EntryKind.CATCH
|
|
else PlayerInventoryLayout.item_key(slot.entry_identity)
|
|
)
|
|
slot.set_staged(not key.is_empty() and _staged.has(key))
|
|
|
|
|
|
func _configure_focus() -> void:
|
|
var controls: Array[Control] = []
|
|
controls.assign(_inventory_grid.get_slots())
|
|
for child: Node in _tray_grid.get_children():
|
|
var control := child as Control
|
|
if control != null and control.focus_mode != Control.FOCUS_NONE:
|
|
controls.append(control)
|
|
controls.append(_sell_button)
|
|
ControllerFocusNavigation.configure_spatial_neighbors(controls)
|
|
|
|
|
|
func _calculate_total() -> int:
|
|
var total: int = 0
|
|
for record: Dictionary in _staged.values():
|
|
var identity := StringName(str(record["identity"]))
|
|
if int(record["kind"]) == PlayerInventoryLayout.EntryKind.CATCH:
|
|
var fish_catch := _fish_inventory.get_catch_by_id(identity)
|
|
if fish_catch == null:
|
|
return -1
|
|
total += _buyer.get_quality_offer(
|
|
fish_catch.fish.get_sale_value_for_weight(fish_catch.weight_lb),
|
|
fish_catch.quality,
|
|
)
|
|
else:
|
|
var item := _item_catalog.get_item_by_id(identity)
|
|
var unit_value := ItemResalePolicy.get_unit_value(item)
|
|
if unit_value < 0:
|
|
return -1
|
|
total += unit_value * int(record.get("quantity", 0))
|
|
return total
|
|
|
|
|
|
func _build_ui() -> void:
|
|
set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
|
var outer := MarginContainer.new()
|
|
outer.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
|
outer.add_theme_constant_override("margin_left", 68)
|
|
outer.add_theme_constant_override("margin_top", 92)
|
|
outer.add_theme_constant_override("margin_right", 68)
|
|
outer.add_theme_constant_override("margin_bottom", 44)
|
|
add_child(outer)
|
|
var columns := HBoxContainer.new()
|
|
columns.add_theme_constant_override("separation", 20)
|
|
outer.add_child(columns)
|
|
|
|
var source := _build_panel(columns, "inventory")
|
|
_inventory_grid = GeneralInventoryGrid.new()
|
|
(source as VBoxContainer).add_child(_inventory_grid)
|
|
var tray := _build_panel(columns, "sell tray")
|
|
var tray_scroll := ScrollContainer.new()
|
|
tray_scroll.size_flags_vertical = Control.SIZE_EXPAND_FILL
|
|
tray_scroll.horizontal_scroll_mode = ScrollContainer.SCROLL_MODE_DISABLED
|
|
(tray as VBoxContainer).add_child(tray_scroll)
|
|
_tray_grid = GridContainer.new()
|
|
_tray_grid.columns = PlayerInventoryLayout.INVENTORY_COLUMNS
|
|
_tray_grid.add_theme_constant_override(
|
|
"h_separation", GeneralInventoryGrid.DEFAULT_SLOT_SEPARATION
|
|
)
|
|
_tray_grid.add_theme_constant_override(
|
|
"v_separation", GeneralInventoryGrid.DEFAULT_SLOT_SEPARATION
|
|
)
|
|
tray_scroll.add_child(_tray_grid)
|
|
_feedback = Label.new()
|
|
_feedback.text = "select or drag items into the sell tray"
|
|
_feedback.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
|
_feedback.add_theme_color_override(
|
|
"font_color", UtilityPageStyle.OCEAN_TEXT_SECONDARY
|
|
)
|
|
(tray as VBoxContainer).add_child(_feedback)
|
|
var total_row := HBoxContainer.new()
|
|
(tray as VBoxContainer).add_child(total_row)
|
|
var total_title := Label.new()
|
|
total_title.text = "total"
|
|
total_title.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
total_row.add_child(total_title)
|
|
var coin := TextureRect.new()
|
|
coin.custom_minimum_size = Vector2(24.0, 24.0)
|
|
coin.texture = preload("res://items/icons/shop/32_currency.png")
|
|
coin.expand_mode = TextureRect.EXPAND_IGNORE_SIZE
|
|
coin.stretch_mode = TextureRect.STRETCH_KEEP_ASPECT_CENTERED
|
|
coin.texture_filter = CanvasItem.TEXTURE_FILTER_NEAREST
|
|
total_row.add_child(coin)
|
|
_total_label = Label.new()
|
|
_total_label.text = "0"
|
|
total_row.add_child(_total_label)
|
|
_sell_button = Button.new()
|
|
_sell_button.text = "sell"
|
|
UtilityPageStyle.apply_ocean_button(_sell_button)
|
|
_sell_button.pressed.connect(_submit_sale)
|
|
(tray as VBoxContainer).add_child(_sell_button)
|
|
|
|
|
|
func _build_panel(parent: HBoxContainer, title_text: String) -> VBoxContainer:
|
|
var panel := PanelContainer.new()
|
|
panel.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
|
panel.add_theme_stylebox_override(
|
|
"panel",
|
|
UtilityPageStyle.rounded_style(UtilityPageStyle.OCEAN_PANEL_MID, 16),
|
|
)
|
|
parent.add_child(panel)
|
|
var margin := MarginContainer.new()
|
|
for side: StringName in [&"margin_left", &"margin_top", &"margin_right", &"margin_bottom"]:
|
|
margin.add_theme_constant_override(side, 12)
|
|
panel.add_child(margin)
|
|
var column := VBoxContainer.new()
|
|
column.add_theme_constant_override("separation", 8)
|
|
margin.add_child(column)
|
|
var title := Label.new()
|
|
title.text = title_text
|
|
title.add_theme_font_size_override("font_size", 22)
|
|
column.add_child(title)
|
|
return column
|