Expand fish and equipment catalogs

This commit is contained in:
Alexander Sellite 2026-08-14 16:31:28 -04:00
parent 63e50e285d
commit f55663c105
380 changed files with 9430 additions and 575 deletions

View file

@ -21,6 +21,7 @@ var origin: Vector3
var target: Vector3
var bobber_position: Vector3
var fish_id: StringName
var rod_id: StringName
var bait_tags: Array[StringName] = []
var lure_effects: Array[StringName] = []
var encounter_seed: int = 0

View file

@ -5,6 +5,7 @@ const FishCatchType = preload("res://fish/fish_catch.gd")
const FishDataType = preload("res://fish/fish_data.gd")
const FishPoolType = preload("res://fish/fish_pool.gd")
const FishSelectorType = preload("res://fish/fish_selector.gd")
const FishingRodDataType = preload("res://items/fishing_rod_data.gd")
const FishingContextType = preload("res://fishing/fishing_context.gd")
const CollectionLogType = preload("res://collection/collection_log.gd")
const FishExperienceType = preload("res://fish/fish_experience.gd")
@ -254,10 +255,10 @@ func _handle_cast_request(peer_id: int, data: Dictionary) -> void:
if not bool(data["capacity_available"]):
_record_and_reject(peer_id, request_id, "Cooler is full.")
return
var rod: ItemData = _item_catalog.get_item_by_id(
var rod := _item_catalog.get_item_by_id(
StringName(str(data["rod_id"]))
)
if rod == null or rod.category != ItemData.Category.ROD:
) as FishingRodDataType
if rod == null or not rod.is_available():
_record_and_reject(peer_id, request_id, "Select a fishing rod to cast.")
return
var avatar: Player = _spawn_service.get_avatar(peer_id)
@ -312,7 +313,7 @@ func _handle_cast_request(peer_id: int, data: Dictionary) -> void:
if _item_use != null else null
)
var selected_fish: FishDataType = _select_authoritative_fish(
region, data, effects
region, data, effects, rod
)
if selected_fish == null or not selected_fish.is_allowed_in_water(
region.water_type
@ -339,6 +340,7 @@ func _handle_cast_request(peer_id: int, data: Dictionary) -> void:
not lure_id.is_empty()
and (
lure == null
or not lure.is_available()
or not lure.is_lure()
or (
owns_authoritative_bag
@ -353,6 +355,7 @@ func _handle_cast_request(peer_id: int, data: Dictionary) -> void:
return
if not bait_id.is_empty() and (
bait == null
or not bait.is_available()
or not bait.is_bait()
or (
owns_authoritative_bag
@ -374,20 +377,38 @@ func _handle_cast_request(peer_id: int, data: Dictionary) -> void:
attempt.target = authoritative_target
attempt.bobber_position = authoritative_target
attempt.fish_id = selected_fish.id
attempt.rod_id = rod.item_id
attempt.bait_tags = _bait_tags_for_request(data)
attempt.lure_effects.clear()
if lure != null:
for effect_id: StringName in lure.lure_effects:
attempt.lure_effects.append(effect_id)
attempt.reel_speed = float(data["reel_speed"]) * (
var effect_reel_multiplier: float = (
effects.get_reel_multiplier() if effects != null else 1.0
)
attempt.barrier_damage = int(data["barrier_damage"]) + (
var effect_barrier_bonus: int = (
effects.get_barrier_bonus() if effects != null else 0
)
attempt.bite_time_remaining = _fishing_spot.roll_bite_wait_time() * float(
var effect_bite_multiplier: float = (
effects.get_bite_time_multiplier() if effects != null else 1.0
)
attempt.reel_speed = (
float(data["reel_speed"])
* effect_reel_multiplier
* rod.reel_speed_multiplier
)
attempt.barrier_damage = maxi(
roundi(
(float(data["barrier_damage"]) + effect_barrier_bonus)
* rod.barrier_power_multiplier
),
1,
)
attempt.bite_time_remaining = (
_fishing_spot.roll_bite_wait_time()
* effect_bite_multiplier
* rod.bite_time_multiplier
)
attempt.controller = CatchController.new()
add_child(attempt.controller)
attempt.controller.encounter_updated.connect(
@ -472,6 +493,7 @@ func _select_authoritative_fish(
region: FishableWaterRegion,
data: Dictionary,
effects: PlayerItemEffects,
rod: FishingRodDataType,
) -> FishDataType:
var evidence_log := CollectionLogType.new()
for value: Variant in data["discovered_fish_ids"]:
@ -480,6 +502,7 @@ func _select_authoritative_fish(
return null
evidence_log.mark_discovered(fish_id)
var selector := FishSelectorType.new()
selector.active_rod = rod
selector.undiscovered_weight_multiplier = (
_fishing_spot.undiscovered_weight_multiplier
)
@ -503,7 +526,11 @@ func _bait_tags_for_request(data: Dictionary) -> Array[StringName]:
if bait_id.is_empty() or _item_catalog == null:
return []
var bait: ItemDataType = _item_catalog.get_item_by_id(bait_id)
return bait.bait_tags.duplicate() if bait != null and bait.is_bait() else []
return (
bait.bait_tags.duplicate()
if bait != null and bait.is_available() and bait.is_bait()
else []
)
func _set_bite_pending(attempt: NetworkFishingAttempt) -> void:
@ -527,13 +554,20 @@ func _start_bite(attempt: NetworkFishingAttempt) -> void:
if attempt.phase != NetworkFishingAttempt.Phase.WAITING_FOR_BITE:
return
var fish: FishDataType = _fish_catalog.get_fish_by_id(attempt.fish_id)
if fish == null or fish.catch_profile == null:
var rod := _item_catalog.get_item_by_id(attempt.rod_id) as FishingRodDataType
if (
fish == null
or fish.catch_profile == null
or rod == null
or not rod.is_available()
):
_cancel_attempt(attempt.owner_peer_id, "Fishing attempt ended.")
return
attempt.bite_confirmation_pending = false
attempt.phase = NetworkFishingAttempt.Phase.FIGHTING
attempt.encounter_seed = _new_seed()
var selector := FishSelectorType.new()
selector.active_rod = rod
selector.use_deterministic_test_seed = true
selector.deterministic_test_seed = attempt.encounter_seed ^ 0x5F3759DF
selector.begin_roll()

View file

@ -1,6 +1,8 @@
class_name NetworkItemUseService
extends Node
const FishingRodDataType = preload("res://items/fishing_rod_data.gd")
const ArtShopStockType = preload("res://economy/art_shop_stock.gd")
const MAX_LEDGER_ENTRIES: int = 64
@ -128,6 +130,7 @@ func _handle_use_request(peer_id: int, data: Dictionary) -> void:
)
if error.is_empty() and (
item == null
or not item.is_available()
or (
item.category != ItemData.Category.CONSUMABLE
and not is_fish_finder
@ -294,13 +297,16 @@ func submit_local_equipped(item_id: StringName, owns_item: bool) -> void:
return
_local_equipped_revision += 1
var item: ItemData = _catalog.get_item_by_id(item_id)
var can_equip: bool = (
item != null and item.is_available() and owns_item
)
var data := {
"session_id": _session.get_session_id(),
"owner_peer_id": _session.get_local_peer_id(),
"item_id": str(item_id) if item != null and owns_item else "",
"category": int(item.category) if item != null and owns_item else -1,
"item_id": str(item_id) if can_equip else "",
"category": int(item.category) if can_equip else -1,
"revision": _local_equipped_revision,
"owns_item": owns_item and item != null,
"owns_item": can_equip,
}
if _session.is_host():
_handle_equipped_state(_session.get_local_peer_id(), data)
@ -331,6 +337,7 @@ func _handle_equipped_state(peer_id: int, data: Dictionary) -> void:
var item: ItemData = _catalog.get_item_by_id(item_id)
if not item_id.is_empty() and (
item == null
or not item.is_available()
or not bool(data["owns_item"])
or int(item.category) != int(data["category"])
):
@ -353,8 +360,17 @@ func _apply_equipped(data: Dictionary) -> void:
if avatar != null:
var item_id := StringName(str(data["item_id"]))
var item: ItemData = _catalog.get_item_by_id(item_id)
avatar.set_active_item_is_rod(
int(data["category"]) == ItemData.Category.ROD,
avatar.set_active_fishing_rod(
(
item as FishingRodDataType
if (
item != null
and item.is_available()
and int(data["category"]) == ItemData.Category.ROD
and bool(data["owns_item"])
)
else null
),
true,
)
avatar.set_active_art_kit(

View file

@ -5,6 +5,7 @@ const FishingShopStockType = preload(
"res://economy/fishing_shop_stock.gd"
)
const ItemDataType = preload("res://items/item_data.gd")
const FishingRodDataType = preload("res://items/fishing_rod_data.gd")
const OwnedItemType = preload("res://items/owned_item.gd")
const ArtShopStockType = preload("res://economy/art_shop_stock.gd")
@ -126,6 +127,15 @@ func request_supply(item_id: StringName) -> String:
)
func request_rod(item_id: StringName) -> String:
return _request_purchase(
item_id,
NetworkShopProtocol.ProductCategory.ROD,
1,
_bag.get_quantity(item_id) if _bag != null else 0,
)
func request_reel_speed_upgrade() -> String:
return _request_purchase(
REEL_PRODUCT_ID,
@ -340,7 +350,7 @@ func _build_authoritative_result(
)
if (
item == null
or not item.is_valid()
or not item.is_available()
or product_id not in (
FishingShopStockType.get_stock_item_ids()
)
@ -384,7 +394,29 @@ func _build_authoritative_result(
rejection = "Purchase could not be completed."
resulting_state = current_state + quantity
NetworkShopProtocol.ProductCategory.ROD:
rejection = "This item is not sold here."
var rod := (
_item_catalog.get_item_by_id(product_id)
as FishingRodDataType
if _item_catalog != null else null
)
var available_rods: Array[FishingRodDataType] = (
FishingShopStockType.get_rod_stock(_item_catalog)
)
if (
rod == null
or not rod.is_shop_available()
or rod not in available_rods
or quantity != 1
or current_state != 0
):
rejection = (
"Rod already owned."
if current_state > 0
else "Purchase could not be completed."
)
else:
cost = rod.shop_price
resulting_state = 1
NetworkShopProtocol.ProductCategory.REEL_SPEED_UPGRADE:
if (
product_id != REEL_PRODUCT_ID
@ -434,7 +466,7 @@ func _build_authoritative_result(
if (
product_id != ArtShopStockType.ART_KIT_ITEM_ID
or art_item == null
or not art_item.is_valid()
or not art_item.is_available()
or art_item.category != ItemDataType.Category.TOOL
or art_item.stackable
or not art_item.equippable
@ -643,6 +675,7 @@ func _validate_local_result(data: Dictionary) -> String:
var bait_unlocked: bool = _bag.is_bait_unlocked(product_id)
if (
item == null
or not item.is_available()
or _bag.get_quantity(product_id) != expected_state
or int(data["quantity"]) != FishingShopStockType.get_purchase_quantity(
product_id, expected_state, item.max_stack
@ -655,6 +688,22 @@ func _validate_local_result(data: Dictionary) -> String:
return "Purchase could not be completed."
if not _bag.can_add_item(product_id, data["quantity"]):
return "Your Bag is full."
NetworkShopProtocol.ProductCategory.ROD:
var rod := (
_item_catalog.get_item_by_id(product_id)
as FishingRodDataType
if _item_catalog != null else null
)
if (
rod == null
or not rod.is_shop_available()
or _bag.get_quantity(product_id) != expected_state
or expected_state != 0
or int(data["quantity"]) != 1
or cost != rod.shop_price
or not _bag.can_add_item(product_id, 1)
):
return "Purchase could not be completed."
NetworkShopProtocol.ProductCategory.REEL_SPEED_UPGRADE:
if _upgrades.get_reel_speed_level() != expected_state:
return "Purchase could not be completed."
@ -671,8 +720,14 @@ func _validate_local_result(data: Dictionary) -> String:
if _cooler_capacity.get_next_cost() != cost:
return "Purchase could not be completed."
NetworkShopProtocol.ProductCategory.ART_KIT:
var art_item: ItemDataType = (
_item_catalog.get_item_by_id(product_id)
if _item_catalog != null else null
)
if (
product_id != ArtShopStockType.ART_KIT_ITEM_ID
or art_item == null
or not art_item.is_available()
or _bag.get_quantity(product_id) != expected_state
or not _bag.can_add_item(product_id, 1)
or cost != ArtShopStockType.ART_KIT_PRICE
@ -702,6 +757,11 @@ func _apply_local_product(data: Dictionary) -> bool:
_wallet.debit(data["total_cost"])
and _bag.add_item(product_id, data["quantity"])
)
NetworkShopProtocol.ProductCategory.ROD:
return (
_wallet.debit(int(data["total_cost"]))
and _bag.add_item(product_id, 1)
)
NetworkShopProtocol.ProductCategory.REEL_SPEED_UPGRADE:
return _upgrades.purchase_reel_speed(_wallet)
NetworkShopProtocol.ProductCategory.BARRIER_POWER_UPGRADE: