173 lines
4.3 KiB
GDScript
173 lines
4.3 KiB
GDScript
class_name FishCatch
|
|
extends Resource
|
|
|
|
const FishDataType = preload("res://fish/fish_data.gd")
|
|
|
|
const MAX_SAFE_WEIGHT_LB: float = 1000000.0
|
|
const MAX_SAFE_DISPLAY_SCALE: float = 10000.0
|
|
const MAX_SAFE_SALE_VALUE: int = 1000000000
|
|
const MAX_SAFE_SEQUENCE: int = 2147483647
|
|
|
|
@export var fish: FishDataType
|
|
@export var fish_id: StringName
|
|
@export var catch_id: StringName
|
|
@export var catch_sequence: int = 0
|
|
@export var weight_lb: float = 0.0
|
|
@export var display_scale: float = 1.0
|
|
@export var sale_value: int = 0
|
|
@export var is_favorited: bool = false
|
|
|
|
|
|
func ensure_identity() -> void:
|
|
if not catch_id.is_empty():
|
|
return
|
|
var random_bytes: PackedByteArray = Crypto.new().generate_random_bytes(16)
|
|
var identity_suffix: String = random_bytes.hex_encode()
|
|
if identity_suffix.is_empty():
|
|
identity_suffix = "%d:%d:%d" % [
|
|
int(Time.get_unix_time_from_system()),
|
|
Time.get_ticks_usec(),
|
|
get_instance_id(),
|
|
]
|
|
catch_id = StringName(
|
|
"%s:%s" % [fish_id, identity_suffix]
|
|
)
|
|
|
|
|
|
func is_valid() -> bool:
|
|
return (
|
|
fish != null
|
|
and not fish_id.is_empty()
|
|
and not catch_id.is_empty()
|
|
and fish.id == fish_id
|
|
and catch_sequence >= 0
|
|
and catch_sequence < MAX_SAFE_SEQUENCE
|
|
and is_finite(weight_lb)
|
|
and weight_lb > 0.0
|
|
and weight_lb <= MAX_SAFE_WEIGHT_LB
|
|
and is_finite(display_scale)
|
|
and display_scale > 0.0
|
|
and display_scale <= MAX_SAFE_DISPLAY_SCALE
|
|
and sale_value >= 0
|
|
and sale_value <= MAX_SAFE_SALE_VALUE
|
|
)
|
|
|
|
|
|
func to_save_dict() -> Dictionary:
|
|
return {
|
|
"catch_id": String(catch_id),
|
|
"catch_sequence": catch_sequence,
|
|
"fish_id": String(fish_id),
|
|
"weight_lb": weight_lb,
|
|
"display_scale": display_scale,
|
|
"sale_value": sale_value,
|
|
"is_favorited": is_favorited,
|
|
}
|
|
|
|
|
|
static func from_save_dict(
|
|
data: Dictionary,
|
|
resolved_fish: FishDataType,
|
|
) -> FishCatch:
|
|
if resolved_fish == null:
|
|
return null
|
|
for required_key: String in [
|
|
"catch_id",
|
|
"catch_sequence",
|
|
"fish_id",
|
|
"weight_lb",
|
|
"display_scale",
|
|
"sale_value",
|
|
"is_favorited",
|
|
]:
|
|
if not data.has(required_key):
|
|
return null
|
|
if (
|
|
typeof(data["catch_id"]) not in [TYPE_STRING, TYPE_STRING_NAME]
|
|
or typeof(data["fish_id"]) not in [TYPE_STRING, TYPE_STRING_NAME]
|
|
):
|
|
return null
|
|
|
|
var loaded_catch_id: StringName = StringName(str(data["catch_id"]))
|
|
var loaded_fish_id: StringName = StringName(str(data["fish_id"]))
|
|
var loaded_sequence: int = _read_safe_integer(
|
|
data["catch_sequence"],
|
|
-1,
|
|
MAX_SAFE_SEQUENCE
|
|
)
|
|
var loaded_sale_value: int = _read_safe_integer(
|
|
data["sale_value"],
|
|
-1,
|
|
MAX_SAFE_SALE_VALUE
|
|
)
|
|
var loaded_weight: float = _read_safe_float(
|
|
data["weight_lb"],
|
|
0.0,
|
|
MAX_SAFE_WEIGHT_LB
|
|
)
|
|
var loaded_scale: float = _read_safe_float(
|
|
data["display_scale"],
|
|
0.0,
|
|
MAX_SAFE_DISPLAY_SCALE
|
|
)
|
|
var loaded_favorite: Variant = data["is_favorited"]
|
|
if (
|
|
loaded_catch_id.is_empty()
|
|
or loaded_fish_id != resolved_fish.id
|
|
or loaded_sequence <= 0
|
|
or loaded_sequence >= MAX_SAFE_SEQUENCE
|
|
or loaded_sale_value < 0
|
|
or loaded_weight <= 0.0
|
|
or loaded_scale <= 0.0
|
|
or typeof(loaded_favorite) != TYPE_BOOL
|
|
):
|
|
return null
|
|
|
|
var fish_catch := FishCatch.new()
|
|
fish_catch.fish = resolved_fish
|
|
fish_catch.fish_id = loaded_fish_id
|
|
fish_catch.catch_id = loaded_catch_id
|
|
fish_catch.catch_sequence = loaded_sequence
|
|
fish_catch.weight_lb = loaded_weight
|
|
fish_catch.display_scale = loaded_scale
|
|
fish_catch.sale_value = loaded_sale_value
|
|
fish_catch.is_favorited = bool(loaded_favorite)
|
|
return fish_catch if fish_catch.is_valid() else null
|
|
|
|
|
|
static func _read_safe_integer(
|
|
value: Variant,
|
|
invalid_value: int,
|
|
maximum_value: int,
|
|
) -> int:
|
|
if typeof(value) == TYPE_INT:
|
|
var integer_value: int = int(value)
|
|
if integer_value >= 0 and integer_value <= maximum_value:
|
|
return integer_value
|
|
elif typeof(value) == TYPE_FLOAT:
|
|
var float_value: float = float(value)
|
|
if (
|
|
is_finite(float_value)
|
|
and float_value >= 0.0
|
|
and float_value <= float(maximum_value)
|
|
and is_equal_approx(float_value, round(float_value))
|
|
):
|
|
return int(round(float_value))
|
|
return invalid_value
|
|
|
|
|
|
static func _read_safe_float(
|
|
value: Variant,
|
|
invalid_value: float,
|
|
maximum_value: float,
|
|
) -> float:
|
|
if typeof(value) not in [TYPE_INT, TYPE_FLOAT]:
|
|
return invalid_value
|
|
var float_value: float = float(value)
|
|
if (
|
|
not is_finite(float_value)
|
|
or float_value <= 0.0
|
|
or float_value > maximum_value
|
|
):
|
|
return invalid_value
|
|
return float_value
|