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 var _reservations: PlayerAssetReservationService func setup( inventory: FishInventoryType, wallet: PlayerWalletType, reservations: PlayerAssetReservationService = null, ) -> void: _inventory = inventory _wallet = wallet _reservations = reservations func can_sell( catch_id: StringName, buyer: FishBuyerProfileType, ) -> bool: 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: 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 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.TRANSACTION_FAILED return result if not _wallet.credit(result.payout): 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 sale_completed.emit(result) return result func _validate_batch( catch_ids: Array[StringName], buyer: FishBuyerProfileType, ) -> FishSaleResultType: var result := FishSaleResultType.new() 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 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 ) 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 ( _reservations != null and _reservations.is_fish_reserved(catch_id) ): result.status = FishSaleResultType.Status.RESERVED 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 ordinary_value: int = ( fish_catch.fish.get_sale_value_for_weight( fish_catch.weight_lb ) ) var offer: int = buyer.get_quality_offer( ordinary_value, fish_catch.quality, ) 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 return result if not _wallet.can_credit(result.payout): result.status = FishSaleResultType.Status.TRANSACTION_FAILED 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." % [ result.fish_count, result.buyer_animal_name, ] ) return result