Expand bait progression and retune fishing
This commit is contained in:
parent
2f30691808
commit
2a5e0a8f26
21 changed files with 555 additions and 80 deletions
|
|
@ -9,13 +9,33 @@ const WORM_MAX_STACK: int = 10
|
||||||
|
|
||||||
const ITEM_PRICES: Dictionary[StringName, int] = {
|
const ITEM_PRICES: Dictionary[StringName, int] = {
|
||||||
&"worms": 1,
|
&"worms": 1,
|
||||||
|
&"snails": 3,
|
||||||
|
&"shrimp": 7,
|
||||||
|
&"squid_chunks": 15,
|
||||||
|
&"whole_anchovy": 30,
|
||||||
|
&"whole_sardine": 60,
|
||||||
|
&"luminous_roe": 125,
|
||||||
&"coffee": 20,
|
&"coffee": 20,
|
||||||
&"energy_drink": 35,
|
&"energy_drink": 35,
|
||||||
&"snack": 30,
|
&"snack": 30,
|
||||||
&"fish_finder": 60,
|
&"fish_finder": 60,
|
||||||
}
|
}
|
||||||
|
const BAIT_UNLOCK_PRICES: Dictionary[StringName, int] = {
|
||||||
|
&"snails": 400,
|
||||||
|
&"shrimp": 1200,
|
||||||
|
&"squid_chunks": 3500,
|
||||||
|
&"whole_anchovy": 9000,
|
||||||
|
&"whole_sardine": 22000,
|
||||||
|
&"luminous_roe": 50000,
|
||||||
|
}
|
||||||
const ITEM_ORDER: Array[StringName] = [
|
const ITEM_ORDER: Array[StringName] = [
|
||||||
&"worms",
|
&"worms",
|
||||||
|
&"snails",
|
||||||
|
&"shrimp",
|
||||||
|
&"squid_chunks",
|
||||||
|
&"whole_anchovy",
|
||||||
|
&"whole_sardine",
|
||||||
|
&"luminous_roe",
|
||||||
&"coffee",
|
&"coffee",
|
||||||
&"energy_drink",
|
&"energy_drink",
|
||||||
&"snack",
|
&"snack",
|
||||||
|
|
@ -31,14 +51,35 @@ static func get_stock_item_ids() -> Array[StringName]:
|
||||||
return ITEM_ORDER.duplicate()
|
return ITEM_ORDER.duplicate()
|
||||||
|
|
||||||
|
|
||||||
static func get_purchase_quantity(item_id: StringName, owned: int) -> int:
|
static func get_unlock_price(item_id: StringName) -> int:
|
||||||
if item_id == &"worms":
|
return BAIT_UNLOCK_PRICES.get(item_id, -1)
|
||||||
return maxi(WORM_MAX_STACK - owned, 0)
|
|
||||||
|
|
||||||
|
static func get_purchase_quantity(
|
||||||
|
item_id: StringName,
|
||||||
|
owned: int,
|
||||||
|
maximum_stack: int = WORM_MAX_STACK,
|
||||||
|
) -> int:
|
||||||
|
if is_bait_topoff(item_id):
|
||||||
|
return maxi(maximum_stack - owned, 0)
|
||||||
return 1
|
return 1
|
||||||
|
|
||||||
|
|
||||||
static func is_bait_topoff(item_id: StringName) -> bool:
|
static func is_bait_topoff(item_id: StringName) -> bool:
|
||||||
return item_id == &"worms"
|
return item_id == &"worms" or BAIT_UNLOCK_PRICES.has(item_id)
|
||||||
|
|
||||||
|
|
||||||
|
static func get_purchase_cost(
|
||||||
|
item_id: StringName,
|
||||||
|
quantity: int,
|
||||||
|
bait_unlocked: bool,
|
||||||
|
) -> int:
|
||||||
|
if quantity <= 0:
|
||||||
|
return -1
|
||||||
|
if is_bait_topoff(item_id) and item_id != &"worms" and not bait_unlocked:
|
||||||
|
return get_unlock_price(item_id)
|
||||||
|
var unit_price: int = get_price(item_id)
|
||||||
|
return unit_price * quantity if unit_price >= 0 else -1
|
||||||
|
|
||||||
|
|
||||||
static func purchase_one(
|
static func purchase_one(
|
||||||
|
|
@ -49,22 +90,32 @@ static func purchase_one(
|
||||||
) -> bool:
|
) -> bool:
|
||||||
if wallet == null or bag == null or catalog == null:
|
if wallet == null or bag == null or catalog == null:
|
||||||
return false
|
return false
|
||||||
var price: int = get_price(item_id)
|
|
||||||
var item: ItemDataType = catalog.get_item_by_id(item_id)
|
var item: ItemDataType = catalog.get_item_by_id(item_id)
|
||||||
|
var bait_topoff: bool = is_bait_topoff(item_id)
|
||||||
|
var quantity: int = get_purchase_quantity(
|
||||||
|
item_id,
|
||||||
|
bag.get_quantity(item_id),
|
||||||
|
item.max_stack if item != null else WORM_MAX_STACK,
|
||||||
|
)
|
||||||
|
var price: int = get_purchase_cost(
|
||||||
|
item_id,
|
||||||
|
quantity,
|
||||||
|
bag.is_bait_unlocked(item_id),
|
||||||
|
)
|
||||||
if (
|
if (
|
||||||
price < 0
|
price < 0
|
||||||
or item == null
|
or item == null
|
||||||
or not item.is_valid()
|
or not item.is_valid()
|
||||||
or (item.category != ItemDataType.Category.CONSUMABLE and not is_bait_topoff(item_id))
|
or (item.category != ItemDataType.Category.CONSUMABLE and not bait_topoff)
|
||||||
or not item.stackable
|
or not item.stackable
|
||||||
or (not item.usable and not is_bait_topoff(item_id))
|
or (not item.usable and not bait_topoff)
|
||||||
or not bag.can_add_item(item_id, 1)
|
or not bag.can_add_item(item_id, quantity)
|
||||||
or not wallet.can_afford(price)
|
or not wallet.can_afford(price)
|
||||||
):
|
):
|
||||||
return false
|
return false
|
||||||
if not wallet.debit(price):
|
if not wallet.debit(price):
|
||||||
return false
|
return false
|
||||||
if bag.add_item(item_id, 1):
|
if bag.add_item(item_id, quantity):
|
||||||
return true
|
return true
|
||||||
if not wallet.credit(price):
|
if not wallet.credit(price):
|
||||||
push_error("Fishing Shop failed to roll back an item purchase.")
|
push_error("Fishing Shop failed to roll back an item purchase.")
|
||||||
|
|
|
||||||
|
|
@ -16,6 +16,12 @@ const ALL_TIERS_MASK: int = (1 << TIER_COUNT) - 1
|
||||||
# multipliers later without changing catch serialization or tier identity.
|
# multipliers later without changing catch serialization or tier identity.
|
||||||
const BASE_ROLL_WEIGHTS: Array[float] = [40.0, 32.0, 18.0, 8.0, 2.0]
|
const BASE_ROLL_WEIGHTS: Array[float] = [40.0, 32.0, 18.0, 8.0, 2.0]
|
||||||
const WORM_ROLL_WEIGHTS: Array[float] = [80.0, 10.0, 5.0, 4.0, 1.0]
|
const WORM_ROLL_WEIGHTS: Array[float] = [80.0, 10.0, 5.0, 4.0, 1.0]
|
||||||
|
const SNAIL_ROLL_WEIGHTS: Array[float] = [68.0, 16.0, 8.0, 6.0, 2.0]
|
||||||
|
const SHRIMP_ROLL_WEIGHTS: Array[float] = [56.0, 21.0, 12.0, 8.0, 3.0]
|
||||||
|
const SQUID_ROLL_WEIGHTS: Array[float] = [44.0, 25.0, 16.0, 11.0, 4.0]
|
||||||
|
const ANCHOVY_ROLL_WEIGHTS: Array[float] = [34.0, 27.0, 20.0, 13.0, 6.0]
|
||||||
|
const SARDINE_ROLL_WEIGHTS: Array[float] = [25.0, 28.0, 23.0, 16.0, 8.0]
|
||||||
|
const LUMINOUS_ROE_ROLL_WEIGHTS: Array[float] = [16.0, 26.0, 26.0, 20.0, 12.0]
|
||||||
const SALE_MULTIPLIERS: Array[float] = [1.0, 1.1, 1.25, 1.5, 2.0]
|
const SALE_MULTIPLIERS: Array[float] = [1.0, 1.1, 1.25, 1.5, 2.0]
|
||||||
# Legacy profile multiplier retained for serialized/profile compatibility.
|
# Legacy profile multiplier retained for serialized/profile compatibility.
|
||||||
# New encounters use the weighted quality/rarity/weight bands below.
|
# New encounters use the weighted quality/rarity/weight bands below.
|
||||||
|
|
@ -136,17 +142,34 @@ static func roll(
|
||||||
|
|
||||||
|
|
||||||
static func roll_weights_for_bait(active_bait_tags: Array[StringName]) -> Array[float]:
|
static func roll_weights_for_bait(active_bait_tags: Array[StringName]) -> Array[float]:
|
||||||
|
var bait_weights: Array[float] = rarity_weights_for_bait(active_bait_tags)
|
||||||
|
var weights: Array[float] = []
|
||||||
|
for quality: int in TIER_COUNT:
|
||||||
|
weights.append(bait_weights[quality] / BASE_ROLL_WEIGHTS[quality])
|
||||||
|
return weights
|
||||||
|
|
||||||
|
|
||||||
|
static func rarity_weights_for_bait(
|
||||||
|
active_bait_tags: Array[StringName],
|
||||||
|
) -> Array[float]:
|
||||||
var weights: Array[float] = []
|
var weights: Array[float] = []
|
||||||
if active_bait_tags.is_empty():
|
if active_bait_tags.is_empty():
|
||||||
weights.assign([2.5, 0.0, 0.0, 0.0, 0.0])
|
weights.assign([100.0, 0.0, 0.0, 0.0, 0.0])
|
||||||
|
elif active_bait_tags.has(&"bait_rarity_6"):
|
||||||
|
weights.assign(LUMINOUS_ROE_ROLL_WEIGHTS)
|
||||||
|
elif active_bait_tags.has(&"bait_rarity_5"):
|
||||||
|
weights.assign(SARDINE_ROLL_WEIGHTS)
|
||||||
|
elif active_bait_tags.has(&"bait_rarity_4"):
|
||||||
|
weights.assign(ANCHOVY_ROLL_WEIGHTS)
|
||||||
|
elif active_bait_tags.has(&"bait_rarity_3"):
|
||||||
|
weights.assign(SQUID_ROLL_WEIGHTS)
|
||||||
|
elif active_bait_tags.has(&"bait_rarity_2"):
|
||||||
|
weights.assign(SHRIMP_ROLL_WEIGHTS)
|
||||||
|
elif active_bait_tags.has(&"bait_rarity_1"):
|
||||||
|
weights.assign(SNAIL_ROLL_WEIGHTS)
|
||||||
else:
|
else:
|
||||||
weights.assign([
|
# Unknown and legacy bait tags retain the original worms behavior.
|
||||||
WORM_ROLL_WEIGHTS[0] / BASE_ROLL_WEIGHTS[0],
|
weights.assign(WORM_ROLL_WEIGHTS)
|
||||||
WORM_ROLL_WEIGHTS[1] / BASE_ROLL_WEIGHTS[1],
|
|
||||||
WORM_ROLL_WEIGHTS[2] / BASE_ROLL_WEIGHTS[2],
|
|
||||||
WORM_ROLL_WEIGHTS[3] / BASE_ROLL_WEIGHTS[3],
|
|
||||||
WORM_ROLL_WEIGHTS[4] / BASE_ROLL_WEIGHTS[4],
|
|
||||||
])
|
|
||||||
return weights
|
return weights
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -81,9 +81,9 @@ func select_fish(
|
||||||
|
|
||||||
|
|
||||||
func _roll_rarity(context: FishingContextType) -> int:
|
func _roll_rarity(context: FishingContextType) -> int:
|
||||||
var weights: Array[float] = [100.0, 0.0, 0.0, 0.0, 0.0]
|
var weights: Array[float] = FishQualityType.rarity_weights_for_bait(
|
||||||
if not context.active_bait_tags.is_empty():
|
context.active_bait_tags
|
||||||
weights = [80.0, 10.0, 5.0, 4.0, 1.0]
|
)
|
||||||
for index: int in range(weights.size()):
|
for index: int in range(weights.size()):
|
||||||
if index < rarity_weight_multipliers.size():
|
if index < rarity_weight_multipliers.size():
|
||||||
weights[index] *= maxf(rarity_weight_multipliers[index], 0.0)
|
weights[index] *= maxf(rarity_weight_multipliers[index], 0.0)
|
||||||
|
|
|
||||||
|
|
@ -100,14 +100,14 @@ enum FishingState {
|
||||||
@export_range(0.0, 1.0, 0.01) var withdrawal_surface_clearance: float = 0.4
|
@export_range(0.0, 1.0, 0.01) var withdrawal_surface_clearance: float = 0.4
|
||||||
|
|
||||||
@export_category("Timing")
|
@export_category("Timing")
|
||||||
const BITE_QUICK_MIN_SECONDS: float = 8.0
|
const BITE_QUICK_MIN_SECONDS: float = 6.0
|
||||||
const BITE_QUICK_MAX_SECONDS: float = 24.0
|
const BITE_QUICK_MAX_SECONDS: float = 18.0
|
||||||
const BITE_TYPICAL_MAX_SECONDS: float = 72.0
|
const BITE_TYPICAL_MAX_SECONDS: float = 48.0
|
||||||
const BITE_LONG_MAX_SECONDS: float = 144.0
|
const BITE_LONG_MAX_SECONDS: float = 72.0
|
||||||
const BITE_MAX_SECONDS: float = 192.0
|
const BITE_MAX_SECONDS: float = 90.0
|
||||||
const BITE_QUICK_PROBABILITY: float = 0.25
|
const BITE_QUICK_PROBABILITY: float = 0.30
|
||||||
const BITE_TYPICAL_PROBABILITY: float = 0.65
|
const BITE_TYPICAL_PROBABILITY: float = 0.65
|
||||||
const BITE_LONG_PROBABILITY: float = 0.08
|
const BITE_LONG_PROBABILITY: float = 0.04
|
||||||
const NETWORK_INPUT_RESEND_INTERVAL_SECONDS: float = 0.1
|
const NETWORK_INPUT_RESEND_INTERVAL_SECONDS: float = 0.1
|
||||||
@export_range(0.1, 10.0, 0.1) var cooldown_duration: float = 1.0
|
@export_range(0.1, 10.0, 0.1) var cooldown_duration: float = 1.0
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -5,10 +5,15 @@ const ItemCatalogType = preload("res://items/item_catalog.gd")
|
||||||
const ItemDataType = preload("res://items/item_data.gd")
|
const ItemDataType = preload("res://items/item_data.gd")
|
||||||
const OwnedItemType = preload("res://items/owned_item.gd")
|
const OwnedItemType = preload("res://items/owned_item.gd")
|
||||||
|
|
||||||
|
const DEFAULT_UNLOCKED_BAIT_IDS: Array[StringName] = [&"worms"]
|
||||||
|
|
||||||
signal contents_changed
|
signal contents_changed
|
||||||
|
|
||||||
var _catalog: ItemCatalogType
|
var _catalog: ItemCatalogType
|
||||||
var _items: Array[OwnedItemType] = []
|
var _items: Array[OwnedItemType] = []
|
||||||
|
var _unlocked_bait_ids: Array[StringName] = (
|
||||||
|
DEFAULT_UNLOCKED_BAIT_IDS.duplicate()
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
func setup(catalog: ItemCatalogType) -> void:
|
func setup(catalog: ItemCatalogType) -> void:
|
||||||
|
|
@ -27,6 +32,8 @@ func add_item(item_id: StringName, quantity: int = 1) -> bool:
|
||||||
owned.item_id = item_id
|
owned.item_id = item_id
|
||||||
owned.quantity = quantity
|
owned.quantity = quantity
|
||||||
_items.append(owned)
|
_items.append(owned)
|
||||||
|
if item.is_bait() and not _unlocked_bait_ids.has(item_id):
|
||||||
|
_unlocked_bait_ids.append(item_id)
|
||||||
contents_changed.emit()
|
contents_changed.emit()
|
||||||
return true
|
return true
|
||||||
|
|
||||||
|
|
@ -85,6 +92,46 @@ func get_all_items() -> Array[OwnedItemType]:
|
||||||
return result
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
func get_unlocked_bait_ids() -> Array[StringName]:
|
||||||
|
return _unlocked_bait_ids.duplicate()
|
||||||
|
|
||||||
|
|
||||||
|
func is_bait_unlocked(item_id: StringName) -> bool:
|
||||||
|
return _unlocked_bait_ids.has(item_id)
|
||||||
|
|
||||||
|
|
||||||
|
func get_unlocked_bait_items() -> Array[OwnedItemType]:
|
||||||
|
var result: Array[OwnedItemType] = []
|
||||||
|
for item_id: StringName in _unlocked_bait_ids:
|
||||||
|
var owned: OwnedItemType = get_owned_item(item_id)
|
||||||
|
if owned != null:
|
||||||
|
result.append(owned.duplicate_record())
|
||||||
|
continue
|
||||||
|
var empty_record := OwnedItemType.new()
|
||||||
|
empty_record.item_id = item_id
|
||||||
|
empty_record.quantity = 0
|
||||||
|
result.append(empty_record)
|
||||||
|
return result
|
||||||
|
|
||||||
|
|
||||||
|
func replace_unlocked_bait_ids(item_ids: Array[StringName]) -> bool:
|
||||||
|
var validated: Array[StringName] = DEFAULT_UNLOCKED_BAIT_IDS.duplicate()
|
||||||
|
var seen: Dictionary[StringName, bool] = {}
|
||||||
|
for item_id: StringName in item_ids:
|
||||||
|
if item_id.is_empty() or seen.has(item_id):
|
||||||
|
return false
|
||||||
|
seen[item_id] = true
|
||||||
|
if validated.has(item_id):
|
||||||
|
continue
|
||||||
|
var item: ItemDataType = _resolve_valid_item(item_id)
|
||||||
|
if item == null or not item.is_bait():
|
||||||
|
return false
|
||||||
|
validated.append(item_id)
|
||||||
|
_unlocked_bait_ids = validated
|
||||||
|
contents_changed.emit()
|
||||||
|
return true
|
||||||
|
|
||||||
|
|
||||||
func replace_all_items(items: Array[OwnedItemType]) -> bool:
|
func replace_all_items(items: Array[OwnedItemType]) -> bool:
|
||||||
var validated: Array[OwnedItemType] = []
|
var validated: Array[OwnedItemType] = []
|
||||||
var seen: Dictionary[StringName, bool] = {}
|
var seen: Dictionary[StringName, bool] = {}
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,4 @@
|
||||||
[gd_resource type="Resource" script_class="ItemCatalog" load_steps=10 format=3]
|
[gd_resource type="Resource" script_class="ItemCatalog" load_steps=16 format=3]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://items/item_catalog.gd" id="1_script"]
|
[ext_resource type="Script" path="res://items/item_catalog.gd" id="1_script"]
|
||||||
[ext_resource type="Resource" path="res://items/catalog/basic_fishing_rod.tres" id="2_rod"]
|
[ext_resource type="Resource" path="res://items/catalog/basic_fishing_rod.tres" id="2_rod"]
|
||||||
|
|
@ -9,7 +9,13 @@
|
||||||
[ext_resource type="Resource" path="res://items/catalog/cooler_expansion.tres" id="7_cooler"]
|
[ext_resource type="Resource" path="res://items/catalog/cooler_expansion.tres" id="7_cooler"]
|
||||||
[ext_resource type="Resource" path="res://items/catalog/art_kit.tres" id="8_art_kit"]
|
[ext_resource type="Resource" path="res://items/catalog/art_kit.tres" id="8_art_kit"]
|
||||||
[ext_resource type="Resource" path="res://items/catalog/worms.tres" id="9_worms"]
|
[ext_resource type="Resource" path="res://items/catalog/worms.tres" id="9_worms"]
|
||||||
|
[ext_resource type="Resource" path="res://items/catalog/snails.tres" id="10_snails"]
|
||||||
|
[ext_resource type="Resource" path="res://items/catalog/shrimp.tres" id="11_shrimp"]
|
||||||
|
[ext_resource type="Resource" path="res://items/catalog/squid_chunks.tres" id="12_squid"]
|
||||||
|
[ext_resource type="Resource" path="res://items/catalog/whole_anchovy.tres" id="13_anchovy"]
|
||||||
|
[ext_resource type="Resource" path="res://items/catalog/whole_sardine.tres" id="14_sardine"]
|
||||||
|
[ext_resource type="Resource" path="res://items/catalog/luminous_roe.tres" id="15_roe"]
|
||||||
|
|
||||||
[resource]
|
[resource]
|
||||||
script = ExtResource("1_script")
|
script = ExtResource("1_script")
|
||||||
items = [ExtResource("2_rod"), ExtResource("3_coffee"), ExtResource("4_energy"), ExtResource("5_snack"), ExtResource("6_finder"), ExtResource("7_cooler"), ExtResource("8_art_kit"), ExtResource("9_worms")]
|
items = [ExtResource("2_rod"), ExtResource("3_coffee"), ExtResource("4_energy"), ExtResource("5_snack"), ExtResource("6_finder"), ExtResource("7_cooler"), ExtResource("8_art_kit"), ExtResource("9_worms"), ExtResource("10_snails"), ExtResource("11_shrimp"), ExtResource("12_squid"), ExtResource("13_anchovy"), ExtResource("14_sardine"), ExtResource("15_roe")]
|
||||||
|
|
|
||||||
14
items/catalog/luminous_roe.tres
Normal file
14
items/catalog/luminous_roe.tres
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
[gd_resource type="Resource" script_class="ItemData" load_steps=2 format=3]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://items/item_data.gd" id="1"]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
script = ExtResource("1")
|
||||||
|
item_id = &"luminous_roe"
|
||||||
|
display_name = "luminous roe"
|
||||||
|
description = "rare glowing roe with the strongest rare catch odds."
|
||||||
|
category = 2
|
||||||
|
bait_tags = Array[StringName]([&"bait_rarity_6"])
|
||||||
|
stackable = true
|
||||||
|
max_stack = 10
|
||||||
|
hotbar_allowed = false
|
||||||
14
items/catalog/shrimp.tres
Normal file
14
items/catalog/shrimp.tres
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
[gd_resource type="Resource" script_class="ItemData" load_steps=2 format=3]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://items/item_data.gd" id="1"]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
script = ExtResource("1")
|
||||||
|
item_id = &"shrimp"
|
||||||
|
display_name = "shrimp"
|
||||||
|
description = "lively bait that noticeably improves rare catch chances."
|
||||||
|
category = 2
|
||||||
|
bait_tags = Array[StringName]([&"bait_rarity_2"])
|
||||||
|
stackable = true
|
||||||
|
max_stack = 10
|
||||||
|
hotbar_allowed = false
|
||||||
14
items/catalog/snails.tres
Normal file
14
items/catalog/snails.tres
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
[gd_resource type="Resource" script_class="ItemData" load_steps=2 format=3]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://items/item_data.gd" id="1"]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
script = ExtResource("1")
|
||||||
|
item_id = &"snails"
|
||||||
|
display_name = "snails"
|
||||||
|
description = "slow-moving bait with a better chance for rarer catches."
|
||||||
|
category = 2
|
||||||
|
bait_tags = Array[StringName]([&"bait_rarity_1"])
|
||||||
|
stackable = true
|
||||||
|
max_stack = 10
|
||||||
|
hotbar_allowed = false
|
||||||
14
items/catalog/squid_chunks.tres
Normal file
14
items/catalog/squid_chunks.tres
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
[gd_resource type="Resource" script_class="ItemData" load_steps=2 format=3]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://items/item_data.gd" id="1"]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
script = ExtResource("1")
|
||||||
|
item_id = &"squid_chunks"
|
||||||
|
display_name = "squid chunks"
|
||||||
|
description = "rich cut bait that strongly favors rarer catches."
|
||||||
|
category = 2
|
||||||
|
bait_tags = Array[StringName]([&"bait_rarity_3"])
|
||||||
|
stackable = true
|
||||||
|
max_stack = 10
|
||||||
|
hotbar_allowed = false
|
||||||
14
items/catalog/whole_anchovy.tres
Normal file
14
items/catalog/whole_anchovy.tres
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
[gd_resource type="Resource" script_class="ItemData" load_steps=2 format=3]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://items/item_data.gd" id="1"]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
script = ExtResource("1")
|
||||||
|
item_id = &"whole_anchovy"
|
||||||
|
display_name = "whole anchovy"
|
||||||
|
description = "premium whole bait with high rare catch potential."
|
||||||
|
category = 2
|
||||||
|
bait_tags = Array[StringName]([&"bait_rarity_4"])
|
||||||
|
stackable = true
|
||||||
|
max_stack = 10
|
||||||
|
hotbar_allowed = false
|
||||||
14
items/catalog/whole_sardine.tres
Normal file
14
items/catalog/whole_sardine.tres
Normal file
|
|
@ -0,0 +1,14 @@
|
||||||
|
[gd_resource type="Resource" script_class="ItemData" load_steps=2 format=3]
|
||||||
|
|
||||||
|
[ext_resource type="Script" path="res://items/item_data.gd" id="1"]
|
||||||
|
|
||||||
|
[resource]
|
||||||
|
script = ExtResource("1")
|
||||||
|
item_id = &"whole_sardine"
|
||||||
|
display_name = "whole sardine"
|
||||||
|
description = "valuable whole bait tuned for exceptional catches."
|
||||||
|
category = 2
|
||||||
|
bait_tags = Array[StringName]([&"bait_rarity_5"])
|
||||||
|
stackable = true
|
||||||
|
max_stack = 10
|
||||||
|
hotbar_allowed = false
|
||||||
|
|
@ -1,14 +1,16 @@
|
||||||
[gd_resource type="Resource" script_class="ItemData" load_steps=2 format=3]
|
[gd_resource type="Resource" script_class="ItemData" load_steps=3 format=3]
|
||||||
|
|
||||||
[ext_resource type="Script" path="res://items/item_data.gd" id="1"]
|
[ext_resource type="Script" path="res://items/item_data.gd" id="1"]
|
||||||
|
[ext_resource type="Texture2D" path="res://items/icons/bait/64_bait_worms.png" id="2_icon"]
|
||||||
|
|
||||||
[resource]
|
[resource]
|
||||||
script = ExtResource("1")
|
script = ExtResource("1")
|
||||||
item_id = &"worms"
|
item_id = &"worms"
|
||||||
display_name = "worms"
|
display_name = "worms"
|
||||||
description = "common bait. fills your tacklebox at the fishing shop."
|
description = "basic bait that opens a small chance for rarer catches."
|
||||||
|
icon = ExtResource("2_icon")
|
||||||
category = 2
|
category = 2
|
||||||
bait_tags = Array[StringName]([&"worm"])
|
bait_tags = Array[StringName]([&"worm", &"bait_rarity_0"])
|
||||||
stackable = true
|
stackable = true
|
||||||
max_stack = 10
|
max_stack = 10
|
||||||
hotbar_allowed = false
|
hotbar_allowed = false
|
||||||
|
|
|
||||||
BIN
items/icons/bait/64_bait_worms.png
Normal file
BIN
items/icons/bait/64_bait_worms.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.6 KiB |
40
items/icons/bait/64_bait_worms.png.import
Normal file
40
items/icons/bait/64_bait_worms.png.import
Normal file
|
|
@ -0,0 +1,40 @@
|
||||||
|
[remap]
|
||||||
|
|
||||||
|
importer="texture"
|
||||||
|
type="CompressedTexture2D"
|
||||||
|
uid="uid://bn6815x0rrspw"
|
||||||
|
path="res://.godot/imported/64_bait_worms.png-c9f31d77527e73888fc56963d4e78e30.ctex"
|
||||||
|
metadata={
|
||||||
|
"vram_texture": false
|
||||||
|
}
|
||||||
|
|
||||||
|
[deps]
|
||||||
|
|
||||||
|
source_file="res://items/icons/bait/64_bait_worms.png"
|
||||||
|
dest_files=["res://.godot/imported/64_bait_worms.png-c9f31d77527e73888fc56963d4e78e30.ctex"]
|
||||||
|
|
||||||
|
[params]
|
||||||
|
|
||||||
|
compress/mode=0
|
||||||
|
compress/high_quality=false
|
||||||
|
compress/lossy_quality=0.7
|
||||||
|
compress/uastc_level=0
|
||||||
|
compress/rdo_quality_loss=0.0
|
||||||
|
compress/hdr_compression=1
|
||||||
|
compress/normal_map=0
|
||||||
|
compress/channel_pack=0
|
||||||
|
mipmaps/generate=false
|
||||||
|
mipmaps/limit=-1
|
||||||
|
roughness/mode=0
|
||||||
|
roughness/src_normal=""
|
||||||
|
process/channel_remap/red=0
|
||||||
|
process/channel_remap/green=1
|
||||||
|
process/channel_remap/blue=2
|
||||||
|
process/channel_remap/alpha=3
|
||||||
|
process/fix_alpha_border=true
|
||||||
|
process/premult_alpha=false
|
||||||
|
process/normal_map_invert_y=false
|
||||||
|
process/hdr_as_srgb=false
|
||||||
|
process/hdr_clamp_exposure=false
|
||||||
|
process/size_limit=0
|
||||||
|
detect_3d/compress_to=1
|
||||||
|
|
@ -992,6 +992,7 @@ func _capture_assets() -> Dictionary:
|
||||||
return {
|
return {
|
||||||
"wallet": _wallet.get_balance(),
|
"wallet": _wallet.get_balance(),
|
||||||
"bag": _bag.get_all_items(),
|
"bag": _bag.get_all_items(),
|
||||||
|
"unlocked_bait_ids": _bag.get_unlocked_bait_ids(),
|
||||||
"catches": _inventory.get_all_catches(),
|
"catches": _inventory.get_all_catches(),
|
||||||
"next_sequence": _inventory.get_next_catch_sequence(),
|
"next_sequence": _inventory.get_next_catch_sequence(),
|
||||||
"discovered": _collection_log.get_discovered_ids(),
|
"discovered": _collection_log.get_discovered_ids(),
|
||||||
|
|
@ -1002,6 +1003,7 @@ func _capture_assets() -> Dictionary:
|
||||||
func _restore_assets(snapshot: Dictionary) -> void:
|
func _restore_assets(snapshot: Dictionary) -> void:
|
||||||
_wallet.restore_balance(int(snapshot["wallet"]))
|
_wallet.restore_balance(int(snapshot["wallet"]))
|
||||||
_bag.replace_all_items(snapshot["bag"])
|
_bag.replace_all_items(snapshot["bag"])
|
||||||
|
_bag.replace_unlocked_bait_ids(snapshot["unlocked_bait_ids"])
|
||||||
_inventory.replace_all_catches(
|
_inventory.replace_all_catches(
|
||||||
snapshot["catches"], int(snapshot["next_sequence"])
|
snapshot["catches"], int(snapshot["next_sequence"])
|
||||||
)
|
)
|
||||||
|
|
|
||||||
|
|
@ -56,6 +56,11 @@ static func validate_request(data: Variant) -> String:
|
||||||
var quantity: int = payload["quantity"]
|
var quantity: int = payload["quantity"]
|
||||||
var wallet_balance: int = payload["wallet_balance"]
|
var wallet_balance: int = payload["wallet_balance"]
|
||||||
var current_state: int = payload["current_state"]
|
var current_state: int = payload["current_state"]
|
||||||
|
if (
|
||||||
|
payload.has("bait_unlocked")
|
||||||
|
and typeof(payload["bait_unlocked"]) != TYPE_BOOL
|
||||||
|
):
|
||||||
|
return "Purchase could not be completed."
|
||||||
if (
|
if (
|
||||||
request_id.is_empty()
|
request_id.is_empty()
|
||||||
or request_id.length() > MAX_ID_LENGTH
|
or request_id.length() > MAX_ID_LENGTH
|
||||||
|
|
|
||||||
|
|
@ -109,7 +109,15 @@ func is_local_purchase_pending() -> bool:
|
||||||
|
|
||||||
func request_supply(item_id: StringName) -> String:
|
func request_supply(item_id: StringName) -> String:
|
||||||
var owned: int = _bag.get_quantity(item_id) if _bag != null else 0
|
var owned: int = _bag.get_quantity(item_id) if _bag != null else 0
|
||||||
var quantity: int = FishingShopStockType.get_purchase_quantity(item_id, owned)
|
var item: ItemDataType = (
|
||||||
|
_item_catalog.get_item_by_id(item_id)
|
||||||
|
if _item_catalog != null else null
|
||||||
|
)
|
||||||
|
var quantity: int = FishingShopStockType.get_purchase_quantity(
|
||||||
|
item_id,
|
||||||
|
owned,
|
||||||
|
item.max_stack if item != null else FishingShopStockType.WORM_MAX_STACK,
|
||||||
|
)
|
||||||
return _request_purchase(
|
return _request_purchase(
|
||||||
item_id,
|
item_id,
|
||||||
NetworkShopProtocol.ProductCategory.SUPPLY,
|
NetworkShopProtocol.ProductCategory.SUPPLY,
|
||||||
|
|
@ -223,6 +231,13 @@ func _request_purchase(
|
||||||
"wallet_balance": _wallet.get_balance() if _wallet != null else 0,
|
"wallet_balance": _wallet.get_balance() if _wallet != null else 0,
|
||||||
"current_state": current_state,
|
"current_state": current_state,
|
||||||
}
|
}
|
||||||
|
if (
|
||||||
|
category == NetworkShopProtocol.ProductCategory.SUPPLY
|
||||||
|
and FishingShopStockType.is_bait_topoff(product_id)
|
||||||
|
):
|
||||||
|
request["bait_unlocked"] = (
|
||||||
|
_bag != null and _bag.is_bait_unlocked(product_id)
|
||||||
|
)
|
||||||
_pending_local_request = request.duplicate(true)
|
_pending_local_request = request.duplicate(true)
|
||||||
local_purchase_pending.emit(request_id)
|
local_purchase_pending.emit(request_id)
|
||||||
if _session.is_host():
|
if _session.is_host():
|
||||||
|
|
@ -317,16 +332,18 @@ func _build_authoritative_result(
|
||||||
var item: ItemDataType = _item_catalog.get_item_by_id(
|
var item: ItemDataType = _item_catalog.get_item_by_id(
|
||||||
product_id
|
product_id
|
||||||
) if _item_catalog != null else null
|
) if _item_catalog != null else null
|
||||||
cost = FishingShopStockType.get_price(product_id)
|
var bait_topoff: bool = (
|
||||||
|
FishingShopStockType.is_bait_topoff(product_id)
|
||||||
|
)
|
||||||
if (
|
if (
|
||||||
item == null
|
item == null
|
||||||
or not item.is_valid()
|
or not item.is_valid()
|
||||||
or product_id not in (
|
or product_id not in (
|
||||||
FishingShopStockType.get_stock_item_ids()
|
FishingShopStockType.get_stock_item_ids()
|
||||||
)
|
)
|
||||||
or (item.category != ItemDataType.Category.CONSUMABLE and not FishingShopStockType.is_bait_topoff(product_id))
|
or (item.category != ItemDataType.Category.CONSUMABLE and not bait_topoff)
|
||||||
or not item.stackable
|
or not item.stackable
|
||||||
or (not item.usable and not FishingShopStockType.is_bait_topoff(product_id))
|
or (not item.usable and not bait_topoff)
|
||||||
or current_state >= item.max_stack
|
or current_state >= item.max_stack
|
||||||
or current_state + quantity > item.max_stack
|
or current_state + quantity > item.max_stack
|
||||||
):
|
):
|
||||||
|
|
@ -336,10 +353,21 @@ func _build_authoritative_result(
|
||||||
else "Purchase could not be completed."
|
else "Purchase could not be completed."
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
if not FishingShopStockType.is_bait_topoff(product_id) and quantity != 1:
|
var expected_quantity: int = (
|
||||||
|
FishingShopStockType.get_purchase_quantity(
|
||||||
|
product_id, current_state, item.max_stack
|
||||||
|
)
|
||||||
|
)
|
||||||
|
if quantity != expected_quantity:
|
||||||
rejection = "Purchase could not be completed."
|
rejection = "Purchase could not be completed."
|
||||||
else:
|
else:
|
||||||
cost *= quantity
|
cost = FishingShopStockType.get_purchase_cost(
|
||||||
|
product_id,
|
||||||
|
quantity,
|
||||||
|
bool(request.get("bait_unlocked", true)),
|
||||||
|
)
|
||||||
|
if cost < 0:
|
||||||
|
rejection = "Purchase could not be completed."
|
||||||
resulting_state = current_state + quantity
|
resulting_state = current_state + quantity
|
||||||
NetworkShopProtocol.ProductCategory.ROD:
|
NetworkShopProtocol.ProductCategory.ROD:
|
||||||
rejection = "This item is not sold here."
|
rejection = "This item is not sold here."
|
||||||
|
|
@ -532,6 +560,9 @@ func _apply_purchase_result(data: Dictionary) -> void:
|
||||||
return
|
return
|
||||||
var wallet_snapshot: int = _wallet.get_balance()
|
var wallet_snapshot: int = _wallet.get_balance()
|
||||||
var bag_snapshot: Array[OwnedItemType] = _bag.get_all_items()
|
var bag_snapshot: Array[OwnedItemType] = _bag.get_all_items()
|
||||||
|
var bait_unlock_snapshot: Array[StringName] = (
|
||||||
|
_bag.get_unlocked_bait_ids()
|
||||||
|
)
|
||||||
var reel_snapshot: int = _upgrades.get_reel_speed_level()
|
var reel_snapshot: int = _upgrades.get_reel_speed_level()
|
||||||
var barrier_snapshot: int = _upgrades.get_barrier_power_level()
|
var barrier_snapshot: int = _upgrades.get_barrier_power_level()
|
||||||
var cooler_snapshot: int = _cooler_capacity.get_level()
|
var cooler_snapshot: int = _cooler_capacity.get_level()
|
||||||
|
|
@ -539,6 +570,7 @@ func _apply_purchase_result(data: Dictionary) -> void:
|
||||||
var applied: bool = _apply_local_product(data)
|
var applied: bool = _apply_local_product(data)
|
||||||
if not applied or not _save_manager.save_if_dirty():
|
if not applied or not _save_manager.save_if_dirty():
|
||||||
_bag.replace_all_items(bag_snapshot)
|
_bag.replace_all_items(bag_snapshot)
|
||||||
|
_bag.replace_unlocked_bait_ids(bait_unlock_snapshot)
|
||||||
_upgrades.restore_levels(reel_snapshot, barrier_snapshot)
|
_upgrades.restore_levels(reel_snapshot, barrier_snapshot)
|
||||||
_cooler_capacity.restore_level(cooler_snapshot)
|
_cooler_capacity.restore_level(cooler_snapshot)
|
||||||
_art_unlocks.restore_mask(art_snapshot)
|
_art_unlocks.restore_mask(art_snapshot)
|
||||||
|
|
@ -590,9 +622,22 @@ func _validate_local_result(data: Dictionary) -> String:
|
||||||
return "Not enough fish coin."
|
return "Not enough fish coin."
|
||||||
match category:
|
match category:
|
||||||
NetworkShopProtocol.ProductCategory.SUPPLY:
|
NetworkShopProtocol.ProductCategory.SUPPLY:
|
||||||
if _bag.get_quantity(product_id) != expected_state:
|
var item: ItemDataType = (
|
||||||
|
_item_catalog.get_item_by_id(product_id)
|
||||||
|
if _item_catalog != null else null
|
||||||
|
)
|
||||||
|
var bait_unlocked: bool = _bag.is_bait_unlocked(product_id)
|
||||||
|
if (
|
||||||
|
item == null
|
||||||
|
or _bag.get_quantity(product_id) != expected_state
|
||||||
|
or int(data["quantity"]) != FishingShopStockType.get_purchase_quantity(
|
||||||
|
product_id, expected_state, item.max_stack
|
||||||
|
)
|
||||||
|
):
|
||||||
return "Purchase could not be completed."
|
return "Purchase could not be completed."
|
||||||
if cost != FishingShopStockType.get_price(product_id) * int(data["quantity"]):
|
if cost != FishingShopStockType.get_purchase_cost(
|
||||||
|
product_id, int(data["quantity"]), bait_unlocked
|
||||||
|
):
|
||||||
return "Purchase could not be completed."
|
return "Purchase could not be completed."
|
||||||
if not _bag.can_add_item(product_id, data["quantity"]):
|
if not _bag.can_add_item(product_id, data["quantity"]):
|
||||||
return "Your Bag is full."
|
return "Your Bag is full."
|
||||||
|
|
|
||||||
|
|
@ -43,6 +43,7 @@ class LoadSnapshot:
|
||||||
var wallet_balance: int = 0
|
var wallet_balance: int = 0
|
||||||
var next_catch_sequence: int = 1
|
var next_catch_sequence: int = 1
|
||||||
var bag_items: Array[OwnedItemType] = []
|
var bag_items: Array[OwnedItemType] = []
|
||||||
|
var unlocked_bait_ids: Array[StringName] = []
|
||||||
var hotbar_slots: Array[StringName] = []
|
var hotbar_slots: Array[StringName] = []
|
||||||
var fish_hotbar_slots: Array[StringName] = []
|
var fish_hotbar_slots: Array[StringName] = []
|
||||||
var selected_hotbar_slot: int = 0
|
var selected_hotbar_slot: int = 0
|
||||||
|
|
@ -258,7 +259,10 @@ func load_player_data() -> bool:
|
||||||
var wallet_restored: bool = _wallet.restore_balance(
|
var wallet_restored: bool = _wallet.restore_balance(
|
||||||
snapshot.wallet_balance
|
snapshot.wallet_balance
|
||||||
)
|
)
|
||||||
var bag_restored: bool = _bag.replace_all_items(snapshot.bag_items)
|
var bag_restored: bool = (
|
||||||
|
_bag.replace_all_items(snapshot.bag_items)
|
||||||
|
and _bag.replace_unlocked_bait_ids(snapshot.unlocked_bait_ids)
|
||||||
|
)
|
||||||
var hotbar_restored: bool = _hotbar.replace_state(
|
var hotbar_restored: bool = _hotbar.replace_state(
|
||||||
snapshot.hotbar_slots,
|
snapshot.hotbar_slots,
|
||||||
snapshot.selected_hotbar_slot,
|
snapshot.selected_hotbar_slot,
|
||||||
|
|
@ -492,6 +496,12 @@ func _build_save_dictionary() -> Dictionary:
|
||||||
if owned == null or not owned.is_valid():
|
if owned == null or not owned.is_valid():
|
||||||
return {}
|
return {}
|
||||||
serialized_items.append(owned.to_save_dict())
|
serialized_items.append(owned.to_save_dict())
|
||||||
|
var serialized_unlocked_baits: Array[String] = []
|
||||||
|
for item_id: StringName in _bag.get_unlocked_bait_ids():
|
||||||
|
var item_data = _item_catalog.get_item_by_id(item_id)
|
||||||
|
if item_data == null or not item_data.is_valid() or not item_data.is_bait():
|
||||||
|
return {}
|
||||||
|
serialized_unlocked_baits.append(String(item_id))
|
||||||
var serialized_slots: Array[String] = []
|
var serialized_slots: Array[String] = []
|
||||||
for item_id: StringName in _hotbar.get_slots():
|
for item_id: StringName in _hotbar.get_slots():
|
||||||
serialized_slots.append(String(item_id))
|
serialized_slots.append(String(item_id))
|
||||||
|
|
@ -524,6 +534,7 @@ func _build_save_dictionary() -> Dictionary:
|
||||||
},
|
},
|
||||||
"bag": {
|
"bag": {
|
||||||
"items": serialized_items,
|
"items": serialized_items,
|
||||||
|
"unlocked_bait_ids": serialized_unlocked_baits,
|
||||||
},
|
},
|
||||||
"hotbar": {
|
"hotbar": {
|
||||||
"selected_slot": _hotbar.get_selected_slot(),
|
"selected_slot": _hotbar.get_selected_slot(),
|
||||||
|
|
@ -589,6 +600,10 @@ func _build_load_snapshot(save_data: Dictionary) -> LoadSnapshot:
|
||||||
or typeof(inventory_data.get("catches")) != TYPE_ARRAY
|
or typeof(inventory_data.get("catches")) != TYPE_ARRAY
|
||||||
or not inventory_data.has("next_catch_sequence")
|
or not inventory_data.has("next_catch_sequence")
|
||||||
or typeof(bag_data.get("items")) != TYPE_ARRAY
|
or typeof(bag_data.get("items")) != TYPE_ARRAY
|
||||||
|
or (
|
||||||
|
bag_data.has("unlocked_bait_ids")
|
||||||
|
and typeof(bag_data.get("unlocked_bait_ids")) != TYPE_ARRAY
|
||||||
|
)
|
||||||
or typeof(hotbar_data.get("slots")) != TYPE_ARRAY
|
or typeof(hotbar_data.get("slots")) != TYPE_ARRAY
|
||||||
or not hotbar_data.has("selected_slot")
|
or not hotbar_data.has("selected_slot")
|
||||||
or not experience_data.has("total_experience")
|
or not experience_data.has("total_experience")
|
||||||
|
|
@ -781,6 +796,34 @@ func _build_load_snapshot(save_data: Dictionary) -> LoadSnapshot:
|
||||||
seen_items[item_id] = true
|
seen_items[item_id] = true
|
||||||
snapshot.bag_items.append(owned)
|
snapshot.bag_items.append(owned)
|
||||||
|
|
||||||
|
var seen_unlocked_baits: Dictionary[StringName, bool] = {}
|
||||||
|
if bag_data.has("unlocked_bait_ids"):
|
||||||
|
var unlocked_bait_values: Array = bag_data["unlocked_bait_ids"]
|
||||||
|
for value: Variant in unlocked_bait_values:
|
||||||
|
if typeof(value) not in [TYPE_STRING, TYPE_STRING_NAME]:
|
||||||
|
continue
|
||||||
|
var bait_id: StringName = StringName(str(value))
|
||||||
|
var bait_data = _item_catalog.get_item_by_id(bait_id)
|
||||||
|
if (
|
||||||
|
bait_id.is_empty()
|
||||||
|
or seen_unlocked_baits.has(bait_id)
|
||||||
|
or bait_data == null
|
||||||
|
or not bait_data.is_valid()
|
||||||
|
or not bait_data.is_bait()
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
seen_unlocked_baits[bait_id] = true
|
||||||
|
snapshot.unlocked_bait_ids.append(bait_id)
|
||||||
|
for owned: OwnedItemType in snapshot.bag_items:
|
||||||
|
var owned_item_data = _item_catalog.get_item_by_id(owned.item_id)
|
||||||
|
if (
|
||||||
|
owned_item_data != null
|
||||||
|
and owned_item_data.is_bait()
|
||||||
|
and not seen_unlocked_baits.has(owned.item_id)
|
||||||
|
):
|
||||||
|
seen_unlocked_baits[owned.item_id] = true
|
||||||
|
snapshot.unlocked_bait_ids.append(owned.item_id)
|
||||||
|
|
||||||
var slot_values: Array = hotbar_data["slots"]
|
var slot_values: Array = hotbar_data["slots"]
|
||||||
snapshot.hotbar_slots.resize(PlayerHotbarType.SLOT_COUNT)
|
snapshot.hotbar_slots.resize(PlayerHotbarType.SLOT_COUNT)
|
||||||
snapshot.hotbar_slots.fill(StringName())
|
snapshot.hotbar_slots.fill(StringName())
|
||||||
|
|
@ -1098,6 +1141,8 @@ func _restore_defaults() -> void:
|
||||||
)
|
)
|
||||||
_wallet.restore_balance(0)
|
_wallet.restore_balance(0)
|
||||||
_bag.replace_all_items(default_items)
|
_bag.replace_all_items(default_items)
|
||||||
|
var default_unlocked_baits: Array[StringName] = []
|
||||||
|
_bag.replace_unlocked_bait_ids(default_unlocked_baits)
|
||||||
_hotbar.replace_state(default_slots, 0)
|
_hotbar.replace_state(default_slots, 0)
|
||||||
_fishing_upgrades.reset_to_defaults()
|
_fishing_upgrades.reset_to_defaults()
|
||||||
_cooler_capacity.reset_to_defaults()
|
_cooler_capacity.reset_to_defaults()
|
||||||
|
|
|
||||||
|
|
@ -423,34 +423,71 @@ func _refresh_supplies() -> void:
|
||||||
button.icon = item.icon
|
button.icon = item.icon
|
||||||
button.expand_icon = true
|
button.expand_icon = true
|
||||||
button.alignment = HORIZONTAL_ALIGNMENT_LEFT
|
button.alignment = HORIZONTAL_ALIGNMENT_LEFT
|
||||||
|
var owned: int = _bag.get_quantity(item_id)
|
||||||
|
var bait_topoff: bool = FishingShopStockType.is_bait_topoff(item_id)
|
||||||
|
var bait_unlocked: bool = (
|
||||||
|
not bait_topoff or _bag.is_bait_unlocked(item_id)
|
||||||
|
)
|
||||||
|
var quantity: int = FishingShopStockType.get_purchase_quantity(
|
||||||
|
item_id, owned, item.max_stack
|
||||||
|
)
|
||||||
|
var total_cost: int = FishingShopStockType.get_purchase_cost(
|
||||||
|
item_id, quantity, bait_unlocked
|
||||||
|
)
|
||||||
button.text = "%s\n$%d • owned %d" % [
|
button.text = "%s\n$%d • owned %d" % [
|
||||||
item.display_name,
|
item.display_name,
|
||||||
FishingShopStockType.get_price(item_id),
|
FishingShopStockType.get_price(item_id),
|
||||||
_bag.get_quantity(item_id),
|
owned,
|
||||||
]
|
]
|
||||||
if FishingShopStockType.is_bait_topoff(item_id):
|
var tooltip_text: String = item.description
|
||||||
button.text = "%s\n$%d each • %d/%d" % [
|
if bait_topoff:
|
||||||
|
if item.icon != null:
|
||||||
|
button.custom_minimum_size = Vector2(72, 72)
|
||||||
|
button.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
|
||||||
|
button.alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
|
button.text = ""
|
||||||
|
else:
|
||||||
|
button.text = (
|
||||||
|
"%s\nunlock $%d" % [item.display_name, total_cost]
|
||||||
|
if not bait_unlocked
|
||||||
|
else "%s\n$%d each • %d/%d" % [
|
||||||
item.display_name,
|
item.display_name,
|
||||||
FishingShopStockType.get_price(item_id),
|
FishingShopStockType.get_price(item_id),
|
||||||
_bag.get_quantity(item_id),
|
owned,
|
||||||
FishingShopStockType.WORM_MAX_STACK,
|
item.max_stack,
|
||||||
]
|
]
|
||||||
button.tooltip_text = item.description
|
)
|
||||||
|
tooltip_text = (
|
||||||
|
"%s\nunlock $%d • fills to %d/%d\n%s" % [
|
||||||
|
item.display_name,
|
||||||
|
total_cost,
|
||||||
|
item.max_stack,
|
||||||
|
item.max_stack,
|
||||||
|
item.description,
|
||||||
|
]
|
||||||
|
if not bait_unlocked
|
||||||
|
else "%s\n$%d each • %d/%d\n%s" % [
|
||||||
|
item.display_name,
|
||||||
|
FishingShopStockType.get_price(item_id),
|
||||||
|
owned,
|
||||||
|
item.max_stack,
|
||||||
|
item.description,
|
||||||
|
]
|
||||||
|
)
|
||||||
button.disabled = (
|
button.disabled = (
|
||||||
_transaction_in_progress
|
_transaction_in_progress
|
||||||
or _closing
|
or _closing
|
||||||
or _network_shop == null
|
or _network_shop == null
|
||||||
or not _network_shop.can_request_purchase()
|
or not _network_shop.can_request_purchase()
|
||||||
or FishingShopStockType.get_purchase_quantity(item_id, _bag.get_quantity(item_id)) <= 0
|
or quantity <= 0
|
||||||
or not _bag.can_add_item(item_id, FishingShopStockType.get_purchase_quantity(item_id, _bag.get_quantity(item_id)))
|
or total_cost < 0
|
||||||
or not _wallet.can_afford(
|
or not _bag.can_add_item(item_id, quantity)
|
||||||
FishingShopStockType.get_price(item_id) * FishingShopStockType.get_purchase_quantity(item_id, _bag.get_quantity(item_id))
|
or not _wallet.can_afford(total_cost)
|
||||||
)
|
|
||||||
)
|
)
|
||||||
button.tooltip_text = (
|
button.tooltip_text = (
|
||||||
"Purchases are unavailable in this session."
|
"Purchases are unavailable in this session."
|
||||||
if _network_shop == null or not _network_shop.can_request_purchase()
|
if _network_shop == null or not _network_shop.can_request_purchase()
|
||||||
else item.description
|
else tooltip_text
|
||||||
)
|
)
|
||||||
UtilityPageStyleType.apply_ocean_button(button)
|
UtilityPageStyleType.apply_ocean_button(button)
|
||||||
button.pressed.connect(_purchase_supply.bind(item_id))
|
button.pressed.connect(_purchase_supply.bind(item_id))
|
||||||
|
|
@ -585,11 +622,20 @@ func _purchase_supply(item_id: StringName) -> void:
|
||||||
_feedback.text = "unable to complete purchase."
|
_feedback.text = "unable to complete purchase."
|
||||||
return
|
return
|
||||||
var owned: int = _bag.get_quantity(item_id)
|
var owned: int = _bag.get_quantity(item_id)
|
||||||
var quantity: int = FishingShopStockType.get_purchase_quantity(item_id, owned)
|
var item: ItemDataType = _item_catalog.get_item_by_id(item_id)
|
||||||
|
if item == null:
|
||||||
|
_feedback.text = "Purchase could not be completed."
|
||||||
|
return
|
||||||
|
var quantity: int = FishingShopStockType.get_purchase_quantity(
|
||||||
|
item_id, owned, item.max_stack
|
||||||
|
)
|
||||||
if quantity <= 0 or not _bag.can_add_item(item_id, quantity):
|
if quantity <= 0 or not _bag.can_add_item(item_id, quantity):
|
||||||
_feedback.text = "Your Bag is full."
|
_feedback.text = "Your Bag is full."
|
||||||
return
|
return
|
||||||
if not _wallet.can_afford(FishingShopStockType.get_price(item_id) * quantity):
|
var total_cost: int = FishingShopStockType.get_purchase_cost(
|
||||||
|
item_id, quantity, _bag.is_bait_unlocked(item_id)
|
||||||
|
)
|
||||||
|
if total_cost < 0 or not _wallet.can_afford(total_cost):
|
||||||
_feedback.text = "Not enough fish coin."
|
_feedback.text = "Not enough fish coin."
|
||||||
return
|
return
|
||||||
if _network_shop == null:
|
if _network_shop == null:
|
||||||
|
|
|
||||||
|
|
@ -189,7 +189,7 @@ const SALE_CONFIRMATION_SIZE := Vector2(520.0, 190.0)
|
||||||
@onready var _lures_filter: Button = %LuresFilter
|
@onready var _lures_filter: Button = %LuresFilter
|
||||||
@onready var _tackle_empty: Label = %TackleEmpty
|
@onready var _tackle_empty: Label = %TackleEmpty
|
||||||
@onready var _tackle_detail_text: Label = %TackleDetailText
|
@onready var _tackle_detail_text: Label = %TackleDetailText
|
||||||
@onready var _tackle_equip_button: Button = %TackleEquipButton
|
@onready var _tackle_equip_button: NotepadInkActionType = %TackleEquipButton
|
||||||
@onready var _tackle_item_list: VBoxContainer = %TackleItemList
|
@onready var _tackle_item_list: VBoxContainer = %TackleItemList
|
||||||
@onready var _bag_outer_wall: PanelContainer = %BagOuterWall
|
@onready var _bag_outer_wall: PanelContainer = %BagOuterWall
|
||||||
@onready var _bag_inner_liner: PanelContainer = %BagInnerLiner
|
@onready var _bag_inner_liner: PanelContainer = %BagInnerLiner
|
||||||
|
|
@ -847,7 +847,7 @@ func mount_shop_cooler(
|
||||||
_set_content_interactive(true)
|
_set_content_interactive(true)
|
||||||
_update_cooler_water_mask()
|
_update_cooler_water_mask()
|
||||||
set_process(true)
|
set_process(true)
|
||||||
call_deferred("_focus_shop_cooler")
|
_cooler_sort_option.call_deferred("grab_focus")
|
||||||
return true
|
return true
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1323,7 +1323,12 @@ func _refresh_tackle_box() -> void:
|
||||||
child.queue_free()
|
child.queue_free()
|
||||||
var matching_items: Array[OwnedItemType] = []
|
var matching_items: Array[OwnedItemType] = []
|
||||||
if _bag != null and _item_catalog != null:
|
if _bag != null and _item_catalog != null:
|
||||||
for owned: OwnedItemType in _bag.get_all_items():
|
var available_items: Array[OwnedItemType] = (
|
||||||
|
_bag.get_unlocked_bait_items()
|
||||||
|
if _tackle_view == TackleView.BAIT
|
||||||
|
else _bag.get_all_items()
|
||||||
|
)
|
||||||
|
for owned: OwnedItemType in available_items:
|
||||||
var item: ItemDataType = _item_catalog.get_item_by_id(
|
var item: ItemDataType = _item_catalog.get_item_by_id(
|
||||||
owned.item_id
|
owned.item_id
|
||||||
)
|
)
|
||||||
|
|
@ -1343,11 +1348,22 @@ func _refresh_tackle_box() -> void:
|
||||||
for owned: OwnedItemType in matching_items:
|
for owned: OwnedItemType in matching_items:
|
||||||
var item: ItemDataType = _item_catalog.get_item_by_id(owned.item_id)
|
var item: ItemDataType = _item_catalog.get_item_by_id(owned.item_id)
|
||||||
var row := Button.new()
|
var row := Button.new()
|
||||||
row.text = "%s ×%d" % [item.display_name, owned.quantity]
|
|
||||||
row.icon = item.icon
|
row.icon = item.icon
|
||||||
|
row.tooltip_text = "%s ×%d" % [item.display_name, owned.quantity]
|
||||||
|
if item.is_bait() and item.icon != null:
|
||||||
|
row.custom_minimum_size = Vector2(72, 72)
|
||||||
|
row.size_flags_horizontal = Control.SIZE_SHRINK_BEGIN
|
||||||
|
row.expand_icon = true
|
||||||
|
row.alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
|
else:
|
||||||
|
row.text = "%s ×%d" % [item.display_name, owned.quantity]
|
||||||
row.alignment = HORIZONTAL_ALIGNMENT_LEFT
|
row.alignment = HORIZONTAL_ALIGNMENT_LEFT
|
||||||
row.toggle_mode = true
|
row.toggle_mode = true
|
||||||
row.button_pressed = owned.item_id == _selected_tackle_item_id
|
row.button_pressed = owned.item_id == _selected_tackle_item_id
|
||||||
|
if item.is_bait() and item.icon != null:
|
||||||
|
_apply_tackle_bait_button_style(row)
|
||||||
|
_add_tackle_quantity_badge(row, owned.quantity)
|
||||||
|
else:
|
||||||
UtilityPageStyle.apply_ocean_button(row)
|
UtilityPageStyle.apply_ocean_button(row)
|
||||||
row.pressed.connect(_select_tackle_item.bind(owned.item_id))
|
row.pressed.connect(_select_tackle_item.bind(owned.item_id))
|
||||||
_tackle_item_list.add_child(row)
|
_tackle_item_list.add_child(row)
|
||||||
|
|
@ -1360,6 +1376,68 @@ func _refresh_tackle_box() -> void:
|
||||||
_update_tackle_detail()
|
_update_tackle_detail()
|
||||||
|
|
||||||
|
|
||||||
|
func _add_tackle_quantity_badge(row: Button, quantity: int) -> void:
|
||||||
|
var badge := Panel.new()
|
||||||
|
badge.name = "QuantityBadge"
|
||||||
|
badge.anchor_left = 1.0
|
||||||
|
badge.anchor_right = 1.0
|
||||||
|
badge.offset_left = -27.0
|
||||||
|
badge.offset_top = 3.0
|
||||||
|
badge.offset_right = -3.0
|
||||||
|
badge.offset_bottom = 27.0
|
||||||
|
badge.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
|
badge.z_index = 2
|
||||||
|
var badge_style := StyleBoxFlat.new()
|
||||||
|
badge_style.bg_color = Color("0b5558")
|
||||||
|
badge_style.set_corner_radius_all(12)
|
||||||
|
badge_style.anti_aliasing = false
|
||||||
|
badge.add_theme_stylebox_override("panel", badge_style)
|
||||||
|
row.add_child(badge)
|
||||||
|
var quantity_label := Label.new()
|
||||||
|
quantity_label.set_anchors_and_offsets_preset(Control.PRESET_FULL_RECT)
|
||||||
|
quantity_label.offset_left = 1.0
|
||||||
|
quantity_label.offset_right = 1.0
|
||||||
|
quantity_label.mouse_filter = Control.MOUSE_FILTER_IGNORE
|
||||||
|
quantity_label.text = str(quantity)
|
||||||
|
quantity_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER
|
||||||
|
quantity_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER
|
||||||
|
quantity_label.add_theme_font_override("font", UtilityPageStyle.TuffyFont)
|
||||||
|
quantity_label.add_theme_font_size_override(
|
||||||
|
"font_size",
|
||||||
|
13 if quantity >= 100 else 15,
|
||||||
|
)
|
||||||
|
quantity_label.add_theme_color_override("font_color", Color("e7f5f4"))
|
||||||
|
badge.add_child(quantity_label)
|
||||||
|
|
||||||
|
|
||||||
|
func _apply_tackle_bait_button_style(button: Button) -> void:
|
||||||
|
var profile := BubbleMenuProfile.new()
|
||||||
|
var normal_style: StyleBoxFlat = profile.make_normal_style()
|
||||||
|
var hover_style: StyleBoxFlat = profile.make_hover_style()
|
||||||
|
var pressed_style: StyleBoxFlat = profile.make_pressed_style()
|
||||||
|
var disabled_style: StyleBoxFlat = profile.make_disabled_style()
|
||||||
|
for style: StyleBoxFlat in [
|
||||||
|
normal_style,
|
||||||
|
hover_style,
|
||||||
|
pressed_style,
|
||||||
|
disabled_style,
|
||||||
|
]:
|
||||||
|
style.set_corner_radius_all(36)
|
||||||
|
style.content_margin_left = 13.5
|
||||||
|
style.content_margin_top = 13.5
|
||||||
|
style.content_margin_right = 13.5
|
||||||
|
style.content_margin_bottom = 13.5
|
||||||
|
button.add_theme_stylebox_override("normal", normal_style)
|
||||||
|
button.add_theme_stylebox_override("hover", hover_style)
|
||||||
|
button.add_theme_stylebox_override("focus", hover_style)
|
||||||
|
button.add_theme_stylebox_override("pressed", pressed_style)
|
||||||
|
button.add_theme_stylebox_override("disabled", disabled_style)
|
||||||
|
button.add_theme_color_override("icon_normal_color", Color.WHITE)
|
||||||
|
button.add_theme_color_override("icon_hover_color", Color.WHITE)
|
||||||
|
button.add_theme_color_override("icon_focus_color", Color.WHITE)
|
||||||
|
button.add_theme_color_override("icon_pressed_color", Color.WHITE)
|
||||||
|
|
||||||
|
|
||||||
func _select_tackle_item(item_id: StringName) -> void:
|
func _select_tackle_item(item_id: StringName) -> void:
|
||||||
_selected_tackle_item_id = item_id
|
_selected_tackle_item_id = item_id
|
||||||
_refresh_tackle_box()
|
_refresh_tackle_box()
|
||||||
|
|
@ -1373,6 +1451,7 @@ func _update_tackle_detail() -> void:
|
||||||
)
|
)
|
||||||
if item == null:
|
if item == null:
|
||||||
_tackle_equip_button.visible = false
|
_tackle_equip_button.visible = false
|
||||||
|
_tackle_equip_button.persistent_mark = false
|
||||||
_tackle_detail_text.text = (
|
_tackle_detail_text.text = (
|
||||||
"Select bait for details."
|
"Select bait for details."
|
||||||
if _tackle_view == TackleView.BAIT
|
if _tackle_view == TackleView.BAIT
|
||||||
|
|
@ -1387,28 +1466,28 @@ func _update_tackle_detail() -> void:
|
||||||
if _hotbar.get_item_id(slot_index) == item.item_id:
|
if _hotbar.get_item_id(slot_index) == item.item_id:
|
||||||
assigned_slot = slot_index
|
assigned_slot = slot_index
|
||||||
break
|
break
|
||||||
var state_text: String = (
|
var detail_lines: Array[String] = [
|
||||||
"hotbar slot %d" % (assigned_slot + 1)
|
|
||||||
if assigned_slot >= 0
|
|
||||||
else "not assigned"
|
|
||||||
)
|
|
||||||
_tackle_detail_text.text = (
|
|
||||||
"%s\n\nType: %s\nQuantity: %d\n%s\n\n%s"
|
|
||||||
% [
|
|
||||||
item.display_name,
|
item.display_name,
|
||||||
item.get_category_name(),
|
"",
|
||||||
quantity,
|
"quantity: %d" % quantity,
|
||||||
state_text,
|
|
||||||
item.description,
|
|
||||||
]
|
]
|
||||||
)
|
if assigned_slot >= 0:
|
||||||
|
detail_lines.append("hotbar slot %d" % (assigned_slot + 1))
|
||||||
|
detail_lines.append("")
|
||||||
|
detail_lines.append(item.description)
|
||||||
|
_tackle_detail_text.text = "\n".join(detail_lines)
|
||||||
if item.is_bait():
|
if item.is_bait():
|
||||||
|
var is_equipped: bool = (
|
||||||
|
_player != null and _player.active_bait_id == item.item_id
|
||||||
|
)
|
||||||
_tackle_equip_button.text = (
|
_tackle_equip_button.text = (
|
||||||
"dequip %s" % item.display_name
|
"dequip %s" % item.display_name
|
||||||
if _player != null and _player.active_bait_id == item.item_id
|
if is_equipped
|
||||||
else "equip %s" % item.display_name
|
else "equip %s" % item.display_name
|
||||||
)
|
)
|
||||||
|
_tackle_equip_button.persistent_mark = is_equipped
|
||||||
_tackle_equip_button.disabled = quantity <= 0
|
_tackle_equip_button.disabled = quantity <= 0
|
||||||
|
_tackle_equip_button.refresh_ink_state()
|
||||||
|
|
||||||
|
|
||||||
func _toggle_active_bait() -> void:
|
func _toggle_active_bait() -> void:
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue