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
|
||||
Loading…
Add table
Add a link
Reference in a new issue