Refactor save management; update validation logic in save_manager.gd and enhance action handling in actions.gd for improved data integrity and user experience
This commit is contained in:
@@ -146,10 +146,10 @@ func validate_data(data: Variant) -> String:
|
||||
return "missing total_amount for " + id
|
||||
if not saved.get("binds") is Dictionary or not saved.get("batches") is Dictionary:
|
||||
return "state.binds and state.batches must be Dictionaries"
|
||||
for owner in saved.binds:
|
||||
if not saved.amount.has(owner) or not _number_map(saved.binds[owner], true):
|
||||
return "invalid state.binds." + owner
|
||||
for id in saved.binds[owner]:
|
||||
for owner_id in saved.binds:
|
||||
if not saved.amount.has(owner_id) or not _number_map(saved.binds[owner_id], true):
|
||||
return "invalid state.binds." + owner_id
|
||||
for id in saved.binds[owner_id]:
|
||||
if not saved.amount.has(id):
|
||||
return "unknown bound resource " + id
|
||||
for id in saved.batches:
|
||||
|
||||
+13
-7
@@ -1,14 +1,17 @@
|
||||
[runnable_presets]
|
||||
|
||||
"Windows Desktop"="Windows Desktop"
|
||||
|
||||
[preset.0]
|
||||
|
||||
name="Windows Desktop"
|
||||
platform="Windows Desktop"
|
||||
runnable=true
|
||||
dedicated_server=false
|
||||
custom_features=""
|
||||
export_filter="all_resources"
|
||||
include_filter=""
|
||||
exclude_filter=""
|
||||
export_path="../../Documents/Owad/Owniverse.I/Builds/owniverse.0.6.13-129.exe"
|
||||
export_path="../../Documents/Owad/Owniverse.I/Builds/owniverse.0.6.28-144.exe"
|
||||
patches=PackedStringArray()
|
||||
patch_delta_encoding=false
|
||||
patch_delta_compression_level_zstd=19
|
||||
@@ -74,7 +77,6 @@ Remove-Item -Recurse -Force '{temp_dir}'"
|
||||
|
||||
name="Android"
|
||||
platform="Android"
|
||||
runnable=false
|
||||
dedicated_server=false
|
||||
custom_features=""
|
||||
export_filter="all_resources"
|
||||
@@ -110,7 +112,7 @@ architectures/armeabi-v7a=false
|
||||
architectures/arm64-v8a=true
|
||||
architectures/x86=false
|
||||
architectures/x86_64=false
|
||||
version/code=143
|
||||
version/code=144
|
||||
version/name=""
|
||||
package/unique_name="com.KIDGameProduction.Owniverse"
|
||||
package/name="Owniverse"
|
||||
@@ -136,11 +138,12 @@ screen/support_normal=true
|
||||
screen/support_large=true
|
||||
screen/support_xlarge=true
|
||||
screen/background_color=Color(0, 0, 0, 1)
|
||||
splash_screen/disable_godot_boot_splash=false
|
||||
splash_screen/icon=""
|
||||
splash_screen/branding_image=""
|
||||
splash_screen/background_color=Color(0, 0, 0, 1)
|
||||
user_data_backup/allow=false
|
||||
command_line/extra_args=""
|
||||
apk_expansion/enable=false
|
||||
apk_expansion/SALT=""
|
||||
apk_expansion/public_key=""
|
||||
permissions/custom_permissions=PackedStringArray()
|
||||
permissions/access_checkin_properties=false
|
||||
permissions/access_coarse_location=false
|
||||
@@ -295,3 +298,6 @@ permissions/write_sms=false
|
||||
permissions/write_social_stream=false
|
||||
permissions/write_sync_settings=false
|
||||
permissions/write_user_dictionary=false
|
||||
apk_expansion/enable=false
|
||||
apk_expansion/SALT=""
|
||||
apk_expansion/public_key=""
|
||||
|
||||
+1
-1
@@ -11,7 +11,7 @@ config_version=5
|
||||
[application]
|
||||
|
||||
config/name="Owniverse"
|
||||
config/version="0.6.27"
|
||||
config/version="0.6.28"
|
||||
run/main_scene="uid://bv384dpmvjv8o"
|
||||
config/features=PackedStringArray("4.7", "Mobile")
|
||||
boot_splash/show_image=false
|
||||
|
||||
+10
-1
@@ -5,6 +5,7 @@ const OWNIVERSE_ITEM = preload("uid://d3xjfxwe3w16v")
|
||||
const SPEED_TEXTURE_NAME_TEMPLATE = "res://ui/topmenu/time&speed/time%s.png"
|
||||
|
||||
const PAGE_COUNT = 5
|
||||
const RESOURCE_PAGE = 1
|
||||
const PAGE_WIDTH = 1080
|
||||
const DRAG_TOLERANCE = 360
|
||||
const SWIPE_DURATION: float = 0.4
|
||||
@@ -256,6 +257,11 @@ func _set_active_page_actions() -> void:
|
||||
#var amount = owniverse.get_available_to_produce(entity)
|
||||
var amount = owniverse.calculate_max_producible(entity)
|
||||
#print(item_id, " : ", amount)
|
||||
if item_id == "SMBH":
|
||||
var preview: Callable = func(requested: float) -> float:
|
||||
return owniverse.calculate_max_producible(entity, requested)
|
||||
page_actions[pa].set_actions(item_id, amount, preview)
|
||||
else:
|
||||
page_actions[pa].set_actions(item_id, amount)
|
||||
|
||||
|
||||
@@ -329,7 +335,7 @@ func _change_speed(delta: int) -> void:
|
||||
g_time_plus.texture_normal = load(SPEED_TEXTURE_NAME_TEMPLATE % str(speed + 1))
|
||||
|
||||
|
||||
func _on_topmenu_button_pressed(ButtonName) -> void:
|
||||
func _on_topmenu_button_pressed(_ButtonName) -> void:
|
||||
print("Debug → ")
|
||||
#, owniverse.state)
|
||||
for id in owniverse.state.batches:
|
||||
@@ -359,6 +365,9 @@ func _on_unlock_and_save_timeout() -> void:
|
||||
func load_data() -> bool:
|
||||
var game_data: Dictionary = SaveManager.load_data("owniverse", "qwerty")
|
||||
if game_data.is_empty():
|
||||
if SaveManager.last_error == "":
|
||||
pending_page = RESOURCE_PAGE
|
||||
_restore_page.call_deferred()
|
||||
return false
|
||||
var restored: Dictionary = game_data.state
|
||||
# Current definitions are initialized first; reject incompatible saves before publishing.
|
||||
|
||||
+10
-4
@@ -23,9 +23,14 @@ var action_values: Dictionary = {
|
||||
}
|
||||
|
||||
|
||||
func set_actions(item_id: String, value: float) -> void:
|
||||
func set_actions(item_id: String, value: float, preview: Callable = Callable()) -> void:
|
||||
current_item_id = item_id
|
||||
_set_action_values(_get_distribution(value))
|
||||
var requests: Array = _get_distribution(value)
|
||||
var displayed: Array = requests.duplicate()
|
||||
if preview.is_valid():
|
||||
for i in range(displayed.size()):
|
||||
displayed[i] = preview.call(requests[i]) if requests[i] > 0.0 else 0.0
|
||||
_set_action_values(requests, displayed)
|
||||
|
||||
func _get_distribution(value: float) -> Array:
|
||||
if value <= 0.0 or not is_finite(value):
|
||||
@@ -38,10 +43,11 @@ func _get_distribution(value: float) -> Array:
|
||||
distribution.push_front(pow(10.0, ev))
|
||||
return distribution
|
||||
|
||||
func _set_action_values(values: Array) -> void:
|
||||
func _set_action_values(values: Array, displayed: Array) -> void:
|
||||
for ind in range(0, 5):
|
||||
action_values[ind] = values[ind]
|
||||
action_labels[ind].text = Global.format_number(values[ind]) if values[ind] > 0 else ""
|
||||
# Keep the original request for execution; show the exact planned output.
|
||||
action_labels[ind].text = Global.format_number(displayed[ind]) if displayed[ind] > 0 else ""
|
||||
|
||||
|
||||
func _on_action_button_pressed(button_id: int) -> void:
|
||||
|
||||
+120
-42
@@ -12,13 +12,8 @@ var items_to_update: Dictionary[String, float] = {}
|
||||
var auto_production: Array = []
|
||||
var locked_entities: Array = []
|
||||
|
||||
#var cumulative_amount: Dictionary[String, float] = {}
|
||||
#var current_amount: Dictionary[String, float] = {}
|
||||
|
||||
|
||||
# ------------------------------------
|
||||
|
||||
|
||||
var state: Dictionary = {
|
||||
'version': '0.1.0',
|
||||
'time': {
|
||||
@@ -226,7 +221,7 @@ func _credit(target: Dictionary, id: String, amount: float) -> float:
|
||||
target.production_remainders[id] = expected - amount
|
||||
target.amount[id] = target.amount.get(id, 0.0) + amount
|
||||
target.stats.total_amount[id] = target.stats.total_amount.get(id, 0.0) + amount
|
||||
if entities.has(id) and entities[id].group_as != "":
|
||||
if entities.has(id) and entities[id].group_as != "" and entities[id].group_as != id:
|
||||
var group: String = entities[id].group_as
|
||||
target.stats.total_amount[group] = target.stats.total_amount.get(group, 0.0) + amount
|
||||
return amount
|
||||
@@ -239,9 +234,9 @@ func _round_object_allocations(target: Dictionary) -> void:
|
||||
if not _is_discrete(id):
|
||||
continue
|
||||
var slots: Array = [ {"owner": "", "value": target.amount.get(id, 0.0)}]
|
||||
for owner in target.binds:
|
||||
if target.binds[owner].has(id):
|
||||
slots.append({"owner": owner, "value": target.binds[owner][id]})
|
||||
for owner_id in target.binds:
|
||||
if target.binds[owner_id].has(id):
|
||||
slots.append({"owner": owner_id, "value": target.binds[owner_id][id]})
|
||||
var assigned: float = 0.0
|
||||
for slot: Dictionary in slots:
|
||||
slot.whole = floorf(slot.value)
|
||||
@@ -426,21 +421,21 @@ func _evolve_batch_generation(target: Dictionary, to_year: float) -> bool:
|
||||
# Use a frozen binding table so simultaneous transitions cannot cascade by type order.
|
||||
var bindings: Dictionary = target.binds.duplicate(true)
|
||||
var incoming: Dictionary = {}
|
||||
for owner in bindings:
|
||||
var owner_survival: float = 1.0 - fractions.get(owner, 0.0)
|
||||
target.binds[owner] = {}
|
||||
for id in bindings[owner]:
|
||||
var held: float = bindings[owner][id]
|
||||
for owner_id in bindings:
|
||||
var owner_survival: float = 1.0 - fractions.get(owner_id, 0.0)
|
||||
target.binds[owner_id] = {}
|
||||
for id in bindings[owner_id]:
|
||||
var held: float = bindings[owner_id][id]
|
||||
var released: float = held * (1.0 - owner_survival)
|
||||
target.amount[id] = target.amount.get(id, 0.0) + released
|
||||
var fraction: float = fractions.get(id, 0.0)
|
||||
var remaining: float = held * owner_survival * (1.0 - fraction)
|
||||
target.binds[owner][id] = target.binds[owner].get(id, 0.0) + remaining
|
||||
target.binds[owner_id][id] = target.binds[owner_id].get(id, 0.0) + remaining
|
||||
for successor in outputs.get(id, {}):
|
||||
var transferred: float = held * owner_survival * fraction * maxf(0.0, outputs[id][successor])
|
||||
if not incoming.has(owner):
|
||||
incoming[owner] = {}
|
||||
incoming[owner][successor] = incoming[owner].get(successor, 0.0) + transferred
|
||||
if not incoming.has(owner_id):
|
||||
incoming[owner_id] = {}
|
||||
incoming[owner_id][successor] = incoming[owner_id].get(successor, 0.0) + transferred
|
||||
|
||||
for id in fractions:
|
||||
target.amount[id] = maxf(0.0, target.amount.get(id, 0.0) * (1.0 - fractions[id]))
|
||||
@@ -453,10 +448,10 @@ func _evolve_batch_generation(target: Dictionary, to_year: float) -> bool:
|
||||
amount = _credit(target, successor, amount)
|
||||
_append_batch(target, successor, amount, part.from, part.to)
|
||||
new_owners[successor] = new_owners.get(successor, 0.0) + amount
|
||||
for owner in incoming:
|
||||
for id in incoming[owner]:
|
||||
var transferred: float = minf(incoming[owner][id], target.amount.get(id, 0.0))
|
||||
target.binds[owner][id] = target.binds[owner].get(id, 0.0) + transferred
|
||||
for owner_id in incoming:
|
||||
for id in incoming[owner_id]:
|
||||
var transferred: float = minf(incoming[owner_id][id], target.amount.get(id, 0.0))
|
||||
target.binds[owner_id][id] = target.binds[owner_id].get(id, 0.0) + transferred
|
||||
target.amount[id] = maxf(0.0, target.amount.get(id, 0.0) - transferred)
|
||||
_bind_new_owners(target, new_owners)
|
||||
_round_object_allocations(target)
|
||||
@@ -467,15 +462,15 @@ func _evolve_batch_generation(target: Dictionary, to_year: float) -> bool:
|
||||
func _bind_full(target: Dictionary, amounts: Dictionary) -> bool:
|
||||
var trial: Dictionary = target.duplicate(true)
|
||||
var requests: Array = []
|
||||
for owner in amounts:
|
||||
if not entities.has(owner):
|
||||
for owner_id in amounts:
|
||||
if not entities.has(owner_id):
|
||||
return false
|
||||
for id in entities[owner].bind:
|
||||
var quantity: float = amounts[owner] * maxf(0.0, entities[owner].bind[id])
|
||||
for id in entities[owner_id].bind:
|
||||
var quantity: float = amounts[owner_id] * maxf(0.0, entities[owner_id].bind[id])
|
||||
if not is_equal_approx(quantity, roundf(quantity)):
|
||||
return false
|
||||
if quantity > 0.0:
|
||||
requests.append({"owner": owner, "id": id, "amount": roundf(quantity), "members": _members(id).size()})
|
||||
requests.append({"owner": owner_id, "id": id, "amount": roundf(quantity), "members": _members(id).size()})
|
||||
requests.sort_custom(func(a: Dictionary, b: Dictionary) -> bool:
|
||||
if a.members != b.members:
|
||||
return a.members < b.members
|
||||
@@ -532,11 +527,11 @@ func _bind_new_owners(target: Dictionary, amounts: Dictionary, require_full: boo
|
||||
if require_full:
|
||||
return _bind_full(target, amounts)
|
||||
var requests: Dictionary = {}
|
||||
for owner in amounts:
|
||||
if not entities.has(owner):
|
||||
for owner_id in amounts:
|
||||
if not entities.has(owner_id):
|
||||
continue
|
||||
for id in entities[owner].bind:
|
||||
var quantity: float = amounts[owner] * maxf(0.0, entities[owner].bind[id])
|
||||
for id in entities[owner_id].bind:
|
||||
var quantity: float = amounts[owner_id] * maxf(0.0, entities[owner_id].bind[id])
|
||||
var weights: Dictionary = {}
|
||||
for member in _members(id):
|
||||
weights[member] = target.amount.get(member, 0.0)
|
||||
@@ -544,18 +539,18 @@ func _bind_new_owners(target: Dictionary, amounts: Dictionary, require_full: boo
|
||||
for member in weights:
|
||||
if not requests.has(member):
|
||||
requests[member] = {}
|
||||
requests[member][owner] = requests[member].get(owner, 0.0) + quantity * weights[member]
|
||||
requests[member][owner_id] = requests[member].get(owner_id, 0.0) + quantity * weights[member]
|
||||
for id in requests:
|
||||
var requested: float = 0.0
|
||||
for quantity in requests[id].values():
|
||||
requested += quantity
|
||||
var available: float = _available(target, id)
|
||||
var factor: float = minf(1.0, available / requested) if requested > 0.0 else 0.0
|
||||
for owner in requests[id]:
|
||||
var quantity: float = requests[id][owner] * factor
|
||||
if not target.binds.has(owner):
|
||||
target.binds[owner] = {}
|
||||
target.binds[owner][id] = target.binds[owner].get(id, 0.0) + quantity
|
||||
for owner_id in requests[id]:
|
||||
var quantity: float = requests[id][owner_id] * factor
|
||||
if not target.binds.has(owner_id):
|
||||
target.binds[owner_id] = {}
|
||||
target.binds[owner_id][id] = target.binds[owner_id].get(id, 0.0) + quantity
|
||||
target.amount[id] = maxf(0.0, target.amount.get(id, 0.0) - quantity)
|
||||
|
||||
return true
|
||||
@@ -774,7 +769,90 @@ func _auto_capacity(source: Dictionary, entity: OwniverseEntity, budget: Diction
|
||||
## @param item_id - entity id to produce
|
||||
## @param desired_amount - desired quantity to produce
|
||||
## @return actual amount produced based on available resources
|
||||
func _smbh_sources() -> Dictionary:
|
||||
var masses: Dictionary = {}
|
||||
for id in entities:
|
||||
var entity: OwniverseEntity = entities[id]
|
||||
if entity.count_as == "BH" and entity.mass > 0.0:
|
||||
masses[id] = floorf(maxf(0.0, state.amount.get(id, 0.0))) * entity.mass
|
||||
return masses
|
||||
|
||||
|
||||
func _smbh_available() -> float:
|
||||
var mass: float = 0.0
|
||||
for value in _smbh_sources().values():
|
||||
mass += value
|
||||
return floorf(mass)
|
||||
|
||||
|
||||
func _plan_smbh(desired_amount: float) -> Dictionary:
|
||||
var empty: Dictionary = {"amount": 0.0, "taken": {}}
|
||||
if desired_amount <= 0.0 or not is_finite(desired_amount):
|
||||
return empty
|
||||
var total_mass: float = _smbh_available()
|
||||
if total_mass <= 0.0:
|
||||
return empty
|
||||
var requested: float = minf(floorf(desired_amount), total_mass)
|
||||
if requested <= 0.0:
|
||||
return empty
|
||||
var masses: Dictionary = _smbh_sources()
|
||||
var distribution: Dictionary = _normalize_distribution(masses)
|
||||
var taken: Dictionary = {}
|
||||
var order: Array = []
|
||||
var consumed_mass: float = 0.0
|
||||
for id in distribution:
|
||||
var expected: float = requested * distribution[id] / entities[id].mass
|
||||
taken[id] = minf(floorf(expected), floorf(state.amount[id]))
|
||||
consumed_mass += taken[id] * entities[id].mass
|
||||
order.append({"id": id, "remainder": expected - taken[id]})
|
||||
order.sort_custom(func(a: Dictionary, b: Dictionary) -> bool:
|
||||
return a.remainder > b.remainder if a.remainder != b.remainder else a.id < b.id)
|
||||
# Consume whole BH only; all excess mass becomes SMBH, never disappears.
|
||||
for candidate: Dictionary in order:
|
||||
if consumed_mass >= requested:
|
||||
break
|
||||
var id: String = candidate.id
|
||||
if taken[id] < floorf(state.amount[id]):
|
||||
taken[id] += 1.0
|
||||
consumed_mass += entities[id].mass
|
||||
if consumed_mass <= 0.0 or consumed_mass != floorf(consumed_mass):
|
||||
return empty
|
||||
return {"amount": consumed_mass, "taken": taken}
|
||||
|
||||
|
||||
func _produce_smbh(desired_amount: float) -> float:
|
||||
var plan: Dictionary = _plan_smbh(desired_amount)
|
||||
var consumed_mass: float = plan.amount
|
||||
var taken: Dictionary = plan.taken
|
||||
if consumed_mass <= 0.0:
|
||||
return 0.0
|
||||
var next: Dictionary = state.duplicate(true)
|
||||
var physical: Dictionary = _physical_amounts(state)
|
||||
for id in taken:
|
||||
if taken[id] <= 0.0:
|
||||
continue
|
||||
next.amount[id] -= taken[id]
|
||||
var fraction: float = taken[id] / physical[id]
|
||||
_scale_batches(next, id, 1.0 - fraction)
|
||||
# Release the consumed owners' share of any resources they held.
|
||||
for resource in next.binds.get(id, {}):
|
||||
var released: float = next.binds[id][resource] * fraction
|
||||
next.binds[id][resource] -= released
|
||||
next.amount[resource] = next.amount.get(resource, 0.0) + released
|
||||
_credit(next, "SMBH", consumed_mass)
|
||||
_append_batch(next, "SMBH", consumed_mass, state.time.datetime, state.time.datetime)
|
||||
_round_object_allocations(next)
|
||||
_refresh_groups(next)
|
||||
_clamp_auto_resources(next)
|
||||
state = next
|
||||
new_state = next
|
||||
refresh_item_amounts()
|
||||
return consumed_mass
|
||||
|
||||
|
||||
func produce(item_id: String, desired_amount: float) -> float:
|
||||
if item_id == "SMBH":
|
||||
return _produce_smbh(desired_amount)
|
||||
# Use special production for S and H
|
||||
if item_id == "S" or item_id == "H":
|
||||
return produce_hs(item_id, desired_amount)
|
||||
@@ -852,6 +930,8 @@ func _whole_gcd(a: float, b: float) -> float:
|
||||
|
||||
|
||||
func calculate_max_producible(entity: OwniverseEntity, desired_amount: float = 1.0E150) -> float:
|
||||
if entity.id == "SMBH":
|
||||
return _plan_smbh(desired_amount).amount
|
||||
var requirements: Dictionary = {}
|
||||
for resource in entity.cost:
|
||||
requirements[resource] = maxf(0.0, entity.cost[resource])
|
||||
@@ -930,9 +1010,9 @@ func consume_from_batches(entity: OwniverseEntity, current_year: float) -> float
|
||||
total += part.amount
|
||||
var fraction: float = clampf(total / physical[entity.id], 0.0, 1.0) if physical.get(entity.id, 0.0) > 0.0 else 0.0
|
||||
state.amount[entity.id] = state.amount.get(entity.id, 0.0) * (1.0 - fraction)
|
||||
for owner in state.binds:
|
||||
if state.binds[owner].has(entity.id):
|
||||
state.binds[owner][entity.id] *= 1.0 - fraction
|
||||
for owner_id in state.binds:
|
||||
if state.binds[owner_id].has(entity.id):
|
||||
state.binds[owner_id][entity.id] *= 1.0 - fraction
|
||||
_round_object_allocations(state)
|
||||
_refresh_groups(state)
|
||||
refresh_item_amounts()
|
||||
@@ -999,8 +1079,6 @@ func _process_enitity(entity_id: String, amount: float) -> void:
|
||||
refresh_item_amounts()
|
||||
|
||||
|
||||
|
||||
|
||||
#endregion
|
||||
|
||||
#region Time
|
||||
|
||||
Reference in New Issue
Block a user