Add fish selling, buyer profiles, and player wallet
This commit is contained in:
parent
fb04615600
commit
d2fe367891
25 changed files with 738 additions and 5 deletions
11
economy/buyers/main_fishing_shop.tres
Normal file
11
economy/buyers/main_fishing_shop.tres
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
[gd_resource type="Resource" load_steps=2 format=3]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://economy/fish_buyer_profile.gd" id="1_profile"]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
script = ExtResource("1_profile")
|
||||||
|
id = &"main_fishing_shop"
|
||||||
|
display_name = "Main Fishing Shop"
|
||||||
|
animal_name_singular = "shopkeeper"
|
||||||
|
animal_name_plural = "shopkeepers"
|
||||||
|
payout_multiplier = 1.0
|
||||||
12
economy/buyers/pelicans.tres
Normal file
12
economy/buyers/pelicans.tres
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
[gd_resource type="Resource" load_steps=2 format=3]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://economy/fish_buyer_profile.gd" id="1_profile"]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
script = ExtResource("1_profile")
|
||||||
|
id = &"pelicans"
|
||||||
|
display_name = "Pelicans"
|
||||||
|
animal_name_singular = "pelican"
|
||||||
|
animal_name_plural = "pelicans"
|
||||||
|
payout_multiplier = 0.7
|
||||||
|
sale_message_template = "You sold your {fish} to the pelicans for ${payout}."
|
||||||
11
economy/buyers/trading_post.tres
Normal file
11
economy/buyers/trading_post.tres
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
[gd_resource type="Resource" load_steps=2 format=3]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://economy/fish_buyer_profile.gd" id="1_profile"]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
script = ExtResource("1_profile")
|
||||||
|
id = &"trading_post"
|
||||||
|
display_name = "Trading Post"
|
||||||
|
animal_name_singular = "otter"
|
||||||
|
animal_name_plural = "otters"
|
||||||
|
payout_multiplier = 0.85
|
||||||
46
economy/fish_buyer_profile.gd
Normal file
46
economy/fish_buyer_profile.gd
Normal file
|
|
@ -0,0 +1,46 @@
|
||||||
|
class_name FishBuyerProfile
|
||||||
|
extends Resource
|
||||||
|
|
||||||
|
@export var id: StringName
|
||||||
|
@export var display_name: String
|
||||||
|
@export var animal_name_singular: String
|
||||||
|
@export var animal_name_plural: String
|
||||||
|
@export_range(0.0, 10.0, 0.01) var payout_multiplier: float = 1.0
|
||||||
|
@export_multiline var sale_message_template: String
|
||||||
|
|
||||||
|
|
||||||
|
func is_valid() -> bool:
|
||||||
|
return (
|
||||||
|
not id.is_empty()
|
||||||
|
and not display_name.is_empty()
|
||||||
|
and payout_multiplier >= 0.0
|
||||||
|
and is_finite(payout_multiplier)
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func get_offer(base_value: int) -> int:
|
||||||
|
if not is_valid() or base_value < 0:
|
||||||
|
return -1
|
||||||
|
var offer: int = roundi(float(base_value) * payout_multiplier)
|
||||||
|
return maxi(offer, 0)
|
||||||
|
|
||||||
|
|
||||||
|
func get_sale_message(
|
||||||
|
fish_name: String,
|
||||||
|
payout: int,
|
||||||
|
) -> String:
|
||||||
|
if not sale_message_template.is_empty():
|
||||||
|
return sale_message_template.format(
|
||||||
|
{
|
||||||
|
"fish": fish_name,
|
||||||
|
"payout": payout,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
var buyer_name: String = animal_name_plural
|
||||||
|
if buyer_name.is_empty():
|
||||||
|
buyer_name = display_name
|
||||||
|
return "You sold your %s to the %s for $%d." % [
|
||||||
|
fish_name,
|
||||||
|
buyer_name,
|
||||||
|
payout,
|
||||||
|
]
|
||||||
1
economy/fish_buyer_profile.gd.uid
Normal file
1
economy/fish_buyer_profile.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
uid://cq1b2avld4qay
|
||||||
45
economy/fish_sale_result.gd
Normal file
45
economy/fish_sale_result.gd
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
class_name FishSaleResult
|
||||||
|
extends RefCounted
|
||||||
|
|
||||||
|
enum Status {
|
||||||
|
SUCCESS,
|
||||||
|
NOT_FOUND,
|
||||||
|
FAVORITED,
|
||||||
|
INVALID_VALUE,
|
||||||
|
INVALID_BUYER,
|
||||||
|
INVALID_OFFER,
|
||||||
|
TRANSACTION_FAILED,
|
||||||
|
}
|
||||||
|
|
||||||
|
var success: bool = false
|
||||||
|
var status: Status = Status.TRANSACTION_FAILED
|
||||||
|
var catch_id: StringName
|
||||||
|
var fish_name: String = ""
|
||||||
|
var buyer_id: StringName
|
||||||
|
var buyer_display_name: String = ""
|
||||||
|
var buyer_animal_name: String = ""
|
||||||
|
var base_value: int = 0
|
||||||
|
var payout: int = 0
|
||||||
|
var sale_message: String = ""
|
||||||
|
|
||||||
|
|
||||||
|
func is_success() -> bool:
|
||||||
|
return success and status == Status.SUCCESS
|
||||||
|
|
||||||
|
|
||||||
|
func get_message() -> String:
|
||||||
|
match status:
|
||||||
|
Status.SUCCESS:
|
||||||
|
return sale_message
|
||||||
|
Status.NOT_FOUND:
|
||||||
|
return "Fish no longer exists."
|
||||||
|
Status.FAVORITED:
|
||||||
|
return "Favorited fish cannot be sold."
|
||||||
|
Status.INVALID_VALUE:
|
||||||
|
return "Invalid sale value."
|
||||||
|
Status.INVALID_BUYER:
|
||||||
|
return "Buyer is unavailable."
|
||||||
|
Status.INVALID_OFFER:
|
||||||
|
return "Invalid buyer offer."
|
||||||
|
_:
|
||||||
|
return "Transaction failed."
|
||||||
1
economy/fish_sale_result.gd.uid
Normal file
1
economy/fish_sale_result.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
uid://c02hcw8fkn5i8
|
||||||
98
economy/fish_sale_service.gd
Normal file
98
economy/fish_sale_service.gd
Normal file
|
|
@ -0,0 +1,98 @@
|
||||||
|
class_name FishSaleService
|
||||||
|
extends Node
|
||||||
|
|
||||||
|
const FishCatchType = preload("res://fish/fish_catch.gd")
|
||||||
|
const FishBuyerProfileType = preload("res://economy/fish_buyer_profile.gd")
|
||||||
|
const FishInventoryType = preload("res://inventory/fish_inventory.gd")
|
||||||
|
const FishSaleResultType = preload("res://economy/fish_sale_result.gd")
|
||||||
|
const PlayerWalletType = preload("res://economy/player_wallet.gd")
|
||||||
|
|
||||||
|
signal sale_completed(result: FishSaleResultType)
|
||||||
|
|
||||||
|
var _inventory: FishInventoryType
|
||||||
|
var _wallet: PlayerWalletType
|
||||||
|
|
||||||
|
|
||||||
|
func setup(
|
||||||
|
inventory: FishInventoryType,
|
||||||
|
wallet: PlayerWalletType,
|
||||||
|
) -> void:
|
||||||
|
_inventory = inventory
|
||||||
|
_wallet = wallet
|
||||||
|
|
||||||
|
|
||||||
|
func can_sell(
|
||||||
|
catch_id: StringName,
|
||||||
|
buyer: FishBuyerProfileType,
|
||||||
|
) -> bool:
|
||||||
|
return _validate_sale(catch_id, buyer).is_success()
|
||||||
|
|
||||||
|
|
||||||
|
func sell(
|
||||||
|
catch_id: StringName,
|
||||||
|
buyer: FishBuyerProfileType,
|
||||||
|
) -> FishSaleResultType:
|
||||||
|
var result: FishSaleResultType = _validate_sale(catch_id, buyer)
|
||||||
|
if not result.is_success():
|
||||||
|
return result
|
||||||
|
|
||||||
|
var removed_catch: FishCatchType = _inventory.remove_catch_by_id(catch_id)
|
||||||
|
if removed_catch == null:
|
||||||
|
result.success = false
|
||||||
|
result.status = FishSaleResultType.Status.NOT_FOUND
|
||||||
|
return result
|
||||||
|
if not _wallet.credit(result.payout):
|
||||||
|
_inventory.add_catch(removed_catch)
|
||||||
|
result.success = false
|
||||||
|
result.status = FishSaleResultType.Status.TRANSACTION_FAILED
|
||||||
|
return result
|
||||||
|
sale_completed.emit(result)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
func _validate_sale(
|
||||||
|
catch_id: 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
|
||||||
|
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 = (
|
||||||
|
buyer.animal_name_plural
|
||||||
|
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:
|
||||||
|
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):
|
||||||
|
result.status = FishSaleResultType.Status.TRANSACTION_FAILED
|
||||||
|
else:
|
||||||
|
result.status = FishSaleResultType.Status.SUCCESS
|
||||||
|
result.success = true
|
||||||
|
result.sale_message = buyer.get_sale_message(
|
||||||
|
result.fish_name,
|
||||||
|
result.payout
|
||||||
|
)
|
||||||
|
return result
|
||||||
1
economy/fish_sale_service.gd.uid
Normal file
1
economy/fish_sale_service.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
uid://cg3ec2ypo26xh
|
||||||
45
economy/player_wallet.gd
Normal file
45
economy/player_wallet.gd
Normal file
|
|
@ -0,0 +1,45 @@
|
||||||
|
class_name PlayerWallet
|
||||||
|
extends Node
|
||||||
|
|
||||||
|
signal balance_changed(new_balance: int, delta: int)
|
||||||
|
|
||||||
|
@export var current_balance: int = 0
|
||||||
|
|
||||||
|
|
||||||
|
func _ready() -> void:
|
||||||
|
current_balance = maxi(current_balance, 0)
|
||||||
|
|
||||||
|
|
||||||
|
func get_balance() -> int:
|
||||||
|
return current_balance
|
||||||
|
|
||||||
|
|
||||||
|
func can_afford(amount: int) -> bool:
|
||||||
|
return amount >= 0 and current_balance >= amount
|
||||||
|
|
||||||
|
|
||||||
|
func can_credit(amount: int) -> bool:
|
||||||
|
return (
|
||||||
|
amount >= 0
|
||||||
|
and current_balance <= 9223372036854775807 - amount
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func credit(amount: int) -> bool:
|
||||||
|
if not can_credit(amount):
|
||||||
|
return false
|
||||||
|
if amount == 0:
|
||||||
|
return true
|
||||||
|
current_balance += amount
|
||||||
|
balance_changed.emit(current_balance, amount)
|
||||||
|
return true
|
||||||
|
|
||||||
|
|
||||||
|
func debit(amount: int) -> bool:
|
||||||
|
if not can_afford(amount):
|
||||||
|
return false
|
||||||
|
if amount == 0:
|
||||||
|
return true
|
||||||
|
current_balance -= amount
|
||||||
|
balance_changed.emit(current_balance, -amount)
|
||||||
|
return true
|
||||||
1
economy/player_wallet.gd.uid
Normal file
1
economy/player_wallet.gd.uid
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
uid://dp1uf8h8eevs
|
||||||
|
|
@ -9,6 +9,8 @@ const FishDataType = preload("res://fish/fish_data.gd")
|
||||||
@export var catch_sequence: int = 0
|
@export var catch_sequence: int = 0
|
||||||
@export var weight_lb: float = 0.0
|
@export var weight_lb: float = 0.0
|
||||||
@export var display_scale: float = 1.0
|
@export var display_scale: float = 1.0
|
||||||
|
@export var sale_value: int = 0
|
||||||
|
@export var is_favorited: bool = false
|
||||||
|
|
||||||
|
|
||||||
func ensure_identity() -> void:
|
func ensure_identity() -> void:
|
||||||
|
|
@ -29,4 +31,5 @@ func is_valid() -> bool:
|
||||||
and weight_lb >= fish.get_minimum_weight()
|
and weight_lb >= fish.get_minimum_weight()
|
||||||
and weight_lb <= fish.get_maximum_weight()
|
and weight_lb <= fish.get_maximum_weight()
|
||||||
and display_scale > 0.0
|
and display_scale > 0.0
|
||||||
|
and sale_value >= 0
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -25,6 +25,9 @@ enum Rarity {
|
||||||
@export_range(0.01, 20.0, 0.01) var display_scale_min: float = 0.8
|
@export_range(0.01, 20.0, 0.01) var display_scale_min: float = 0.8
|
||||||
@export_range(0.01, 20.0, 0.01) var display_scale_max: float = 1.2
|
@export_range(0.01, 20.0, 0.01) var display_scale_max: float = 1.2
|
||||||
@export_range(0.01, 10.0, 0.01) var display_scale_curve: float = 1.0
|
@export_range(0.01, 10.0, 0.01) var display_scale_curve: float = 1.0
|
||||||
|
@export_range(0, 1000000, 1) var sell_value_min: int = 0
|
||||||
|
@export_range(0, 1000000, 1) var sell_value_max: int = 1
|
||||||
|
@export_range(0.01, 10.0, 0.01) var sell_value_curve: float = 1.0
|
||||||
@export var display_texture: Texture2D
|
@export var display_texture: Texture2D
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -38,6 +41,9 @@ func is_selectable() -> bool:
|
||||||
and display_scale_min > 0.0
|
and display_scale_min > 0.0
|
||||||
and display_scale_max >= display_scale_min
|
and display_scale_max >= display_scale_min
|
||||||
and display_scale_curve > 0.0
|
and display_scale_curve > 0.0
|
||||||
|
and sell_value_min >= 0
|
||||||
|
and sell_value_max >= sell_value_min
|
||||||
|
and sell_value_curve > 0.0
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -68,5 +74,28 @@ func get_display_scale_for_weight(weight_lb: float) -> float:
|
||||||
return lerpf(minimum_scale, maximum_scale, normalized_weight)
|
return lerpf(minimum_scale, maximum_scale, normalized_weight)
|
||||||
|
|
||||||
|
|
||||||
|
func get_sale_value_for_weight(weight_lb: float) -> int:
|
||||||
|
var minimum_value: int = maxi(sell_value_min, 0)
|
||||||
|
var maximum_value: int = maxi(sell_value_max, minimum_value)
|
||||||
|
var normalized_weight: float = 0.0
|
||||||
|
var minimum_weight: float = get_minimum_weight()
|
||||||
|
var maximum_weight: float = get_maximum_weight()
|
||||||
|
if maximum_weight > minimum_weight:
|
||||||
|
normalized_weight = inverse_lerp(
|
||||||
|
minimum_weight,
|
||||||
|
maximum_weight,
|
||||||
|
weight_lb
|
||||||
|
)
|
||||||
|
normalized_weight = pow(
|
||||||
|
clampf(normalized_weight, 0.0, 1.0),
|
||||||
|
maxf(sell_value_curve, 0.01)
|
||||||
|
)
|
||||||
|
return clampi(
|
||||||
|
roundi(lerpf(minimum_value, maximum_value, normalized_weight)),
|
||||||
|
minimum_value,
|
||||||
|
maximum_value
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
func get_rarity_name() -> String:
|
func get_rarity_name() -> String:
|
||||||
return Rarity.keys()[rarity].capitalize()
|
return Rarity.keys()[rarity].capitalize()
|
||||||
|
|
|
||||||
|
|
@ -76,4 +76,7 @@ func create_catch(fish: FishDataType) -> FishCatchType:
|
||||||
caught_fish.display_scale = fish.get_display_scale_for_weight(
|
caught_fish.display_scale = fish.get_display_scale_for_weight(
|
||||||
caught_fish.weight_lb
|
caught_fish.weight_lb
|
||||||
)
|
)
|
||||||
|
caught_fish.sale_value = fish.get_sale_value_for_weight(
|
||||||
|
caught_fish.weight_lb
|
||||||
|
)
|
||||||
return caught_fish
|
return caught_fish
|
||||||
|
|
|
||||||
|
|
@ -25,4 +25,7 @@ weight_max_lb = 8.0
|
||||||
display_scale_min = 0.9
|
display_scale_min = 0.9
|
||||||
display_scale_max = 1.5
|
display_scale_max = 1.5
|
||||||
display_scale_curve = 0.9
|
display_scale_curve = 0.9
|
||||||
|
sell_value_min = 12
|
||||||
|
sell_value_max = 45
|
||||||
|
sell_value_curve = 0.9
|
||||||
display_texture = ExtResource("4_texture")
|
display_texture = ExtResource("4_texture")
|
||||||
|
|
|
||||||
|
|
@ -18,4 +18,7 @@ weight_max_lb = 2.0
|
||||||
display_scale_min = 0.7
|
display_scale_min = 0.7
|
||||||
display_scale_max = 1.1
|
display_scale_max = 1.1
|
||||||
display_scale_curve = 1.0
|
display_scale_curve = 1.0
|
||||||
|
sell_value_min = 3
|
||||||
|
sell_value_max = 12
|
||||||
|
sell_value_curve = 1.0
|
||||||
display_texture = ExtResource("4_texture")
|
display_texture = ExtResource("4_texture")
|
||||||
|
|
|
||||||
|
|
@ -25,4 +25,7 @@ weight_max_lb = 18.0
|
||||||
display_scale_min = 1.0
|
display_scale_min = 1.0
|
||||||
display_scale_max = 1.8
|
display_scale_max = 1.8
|
||||||
display_scale_curve = 0.85
|
display_scale_curve = 0.85
|
||||||
|
sell_value_min = 25
|
||||||
|
sell_value_max = 90
|
||||||
|
sell_value_curve = 0.85
|
||||||
display_texture = ExtResource("4_texture")
|
display_texture = ExtResource("4_texture")
|
||||||
|
|
|
||||||
|
|
@ -13,7 +13,7 @@ var _next_catch_sequence: int = 1
|
||||||
func add_catch(fish_catch: FishCatchType) -> void:
|
func add_catch(fish_catch: FishCatchType) -> void:
|
||||||
if fish_catch == null or not fish_catch.is_valid():
|
if fish_catch == null or not fish_catch.is_valid():
|
||||||
return
|
return
|
||||||
if get_catch(fish_catch.catch_id) != null:
|
if get_catch_by_id(fish_catch.catch_id) != null:
|
||||||
return
|
return
|
||||||
if fish_catch.catch_sequence <= 0:
|
if fish_catch.catch_sequence <= 0:
|
||||||
fish_catch.catch_sequence = _next_catch_sequence
|
fish_catch.catch_sequence = _next_catch_sequence
|
||||||
|
|
@ -37,7 +37,7 @@ func get_all_catches() -> Array[FishCatchType]:
|
||||||
return _catches.duplicate()
|
return _catches.duplicate()
|
||||||
|
|
||||||
|
|
||||||
func get_catch(catch_id: StringName) -> FishCatchType:
|
func get_catch_by_id(catch_id: StringName) -> FishCatchType:
|
||||||
if catch_id.is_empty():
|
if catch_id.is_empty():
|
||||||
return null
|
return null
|
||||||
for fish_catch: FishCatchType in _catches:
|
for fish_catch: FishCatchType in _catches:
|
||||||
|
|
@ -46,6 +46,54 @@ func get_catch(catch_id: StringName) -> FishCatchType:
|
||||||
return null
|
return null
|
||||||
|
|
||||||
|
|
||||||
|
func get_catch(catch_id: StringName) -> FishCatchType:
|
||||||
|
return get_catch_by_id(catch_id)
|
||||||
|
|
||||||
|
|
||||||
|
func contains_catch_id(catch_id: StringName) -> bool:
|
||||||
|
return get_catch_by_id(catch_id) != null
|
||||||
|
|
||||||
|
|
||||||
|
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
|
||||||
|
|
||||||
|
|
||||||
|
func set_catch_favorited(
|
||||||
|
catch_id: StringName,
|
||||||
|
is_favorited: bool,
|
||||||
|
) -> bool:
|
||||||
|
var fish_catch: FishCatchType = get_catch_by_id(catch_id)
|
||||||
|
if fish_catch == null:
|
||||||
|
return false
|
||||||
|
if fish_catch.is_favorited == is_favorited:
|
||||||
|
return true
|
||||||
|
fish_catch.is_favorited = is_favorited
|
||||||
|
catches_changed.emit()
|
||||||
|
return true
|
||||||
|
|
||||||
|
|
||||||
|
func get_total_sale_value() -> int:
|
||||||
|
var total: int = 0
|
||||||
|
for fish_catch: FishCatchType in _catches:
|
||||||
|
if fish_catch == null or fish_catch.sale_value < 0:
|
||||||
|
continue
|
||||||
|
total += fish_catch.sale_value
|
||||||
|
return total
|
||||||
|
|
||||||
|
|
||||||
func get_catches_by_fish_id(fish_id: StringName) -> Array[FishCatchType]:
|
func get_catches_by_fish_id(fish_id: StringName) -> Array[FishCatchType]:
|
||||||
var matching: Array[FishCatchType] = []
|
var matching: Array[FishCatchType] = []
|
||||||
for fish_catch: FishCatchType in _catches:
|
for fish_catch: FishCatchType in _catches:
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,13 @@
|
||||||
extends Node3D
|
extends Node3D
|
||||||
|
|
||||||
const FishingSpotType = preload("res://fishing/fishing_spot.gd")
|
const FishingSpotType = preload("res://fishing/fishing_spot.gd")
|
||||||
|
const FishBuyerProfileType = preload("res://economy/fish_buyer_profile.gd")
|
||||||
const FishPoolType = preload("res://fish/fish_pool.gd")
|
const FishPoolType = preload("res://fish/fish_pool.gd")
|
||||||
const GameUIType = preload("res://ui/game_ui.gd")
|
const GameUIType = preload("res://ui/game_ui.gd")
|
||||||
const PlayerType = preload("res://player/player.gd")
|
const PlayerType = preload("res://player/player.gd")
|
||||||
|
|
||||||
@export var fish_catalog: FishPoolType
|
@export var fish_catalog: FishPoolType
|
||||||
|
@export var pelican_buyer_profile: FishBuyerProfileType
|
||||||
|
|
||||||
@onready var _player: PlayerType = %Player
|
@onready var _player: PlayerType = %Player
|
||||||
@onready var _fishing_spot: FishingSpotType = %FishingSpot
|
@onready var _fishing_spot: FishingSpotType = %FishingSpot
|
||||||
|
|
@ -13,6 +15,10 @@ const PlayerType = preload("res://player/player.gd")
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
|
_player.fish_sale_service.setup(
|
||||||
|
_player.inventory,
|
||||||
|
_player.wallet
|
||||||
|
)
|
||||||
_fishing_spot.setup(
|
_fishing_spot.setup(
|
||||||
_player,
|
_player,
|
||||||
_player.inventory,
|
_player.inventory,
|
||||||
|
|
@ -22,6 +28,9 @@ func _ready() -> void:
|
||||||
_player,
|
_player,
|
||||||
_player.inventory,
|
_player.inventory,
|
||||||
_player.collection_log,
|
_player.collection_log,
|
||||||
|
_player.wallet,
|
||||||
|
_player.fish_sale_service,
|
||||||
|
pelican_buyer_profile,
|
||||||
fish_catalog,
|
fish_catalog,
|
||||||
_fishing_spot
|
_fishing_spot
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
[gd_scene load_steps=7 format=3]
|
[gd_scene load_steps=8 format=3]
|
||||||
|
|
||||||
[ext_resource type="PackedScene" path="res://world/test_world.tscn" id="1_world"]
|
[ext_resource type="PackedScene" path="res://world/test_world.tscn" id="1_world"]
|
||||||
[ext_resource type="PackedScene" path="res://player/player.tscn" id="2_player"]
|
[ext_resource type="PackedScene" path="res://player/player.tscn" id="2_player"]
|
||||||
|
|
@ -6,10 +6,12 @@
|
||||||
[ext_resource type="PackedScene" path="res://fishing/fishing_spot.tscn" id="4_spot"]
|
[ext_resource type="PackedScene" path="res://fishing/fishing_spot.tscn" id="4_spot"]
|
||||||
[ext_resource type="PackedScene" path="res://ui/game_ui.tscn" id="5_ui"]
|
[ext_resource type="PackedScene" path="res://ui/game_ui.tscn" id="5_ui"]
|
||||||
[ext_resource type="Resource" path="res://fish/pools/test_water_pool.tres" id="6_pool"]
|
[ext_resource type="Resource" path="res://fish/pools/test_water_pool.tres" id="6_pool"]
|
||||||
|
[ext_resource type="Resource" path="res://economy/buyers/pelicans.tres" id="7_pelicans"]
|
||||||
|
|
||||||
[node name="Main" type="Node3D"]
|
[node name="Main" type="Node3D"]
|
||||||
script = ExtResource("3_main")
|
script = ExtResource("3_main")
|
||||||
fish_catalog = ExtResource("6_pool")
|
fish_catalog = ExtResource("6_pool")
|
||||||
|
pelican_buyer_profile = ExtResource("7_pelicans")
|
||||||
|
|
||||||
[node name="TestWorld" parent="." instance=ExtResource("1_world")]
|
[node name="TestWorld" parent="." instance=ExtResource("1_world")]
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -4,6 +4,8 @@ extends CharacterBody3D
|
||||||
const FishInventoryType = preload("res://inventory/fish_inventory.gd")
|
const FishInventoryType = preload("res://inventory/fish_inventory.gd")
|
||||||
const CollectionLogType = preload("res://collection/collection_log.gd")
|
const CollectionLogType = preload("res://collection/collection_log.gd")
|
||||||
const FishCatchType = preload("res://fish/fish_catch.gd")
|
const FishCatchType = preload("res://fish/fish_catch.gd")
|
||||||
|
const FishSaleServiceType = preload("res://economy/fish_sale_service.gd")
|
||||||
|
const PlayerWalletType = preload("res://economy/player_wallet.gd")
|
||||||
|
|
||||||
class ShowcaseCameraSnapshot:
|
class ShowcaseCameraSnapshot:
|
||||||
extends RefCounted
|
extends RefCounted
|
||||||
|
|
@ -59,6 +61,8 @@ class ShowcaseCameraSnapshot:
|
||||||
@onready var _camera: Camera3D = %Camera3D
|
@onready var _camera: Camera3D = %Camera3D
|
||||||
@onready var inventory: FishInventoryType = %Inventory
|
@onready var inventory: FishInventoryType = %Inventory
|
||||||
@onready var collection_log: CollectionLogType = %CollectionLog
|
@onready var collection_log: CollectionLogType = %CollectionLog
|
||||||
|
@onready var wallet: PlayerWalletType = %Wallet
|
||||||
|
@onready var fish_sale_service: FishSaleServiceType = %FishSaleService
|
||||||
@onready var _cast_origin: Marker3D = %CastOrigin
|
@onready var _cast_origin: Marker3D = %CastOrigin
|
||||||
@onready var _fishing_rod: Node3D = %FishingRod
|
@onready var _fishing_rod: Node3D = %FishingRod
|
||||||
@onready var _fishing_rod_tip: Marker3D = %FishingRodTip
|
@onready var _fishing_rod_tip: Marker3D = %FishingRodTip
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,10 @@
|
||||||
[gd_scene load_steps=11 format=3]
|
[gd_scene load_steps=13 format=3]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://player/player.gd" id="1_script"]
|
[ext_resource type="Script" path="res://player/player.gd" id="1_script"]
|
||||||
[ext_resource type="Script" path="res://inventory/fish_inventory.gd" id="2_inventory"]
|
[ext_resource type="Script" path="res://inventory/fish_inventory.gd" id="2_inventory"]
|
||||||
[ext_resource type="Script" path="res://collection/collection_log.gd" id="3_collection"]
|
[ext_resource type="Script" path="res://collection/collection_log.gd" id="3_collection"]
|
||||||
|
[ext_resource type="Script" path="res://economy/player_wallet.gd" id="4_wallet"]
|
||||||
|
[ext_resource type="Script" path="res://economy/fish_sale_service.gd" id="5_sale_service"]
|
||||||
|
|
||||||
[sub_resource type="CapsuleShape3D" id="PlayerShape"]
|
[sub_resource type="CapsuleShape3D" id="PlayerShape"]
|
||||||
radius = 0.45
|
radius = 0.45
|
||||||
|
|
@ -78,6 +80,14 @@ script = ExtResource("2_inventory")
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
script = ExtResource("3_collection")
|
script = ExtResource("3_collection")
|
||||||
|
|
||||||
|
[node name="Wallet" type="Node" parent="."]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
script = ExtResource("4_wallet")
|
||||||
|
|
||||||
|
[node name="FishSaleService" type="Node" parent="."]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
script = ExtResource("5_sale_service")
|
||||||
|
|
||||||
[node name="CatchDisplayAnchor" type="Marker3D" parent="Visuals"]
|
[node name="CatchDisplayAnchor" type="Marker3D" parent="Visuals"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
position = Vector3(0, 1.45, -1.15)
|
position = Vector3(0, 1.45, -1.15)
|
||||||
|
|
|
||||||
|
|
@ -2,11 +2,14 @@ class_name GameUI
|
||||||
extends CanvasLayer
|
extends CanvasLayer
|
||||||
|
|
||||||
const CollectionLogType = preload("res://collection/collection_log.gd")
|
const CollectionLogType = preload("res://collection/collection_log.gd")
|
||||||
|
const FishBuyerProfileType = preload("res://economy/fish_buyer_profile.gd")
|
||||||
|
const FishSaleServiceType = preload("res://economy/fish_sale_service.gd")
|
||||||
const FishPoolType = preload("res://fish/fish_pool.gd")
|
const FishPoolType = preload("res://fish/fish_pool.gd")
|
||||||
const FishInventoryType = preload("res://inventory/fish_inventory.gd")
|
const FishInventoryType = preload("res://inventory/fish_inventory.gd")
|
||||||
const FishingSpotType = preload("res://fishing/fishing_spot.gd")
|
const FishingSpotType = preload("res://fishing/fishing_spot.gd")
|
||||||
const PlayerMenuType = preload("res://ui/player_menu.gd")
|
const PlayerMenuType = preload("res://ui/player_menu.gd")
|
||||||
const PlayerType = preload("res://player/player.gd")
|
const PlayerType = preload("res://player/player.gd")
|
||||||
|
const PlayerWalletType = preload("res://economy/player_wallet.gd")
|
||||||
|
|
||||||
@onready var _status_label: Label = %StatusLabel
|
@onready var _status_label: Label = %StatusLabel
|
||||||
@onready var _catch_track: Control = %CatchTrack
|
@onready var _catch_track: Control = %CatchTrack
|
||||||
|
|
@ -26,6 +29,9 @@ func setup(
|
||||||
player: PlayerType,
|
player: PlayerType,
|
||||||
inventory: FishInventoryType,
|
inventory: FishInventoryType,
|
||||||
collection_log: CollectionLogType,
|
collection_log: CollectionLogType,
|
||||||
|
wallet: PlayerWalletType,
|
||||||
|
sale_service: FishSaleServiceType,
|
||||||
|
default_buyer: FishBuyerProfileType,
|
||||||
catalog: FishPoolType,
|
catalog: FishPoolType,
|
||||||
fishing_spot: FishingSpotType,
|
fishing_spot: FishingSpotType,
|
||||||
) -> void:
|
) -> void:
|
||||||
|
|
@ -39,6 +45,9 @@ func setup(
|
||||||
player,
|
player,
|
||||||
inventory,
|
inventory,
|
||||||
collection_log,
|
collection_log,
|
||||||
|
wallet,
|
||||||
|
sale_service,
|
||||||
|
default_buyer,
|
||||||
catalog,
|
catalog,
|
||||||
fishing_spot
|
fishing_spot
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -4,10 +4,14 @@ extends Control
|
||||||
const CollectionLogType = preload("res://collection/collection_log.gd")
|
const CollectionLogType = preload("res://collection/collection_log.gd")
|
||||||
const FishCatchType = preload("res://fish/fish_catch.gd")
|
const FishCatchType = preload("res://fish/fish_catch.gd")
|
||||||
const FishDataType = preload("res://fish/fish_data.gd")
|
const FishDataType = preload("res://fish/fish_data.gd")
|
||||||
|
const FishBuyerProfileType = preload("res://economy/fish_buyer_profile.gd")
|
||||||
|
const FishSaleResultType = preload("res://economy/fish_sale_result.gd")
|
||||||
|
const FishSaleServiceType = preload("res://economy/fish_sale_service.gd")
|
||||||
const FishInventoryType = preload("res://inventory/fish_inventory.gd")
|
const FishInventoryType = preload("res://inventory/fish_inventory.gd")
|
||||||
const FishPoolType = preload("res://fish/fish_pool.gd")
|
const FishPoolType = preload("res://fish/fish_pool.gd")
|
||||||
const FishingSpotType = preload("res://fishing/fishing_spot.gd")
|
const FishingSpotType = preload("res://fishing/fishing_spot.gd")
|
||||||
const PlayerType = preload("res://player/player.gd")
|
const PlayerType = preload("res://player/player.gd")
|
||||||
|
const PlayerWalletType = preload("res://economy/player_wallet.gd")
|
||||||
|
|
||||||
signal menu_visibility_changed(is_open: bool)
|
signal menu_visibility_changed(is_open: bool)
|
||||||
|
|
||||||
|
|
@ -25,21 +29,34 @@ enum SortMode {
|
||||||
@onready var _inventory_tab: Button = %InventoryTab
|
@onready var _inventory_tab: Button = %InventoryTab
|
||||||
@onready var _logbook_tab: Button = %LogbookTab
|
@onready var _logbook_tab: Button = %LogbookTab
|
||||||
@onready var _close_button: Button = %CloseButton
|
@onready var _close_button: Button = %CloseButton
|
||||||
|
@onready var _wallet_balance: Label = %WalletBalance
|
||||||
@onready var _inventory_section: Control = %InventorySection
|
@onready var _inventory_section: Control = %InventorySection
|
||||||
@onready var _logbook_section: Control = %LogbookSection
|
@onready var _logbook_section: Control = %LogbookSection
|
||||||
@onready var _sort_option: OptionButton = %SortOption
|
@onready var _sort_option: OptionButton = %SortOption
|
||||||
@onready var _sort_direction: Button = %SortDirection
|
@onready var _sort_direction: Button = %SortDirection
|
||||||
|
@onready var _held_value: Label = %HeldValue
|
||||||
@onready var _inventory_empty: Label = %InventoryEmpty
|
@onready var _inventory_empty: Label = %InventoryEmpty
|
||||||
@onready var _inventory_grid: GridContainer = %InventoryGrid
|
@onready var _inventory_grid: GridContainer = %InventoryGrid
|
||||||
@onready var _detail_texture: TextureRect = %DetailTexture
|
@onready var _detail_texture: TextureRect = %DetailTexture
|
||||||
@onready var _detail_name: Label = %DetailName
|
@onready var _detail_name: Label = %DetailName
|
||||||
@onready var _detail_data: Label = %DetailData
|
@onready var _detail_data: Label = %DetailData
|
||||||
|
@onready var _favorite_button: Button = %FavoriteButton
|
||||||
|
@onready var _sell_button: Button = %SellButton
|
||||||
|
@onready var _sale_unavailable: Label = %SaleUnavailable
|
||||||
|
@onready var _transaction_feedback: Label = %TransactionFeedback
|
||||||
|
@onready var _sale_confirmation: PanelContainer = %SaleConfirmation
|
||||||
|
@onready var _confirmation_message: Label = %ConfirmationMessage
|
||||||
|
@onready var _confirm_sale_button: Button = %ConfirmSaleButton
|
||||||
|
@onready var _cancel_sale_button: Button = %CancelSaleButton
|
||||||
@onready var _logbook_empty: Label = %LogbookEmpty
|
@onready var _logbook_empty: Label = %LogbookEmpty
|
||||||
@onready var _logbook_grid: GridContainer = %LogbookGrid
|
@onready var _logbook_grid: GridContainer = %LogbookGrid
|
||||||
|
|
||||||
var _player: PlayerType
|
var _player: PlayerType
|
||||||
var _inventory: FishInventoryType
|
var _inventory: FishInventoryType
|
||||||
var _collection_log: CollectionLogType
|
var _collection_log: CollectionLogType
|
||||||
|
var _wallet: PlayerWalletType
|
||||||
|
var _sale_service: FishSaleServiceType
|
||||||
|
var _default_buyer: FishBuyerProfileType
|
||||||
var _catalog: FishPoolType
|
var _catalog: FishPoolType
|
||||||
var _fishing_spot: FishingSpotType
|
var _fishing_spot: FishingSpotType
|
||||||
var _current_section: Section = Section.INVENTORY
|
var _current_section: Section = Section.INVENTORY
|
||||||
|
|
@ -50,6 +67,10 @@ var _prior_movement_enabled: bool = true
|
||||||
var _prior_camera_input_enabled: bool = true
|
var _prior_camera_input_enabled: bool = true
|
||||||
var _control_snapshot_stored: bool = false
|
var _control_snapshot_stored: bool = false
|
||||||
var _menu_generation: int = 0
|
var _menu_generation: int = 0
|
||||||
|
var _confirmation_catch_id: StringName
|
||||||
|
var _confirmation_buyer: FishBuyerProfileType
|
||||||
|
var _confirmation_buyer_id: StringName
|
||||||
|
var _sale_in_progress: bool = false
|
||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
|
|
@ -62,6 +83,10 @@ func _ready() -> void:
|
||||||
_close_button.pressed.connect(close_menu)
|
_close_button.pressed.connect(close_menu)
|
||||||
_sort_option.item_selected.connect(_on_sort_selected)
|
_sort_option.item_selected.connect(_on_sort_selected)
|
||||||
_sort_direction.pressed.connect(_on_sort_direction_pressed)
|
_sort_direction.pressed.connect(_on_sort_direction_pressed)
|
||||||
|
_favorite_button.pressed.connect(_on_favorite_pressed)
|
||||||
|
_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("Catch order", SortMode.CATCH_ORDER)
|
||||||
_sort_option.add_item("Name", SortMode.NAME)
|
_sort_option.add_item("Name", SortMode.NAME)
|
||||||
_sort_option.add_item("Rarity", SortMode.RARITY)
|
_sort_option.add_item("Rarity", SortMode.RARITY)
|
||||||
|
|
@ -74,18 +99,26 @@ func setup(
|
||||||
player: PlayerType,
|
player: PlayerType,
|
||||||
inventory: FishInventoryType,
|
inventory: FishInventoryType,
|
||||||
collection_log: CollectionLogType,
|
collection_log: CollectionLogType,
|
||||||
|
wallet: PlayerWalletType,
|
||||||
|
sale_service: FishSaleServiceType,
|
||||||
|
default_buyer: FishBuyerProfileType,
|
||||||
catalog: FishPoolType,
|
catalog: FishPoolType,
|
||||||
fishing_spot: FishingSpotType,
|
fishing_spot: FishingSpotType,
|
||||||
) -> void:
|
) -> void:
|
||||||
_player = player
|
_player = player
|
||||||
_inventory = inventory
|
_inventory = inventory
|
||||||
_collection_log = collection_log
|
_collection_log = collection_log
|
||||||
|
_wallet = wallet
|
||||||
|
_sale_service = sale_service
|
||||||
|
_default_buyer = default_buyer
|
||||||
_catalog = catalog
|
_catalog = catalog
|
||||||
_fishing_spot = fishing_spot
|
_fishing_spot = fishing_spot
|
||||||
if not _inventory.catches_changed.is_connected(_on_inventory_changed):
|
if not _inventory.catches_changed.is_connected(_on_inventory_changed):
|
||||||
_inventory.catches_changed.connect(_on_inventory_changed)
|
_inventory.catches_changed.connect(_on_inventory_changed)
|
||||||
if not _collection_log.fish_discovered.is_connected(_on_fish_discovered):
|
if not _collection_log.fish_discovered.is_connected(_on_fish_discovered):
|
||||||
_collection_log.fish_discovered.connect(_on_fish_discovered)
|
_collection_log.fish_discovered.connect(_on_fish_discovered)
|
||||||
|
if not _wallet.balance_changed.is_connected(_on_wallet_balance_changed):
|
||||||
|
_wallet.balance_changed.connect(_on_wallet_balance_changed)
|
||||||
if not _fishing_spot.bite_activated.is_connected(_on_bite_activated):
|
if not _fishing_spot.bite_activated.is_connected(_on_bite_activated):
|
||||||
_fishing_spot.bite_activated.connect(_on_bite_activated)
|
_fishing_spot.bite_activated.connect(_on_bite_activated)
|
||||||
_refresh_all()
|
_refresh_all()
|
||||||
|
|
@ -95,6 +128,13 @@ func _input(event: InputEvent) -> void:
|
||||||
if event is InputEventKey and event.echo:
|
if event is InputEventKey and event.echo:
|
||||||
return
|
return
|
||||||
if visible:
|
if visible:
|
||||||
|
if (
|
||||||
|
event.is_action_pressed("ui_cancel")
|
||||||
|
and _sale_confirmation.visible
|
||||||
|
):
|
||||||
|
_close_sale_confirmation()
|
||||||
|
get_viewport().set_input_as_handled()
|
||||||
|
return
|
||||||
if (
|
if (
|
||||||
event.is_action_pressed("open_backpack")
|
event.is_action_pressed("open_backpack")
|
||||||
or event.is_action_pressed("ui_cancel")
|
or event.is_action_pressed("ui_cancel")
|
||||||
|
|
@ -135,6 +175,7 @@ func open_menu() -> void:
|
||||||
func close_menu() -> void:
|
func close_menu() -> void:
|
||||||
if not visible:
|
if not visible:
|
||||||
return
|
return
|
||||||
|
_close_sale_confirmation()
|
||||||
var closing_generation: int = _menu_generation
|
var closing_generation: int = _menu_generation
|
||||||
visible = false
|
visible = false
|
||||||
get_viewport().gui_release_focus()
|
get_viewport().gui_release_focus()
|
||||||
|
|
@ -209,11 +250,14 @@ func _update_sort_direction_text() -> void:
|
||||||
|
|
||||||
|
|
||||||
func _refresh_all() -> void:
|
func _refresh_all() -> void:
|
||||||
|
_refresh_economy_summary()
|
||||||
_refresh_inventory()
|
_refresh_inventory()
|
||||||
_refresh_logbook()
|
_refresh_logbook()
|
||||||
|
|
||||||
|
|
||||||
func _on_inventory_changed() -> void:
|
func _on_inventory_changed() -> void:
|
||||||
|
_refresh_economy_summary()
|
||||||
|
_revalidate_confirmation()
|
||||||
_refresh_inventory()
|
_refresh_inventory()
|
||||||
_refresh_logbook()
|
_refresh_logbook()
|
||||||
|
|
||||||
|
|
@ -222,6 +266,26 @@ func _on_fish_discovered(_fish_id: StringName) -> void:
|
||||||
_refresh_logbook()
|
_refresh_logbook()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_wallet_balance_changed(
|
||||||
|
_new_balance: int,
|
||||||
|
_delta: int,
|
||||||
|
) -> void:
|
||||||
|
_refresh_economy_summary()
|
||||||
|
|
||||||
|
|
||||||
|
func _refresh_economy_summary() -> void:
|
||||||
|
if not is_node_ready():
|
||||||
|
return
|
||||||
|
var balance: int = _wallet.get_balance() if _wallet != null else 0
|
||||||
|
var held_total: int = (
|
||||||
|
_inventory.get_total_sale_value()
|
||||||
|
if _inventory != null
|
||||||
|
else 0
|
||||||
|
)
|
||||||
|
_wallet_balance.text = "Wallet: $%d" % balance
|
||||||
|
_held_value.text = "Held fish base value: $%d" % held_total
|
||||||
|
|
||||||
|
|
||||||
func _refresh_inventory() -> void:
|
func _refresh_inventory() -> void:
|
||||||
if not is_node_ready():
|
if not is_node_ready():
|
||||||
return
|
return
|
||||||
|
|
@ -286,6 +350,9 @@ func _create_inventory_card(fish_catch: FishCatchType) -> Button:
|
||||||
fish_catch.fish.get_rarity_name(),
|
fish_catch.fish.get_rarity_name(),
|
||||||
fish_catch.catch_sequence,
|
fish_catch.catch_sequence,
|
||||||
]
|
]
|
||||||
|
data_label.text += "\nBase value: $%d" % fish_catch.sale_value
|
||||||
|
if fish_catch.is_favorited:
|
||||||
|
data_label.text += " • ★"
|
||||||
data_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
data_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
data_label.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
data_label.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
content.add_child(data_label)
|
content.add_child(data_label)
|
||||||
|
|
@ -308,16 +375,198 @@ func _update_inventory_detail(fish_catch: FishCatchType) -> void:
|
||||||
_detail_texture.visible = false
|
_detail_texture.visible = false
|
||||||
_detail_name.text = ""
|
_detail_name.text = ""
|
||||||
_detail_data.text = ""
|
_detail_data.text = ""
|
||||||
|
_favorite_button.disabled = true
|
||||||
|
_favorite_button.text = "Favorite"
|
||||||
|
_sell_button.disabled = true
|
||||||
|
_sale_unavailable.text = ""
|
||||||
|
_sale_unavailable.visible = false
|
||||||
return
|
return
|
||||||
_detail_texture.texture = fish_catch.fish.display_texture
|
_detail_texture.texture = fish_catch.fish.display_texture
|
||||||
_detail_texture.visible = fish_catch.fish.display_texture != null
|
_detail_texture.visible = fish_catch.fish.display_texture != null
|
||||||
_detail_name.text = fish_catch.fish.display_name
|
_detail_name.text = fish_catch.fish.display_name
|
||||||
_detail_data.text = "%.2f lb\n%s\nCatch #%d\n%s" % [
|
var buyer_offer: int = (
|
||||||
|
_default_buyer.get_offer(fish_catch.sale_value)
|
||||||
|
if _default_buyer != null
|
||||||
|
else -1
|
||||||
|
)
|
||||||
|
var buyer_offer_text: String = "Buyer unavailable"
|
||||||
|
if buyer_offer >= 0:
|
||||||
|
buyer_offer_text = "%s offer: $%d" % [
|
||||||
|
_default_buyer.display_name,
|
||||||
|
buyer_offer,
|
||||||
|
]
|
||||||
|
_detail_data.text = "%.2f lb\n%s\nBase value: $%d\n%s\nCatch #%d\n%s" % [
|
||||||
fish_catch.weight_lb,
|
fish_catch.weight_lb,
|
||||||
fish_catch.fish.get_rarity_name(),
|
fish_catch.fish.get_rarity_name(),
|
||||||
|
fish_catch.sale_value,
|
||||||
|
buyer_offer_text,
|
||||||
fish_catch.catch_sequence,
|
fish_catch.catch_sequence,
|
||||||
String(fish_catch.catch_id),
|
String(fish_catch.catch_id),
|
||||||
]
|
]
|
||||||
|
_favorite_button.disabled = false
|
||||||
|
_favorite_button.text = (
|
||||||
|
"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
|
||||||
|
)
|
||||||
|
_sell_button.disabled = (
|
||||||
|
fish_catch.is_favorited
|
||||||
|
or not valid_sale_value
|
||||||
|
or not valid_buyer
|
||||||
|
)
|
||||||
|
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."
|
||||||
|
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():
|
||||||
|
return
|
||||||
|
var fish_catch: FishCatchType = _inventory.get_catch_by_id(
|
||||||
|
_selected_catch_id
|
||||||
|
)
|
||||||
|
if fish_catch == null:
|
||||||
|
_refresh_inventory()
|
||||||
|
return
|
||||||
|
if _inventory.set_catch_favorited(
|
||||||
|
fish_catch.catch_id,
|
||||||
|
not fish_catch.is_favorited
|
||||||
|
):
|
||||||
|
_transaction_feedback.text = (
|
||||||
|
"%s %s."
|
||||||
|
% [
|
||||||
|
fish_catch.fish.display_name,
|
||||||
|
"favorited" if fish_catch.is_favorited else "unfavorited",
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
func _on_sell_pressed() -> void:
|
||||||
|
if (
|
||||||
|
_inventory == null
|
||||||
|
or _sale_service == null
|
||||||
|
or _default_buyer == null
|
||||||
|
or _selected_catch_id.is_empty()
|
||||||
|
):
|
||||||
|
return
|
||||||
|
var fish_catch: FishCatchType = _inventory.get_catch_by_id(
|
||||||
|
_selected_catch_id
|
||||||
|
)
|
||||||
|
if fish_catch == null:
|
||||||
|
_transaction_feedback.text = "Fish no longer exists."
|
||||||
|
_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_buyer = _default_buyer
|
||||||
|
_confirmation_buyer_id = _default_buyer.id
|
||||||
|
_confirmation_message.text = (
|
||||||
|
"Sell this %.2f lb %s to the %s for $%d?\nBase value: $%d"
|
||||||
|
% [
|
||||||
|
fish_catch.weight_lb,
|
||||||
|
fish_catch.fish.display_name,
|
||||||
|
_get_buyer_display_group(_default_buyer),
|
||||||
|
buyer_offer,
|
||||||
|
fish_catch.sale_value,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
_sale_confirmation.visible = true
|
||||||
|
_confirm_sale_button.disabled = false
|
||||||
|
_confirm_sale_button.grab_focus()
|
||||||
|
|
||||||
|
|
||||||
|
func _on_confirm_sale_pressed() -> void:
|
||||||
|
if (
|
||||||
|
_sale_in_progress
|
||||||
|
or _sale_service == null
|
||||||
|
or _confirmation_catch_id.is_empty()
|
||||||
|
or _confirmation_buyer == null
|
||||||
|
or _confirmation_buyer.id != _confirmation_buyer_id
|
||||||
|
):
|
||||||
|
return
|
||||||
|
_sale_in_progress = true
|
||||||
|
var requested_catch_id: StringName = _confirmation_catch_id
|
||||||
|
_confirm_sale_button.disabled = true
|
||||||
|
var result: FishSaleResultType = _sale_service.sell(
|
||||||
|
requested_catch_id,
|
||||||
|
_confirmation_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()
|
||||||
|
_refresh_all()
|
||||||
|
_inventory_tab.grab_focus()
|
||||||
|
|
||||||
|
|
||||||
|
func _close_sale_confirmation() -> void:
|
||||||
|
_confirmation_catch_id = StringName()
|
||||||
|
_confirmation_buyer = null
|
||||||
|
_confirmation_buyer_id = StringName()
|
||||||
|
_sale_confirmation.visible = false
|
||||||
|
_confirmation_message.text = ""
|
||||||
|
_confirm_sale_button.disabled = false
|
||||||
|
|
||||||
|
|
||||||
|
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:
|
||||||
|
_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 (
|
||||||
|
_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."
|
||||||
|
|
||||||
|
|
||||||
|
func _get_buyer_display_group(
|
||||||
|
buyer: FishBuyerProfileType,
|
||||||
|
) -> String:
|
||||||
|
if buyer == null:
|
||||||
|
return "buyer"
|
||||||
|
if not buyer.animal_name_plural.is_empty():
|
||||||
|
return buyer.animal_name_plural
|
||||||
|
if not buyer.display_name.is_empty():
|
||||||
|
return buyer.display_name
|
||||||
|
return "buyer"
|
||||||
|
|
||||||
|
|
||||||
func _refresh_logbook() -> void:
|
func _refresh_logbook() -> void:
|
||||||
|
|
|
||||||
|
|
@ -58,6 +58,11 @@ size_flags_horizontal = 3
|
||||||
theme_override_font_sizes/font_size = 24
|
theme_override_font_sizes/font_size = 24
|
||||||
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"
|
||||||
|
|
||||||
[node name="InventoryTab" type="Button" parent="MenuPanel/Margin/Layout/Header"]
|
[node name="InventoryTab" type="Button" parent="MenuPanel/Margin/Layout/Header"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
@ -78,6 +83,12 @@ text = "Close"
|
||||||
[node name="Separator" type="HSeparator" parent="MenuPanel/Margin/Layout"]
|
[node name="Separator" type="HSeparator" parent="MenuPanel/Margin/Layout"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
||||||
|
[node name="TransactionFeedback" type="Label" parent="MenuPanel/Margin/Layout"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 2
|
||||||
|
text = ""
|
||||||
|
horizontal_alignment = 1
|
||||||
|
|
||||||
[node name="Content" type="Control" parent="MenuPanel/Margin/Layout"]
|
[node name="Content" type="Control" parent="MenuPanel/Margin/Layout"]
|
||||||
custom_minimum_size = Vector2(0, 530)
|
custom_minimum_size = Vector2(0, 530)
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
|
|
@ -110,6 +121,15 @@ layout_mode = 2
|
||||||
custom_minimum_size = Vector2(130, 0)
|
custom_minimum_size = Vector2(130, 0)
|
||||||
text = "Newest first"
|
text = "Newest first"
|
||||||
|
|
||||||
|
[node name="SortSpacer" type="Control" parent="MenuPanel/Margin/Layout/Content/InventorySection/SortBar"]
|
||||||
|
layout_mode = 2
|
||||||
|
size_flags_horizontal = 3
|
||||||
|
|
||||||
|
[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"
|
||||||
|
|
||||||
[node name="InventoryBody" type="HSplitContainer" parent="MenuPanel/Margin/Layout/Content/InventorySection"]
|
[node name="InventoryBody" type="HSplitContainer" parent="MenuPanel/Margin/Layout/Content/InventorySection"]
|
||||||
layout_mode = 2
|
layout_mode = 2
|
||||||
size_flags_vertical = 3
|
size_flags_vertical = 3
|
||||||
|
|
@ -183,6 +203,25 @@ layout_mode = 2
|
||||||
autowrap_mode = 2
|
autowrap_mode = 2
|
||||||
horizontal_alignment = 1
|
horizontal_alignment = 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"
|
||||||
|
|
||||||
|
[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"
|
||||||
|
|
||||||
|
[node name="SaleUnavailable" type="Label" parent="MenuPanel/Margin/Layout/Content/InventorySection/InventoryBody/DetailPanel/DetailMargin/DetailStack"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
visible = false
|
||||||
|
layout_mode = 2
|
||||||
|
autowrap_mode = 2
|
||||||
|
horizontal_alignment = 1
|
||||||
|
|
||||||
[node name="LogbookSection" type="VBoxContainer" parent="MenuPanel/Margin/Layout/Content"]
|
[node name="LogbookSection" type="VBoxContainer" parent="MenuPanel/Margin/Layout/Content"]
|
||||||
unique_name_in_owner = true
|
unique_name_in_owner = true
|
||||||
visible = false
|
visible = false
|
||||||
|
|
@ -211,3 +250,50 @@ size_flags_horizontal = 3
|
||||||
theme_override_constants/h_separation = 12
|
theme_override_constants/h_separation = 12
|
||||||
theme_override_constants/v_separation = 12
|
theme_override_constants/v_separation = 12
|
||||||
columns = 4
|
columns = 4
|
||||||
|
|
||||||
|
[node name="SaleConfirmation" type="PanelContainer" parent="MenuPanel/Margin/Layout/Content"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
visible = false
|
||||||
|
layout_mode = 1
|
||||||
|
anchors_preset = 8
|
||||||
|
anchor_left = 0.5
|
||||||
|
anchor_top = 0.5
|
||||||
|
anchor_right = 0.5
|
||||||
|
anchor_bottom = 0.5
|
||||||
|
offset_left = -220.0
|
||||||
|
offset_top = -90.0
|
||||||
|
offset_right = 220.0
|
||||||
|
offset_bottom = 90.0
|
||||||
|
grow_horizontal = 2
|
||||||
|
grow_vertical = 2
|
||||||
|
|
||||||
|
[node name="ConfirmationMargin" type="MarginContainer" parent="MenuPanel/Margin/Layout/Content/SaleConfirmation"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_constants/margin_left = 18
|
||||||
|
theme_override_constants/margin_top = 16
|
||||||
|
theme_override_constants/margin_right = 18
|
||||||
|
theme_override_constants/margin_bottom = 16
|
||||||
|
|
||||||
|
[node name="ConfirmationLayout" type="VBoxContainer" parent="MenuPanel/Margin/Layout/Content/SaleConfirmation/ConfirmationMargin"]
|
||||||
|
layout_mode = 2
|
||||||
|
theme_override_constants/separation = 12
|
||||||
|
|
||||||
|
[node name="ConfirmationMessage" type="Label" parent="MenuPanel/Margin/Layout/Content/SaleConfirmation/ConfirmationMargin/ConfirmationLayout"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 2
|
||||||
|
autowrap_mode = 2
|
||||||
|
horizontal_alignment = 1
|
||||||
|
|
||||||
|
[node name="ConfirmationButtons" type="HBoxContainer" parent="MenuPanel/Margin/Layout/Content/SaleConfirmation/ConfirmationMargin/ConfirmationLayout"]
|
||||||
|
layout_mode = 2
|
||||||
|
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"
|
||||||
|
|
||||||
|
[node name="CancelSaleButton" type="Button" parent="MenuPanel/Margin/Layout/Content/SaleConfirmation/ConfirmationMargin/ConfirmationLayout/ConfirmationButtons"]
|
||||||
|
unique_name_in_owner = true
|
||||||
|
layout_mode = 2
|
||||||
|
text = "Cancel"
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue