Add session mail and player gifts

This commit is contained in:
Alexander Sellite 2026-07-29 20:10:44 -04:00
parent 5dda74c9e4
commit e53ad1ac38
19 changed files with 1929 additions and 22 deletions

View file

@ -0,0 +1,200 @@
class_name PlayerAssetReservationService
extends Node
enum AttachmentType { NONE, FISH_COIN, FISH, CONSUMABLE }
signal reservations_changed
var _wallet: PlayerWallet
var _inventory: FishInventory
var _bag: PlayerBag
var _catalog: ItemCatalog
var _reservations: Dictionary[String, Dictionary] = {}
func setup(
wallet: PlayerWallet,
inventory: FishInventory,
bag: PlayerBag,
catalog: ItemCatalog,
) -> void:
_wallet = wallet
_inventory = inventory
_bag = bag
_catalog = catalog
func reserve(reservation_id: String, attachment: Dictionary) -> bool:
if (
reservation_id.is_empty()
or reservation_id.length() > 96
or _reservations.has(reservation_id)
or not validate_attachment(attachment)
):
return false
var kind: int = attachment["type"]
match kind:
AttachmentType.NONE:
return true
AttachmentType.FISH_COIN:
if int(attachment["amount"]) > get_available_fish_coin():
return false
AttachmentType.FISH:
var catch_id := StringName(str(attachment["catch_id"]))
var fish_catch := _inventory.get_catch_by_id(catch_id)
if (
fish_catch == null
or fish_catch.is_favorited
or is_fish_reserved(catch_id)
):
return false
AttachmentType.CONSUMABLE:
var item_id := StringName(str(attachment["item_id"]))
if (
int(attachment["quantity"])
> get_available_item_quantity(item_id)
):
return false
_reservations[reservation_id] = attachment.duplicate(true)
reservations_changed.emit()
return true
func release(reservation_id: String) -> bool:
if not _reservations.erase(reservation_id):
return false
reservations_changed.emit()
return true
func release_all() -> void:
if _reservations.is_empty():
return
_reservations.clear()
reservations_changed.emit()
func has_reservation(reservation_id: String) -> bool:
return _reservations.has(reservation_id)
func get_reservation(reservation_id: String) -> Dictionary:
return _reservations.get(reservation_id, {}).duplicate(true)
func get_available_fish_coin() -> int:
return maxi(_wallet.get_balance() - get_reserved_fish_coin(), 0)
func get_reserved_fish_coin() -> int:
var total := 0
for attachment: Dictionary in _reservations.values():
if int(attachment.get("type", 0)) == AttachmentType.FISH_COIN:
total += int(attachment.get("amount", 0))
return total
func get_available_item_quantity(item_id: StringName) -> int:
return maxi(_bag.get_quantity(item_id) - get_reserved_item_quantity(item_id), 0)
func get_reserved_item_quantity(item_id: StringName) -> int:
var total := 0
for attachment: Dictionary in _reservations.values():
if (
int(attachment.get("type", 0)) == AttachmentType.CONSUMABLE
and StringName(str(attachment.get("item_id", ""))) == item_id
):
total += int(attachment.get("quantity", 0))
return total
func is_fish_reserved(catch_id: StringName) -> bool:
for attachment: Dictionary in _reservations.values():
if (
int(attachment.get("type", 0)) == AttachmentType.FISH
and StringName(str(attachment.get("catch_id", ""))) == catch_id
):
return true
return false
func can_commit(reservation_id: String) -> bool:
var attachment := get_reservation(reservation_id)
if attachment.is_empty():
return false
match int(attachment["type"]):
AttachmentType.FISH_COIN:
return _wallet.can_afford(int(attachment["amount"]))
AttachmentType.FISH:
return _inventory.get_catch_by_id(
StringName(str(attachment["catch_id"]))
) != null
AttachmentType.CONSUMABLE:
return (
_bag.get_quantity(StringName(str(attachment["item_id"])))
>= int(attachment["quantity"])
)
return false
func commit_removal(reservation_id: String) -> bool:
var attachment := get_reservation(reservation_id)
if attachment.is_empty() or not can_commit(reservation_id):
return false
var applied := false
match int(attachment["type"]):
AttachmentType.FISH_COIN:
applied = _wallet.debit(int(attachment["amount"]))
AttachmentType.FISH:
applied = _inventory.remove_catch_by_id(
StringName(str(attachment["catch_id"]))
) != null
AttachmentType.CONSUMABLE:
applied = _bag.remove_item(
StringName(str(attachment["item_id"])),
int(attachment["quantity"])
)
if applied:
release(reservation_id)
return applied
func validate_attachment(attachment: Dictionary) -> bool:
if typeof(attachment.get("type")) != TYPE_INT:
return false
match int(attachment["type"]):
AttachmentType.NONE:
return attachment.size() == 1
AttachmentType.FISH_COIN:
return (
attachment.size() == 2
and
typeof(attachment.get("amount")) == TYPE_INT
and int(attachment["amount"]) >= 1
and int(attachment["amount"]) <= 1000000000
)
AttachmentType.FISH:
return (
attachment.size() == 3
and
typeof(attachment.get("catch_id")) == TYPE_STRING
and not str(attachment["catch_id"]).is_empty()
and str(attachment["catch_id"]).length() <= 160
and typeof(attachment.get("catch")) == TYPE_DICTIONARY
)
AttachmentType.CONSUMABLE:
if (
attachment.size() != 3
or
typeof(attachment.get("item_id")) != TYPE_STRING
or typeof(attachment.get("quantity")) != TYPE_INT
or int(attachment["quantity"]) < 1
or int(attachment["quantity"]) > 999
):
return false
var item := _catalog.get_item_by_id(
StringName(str(attachment["item_id"]))
)
return item != null and item.category == ItemData.Category.CONSUMABLE
return false

View file

@ -0,0 +1 @@
uid://bheplri3k6ch7