Compare commits
53
Commits
285528b7a3
...
02d047c72a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
02d047c72a | ||
|
|
e58c5f5e17 | ||
|
|
2a53dbb596 | ||
|
|
381083dcaf | ||
|
|
8b6e515dbf | ||
|
|
21fce14419 | ||
|
|
6a709be988 | ||
|
|
ce36e98dde | ||
|
|
083dbe8d02 | ||
|
|
6f91d35667 | ||
|
|
58a3534bf9 | ||
|
|
0cbe2641ff | ||
|
|
f710119423 | ||
|
|
c8b730e6a2 | ||
|
|
8291273392 | ||
|
|
c2a736b46e | ||
|
|
8e0d8ef32a | ||
|
|
8b4e6b99aa | ||
|
|
aae045d3c6 | ||
|
|
17390c9b0d | ||
|
|
6a80649a7f | ||
|
|
f69cb05c07 | ||
|
|
603354f89a | ||
|
|
21d89b988d | ||
|
|
6b40805e64 | ||
|
|
529047836f | ||
|
|
fd4b48b94b | ||
|
|
32be96ac76 | ||
|
|
c67facdf48 | ||
|
|
8d55771647 | ||
|
|
5055cc990b | ||
|
|
1c9ec77812 | ||
|
|
c34a7d3c6b | ||
|
|
d7b6608d0a | ||
|
|
5516997fe1 | ||
|
|
ebf0fbe04b | ||
|
|
999a8b5e97 | ||
|
|
dc2ccccb36 | ||
|
|
89392846b7 | ||
|
|
e2d9144cca | ||
|
|
4013249135 | ||
|
|
3fe0b20207 | ||
|
|
f7189923dd | ||
|
|
b9866afa02 | ||
|
|
19daef97d3 | ||
|
|
002400a080 | ||
|
|
fefe7d847f | ||
|
|
b1253fb066 | ||
|
|
a172288223 | ||
|
|
2be3b9cab9 | ||
|
|
d853b91b0b | ||
|
|
b84bade045 | ||
|
|
39f6068461 |
@@ -40,6 +40,7 @@ test-s1:
|
||||
$(GODOT) --headless --path . --script res://tests/s1/test_original_facility_dimensions.gd
|
||||
$(GODOT) --headless --path . --script res://tests/s1/test_world_service.gd
|
||||
$(GODOT) --headless --path . --script res://tests/s1/test_path_service.gd
|
||||
$(GODOT) --headless --path . --script res://tests/s1/test_path_snapshot_transit_guard.gd
|
||||
$(GODOT) --headless --path . --script res://tests/s1/test_path_congestion.gd
|
||||
$(GODOT) --headless --path . --script res://tests/s1/test_construction_service.gd
|
||||
$(GODOT) --headless --path . --script res://tests/s1/test_excavation_service.gd
|
||||
@@ -86,6 +87,9 @@ test-s5:
|
||||
|
||||
test-s6:
|
||||
$(GODOT) --headless --path . --script res://tests/s6/test_save_operations.gd
|
||||
$(GODOT) --headless --path . --script res://tests/s6/test_save_preflight.gd
|
||||
$(GODOT) --headless --path . --script res://tests/s6/test_save_recovery_preservation.gd
|
||||
$(GODOT) --headless --path . --script res://tests/s6/test_runtime_snapshot_serialization.gd
|
||||
$(GODOT) --headless --path . --script res://tests/s6/test_performance_budget.gd
|
||||
$(GODOT) --headless --path . --script res://tests/s6/test_runtime_save_lifecycle.gd
|
||||
|
||||
@@ -131,6 +135,7 @@ test-ui:
|
||||
$(GODOT) --headless --path . --script res://tests/ui/test_main_dashboard.gd
|
||||
$(GODOT) --headless --path . --script res://tests/ui/test_staff_management.gd
|
||||
$(GODOT) --headless --path . --script res://tests/ui/test_supply_management.gd
|
||||
$(GODOT) --headless --path . --script res://tests/ui/test_supply_auto_purchase_ui.gd
|
||||
$(GODOT) --headless --path . --script res://tests/ui/test_business_management.gd
|
||||
$(GODOT) --headless --path . --script res://tests/ui/test_elevator_management.gd
|
||||
$(MAKE) test-opening
|
||||
@@ -183,6 +188,10 @@ test-supply:
|
||||
$(GODOT) --headless --path . --script res://tests/supply/test_supply_save_migration.gd
|
||||
$(GODOT) --headless --path . --script res://tests/supply/test_supply_logistics_bridge.gd
|
||||
$(GODOT) --headless --path . --script res://tests/supply/test_supply_management_service.gd
|
||||
$(GODOT) --headless --path . --script res://tests/supply/test_supply_auto_purchase.gd
|
||||
$(GODOT) --headless --path . --script res://tests/supply/test_normal_auto_purchase_chain.gd
|
||||
$(GODOT) --headless --path . --script res://tests/supply/test_supply_auto_purchase_boundaries.gd
|
||||
$(GODOT) --headless --path . --script res://tests/supply/test_supply_auto_purchase_destruction.gd
|
||||
$(GODOT) --headless --path . --script res://tests/supply/test_normal_supply_chain.gd
|
||||
|
||||
capture-supply:
|
||||
|
||||
Binary file not shown.
@@ -0,0 +1,24 @@
|
||||
[remap]
|
||||
|
||||
importer="wav"
|
||||
type="AudioStreamWAV"
|
||||
uid="uid://qpe5mfy3o1cw"
|
||||
path="res://.godot/imported/tree_theme_back_candidate.wav-1af8eac19cfe5ccb9a16a9a74b171ffd.sample"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/audio/bgm/tree_theme_back_candidate.wav"
|
||||
dest_files=["res://.godot/imported/tree_theme_back_candidate.wav-1af8eac19cfe5ccb9a16a9a74b171ffd.sample"]
|
||||
|
||||
[params]
|
||||
|
||||
force/8_bit=false
|
||||
force/mono=false
|
||||
force/max_rate=false
|
||||
force/max_rate_hz=44100
|
||||
edit/trim=false
|
||||
edit/normalize=false
|
||||
edit/loop_mode=0
|
||||
edit/loop_begin=0
|
||||
edit/loop_end=-1
|
||||
compress/mode=0
|
||||
Binary file not shown.
@@ -0,0 +1,24 @@
|
||||
[remap]
|
||||
|
||||
importer="wav"
|
||||
type="AudioStreamWAV"
|
||||
uid="uid://dr4atdrw3iv1o"
|
||||
path="res://.godot/imported/tree_theme_front_candidate.wav-dd40d47f468b616e1eb3517c0056cc0c.sample"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/audio/bgm/tree_theme_front_candidate.wav"
|
||||
dest_files=["res://.godot/imported/tree_theme_front_candidate.wav-dd40d47f468b616e1eb3517c0056cc0c.sample"]
|
||||
|
||||
[params]
|
||||
|
||||
force/8_bit=false
|
||||
force/mono=false
|
||||
force/max_rate=false
|
||||
force/max_rate_hz=44100
|
||||
edit/trim=false
|
||||
edit/normalize=false
|
||||
edit/loop_mode=0
|
||||
edit/loop_begin=0
|
||||
edit/loop_end=-1
|
||||
compress/mode=0
|
||||
@@ -0,0 +1,22 @@
|
||||
|
||||
|
||||
Interface Sounds (1.0)
|
||||
|
||||
Created/distributed by Kenney (www.kenney.nl)
|
||||
Creation date: 11-02-2020
|
||||
|
||||
------------------------------
|
||||
|
||||
License: (Creative Commons Zero, CC0)
|
||||
http://creativecommons.org/publicdomain/zero/1.0/
|
||||
|
||||
This content is free to use in personal, educational and commercial projects.␍
|
||||
Support us by crediting Kenney or www.kenney.nl (this is not mandatory)
|
||||
|
||||
------------------------------
|
||||
|
||||
Donate: http://support.kenney.nl
|
||||
Patreon: http://patreon.com/kenney/
|
||||
|
||||
Follow on Twitter for updates:
|
||||
http://twitter.com/KenneyNL
|
||||
Binary file not shown.
@@ -0,0 +1,19 @@
|
||||
[remap]
|
||||
|
||||
importer="oggvorbisstr"
|
||||
type="AudioStreamOggVorbis"
|
||||
uid="uid://cg3ltmy3l1tut"
|
||||
path="res://.godot/imported/bong_001.ogg-036c448bb5d6a2a3d929d1a393030267.oggvorbisstr"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/audio/ui/bong_001.ogg"
|
||||
dest_files=["res://.godot/imported/bong_001.ogg-036c448bb5d6a2a3d929d1a393030267.oggvorbisstr"]
|
||||
|
||||
[params]
|
||||
|
||||
loop=false
|
||||
loop_offset=0
|
||||
bpm=0
|
||||
beat_count=0
|
||||
bar_beats=4
|
||||
Binary file not shown.
@@ -0,0 +1,19 @@
|
||||
[remap]
|
||||
|
||||
importer="oggvorbisstr"
|
||||
type="AudioStreamOggVorbis"
|
||||
uid="uid://b6gxkug03pmia"
|
||||
path="res://.godot/imported/click_001.ogg-faef30153c7c6ab499289b7b03f0bade.oggvorbisstr"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/audio/ui/click_001.ogg"
|
||||
dest_files=["res://.godot/imported/click_001.ogg-faef30153c7c6ab499289b7b03f0bade.oggvorbisstr"]
|
||||
|
||||
[params]
|
||||
|
||||
loop=false
|
||||
loop_offset=0
|
||||
bpm=0
|
||||
beat_count=0
|
||||
bar_beats=4
|
||||
Binary file not shown.
@@ -0,0 +1,19 @@
|
||||
[remap]
|
||||
|
||||
importer="oggvorbisstr"
|
||||
type="AudioStreamOggVorbis"
|
||||
uid="uid://csfga2avhq4ul"
|
||||
path="res://.godot/imported/confirmation_001.ogg-3e5a493cd453540435a25930de468964.oggvorbisstr"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/audio/ui/confirmation_001.ogg"
|
||||
dest_files=["res://.godot/imported/confirmation_001.ogg-3e5a493cd453540435a25930de468964.oggvorbisstr"]
|
||||
|
||||
[params]
|
||||
|
||||
loop=false
|
||||
loop_offset=0
|
||||
bpm=0
|
||||
beat_count=0
|
||||
bar_beats=4
|
||||
Binary file not shown.
@@ -0,0 +1,19 @@
|
||||
[remap]
|
||||
|
||||
importer="oggvorbisstr"
|
||||
type="AudioStreamOggVorbis"
|
||||
uid="uid://cwdh8pkwvyuf2"
|
||||
path="res://.godot/imported/error_008.ogg-41fd046ecd9f266bdea4813897127346.oggvorbisstr"
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/audio/ui/error_008.ogg"
|
||||
dest_files=["res://.godot/imported/error_008.ogg-41fd046ecd9f266bdea4813897127346.oggvorbisstr"]
|
||||
|
||||
[params]
|
||||
|
||||
loop=false
|
||||
loop_offset=0
|
||||
bpm=0
|
||||
beat_count=0
|
||||
bar_beats=4
|
||||
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"official_page": "https://kenney.nl/assets/interface-sounds",
|
||||
"official_archive_sha256": "f2193d072726d6758a5f7871b2dcc54dcce0d5c35c6f0a62f92549b327c81232",
|
||||
"license": {
|
||||
"name": "Creative Commons Zero, CC0",
|
||||
"official_url": "https://creativecommons.org/publicdomain/zero/1.0/",
|
||||
"path": "assets/audio/ui/License.txt",
|
||||
"sha256": "f7966c773bbed0eca6a9c75081c44a178b38eae112724dbb5fdfbd4192d118a9",
|
||||
"original_byte_preserved": true
|
||||
},
|
||||
"assets": [
|
||||
{
|
||||
"role": "click",
|
||||
"path": "assets/audio/ui/click_001.ogg",
|
||||
"sha256": "ccfb7fa0cccdd9faec0eb16033c732b1e308d139d80f799161495d58f7adcdb9",
|
||||
"bytes": 4876
|
||||
},
|
||||
{
|
||||
"role": "success",
|
||||
"path": "assets/audio/ui/confirmation_001.ogg",
|
||||
"sha256": "063564703b6094d70718a3e787a55cc9141611e4ecd6b6637f8828f79b4a8c3a",
|
||||
"bytes": 8968
|
||||
},
|
||||
{
|
||||
"role": "refusal",
|
||||
"path": "assets/audio/ui/error_008.ogg",
|
||||
"sha256": "eba17ecb2a426bfd4a8a6acff5f8a86202b6424a77af0fd842e37809a1ab6d81",
|
||||
"bytes": 9215
|
||||
},
|
||||
{
|
||||
"role": "notification",
|
||||
"path": "assets/audio/ui/bong_001.ogg",
|
||||
"sha256": "d21d0f0b782445db579d11e2506b24cd1ac9d664ee33aeaf807761aa7b6fd710",
|
||||
"bytes": 4848
|
||||
}
|
||||
],
|
||||
"usage": "UI click, committed action success/refusal, new foreground notification"
|
||||
}
|
||||
+335
-32
@@ -19,7 +19,12 @@ const CONSTRUCTION_EXPENSE_ACCOUNT := &"CONSTRUCTION_EXPENSE"
|
||||
const OPERATING_EXPENSE_ACCOUNT := &"OPERATING_EXPENSE"
|
||||
const AUTHORITY_FINE_ACCOUNT := &"AUTHORITY_FINE_EXPENSE"
|
||||
const WAGE_EXPENSE_ACCOUNT := &"WAGE_EXPENSE"
|
||||
const RUNTIME_SNAPSHOT_VERSION := 13
|
||||
const RUNTIME_SNAPSHOT_VERSION := 14
|
||||
const AUTO_PURCHASE_RUNTIME_SNAPSHOT_VERSION := 14
|
||||
const EVENT_THREAT_RUNTIME_SNAPSHOT_VERSION := 13
|
||||
const MIMI_PURCHASE_RUNTIME_SNAPSHOT_VERSION := 15
|
||||
const PurchaseQuoteV2 := preload("res://scripts/supply/supply_purchase_quote_v2.gd")
|
||||
const AutoPurchasePlansType := preload("res://scripts/supply/supply_auto_purchase_quote_plans.gd")
|
||||
const CLEANING_RUNTIME_SNAPSHOT_VERSION := 12
|
||||
const WEATHER_RUNTIME_SNAPSHOT_VERSION := 11
|
||||
const FacilityCleanliness = preload("res://scripts/environment/facility_cleanliness_service.gd")
|
||||
@@ -110,6 +115,7 @@ var logistics_dispatcher := LogisticsDispatcher.new()
|
||||
var logistics_requests: Dictionary = {}
|
||||
var kitchen_production := KitchenProductionService.new()
|
||||
var supply_management := SupplyManagementService.new()
|
||||
var auto_purchase_plans := AutoPurchasePlansType.new()
|
||||
var supply_logistics := SupplyLogisticsBridge.new()
|
||||
var elevators := ElevatorService.new()
|
||||
var legacy_stairs := LegacyStairService.new()
|
||||
@@ -288,6 +294,7 @@ func initialize(clock: SimulationKernel, state: Variant, seed := DEFAULT_WORLD_S
|
||||
_executors.clear()
|
||||
register_command_executor(&"change_resources", Callable(self, "_execute_change_resources"))
|
||||
register_command_executor(&"spend_resources", Callable(self, "_execute_spend_resources"))
|
||||
register_command_executor(&"repay_loan_principal", Callable(self, "_execute_repay_loan_principal"))
|
||||
register_command_executor(&"build_facility", Callable(self, "_execute_build_facility"))
|
||||
register_command_executor(&"cancel_construction", Callable(self, "_execute_cancel_construction"))
|
||||
register_command_executor(&"demolish_facility", Callable(self, "_execute_demolish_facility"))
|
||||
@@ -300,6 +307,7 @@ func initialize(clock: SimulationKernel, state: Variant, seed := DEFAULT_WORLD_S
|
||||
register_command_executor(&"complete_progression_quest", Callable(self, "_execute_complete_progression_quest"))
|
||||
register_command_executor(&"acknowledge_operations_pause", Callable(self, "_execute_acknowledge_operations_pause"))
|
||||
register_command_executor(&"purchase_supplies", Callable(self, "_execute_purchase_supplies"))
|
||||
register_command_executor(&"set_supply_auto_purchase", Callable(self, "_execute_set_supply_auto_purchase"))
|
||||
register_command_executor(&"set_kitchen_production", Callable(self, "_execute_set_kitchen_production"))
|
||||
register_command_executor(&"add_inventory_stack", Callable(self, "_execute_add_inventory_stack"))
|
||||
register_command_executor(&"create_logistics_request", Callable(self, "_execute_create_logistics_request"))
|
||||
@@ -502,9 +510,7 @@ func get_rng_stream(stream_id: StringName) -> DeterministicRng:
|
||||
|
||||
func create_snapshot() -> Dictionary:
|
||||
var rng_data := {}
|
||||
var stream_ids: Array = _rng_streams.keys()
|
||||
stream_ids.sort()
|
||||
for stream_id: Variant in stream_ids:
|
||||
for stream_id: StringName in _sorted_name_keys(_rng_streams):
|
||||
var stream: DeterministicRng = _rng_streams[stream_id]
|
||||
rng_data[String(stream_id)] = stream.snapshot()
|
||||
var owned_systems := {
|
||||
@@ -531,6 +537,7 @@ func create_snapshot() -> Dictionary:
|
||||
&"upkeep_settlement": upkeep_settlement.create_snapshot(),
|
||||
&"inventory": inventory_service.create_snapshot(),
|
||||
&"kitchen_production": kitchen_production.create_snapshot(),
|
||||
&"auto_purchase_plans": auto_purchase_plans.create_snapshot(),
|
||||
&"elevators": elevators.create_snapshot(),
|
||||
&"legacy_stairs": legacy_stairs.create_snapshot(),
|
||||
&"logistics_dispatcher": logistics_dispatcher.create_snapshot(),
|
||||
@@ -555,6 +562,8 @@ func create_snapshot() -> Dictionary:
|
||||
&"facility_infrastructure": facility_infrastructure_projector.create_snapshot(),
|
||||
},
|
||||
}
|
||||
if PurchaseQuoteV2.has_history(owned_systems[&"runtime"]):
|
||||
owned_systems[&"runtime"][&"runtime_schema_version"] = MIMI_PURCHASE_RUNTIME_SNAPSHOT_VERSION
|
||||
# Every component above returns detached containers. Transfer this fresh
|
||||
# tree into the envelope instead of deep-copying all component snapshots a
|
||||
# second time. Keep generic capture's caller-owned-extra contract unchanged.
|
||||
@@ -610,9 +619,14 @@ func _restore_snapshot_internal(snapshot: Dictionary, recovery_mode: bool) -> bo
|
||||
UPKEEP_RUNTIME_SNAPSHOT_VERSION,
|
||||
WEATHER_RUNTIME_SNAPSHOT_VERSION,
|
||||
CLEANING_RUNTIME_SNAPSHOT_VERSION,
|
||||
EVENT_THREAT_RUNTIME_SNAPSHOT_VERSION,
|
||||
RUNTIME_SNAPSHOT_VERSION,
|
||||
MIMI_PURCHASE_RUNTIME_SNAPSHOT_VERSION,
|
||||
]:
|
||||
return false
|
||||
var has_purchase_v2_history := PurchaseQuoteV2.has_history(runtime_data)
|
||||
if (runtime_schema_version == MIMI_PURCHASE_RUNTIME_SNAPSHOT_VERSION) != has_purchase_v2_history:
|
||||
return false
|
||||
if runtime_schema_version < OPERATIONS_RUNTIME_SNAPSHOT_VERSION \
|
||||
and not _legacy_operations_history_is_absent(runtime_data):
|
||||
return false
|
||||
@@ -649,6 +663,8 @@ func _restore_snapshot_internal(snapshot: Dictionary, recovery_mode: bool) -> bo
|
||||
runtime_current_keys.append(&"weather")
|
||||
if runtime_schema_version >= CLEANING_RUNTIME_SNAPSHOT_VERSION:
|
||||
runtime_current_keys.append(&"facility_cleanliness")
|
||||
if runtime_schema_version >= AUTO_PURCHASE_RUNTIME_SNAPSHOT_VERSION:
|
||||
runtime_current_keys.append(&"auto_purchase_plans")
|
||||
if not _dictionary_has_exact_canonical_keys(runtime_data, runtime_current_keys) \
|
||||
or typeof(runtime_data.get(&"runtime_schema_version", null)) != TYPE_INT \
|
||||
or typeof(runtime_data.get(&"world_seed", null)) != TYPE_INT \
|
||||
@@ -688,6 +704,10 @@ func _restore_snapshot_internal(snapshot: Dictionary, recovery_mode: bool) -> bo
|
||||
return false
|
||||
if runtime_schema_version >= STAIRS_RUNTIME_SNAPSHOT_VERSION and not runtime_data.get(&"legacy_stairs", null) is Dictionary:
|
||||
return false
|
||||
if runtime_schema_version < AUTO_PURCHASE_RUNTIME_SNAPSHOT_VERSION and runtime_data.has(&"auto_purchase_plans"):
|
||||
return false
|
||||
if runtime_schema_version >= AUTO_PURCHASE_RUNTIME_SNAPSHOT_VERSION and not runtime_data.get(&"auto_purchase_plans", null) is Dictionary:
|
||||
return false
|
||||
var restored_catalog := MvpContentCatalog.new()
|
||||
var restored_catalog_report := restored_catalog.rebuild()
|
||||
if not bool(restored_catalog_report.get(&"valid", false)):
|
||||
@@ -738,6 +758,13 @@ func _restore_snapshot_internal(snapshot: Dictionary, recovery_mode: bool) -> bo
|
||||
var restored_queue := CommandQueue.new()
|
||||
if not restored_queue.restore_snapshot(runtime_data[&"command_queue"]):
|
||||
return false
|
||||
if runtime_schema_version < AUTO_PURCHASE_RUNTIME_SNAPSHOT_VERSION:
|
||||
# Old saves must not reinterpret an unsupported queued action as a new
|
||||
# paid feature when loading into this release. Failed historical actions
|
||||
# remain ordinary recorded history; only resumable pending work is gated.
|
||||
for row: Dictionary in restored_queue.create_snapshot()["pending"]:
|
||||
if row["command_type"] == "set_supply_auto_purchase":
|
||||
return false
|
||||
var restored_id_generator := StableIdGenerator.new()
|
||||
if not restored_id_generator.restore_snapshot(runtime_data[&"id_generator"]):
|
||||
return false
|
||||
@@ -822,6 +849,8 @@ func _restore_snapshot_internal(snapshot: Dictionary, recovery_mode: bool) -> bo
|
||||
var restored_economy := EconomyService.new()
|
||||
if not restored_economy.restore_snapshot(runtime_data[&"economy"]):
|
||||
return false
|
||||
if not _loan_principal_ledger_is_valid(restored_economy):
|
||||
return false
|
||||
if runtime_schema_version < OPERATIONS_RUNTIME_SNAPSHOT_VERSION:
|
||||
if not restored_economy.has_account(WAGE_EXPENSE_ACCOUNT) \
|
||||
and not restored_economy.register_account(WAGE_EXPENSE_ACCOUNT):
|
||||
@@ -1203,6 +1232,13 @@ func _restore_snapshot_internal(snapshot: Dictionary, recovery_mode: bool) -> bo
|
||||
return false
|
||||
if restored_kitchen.current_minute != restored_runtime_domain_minute or not restored_kitchen.validate_crosslinks():
|
||||
return false
|
||||
if runtime_schema_version == MIMI_PURCHASE_RUNTIME_SNAPSHOT_VERSION \
|
||||
and not PurchaseQuoteV2.valid_history(runtime_data, restored_economy):
|
||||
return false
|
||||
var restored_auto_purchase := AutoPurchasePlansType.new()
|
||||
if runtime_schema_version >= AUTO_PURCHASE_RUNTIME_SNAPSHOT_VERSION:
|
||||
if not restored_auto_purchase.restore_snapshot(runtime_data[&"auto_purchase_plans"], restored_facilities, restored_economy, restored_runtime_domain_minute):
|
||||
return false
|
||||
if not SupplyLogisticsBridge.validate_crosslinks(restored_logistics, restored_staff, restored_inventory, restored_facilities):
|
||||
return false
|
||||
var restored_weather := WeatherTimeline.new()
|
||||
@@ -1271,6 +1307,7 @@ func _restore_snapshot_internal(snapshot: Dictionary, recovery_mode: bool) -> bo
|
||||
economy_service = restored_economy
|
||||
inventory_service = restored_inventory
|
||||
kitchen_production = restored_kitchen
|
||||
auto_purchase_plans = restored_auto_purchase
|
||||
logistics_dispatcher = restored_dispatcher
|
||||
logistics_requests = restored_logistics
|
||||
facility_service = restored_facilities
|
||||
@@ -1966,6 +2003,7 @@ func _build_operations_external_state() -> Dictionary:
|
||||
&"upkeep_settlement": upkeep_settlement.create_snapshot(),
|
||||
&"inventory": inventory_service.create_snapshot(),
|
||||
&"kitchen_production": kitchen_production.create_snapshot(),
|
||||
&"auto_purchase_plans": auto_purchase_plans.create_snapshot(),
|
||||
&"facilities": facility_service.create_snapshot(),
|
||||
&"facility_cleanliness": facility_cleanliness.create_snapshot(),
|
||||
&"guests": guest_service.create_snapshot(),
|
||||
@@ -2023,7 +2061,7 @@ func _restore_operations_external_state(token: Variant) -> bool:
|
||||
var saved: Dictionary = token
|
||||
var required_dictionary_keys: Array[StringName] = [
|
||||
&"facility_upkeep", &"upkeep_settlement", &"facility_cleanliness",
|
||||
&"game_state", &"command_queue", &"economy", &"inventory", &"kitchen_production", &"facilities", &"guests",
|
||||
&"game_state", &"command_queue", &"economy", &"inventory", &"kitchen_production", &"auto_purchase_plans", &"facilities", &"guests",
|
||||
&"staff", &"relationships", &"events", &"authority", &"world", &"path", &"elevators", &"legacy_stairs",
|
||||
&"excavation", &"spatial_subslots", &"route_projection",
|
||||
&"facility_infrastructure",
|
||||
@@ -2066,6 +2104,7 @@ func _restore_operations_external_state(token: Variant) -> bool:
|
||||
var facilities_ok := facility_service.restore_snapshot(saved[&"facilities"])
|
||||
facilities_ok = facility_cleanliness.restore_snapshot(saved.get(&"facility_cleanliness", {})) and facilities_ok
|
||||
var kitchen_ok := kitchen_production.restore_snapshot(saved[&"kitchen_production"])
|
||||
var auto_purchase_ok := auto_purchase_plans.restore_snapshot(saved[&"auto_purchase_plans"], facility_service, economy_service, int(saved[&"captured_minute"]))
|
||||
var guests_ok := guest_service.restore_snapshot(saved[&"guests"])
|
||||
var staff_ok := staff_service.restore_snapshot(saved[&"staff"])
|
||||
var relationships_ok := relationship_service.restore_snapshot(saved[&"relationships"])
|
||||
@@ -2116,7 +2155,7 @@ func _restore_operations_external_state(token: Variant) -> bool:
|
||||
rng_ok = false
|
||||
|
||||
_discard_operations_domain_events()
|
||||
var participants_ok := upkeep_ok and state_ok and command_queue_ok and economy_ok and inventory_ok and kitchen_ok \
|
||||
var participants_ok := upkeep_ok and state_ok and command_queue_ok and economy_ok and inventory_ok and kitchen_ok and auto_purchase_ok \
|
||||
and facilities_ok and guests_ok and staff_ok and relationships_ok \
|
||||
and events_ok and authority_ok and world_ok and spatial_ok and path_ok \
|
||||
and route_ok and infrastructure_ok and excavation_ok \
|
||||
@@ -2173,6 +2212,7 @@ func _operations_external_state_matches(saved: Dictionary) -> bool:
|
||||
or not _strict_variants_equal(economy_service.create_snapshot(), saved[&"economy"]) \
|
||||
or not _strict_variants_equal(inventory_service.create_snapshot(), saved[&"inventory"]) \
|
||||
or not _strict_variants_equal(kitchen_production.create_snapshot(), saved[&"kitchen_production"]) \
|
||||
or not _strict_variants_equal(auto_purchase_plans.create_snapshot(), saved[&"auto_purchase_plans"]) \
|
||||
or not _strict_variants_equal(facility_service.create_snapshot(), saved[&"facilities"]) \
|
||||
or not _strict_variants_equal(facility_cleanliness.create_snapshot(), saved.get(&"facility_cleanliness", {})) \
|
||||
or not _strict_variants_equal(guest_service.create_snapshot(), saved[&"guests"]) \
|
||||
@@ -2535,6 +2575,8 @@ func _build_operations_daily_financials(
|
||||
) -> Dictionary:
|
||||
if period_start < 0 or period_end_exclusive <= period_start or new_wages_total < 0:
|
||||
return {&"ok": false, &"reason": &"DAILY_FINANCIAL_PERIOD_INVALID"}
|
||||
if not _loan_principal_ledger_is_valid(economy_service):
|
||||
return {&"ok": false, &"reason": &"INVALID_LOAN_PRINCIPAL_LEDGER"}
|
||||
var category_totals := {
|
||||
&"revenue": 0,
|
||||
&"refunds": 0,
|
||||
@@ -2564,6 +2606,8 @@ func _build_operations_daily_financials(
|
||||
&"EVENT_COST_COMMITTED": &"cost_of_goods",
|
||||
&"DEFENSE_SKILL_COST": &"cost_of_goods",
|
||||
&"MAINTENANCE_ACCRUAL": &"maintenance",
|
||||
&"LOAN_PRINCIPAL_BORROWED": &"financing_principal_inflows",
|
||||
&"LOAN_PRINCIPAL_REPAID": &"financing_principal_outflows",
|
||||
}
|
||||
var operating_refund_total := 0
|
||||
for entry: LedgerEntry in economy_service.get_entries():
|
||||
@@ -2583,10 +2627,10 @@ func _build_operations_daily_financials(
|
||||
&"transaction_id": entry.transaction_id,
|
||||
}
|
||||
var category: StringName = category_mapping[entry.category]
|
||||
category_totals[category] = int(category_totals[category]) + entry.amount
|
||||
category_totals[category] = int(category_totals.get(category, 0)) + entry.amount
|
||||
if not entry.source_entity_id.is_empty() \
|
||||
and (facility_service.has_instance(entry.source_entity_id) or entry.category == &"MAINTENANCE_ACCRUAL") \
|
||||
and category != &"construction_cash_flow":
|
||||
and category not in [&"construction_cash_flow", &"financing_principal_inflows", &"financing_principal_outflows"]:
|
||||
if not facility_rows.has(entry.source_entity_id):
|
||||
facility_rows[entry.source_entity_id] = {
|
||||
&"revenue": 0, &"expenses": 0, &"net_profit": 0,
|
||||
@@ -3476,6 +3520,108 @@ func _operations_payroll_crosslinks_are_valid(
|
||||
return true
|
||||
|
||||
|
||||
## Existing liabilities only. This projection does not issue financing or infer terms.
|
||||
func get_loan_principal_report() -> Dictionary:
|
||||
if not _initialized or economy_service == null or _state == null:
|
||||
return {&"available": false, &"loans": []}
|
||||
var cash := economy_service.get_balance(PLAYER_CASH_ACCOUNT)
|
||||
var pending: Dictionary = {}
|
||||
for command: SimulationCommand in command_queue.get_pending_commands():
|
||||
if command.command_type != &"repay_loan_principal":
|
||||
continue
|
||||
var id: Variant = command.payload.get(&"loan_id", null)
|
||||
if typeof(id) not in [TYPE_STRING, TYPE_STRING_NAME]:
|
||||
continue
|
||||
var key := StringName(id)
|
||||
if not pending.has(key): pending[key] = []
|
||||
pending[key].append(command.command_id)
|
||||
var loans: Array[Dictionary] = []
|
||||
for account: Dictionary in economy_service.get_accounts():
|
||||
var id := String(account[&"account_id"])
|
||||
if not id.begins_with("LOAN_LIABILITY:"):
|
||||
continue
|
||||
var loan := StringName(id.trim_prefix("LOAN_LIABILITY:"))
|
||||
loans.append({&"loan_id": loan, &"outstanding_principal": -economy_service.get_balance(StringName(id)), &"pending_command_ids": (pending.get(loan, []) as Array).duplicate()})
|
||||
loans.sort_custom(func(left: Dictionary, right: Dictionary) -> bool: return String(left[&"loan_id"]) < String(right[&"loan_id"]))
|
||||
var valid: bool = _state.get_resource(&"gold") == cash and (loans.is_empty() or _loan_principal_ledger_is_valid(economy_service))
|
||||
var ended: bool = _operations_enabled and operations_coordinator != null and operations_coordinator.is_terminal()
|
||||
return {&"available": true, &"ledger_valid": valid, &"terminal": ended, &"cash": cash, &"next_command_id": _next_command_id, &"loans": loans}
|
||||
|
||||
|
||||
## Atomic principal payment against an already attested loan liability.
|
||||
## Loan issuance, rates, due dates and bank eligibility belong to the bank
|
||||
## producer; this API does not create or approve any financing contract.
|
||||
func repay_loan_principal(
|
||||
loan_id: StringName,
|
||||
amount: Variant,
|
||||
idempotency_key: StringName
|
||||
) -> CommandResult:
|
||||
if not _initialized or economy_service == null or _state == null:
|
||||
return CommandResult.failed(CommandResult.Code.EXECUTOR_FAILED, 0, "Simulation runtime is not initialized.")
|
||||
if _operations_enabled and operations_coordinator != null and operations_coordinator.is_terminal():
|
||||
return CommandResult.failed(CommandResult.Code.VALIDATION_FAILED, 0, "Repayment is unavailable after the game ends.")
|
||||
if loan_id.is_empty() or idempotency_key.is_empty() or typeof(amount) != TYPE_INT \
|
||||
or amount <= 0 or amount > OperationsCoordinator.MAX_FINANCIAL_AMOUNT:
|
||||
return CommandResult.failed(CommandResult.Code.INVALID_PAYLOAD, 0, "A loan id, positive integer principal and idempotency key are required.")
|
||||
if not economy_service.get_transaction_id_for_idempotency_key(idempotency_key).is_empty():
|
||||
return CommandResult.failed(CommandResult.Code.DUPLICATE_IDEMPOTENCY_KEY, 0, "The principal payment key has already been posted.")
|
||||
if not _loan_principal_ledger_is_valid(economy_service):
|
||||
return CommandResult.failed(CommandResult.Code.VALIDATION_FAILED, 0, "The principal ledger is inconsistent.")
|
||||
var liability := StringName("LOAN_LIABILITY:" + String(loan_id))
|
||||
if not economy_service.has_account(liability):
|
||||
return CommandResult.failed(CommandResult.Code.NOT_FOUND, 0, "The loan liability does not exist.")
|
||||
var remaining := -economy_service.get_balance(liability)
|
||||
var cash := economy_service.get_balance(PLAYER_CASH_ACCOUNT)
|
||||
if amount > remaining or amount > cash or _state.get_resource(&"gold") != cash:
|
||||
return CommandResult.failed(CommandResult.Code.VALIDATION_FAILED, 0, "Principal payment exceeds the outstanding principal or available Gold, or its mirror is inconsistent.")
|
||||
# The single balanced posting is atomic. Defer receipts until the Gold
|
||||
# mirror is synchronized, also respecting any enclosing effect scope.
|
||||
economy_service.begin_notification_scope()
|
||||
var result := economy_service.transfer(
|
||||
PLAYER_CASH_ACCOUNT, liability, amount, _runtime_event_minute(),
|
||||
&"LOAN_PRINCIPAL_REPAID", loan_id, idempotency_key
|
||||
)
|
||||
if result.is_success():
|
||||
_state.set_resource(&"gold", economy_service.get_balance(PLAYER_CASH_ACCOUNT))
|
||||
result.details[&"loan_id"] = loan_id
|
||||
result.details[&"paid_principal"] = amount
|
||||
result.details[&"outstanding_principal"] = remaining - amount
|
||||
economy_service.end_notification_scope()
|
||||
return result
|
||||
|
||||
|
||||
func _loan_principal_ledger_is_valid(economy: EconomyService) -> bool:
|
||||
if economy == null:
|
||||
return false
|
||||
var outstanding: Dictionary = {}
|
||||
for entry: LedgerEntry in economy.get_entries():
|
||||
if entry.category not in [&"LOAN_PRINCIPAL_BORROWED", &"LOAN_PRINCIPAL_REPAID"]:
|
||||
continue
|
||||
if entry.source_entity_id.is_empty():
|
||||
return false
|
||||
var liability := StringName("LOAN_LIABILITY:" + String(entry.source_entity_id))
|
||||
if not economy.has_account(liability) or not economy.account_allows_negative(liability):
|
||||
return false
|
||||
var remaining := int(outstanding.get(liability, 0))
|
||||
if entry.category == &"LOAN_PRINCIPAL_BORROWED":
|
||||
if entry.debit_account != PLAYER_CASH_ACCOUNT or entry.credit_account != liability \
|
||||
or entry.amount > OperationsCoordinator.MAX_FINANCIAL_AMOUNT - remaining:
|
||||
return false
|
||||
remaining += entry.amount
|
||||
else:
|
||||
if entry.debit_account != liability or entry.credit_account != PLAYER_CASH_ACCOUNT \
|
||||
or entry.amount > remaining:
|
||||
return false
|
||||
remaining -= entry.amount
|
||||
outstanding[liability] = remaining
|
||||
for account: Dictionary in economy.get_accounts():
|
||||
var id: StringName = account[&"account_id"]
|
||||
if String(id).begins_with("LOAN_LIABILITY:"):
|
||||
if not economy.account_allows_negative(id) or economy.get_balance(id) != -int(outstanding.get(id, 0)):
|
||||
return false
|
||||
return true
|
||||
|
||||
|
||||
func _operations_expected_category_totals(
|
||||
economy: EconomyService,
|
||||
period_start: int,
|
||||
@@ -3485,6 +3631,8 @@ func _operations_expected_category_totals(
|
||||
if economy == null or period_start < 0 or period_end_exclusive <= period_start \
|
||||
or new_wages_total < 0:
|
||||
return null
|
||||
if not _loan_principal_ledger_is_valid(economy):
|
||||
return null
|
||||
var totals := {
|
||||
&"revenue": 0, &"refunds": 0, &"cost_of_goods": 0,
|
||||
&"wages": new_wages_total, &"maintenance": 0, &"utilities": 0,
|
||||
@@ -3505,6 +3653,8 @@ func _operations_expected_category_totals(
|
||||
&"EVENT_COST_COMMITTED": &"cost_of_goods",
|
||||
&"DEFENSE_SKILL_COST": &"cost_of_goods",
|
||||
&"MAINTENANCE_ACCRUAL": &"maintenance",
|
||||
&"LOAN_PRINCIPAL_BORROWED": &"financing_principal_inflows",
|
||||
&"LOAN_PRINCIPAL_REPAID": &"financing_principal_outflows",
|
||||
}
|
||||
var operating_refunds := 0
|
||||
for entry: LedgerEntry in economy.get_entries():
|
||||
@@ -3519,7 +3669,7 @@ func _operations_expected_category_totals(
|
||||
if not mapping.has(entry.category):
|
||||
return null
|
||||
var category: StringName = mapping[entry.category]
|
||||
totals[category] = int(totals[category]) + entry.amount
|
||||
totals[category] = int(totals.get(category, 0)) + entry.amount
|
||||
var offset := mini(operating_refunds, int(totals[&"cost_of_goods"]))
|
||||
totals[&"cost_of_goods"] = int(totals[&"cost_of_goods"]) - offset
|
||||
totals[&"revenue"] = int(totals[&"revenue"]) + operating_refunds - offset
|
||||
@@ -4412,6 +4562,23 @@ func _perform_checkpoint_save(
|
||||
&"message": "Simulation runtime is not initialized.",
|
||||
}
|
||||
var started_at_us := Time.get_ticks_usec()
|
||||
# A rejected Path projection is not a complete Runtime save. Refuse before
|
||||
# pausing or touching formal/tmp/backup files, and keep the refusal observable
|
||||
# without publishing a persisted event into the live snapshot.
|
||||
if path_service.create_snapshot().is_empty():
|
||||
var refused: Dictionary = {
|
||||
&"ok": false, &"code": &"INVALID_SNAPSHOT",
|
||||
&"message": "Incomplete Path snapshot; original save preserved.",
|
||||
&"operation": &"autosave" if use_autosave else &"save",
|
||||
&"save_path": save_path, &"reason": reason, &"request_id": request_id,
|
||||
&"checkpoint_absolute_minute": _clock.absolute_minute,
|
||||
&"runtime_duration_us": maxi(int(Time.get_ticks_usec() - started_at_us), 0),
|
||||
&"clock_pause_restored": true, &"formal_save_preserved": true,
|
||||
&"live_state_preserved": true,
|
||||
}
|
||||
latest_save_operation_result = _save_event_payload(refused)
|
||||
save_operation_completed.emit(refused.duplicate(true))
|
||||
return refused
|
||||
var previous_paused := _clock.paused
|
||||
_clock.set_paused(true)
|
||||
var snapshot := create_snapshot()
|
||||
@@ -5081,6 +5248,7 @@ func _initialize_s2_operations() -> void:
|
||||
facility_service.current_minute = _clock.absolute_minute
|
||||
_configure_facility_boundaries()
|
||||
_connect_facility_signals()
|
||||
auto_purchase_plans = AutoPurchasePlansType.new()
|
||||
kitchen_production = KitchenProductionService.new()
|
||||
kitchen_production.configure(inventory_service, facility_service, definition_registry, _clock.absolute_minute)
|
||||
|
||||
@@ -5214,6 +5382,7 @@ func _configure_relationship_event_boundaries() -> void:
|
||||
Callable(self, "_on_relationship_threshold_candidate")
|
||||
)
|
||||
event_service.configure_relationship_service(relationship_service)
|
||||
event_service.configure_candidate_eligibility_guard(Callable(self, "_event_candidate_placement_permitted"))
|
||||
event_service.configure_callbacks(
|
||||
Callable(self, "_event_actor_exists"),
|
||||
Callable(self, "_execute_event_action_boundary"),
|
||||
@@ -5224,6 +5393,32 @@ func _configure_relationship_event_boundaries() -> void:
|
||||
Callable(self, "_event_path_exists")
|
||||
)
|
||||
|
||||
## Original 6.2/6.4: Tomari's placement prevents floor trouble. This does not
|
||||
## choose a joining date, probability, defense score, or narrative content.
|
||||
func is_floor_trouble_protected(floor_y: int) -> bool:
|
||||
var tomari := staff_service.get_staff(&"heroine_tomari")
|
||||
if tomari == null or tomari.employment_kind != StaffRuntime.EmploymentKind.UNIQUE:
|
||||
return false
|
||||
var assignment := staff_service.get_work_assignment(tomari.staff_id)
|
||||
var facility := facility_service.get_instance(StringName(assignment.get(&"facility_id", &"")))
|
||||
return facility != null and facility.origin.y == floor_y
|
||||
|
||||
|
||||
func _event_candidate_placement_permitted(definition: EventDefinition, context: Dictionary) -> bool:
|
||||
if definition.category != &"TROUBLE":
|
||||
return true
|
||||
# A business trouble identifies its existing target facility. Missing or
|
||||
# conflicting location data cannot silently bypass placement protection.
|
||||
var target: Variant = context.get(&"facility_id", definition.location_rule.get(&"facility_id", null))
|
||||
if typeof(target) not in [TYPE_STRING, TYPE_STRING_NAME]:
|
||||
return false
|
||||
var declared: Variant = definition.location_rule.get(&"facility_id", target)
|
||||
if typeof(declared) not in [TYPE_STRING, TYPE_STRING_NAME] or StringName(declared) != StringName(target):
|
||||
return false
|
||||
var facility := facility_service.get_instance(StringName(target))
|
||||
return facility != null and not is_floor_trouble_protected(facility.origin.y)
|
||||
|
||||
|
||||
func _connect_relationship_signals() -> void:
|
||||
if relationship_service == null:
|
||||
return
|
||||
@@ -5240,6 +5435,8 @@ func _connect_event_signals() -> void:
|
||||
event_service.event_finished.connect(Callable(self, "_on_event_finished"))
|
||||
event_service.event_cancelled.connect(Callable(self, "_on_event_cancelled"))
|
||||
event_service.choice_resolved.connect(Callable(self, "_on_event_choice_resolved"))
|
||||
event_service.effect_transaction_started.connect(Callable(self, "_on_event_effect_transaction_started"))
|
||||
event_service.effect_transaction_finished.connect(Callable(self, "_on_event_effect_transaction_finished"))
|
||||
|
||||
func _initialize_s5_authority() -> void:
|
||||
authority_service = AuthorityService.new()
|
||||
@@ -5619,6 +5816,20 @@ func _execute_change_resources(command: SimulationCommand) -> CommandResult:
|
||||
&"faith_lifetime": int(progression_service.metrics[&"faith_lifetime"]),
|
||||
})
|
||||
|
||||
# Repay an existing attested liability through the ordinary command queue.
|
||||
# No rate, issuance, eligibility or due-date values are supplied here.
|
||||
func _execute_repay_loan_principal(command: SimulationCommand) -> CommandResult:
|
||||
var loan_id: Variant = command.payload.get(&"loan_id", null)
|
||||
if typeof(loan_id) not in [TYPE_STRING, TYPE_STRING_NAME] or String(loan_id).is_empty():
|
||||
return CommandResult.failed(CommandResult.Code.INVALID_PAYLOAD, command.command_id, "loan_id must be a nonempty String or StringName.")
|
||||
var result := repay_loan_principal(
|
||||
StringName(loan_id), command.payload.get(&"amount", null), command.idempotency_key
|
||||
)
|
||||
result.command_id = command.command_id
|
||||
result.target_id = StringName(loan_id)
|
||||
return result
|
||||
|
||||
|
||||
func _execute_spend_resources(command: SimulationCommand) -> CommandResult:
|
||||
var cost: Variant = command.payload.get(&"cost", command.payload.get("cost", null))
|
||||
if not cost is Dictionary:
|
||||
@@ -6401,6 +6612,17 @@ func _rollback_staff_candidate_hire(snapshot: Dictionary) -> bool:
|
||||
_runtime_event_minute()
|
||||
)
|
||||
|
||||
func _staff_role_matches_facility(role_id: StringName, definition: FacilityDefinition) -> bool:
|
||||
if definition.required_roles.is_empty() or role_id in definition.required_roles:
|
||||
return true
|
||||
# Existing public-bath data uses a legacy job ID; the paid roster exposes
|
||||
# the canonical attendant. The other aliases likewise retain saved job IDs.
|
||||
if definition.id == &"public_bath" and role_id == &"attendant" and &"bath_attendant" in definition.required_roles:
|
||||
return true
|
||||
if definition.id == &"massage_room" and role_id == &"masseur" and &"therapist" in definition.required_roles:
|
||||
return true
|
||||
return definition.id == &"security_station" and role_id == &"guard" and &"security" in definition.required_roles
|
||||
|
||||
func _execute_set_staff_work_assignment(command: SimulationCommand) -> CommandResult:
|
||||
for key: StringName in [&"staff_id", &"facility_id", &"template_id"]:
|
||||
if typeof(command.payload.get(key, null)) not in [TYPE_STRING, TYPE_STRING_NAME]:
|
||||
@@ -6429,7 +6651,7 @@ func _execute_set_staff_work_assignment(command: SimulationCommand) -> CommandRe
|
||||
var definition := definition_registry.get_facility(facility.definition_id)
|
||||
if definition == null or definition.staff_max <= 0:
|
||||
return _staff_assignment_failure(command.command_id, &"FACILITY_UNSTAFFED", "This facility does not accept Staff assignments.")
|
||||
if not definition.required_roles.is_empty() and staff.role_id not in definition.required_roles and not _is_anri_manager(staff):
|
||||
if not _staff_role_matches_facility(staff.role_id, definition) and not _is_anri_manager(staff):
|
||||
return _staff_assignment_failure(command.command_id, &"ROLE_MISMATCH", "Staff role does not match this facility.")
|
||||
if _staff_assignment_count(facility_id, template_id, staff_id) >= definition.staff_max:
|
||||
return _staff_assignment_failure(command.command_id, &"FACILITY_FULL", "This shift has reached the facility Staff limit.")
|
||||
@@ -7166,6 +7388,8 @@ func _execute_create_service_request(command: SimulationCommand) -> CommandResul
|
||||
options[&"quoted_price"] = quoted_price
|
||||
if not instance.operator_open or not instance.is_within_opening_hours(_runtime_event_minute()):
|
||||
return CommandResult.failed(CommandResult.Code.VALIDATION_FAILED, command.command_id, "This facility is closed to new service requests.", {&"reason": &"FACILITY_CLOSED"})
|
||||
if instance.requires_operating_kitchen() and not bool(facility_service.get_required_kitchen_report(facility_id).get(&"operating", false)):
|
||||
return CommandResult.failed(CommandResult.Code.VALIDATION_FAILED, command.command_id, "This dining facility requires an operating BACK kitchen.", {&"reason": &"FACILITY_CLOSED", &"dependency_reason": facility_service.get_required_kitchen_report(facility_id).get(&"reason", &"")})
|
||||
var payer_account := _guest_wallet_account(guest_ids[0])
|
||||
var committed_price := _outstanding_service_commitment(guest_ids[0])
|
||||
if not economy_service.account_allows_negative(payer_account) \
|
||||
@@ -7226,7 +7450,7 @@ func _execute_create_service_request(command: SimulationCommand) -> CommandResul
|
||||
if guest_service.has_guest(guest_id):
|
||||
runtime_guests.append(guest_id)
|
||||
if runtime_guests.is_empty():
|
||||
enqueue_result = facility_service.enqueue_request(request, _runtime_event_minute())
|
||||
enqueue_result = facility_service.enqueue_request(request, _runtime_event_minute(), not (_operations_enabled and _active_command_minute == _clock.absolute_minute + 1))
|
||||
else:
|
||||
var first_guest := runtime_guests[0]
|
||||
var queue_result := guest_service.enter_queue(
|
||||
@@ -7236,7 +7460,7 @@ func _execute_create_service_request(command: SimulationCommand) -> CommandResul
|
||||
_runtime_event_minute(),
|
||||
int(options.get(&"initial_patience", options.get("initial_patience", -1))),
|
||||
func(_guest_id: StringName, _facility_id: StringName, _request_id: StringName) -> CommandResult:
|
||||
return facility_service.enqueue_request(request, _runtime_event_minute())
|
||||
return facility_service.enqueue_request(request, _runtime_event_minute(), not (_operations_enabled and _active_command_minute == _clock.absolute_minute + 1))
|
||||
)
|
||||
if bool(queue_result.get(&"ok", false)):
|
||||
for guest_index in range(1, runtime_guests.size()):
|
||||
@@ -7541,6 +7765,7 @@ func _execute_demolish_facility(command: SimulationCommand) -> CommandResult:
|
||||
result.command_id = command.command_id
|
||||
if result.is_success():
|
||||
kitchen_production.remove_plan(instance_id)
|
||||
auto_purchase_plans.remove_warehouse(instance_id)
|
||||
return result
|
||||
|
||||
|
||||
@@ -7570,7 +7795,8 @@ func _capture_infrastructure_demolition_state() -> Dictionary:
|
||||
|
||||
func _restore_infrastructure_demolition_state(snapshot: Dictionary) -> bool:
|
||||
for key: StringName in [
|
||||
&"game_state", &"economy", &"world", &"path", &"elevators", &"legacy_stairs", &"construction",
|
||||
&"game_state", &"economy", &"facility_upkeep", &"upkeep_settlement",
|
||||
&"world", &"path", &"elevators", &"legacy_stairs", &"construction",
|
||||
&"excavation", &"spatial_subslots", &"route_projection",
|
||||
&"facility_infrastructure", &"facilities", &"facility_cleanliness", &"mana", &"movement_credits",
|
||||
&"domain_events", &"id_generator",
|
||||
@@ -7579,6 +7805,8 @@ func _restore_infrastructure_demolition_state(snapshot: Dictionary) -> bool:
|
||||
return false
|
||||
var restored: bool = bool(_state.restore_snapshot(snapshot[&"game_state"]))
|
||||
restored = economy_service.restore_snapshot(snapshot[&"economy"]) and restored
|
||||
restored = facility_upkeep.restore_snapshot(snapshot[&"facility_upkeep"]) and restored
|
||||
restored = upkeep_settlement.restore_snapshot(snapshot[&"upkeep_settlement"], economy_service) and restored
|
||||
restored = world_service.restore_snapshot(snapshot[&"world"]) and restored
|
||||
restored = spatial_subslot_service.restore_snapshot(snapshot[&"spatial_subslots"]) and restored
|
||||
var route_snapshot: Dictionary = snapshot[&"route_projection"]
|
||||
@@ -7617,6 +7845,8 @@ func _restore_infrastructure_demolition_state(snapshot: Dictionary) -> bool:
|
||||
func _infrastructure_demolition_state_matches(snapshot: Dictionary) -> bool:
|
||||
return _strict_variants_equal(_state.create_snapshot(), snapshot.get(&"game_state", {})) \
|
||||
and _strict_variants_equal(economy_service.create_snapshot(), snapshot.get(&"economy", {})) \
|
||||
and _strict_variants_equal(facility_upkeep.create_snapshot(), snapshot.get(&"facility_upkeep", {})) \
|
||||
and _strict_variants_equal(upkeep_settlement.create_snapshot(), snapshot.get(&"upkeep_settlement", {})) \
|
||||
and _strict_variants_equal(world_service.create_snapshot(), snapshot.get(&"world", {})) \
|
||||
and _strict_variants_equal(path_service.create_snapshot(), snapshot.get(&"path", {})) \
|
||||
and _strict_variants_equal(elevators.create_snapshot(), snapshot.get(&"elevators", {})) \
|
||||
@@ -8184,6 +8414,7 @@ func _advance_inventory_and_logistics(absolute_minute: int) -> void:
|
||||
inventory_service.current_minute = absolute_minute
|
||||
inventory_service.process_expired(absolute_minute, &"inventory.waste")
|
||||
if int(_state.get_resource(&"mana")) > 0:
|
||||
supply_management.advance_auto_purchases(self, absolute_minute)
|
||||
supply_management.ensure_deliveries(self, absolute_minute)
|
||||
supply_logistics.tick(absolute_minute)
|
||||
|
||||
@@ -8991,6 +9222,10 @@ func _project_guest_candidates(guest_id: StringName) -> Array:
|
||||
var candidates: Array = []
|
||||
if guest == null:
|
||||
return candidates
|
||||
var query_goals: Array = []
|
||||
for instance: FacilityInstance in facility_service.get_instances():
|
||||
query_goals.append_array(_facility_access_candidates(instance))
|
||||
path_service.begin_guest_path_query_batch(guest.current_cell, query_goals)
|
||||
for facility: FacilityInstance in facility_service.get_instances():
|
||||
var definition := definition_registry.get_facility(facility.definition_id)
|
||||
if definition == null or &"staff_only" in definition.tags:
|
||||
@@ -9029,6 +9264,7 @@ func _project_guest_candidates(guest_id: StringName) -> Array:
|
||||
&"graph_revision": path_service.graph_revision,
|
||||
},
|
||||
})
|
||||
path_service.end_guest_path_query_batch()
|
||||
return candidates
|
||||
|
||||
func _facility_access_candidates(facility: FacilityInstance) -> Array:
|
||||
@@ -9060,7 +9296,7 @@ func _find_facility_access_path(start: Vector3i, facility: FacilityInstance) ->
|
||||
var path := path_service.find_path(start, candidate)
|
||||
if path.is_empty():
|
||||
continue
|
||||
if best_path.is_empty() or path_service.calculate_path_cost(path) < path_service.calculate_path_cost(best_path):
|
||||
if best_path.is_empty() or path_service.calculate_guest_batch_path_cost(start, candidate, path) < path_service.calculate_guest_batch_path_cost(start, best_cell, best_path):
|
||||
best_path.assign(path)
|
||||
best_cell = candidate
|
||||
return {&"reachable": not best_path.is_empty(), &"path": best_path, &"target_cell": best_cell}
|
||||
@@ -9660,7 +9896,11 @@ func _execute_event_action_boundary(
|
||||
token[&"defense"] = defense_service.create_snapshot()
|
||||
var result := _apply_event_action(action, context)
|
||||
if not bool(result.get(&"ok", false)):
|
||||
_restore_event_action_token(token)
|
||||
var rollback := _restore_event_action_token(token)
|
||||
if not bool(rollback.get(&"ok", false)):
|
||||
result[&"fatal"] = true
|
||||
result[&"rollback"] = rollback
|
||||
result[&"error"] = "Event Action failed and rollback failed: %s; %s" % [result.get(&"error", ""), rollback.get(&"error", "")]
|
||||
return result
|
||||
result[&"token"] = token
|
||||
return result
|
||||
@@ -9857,8 +10097,19 @@ func _reserve_event_cost(cost: Dictionary, context: Dictionary) -> Dictionary:
|
||||
normalized[resource_id] = amount
|
||||
var economy_before := economy_service.create_snapshot()
|
||||
var state_before: Dictionary = _state.create_snapshot()
|
||||
var ids_before := id_generator.create_snapshot()
|
||||
var instance_id := StringName(context.get(&"event_instance_id", ""))
|
||||
var reservation_id: StringName = &""
|
||||
for amount: int in normalized.values():
|
||||
if amount > 0:
|
||||
if id_generator.get_counter(&"event_cost") >= StableIdGenerator.MAX_COUNTER:
|
||||
return {&"ok": false, &"error": "Event cost reservation IDs are exhausted."}
|
||||
reservation_id = id_generator.next_id(&"event_cost")
|
||||
break
|
||||
var escrow_account := StringName("EVENT_ESCROW:%s" % instance_id)
|
||||
var reservation := {&"event_instance_id": instance_id, &"cost": normalized, &"escrow_account": escrow_account}
|
||||
if not reservation_id.is_empty():
|
||||
reservation[&"reservation_id"] = reservation_id
|
||||
var gold := int(normalized.get(&"gold", 0))
|
||||
if gold > 0:
|
||||
if not economy_service.has_account(escrow_account):
|
||||
@@ -9866,23 +10117,21 @@ func _reserve_event_cost(cost: Dictionary, context: Dictionary) -> Dictionary:
|
||||
var transfer := economy_service.transfer(
|
||||
PLAYER_CASH_ACCOUNT, escrow_account, gold, _runtime_event_minute(),
|
||||
&"EVENT_COST_RESERVED", instance_id,
|
||||
StringName("event.cost.reserve.%s" % instance_id)
|
||||
_event_cost_ledger_key(reservation, &"reserve")
|
||||
)
|
||||
if not transfer.is_success():
|
||||
economy_service.restore_snapshot(economy_before)
|
||||
_state.restore_snapshot(state_before)
|
||||
id_generator.restore_snapshot(ids_before)
|
||||
return {&"ok": false, &"error": transfer.message}
|
||||
var other_costs := normalized.duplicate(true)
|
||||
other_costs.erase(&"gold")
|
||||
if not other_costs.is_empty() and not _state.spend(other_costs):
|
||||
economy_service.restore_snapshot(economy_before)
|
||||
_state.restore_snapshot(state_before)
|
||||
id_generator.restore_snapshot(ids_before)
|
||||
return {&"ok": false, &"error": "Event Choice resources are unavailable."}
|
||||
return {&"ok": true, &"token": {
|
||||
&"event_instance_id": instance_id,
|
||||
&"cost": normalized,
|
||||
&"escrow_account": escrow_account,
|
||||
}}
|
||||
return {&"ok": true, &"token": reservation}
|
||||
|
||||
func _release_event_cost(token: Variant, _context: Dictionary) -> Dictionary:
|
||||
if token is Dictionary and token.is_empty():
|
||||
@@ -9897,7 +10146,7 @@ func _release_event_cost(token: Variant, _context: Dictionary) -> Dictionary:
|
||||
var released := economy_service.transfer(
|
||||
escrow_account, PLAYER_CASH_ACCOUNT, gold, _runtime_event_minute(),
|
||||
&"EVENT_COST_RELEASED", instance_id,
|
||||
StringName("event.cost.release.%s" % instance_id)
|
||||
_event_cost_ledger_key(token, &"release")
|
||||
)
|
||||
if not released.is_success():
|
||||
return {&"ok": false, &"error": released.message}
|
||||
@@ -9920,10 +10169,17 @@ func _commit_event_cost(token: Variant, _context: Dictionary) -> Dictionary:
|
||||
var committed := economy_service.transfer(
|
||||
StringName(token.get(&"escrow_account", "")), OPERATING_EXPENSE_ACCOUNT,
|
||||
gold, _runtime_event_minute(), &"EVENT_COST_COMMITTED", instance_id,
|
||||
StringName("event.cost.commit.%s" % instance_id)
|
||||
_event_cost_ledger_key(token, &"commit")
|
||||
)
|
||||
return {&"ok": committed.is_success(), &"error": committed.message}
|
||||
|
||||
func _event_cost_ledger_key(token: Dictionary, operation: StringName) -> StringName:
|
||||
var reservation_id := StringName(token.get(&"reservation_id", ""))
|
||||
if not reservation_id.is_empty():
|
||||
return StringName("event.reserved_cost.%s.%s" % [operation, reservation_id])
|
||||
# Existing pending save tokens keep the original ledger operation identity.
|
||||
return StringName("event.cost.%s.%s" % [operation, token.get(&"event_instance_id", "")])
|
||||
|
||||
func _on_relationship_threshold_candidate(candidate: Dictionary) -> void:
|
||||
var candidate_id := StringName(candidate.get(&"candidate_id", ""))
|
||||
if candidate_id.is_empty() or event_service.get_instance(candidate_id) != null:
|
||||
@@ -10067,7 +10323,7 @@ func _validate_infrastructure_crosslinks(
|
||||
or (require_minute_alignment \
|
||||
and excavation.current_minute != construction.current_minute) \
|
||||
or not _same_vector3i_set(
|
||||
adapter.get_base_walkable_cells(), world.get_walkable_cells()
|
||||
adapter.get_base_walkable_cells(false), world.get_walkable_cells(false)
|
||||
) \
|
||||
or not _same_vector3i_set(
|
||||
adapter.get_projected_route_cells(), spatial.get_route_cells()
|
||||
@@ -10152,7 +10408,7 @@ func _validate_runtime_crosslinks(
|
||||
var assigned_staff := staff.get_staff(StringName(assignment[&"staff_id"]))
|
||||
var definition := definition_registry.get_facility(assigned_facility.definition_id)
|
||||
if assigned_staff == null or definition == null or definition.staff_max <= 0 \
|
||||
or (not definition.required_roles.is_empty() and assigned_staff.role_id not in definition.required_roles and not _is_anri_manager(assigned_staff)):
|
||||
or (not _staff_role_matches_facility(assigned_staff.role_id, definition) and not _is_anri_manager(assigned_staff)):
|
||||
return false
|
||||
var queued_links: Dictionary = {}
|
||||
var service_links: Dictionary = {}
|
||||
@@ -11453,6 +11709,22 @@ func _complete_service_transaction(
|
||||
request: ServiceRequest,
|
||||
facility: FacilityInstance,
|
||||
absolute_minute: int
|
||||
) -> Variant:
|
||||
# A sale is only observable after its inventory, rewards and cleanliness
|
||||
# effects commit. Rollback removes provisional receipts before scope exit.
|
||||
economy_service.begin_notification_scope()
|
||||
var result: Variant = _apply_service_transaction(service, request, facility, absolute_minute)
|
||||
if result is bool and result:
|
||||
_state.set_resource(&"gold", economy_service.get_balance(PLAYER_CASH_ACCOUNT))
|
||||
economy_service.end_notification_scope()
|
||||
return result
|
||||
|
||||
|
||||
func _apply_service_transaction(
|
||||
service: ServiceInstance,
|
||||
request: ServiceRequest,
|
||||
facility: FacilityInstance,
|
||||
absolute_minute: int
|
||||
) -> Variant:
|
||||
for guest_id: StringName in service.guest_ids:
|
||||
var guest := guest_service.get_guest(guest_id)
|
||||
@@ -11993,6 +12265,18 @@ func _on_relationship_route_lock_changed(
|
||||
&"route_id": route_id,
|
||||
})
|
||||
|
||||
func _on_event_effect_transaction_started() -> void:
|
||||
economy_service.begin_notification_scope()
|
||||
|
||||
|
||||
func _on_event_effect_transaction_finished() -> void:
|
||||
# A nested action batch ends before the enclosing choice cost commits.
|
||||
# Keep the authoritative mirror current without releasing observer receipts.
|
||||
if economy_service.notification_scope_depth() == 1:
|
||||
_state.set_resource(&"gold", economy_service.get_balance(PLAYER_CASH_ACCOUNT))
|
||||
economy_service.end_notification_scope()
|
||||
|
||||
|
||||
func _on_event_enqueued(instance: EventInstance) -> void:
|
||||
_publish_actor_event(
|
||||
&"EVENT_ENQUEUED", &"event_service", instance.actor_ids,
|
||||
@@ -12014,7 +12298,8 @@ func _on_event_finished(instance: EventInstance) -> void:
|
||||
|
||||
func _on_event_cancelled(instance: EventInstance, reason: StringName) -> void:
|
||||
_consume_relationship_candidate_from_event(instance)
|
||||
if event_service.get_foreground_instance() == null and not event_service.pause_requested:
|
||||
# Placement rejection occurs before an Event starts and never owns a pause.
|
||||
if reason != &"PLACEMENT_PROTECTION" and event_service.get_foreground_instance() == null and not event_service.pause_requested:
|
||||
_clock.set_paused(false)
|
||||
_publish_actor_event(&"EVENT_CANCELLED", &"event_service", instance.actor_ids, {
|
||||
&"reason": reason,
|
||||
@@ -12138,6 +12423,10 @@ func _execute_purchase_supplies(command: SimulationCommand) -> CommandResult:
|
||||
return supply_management.purchase(self, command)
|
||||
|
||||
|
||||
func _execute_set_supply_auto_purchase(command: SimulationCommand) -> CommandResult:
|
||||
return supply_management.set_auto_purchase(self, command)
|
||||
|
||||
|
||||
func _execute_set_kitchen_production(command: SimulationCommand) -> CommandResult:
|
||||
return supply_management.set_production(self, command)
|
||||
|
||||
@@ -12208,10 +12497,13 @@ func get_business_management_report() -> Dictionary:
|
||||
elif entry.category in [&"OPERATING_EXPENSE", &"STAFF_SIGNING_COST", &"EVENT_ACTION", &"EVENT_COST_COMMITTED", &"DEFENSE_SKILL_COST", &"AUTHORITY_FINE", &"REFUND"]:
|
||||
totals[entry.source_entity_id][&"expenses"] += entry.amount
|
||||
var rows: Array[Dictionary] = []
|
||||
var kitchens: Array[Dictionary] = []
|
||||
for facility: FacilityInstance in facility_service.get_instances():
|
||||
var definition := definition_registry.get_facility(facility.definition_id)
|
||||
if definition == null:
|
||||
continue
|
||||
if facility.definition_id == &"kitchen" and facility.origin.z == WorldService.LAYER_BACK:
|
||||
kitchens.append({&"facility_id": facility.instance_id, &"display_name": definition.display_name, &"origin": Vector2i(facility.origin.x, facility.origin.y), &"layer": facility.origin.z, &"operation_state": StringName(FacilityInstance.OperationState.keys()[facility.operation_state])})
|
||||
var amounts: Dictionary = totals.get(facility.instance_id, {})
|
||||
var base := _business_base_price(facility)
|
||||
var service_pricing := get_facility_service_pricing_readmodel(facility)
|
||||
@@ -12227,6 +12519,7 @@ func get_business_management_report() -> Dictionary:
|
||||
&"origin": Vector2i(facility.origin.x, facility.origin.y),
|
||||
&"layer": facility.origin.z,
|
||||
&"state": StringName(FacilityInstance.State.keys()[facility.state]),
|
||||
&"kitchen_dependency": facility_service.get_required_kitchen_report(facility.instance_id),
|
||||
&"business_open": facility.operator_open,
|
||||
&"price_mode": StringName(facility.policy_current.get(&"price_mode", &"standard")),
|
||||
&"base_price": base,
|
||||
@@ -12246,7 +12539,7 @@ func get_business_management_report() -> Dictionary:
|
||||
&"today_revenue": int(amounts.get(&"revenue", 0)),
|
||||
&"today_expenses": int(amounts.get(&"expenses", 0)),
|
||||
})
|
||||
return {&"minute": minute, &"modes": FacilityPricing.modes(), &"facilities": rows}
|
||||
return {&"minute": minute, &"modes": FacilityPricing.modes(), &"facilities": rows, &"kitchens": kitchens}
|
||||
|
||||
|
||||
func _guest_has_checked_in(guest: GuestRuntime) -> bool:
|
||||
@@ -12505,9 +12798,16 @@ func is_staff_in_transit(staff_id: StringName) -> bool:
|
||||
|
||||
func _estimated_actor_path_minutes(path: Array, access: Array[StringName]) -> int:
|
||||
for index: int in range(1, path.size()):
|
||||
if elevators.owns_connector(path_service.get_step_connector(path[index - 1], path[index], access)) \
|
||||
or _step_movement_credit(path_service, path[index - 1], path[index], access) > MOVEMENT_CREDIT_SCALE:
|
||||
return ceili(float(path_service.calculate_path_cost(path, access)) / PathService.ORTHOGONAL_MOVE_COST)
|
||||
var connector_id := path_service.get_step_connector(path[index - 1], path[index], access)
|
||||
if elevators.owns_connector(connector_id) \
|
||||
or _step_movement_credit_for_connector(path_service, path[index - 1], path[index], connector_id) > MOVEMENT_CREDIT_SCALE:
|
||||
var cost: int
|
||||
if access.is_empty() and path_service.get_script().resource_path == "res://scripts/world/path_service.gd" \
|
||||
and path[0] is Vector3i and path[path.size() - 1] is Vector3i:
|
||||
cost = path_service.calculate_guest_batch_path_cost(path[0], path[path.size() - 1], path)
|
||||
else:
|
||||
cost = path_service.calculate_path_cost(path, access)
|
||||
return ceili(float(cost) / PathService.ORTHOGONAL_MOVE_COST)
|
||||
return maxi(path.size() - 1, 0)
|
||||
|
||||
|
||||
@@ -12538,7 +12838,10 @@ func _prune_expired_staff_elevator_waits(minute: int) -> void:
|
||||
## one-step walking behavior, and elevator time is owned by the cabin service.
|
||||
static func _step_movement_credit(path: PathService, from: Vector3i, to: Vector3i,
|
||||
access: Array[StringName]) -> int:
|
||||
var connector_id := path.get_step_connector(from, to, access)
|
||||
return _step_movement_credit_for_connector(path,from,to,path.get_step_connector(from,to,access))
|
||||
|
||||
|
||||
static func _step_movement_credit_for_connector(path: PathService, from: Vector3i, to: Vector3i, connector_id: StringName) -> int:
|
||||
if String(connector_id).begins_with("infra.connector:") and from.x == to.x \
|
||||
and from.z == to.z and absi(from.y - to.y) == 1:
|
||||
var connector := path.get_connector(connector_id)
|
||||
@@ -12965,7 +13268,7 @@ func _prepare_construction_dimension_snapshot(job_id: StringName) -> Dictionary:
|
||||
|
||||
## Read-only saved-game preflight used by the load menu before confirmation.
|
||||
func preview_facility_dimensions_at_path(save_path: String) -> Dictionary:
|
||||
var loaded := SaveService.load_snapshot(save_path)
|
||||
var loaded := SaveService.inspect_snapshot(save_path)
|
||||
if not loaded[&"ok"]: return loaded
|
||||
var isolated: Variant = get_script().new()
|
||||
var preview_clock: Variant = load("res://autoload/game_clock.gd").new()
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
# 資産・Windows証拠の再監査 2026-10-02
|
||||
|
||||
判定: **未完了**。P01-02 / P02-03 / P22の静的証拠再監査。通常制作コピーを読み、旧成果は保持した。新規ゲーム/Godot/Windows EXEの起動、配布、素材導入は行っていない。
|
||||
|
||||
基準HEAD: `285528b7a3b1b46a2a67af5aa90d9085539b42f4`。最初の読取では採用1396マップと実ファイル差分0。その後の並行作業中に記録した観測時刻 `2026-10-02T01:03:35.152951+09:00` では下記差分を認めた。資産SHAは変化していない。採用状況はrootの証拠で判定する。
|
||||
|
||||
- 基準マップとの差分: `autoload/simulation_runtime.gd`
|
||||
- 基準マップとの差分: `scripts/core/save_service.gd`
|
||||
|
||||
この監査は基準資産の再利用性を記録する。作業中の変更を採用済みと断定しない。
|
||||
|
||||
## 集計
|
||||
|
||||
| 確認項目 | 再監査値 | 受入で欠けているもの |
|
||||
|---|---|---|
|
||||
| 媒体 | 290(Kenney 272、参考mockups 11、旧生成テーマ6、icon 1) | 利用枠ごとの完成承認 |
|
||||
| リテラル参照 | 8、欠損0 | 動的参照と自然な到達性 |
|
||||
| 原本施設 | 30施設、360利用枠 | 全Lv1〜3・表裏・外観/内装の対応と画面 |
|
||||
| コード候補 | 16施設・192枠に同一コード候補 | 専用差分の完成認定 |
|
||||
| ヒロイン | 7名・旧84枠群 | 固有Portrait/SD/表情/衣装/汚れ/破損/刻印 |
|
||||
| その他 | 旧17枠群と分割不足9群 | CG、ライバル、効果、UI、音などの必要量 |
|
||||
| 音 | ファイル0、AudioStream等の接続記号0 | BGMペア、crossfade、環境音、操作音、声、原本SE |
|
||||
| 完成承認記録 | 0 | 作品、権利、受入画像、担当 |
|
||||
|
||||
利用枠は画像枚数でも最終原子要件の分母でもない。360 + 84 + 17は旧台帳の利用単位を保全した表示。追加不足群は重複分割を含むため機械加算しない。総合実装率/受入率は未算出。
|
||||
|
||||
## 既存成果の再利用
|
||||
|
||||
既存 `tools/build_original_asset_inventory.py` をOUTだけメモリ上で変更し、`asset_inventory_reaudit/`へ再実行した。旧台帳290媒体のSHA差分0。旧1394配布マップと現1396基準でもassets関連600ファイル・pixel描画関連6ファイルのSHA差分0。資産側の静的調査は再利用できるが、現版の通常操作・完成素材・Windows受入へ旧試験を転用しない。
|
||||
|
||||
`PixelTowerArt.room` は施設ID/層/rectを受けるコード描画でlevelを受けない。`inactive_layer_rooms.gd` は同じ内装を低透明度で再利用する。`PixelPeople.pose` は汎用mode/frame/variant/staffを受け、`world_preview.gd`で人物keyのhashから色を選ぶ。7人物固有外観/職種別歩行の完成証拠にはならない。`LayerInterfaceSkin`の表裏配色変更は部分成果として保持する。
|
||||
|
||||
旧1394版macOS PCKの `actor-restored.png` を目視した。操作パネルと汎用コード人物の表示は確認できるが、全施設・7人物の専用差分や現Windows受入画像ではない。
|
||||
|
||||
## 原本30施設
|
||||
|
||||
| 原本施設 | definition ID | 資産区分 | 欠損 |
|
||||
|---|---|---|
|
||||
| 一般客室 | standard_room | コード描画候補 | 12利用枠の完成/差分/画面受入 |
|
||||
| 上級客室 | premium_room | コード描画候補 | 12利用枠の完成/差分/画面受入 |
|
||||
| VIPスイート | 未定義 | 専用資産対応なし | 12利用枠の完成/差分/画面受入 |
|
||||
| ドミトリー | 未定義 | 専用資産対応なし | 12利用枠の完成/差分/画面受入 |
|
||||
| 大衆食堂 | cafeteria | コード描画候補 | 12利用枠の完成/差分/画面受入 |
|
||||
| レストラン | 未定義 | 専用資産対応なし | 12利用枠の完成/差分/画面受入 |
|
||||
| 酒場 | tavern | コード描画候補 | 12利用枠の完成/差分/画面受入 |
|
||||
| 屋台 | 未定義 | 専用資産対応なし | 12利用枠の完成/差分/画面受入 |
|
||||
| 大浴場 | public_bath | コード描画候補 | 12利用枠の完成/差分/画面受入 |
|
||||
| カジノ | 未定義 | 専用資産対応なし | 12利用枠の完成/差分/画面受入 |
|
||||
| 闘技場 | 未定義 | 専用資産対応なし | 12利用枠の完成/差分/画面受入 |
|
||||
| 礼拝堂 | chapel | コード描画候補 | 12利用枠の完成/差分/画面受入 |
|
||||
| 風俗個室 | adult_private_room | コード描画候補 | 12利用枠の完成/差分/画面受入 |
|
||||
| 高級ソープ | 未定義 | 専用資産対応なし | 12利用枠の完成/差分/画面受入 |
|
||||
| SM拷問室 | 未定義 | 専用資産対応なし | 12利用枠の完成/差分/画面受入 |
|
||||
| 闇市場 | black_market | 専用資産対応なし | 12利用枠の完成/差分/画面受入 |
|
||||
| 厨房 | kitchen | コード描画候補 | 12利用枠の完成/差分/画面受入 |
|
||||
| 倉庫 | warehouse | コード描画候補 | 12利用枠の完成/差分/画面受入 |
|
||||
| 待機室 | staff_waiting_room | コード描画候補 | 12利用枠の完成/差分/画面受入 |
|
||||
| 従業員寮 | staff_dormitory | コード描画候補 | 12利用枠の完成/差分/画面受入 |
|
||||
| 警備員詰所 | security_station | コード描画候補 | 12利用枠の完成/差分/画面受入 |
|
||||
| 清掃員室 | cleaning_room | コード描画候補 | 12利用枠の完成/差分/画面受入 |
|
||||
| 医務室 | infirmary | コード描画候補 | 12利用枠の完成/差分/画面受入 |
|
||||
| 覗き部屋 | 未定義 | 専用資産対応なし | 12利用枠の完成/差分/画面受入 |
|
||||
| 地下牢 | 未定義 | 専用資産対応なし | 12利用枠の完成/差分/画面受入 |
|
||||
| マナ炉 | 未定義 | 専用資産対応なし | 12利用枠の完成/差分/画面受入 |
|
||||
| 蔓の昇降機 | guest_elevator | コード描画候補 | 12利用枠の完成/差分/画面受入 |
|
||||
| 業務用EV | service_elevator | コード描画候補 | 12利用枠の完成/差分/画面受入 |
|
||||
| ワープ | 未定義 | 専用資産対応なし | 12利用枠の完成/差分/画面受入 |
|
||||
| 魔法砲台 | 未定義 | 専用資産対応なし | 12利用枠の完成/差分/画面受入 |
|
||||
|
||||
## 人物と原本の不足枠
|
||||
|
||||
| ヒロイン | 専用Portrait | 専用SD | 差分数/担当 |
|
||||
|---|---|---|
|
||||
| アンリ | 対応なし | 汎用コード候補のみ | 未設定/未割当 |
|
||||
| フィーネ | 対応なし | 汎用コード候補のみ | 未設定/未割当 |
|
||||
| トマリ | 対応なし | 汎用コード候補のみ | 未設定/未割当 |
|
||||
| アメリア | 対応なし | 汎用コード候補のみ | 未設定/未割当 |
|
||||
| リズ | 対応なし | 汎用コード候補のみ | 未設定/未割当 |
|
||||
| ミミ | 対応なし | 汎用コード候補のみ | 未設定/未割当 |
|
||||
| サツキ | 対応なし | 汎用コード候補のみ | 未設定/未割当 |
|
||||
|
||||
ライ、リーゼンバルト、ソウ等は別集合。旧 `other_characters` 1群では人物/状態別必要量が未分離。アンリとライを2/7ヒロイン完成と数えない。
|
||||
|
||||
旧台帳では刻印、ルート別立ち絵演出、人物×ルートCG差分、モブスチル、職種別歩行、呼吸等、成人向けSD/SEが個別化されていない。JSONでは9不足群と7×3のCG利用群を記録した。21利用群は21枚CGの確定ではなく、必要差分数/脚本/担当は未設定。原本の必須内容を対象外の名目で分母から落とさない。
|
||||
|
||||
## 出典とライセンス
|
||||
|
||||
Tiny Town/Tiny Dungeonの同梱License.txtにCC0を確認し、公式URL/取得日記録/原アーカイブSHA/License SHAをJSONに保持。272画像は出典確認済みだが、完成利用枠の認定とは別。旧テーマ6画像はREADMEに組込画像生成ツールとpromptの記録がある。制作方法は再利用できるが、権利所有者/個別許諾/品質承認のレコードは未確認。参考mockups11件は完成配布資産へ計上しない。
|
||||
|
||||
2026-10-02に以下の公式ページで候補とCC0記載を確認した。素材は導入していない。
|
||||
|
||||
| 候補 | 対象 | 残る確認 |
|
||||
|---|---|---|
|
||||
| [Interface Sounds](https://kenney.nl/assets/interface-sounds) | UI操作音、100ファイル | 試聴/選定/音量/連打/ミュート/License同梱 |
|
||||
| [Input Prompts Pixel](https://kenney.nl/assets/input-prompts-pixel) | 16pxキー/マウス表示、800ファイル | 操作対応/視認性/License同梱 |
|
||||
| [UI Pack - Adventure](https://kenney.nl/assets/ui-pack-adventure) | 表UI枠/ボタン、130ファイル | 色調/文字/伸縮/完成承認 |
|
||||
| [UI Pack - Sci-Fi](https://kenney.nl/assets/ui-pack-sci-fi) | 裏UI枠/ボタン、130ファイル | 世界観/黒鉄monitor表現/完成承認 |
|
||||
| [Music Jingles](https://kenney.nl/assets/music-jingles) | 短い通知、85ファイル | 原本の同曲表裏BGMペアの合格証拠にはしない |
|
||||
|
||||
## Windowsの版一致
|
||||
|
||||
最新存在確認した開発ZIPは `build/windows/TheTree-Windows-accepted-unique-contract-ui-2026-10-01-r02.zip`。読取でCRC、同梱9ファイルSHA、BUILD_MANIFEST一致を再検査して合格。
|
||||
|
||||
| 対象 | SHA-256 |
|
||||
|---|---|
|
||||
| ZIP | `30d86f426c5d1f5c2bef6a5aef2dfa5821143c750f7f86eb482cd323b8012c24` |
|
||||
| EXE | `c52c04ae1283256102b446df50499420d29ab11758041b8abfc043b61dd270a1` |
|
||||
| PCK | `8b9ec8d80dc108d331636766b71c1286f49834075544497ebff32576415ffa8f` |
|
||||
| source map(1394) | `513ce23f329c0aaa4fc5d9b249a513dc7f524cf18fc2134eeb25abd3027e7a2e` |
|
||||
|
||||
現1396基準との差分はRuntime、FacilityPricing、BusinessManagementPanelの変更3件とpending-pricing試験/UIDの追加2件。旧版のmacOS PCK 23 + 4 freshprocess、4,323 + 166 checks等の履歴はあるがmanifestで `windows_native=false`、`performance_budget_pass=false`。現1396版Windows配布/同一EXE実機受入は欠損。`CURRENT_WINDOWS_PACKAGE.md`はさらに古いevent-defense-labels版を指すため、現在配布エイリアスも未確定。
|
||||
|
||||
`list_projects`はThe Treeのローカル制作コピーを返し、Windows側The Treeプロジェクトを返さなかった。公開されたhandoff tool定義にDESKTOP-D2UGDR4が候補として載るが、接続可能/OS/CPU/GPU/RAM/倍率/解像度/実機上配布SHAの証拠はない。全項目を未確認として記録。Windowsへの書込/配布/起動は行っていない。
|
||||
|
||||
## 次の低依存作業
|
||||
|
||||
1. 意味ID/施設/人物/Lv/層/状態、寸法/原点/frame順/時間、出典/担当/受入画像の契約を台帳へ追記。受入: 仮素材/コード候補/参考/完成を再計算でき、未設定を推測で埋めない。
|
||||
2. Kenney Interface Soundsの試聴と通常UI操作音差替契約。受入: 個別SHA/原License/音量/連打/ミュート/保存再開/Windows音を対応。
|
||||
3. 既存16施設をLv1-3/表裏/外内contact sheetへ並べ不足だけ制作。受入: コード描画を昇格する場合も各差分の画面と承認を保持。
|
||||
4. 7人物とライバル3名の固有外観/Portrait/SD/職種別動作briefと担当/予算案を分離。受入: 衣装/表情/刻印/ルート差分数を捏造しない。
|
||||
5. rootへ現採用ソースWindows生成/対象機記録/同一EXE検証を提案。受入: 同一Windows EXEの通常操作/音/保存再開/描画性能を実測。
|
||||
|
||||
この監査でP22/P02-03や全体を完了へ昇格しない。詳細は [assets_platform_audit.json](assets_platform_audit.json)、再生成台帳と整合性検査は `asset_inventory_reaudit/`。基準ソースの並行変更2件を記録したためinventory_validationの総合passedはfalse、媒体/ライセンス/旧ZIPの検査は合格。
|
||||
@@ -0,0 +1,33 @@
|
||||
# P01 原子要件台帳 v1
|
||||
|
||||
2026-10-02に指定原本の保存版を分割した。対象はローカル制作コピーであり、新たなDrive取得はしていない。
|
||||
|
||||
既知条件v1の機能・内容分母は **1,291原子**。原本に直接対応する889原子と、追加・承認のみを出典とする402原子を含む。工程4群は18個の期日付き条件へ分割し、機能分母から除外した。これは必要な条件の件数であり、工数の割合ではない。
|
||||
|
||||
原本258行・75群、30施設、施設追加F01〜F10、7人物と3ルート計21件、明示ライバル3人、承認A01〜A05を対応付けた。原本で有限に指定された施設素材は30施設×Lv1〜3×表裏×外観/内装=360枠として個別化した。施設や人物のキーは原本の監査IDであり、現在の本番リソースが存在することを意味しない。
|
||||
|
||||
**最終分母は確定していない。** 全エンド、図鑑対象、章、サブ人物、イベントCG/表情/衣装/音の差分量には未指定部分がある。集合条件を1件記録したことを、全対象の個別網羅の代用にしない。D08/D09の対象確定時には追加・削除・統合ID、出典、理由、判定者を記録して分母を改訂する。明示値のない条件を捏造して分割しない。
|
||||
|
||||
第10章30施設以外にも受付/精算所・金庫室・購買部・闇銀行(原本190〜193行)、客/裏通路・従業員ドア・階段・リネンシュート・隠し扉(46/49/50/86/176/177行)がある。通常機能条件は台帳に存在するが、F01〜F10やLv1〜3/表裏/外内素材の適用範囲は未確定である。open_expansion_registerに対象例と適用範囲の確定・分母改訂義務を記録した。これらを除外した30施設の展開を、最終全施設網羅と認定しない。一律12素材枠を追加することも避ける。
|
||||
|
||||
`atomic_requirements.json`が条件台帳、`source_atomic_coverage.json`が行・群・施設・人物・承認・追加の逆引き、`atomic_denominator_freeze.json`が分母/分子IDと集計契約である。原本の繰返しは同じ意味IDへ複数出典を付けて統合し、承認が数値を精緻化する場合も加点を重ねない。
|
||||
|
||||
8観点すべてを`unverified`とし、実装状態は`not_reaudited`に置いた。現在の全観点認定済み原子は0、受入認定済み原子は0である。この0は既存実装の不存在を示すものではない。通常UI入口は受入で使う予定入口であり、実入口の存在確認ではない。実装パス・証拠はP01-02の再監査結果を結合する。既存のテスト総数や過去群の部分状態を原子合格に転記しない。
|
||||
|
||||
適用範囲は全件`conservative_pending_review`である。適用外を付ける場合には、その観点へ理由・原本または明示判断の参照・受入判定者を記録する。全観点を無条件に適用外にして達成としない。判定者の役割を保持しているが、個人は未割当であり独立レビュー待ちである。
|
||||
|
||||
施設人数や料金の必要値は未設定のまま残件へ含める。一方、D03の休止復帰・複数関連先/AND/ORは、明示未設定の拡張なので`informational_explicitly_unset_optional_extension`で管理する。単一関連先なしは表現できる。これらの未設定を、新規仕様の補完や独立作業の停止理由にしない。A04/A05の値は`approved_initial_trial_value`として原本の値と区別する。
|
||||
|
||||
原本の内容や素材について中立的な条件名を用いている。台帳の記録だけで本来の内容・完成資産の受入を置き換えることはできない。未制作の内容は未制作として残す。
|
||||
|
||||
既存計画の進捗追記で出典SHAが変わらないよう、計画の原初本文は`execution/IMPLEMENTATION_PLAN.initial.md`へそのまま保存した。原子台帳の計画ピンはこの履歴コピーを参照する。
|
||||
|
||||
検証は次で再実行できる。
|
||||
|
||||
```sh
|
||||
python3 docs/plans/original-completion-2026-10-02/validate_atomic_requirements.py --self-test
|
||||
```
|
||||
|
||||
1,291原子と工程18原子の構造・出典・逆引き・有限列挙・分母再計算が合格し、13種の意図的な破損を拒否した。これは台帳整合性の検証であり、ゲーム受入の合格ではない。結果は`atomic_requirements_validation.json`に保存する。
|
||||
|
||||
`build_atomic_requirements.py`は今回の分割を再生成するための作成記録である。再監査・判定を結合した後に無条件に再生成すると判定を初期化するため、その後は現台帳を版管理して更新する。証拠オーバーレイは`requirements[].id`と観点名(`source_condition`, `definition`, `runtime`, `normal_ui`, `save_migration`, `boundary_verification`, `assets_platform`, `evidence`)で結合できる。
|
||||
@@ -0,0 +1,25 @@
|
||||
# 原子要件1291件の独立意味監査
|
||||
|
||||
最新台帳SHA `82f4b4c0fbf32bd296a83e6c565dd2b64d2449336c9ada7be11742761d4a5cfb` を、指定原本258行、承認A01〜A05、保持された施設追加仕様と対照した。ゲーム機能1291件と工程18件の分離、原本71機能群+4工程群、7人物×3ルート、chapter10の30施設×13値項目と360素材利用slotが保持されている。17件の承認値精緻化mergeに追加の重複計上問題は見つからなかった。チェックの根拠とSHAは `atomic_semantic_review.json` に記録した。
|
||||
|
||||
これは要件台帳の監査であり、実装・受入へ昇格した項目は0件。1291は既知条件の分母で、全制作対象の最終分母ではない。`final_denominator_established=false`、全1291件の証拠未確認を維持する扱いは適切である。
|
||||
|
||||
## 未確定の適用対象
|
||||
|
||||
原本chapter10の有限30施設以外に、受付/精算所・金庫室・購買部・闇銀行(190〜193行)、客用/裏通路・階段・職員ドア(46、49、50、86、176〜177行)、リネンシュート・隠し扉(49行)が名指しされる。通常機能の要件は台帳にあるが、追加F01〜F10とLv1〜3/表裏/外内素材(248行)の全適用範囲は未確定である。
|
||||
|
||||
この指摘は最新 `open_expansion_register` の `additional_named_facilities_and_infrastructure_applicability` に取り込まれた。30施設の展開から全施設追加項目と全素材の最終網羅を合格にしてはいけない。設備を全部同じ施設型と扱い、一律12素材slotや全項目を推測追加することもしない。分類と適用対象を固定し、対象が増える場合は原子ID/分母改訂履歴へ記録する。
|
||||
|
||||
## 図鑑・全エンド・実制作の判定
|
||||
|
||||
原本9行の全エンドと図鑑コンプは独立した集合要件として残り、D08未確定になっている。対象catalog、個別ID、取得条件がない間、汎用の完了flagや部分ルート一覧で合格にできない。承認した対象集合に対し、個々の通常進行到達・取得・保存終了再起動を確認する必要がある。
|
||||
|
||||
7人物と21ルート利用は有限で確認できるが、それを21枚のCGと読み替えない。メイン立ち絵や他ルート色調の集合要件は、全既知人物と原本235行の各ルート/陰影/表情の軸を全部満たすことが必要である。衣装・表情・CG・ボイスの総数は未指定で、捏造できない。D08/D09の個別制作台帳と全軸AND受入を必要とする運用注意を残した。ゲーム状態やパラメータだけの実装は実シーン・素材の制作を証明しない。
|
||||
|
||||
## 重複と未設定値
|
||||
|
||||
A03のアンリ補正、A04/A05の寸法・層・金額・人数等は同じIDへの精緻化で保持され、二重に重みを加えていない。LvUPの価格設定、刻印、職種歩行/ループの繰り返し原本文も統合されている。同じ文の観察対応が3件あるが、VIPスイート・大浴場・風俗個室という独立対象なので重複ではない。
|
||||
|
||||
追加F08は全30施設で最低職員数と上限を別の値項目にしている。F07の人数専用上限は汎用capacityから推定せず、待機者と利用中顧客を分ける契約を保持する。必要値の未設定は台帳表示として許されても、営業受入の代用にならない。単一関連先なしと未承認の休止復帰/複数関連条件は、必須営業値の未確定と分けて扱う。
|
||||
|
||||
現時点の判定は「既知条件台帳を、適用対象と内容catalogの未確定を残して意味監査済み」である。全原本実装、全施設最終網羅、図鑑/全エンド/全素材の最終受入は引き続き未成立である。
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,21 @@
|
||||
# Godot import Unicode 2行の原因と表記修正
|
||||
|
||||
guard検証用fresh stageのreference/candidate両方で発生した `Unexpected NUL character` 2行は、`tests/performance/test_digest_performance.gd` の2つの `\u0000` リテラルから発生した。両stageの対象スクリプトは同一SHAで、テキストの物理NULと不正UTF-8を走査して0件だった。候補guard変更と独立したimport時診断である。元stageを変更していない。
|
||||
|
||||
Godot 4.7.2の独立した小規模fresh projectを3種類作り、それぞれ新importと値のprobeを実行した。実験6プロセスはすべてexit 0。
|
||||
|
||||
| 表記 | fresh import のNUL診断 | 2つの値のcodepoint/UTF-8/Variant bytes |
|
||||
|---|---:|---|
|
||||
| 元の `\u0000` リテラル2個 | 2 | 元値 |
|
||||
| 実行時 `String.chr(0)` 2個 | 0 | 元値と完全一致 |
|
||||
| 実行時 `String.chr(0xfffd)` 2個 | 0 | 元値と完全一致 |
|
||||
|
||||
元の実行値はNULを内包せず、U+FFFDとなる。日本語と絵文字の値はcodepoints `[21416,25151,65533,127795]`、UTF-8 `e58ea8e688bfefbfbdf09f8cb3`、Variant bytes `040000000d000000e58ea8e688bfefbfbdf09f8cb3000000`。単文字の値はcodepoints `[65533]`、UTF-8 `efbfbd`、Variant bytes `0400000003000000efbfbd00`。これらが3表記で一致した。
|
||||
|
||||
Godotのtokenizerはunicode escapeを文字列へ追加する。[4.7.2 tokenizer source](https://raw.githubusercontent.com/godotengine/godot/4.7.2-stable/modules/gdscript/gdscript_tokenizer.cpp) の1006〜1007行が対象。`String::append_utf32(0)` は診断を出してreplacement characterへ置換する。[4.7.2 string source](https://raw.githubusercontent.com/godotengine/godot/4.7.2-stable/core/string/ustring.cpp) の187〜194行が対象。`String::chr()` も同じ追加処理を使う。[4.7.2 string header](https://raw.githubusercontent.com/godotengine/godot/4.7.2-stable/core/string/ustring.h) の411〜414行を確認した。この一次ソースと実験ログが置換の因果根拠である。
|
||||
|
||||
root承認に従い、本体テストの2リテラルだけを `String.chr(0xfffd)` で構成する表記へ変更し、理由コメントを付けた。ゲームコードは変更していない。テスト内の実行時 `String.chr(0)` と `PackedByteArray([0,127,255])` の境界試験は維持している。**NUL境界試験を削除してエラー行を隠した変更ではない。** GodotがNULを受け付けず置換する事実を踏まえた表記修正である。
|
||||
|
||||
修正後スクリプトSHAは `363772fc43975f12c9e2056e2b01beb4cd8b2d7544db7528f80c6bd127710c1f`。本体で焦点実行した結果は `Digest performance: 83 checks, 0 failures`、exit 0。実行時NUL診断321行は保持した境界試験の63+64+65+129回と一致する。焦点runの前後SHAは同一だった。
|
||||
|
||||
証拠は `artifacts/original-completion-2026-10-02/unicode-nul-cause-repro-r01/verification.json` と各import/probe log、fixture copy、`focused-verification.json` と `focused-test.log` に保存した。Godot実行binary SHAは `c7cccbf8fb143e34e02fd6521e09be2c2b974f0d5db080b19071c9c570718ccf`。本体fresh importでの2行除去と全回帰の確認はrootが別に担当し、この焦点結果だけから全体受入を主張しない。
|
||||
@@ -0,0 +1,98 @@
|
||||
# P09 施設必須値の再監査(2026-10-02)
|
||||
|
||||
原本30施設について、定義あり17、通常登録16、定義のみ1(black_market)、未定義13を静的に確認した。現在の全`.tres`は37件で、そのうち原本第10章の対応外は20件。これは最終の全施設数でも、実装・受入完了率でもない。今回のエンジン実行は0件、本体変更は0件。
|
||||
|
||||
機械可読台帳は `facility_value_contracts.json`。各値は原本文、A01〜A05、別冊A作成時の値と由来、現`.tres`明示値またはクラス既定値を別欄に保存し、出典行とSHAを保持する。生成は `python3 docs/plans/original-completion-2026-10-02/build_facility_value_contracts.py`。検証は `validate_facility_value_contracts.py`。承認差異の静的一致からRuntime/UI/保存のPASSを作らない。
|
||||
|
||||
原本を最上位とし、A01〜A05と会話追加10項目を重ねる。別冊Aは旧実装の完全一覧であり、数値承認書ではない。冒頭3・10・14〜19・76・106行が既定値/旧状態/仮値/異なる処理系を区別している。参照先 `docs/FACILITY_UNIT_PARAMETERS.md` は現ローカルに存在しないため、その内容を推定補完しなかった。既存の保存版TRACEABILITY 978〜995行を追加指定の記録に用いた。
|
||||
|
||||
## 30施設の現値と根拠
|
||||
|
||||
表のmin/max、人数専用、capacity、料金は現実装の観測値である。礼拝堂・清掃員室以外の数値を承認済みに扱わない。未設定は人数専用-1、未定義は独立定義なしを表す。
|
||||
|
||||
| 原本施設 | 定義/通常登録 | 原本サイズ・層 | min/max現値 | 人数専用/汎用capacity現値 | 料金現値 | 根拠 |
|
||||
|---|---|---|---|---|---|---|
|
||||
| 一般客室 | normal_registered | 2×1 表 | 0/0 | 未設定/2 | 120 | 数値は旧/現実装・個別根拠未確認 |
|
||||
| 上級客室 | normal_registered | 4×1 表 | 0/0 | 2/2 | 220 | 数値は旧/現実装・個別根拠未確認 |
|
||||
| VIPスイート | no_definition | 6×2 表 | 未定義/未定義 | 未定義/未定義 | 未定義 | 原本の寸法等のみ確定 |
|
||||
| ドミトリー | no_definition | 4×1 表 | 未定義/未定義 | 未定義/未定義 | 未定義 | 原本の寸法等のみ確定 |
|
||||
| 大衆食堂 | normal_registered | 4×2 表 | 2/4 | 未設定/12 | 25 | 数値は旧/現実装・個別根拠未確認 |
|
||||
| レストラン | no_definition | 6×2 表 | 未定義/未定義 | 未定義/未定義 | 未定義 | 原本の寸法等のみ確定 |
|
||||
| 酒場 | normal_registered | 4×2 表 | 2/4 | 未設定/14 | 40 | 数値は旧/現実装・個別根拠未確認 |
|
||||
| 屋台 | no_definition | 2×1 表 | 未定義/未定義 | 未定義/未定義 | 未定義 | 原本の寸法等のみ確定 |
|
||||
| 大浴場 | normal_registered | 6×2 表 | 1/2 | 未設定/12 | 20 | 数値は旧/現実装・個別根拠未確認 |
|
||||
| カジノ | no_definition | 6×3 表 | 未定義/未定義 | 未定義/未定義 | 未定義 | 原本の寸法等のみ確定 |
|
||||
| 闘技場 | no_definition | 8×4 表 | 未定義/未定義 | 未定義/未定義 | 未定義 | 原本の寸法等のみ確定 |
|
||||
| 礼拝堂 | normal_registered | 4×3 表 | 1/2 | 8/8 | 20 | A04初期値 |
|
||||
| 風俗個室 | normal_registered | 2×1 表 | 1/1 | 未設定/2 | 250 | 数値は旧/現実装・個別根拠未確認 |
|
||||
| 高級ソープ | no_definition | 4×2 表 | 未定義/未定義 | 未定義/未定義 | 未定義 | 原本の寸法等のみ確定 |
|
||||
| SM拷問室 | no_definition | 4×2 表 | 未定義/未定義 | 未定義/未定義 | 未定義 | 原本の寸法等のみ確定 |
|
||||
| 闇市場 | definition_only | 4×2 表 | 1/2 | 未設定/8 | 50 | 数値は旧/現実装・個別根拠未確認 |
|
||||
| 厨房 | normal_registered | 4×2 裏 | 1/3 | 未設定/20 | 0 | 数値は旧/現実装・個別根拠未確認 |
|
||||
| 倉庫 | normal_registered | 4×2 裏 | 1/3 | 未設定/100 | 0 | 数値は旧/現実装・個別根拠未確認 |
|
||||
| 待機室 | normal_registered | 4×2 裏 | 0/0 | 未設定/8 | 0 | 数値は旧/現実装・個別根拠未確認 |
|
||||
| 従業員寮 | normal_registered | 2×1 裏 | 0/0 | 0/12 | 0 | 数値は旧/現実装・個別根拠未確認 |
|
||||
| 警備員詰所 | normal_registered | 2×2 裏 | 1/3 | 未設定/3 | 0 | 数値は旧/現実装・個別根拠未確認 |
|
||||
| 清掃員室 | normal_registered | 2×1 裏 | 1/2 | 0/0 | 0 | A05初期値 |
|
||||
| 医務室 | normal_registered | 4×2 裏 | 0/2 | 未設定/0 | 0 | 数値は旧/現実装・個別根拠未確認 |
|
||||
| 覗き部屋 | no_definition | 2×1 裏 | 未定義/未定義 | 未定義/未定義 | 未定義 | 原本の寸法等のみ確定 |
|
||||
| 地下牢 | no_definition | 4×2 裏 | 未定義/未定義 | 未定義/未定義 | 未定義 | 原本の寸法等のみ確定 |
|
||||
| マナ炉 | no_definition | 6×3 裏 | 未定義/未定義 | 未定義/未定義 | 未定義 | 原本の寸法等のみ確定 |
|
||||
| 蔓の昇降機 | normal_registered | 2×2 表 | 0/0 | 未設定/4 | 0 | 数値は旧/現実装・個別根拠未確認 |
|
||||
| 業務用EV | normal_registered | 3×2 裏 | 0/0 | 未設定/8 | 0 | 数値は旧/現実装・個別根拠未確認 |
|
||||
| ワープ | no_definition | 2×2 表 | 未定義/未定義 | 未定義/未定義 | 未定義 | 原本の寸法等のみ確定 |
|
||||
| 魔法砲台 | no_definition | 未指定 外壁 | 未定義/未定義 | 未定義/未定義 | 未定義 | 原本の寸法等のみ確定 |
|
||||
|
||||
原本で寸法のある現定義17施設は寸法と層が静的一致する。一般的な「表」は地上専用を意味しない。地下や高層の指定は個別に保存し、魔法砲台の幅・高さ・層は捏造しない。premium_room、staff_dormitory、cleaning_room、chapelの通常登録は今回変更ではなく現基準の既存成果である。
|
||||
|
||||
## 10項目と必須値の区別
|
||||
|
||||
1. 配置コスト: 原本は資金/資材と建設ルールを指定するが、施設別コストと工期の数値はA04/A05以外に個別承認を確認できない。現値は保存しても正式値へ格上げしない。
|
||||
2. 運用コスト: A01の保有按分、休業/休止中計上、00:00精算、不足繰越とA02の0.5%初期試験率は確定。既存ceil/最低1Gを出典付きで保持する。礼拝堂6G/日、清掃員室2G/日は個別上書き。率の承認は建築費自体の個別承認を意味しない。闇市場の現maintenance_gold=既定0は、現1600Gに率を当てた条件付き8Gと異なる。未登録定義を登録する前にこの差異を解消する契約が必要。施設別マナ量は原本では未指定。原本183行のマナ0で全停止を別に保持する。
|
||||
3. 利用料金: 礼拝堂20G寄付、清掃員室利用料なしを承認初期値として保持。他施設の単価や5段階60/80/100/150/250%は旧実装値。D01の不足基準、加算額/率、属性割当は未設定。元の四欲求0〜100や清掃予約75を不足基準に流用しない。A03の20%補正は承認済みだが、新しい一致加算との順序/丸めはP09-02接続前に固定する。
|
||||
4. サイズ: 29施設の原本寸法と30対象の層/地下/高層/外壁指定を追跡。魔法砲台寸法は未指定のまま。
|
||||
5. 役割: 職種とサービス機能の原文、required_rolesとstaff_minを分離。A04聖職者min1/max2、A05清掃員min1/max2は確定。原本の記録係配置などの役割と人数の数値は同一視しない。
|
||||
6. 稼働状態: INACTIVE/OPERATING/SUSPENDEDと破壊/摘発後の持続休止は会話追加の確定契約。復帰条件D03はユーザー指定の未設定拡張であり、必須値欠損に数えない。
|
||||
7. 顧客同時利用最大人数: 承認値は礼拝堂8、清掃員室0のみ。上級客室2・従業員寮0は現明示値だが根拠未確認。残る定義13件は-1継承、未定義13件には値なし。待機者は利用人数とは別。厨房capacity20/profile生産4、倉庫capacity100、従業員寮capacity12などをcustomer_capacityへ転記しない。
|
||||
8. 配置可能従業員最大数: staff_maxはstaff_minと別。2施設の個別承認、15施設の旧/現値、13施設の未定義を保持する。Lv別変更もD02の未確定に残す。
|
||||
9. 関連施設/稼働フラグ: 原本168・173行の食堂/レストラン/酒場と厨房セット、52・174行の覗き対象などを個別追跡。現汎用単一依存APIは循環拒否と実稼働評価を持つが、建設からのcreate_instanceにはrelated_facility_idを渡していない。原本の対応を自動設定済みと認定しない。D03の複数依存AND/ORは未設定維持が許され、必須欠損へ数えない。
|
||||
10. 属性: 許可型appetite/relaxation/sexual/gamblingは確定、施設への割当は全37定義で空文字。Runtimeのhunger/sleep/hygiene/medical/leisure/intimacy効果と提供属性は別の名前空間・用途。効果タグから勝手に属性を選ばない。不一致の利用は許可する。
|
||||
|
||||
## Lv1〜3と解放
|
||||
|
||||
全30施設にLv1/2/3を別レコードとして置き、価格・人数・staff_min/max・解放条件・Policy解放割当を未確定として追跡する。A04/A05の初期値をLv1候補へ参照するが、承認本文にLv指定はなく、Lv2/3で不変とも自動増加とも承認されていない。原本165行のLvMAXに数値はない。計画P09-03のLv1〜3と現既定max_level3を混同しない。
|
||||
|
||||
現FacilityDefinitionにレベル別料金/人数の表はない。厨房などのupgrade_costsは旧実装の費用列であり、レベル別営業値ではない。unlock_rank既定1は正式な個別解放承認ではなく、現通常建設コマンドにunlock_rank/research_requirementsの評価もない。別冊Aの世界樹ランク表や初回Day案内は派生計画/実装欄に保全し、原本施設別解放条件へ昇格しない。原本179行の価格、食材、混浴、ハード設定、イカサマ率のLv解放は機能要件として残り、どのLvでどの値にするかを捏造しない。
|
||||
|
||||
## 未定義13施設から抽出できる範囲
|
||||
|
||||
| 未定義施設 | 確定できる項目 | 原本行 |
|
||||
|---|---|---|
|
||||
| VIPスイート | 6×2; VIP宿泊・覗き対応・要高景観; FRONT高層 | 199 |
|
||||
| ドミトリー | 4×1; 地下の宿泊施設; FRONT地下 | 199 |
|
||||
| レストラン | 6×2; 飲食・厨房とセット運用; FRONT | 201 |
|
||||
| 屋台 | 2×1; 飲食施設; FRONT | 201 |
|
||||
| カジノ | 6×3; 娯楽欲解消・イカサマ率設定をLvUPで解放; FRONT地下 | 203 |
|
||||
| 闘技場 | 8×4; 娯楽施設; FRONT地下 | 203 |
|
||||
| 高級ソープ | 4×2; 地下アングラ施設; FRONT地下 | 205 |
|
||||
| SM拷問室 | 4×2; 地下アングラ施設; FRONT地下 | 205 |
|
||||
| 覗き部屋 | 2×1; VIP/浴場/風俗の裏限定・裏通路寸断・記録係配置で脅迫ネタ収集; BACK | 209 |
|
||||
| 地下牢 | 4×2; 地下特殊施設; BACK地下 | 209 |
|
||||
| マナ炉 | 6×3; 地下特殊施設・奴隷消費; BACK地下 | 209 |
|
||||
| ワープ | 2×2; 瞬間移動・マナコスト大; FRONT | 211 |
|
||||
| 魔法砲台 | 寸法未指定; 外壁の防衛設備; 外壁 | 211 |
|
||||
|
||||
これらはsource-only blueprintとして先行定義できる。費用・人数・職員上限・マナ量・属性・個別解放・Lv2/3の値が未確定のまま、FacilityDefinitionの既定100G/1資材/60分などを適用して通常建設へ公開しない。既存地下客室をドミトリー、地下VIPサロンをVIPスイート、魔力制御室をマナ炉の完成代替と扱わない。
|
||||
|
||||
## 次に進める低依存作業
|
||||
|
||||
- A04/A05の承認済み人数・最少/最大職員・料金の契約を通常UIと保存へつなぐ境界検証を先行。current値を変更する前にsource/value確定と表示を区別。
|
||||
- customer_capacity=-1とservice_attribute空文字をUIで未設定と表示し、現在明示2/0も承認由来の別表示にする。generic容量・人数・待機人数を分ける。
|
||||
- 13未定義施設のsource-only blueprint/設計カタログを追加。確定寸法/層/地下/高層/外壁/厨房/覗き制約だけを保持し、未確定の値はnullとする。通常建設へ登録しない。
|
||||
- 食堂・酒場の厨房セット運用を現単一関連APIと実稼働判定へ接続する契約/候補検証を用意。厨房停止・破壊・未配置・保存復元時の動作を検証。
|
||||
- Lv1–3の明示tableと未設定guardを作り、Lv2/3料金や人数をbase値の複写で確定しない。既存upgrade_costsとrank1は旧実装欄に保全。
|
||||
- 欲求加算はD01確定まで請求を接続せず、nonmatch利用可・未解決見積拒否・財布/収入一致の計算契約と比較例を準備する。
|
||||
|
||||
この監査は値の出典と欠損を整理したもので、P09や各施設の受入完了ではない。通常操作、グループ利用中変更、日跨ぎ、途中保存、修正後同一ソース回帰の証拠は引き続き必要である。第9章経済施設、通路/階段/ドア/リネンシュート/隠し扉へのF各施設/Lv素材適用範囲はatomicのopen expansion registerと同じ未確定状態を維持する。
|
||||
|
||||
静的データの追加観測として、医務室定義には同値のstaff_max/capacity二重代入と、FacilityDefinitionに宣言がないqueue_staff_minがある。台帳のdefinition_anomaliesへ保存し、今回Runtimeへの影響を推測したり本体を修正したりしない。validatorは49出典ファイルのSHA、30施設/780値/90Lvの構造、全出典行、D03除外、架空値拒否を確認する。PASSは台帳の整合性だけを意味する。
|
||||
@@ -0,0 +1,36 @@
|
||||
# P28-01 実 Runtime 代表負荷の再監査
|
||||
|
||||
指定の20,000マス・200客・50職員・150施設に行列・物流・イベントを付けた、現版の実 Runtime 全体の受入証拠は未確認である。現在の通常 Operations の人口上限は100で、200客の通常営業受入へそのまま進めない。人口上限200への生改変や保存基準値変更は採用していない。
|
||||
|
||||
この文書はソースの読み取りと driver 案である。新しい性能測定は実施しておらず、root の全157回帰実行中に CPU を競合させていない。読み取ったファイルのSHA、時刻、詳細は `performance_audit.json` に記録した。
|
||||
|
||||
## 既存 fixture の再利用範囲
|
||||
|
||||
| 既存成果 | 確認済みの範囲 | 代表負荷との差 |
|
||||
|---|---|---|
|
||||
| `perf_mvp_001_harness.gd` | 実サービスを別々に作り、20000/200/50/150、行列500、未完物流200、イベント50を独自ループで処理 | SimulationRuntime/GameClock/実保存パッケージ/UI/GPUを通さない。200×50×2のマップはRank7正規境界に合わない |
|
||||
| `profile_production_scene.gd` | 実Runtimeとmain画面、Tick phase、x1/x3、snapshot/digest、UI同期更新を測る | PixelTowerDemoの7サービス施設・4職員という小シーン。自動保存無効、描画/GPU対象外 |
|
||||
| `test_runtime_actor_cycle.gd` | 公開commandで建設・職員・客を登録し、実Tickで業務・物流・サービスとsnapshot replayを検証 | 小規模。200客の実保存・読込・持続負荷を証明しない |
|
||||
| `test_save_preflight.gd` | 大型Worldの保存サイズ測定 | `full_runtime_20000_cell_acceptance=false` を明示する |
|
||||
|
||||
モデルの測定値と小Runtime測定値を、指定の全Runtime負荷の合格値へ読み替えない。
|
||||
|
||||
## 100客上限と200客診断
|
||||
|
||||
`OperationsCoordinator.DEFAULT_CONFIG.population_cap` は100(`scripts/operations/operations_coordinator.gd:83`)。Runtimeの `enable_production_operations()` は正規既定値で生成し、上限overrideを受け取らない(`autoload/simulation_runtime.gd:1446`)。有効時の `spawn_guest` は上限を拒否する(5892)。保存復元は正規configとの厳密一致を要求する(3160、3828)。従って手動cap200変更は正規保存契約と一致しない。
|
||||
|
||||
Operationsを無効のまま初期化する診断ではこの上限guardが適用されない。公開 `spawn_guest` は客・財布・経路actor・進行生涯数を接続し、GameClockは実Runtimeの登録phaseを走らせる。`save_to_path`(3991)と `load_from_path`(4019)も利用できる。200客の保存+実Tickは静的には実施候補になるが、まだ200客を作成・保存・読込していない。**production Operations enabled mixed load未受入**を全結果に付ける。
|
||||
|
||||
Rank7の正規境界はx=-60〜60、y=-40〜56、2層で23,474座標slot。20,000はこの境界内で準備する。Rank6は13,774slotで足りない。GameStateだけRank7へ書き換えたり、モデルの200×50境界をコピーすると、Progression/World/経路投影の復元検証と一致しない。公開昇格commandの条件を満たすfixture、または同版で検証済みのRank7保存が必要で、その既存成果は未確認である。
|
||||
|
||||
## 最小 driver 案
|
||||
|
||||
1. root採用版のソース地図、Godot binary、機種、seed、driver、専用一時保存先をpinする。
|
||||
2. 実GameClock/GameState/SimulationRuntimeからOperations無効の診断を作り、正規昇格履歴と境界内20,000マスを準備する。世界・SpatialSubslot・RouteAdapter・施設インフラ投影の全相互参照を検証する。
|
||||
3. 公開commandで150施設を完成させ、50職員へシフト・業務を設定、200客へ実財布を作成する。行列、在庫、未完物流、イベント候補を実サービスへ接続し、開始時と終了時の実数を報告する。診断用の在庫・資源補給は通常プレイ証拠へ昇格させない。
|
||||
4. 測定前にRuntimeの実保存パッケージを作成し、別Runtimeで読み込み、完全snapshot/digest一致を要求する。失敗したfixtureでは時間だけ測って合格にしない。
|
||||
5. 同一checkpointへ戻してx1/x3の全Tick、phase、経路、snapshot、digest、UI同期更新、最大停止、実保存/読込を別々に測る。平均/P95/最大/標本数/区間/速度を保存し、対象が減ったら減少数も報告する。
|
||||
6. Operations有効は100客以下の正規診断として別に測る。200客有効の本番代表負荷は未受入として残す。回帰と同時測定せず、同条件を反復する。
|
||||
7. 描画とWindowsは別の実機受入とする。同一EXE/PCK/ZIP、CPU/GPU/RAM、解像度/拡大率、D10の描画目標が必要である。
|
||||
|
||||
現コード予算はx1平均4ms、x3 P95 12ms、経路P95 2ms、最大main-thread停止50ms、保存5秒、読込8秒(`scripts/core/performance_budget.gd:17`)。新測定はまだない。200客の本番上限、正規Rank7 fixture、通常営業に必要な未確定施設値、持続する行列/物流/イベント、Windows実機と描画目標が受入の残件である。
|
||||
File diff suppressed because one or more lines are too long
@@ -0,0 +1,31 @@
|
||||
# 作業計画の改訂とレビュー
|
||||
|
||||
作成日: 2026-10-02。対象: 同日の原本全実装計画。計画書、作業台帳、対応表、基準と承認台帳の整合性を確認する。ゲームの新規受入は行っていない。
|
||||
|
||||
## 前計画からの修正
|
||||
|
||||
1. 原本の旧75群監査と現在の証拠を分離し、総合割合の未算出を明示した。
|
||||
2. 予定扱いで部分実装がある8パッケージを着手中へ修正した。完了への昇格は行っていない。
|
||||
3. 原本30施設の16/1/13を40入力ファイルの現SHA一致で確認し、登録率と機能完成率を分離した。
|
||||
4. 最新155スイートと12,310チェックの合格、1396本体、旧1394Windows、未採用候補を区別した。
|
||||
5. P20に図鑑コンプを明記し、人物7名とライバルを別集計にした。
|
||||
6. 既承認の維持費、アンリ、礼拝堂、清掃を再確認不要とし、未確定は依存箇所だけに限定した。
|
||||
7. パッケージ全体の完了依存と、必要契約がそろった機能単位の先行着手を分けた。
|
||||
8. 正常/異常の意味を保った軽量化、現在版のWindows、全件受入後の通しデバッグを独立した条件にした。
|
||||
9. 過去180〜390人日を残工数として再利用せず、原子分割と内容量の確定後に再見積する。
|
||||
|
||||
## 確認と制約
|
||||
|
||||
原本範囲と実装証拠について別担当が読み取り監査を行い、施設入力40件と採用ソース1396件の一致を確認した。計画作成では新規Godot実行、最新Windows実機試験、Drive再取得を行っていない。既存原子要件の現在全受入が未確定であるため、全体割合を推定値で補っていない。
|
||||
|
||||
原子台帳、素材必要量、未確定の必須値、対象Windows描画目標、必要な内容制作が今後の不確実性として残る。これらは計画から除外せず、担当パッケージと局所的な固定地点を設定した。
|
||||
|
||||
## 独立レビューで修正した点
|
||||
|
||||
完了条件を示す旧JSONキーを `cross_cutting_done` から `cross_cutting_acceptance` へ改名し、未着手項目の完了証拠と誤読されないよう適用範囲を明記した。旧Windows配布物の確認はZIP・PCK検査とmacOS実行であり、Windows実機ではないと現在地に追記した。過去の性能値は9月30日に計測した候補の値とし、現HEADの測定と区別した。
|
||||
|
||||
シフト・気絶・通勤の原本B-3.3は、基準試験のP02に加えて実業務を担当するP12とP16にも対応付け、本文・台帳・原本対応表の双方向整合を確認する。
|
||||
|
||||
進捗更新の検証は、初期状態の固定検査と通常の更新可能な検査を分離した。着手・保留・証拠付き完了を認め、旧初期状態は履歴として残す。旧ソースの全件SHA比較は明示オプションにし、将来の進捗を初期スナップショットへ固定しない。承認根拠4件の参照を旧計画の実ファイルへ解決できる相対パスに補正した。
|
||||
|
||||
最終の独立読み取りレビューで指摘残件の解消を確認した。検証器について、着手への更新を許可し、作業単位およびパッケージの証拠なし完了を拒否する3ケースをメモリ上の入力で確認した。記録は [検証器の更新確認](validator_update_checks.json)。ゲーム実行や本体変更はない。
|
||||
@@ -0,0 +1,57 @@
|
||||
# Runtime・UI・保存移行 再監査 2026-10-02
|
||||
|
||||
ローカル制作コピーの基準HEAD `285528b7a3b1b46a2a67af5aa90d9085539b42f4` を再監査し、P03の保存事前確認と破損拒否を修正した。P03全体・原本全実装は未完了。機械可読の根拠、SHA、行番号、未受入一覧は [runtime_save_audit.json](runtime_save_audit.json) に記録した。
|
||||
|
||||
## 証拠の版と適用範囲
|
||||
|
||||
開始時に採用後1396ファイルのSHAを照合して差分0を確認した。155スイート/12,310チェックは旧採用証拠の記録であり、本担当で全回帰を新たに実行した値ではない。今回は本体3ファイルと新保存試験を変更したため、統合全回帰と新しいWindows配布版受入を別途必要とする。
|
||||
|
||||
旧 `snapshot-opening-restart-2026-09-29` の283パスは、開始時49パス、今回の保存修正後51パスが不一致となる。現版の受入へ直接転用しない。rootの今回の描画付き開業再起動はwriter83/reader20チェック、両プロセスPASS・実行中ソース差分0だったが、その後の保存3ファイルとPathの計4ファイルが変わった。修正後の同一版へ再実行するまでは変更前の基準証拠として扱う。
|
||||
|
||||
## P03で実装した修正
|
||||
|
||||
`SaveService.inspect_snapshot()` は指定された正式セーブだけを検証し、中断保存の取引を復旧・昇格・削除しない。寸法移行プレビューをこのAPIに接続した。正式セーブが `.bak` に移された中断状態は `SAVE_NOT_FOUND` で拒否し、`.tmp`・`.bak`・取引マーカーを保全する。通常 `load_snapshot()` の中断復旧は維持する。
|
||||
|
||||
修正前に、プレビューで取引マーカーが削除されることを実行再現した。さらに形式版 `true`・`"1"`・`1.5` がすべて1として読込成功することを確認した。manifestの版とサイズは有限の整数値のJSON数値だけを許容し、チェックサムはSHA-256文字列を要求する。整数値のfloatはGodotのJSON数値互換のため許容し、文字列・bool・小数の強制変換は拒否する。
|
||||
|
||||
一般envelopeにも同じ型方針を適用した。schema、時刻、速度、pause、snapshot_versionが破損したときはclock/stateを復元する前に拒否する。旧版の省略されたpause/versionの既定値と旧x4→x3は保った。`SimulationKernel.restore_clock()` の直接呼出契約は変更していない。
|
||||
|
||||
技術的な資源上限として、worldは圧縮・展開それぞれ256 MiB、manifestは4 MiBとした。ファイル長とバイナリheaderの展開サイズを一括読取・解凍前に検査し、超過は `SAVE_SIZE_LIMIT_EXCEEDED` を返す。writerにも同じ上限を適用し、読み戻せない候補を正式保存へ昇格させない。これらはゲーム数値の追加仕様ではなく、破損saveの資源保護方針である。
|
||||
|
||||
現初期Runtimeの実サイズはraw 61,040 B/圧縮7,511 B。既存PERFの2万セルWorldのみの実サイズはraw 640,488 B/圧縮18,873 Bで、world上限には約419倍の余裕がある。全人物・全サービス・全施設を含む最大Runtimeの受入は未検証。正当な旧セーブでも上限を超える場合は拒否されるリスクが残る。
|
||||
|
||||
## 実行した受入と履歴
|
||||
|
||||
最終修正後の7スイートは485チェック、失敗0、実行中ソース差分0。
|
||||
|
||||
| 最終r02試験 | チェック | 区分 |
|
||||
|---|---:|---|
|
||||
| save preflight | 128 | 新規の読取・型・未知版・破損・サイズ・原状保持 |
|
||||
| save operations | 127 | 世代、復旧、旧形式移行、失敗時保持、symlink |
|
||||
| Runtime save lifecycle | 96 | checkpoint、復旧候補、domain検証とwhitelist修復 |
|
||||
| S0 kernel/save | 73 | clock、envelope、保存復元 |
|
||||
| transport save | 29 | 実役者・EV座席所有、旧attendance、crosslinks |
|
||||
| dimension migration UI | 12 | 実メニューのsignal handler、取消、stale-source、backup |
|
||||
| supply save migration | 20 | 旧供給状態と復元 |
|
||||
|
||||
証拠は `artifacts/original-completion-2026-10-02/p03-save-preflight/`。全保存候補と取引artifactのファイルSHAを比較し、拒否時・プレビュー時のバイトと配置を確認した。上限ちょうどの広告サイズは独立したmanifest不一致で解凍前に拒否し、上限超過はsize-limitで先に拒否する境界を試験した。
|
||||
|
||||
修正中の失敗履歴を保持した。初期85チェックではmalformed JSONの正しい拒否でもengine ERRORが出たため受入から除外し、JSONの返却エラー処理へ修正した。続く128チェック中1失敗は整数float速度がArrayの型一致で拒否される互換試験で、型検証後のint比較へ修正した。r02を合格根拠とする。寸法batch23・weighted migration63・寮task/rest25・selection autosave75は最終比較修正前の関連PASSとして保持し、最終版485に合算していない。
|
||||
|
||||
UI試験は実画面や公開コマンド・signal handlerを用いるが、加速clockと制御fixtureを含む。モデル試験、通常画面のhandler経路、OS pointer入力、Windows実機を別状態として扱う。
|
||||
|
||||
## 保存契約と更新順の現在地
|
||||
|
||||
保存はpackage format1、一般envelope schema1、Runtime schema13の三層である。Runtimeは既存1〜13の明示分岐を持ち、v5以上の現在形状ではcanonical key集合を厳格に検証する。会計・維持費・施設状態と人数・マナ・客職員・移動credit・EV・配送・清掃・イベント・防衛・建設・掘削などを保存し、別サービス候補と相互参照の検証後にliveを交換する。未知版・追加key・必須aggregate欠損を無断で補完しない。
|
||||
|
||||
順序はコマンド適用→clock前進と日付signal→天候/維持費/営業境界→マナ→建設→物流→施設完了→欲求環境→事件防衛関係イベント→queue→AI→snapshotとcheckpoint。最後にclockのsnapshot_versionと時刻通知が更新される。通常日次autosaveは06:00で保存済みdayキーにより重複を防ぐ。承認済み維持費精算は00:00で別契約。全同時境界の受入は未完了で、D05のEV停止・費用時点・日跨ぎの未確定を補っていない。
|
||||
|
||||
## 残件
|
||||
|
||||
- P03-01: 全原本機能の保存契約、未実装の欲求加算・全人物・章・エンド・図鑑を含む新状態の保存条件。
|
||||
- P03-02: 移動中役者、EV乗車、救護搬送、物流、複数同時建設、占有再配置とtask retargetの全移行受入。現行は安全な候補だけを受け、失う位置・経路・task destinationは拒否する。
|
||||
- D07: 現5関係値trust/respect/affection/fear/dependenceと原本3値の意味対応、既存routeの扱い。旧値を保全し、推測変換しない。
|
||||
- P03-03: 営業途中の現開業基準に加えて、配送・清掃・防衛・人物進行の途中を実プロセス終了/再起動して同じ操作列の財布・台帳・役者・イベントへ比較する受入。旧開業試験は測定時間を含むdomain event streamを明示除外し、他の状態を比較する限定証拠である。
|
||||
- P02/P28/P29: 修正後同一版での描画・pointer・Windows実機・最大規模・長期通し受入。
|
||||
|
||||
原子分母の固定は別担当のP01台帳へ委ねる。試験件数や保存キー数から総合実装率・受入率を計算しない。
|
||||
+1262
File diff suppressed because it is too large
Load Diff
+6482
File diff suppressed because it is too large
Load Diff
+16
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"facility_mapping": "docs/plans/original-completion-2026-09-29/execution/original_facility_mapping.json",
|
||||
"facility_mapping_sha256": "df0c933220449c8bb3e0fd942ad6f1dee49075abdd6587c4856022e12e3665cf",
|
||||
"baseline_sha256": "75e4bdbdeb379c9269553f0c776bdb9cc96e01f2d6aa5ca650d132c63a8aeb1a",
|
||||
"media_files": 290,
|
||||
"literal_referenced_media_files": 8,
|
||||
"audio_files": 0,
|
||||
"facility_count": 30,
|
||||
"facility_usage_slots": 360,
|
||||
"named_heroines": 7,
|
||||
"character_usage_slot_groups": 84,
|
||||
"other_slot_groups": 17,
|
||||
"approved_assets": 0,
|
||||
"missing_literal_references": [],
|
||||
"scope": "Inventory and candidate mappings only. No asset is promoted to complete by file existence, procedural fallback, or a passing gameplay test. Variant counts and production ownership remain unresolved."
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
{
|
||||
"scope": "hash/inventory/archive consistency only, not game/quality/native acceptance",
|
||||
"checks": {
|
||||
"fresh_media_hashes": true,
|
||||
"unique_media_paths": true,
|
||||
"original_facilities_30": true,
|
||||
"facility_usage_slots_360": true,
|
||||
"heroines_7": true,
|
||||
"unique_facility_slots": true,
|
||||
"current_reference_map_matches": false,
|
||||
"old_assets_unchanged": true,
|
||||
"Kenney_licenses_present": true,
|
||||
"old1394_zip_crc_SHA": true,
|
||||
"no_false_finish": true
|
||||
},
|
||||
"passed": false
|
||||
}
|
||||
+4294
File diff suppressed because it is too large
Load Diff
+192
@@ -0,0 +1,192 @@
|
||||
[
|
||||
{
|
||||
"slot_id": "background.front",
|
||||
"requirement": [
|
||||
"B-13.3"
|
||||
],
|
||||
"candidate": "scenes/main/world_preview.gd",
|
||||
"status": "unverified_required_slot",
|
||||
"creator": "unassigned",
|
||||
"required_variant_count": null,
|
||||
"approved_asset": null
|
||||
},
|
||||
{
|
||||
"slot_id": "background.back",
|
||||
"requirement": [
|
||||
"B-13.3"
|
||||
],
|
||||
"candidate": "scenes/main/world_preview.gd",
|
||||
"status": "unverified_required_slot",
|
||||
"creator": "unassigned",
|
||||
"required_variant_count": null,
|
||||
"approved_asset": null
|
||||
},
|
||||
{
|
||||
"slot_id": "ui.front_skin",
|
||||
"requirement": [
|
||||
"B-13.4"
|
||||
],
|
||||
"candidate": "assets/themes/japanese_fantasy/visual-theme-v1.tres",
|
||||
"status": "unverified_required_slot",
|
||||
"creator": "unassigned",
|
||||
"required_variant_count": null,
|
||||
"approved_asset": null
|
||||
},
|
||||
{
|
||||
"slot_id": "ui.back_skin",
|
||||
"requirement": [
|
||||
"B-13.4"
|
||||
],
|
||||
"candidate": "assets/themes/japanese_fantasy/visual-theme-v1.tres",
|
||||
"status": "unverified_required_slot",
|
||||
"creator": "unassigned",
|
||||
"required_variant_count": null,
|
||||
"approved_asset": null
|
||||
},
|
||||
{
|
||||
"slot_id": "ui.emotion_graph",
|
||||
"requirement": [
|
||||
"B-13.4"
|
||||
],
|
||||
"candidate": null,
|
||||
"status": "unverified_required_slot",
|
||||
"creator": "unassigned",
|
||||
"required_variant_count": null,
|
||||
"approved_asset": null
|
||||
},
|
||||
{
|
||||
"slot_id": "ui.camera_overlay",
|
||||
"requirement": [
|
||||
"B-13.4"
|
||||
],
|
||||
"candidate": null,
|
||||
"status": "unverified_required_slot",
|
||||
"creator": "unassigned",
|
||||
"required_variant_count": null,
|
||||
"approved_asset": null
|
||||
},
|
||||
{
|
||||
"slot_id": "effect.shield",
|
||||
"requirement": [
|
||||
"B-13.2"
|
||||
],
|
||||
"candidate": null,
|
||||
"status": "unverified_required_slot",
|
||||
"creator": "unassigned",
|
||||
"required_variant_count": null,
|
||||
"approved_asset": null
|
||||
},
|
||||
{
|
||||
"slot_id": "effect.smoke",
|
||||
"requirement": [
|
||||
"B-13.2"
|
||||
],
|
||||
"candidate": null,
|
||||
"status": "unverified_required_slot",
|
||||
"creator": "unassigned",
|
||||
"required_variant_count": null,
|
||||
"approved_asset": null
|
||||
},
|
||||
{
|
||||
"slot_id": "effect.mana_light",
|
||||
"requirement": [
|
||||
"B-13.2"
|
||||
],
|
||||
"candidate": null,
|
||||
"status": "unverified_required_slot",
|
||||
"creator": "unassigned",
|
||||
"required_variant_count": null,
|
||||
"approved_asset": null
|
||||
},
|
||||
{
|
||||
"slot_id": "audio.front_bgm",
|
||||
"requirement": [
|
||||
"B-13.5"
|
||||
],
|
||||
"candidate": null,
|
||||
"status": "unverified_required_slot",
|
||||
"creator": "unassigned",
|
||||
"required_variant_count": null,
|
||||
"approved_asset": null
|
||||
},
|
||||
{
|
||||
"slot_id": "audio.back_arrangement",
|
||||
"requirement": [
|
||||
"B-13.5"
|
||||
],
|
||||
"candidate": null,
|
||||
"status": "unverified_required_slot",
|
||||
"creator": "unassigned",
|
||||
"required_variant_count": null,
|
||||
"approved_asset": null
|
||||
},
|
||||
{
|
||||
"slot_id": "audio.front_ambience",
|
||||
"requirement": [
|
||||
"B-13.5"
|
||||
],
|
||||
"candidate": null,
|
||||
"status": "unverified_required_slot",
|
||||
"creator": "unassigned",
|
||||
"required_variant_count": null,
|
||||
"approved_asset": null
|
||||
},
|
||||
{
|
||||
"slot_id": "audio.back_ambience",
|
||||
"requirement": [
|
||||
"B-13.5"
|
||||
],
|
||||
"candidate": null,
|
||||
"status": "unverified_required_slot",
|
||||
"creator": "unassigned",
|
||||
"required_variant_count": null,
|
||||
"approved_asset": null
|
||||
},
|
||||
{
|
||||
"slot_id": "audio.ui",
|
||||
"requirement": [
|
||||
"B-13.5"
|
||||
],
|
||||
"candidate": null,
|
||||
"status": "unverified_required_slot",
|
||||
"creator": "unassigned",
|
||||
"required_variant_count": null,
|
||||
"approved_asset": null
|
||||
},
|
||||
{
|
||||
"slot_id": "audio.character_voice",
|
||||
"requirement": [
|
||||
"B-13.5"
|
||||
],
|
||||
"candidate": null,
|
||||
"status": "unverified_required_slot",
|
||||
"creator": "unassigned",
|
||||
"required_variant_count": null,
|
||||
"approved_asset": null
|
||||
},
|
||||
{
|
||||
"slot_id": "other_characters",
|
||||
"requirement": [
|
||||
"B-13.1"
|
||||
],
|
||||
"candidate": null,
|
||||
"status": "unverified_required_slot",
|
||||
"creator": "unassigned",
|
||||
"required_variant_count": null,
|
||||
"approved_asset": null
|
||||
},
|
||||
{
|
||||
"slot_id": "restricted_content_unproduced",
|
||||
"requirement": [
|
||||
"B-12.3",
|
||||
"B-13.1",
|
||||
"B-13.2",
|
||||
"B-13.5"
|
||||
],
|
||||
"candidate": null,
|
||||
"status": "not_produced_outside_current_content_scope",
|
||||
"creator": "unassigned",
|
||||
"required_variant_count": null,
|
||||
"approved_asset": null
|
||||
}
|
||||
]
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,85 @@
|
||||
{
|
||||
"validator": "P01 register structure and source coverage, not game acceptance",
|
||||
"passed": true,
|
||||
"checked_at_jst": "2026-10-02T01:24:08+09:00",
|
||||
"source_head": "285528b7a3b1b46a2a67af5aa90d9085539b42f4",
|
||||
"known_condition_v1_denominator": 1291,
|
||||
"historical_schedule_atoms": 18,
|
||||
"original_lines": 258,
|
||||
"original_groups": 75,
|
||||
"original_facilities": 30,
|
||||
"heroines": 7,
|
||||
"heroine_routes": 21,
|
||||
"implemented_whole_atoms": 0,
|
||||
"accepted_whole_atoms": 0,
|
||||
"all_evidence_unverified": true,
|
||||
"final_denominator_established": false,
|
||||
"errors": [],
|
||||
"negative_checks": [
|
||||
{
|
||||
"case": "duplicate_id",
|
||||
"rejected": true,
|
||||
"first_error": "duplicate requirement ID"
|
||||
},
|
||||
{
|
||||
"case": "source_line_deleted",
|
||||
"rejected": true,
|
||||
"first_error": "coverage has missing/duplicate source lines"
|
||||
},
|
||||
{
|
||||
"case": "source_hash_changed",
|
||||
"rejected": true,
|
||||
"first_error": "baseline pin mismatch"
|
||||
},
|
||||
{
|
||||
"case": "frozen_denominator_changed",
|
||||
"rejected": true,
|
||||
"first_error": "frozen denominator mismatch"
|
||||
},
|
||||
{
|
||||
"case": "unsupported_pass",
|
||||
"rejected": true,
|
||||
"first_error": "AR.art.anime_portrait: unsupported PASS runtime"
|
||||
},
|
||||
{
|
||||
"case": "unsupported_not_applicable",
|
||||
"rejected": true,
|
||||
"first_error": "AR.art.anime_portrait: unsupported N/A runtime"
|
||||
},
|
||||
{
|
||||
"case": "partial_implementation_counted",
|
||||
"rejected": true,
|
||||
"first_error": "AR.art.anime_portrait: incomplete implementation counted"
|
||||
},
|
||||
{
|
||||
"case": "unverified_acceptance_counted",
|
||||
"rejected": true,
|
||||
"first_error": "AR.art.anime_portrait: incomplete acceptance counted"
|
||||
},
|
||||
{
|
||||
"case": "all_content_final_claim",
|
||||
"rejected": true,
|
||||
"first_error": "final denominator claimed despite unresolved catalogs"
|
||||
},
|
||||
{
|
||||
"case": "facility_removed",
|
||||
"rejected": true,
|
||||
"first_error": "register denominator changed/duplicated"
|
||||
},
|
||||
{
|
||||
"case": "heroine_route_removed",
|
||||
"rejected": true,
|
||||
"first_error": "register denominator changed/duplicated"
|
||||
},
|
||||
{
|
||||
"case": "schedule_counted_as_function",
|
||||
"rejected": true,
|
||||
"first_error": "register denominator changed/duplicated"
|
||||
},
|
||||
{
|
||||
"case": "altered_percentage",
|
||||
"rejected": true,
|
||||
"first_error": "implementation percentage not recomputable"
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,157 @@
|
||||
{
|
||||
"schema_version": 1,
|
||||
"reviewed_at_jst": "2026-10-02T01:29:36.666245+09:00",
|
||||
"reviewer": "/root/assets_platform_audit",
|
||||
"review_scope": "independent read-only semantic review: source conditions, duplicate identities, encyclopedia/endings, named facility scope, undefined content",
|
||||
"review_limits": "This is a register review, not implementation or game acceptance. Final denominator and final catalog coverage remain unresolved.",
|
||||
"reviewed_files": {
|
||||
"atomic_requirements.json": "82f4b4c0fbf32bd296a83e6c565dd2b64d2449336c9ada7be11742761d4a5cfb",
|
||||
"atomic_denominator_freeze.json": "467e960eb0fe47241edb217f5fa2044f2de304441daf9e51a7b0cb46ffa9194d",
|
||||
"source_atomic_coverage.json": "2ab7b9e6a2536ef248d4f7ccd695bf75a082b47018aca7a5a1b6b706fe1907bc",
|
||||
"atomic_requirements_validation.json": "8dc300b099cfd90fda862f138b6a528d01e9c488a2c67fe07a00a92a70f99175",
|
||||
"build_atomic_requirements.py": "6d6ea9e4216372dbd0f9fed3f885a4481b21a3f539a26f64cf98a6830af984c5"
|
||||
},
|
||||
"source_pins": {
|
||||
"docs/traceability/original/BASELINE.txt": {
|
||||
"expected": "75e4bdbdeb379c9269553f0c776bdb9cc96e01f2d6aa5ca650d132c63a8aeb1a",
|
||||
"observed": "75e4bdbdeb379c9269553f0c776bdb9cc96e01f2d6aa5ca650d132c63a8aeb1a",
|
||||
"matches": true
|
||||
},
|
||||
"docs/traceability/original/requirements.json": {
|
||||
"expected": "eb8ec13aefc3a814644280ebf4dcf8f9c8965c6e2ab24b33f69be65f58b98a57",
|
||||
"observed": "eb8ec13aefc3a814644280ebf4dcf8f9c8965c6e2ab24b33f69be65f58b98a57",
|
||||
"matches": true
|
||||
},
|
||||
"docs/plans/original-completion-2026-10-02/decisions.json": {
|
||||
"expected": "ad0673051b0fa601e525f05128e0b11e2dddd9d4bae6a3e0170bbf47da35efe9",
|
||||
"observed": "ad0673051b0fa601e525f05128e0b11e2dddd9d4bae6a3e0170bbf47da35efe9",
|
||||
"matches": true
|
||||
},
|
||||
"docs/traceability/original/audit-2026-09-29/TRACEABILITY.md": {
|
||||
"expected": "caed024fe5b35a4636975646a32bb3f0960e24eabfb5e9688d0d78f8592d5b5f",
|
||||
"observed": "caed024fe5b35a4636975646a32bb3f0960e24eabfb5e9688d0d78f8592d5b5f",
|
||||
"matches": true
|
||||
},
|
||||
"docs/plans/original-completion-2026-10-02/execution/IMPLEMENTATION_PLAN.initial.md": {
|
||||
"expected": "f9cb3f61e4cb020e37f792c615dfbd4794763884a8ec528de14c3469cf4e5248",
|
||||
"observed": "f9cb3f61e4cb020e37f792c615dfbd4794763884a8ec528de14c3469cf4e5248",
|
||||
"matches": true
|
||||
}
|
||||
},
|
||||
"checks": {
|
||||
"feature_atom_count_1291": true,
|
||||
"historical_schedule_atom_count_18": true,
|
||||
"denominator_ids_unique_and_match_feature_rows": true,
|
||||
"freeze_matches_register": true,
|
||||
"source_pins_current": true,
|
||||
"source_condition_coverage_all_258_lines": true,
|
||||
"source_feature_groups_71_schedule_groups_4": true,
|
||||
"chapter10_facilities_30": true,
|
||||
"chapter10_all_thirteen_value_fields_present": true,
|
||||
"chapter10_visual_slots_360_present": true,
|
||||
"heroines_7_and_route_usages_21_present": true,
|
||||
"17_approved_refinement_merges": true,
|
||||
"no_final_content_denominator_claim": true,
|
||||
"all_unaccepted_evidence_retained": true,
|
||||
"supplement_applicability_open_expansion_retained": true,
|
||||
"external_validator_reports_no_errors": true
|
||||
},
|
||||
"all_scoped_checks_passed": true,
|
||||
"known_condition_denominator": 1291,
|
||||
"historical_schedule_atoms_excluded_from_game_percent": 18,
|
||||
"final_denominator_approved": false,
|
||||
"game_acceptance_added": false,
|
||||
"requirement_files_modified_by_review": false,
|
||||
"count_semantics": {
|
||||
"thirty_facility_value_fields": 390,
|
||||
"thirty_facility_visual_usage_slots": 360,
|
||||
"known_heroine_route_usages": 21,
|
||||
"route_usage_is_exact_cg_file_count": false,
|
||||
"visual_usage_slot_is_unique_image_file": false
|
||||
},
|
||||
"exact_condition_text_duplicates_reviewed": [
|
||||
{
|
||||
"condition": "対象施設は原本指定の観察機能に対応する。",
|
||||
"ids": [
|
||||
"AR.facility.adult_private_room.observation",
|
||||
"AR.facility.public_bath.observation",
|
||||
"AR.facility.vip_suite.observation"
|
||||
],
|
||||
"review": "different facility identity; do not merge"
|
||||
}
|
||||
],
|
||||
"missing_checks": {
|
||||
"facility_fields": [],
|
||||
"visual_slots": [],
|
||||
"routes": []
|
||||
},
|
||||
"findings": [
|
||||
{
|
||||
"id": "ASR-01",
|
||||
"priority": "required_before_final_coverage",
|
||||
"topic": "chapter10 finite thirty versus all named facilities and infrastructure",
|
||||
"source_lines": [
|
||||
46,
|
||||
49,
|
||||
50,
|
||||
86,
|
||||
176,
|
||||
177,
|
||||
190,
|
||||
191,
|
||||
192,
|
||||
193,
|
||||
248
|
||||
],
|
||||
"finding": "Chapter10 thirty-facility field and visual expansion is complete as a finite subset. The original additionally names reception/settlement, vault, purchasing, dark bank, corridors, stairs, staff doors, linen chute and hidden doors. Their normal features exist as atoms, but applicability of F01-F10 and Lv/front/back/exterior/interior visuals is unresolved.",
|
||||
"resolution": "Retain open applicability expansion; do not certify final all-facility field/art coverage, and do not automatically infer twelve slots or thirteen fields for every infrastructure item.",
|
||||
"current_state": "recorded in updated open_expansion_register; final denominator remains unresolved"
|
||||
},
|
||||
{
|
||||
"id": "ASR-02",
|
||||
"priority": "acceptance_operating_note",
|
||||
"topic": "set contracts and finite known usage identities versus unspecified artwork quantities",
|
||||
"source_lines": [
|
||||
9,
|
||||
30,
|
||||
125,
|
||||
235,
|
||||
242,
|
||||
243,
|
||||
245,
|
||||
252
|
||||
],
|
||||
"finding": "The seven identities and twenty-one route usages are explicit. Main portrait and other-route tone asset atoms remain set contracts: every required known identity and both stated tone axes must pass. Individual scene, expression, outfit, voice and CG counts are not defined. A main portrait set or route flag alone cannot certify actual content.",
|
||||
"resolution": "Maintain per-usage asset/content mapping under D08/D09; require all members and all source tone axes. Split finite known usage assertions during future denominator revision if independently adjudicated; do not invent asset counts or equate twenty-one route usages to twenty-one CG files.",
|
||||
"current_state": "open D08/D09 and content-not-produced rejection are retained; no denominator revision requested in this review"
|
||||
},
|
||||
{
|
||||
"id": "ASR-03",
|
||||
"priority": "required_before_full_clear_acceptance",
|
||||
"topic": "all endings and encyclopedia complete",
|
||||
"source_lines": [
|
||||
9
|
||||
],
|
||||
"finding": "AR.progression.all_endings and AR.progression.encyclopedia_complete preserve source set obligations with D08 pending. No endpoint/encyclopedia target catalog was supplied. They cannot be accepted solely from a generic flag, partial route list, or collection implementation.",
|
||||
"resolution": "Pin approved target catalogs, individual IDs, acquisition conditions, normal-route evidence and save/restart checks before accepting the set contract; retain missing targets as unresolved.",
|
||||
"current_state": "correctly unverified; final_denominator_established=false"
|
||||
},
|
||||
{
|
||||
"id": "ASR-04",
|
||||
"priority": "reviewed_no_duplicate_issue",
|
||||
"topic": "duplicates and refinements",
|
||||
"source_lines": [
|
||||
118,
|
||||
161,
|
||||
165,
|
||||
179,
|
||||
235,
|
||||
242
|
||||
],
|
||||
"finding": "Seventeen A03/A04/A05 refinements retain the prior identity rather than add a second weight. Repeated source references for price unlocking, markings and SD walk/loops are merged. Three identical observation condition texts apply to distinct named facilities and are not redundant.",
|
||||
"resolution": "Preserve entity identity when comparing conditions; generic contracts and configured per-facility values remain separate duties.",
|
||||
"current_state": "no additional mandatory duplicate correction identified in this scoped review"
|
||||
}
|
||||
],
|
||||
"completion_verdict": "known-condition register reviewed with unresolved applicability and content catalogs; no final all-original completion verdict"
|
||||
}
|
||||
@@ -0,0 +1,101 @@
|
||||
{
|
||||
"plan_date_jst": "2026-10-02",
|
||||
"source_head": "285528b7a3b1b46a2a67af5aa90d9085539b42f4",
|
||||
"production_root": "/Users/korizatou114/Projects/とりにく工房ゲーム制作部",
|
||||
"original_source_priority": "specified basic source text, explicit user amendments, annex reference, derived docs",
|
||||
"source_pins": {
|
||||
"docs/traceability/original/BASELINE.txt": "75e4bdbdeb379c9269553f0c776bdb9cc96e01f2d6aa5ca650d132c63a8aeb1a",
|
||||
"docs/traceability/original/baseline_metadata.json": "2c672eef85ee46e7f12e3b9cc07a0513031167b57869ac33c9f287255ee7d0b6",
|
||||
"docs/traceability/original/requirements.json": "eb8ec13aefc3a814644280ebf4dcf8f9c8965c6e2ab24b33f69be65f58b98a57",
|
||||
"docs/plans/original-completion-2026-09-29/work_packages.json": "4c2cd6aecd232ab4e1d1662f4cd43bf86fe29fd5b98a8786ff1d116418a19d0d",
|
||||
"docs/plans/original-completion-2026-09-29/requirement_coverage.json": "2b5b57432157f1674e5021f79295835f57a7138832a49ffeabc9effc72afbfa4",
|
||||
"artifacts/original-annex-A-drive-retrieval-2026-09-30-r01/retrieved-readable-annex-A.md": "93e4d644068386d63071332d9787f59184bc81bdc2efdc0b420da244f350bb8c",
|
||||
"artifacts/original-annex-A-schema-comparison-audit-2026-09-30/summary.json": "01217815ce8b18e9b5a39f4b761f912a14e01960ac99eefc9e86e6a08eb2ceb4",
|
||||
"artifacts/facility-need-pricing-production-scale-adoption-2026-10-01-r01/receipt.json": "63b59cd28f563278bb3bf55e2bd9ffc94afdc5542d570a90c79f118c31bb1cfd",
|
||||
"artifacts/facility-need-pricing-production-scale-adoption-2026-10-01-r01/after.json": "44e2752aa435479188f20beaa9929d518623670c98d1a415db6c63aef828ee33"
|
||||
},
|
||||
"source_inventory_files_verified": 1396,
|
||||
"source_inventory_changed_paths": [],
|
||||
"facility_audit_current_pins_verified": {
|
||||
"scripts/data/facility_definition.gd": "e90d56f7eab69c2a75238de465925c1c6df57ee6d44b3b81e7f6fcfd069af0a1",
|
||||
"scripts/data/facility_catalog.gd": "7bb0c5085ddb361d384314cb219620d1483840942bc3f0f46dcb9dfa29d56e29",
|
||||
"docs/plans/original-completion-2026-09-29/execution/original_facility_mapping.json": "df0c933220449c8bb3e0fd942ad6f1dee49075abdd6587c4856022e12e3665cf",
|
||||
"data/facilities/adult_private_room.tres": "d8501586416b5c57a8b3b3b2afd1c0143428d8a4fa1df7112e995bd36649df6d",
|
||||
"data/facilities/cafeteria.tres": "86d540fe2d78b9dcdbe5a0af6e4a001f4cec54934636ed01deb25a943c79128a",
|
||||
"data/facilities/expansion/black_market.tres": "c209d734c82487f6df4c03714618865d6a5f1d2cf991dc0056fe4bbad8f03a42",
|
||||
"data/facilities/expansion/chapel.tres": "9b30a7c51712e7bb99c17870d1f03e3429ea9279851d88284545bb2eb5042fcb",
|
||||
"data/facilities/expansion/cleaning_room.tres": "ae366cf67cbf117db7989c6b76092eb5a65229b93d35999a40d19b8260f95ad8",
|
||||
"data/facilities/expansion/clinic.tres": "339cf22c68d7ab3cb1514b9e8d2ea63dc0772fe93918d70767c11cf7b6ee0e16",
|
||||
"data/facilities/expansion/cold_storage.tres": "1aac824147b52b41e124d2ab2ce63c3bac58de2ec7fc447a5806a1cf9fa9ad99",
|
||||
"data/facilities/expansion/general_store.tres": "76872e0375648b43f05fdc5581ccabe4aa6a166674c3b523980dd1cf0b6e6dc1",
|
||||
"data/facilities/expansion/guild_counter.tres": "93e8736da4aab8a17b1c8e7831c9a7b8de7f32d0ec251aa9aa5c8cf859c8fd7b",
|
||||
"data/facilities/expansion/laundry.tres": "2518ea2e5da12cf4f2997502bf50f00dd8145caca153fb3486b44b99881fdab4",
|
||||
"data/facilities/expansion/maintenance_workshop.tres": "8bd10332a9f45a252febdc626f22f90507317fb4cff5b18b2526aacd1f1a176b",
|
||||
"data/facilities/expansion/mana_control_room.tres": "55e4c52a049ff61ddf3d081ccb990a8572b64bc7cf37d3a0d52ca5a08cc32f25",
|
||||
"data/facilities/expansion/premium_room.tres": "6665090e041f36df5809314cd171be228a94bf77d7e93f5b70be648db4705d98",
|
||||
"data/facilities/expansion/private_bath.tres": "241b807e7fa3a1b9dc4bda0d2495e637155cdaea89a4234e37a121f00098499c",
|
||||
"data/facilities/expansion/secret_tavern.tres": "f0a1372931078fa0f93dd5b9cec6df6b137975479aa706633fdf6a9be4cf1f62",
|
||||
"data/facilities/expansion/staff_dormitory.tres": "8d7c7cd6dfe98d137f7fef62e35eb31f82f1ca282216a40df54ac81728101271",
|
||||
"data/facilities/expansion/tea_lounge.tres": "77d1ace36c5e84f80c0e6d563ae973c7ff1cd12b56956afd4895cdcdd5771a67",
|
||||
"data/facilities/expansion/underground_guest_room.tres": "cdfd0f8cd21812a9972485926dff27daaa6718af16aac1651894728b087bef9e",
|
||||
"data/facilities/expansion/vip_private_salon.tres": "1c94a7a70393262fba82112c3f4a0cb67ad22728664f5ca408f450049d3657a7",
|
||||
"data/facilities/expansion/waste_processing.tres": "9954855c7a07e425376a220c589989beb6ec95da3c5331316ef3615a60b45f1d",
|
||||
"data/facilities/guest_corridor.tres": "d77ccf267e20a26ed70527d29e8d54e6fcab1df213f02d5a4e28ead4f705276b",
|
||||
"data/facilities/guest_elevator.tres": "ad4fb3c439ac4ed90fe52f07e29ed7b4ff654c54d1c400c7c8e226da77127af7",
|
||||
"data/facilities/infrastructure/guest_stairs.tres": "757c687b1ebe2a3f237fb28092cd5f01591f15b2bcd1b4e639042a24635a92c6",
|
||||
"data/facilities/infrastructure/service_stairs.tres": "637217d867bcc99669d27c54b6d2c391c82a4a17356ab9a80c897ccaee38622c",
|
||||
"data/facilities/kitchen.tres": "38197aa8a3e90c03a82e9ccdc88bb444af4eeb0349c41a39e0bdd7b188e7d5d3",
|
||||
"data/facilities/massage_room.tres": "75ecf845fb3cfa8d3150f6b0dc90e901953469af512611384b70cc4878d21e85",
|
||||
"data/facilities/public_bath.tres": "8ec259f48ed75c8ab2e5154a1caccf19f6be04331adbe774b877f0a2d8b2f778",
|
||||
"data/facilities/reception.tres": "bed3de120a0e98c539771d1bb189111d095712f8ae25dfdf7cb2983efc0dee9f",
|
||||
"data/facilities/rescue/infirmary.tres": "c3e861533971e369068ae9e42f71b660fa4ab3893c7c1f798fc367893befd943",
|
||||
"data/facilities/security_station.tres": "69f3f518df7714d00c8e954e9c7405aa44b683dd274181cc3da832cc956815b3",
|
||||
"data/facilities/service_corridor.tres": "5d79fbde201d657740efcba924f595bd7075e069b344d708e424dbebbdc2f254",
|
||||
"data/facilities/service_elevator.tres": "35d552d5b96065413feccb50847e3cd7173e673896ce65afba238f90abc25132",
|
||||
"data/facilities/staff_door.tres": "a993058bfd809ccdbbb9a61b9850259d7bf6e60003e57ccfa3972f6c29170752",
|
||||
"data/facilities/staff_waiting_room.tres": "5c7a2f7f7c508102c4ee89401ad6348191b57d1ff65538acb9d450e464a3a46f",
|
||||
"data/facilities/standard_room.tres": "89f0a3a861d66afa4c1338ff1ec919685cf338b93b8da9a2387ce85e7e5bc336",
|
||||
"data/facilities/tavern.tres": "eb494ae6ab0d1b9e88ae0a38944609702e43c81a370a29cfb030af9ae46f523c",
|
||||
"data/facilities/warehouse.tres": "d0fcf0528da66da7b354ab5efaafe2483a7a76e05b324db255700ef73cdfc791"
|
||||
},
|
||||
"original_requirement_groups": 75,
|
||||
"feature_or_content_groups": 71,
|
||||
"historical_schedule_groups": 4,
|
||||
"legacy_package_state_counts": {
|
||||
"completed": 1,
|
||||
"in_progress": 17,
|
||||
"planned": 12
|
||||
},
|
||||
"revised_package_state_counts": {
|
||||
"completed": 1,
|
||||
"in_progress": 25,
|
||||
"planned": 4
|
||||
},
|
||||
"full_regression": {
|
||||
"suites": 155,
|
||||
"checks": 12310,
|
||||
"test_failures": 0,
|
||||
"evidence": "artifacts/facility-need-pricing-production-scale-adoption-2026-10-01-r01/receipt.json",
|
||||
"new_engine_runs_in_plan_creation": 0
|
||||
},
|
||||
"facility_counts": {
|
||||
"normal_catalog_declared": 16,
|
||||
"missing_definition": 13,
|
||||
"definition_only": 1
|
||||
},
|
||||
"overall_atomic_implementation_percent": null,
|
||||
"overall_atomic_acceptance_percent": null,
|
||||
"atomic_denominator": null,
|
||||
"candidate_only_not_adopted": [
|
||||
"P05 paid auto-purchase corrected20 files and QA driver",
|
||||
"Path invalid transit snapshot partial-success guard"
|
||||
],
|
||||
"current_head_windows_native_verified": false,
|
||||
"current_head_windows_package_available": false,
|
||||
"long_play_acceptance_verified": false,
|
||||
"performance_acceptance_passed": false,
|
||||
"goal_scope_document": "/Users/korizatou114/.codex/attachments/d0e14500-1529-41c7-9c95-b818de1b4b06/pasted-text-1.txt",
|
||||
"source_recapture_note": "Uses retained readable snapshots; no fresh Drive fetch in this planning turn",
|
||||
"revised_state_note": "planned with recorded partial execution becomes in_progress; no package promoted to completed",
|
||||
"scope_note": "Plan and static evidence reconciliation; not a new game acceptance run"
|
||||
}
|
||||
@@ -0,0 +1,734 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Build the reviewed source-condition register, without awarding game acceptance.
|
||||
Only the new P01 deliverables are written. Stable semantic IDs support external evidence joins.
|
||||
"""
|
||||
import hashlib, json, subprocess
|
||||
from datetime import datetime, timezone, timedelta
|
||||
from pathlib import Path
|
||||
ROOT = Path(__file__).resolve().parents[3]
|
||||
OUT = Path(__file__).resolve().parent
|
||||
BASE = 'docs/traceability/original/BASELINE.txt'
|
||||
GROUPS = 'docs/traceability/original/requirements.json'
|
||||
DECISIONS = 'docs/plans/original-completion-2026-10-02/decisions.json'
|
||||
SUPPLEMENT = 'docs/traceability/original/audit-2026-09-29/TRACEABILITY.md'
|
||||
PLAN = 'docs/plans/original-completion-2026-10-02/execution/IMPLEMENTATION_PLAN.initial.md'
|
||||
VIEWS = ['source_condition','definition','runtime','normal_ui','save_migration','boundary_verification','assets_platform','evidence']
|
||||
sha = lambda path: hashlib.sha256((ROOT/path).read_bytes()).hexdigest()
|
||||
pins = {p:sha(p) for p in [BASE,GROUPS,DECISIONS,SUPPLEMENT,PLAN]}
|
||||
lines = (ROOT/BASE).read_text().splitlines()
|
||||
groups = json.loads((ROOT/GROUPS).read_text())
|
||||
coverage_groups = json.loads((OUT/'requirement_coverage.json').read_text())
|
||||
group_by_id={g['id']:g for g in groups}
|
||||
records={}; merges=[]
|
||||
head=subprocess.check_output(['git','rev-parse','HEAD'],cwd=ROOT,text=True).strip()
|
||||
now=datetime.now(timezone(timedelta(hours=9))).isoformat(timespec='seconds')
|
||||
normal_entries={
|
||||
'P00':'タイトル/起動・配布情報','P01':'要件別受入と内容台帳','P03':'保存/読込/終了/再起動',
|
||||
'P04':'時間経過と日付境界','P05':'通常営業/資金/仕入れ/決算','P06':'マナ管理/通常営業',
|
||||
'P07':'天候/環境/流行と通常営業','P08':'建設パレット/配置/移動/撤去','P09':'施設詳細/営業管理/通常利用',
|
||||
'P10':'宿泊/飲食施設の建設と通常営業','P11':'衛生/娯楽/礼拝施設の建設と通常営業',
|
||||
'P12':'裏層施設の建設と業務','P13':'特殊施設の建設と運用','P14':'通常移動とEV/階段/ワープ',
|
||||
'P15':'客来店から行動観察','P16':'雇用/シフト/休息/救護','P17':'従業員画面/人物加入/配置/成長',
|
||||
'P18':'人物詳細/関係/決闘','P19':'人物イベントと分岐','P20':'新規ゲーム/章進行/エンド/図鑑',
|
||||
'P21':'防衛開始から結果','P22':'素材台帳/必要量/差替仕様','P23':'断面マップとユニット表示',
|
||||
'P24':'通常画面とインスペクター','P25':'完成ドット素材/施設差分/音を使う通常画面',
|
||||
'P26':'完成シナリオ/立ち絵/CG/ボイスを使う人物・イベント画面','P27':'同一候補版での要件別受入',
|
||||
'P28':'新規から本編/全要素と実測','P29':'同一Windows配布版の実機起動/操作/保存再開'}
|
||||
|
||||
def add(key, condition, line_nums=None, package='P27', group_ids=None, source=BASE,
|
||||
authority='designated_original_saved_copy', decision=None, supplement=None,
|
||||
pending=None, entity=None, scope='feature_or_content', replace=False, acceptance=None):
|
||||
rid='AR.'+key
|
||||
line_nums=list(line_nums or [])
|
||||
if group_ids is None:
|
||||
group_ids=[g['id'] for g in groups if source==BASE and set(line_nums)&set(g['source_lines'])]
|
||||
if rid in records:
|
||||
rec=records[rid]
|
||||
if rec['condition'] != condition:
|
||||
merges.append({'kept_id':rid,'alias_source':source,'alias_lines':line_nums,
|
||||
'alias_condition':condition,'reason':'same specification condition; source refinement is counted once'})
|
||||
if replace: rec['condition']=condition; rec['acceptance']['condition_assertion']=condition
|
||||
else:
|
||||
rec={
|
||||
'id':rid,'condition':condition,'scope':scope,'mandatory':True,'equal_count_weight':1,
|
||||
'source_anchors':[],'source_sha256':pins[BASE],'user_amendment_refs':[],
|
||||
'source_group_ids':[],'supplement_ids':[],'entity':entity,
|
||||
'source_head':head,'primary_package':package,'package':package,
|
||||
'work_packages':[package],'responsible_role':'実装担当・素材担当(機能別担当の割当は要再監査)',
|
||||
'reviewer':{'required_role':'実装者と独立した受入判定者','assigned':None,'assignment_state':'unassigned'},
|
||||
'normal_entry':normal_entries[package], 'implementation_paths':[],
|
||||
'save_contract':{'state':'not_reaudited','requirement':'関連状態を保存/終了/再起動で維持し、旧版拒否時は原状を保持する。適用外は判定者が出典付きで判断する。'},
|
||||
'applicable_completion_views':VIEWS.copy(),'applicability_state':'conservative_pending_review',
|
||||
'view_results':{v:{'state':'unverified','implementation_state':'not_reaudited','evidence_refs':[],'reviewer':None,'not_applicable_reason':None} for v in VIEWS},
|
||||
'acceptance':{'condition_assertion':acceptance or condition,
|
||||
'normal_operation':'指定通常入口から到達し条件の成立を現Runtimeと画面で照合する。',
|
||||
'persistence':'関連状態の保存/終了/再起動と旧版・欠損・破損拒否を確認する。',
|
||||
'boundaries':'条件の直前/一致/直後、停止/中断/重複、参照喪失の適用範囲を試験設計する。',
|
||||
'source_and_assets':'同一採用版のソースSHA、必要完成素材、配布対象を関連づける。',
|
||||
'evidence_policy':'旧群の状態と過去テスト件数から合格を転記しない。モデル試験、通常操作、Windows実機は区別する。'},
|
||||
'evidence_refs':[],'historical_evidence_refs':[],
|
||||
'implementation_state':'not_reaudited','acceptance_state':'unverified','status':'unverified',
|
||||
'pending_decision_ids':[],'value_contract':None,'decision_dependencies':[], 'difference_state':'not_reaudited','asset_state':'not_reaudited',
|
||||
'updated_at':now}
|
||||
records[rid]=rec
|
||||
anchor={'path':source,'lines':line_nums,'sha256':pins[source],'authority':authority}
|
||||
if decision: anchor['decision_id']=decision
|
||||
if supplement: anchor['supplement_id']=supplement
|
||||
if anchor not in rec['source_anchors']:rec['source_anchors'].append(anchor)
|
||||
rec['source_group_ids']=sorted(set(rec['source_group_ids']+group_ids))
|
||||
rec['work_packages']=sorted(set(rec['work_packages']+[package]))
|
||||
if decision:rec['user_amendment_refs']=sorted(set(rec['user_amendment_refs']+[decision]))
|
||||
if supplement:rec['supplement_ids']=sorted(set(rec['supplement_ids']+[supplement]))
|
||||
if pending:
|
||||
ids=([pending] if isinstance(pending,str) else pending)
|
||||
rec['pending_decision_ids']=sorted(set(rec['pending_decision_ids']+ids))
|
||||
for did in ids:
|
||||
dependency={'decision_id':did,'kind':'informational_explicitly_unset_optional_extension' if did=='D03' else 'required_for_dependent_acceptance','must_not_block_independent_work':True}
|
||||
if dependency not in rec['decision_dependencies']:rec['decision_dependencies'].append(dependency)
|
||||
return rec
|
||||
|
||||
# A line can contain multiple independently judgeable clauses; every clause below is curated.
|
||||
# Repeated semantic keys attach another source anchor instead of another denominator unit.
|
||||
SPEC = '''
|
||||
3|identity.code|プロジェクトコードはProject Yggdrasil。|P00
|
||||
4|identity.title|仮タイトルはThe Tree。|P00
|
||||
5|identity.fantasy_management|ファンタジー世界樹経営シミュレーションのゲームとして成立する。|P20
|
||||
5|identity.tower_building|タワービルディングのゲームとして成立する。|P20
|
||||
5,32|identity.age_rating|原本の成人向け区分を台帳と配布設定で管理する。|P29
|
||||
6|platform.windows|同一Windows配布版でゲームを起動し通常操作できる。|P29|D10
|
||||
8|progression.main_duration|本編クリアの実プレイ時間は20〜30時間。|P28
|
||||
9|progression.full_duration|全要素クリアの実プレイ時間は50時間以上。|P28
|
||||
9|progression.all_endings|全エンドへ通常進行で到達し全エンド取得を判定できる。|P20|D08
|
||||
9|progression.encyclopedia_complete|図鑑の全対象を取得しコンプリートを判定できる。|P20|D08
|
||||
10,37,216|world.cutaway|ゲームマップは2Dサイドビューの断面図で表示する。|P23
|
||||
12|world.tree_setting|地上と地下を同一世界樹として管理する。|P20
|
||||
12|world.dual_management|地上領域と地下領域を同時に管理・拡張できる。|P07
|
||||
13|world.autonomous_observation|客と従業員の自律行動を通常画面で観察できる。|P24
|
||||
13|world.intervention|管理者の介入が人物の進行へ反映される。|P19|D08
|
||||
14|progression.tree_growth|フロアの積上げに伴い世界樹が成長する。|P20|D04
|
||||
14|progression.height_authority|高さの成長が表の権威増加へつながる。|P20|D04
|
||||
14|progression.depth_wealth|地下の深さの成長が裏の富増加へつながる。|P20|D04
|
||||
16|construction.normal_build|通常操作で施設を建設できる。|P08
|
||||
16|construction.normal_links|通常操作で通路・昇降設備を配置できる。|P14
|
||||
16,24|ui.layer_switch|通常UIで表層と裏層を即時に切り替える。|P07
|
||||
17|guest.diverse_arrival|多様な客が通常営業で来店する。|P15
|
||||
17,25|guest.autonomous_ai|客は欲求に従い自律的に行動する。|P15
|
||||
25|staff.autonomous_ai|従業員は全キャラクター自律AIの対象として業務・移動・休息を実行する。|P16
|
||||
19,26|economy.front_faith|表の健全サービスで信仰を獲得する。|P20|D04
|
||||
19|service.healing|治療サービスを通常業務として提供する。|P12|D04
|
||||
19|economy.front_gold|表のサービスで表資金を獲得する。|P05
|
||||
20,26|guest.underground_addiction|地下のサービス利用で客の依存度が上がる。|P15|D04
|
||||
20|economy.back_gold|裏のサービスで裏資金を獲得する。|P05
|
||||
21|staff.back_routes|裏層の動線を確保して職員業務へ接続する。|P16
|
||||
21,34|underground.blackmail|記録した根拠を使う脅迫取引の状態と収支を管理する。|P19|D08
|
||||
21,34|underground.trade_people|ユニット売却の状態と収支を管理する。|P16|D04
|
||||
22|progression.rank_unlock|世界樹ランク上昇で高度な施設を解禁する。|P20|D04
|
||||
22|defense.repulse|防衛力により外敵を撃退できる。|P21|D04
|
||||
24,41|world.two_slots|全グリッド座標に独立した表/裏の二スロットがある。|P07
|
||||
24,42|world.guest_front_visible|客は表層のみを認識する。|P07
|
||||
24,43|world.guest_back_hidden|客は裏層のユニットと機能を認識しない。|P07
|
||||
25|traffic.elevator_congestion|昇降機配置が悪いと渋滞が発生する。|P14
|
||||
25,67,109|staff.shift_day|各施設に昼番06:00〜18:00のスロットを設定する。|P16
|
||||
25,67,109|staff.shift_night|各施設に夜番18:00〜06:00のスロットを設定する。|P16
|
||||
26|progression.faith_story|信仰をストーリー進行へ反映する。|P20|D04
|
||||
26|guest.addiction_all_wealth|依存の進行により原本指定の全財産支出へつながる。|P15|D04
|
||||
26,195|guest.addiction_budget_priority|依存客は生存費より娯楽費を優先する。|P15|D04
|
||||
28|narrative.tree_origin|舞台は旧魔王城跡地に生えた世界樹。|P20|D08
|
||||
29|narrative.protagonist_past|主人公には勇者として使い捨てられた背景がある。|P20|D08
|
||||
29|narrative.protagonist_goal|宿屋の主として再起し真の王への即位を目指す進行がある。|P20|D08
|
||||
33|narrative.route_tones|原本3ルートの内容と表現が指定方向性を満たす。内容が未制作なら受入しない。|P19|D08
|
||||
34|underground.observation|原本の観察機能を状態と通常入口で管理する。|P19|D08
|
||||
37|world.unit_grid|マップを1ブロック単位のグリッドで管理する。|P07
|
||||
38|world.above_ground|Y>0を地上(幹・枝)として扱う。|P07
|
||||
38|world.below_ground|Y<0を地下(根)として扱う。|P07
|
||||
38|world.ground_boundary|Y=0を地面の境界として扱う。|P07
|
||||
39,164|construction.support|地面または他の完成済み施設・幹へ隣接して建設する。|P08
|
||||
39|construction.expand_area|進行に伴い建設可能領域を拡張する。|P20|D04
|
||||
42|world.front_guests|客ユニットは表層で行動する。|P07
|
||||
42|world.front_service_staff|接客中の従業員は表層で業務する。|P16
|
||||
43|world.back_waiting_staff|待機中の従業員は裏層にいる。|P16
|
||||
43|world.back_moving_staff|移動中の従業員は裏層を使う。|P16
|
||||
43|world.back_security|警備員は裏層を使う。|P16
|
||||
43|world.back_goods|運搬物資は裏層を通る。|P12
|
||||
43|world.back_observer|観察中の管理者状態を裏層へ対応付ける。|P19
|
||||
44,216|ui.front_back_transparency|表層モードでは裏層を半透明に表示する。|P07
|
||||
44,216|ui.back_front_wireframe|裏層モードでは表層をワイヤーフレーム表示する。|P07
|
||||
47|environment.beauty|景観度を計算し客満足度へ反映する。|P22|D04
|
||||
47|environment.cleanliness|清潔度を計算し客満足度へ反映する。|P22|D04
|
||||
47|environment.noise|騒音度を計算し客満足度へ反映する。|P22|D04
|
||||
49|infrastructure.linen_chute|リネンシュートを裏層で設置・運用できる。|P12|D04
|
||||
49|infrastructure.hidden_door|隠し扉を裏層で設置・運用できる。|P13|D04
|
||||
50,177|infrastructure.staff_door_crossing|施設奥の職員ドアを通じ表層と裏層を移動する。|P14
|
||||
50|infrastructure.staff_door_no_guest|客は従業員ドアを通過できない。|P14
|
||||
52,174|facility.peeping_room.target_placement|観察部屋は指定された表層対象の裏に限定して建設する。|P13
|
||||
52,174|facility.peeping_room.corridor_loss|観察部屋の占有グリッドは裏通路として利用できない。|P13
|
||||
53|observation.alternate_view|観察機能には通常マップと異なる専用視点がある。|P19
|
||||
53|observation.recorder|記録係の配置により根拠データを収集する。|P19|D04
|
||||
55|environment.ground_light|地上に採光ボーナスがある。|P22|D04
|
||||
55|environment.ground_mana|地上にマナ濃度ボーナスがある。|P06|D04
|
||||
55|guest.ground_classes|地上は一般市民・貴族・聖職者向けとなる。|P15|D04
|
||||
56|environment.underground_sound|地下に遮音ボーナスがある。|P22|D04
|
||||
56|environment.underground_concealment|地下に隠蔽ボーナスがある。|P22|D04
|
||||
56|guest.underground_classes|地下は犯罪者・魔物・依存客向けとなる。|P15|D04
|
||||
59|time.scale|実時間10秒がゲーム内1時間に対応する。|P04
|
||||
60|time.speeds|Pause/x1/x2/x3の再生速度を選択できる。|P04
|
||||
60|time.pause_build|Pause中も建設できる。|P08
|
||||
60|time.pause_commands|Pause中も指示できる。|P24
|
||||
62|time.morning_phase|朝フェーズは06:00〜10:00。|P04
|
||||
62|traffic.morning_down_rush|朝に下りラッシュが生じる。|P14
|
||||
62|guest.morning_checkout|朝にチェックアウトが発生する。|P15
|
||||
62|guest.morning_breakfast|朝に朝食需要が発生する。|P15
|
||||
62|staff.morning_arrival|朝に昼番が出勤する。|P16
|
||||
62|staff.morning_cleaning|朝に清掃業務を実行する。|P16
|
||||
63|time.day_phase|昼フェーズは10:00〜16:00。|P04
|
||||
63|guest.day_arrival|昼に新規客が来店する。|P15
|
||||
63|guest.day_circulation|昼に客が施設を回遊する。|P15
|
||||
63|time.day_building|昼に営業が安定し建設に適するゲームサイクルが成立する。|P28|D04
|
||||
64|time.evening_phase|夕フェーズは16:00〜20:00。|P04
|
||||
64|traffic.evening_up_rush|夕に上りラッシュが生じる。|P14
|
||||
64|guest.evening_bath_demand|帰還した冒険者が夕に大浴場へ向かう。|P15
|
||||
64|guest.evening_food_demand|帰還した冒険者が夕に食堂へ向かう。|P15
|
||||
64|staff.evening_shift_handover|18:00に夜番へ交代する。|P16
|
||||
64,69|staff.shift_route_congestion|交代時刻の裏層動線が悪いと職員の渋滞により業務が滞る。|P16
|
||||
65|time.night_phase|夜フェーズは20:00〜06:00。|P04
|
||||
65|guest.night_sleep|健全客は夜に睡眠行動を取る。|P15
|
||||
65|guest.night_underground|依存客は夜に地下へ向かう。|P15
|
||||
65|guest.need_sexual_night_peak|夜に原本の地下サービス欲求解消がピークとなる。|P15|D04
|
||||
65|events.ntr_night_peak|夜に原本のNTRイベント発生がピークとなる。|P19|D04
|
||||
68|staff.work_stamina|勤務で体力が減少する。|P16|D04
|
||||
68,109|staff.rest_stamina|休憩または非番の待機室等で体力が回復する。|P16|D04
|
||||
68,101,110|staff.zero_collapse|体力0で気絶状態となる。|P16
|
||||
68,110|staff.back_rescue|気絶者を救護班が裏ルートから回収する。|P16
|
||||
68|staff.seen_collapse_reputation|客が気絶者を目撃すると評判が低下する。|P16|D04
|
||||
71|economy.midnight_report|毎日00:00に決算レポートを表示する。|P05
|
||||
72|economy.settlement_calculation|日次決算で表裏の売上から全対象経費を引く。|P05
|
||||
72|progression.three_deficits_gameover|赤字決算が3回連続するとゲームオーバーとなる。|P20
|
||||
74|weather.rain_arrivals|雨で客足が減る。|P22|D04
|
||||
74|weather.rain_stay|雨で滞在が延長する。|P22|D04
|
||||
74|weather.storm_no_arrivals|嵐で新規客がゼロになる。|P22
|
||||
74|weather.fog_magic|マナの霧で魔法効率が上がる。|P22|D04
|
||||
74|weather.fog_excitation|マナの霧で興奮度が上がる。|P22|D04
|
||||
75|news.cadence|数日おきに流行ニュースが発生する。|P22|D04
|
||||
75|news.class_boost|ニュースで特定客層の売上が増える。|P22|D04
|
||||
75|news.facility_boost|ニュースで特定施設の売上が増える。|P22|D04
|
||||
78|guest.budget|客は所持金Budgetを持つ。|P15
|
||||
78|guest.loot|客は戦利品Lootを持つ。|P15
|
||||
79|guest.need_sleep_range|睡眠欲を0〜100で保持する。|P15
|
||||
17,79|guest.need_sleep_relief|睡眠欲を客室により解消する。|P15
|
||||
79|guest.need_sexual_range|原本の地下サービス欲求を0〜100で保持する。|P15
|
||||
17,79|guest.need_sexual_relief|原本の地下サービス欲求を対象施設で解消する。|P15
|
||||
79|guest.need_food_range|食欲を0〜100で保持する。|P15
|
||||
17,79|guest.need_food_relief|食欲を食堂により解消する。|P15
|
||||
79|guest.need_entertainment_range|娯楽欲を0〜100で保持する。|P15
|
||||
17,79|guest.need_entertainment_relief|娯楽欲をカジノ等により解消する。|P15
|
||||
80|guest.patience_loss|渋滞等により忍耐力が減る。|P15|D04
|
||||
80|guest.zero_patience_exit|忍耐力0で激怒退店する。|P15
|
||||
80|guest.dirt_contact|汚れを歩くと清潔度が減る。|P22|D04
|
||||
80|guest.dirt_spread|客が汚れを伝播する。|P22|D04
|
||||
82|guest.highest_need_destination|現在最も高い欲求を基に目的地を決定する。|P15
|
||||
83|guest.choice_distance|施設選択の重みに距離を使う。|P15
|
||||
83|guest.choice_queue|施設選択の重みに行列の短さを使う。|P15
|
||||
83|guest.choice_affordability|施設選択の重みに価格・クラスと所持金の適合を使う。|P15
|
||||
85|traffic.astar|経路探索にA*を用いる。|P14
|
||||
85|traffic.elevator_wait_cost|経路コストへ昇降機の待ち時間を含む。|P14
|
||||
86|traffic.guest_elevator_capacity|客用昇降機に定員がある。|P14|D02
|
||||
86|traffic.service_elevator_capacity|裏層の業務用EVは大容量である。|P14|D02
|
||||
176|traffic.service_elevator_fast|業務用EVは高速に移動する。|P14|D04
|
||||
86|traffic.stairs_slower|階段は他の垂直移動より速度が遅い。|P14|D04
|
||||
86|traffic.warp_instant|マナワープで瞬間移動する。|P14
|
||||
86|traffic.warp_cost|マナワープには大きいコストがかかる。|P14|D04
|
||||
87|traffic.overlap_half_speed|通路上のユニット重なりで移動速度が50%低下する。|P14
|
||||
89|guest.addiction_level_three_exit_refusal|依存Lv3で退店を拒否する。|P15
|
||||
89|guest.addiction_equipment_sale|依存Lv3で装備を売却して滞在を継続する。|P15
|
||||
90|guest.male_bankruptcy_riot|所持金0かつ高依存の男性客で暴動が起きる。|P15|D04
|
||||
90|guest.female_bankruptcy_contract|所持金0かつ高依存の女性客で原本の債務ユニット回収イベントが起きる。|P15|D04
|
||||
92|guest.noble_spending|貴族は金払いがよい。|P15|D04
|
||||
92|guest.noble_patience|貴族の忍耐力は極端に低い。|P15|D04
|
||||
93|guest.cleric_underground_exit|聖職者が地下要素を目撃すると即帰宅する。|P15
|
||||
93|guest.cleric_morality_loss|聖職者が地下要素を目撃すると風紀が下がる。|P15|D04
|
||||
94|guest.elf_nature|エルフは自然物を好む。|P15|D04
|
||||
95|guest.demon_underground_tolerance|魔族は地下で満足度が下がらない。|P15
|
||||
95|guest.demihuman_underground_tolerance|亜人は地下で満足度が下がらない。|P15
|
||||
98|staff.guild_hiring|一般従業員をギルドから雇用する。|P16
|
||||
98|staff.daily_wages|一般従業員に日給が発生する。|P05|D04
|
||||
98|staff.general_stress|一般従業員はストレスが溜まりやすい。|P16|D04
|
||||
98|staff.general_dismissal|一般従業員を通常操作で解雇できる。|P16
|
||||
99|staff.market_acquisition|原本の市場から対象ユニットを獲得できる。|P16|D04
|
||||
99,112|staff.debt_acquisition|破産客から対象ユニットを獲得できる。|P16|D04
|
||||
99|staff.bound_food_only|原本の拘束ユニットには食費のみが発生する。|P05|D04
|
||||
99|staff.bound_stress_resistance|原本の拘束ユニットは高いストレス耐性を持つ。|P16|D04
|
||||
101|staff.stamina|従業員にStaminaを保持する。|P16
|
||||
101|staff.stress|従業員にStressを保持する。|P16
|
||||
101|staff.loyalty|従業員にLoyaltyを保持する。|P16
|
||||
102|staff.skill_operations|料理・清掃に運営技術を用いる。|P16
|
||||
102|staff.skill_service|接客等に奉仕技術を用いる。|P16
|
||||
102|staff.skill_combat|警備に戦闘力を用いる。|P16
|
||||
103|staff.random_traits|生成時にランダム特性を付与する。原本の例示を必須全種と捏造しない。|P16
|
||||
112|staff.recruitment|通常操作で求人募集できる。|P16
|
||||
113|staff.dismissal_severance|解雇で退職金を支払う。|P05|D04
|
||||
113|staff.sale_price_stats|ユニット売却価格はステータスによって変わる。|P16|D04
|
||||
113|staff.sale_loyalty_risk|ユニット売却により他職員の忠誠度が下がるリスクがある。|P16|D04
|
||||
116|unique.irreplaceable|ユニーク人物は代替不可能である。|P17
|
||||
116|unique.hire_free|ユニーク人物の雇用費は無料。|P17
|
||||
116|unique.upkeep_free|ユニーク人物の維持費は無料。|P05
|
||||
116|unique.high_growth|ユニーク人物の成長上限は一般従業員より高い。|P17|D04
|
||||
116|unique.no_dismissal|ユニーク人物を解雇できない。|P17
|
||||
116|unique.no_sale|ユニーク人物を売却できない。|P17
|
||||
127|unique.stat_stamina|ユニーク人物に体力を保持する。|P17
|
||||
127|unique.stat_stress|ユニーク人物にストレスを保持する。|P17
|
||||
127|unique.stat_operations|ユニーク人物に運営能力を保持する。|P17
|
||||
127|unique.stat_service|ユニーク人物に奉仕能力を保持する。|P17
|
||||
127|unique.stat_combat|ユニーク人物に戦闘能力を保持する。|P17
|
||||
127|unique.stat_underground|ユニーク人物にアングラ能力を保持する。|P17
|
||||
128|unique.work_exp|業務によってEXPを蓄積する。|P17|D04
|
||||
128|unique.level_99|ユニーク人物はLv99まで成長する。|P17
|
||||
130|unique.passive_on_assignment|配置によってパッシブスキルが自動発動する。|P17
|
||||
131|unique.active_skill|防衛戦等でアクティブスキルを任意発動できる。|P17|D04
|
||||
132|unique.route_skills|ルート進行度によって習得スキルが変わる。|P17|D08
|
||||
134|unique.work_warning|長時間勤務の警告を表示する。|P17
|
||||
134|unique.work_24_hours|警告を無視して24時間勤務できる。|P17
|
||||
134|unique.work_stress_surge|24時間勤務でストレスが激増する。|P17|D04
|
||||
134|unique.outfit_aptitude|衣装変更で業務適正補正が変わる。|P17|D04
|
||||
135|unique.no_normal_loss|ユニーク人物は原則として通常ロストしない。|P17
|
||||
135|unique.defeat_temporary_absence|防衛敗北で原本の一時離脱状態を管理する。|P21|D08
|
||||
135|unique.bad_end_transformation|指定バッドエンド確定時だけ原本の人物状態変更を行う。|P19|D08
|
||||
138|relationship.three_emotions|感情は原本の3パラメータで管理する。|P18|D07
|
||||
140|relationship.affection_shield_max|好感度が愛の盾の最大耐久値になる。|P18|D07
|
||||
141|relationship.lust_reduces_defense|原本のLust値上昇で盾の防御力(ダメージ軽減率)が下がる。|P18|D04
|
||||
142|relationship.ntr_after_break|NTR度は盾破壊後に上昇する。|P18
|
||||
143|relationship.shield_blocks|愛の盾が有効な間はNTR攻撃を防ぐ。|P18
|
||||
143,146|relationship.zero_break|盾耐久値0でブレイク状態へ移る。|P18
|
||||
145|relationship.coassigned_attack|ライバルと同配置で盾耐久値が減る。|P18|D04
|
||||
147|relationship.contact_after_break|ブレイク後のライバル接触でNTR度が直接上がる。|P18|D04
|
||||
148|relationship.threshold_shield_loss|NTR度が指定閾値を越えると盾が消滅する。|P18|D04
|
||||
148|relationship.threshold_love_loss|指定閾値を越えた陥落後は純愛ルートが完全消失する。|P18|D04
|
||||
151|duel.combat|武力決闘を通常操作で選べる。|P18|D04
|
||||
151|duel.business|商談決闘を通常操作で選べる。|P18|D04
|
||||
151|duel.third_type|原本の第三種決闘を通常操作から実行できる。内容と演出が未制作なら受入しない。|P18|D08
|
||||
152|duel.win_exile|決闘勝利でライバルを追放できる。|P18
|
||||
152|duel.win_subordinate|決闘勝利でライバルを隷属状態へ移せる。|P18
|
||||
152|duel.win_full_shield|決闘勝利で愛の盾を全回復する。|P18
|
||||
153|duel.loss_route_lock|決闘敗北で原本の即時ルート確定状態へ移る。|P18
|
||||
155|route_action.date_heal|デートで盾を回復する。|P18|D04
|
||||
155|route_action.talk_heal|会話で盾を回復する。|P18|D04
|
||||
155|route_action.rival_isolation|ライバルを物理的に隔離して進行を抑止できる。|P18
|
||||
155|route_action.duel_removal|決闘でライバルを排除して進行を抑止できる。|P18
|
||||
156|route_action.neglect_weakens|放置により原本の盾弱体化が進む。|P18|D04
|
||||
156|route_action.underground_weakens|地下業務により原本の盾弱体化が進む。|P18|D04
|
||||
156|route_action.observation_progress|観察部屋から原本のルート進行状態を確認できる。|P19|D08
|
||||
157|route_action.item_lust|原本指定アイテムによってLust値が増す。|P18|D04
|
||||
157|route_action.work_lust|地下業務によってLust値が増す。|P18|D04
|
||||
157|route_action.shield_cap_loss|原本の堕落進行で盾耐久値上限が減る。|P18|D04
|
||||
157|route_action.corruption_terminal|原本の堕落終端状態を管理する。|P19|D08
|
||||
159|history.first_partner|原本の初回相手の履歴フィールドを保持する。|P19
|
||||
159|history.experience_count|原本の経験数の履歴フィールドを保持する。|P19
|
||||
159|history.current_partner|現在のパートナーを記録する。|P19
|
||||
160|history.cycle|原本の周期フィールドを保持する。|P19|D04
|
||||
160|history.parentage|原本の父親判定を保持する。|P19|D04
|
||||
160|history.pregnancy_appearance|原本の妊娠期に外見が変わる。|P25|D08
|
||||
160|history.pregnancy_stats|原本の妊娠期にステータスが変わる。|P19|D04
|
||||
161,235,242|asset.portrait_ring|純愛ルートの指輪差分を立ち絵へ反映する。|P25|D08
|
||||
161,235,242|asset.portrait_kiss_mark|NTRルートのキスマーク差分を立ち絵へ反映する。|P25|D08
|
||||
161,235,242|asset.portrait_sigil|原本の堕落ルート指定の紋差分を立ち絵へ反映する。|P25|D08
|
||||
161,235,242|asset.portrait_collar|原本の堕落ルート指定の首輪差分を立ち絵へ反映する。|P25|D08
|
||||
161,235,242|asset.portrait_brand|原本の堕落ルート指定の刻印差分を立ち絵へ反映する。|P25|D08
|
||||
164|construction.front_access|表層施設は客用通路へアクセス必須。|P08
|
||||
164|construction.back_access|裏層施設は裏通路へアクセス必須。|P08
|
||||
165|facility.upgrade_to_max|施設を指定LvMAXまで拡張できる。|P09|D04
|
||||
165,179|facility.micro_price|レベル上昇で価格設定(高額設定含む)が解禁される。|P09|D04
|
||||
179|facility.micro_food_rank|レベル上昇で食材ランク設定が解禁される。|P11|D04
|
||||
179|facility.micro_bath_policy|レベル上昇で原本の浴場ポリシー設定が解禁される。|P11|D04
|
||||
179|facility.micro_adult_policy|レベル上昇で原本の成人施設ポリシー設定が解禁される。|P13|D08
|
||||
179|facility.micro_casino_cheat|レベル上昇でカジノのイカサマ率設定が解禁される。|P11|D04
|
||||
182|economy.front_gold_account|表金を裏金と分けて保持する。|P05
|
||||
182|economy.back_gold_account|裏金を表金と分けて保持する。|P05
|
||||
183|mana.energy|マナを施設維持エネルギーとして使う。|P06|D04
|
||||
183|mana.zero_global_stop|マナ枯渇時に全館機能が停止する。|P06
|
||||
184|economy.material_construction|建設に資材を使う。|P08
|
||||
184|economy.material_repair|修理に資材を使う。|P21|D04
|
||||
186|economy.income_lodging|宿泊料を表の収益へ記帳する。|P05
|
||||
186|economy.income_food|飲食売上を表の収益へ記帳する。|P05
|
||||
187|economy.income_adult|原本の成人施設収入を裏の収益へ記帳する。|P05
|
||||
187|economy.income_casino|カジノ収入を裏の収益へ記帳する。|P05
|
||||
187|economy.income_blackmail|脅迫金を裏の収益へ記帳する。|P05
|
||||
187|economy.income_trade|ユニット売却収入を裏の収益へ記帳する。|P05
|
||||
188|economy.expense_wages|人件費を経費へ記帳する。|P05
|
||||
188|economy.expense_upkeep|維持費を経費へ記帳する。|P05
|
||||
188|economy.expense_purchase|仕入れ原価を経費へ記帳する。|P05
|
||||
188|economy.expense_loan|闇銀行への借金返済を記帳する。|P05
|
||||
188|economy.expense_rank_tax|世界樹ランク税を経費へ記帳する。|P05|D04
|
||||
190|economic_facility.reception|受付/精算所をロビーへ設置できる。|P05|D04
|
||||
190|economic_facility.reception_loss|受付処理が遅いと機会損失が生じる。|P05|D04
|
||||
191|economic_facility.vault_limit|金庫室で所持金上限を解放する。|P05|D04
|
||||
191|economic_facility.vault_target|金庫室が盗賊の標的になる。|P21|D04
|
||||
192|economic_facility.purchase_auto|購買部で消費品を自動発注する。|P05|D04
|
||||
192|economic_facility.purchase_loot|購買部で戦利品を買い取る。|P05|D04
|
||||
193|economic_facility.bank_instant|闇銀行から即時融資を受ける。|P05|D04
|
||||
193|economic_facility.bank_default|返済滞納で店舗破壊部隊が襲来する。|P21|D04
|
||||
196|guest.credit_exceeded|ツケ払いが信用度を超えると破産する。|P15|D04
|
||||
196|guest.debt_settlement_imprisonment|女性破産客の原本イベントで即座にユニット拘束と投獄へ移る。|P19|D08
|
||||
214|ui.hud_gold|上部に資金を表示する。|P24
|
||||
214|ui.hud_mana|上部にマナを表示する。|P24
|
||||
214|ui.hud_materials|上部に資材を表示する。|P24
|
||||
214|ui.hud_time|上部に時間を表示する。|P24
|
||||
214|ui.hud_weather|上部に天候を表示する。|P24
|
||||
214|ui.hud_rank|上部に世界樹ランクを表示する。|P24
|
||||
215|ui.bottom_layer_toggle|下部にFRONT/BACKの大きなレイヤー切替を表示する。|P24
|
||||
215|ui.bottom_build_menu|下部に建設メニューを表示する。|P24
|
||||
215|ui.bottom_management_menu|下部に管理メニューを表示する。|P24
|
||||
216|ui.mouse_scroll|断面マップをマウスでスクロールできる。|P24
|
||||
216|ui.zoom|断面マップをズームイン/アウトできる。|P24
|
||||
216|ui.layer_emphasis|断面マップで選択中の表/裏層を視覚的に強調する。|P23
|
||||
218|ui.facility_policy|施設詳細からポリシーを設定できる。|P24
|
||||
218|ui.shift_drag_drop|施設のシフトへドラッグ&ドロップで人物を配置する。|P24
|
||||
219|ui.guest_need_bars|客の欲求をバーで表示する。|P24
|
||||
219|ui.guest_ai_log|客のAI思考ログを表示する。|P24
|
||||
219|ui.staff_emotion_graph|従業員の原本3感情を3軸グラフで表示する。|P24|D07
|
||||
219|ui.shield_durability|従業員の愛の盾耐久値を表示する。|P24
|
||||
221|ui.observation_camera_entry|裏層のカメラアイコンから専用観察画面へ移る。|P24
|
||||
222|ui.observation_angle|専用観察画面でアングルを変更できる。|P24
|
||||
222|ui.observation_capture|専用観察画面でスクリーンショットによる根拠収集ができる。|P24
|
||||
222|ui.observation_intervention|専用観察画面で介入操作ができる。|P24
|
||||
224|ui.grid_snap|建設時にグリッドスナップする。|P24
|
||||
224|ui.connection_warning|建設時に接続ラインの警告を表示する。|P24
|
||||
225|ui.bubble_sleep|ユニット頭上にZzzのフキダシを表示する。|P23
|
||||
225|ui.bubble_anger|ユニット頭上に怒りのフキダシを表示する。|P23
|
||||
225|ui.bubble_heart|ユニット頭上にハートのフキダシを表示する。|P23
|
||||
225|ui.event_log|イベントログを表示する。|P24
|
||||
226|ui.warning_route_invasion|原本の侵食開始に重要警告演出を表示する。|P24
|
||||
226|ui.cut_duel|決闘発生時にカットイン演出を表示する。|P24
|
||||
226|ui.cut_defense|防衛開始時にカットイン演出を表示する。|P24
|
||||
229|art.front_theme|表の自然・秩序・光を表現する。|P25|D09
|
||||
229|art.back_theme|裏の人工・混沌・闇を表現する。|P25|D09
|
||||
230|art.pixel_map|マップを高精細ドット絵で表現する。|P25|D09
|
||||
230|art.pixel_sd|SDキャラを高精細ドット絵で表現する。|P25|D09
|
||||
230|art.anime_portrait|イベント・会話の立ち絵を高品質アニメ塗りで表現する。|P25|D09
|
||||
232|art.layer_ripple|レイヤー切替を魔法レンズ風の波紋としてシームレスに演出する。|P25|D09
|
||||
232|audio.no_mechanical_switch|レイヤー切替に原本で否定された機械的な音を用いない。|P26
|
||||
234|asset.sd_breathing|SDキャラの呼吸モーションを用意する。|P25|D09
|
||||
234|asset.sd_secondary_motion|原本指定のSD身体動作差分を完成素材として組み込む。|P25|D09
|
||||
234,245|asset.sd_job_walk|職種別の歩行モーションを用意する。|P25|D09
|
||||
234,245|asset.sd_adult_loop|原本指定の成人向けループ完成素材をゲームに組み込み、制作・受入状態を中立台帳で追跡する。|P25|D09
|
||||
235|asset.portrait_damage|立ち絵の破損差分を用意する。|P25|D09
|
||||
235|asset.portrait_dirt|立ち絵の汚れ差分を用意する。|P25|D09
|
||||
235|asset.portrait_love_tone|純愛ルートの立ち絵をソフトフォーカスとして区別する。|P25|D09
|
||||
235|asset.portrait_other_tone|他ルートの指定陰影と表情の完成差分をゲームへ組み込み、中立素材台帳で区別する。|P25|D09
|
||||
237,250|asset.ui_front_skin|表の羊皮紙・ファンタジーUIスキンを用意する。|P25|D09
|
||||
237,250|asset.ui_back_skin|裏の黒鉄・モニター風UIスキンを用意する。|P25|D09
|
||||
237|ui.dynamic_skin|レイヤーモードに合わせUIスキンを動的に変更する。|P24
|
||||
238|audio.same_theme_arrangements|表裏のBGMは同曲のアレンジ違いとする。|P26|D09
|
||||
238|audio.back_bass|裏BGMの重低音ベースを強調する。|P26|D09
|
||||
238|audio.crossfade|表裏のBGMをクロスフェード再生する。|P26
|
||||
239|audio.front_birds|表の環境音に小鳥を用意する。|P26|D09
|
||||
239|audio.back_steam|裏の環境音に蒸気管を用意する。|P26|D09
|
||||
239,252|asset.audio_adult|原本指定の成人向けSE完成素材をゲームに組み込み、制作・受入状態を中立台帳で追跡する。|P26|D09
|
||||
242|asset.main_portraits|メインキャラの立ち絵を用意する。|P25|D09
|
||||
242|asset.sub_portraits|サブキャラの立ち絵を用意する。|P25|D09
|
||||
242|asset.portrait_expression|立ち絵の表情差分を用意する。|P25|D09
|
||||
242|asset.portrait_outfit|立ち絵の衣装差分を用意する。|P25|D09
|
||||
243|asset.route_event_cg|原本のルート別イベントCG完成素材をゲームに組み込み、制作・受入状態を中立台帳で追跡する。|P25|D09
|
||||
243|asset.event_cg_variants|原本のイベントCG完成差分をゲームに組み込み、制作・受入状態を中立台帳で追跡する。|P25|D09
|
||||
243|asset.mob_stills|原本のモブ汎用スチル完成素材をゲームに組み込み、制作・受入状態を中立台帳で追跡する。|P25|D09
|
||||
245|asset.sd_idle|SDキャラの待機モーションを用意する。|P25|D09
|
||||
245|asset.sd_work|SDキャラの作業モーションを用意する。|P25|D09
|
||||
245|asset.sd_collapse|SDキャラの気絶モーションを用意する。|P25|D09
|
||||
246|asset.effect_shield|愛の盾エフェクトを用意する。|P25|D09
|
||||
246|asset.effect_smoke|煙エフェクトを用意する。|P25|D09
|
||||
246|asset.effect_mana|マナの光エフェクトを用意する。|P25|D09
|
||||
248|asset.distant_background|マップ背景遠景を用意する。|P25|D09
|
||||
250|asset.ui_emotion_graph|感情グラフUI素材を用意する。|P25|D09
|
||||
250|asset.ui_observation_overlay|専用観察画面に原本指定のカメラ風オーバーレイを用意する。|P25|D09
|
||||
252|asset.dynamic_bgm|動的変化対応のBGM素材を用意する。|P26|D09
|
||||
252|asset.environment_audio|環境音素材を用意する。|P26|D09
|
||||
252|asset.ui_audio|UI操作音素材を用意する。|P26|D09
|
||||
252|asset.character_voice|キャラクターボイス素材を用意する。|P26|D09
|
||||
'''
|
||||
for row in SPEC.strip().splitlines():
|
||||
parts=row.split('|'); nums,key,condition,package=parts[:4]
|
||||
group_ids=None
|
||||
if nums=='59' or key=='time.speeds':group_ids=['B-3.1a']
|
||||
if key in ['time.pause_build','time.pause_commands']:group_ids=['B-3.1b']
|
||||
if key in ['economy.midnight_report','economy.settlement_calculation']:group_ids=['B-3.4a']
|
||||
if key=='progression.three_deficits_gameover':group_ids=['B-3.4b']
|
||||
if key in ['traffic.astar','traffic.elevator_wait_cost']:group_ids=['B-4.3a']
|
||||
if key.startswith('traffic.') and nums=='86':group_ids=['B-4.3b']
|
||||
if key=='traffic.overlap_half_speed':group_ids=['B-4.3c']
|
||||
add(key,condition,[int(n) for n in nums.split(',')],package,group_ids,pending=parts[4] if len(parts)>4 else None)
|
||||
|
||||
# Each enumerated job is a separate obligation; role aliases do not add employees.
|
||||
for num, jobs,layer in [(105,[('waiter','ウェイター'),('receptionist','受付係'),('bunny','バニー')],'表層'),
|
||||
(106,[('chef','シェフ'),('cleaner','清掃員'),('carrier','運搬員'),('rescue','救護班')],'裏層'),
|
||||
(107,[('adult_service','原本指定の地下接客職'),('security','警備員'),('trainer','原本指定の調教職')],'原本指定層')]:
|
||||
for key,name in jobs:add('job.'+key,f'{name}の職種と{layer}での業務を通常配置から運用する。',[num],'P16',pending='D04')
|
||||
|
||||
# Original 7 characters are canonical identities, independent of current resource IDs.
|
||||
heroes=[('anri','アンリ','女将',118,[('floor_price','配置フロアの全施設売上を20%増やす。',None)]),
|
||||
('fine','フィーネ','メイド',119,[('cleaning_speed','清掃速度を3倍にする。',None),('assassination','暗殺確率を上げる。','D04')]),
|
||||
('tomari','トマリ','騎士',120,[('floor_trouble','配置フロアのトラブルを0%にする。',None),('defense_score','防衛スコアを加算する。','D04')]),
|
||||
('amelia','アメリア','シスター',121,[('donation','寄付金を3倍にする。',None),('recovery','回復速度を2倍にする。',None)]),
|
||||
('liz','リズ','狩人',122,[('rare_material','レア素材発見率を上げる。','D04'),('food_saving','高級食材消費を減らす。','D04')]),
|
||||
('mimi','ミミ','商人',123,[('purchase','仕入れ価格を20%減らす。',None),('sale','売却価格を20%増やす。',None)]),
|
||||
('satsuki','サツキ','雑用',124,[('vip_satisfaction','VIP客の満足度上昇を1.5倍にする。',None),('tip','チップを増やす。','D04')])]
|
||||
for key,name,role,num,effects in heroes:
|
||||
ent={'kind':'heroine','key':key,'original_name':name}
|
||||
add(f'heroine.{key}.identity',f'{name}を原本7人物の一人として通常進行で利用できる。',[30,num],'P17',entity=ent,pending='D08')
|
||||
add(f'heroine.{key}.role',f'{name}は{role}として業務する。',[num],'P17',entity=ent)
|
||||
for effect,condition,pending in effects:
|
||||
add(f'heroine.{key}.{effect}',name+'の固有効果: '+condition,[num],'P17',entity=ent,pending=pending)
|
||||
for route,label in [('love','純愛'),('ntr','NTR'),('corruption','堕落')]:
|
||||
add(f'heroine.{key}.route.{route}',f'{name}の{label}ルートを通常進行で到達・記録できる。',[9,30,33,138],'P19',entity=ent,pending='D08')
|
||||
for key,name in [('rai','ライ'),('riesenbart','リーゼンバルト'),('sou','ソウ')]:
|
||||
add(f'rival.{key}.hiring',f'ライバル{name}を雇用できる。',[125],'P17',entity={'kind':'rival','key':key,'original_name':name},pending='D08')
|
||||
add(f'rival.{key}.risk',f'ライバル{name}の雇用で原本の侵食リスクが生じる。',[125],'P18',pending='D04')
|
||||
|
||||
# Chapter 10 enumerates exactly 30 original facilities. Meaning keys are audit keys,
|
||||
# NOT a claim that those IDs/resources are present in production.
|
||||
FACS=[
|
||||
('standard_room','一般客室',199,'2x1','FRONT',None,'P10',[167,46]),
|
||||
('premium_room','上級客室',199,'4x1','FRONT',None,'P10',[167,46]),
|
||||
('vip_suite','VIPスイート',199,'6x2','FRONT','高層','P10',[167,46]),
|
||||
('dormitory','ドミトリー',199,'4x1','FRONT','地下','P10',[]),
|
||||
('cafeteria','大衆食堂',201,'4x2','FRONT',None,'P11',[168,46]),
|
||||
('restaurant','レストラン',201,'6x2','FRONT',None,'P11',[168,46]),
|
||||
('tavern','酒場',201,'4x2','FRONT',None,'P11',[168,46]),
|
||||
('food_stall','屋台',201,'2x1','FRONT',None,'P11',[]),
|
||||
('public_bath','大浴場',203,'6x2','FRONT',None,'P11',[169]),
|
||||
('casino','カジノ',203,'6x3','FRONT','地下','P11',[169]),
|
||||
('arena','闘技場',203,'8x4','FRONT','地下','P11',[169]),
|
||||
('chapel','礼拝堂',203,'4x3','FRONT',None,'P11',[169]),
|
||||
('adult_private_room','風俗個室',205,'2x1','FRONT','地下','P13',[170]),
|
||||
('luxury_soap','高級ソープ',205,'4x2','FRONT','地下','P13',[170]),
|
||||
('sm_room','SM拷問室',205,'4x2','FRONT','地下','P13',[170]),
|
||||
('black_market','闇市場',205,'4x2','FRONT','地下','P13',[170]),
|
||||
('kitchen','厨房',207,'4x2','BACK',None,'P12',[173]),
|
||||
('warehouse','倉庫',207,'4x2','BACK',None,'P12',[49,173]),
|
||||
('staff_waiting_room','待機室',207,'4x2','BACK',None,'P12',[49,172]),
|
||||
('staff_dormitory','従業員寮',207,'2x1','BACK',None,'P12',[49,172]),
|
||||
('security_station','警備員詰所',207,'2x2','BACK',None,'P12',[172]),
|
||||
('cleaning_room','清掃員室',207,'2x1','BACK',None,'P12',[173]),
|
||||
('infirmary','医務室',207,'4x2','BACK',None,'P12',[173]),
|
||||
('peeping_room','覗き部屋',209,'2x1','BACK',None,'P13',[49,174]),
|
||||
('prison','地下牢',209,'4x2','BACK','地下','P13',[174]),
|
||||
('mana_furnace','マナ炉',209,'6x3','BACK','地下','P13',[174]),
|
||||
('guest_elevator','蔓の昇降機',211,'2x2','FRONT',None,'P14',[46,86,176]),
|
||||
('service_elevator','業務用EV',211,'3x2','BACK',None,'P14',[49,86,176]),
|
||||
('mana_warp','ワープ',211,'2x2','FRONT',None,'P14',[86,176]),
|
||||
('magic_turret','魔法砲台',211,None,None,'外壁','P21',[])]
|
||||
SUPP_FIELDS=[('construction_gold',1,'建設金額',None),('upkeep_gold',2,'運用金額',None),
|
||||
('upkeep_mana',2,'運用マナ量','D04'),('service_price',3,'利用料金','D04'),
|
||||
('footprint',4,'占有サイズ','D06'),('role',5,'役割','D04'),('operation_state',6,'稼働状態',None),
|
||||
('customer_capacity',7,'同時利用顧客人数上限','D02'),('staff_min',8,'必要最低職員人数','D02'),('staff_max',8,'配置可能職員上限','D02'),
|
||||
('related_facility',9,'関連施設指定','D03'),('related_operating',9,'関連施設の実稼働フラグ',None),
|
||||
('service_attribute',10,'提供サービス属性','D01')]
|
||||
for key,name,num,size,layer,zone,package,extra in FACS:
|
||||
ent={'kind':'original_facility','key':key,'original_name':name}
|
||||
anchors=[num]+extra
|
||||
add(f'facility.{key}.exists',f'原本施設{name}を通常建設・運用できる。',anchors,package,entity=ent)
|
||||
if size:add(f'facility.{key}.footprint',f'{name}の占有寸法は{size}。',[num],package,entity=ent,pending='D06')
|
||||
if layer:add(f'facility.{key}.layer',f'{name}の層は{layer}。',[num],package,entity=ent)
|
||||
if zone:add(f'facility.{key}.zone',f'{name}の配置条件は{zone}。',[num],package,entity=ent,pending='D04' if zone=='高層' else None)
|
||||
for field,no,label,pending in SUPP_FIELDS:
|
||||
rid='AR.'+f'facility.{key}.{field}'
|
||||
cond=records[rid]['condition'] if rid in records else f'{name}の{label}を明示値または明示未設定として保持し、必要確定値は営業受入までに固定する。'
|
||||
rec=add(f'facility.{key}.{field}',cond,[983+no],'P09',source=SUPPLEMENT,
|
||||
authority='retained_record_of_explicit_user_facility_amendment',supplement=f'F{no:02}',entity=ent,pending=pending)
|
||||
if field=='operation_state':rec['acceptance']['condition_assertion']=f'{name}のINACTIVE/OPERATING/SUSPENDEDを区別し、画面・Runtime・保存で一致する。'
|
||||
if field=='customer_capacity':rec['acceptance']['condition_assertion']=f'{name}の利用中人数で専用上限を判定する。待機者とcapacityを流用しない。未設定を確定済み上限として受け入れない。'
|
||||
if field=='staff_max':rec['acceptance']['condition_assertion']=f'{name}の職員上限を最低人数と分離し、上限超過の配置を原状保持で拒否する。'
|
||||
if field=='related_operating':rec['acceptance']['condition_assertion']=f'{name}の関連先について配置有無ではなく実稼働を判定する。'
|
||||
# Unspecified extension values and required operating values have different contracts.
|
||||
for field,no,label,pending in SUPP_FIELDS:
|
||||
rec=records[f'AR.facility.{key}.{field}']
|
||||
rec['value_contract']={'field':field,'known_value':None,'certainty':'not_reaudited',
|
||||
'null_or_unset_allowed_as_final':field=='related_facility',
|
||||
'unset_policy':'単一関連先なしは表現できる。複数関連先/AND/ORは承認なしに追加しない。' if field=='related_facility' else '未設定は台帳で可視化するが必要営業値の確定・受入の代用にしない。',
|
||||
'source_has_numeric_value':False}
|
||||
if field=='footprint' and size:rec['value_contract'].update(known_value=size,certainty='original_explicit',source_has_numeric_value=True)
|
||||
if field=='operation_state':rec['value_contract'].update(known_value=['INACTIVE','OPERATING','SUSPENDED'],certainty='explicit_amendment_enum')
|
||||
# Finished visual slots are finite explicitly specified combinations (30 x 3 x 2 x 2).
|
||||
for level in [1,2,3]:
|
||||
for view in ['front','back']:
|
||||
for part,label in [('exterior','外観'),('interior','内装')]:
|
||||
add(f'asset.facility.{key}.lv{level}.{view}.{part}',f'{name} Lv{level} {view} の{label}完成素材を用意する。',[248],'P25',entity=ent,pending='D09')
|
||||
|
||||
# Other original infrastructure remains outside the chapter-10 count, never silently dropped.
|
||||
for key,name,nums in [('guest_corridor','客用通路',[46,177]),('service_corridor','裏通路',[49,177]),('stairs','階段',[86,176])]:
|
||||
add(f'infrastructure.{key}.exists',f'{name}を通常建設し移動へ接続する。',nums,'P14',pending='D04')
|
||||
for key in ['cafeteria','restaurant','tavern']:
|
||||
add(f'facility.{key}.kitchen_dependency',f'{dict((f[0],f[1]) for f in FACS)[key]}は裏層の厨房とセットで稼働する。',[168],'P11')
|
||||
add('facility.kitchen.behind_food','厨房は表層飲食店の裏に置く。',[173],'P12')
|
||||
add('facility.vip_suite.high_beauty','VIPスイートは高景観を必要とする。',[167],'P10',pending='D04')
|
||||
for key,nums in [('vip_suite',[167,199]),('public_bath',[169,203]),('adult_private_room',[170,205])]:
|
||||
add(f'facility.{key}.observation','対象施設は原本指定の観察機能に対応する。',nums,'P13')
|
||||
add('facility.tavern.noise','酒場が騒音を発生する。',[201],'P22',pending='D04')
|
||||
add('facility.staff_dormitory.night_event','従業員寮に原本指定の夜間イベント対応がある。',[172],'P19',pending='D08')
|
||||
add('facility.mana_furnace.unit_consumption','マナ炉で原本指定のユニット消費が発生する。',[209],'P13',pending='D04')
|
||||
|
||||
# Global semantic contracts for the ten fields. These are distinct from individual configured values.
|
||||
CONTRACTS=[
|
||||
('construction.payment',1,'通常建設で配置コストを支払う。','P08',None),
|
||||
('facility.state_three_values',6,'稼働状態はINACTIVE/OPERATING/SUSPENDEDの3状態として扱う。','P09',None),
|
||||
('facility.destroy_suspend',6,'破壊された施設を休止する。','P09',None),
|
||||
('facility.enforcement_suspend',6,'摘発された施設を休止する。','P09',None),
|
||||
('facility.no_auto_resume',6,'関連施設の条件回復だけで破壊・摘発後の休止を解除しない。','P09','D03'),
|
||||
('facility.customer_not_capacity',7,'customer_capacityを保管量・生産量等のcapacityから推定しない。','P09','D02'),
|
||||
('facility.customer_wait_separate',7,'待機者と利用中顧客数を分ける。','P09',None),
|
||||
('facility.staff_min_separate',8,'職員最低人数と職員上限を別に保持する。','P09','D02'),
|
||||
('facility.related_single',9,'関連先は単一指定の現契約を保持する。','P09','D03'),
|
||||
('facility.related_not_placement',9,'関連施設の配置有無を実稼働判定に置き換えない。','P09',None),
|
||||
|
||||
('pricing.attributes',10,'サービス属性の既存4型(appetite/relaxation/sexual/gambling)を区別する。','P09','D01'),
|
||||
('pricing.match_surcharge',3,'欲求一致時だけ指定した加算を料金へ適用する。','P09','D01'),
|
||||
('pricing.mismatch_allowed',10,'欲求不一致を施設利用不可の理由にしない。','P09',None),
|
||||
('pricing.no_unset_invention',10,'不足基準・加算額/率・複数属性・重複加算の未指定を勝手に補完しない。','P09','D01'),
|
||||
('facility.no_resume_invention',6,'指定されていない休止復帰条件を追加しない。','P09','D03'),
|
||||
('facility.no_multiple_related_invention',9,'指定されていない複数関連施設とAND/ORを追加しない。','P09','D03')]
|
||||
for key,no,cond,package,pending in CONTRACTS:
|
||||
add(key,cond,[983+no],package,source=SUPPLEMENT,authority='retained_record_of_explicit_user_facility_amendment',supplement=f'F{no:02}',pending=pending)
|
||||
|
||||
# Approved A01-A05 refine existing atoms; none create a second weight for the same value.
|
||||
DEC_LINE={'A01':6,'A02':13,'A03':20,'A04':27,'A05':33}
|
||||
def approved(key,cond,decision,package='P09',replace=False,pending=None):
|
||||
r=add(key,cond,[DEC_LINE[decision]],package,source=DECISIONS,
|
||||
authority='explicit_user_approved_amendment',decision=decision,replace=replace,pending=pending)
|
||||
if replace:
|
||||
r['value_contract']={'known_value_statement':cond,'certainty':'approved_initial_trial_value','decision_id':decision,'null_or_unset_allowed_as_final':False}
|
||||
r['pending_decision_ids']=[x for x in r['pending_decision_ids'] if x not in ['D01','D02','D04']]
|
||||
r['decision_dependencies']=[x for x in r['decision_dependencies'] if x['decision_id'] in r['pending_decision_ids']]
|
||||
return r
|
||||
for key,cond in [
|
||||
('upkeep.start_completion','維持費は施設完成時から計上する。'),
|
||||
('upkeep.end_removal','維持費は撤去まで計上する。'),
|
||||
('upkeep.time_proration','維持費日額を保有時間で按分する。'),
|
||||
('upkeep.closed_accrual','閉店中も維持費を計上する。'),
|
||||
('upkeep.suspended_accrual','休止中も維持費を計上する。'),
|
||||
('upkeep.midnight_payment','維持費を00:00に精算する。'),
|
||||
('upkeep.shortfall_carry','維持費不足額を翌日へ繰り越す。')]: approved(key,cond,'A01','P05')
|
||||
for key,cond in [
|
||||
('upkeep.trial_rate','一般施設の維持費初期試験値は建築費の0.5%/日。'),
|
||||
('upkeep.integer_arithmetic','維持費は既存の整数処理を保持する。'),
|
||||
('upkeep.special_override','礼拝堂・清掃員室の維持費に個別承認値を優先する。')]:approved(key,cond,'A02','P05')
|
||||
approved('heroine.anri.recruit_entry','従業員画面でアンリの無料加入を選ぶ。','A03','P17')
|
||||
approved('heroine.anri.floor_price','配置フロアのアンリ補正で全施設の利用料金を20%増加する。','A03','P17',replace=True)
|
||||
approved('heroine.anri.wallet_revenue','アンリ補正後の客支払額と施設収入を一致させる。','A03','P05')
|
||||
for decision,key,vals in [
|
||||
('A04','chapel',{'footprint':'占有寸法4x3','layer':'表層','construction_gold':'建築1200G','construction_materials':'資材12','construction_minutes':'工期180分','customer_capacity':'同時利用8人','staff_min':'聖職者最低1人','staff_max':'聖職者上限2人','service_minutes':'礼拝60分','service_price':'寄付20G','completion_faith':'礼拝完了時信仰+1','upkeep_gold':'維持費6G/日'}),
|
||||
('A05','cleaning_room',{'footprint':'占有寸法2x1','layer':'裏層','construction_gold':'建築350G','construction_materials':'資材4','construction_minutes':'工期60分','staff_min':'清掃員最低1人','staff_max':'清掃員上限2人','customer_capacity':'顧客0人','service_price':'利用料なし','upkeep_gold':'維持費2G/日'})]:
|
||||
for field,val in vals.items():approved(f'facility.{key}.{field}',f'{key}の承認初期試験値は{val}。',decision,replace=True)
|
||||
for key,cond in [
|
||||
('cleaning.cleanliness_range','清潔度の範囲は0〜100。'),
|
||||
('cleaning.initial_cleanliness','清潔度初期値は100。'),
|
||||
('cleaning.reservation_threshold','清潔度75以下で清掃予約する。'),
|
||||
('cleaning.on_site_minutes','現地清掃所要時間は8分。'),
|
||||
('cleaning.recovery_amount','現地清掃完了で清潔度を25回復する。'),
|
||||
('cleaning.defined_dirt_only','定義済み汚れ量を使用する。'),
|
||||
('cleaning.no_undefined_dirt','未定義の汚れ量を勝手に補完しない。')]:approved(key,cond,'A05','P16')
|
||||
|
||||
# Historical milestones: content requirements remain mapped in their feature domains;
|
||||
# these atoms test the dated delivery promise and cannot be retrospectively passed.
|
||||
SCHEDULE=[(255,'B-14.1','2026/03〜08',[('layers','表裏レイヤー切替'),('ai','AI移動ロジック'),('assets','基本素材'),('mvp','MVP')]),
|
||||
(256,'B-14.2','2026/09〜2027/02',[('parameters','パラメータ管理'),('portraits','立ち絵量産'),('tiles','施設タイル量産'),('scenario','シナリオ組込み'),('bgm','通常BGM組込み')]),
|
||||
(257,'B-14.3','2027/03〜08',[('adult','原本指定の成人向け内容'),('observation','専用観察モード'),('voice_record','ボイス収録'),('voice_integrate','ボイス組込み'),('balance','ゲームバランス徹底調整')]),
|
||||
(258,'B-14.4','2027/09〜12',[('debug','進行不能バグ等のデバッグ'),('ui','UIレスポンス向上'),('mastering','マスタリング'),('final','最終完成')])]
|
||||
for num,gid,window,deliverables in SCHEDULE:
|
||||
for key,label in deliverables:
|
||||
r=add(f'schedule.{gid}.{key}',f'{window}の工程内に{label}を達成する。',[num],'P00',[gid],scope='historical_schedule')
|
||||
r['acceptance']['evidence_policy']='日付付き当時の成果が必要。後日の機能実装で過去納期達成をPASSにしない。'
|
||||
r['historical_due_window']=window
|
||||
|
||||
ADDITIONAL_APPLICABILITY_EXPANSION = {'decision_id': 'D09', 'scope': 'additional_named_facilities_and_infrastructure_applicability', 'reason': '原本第10章30施設以外にも有限の名指し施設/設備がある。通常機能条件は存在するが、追加F01〜F10とLv別表裏/外内素材の全適用範囲は未確定。30施設の展開から外れていることを最終全施設網羅として受入しない。適用範囲確定時に原子ID追加/統合と分母改訂の履歴を記録する。一律12素材枠や全項目適用を推測で追加しない。', 'source_examples': [{'lines': [190], 'names': ['受付/精算所']}, {'lines': [191], 'names': ['金庫室']}, {'lines': [192], 'names': ['購買部']}, {'lines': [193], 'names': ['闇銀行']}, {'lines': [49, 177], 'names': ['裏通路']}, {'lines': [46, 177], 'names': ['客用通路']}, {'lines': [50, 177], 'names': ['従業員ドア']}, {'lines': [86, 176], 'names': ['階段']}, {'lines': [49], 'names': ['リネンシュート', '隠し扉']}], 'applicability_decisions_required': ['facility_supplement_F01_to_F10', 'levels_1_to_3', 'front_back_views', 'exterior_interior_visuals']}
|
||||
|
||||
# Align primary responsibility with the reviewed plan's actual package titles.
|
||||
for rec in records.values():
|
||||
rid=rec['id']; primary=rec['primary_package']
|
||||
if primary=='P22':primary='P07' # weather/environment implementation; P22 is the asset inventory
|
||||
if primary=='P07':primary='P23' if rid.startswith(('AR.ui.','AR.world.')) else 'P07'
|
||||
if rid.startswith(('AR.weather.','AR.environment.','AR.news.')):primary='P06' if rid=='AR.environment.ground_mana' else 'P07'
|
||||
if rid in ['AR.world.unit_grid','AR.world.above_ground','AR.world.below_ground','AR.world.ground_boundary']:primary='P08'
|
||||
if rid=='AR.world.dual_management':primary='P20'
|
||||
if rid.startswith(('AR.duel.','AR.route_action.')):primary='P19'
|
||||
if rid.startswith('AR.audio.'):primary='P25'
|
||||
if rid=='AR.art.layer_ripple':primary='P23'
|
||||
if rid.startswith('AR.asset.portrait_') or rid in ['AR.asset.main_portraits','AR.asset.sub_portraits','AR.asset.route_event_cg','AR.asset.event_cg_variants','AR.asset.mob_stills','AR.history.pregnancy_appearance']:primary='P26'
|
||||
if rid in ['AR.asset.dynamic_bgm','AR.asset.environment_audio','AR.asset.ui_audio','AR.asset.audio_adult']:primary='P25'
|
||||
if rid=='AR.asset.character_voice':primary='P26'
|
||||
if rid.startswith(('AR.facility.cafeteria.','AR.facility.restaurant.','AR.facility.tavern.','AR.facility.food_stall.')) and primary=='P11':primary='P10'
|
||||
if rid=='AR.facility.tavern.noise':primary='P07'
|
||||
rec['primary_package']=rec['package']=primary
|
||||
rec['normal_entry']=normal_entries.get(primary,'要件別通常入口の設計と検証')
|
||||
# Group-level routing is kept for cross-cutting work, separately from primary ownership.
|
||||
inherited={p for g in coverage_groups if g['requirement_id'] in rec['source_group_ids'] for p in g['work_packages']}
|
||||
rec['work_packages']=sorted(inherited|{primary})
|
||||
|
||||
# All group mappings use condition anchors. Headings are structural aliases, not extra counts.
|
||||
line_map={n:[] for n in range(1,len(lines)+1)}
|
||||
for r in records.values():
|
||||
for a in r['source_anchors']:
|
||||
if a['path']==BASE:
|
||||
for n in a['lines']:line_map[n].append(r['id'])
|
||||
coverage=[]
|
||||
for n,line in enumerate(lines,1):
|
||||
ids=sorted(set(line_map[n])); kind='condition'
|
||||
if line.startswith('#'):
|
||||
kind='structural_heading'
|
||||
level=len(line)-len(line.lstrip('#'))
|
||||
end=n
|
||||
for j in range(n+1,len(lines)+1):
|
||||
if lines[j-1].startswith('#') and len(lines[j-1])-len(lines[j-1].lstrip('#'))<=level:break
|
||||
end=j
|
||||
ids=sorted({rid for j in range(n+1,end+1) for rid in line_map[j]})
|
||||
elif n==7:
|
||||
kind='structural_label';ids=sorted(set(line_map[8]+line_map[9]))
|
||||
elif n==18:
|
||||
kind='structural_label';ids=sorted(set(line_map[19]+line_map[20]))
|
||||
elif n==150:
|
||||
kind='structural_description';ids=sorted(set(line_map[151]+line_map[152]+line_map[153]))
|
||||
elif n==254:
|
||||
kind='schedule_window';ids=sorted({rid for j in range(255,259) for rid in line_map[j]})
|
||||
coverage.append({'line':n,'kind':kind,'line_sha256':hashlib.sha256(line.encode()).hexdigest(),'atomic_ids':ids})
|
||||
|
||||
feature_ids=sorted(r['id'] for r in records.values() if r['scope']=='feature_or_content')
|
||||
schedule_ids=sorted(r['id'] for r in records.values() if r['scope']=='historical_schedule')
|
||||
register={'schema_version':1,'register_version':'atomic-v1-2026-10-02','generated_at_jst':now,
|
||||
'production_root':str(ROOT),'source_head':head,'source_pins':pins,
|
||||
'authority_policy':'指定原本保存版 > 明示承認/追加仕様(重複条件を精緻化) > 別冊参照 > 派生文書。今回新規Drive取得なし。',
|
||||
'completion_views':VIEWS,'state_policy':'未再監査はunverified/not_reaudited。PASSと適用外は証拠と独立判定者が必要。',
|
||||
'id_policy':'AR.<domain>.<canonical-source-entity>.<condition>; canonical-source-entity is an audit identity, not a production resource id',
|
||||
'denominator_version':'atomic-v1-2026-10-02','denominator_frozen':True,
|
||||
'denominator_scope':'Known-condition register v1 only. Final denominator is NOT established while D08/D09 target catalogs remain unresolved. Saved-original conditions plus retained explicit additions and A01-A05; no invented content targets or unapproved values.',
|
||||
'final_denominator_established':False, 'final_catalog_coverage_claimed':False,
|
||||
'denominator_ids':feature_ids,'historical_schedule_ids':schedule_ids,'duplicate_merges':merges,
|
||||
'open_expansion_register':[{'decision_id':'D08','reason':'全エンド/図鑑対象/章/制作内容の個別IDは未指定。集合契約と各人物3ルートを保持。確定時に原子ID追加・統合を分母改訂履歴へ記録し、集合契約の合格を個別内容の代用にしない。'},
|
||||
{'decision_id':'D09','reason':'サブ人物数・表情/衣装/CG/音の全差分量は未指定。有限明示される施設30×Lv1〜3×表裏×外内のみ個別化済み。確定数量の独立条件は分母改訂が必要。'}],
|
||||
'requirements':sorted(records.values(),key=lambda r:r['id'])}
|
||||
register['open_expansion_register'].append(ADDITIONAL_APPLICABILITY_EXPANSION)
|
||||
source_coverage={'schema_version':1,'source_path':BASE,'source_sha256':pins[BASE],
|
||||
'line_count':len(lines),'group_count':len(groups),'source_lines':coverage,
|
||||
'groups':[{'group_id':g['id'],'source_lines':g['source_lines'],
|
||||
'atomic_ids':sorted(r['id'] for r in records.values() if g['id'] in r['source_group_ids']),
|
||||
'scope':'historical_schedule' if g['id'].startswith('B-14.') else 'feature_or_content'} for g in groups],
|
||||
'facility_ids':[{'key':f[0],'original_name':f[1], 'original_line':f[2],
|
||||
'atomic_ids':sorted(r['id'] for r in records.values() if r['entity'] and r['entity'].get('kind')=='original_facility' and r['entity']['key']==f[0])} for f in FACS],
|
||||
'heroine_ids':[{'key':h[0],'original_name':h[1], 'atomic_ids':sorted(r['id'] for r in records.values() if r['entity'] and r['entity'].get('kind')=='heroine' and r['entity']['key']==h[0])} for h in heroes],
|
||||
'approved_decisions':[{'decision_id':d, 'atomic_ids':sorted(r['id'] for r in records.values() if d in r['user_amendment_refs'])} for d in DEC_LINE],
|
||||
'facility_supplements':[{'supplement_id':f'F{n:02}', 'atomic_ids':sorted(r['id'] for r in records.values() if f'F{n:02}' in r['supplement_ids'])} for n in range(1,11)]}
|
||||
freeze={'schema_version':1,'denominator_version':register['denominator_version'],'source_head':head,
|
||||
'source_pins':pins,'calculated_at_jst':now,'denominator_frozen':True,'counting_unit':'one unique independently judgeable source condition',
|
||||
'freeze_scope':'known-condition v1 only; final content-target denominator remains unresolved',
|
||||
'final_denominator_established':False,'denominator':len(feature_ids),'denominator_ids':feature_ids,'historical_schedule_count':len(schedule_ids),'historical_schedule_ids':schedule_ids,
|
||||
'implemented_ids':[],'accepted_ids':[],'blocked_ids':[], 'partial_ids':[], 'missing_evidence_ids':feature_ids,
|
||||
'difference_ids':[],'difference_audit_state':'not_reaudited',
|
||||
'not_reaudited_ids':feature_ids,'pending_decision_ids':sorted({d for r in records.values() for d in r['pending_decision_ids']}),
|
||||
'implementation_percent':0.0,'acceptance_percent':0.0,
|
||||
'percent_label':'currently evidenced whole-atom completion; 0 does not mean no legacy code exists',
|
||||
'applicability_review_pending_ids':feature_ids,'independent_reviewer':None,
|
||||
'change_control':{'requires':['previous_version','new_version','added_ids','removed_ids','merged_ids','reason','reviewer','source_reference'],
|
||||
'history':[{'previous_version':None,'new_version':register['denominator_version'],'added_ids':feature_ids,
|
||||
'removed_ids':[],'merged_ids':merges,'reason':'initial source split; no game acceptance inferred','reviewer':None,
|
||||
'review_state':'independent_review_required'}]}}
|
||||
for name,data in [('atomic_requirements.json',register),('source_atomic_coverage.json',source_coverage),('atomic_denominator_freeze.json',freeze)]:
|
||||
(OUT/name).write_text(json.dumps(data,ensure_ascii=False,indent=2)+'\n')
|
||||
print(json.dumps({'feature_atoms':len(feature_ids),'historical_schedule_atoms':len(schedule_ids),
|
||||
'source_lines':len(coverage),'unmapped_lines':[r['line'] for r in coverage if not r['atomic_ids']],
|
||||
'unmapped_groups':[r['group_id'] for r in source_coverage['groups'] if not r['atomic_ids']],
|
||||
'merges':len(merges)},ensure_ascii=False))
|
||||
@@ -0,0 +1,522 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Static, source-anchored P09 audit. Never loads Godot or edits production files."""
|
||||
from __future__ import annotations
|
||||
|
||||
import ast
|
||||
import hashlib
|
||||
import json
|
||||
import re
|
||||
import subprocess
|
||||
from collections import Counter
|
||||
from pathlib import Path
|
||||
|
||||
ROOT = Path(__file__).resolve().parents[3]
|
||||
OUT = Path(__file__).resolve().parent
|
||||
BASE = "docs/traceability/original/BASELINE.txt"
|
||||
ANNEX = "artifacts/original-annex-A-drive-retrieval-2026-09-30-r01/retrieved-readable-annex-A.md"
|
||||
DECISIONS = "docs/plans/original-completion-2026-10-02/decisions.json"
|
||||
ADDITION = "docs/traceability/original/audit-2026-09-29/TRACEABILITY.md"
|
||||
PLAN = "docs/plans/original-completion-2026-10-02/execution/IMPLEMENTATION_PLAN.initial.md"
|
||||
DEF = "scripts/data/facility_definition.gd"
|
||||
CATALOG = "scripts/data/facility_catalog.gd"
|
||||
INSTANCE = "scripts/facility/facility_instance.gd"
|
||||
SERVICE = "scripts/facility/facility_service.gd"
|
||||
PRICING = "scripts/facility/facility_pricing.gd"
|
||||
RUNTIME = "autoload/simulation_runtime.gd"
|
||||
UPKEEP = "docs/plans/original-completion-2026-09-29/execution/UPKEEP_IMPLEMENTATION.md"
|
||||
PINS = {}
|
||||
|
||||
|
||||
def pin(path):
|
||||
if path not in PINS:
|
||||
PINS[path] = hashlib.sha256((ROOT / path).read_bytes()).hexdigest()
|
||||
return PINS[path]
|
||||
|
||||
|
||||
def anchor(path, lines, authority):
|
||||
return {"path": path, "lines": lines, "sha256": pin(path), "authority": authority}
|
||||
|
||||
|
||||
def findline(path, literal):
|
||||
matches = [i for i, line in enumerate((ROOT / path).read_text().splitlines(), 1) if literal in line]
|
||||
assert matches, (path, literal)
|
||||
return matches[0]
|
||||
|
||||
|
||||
def literal(raw):
|
||||
"""Decode only data literals used by the retained .tres files; no eval."""
|
||||
raw = raw.strip()
|
||||
enum = {"MapZone.ANY": 0, "ViewLayer.FRONT": 0, "Vector2i.ONE": [1, 1], "LEGACY_SCHEMA_VERSION": 1}
|
||||
if raw in enum:
|
||||
return enum[raw]
|
||||
wrapped = re.fullmatch(r"Array\[[^]]+\]\((.*)\)", raw)
|
||||
if wrapped:
|
||||
raw = wrapped[1]
|
||||
raw = raw.replace('&"', '"')
|
||||
raw = re.sub(r"Vector2i\(([-\d]+),\s*([-\d]+)\)", r"[\1, \2]", raw)
|
||||
raw = re.sub(r"\btrue\b", "True", raw)
|
||||
raw = re.sub(r"\bfalse\b", "False", raw)
|
||||
raw = re.sub(r"\bnull\b", "None", raw)
|
||||
return ast.literal_eval(raw)
|
||||
|
||||
|
||||
DEFAULTS = {}
|
||||
EXPORTED_KEYS = set()
|
||||
for n, line in enumerate((ROOT / DEF).read_text().splitlines(), 1):
|
||||
name = re.match(r"@export(?:_range\([^)]*\)|_multiline)?\s+var\s+(\w+)", line)
|
||||
if name:
|
||||
EXPORTED_KEYS.add(name[1])
|
||||
m = re.match(r"@export(?:_range\([^)]*\)|_multiline)?\s+var\s+(\w+)(?::[^=]+)?\s*:?=\s*(.+)$", line)
|
||||
if m:
|
||||
DEFAULTS[m[1]] = {"raw": m[2], "value": literal(m[2]), "line": n}
|
||||
|
||||
DEFINITIONS = {}
|
||||
for p in sorted((ROOT / "data/facilities").rglob("*.tres")):
|
||||
rel = str(p.relative_to(ROOT))
|
||||
raw_fields = {}
|
||||
duplicates = []
|
||||
for n, line in enumerate(p.read_text().splitlines(), 1):
|
||||
m = re.match(r"(\w+) = (.+)$", line)
|
||||
if not m or m[1] == "script":
|
||||
continue
|
||||
if m[1] in raw_fields:
|
||||
duplicates.append({"field": m[1], "first_line": raw_fields[m[1]]["line"], "duplicate_line": n,
|
||||
"same_raw_value": raw_fields[m[1]]["raw"] == m[2]})
|
||||
raw_fields[m[1]] = {"raw": m[2], "value": literal(m[2]), "line": n}
|
||||
DEFINITIONS[raw_fields["id"]["value"]] = {"path": rel, "fields": raw_fields, "duplicate_assignments": duplicates,
|
||||
"unknown_export_fields": [key for key in raw_fields if key not in EXPORTED_KEYS]}
|
||||
pin(rel)
|
||||
|
||||
catalog_text = (ROOT / CATALOG).read_text()
|
||||
NORMAL_PATHS = set()
|
||||
for key in ["FACILITY_PATHS", "INFRASTRUCTURE_FACILITY_PATHS", "RESCUE_FACILITY_PATHS", "ORIGINAL_FACILITY_PATHS"]:
|
||||
chunk = re.search(r"const " + key + r": Array\[String\] = \[(.*?)\]", catalog_text, re.S)[1]
|
||||
NORMAL_PATHS.update(re.findall(r'"res://([^"]+)"', chunk))
|
||||
|
||||
# Annex rows explicitly describe prior implementation, not original/individual approval.
|
||||
ANNEX_ROWS = {}
|
||||
ANNEX_CARDS = {}
|
||||
annex_id = None
|
||||
for n, line in enumerate((ROOT / ANNEX).read_text().splitlines(), 1):
|
||||
m = re.match(r"### .*(([a-z_]+))", line)
|
||||
if m:
|
||||
annex_id = m[1]
|
||||
ANNEX_CARDS[annex_id] = n
|
||||
ANNEX_ROWS[annex_id] = {}
|
||||
cells = [c.strip() for c in line.strip().strip("|").split("|")]
|
||||
if annex_id and line.startswith("|") and len(cells) == 4 and re.fullmatch(r"`\w+`", cells[1]):
|
||||
ANNEX_ROWS[annex_id][cells[1].strip("`")] = {"reported_value": cells[2], "reported_origin": cells[3],
|
||||
"source": anchor(ANNEX, [n], "retained_legacy_implementation_reference")}
|
||||
|
||||
|
||||
def current(fid, field):
|
||||
if fid is None or fid not in DEFINITIONS:
|
||||
return {"value": None, "raw": None, "origin": "no_definition", "source": None}
|
||||
d = DEFINITIONS[fid]
|
||||
if field in d["fields"]:
|
||||
f = d["fields"][field]
|
||||
return {"value": f["value"], "raw": f["raw"], "origin": "explicit_tres",
|
||||
"source": anchor(d["path"], [f["line"]], "current_implementation_only")}
|
||||
if field in DEFAULTS:
|
||||
f = DEFAULTS[field]
|
||||
return {"value": f["value"], "raw": f["raw"], "origin": "inherited_class_default",
|
||||
"source": anchor(DEF, [f["line"]], "current_implementation_only")}
|
||||
return {"value": None, "raw": None, "origin": "not_a_definition_field", "source": None}
|
||||
|
||||
|
||||
decision_data = json.loads((ROOT / DECISIONS).read_text())
|
||||
APPROVALS = {d["id"]: {**d, "source": anchor(DECISIONS, list(range(findline(DECISIONS, '"id": "' + d["id"] + '"'), findline(DECISIONS, '"id": "' + d["id"] + '"') + 3)),
|
||||
"confirmed_user_decision")} for d in decision_data["confirmed"]}
|
||||
APPROVED_VALUES = {
|
||||
"chapel": {"footprint": [4, 3], "layer": 0, "construction_cost": {"gold": 1200, "materials": 12},
|
||||
"build_minutes": 180, "customer_capacity": 8, "staff_min": 1, "staff_max": 2,
|
||||
"required_roles": ["cleric"], "base_service_minutes": 60, "base_price": 20,
|
||||
"faith_per_service": 1, "maintenance_gold": 6},
|
||||
"cleaning_room": {"footprint": [2, 1], "layer": 1, "construction_cost": {"gold": 350, "materials": 4},
|
||||
"build_minutes": 60, "customer_capacity": 0, "staff_min": 1, "staff_max": 2,
|
||||
"required_roles": ["cleaner"], "base_price": 0, "maintenance_gold": 2},
|
||||
}
|
||||
F_FIELDS = {
|
||||
"F01": ["construction_cost", "build_minutes"],
|
||||
"F02": ["maintenance_gold", "maintenance_mana"],
|
||||
"F03": ["base_price"],
|
||||
"F04": ["footprint", "layer", "zone"],
|
||||
"F05": ["required_roles", "primary_skill", "staff_min", "service_types", "default_service_type", "service_profiles", "base_service_minutes"],
|
||||
"F06": [], "F07": ["customer_capacity", "capacity", "queue_capacity"],
|
||||
"F08": ["staff_max"], "F09": [], "F10": ["service_attribute"],
|
||||
}
|
||||
FIELD_DECISIONS = {"customer_capacity": "D02", "staff_max": "D02", "service_attribute": "D01",
|
||||
"maintenance_mana": "D04"}
|
||||
GROUP_LINES = {"10.1": 199, "10.2": 201, "10.3": 203, "10.4": 205, "10.5": 207, "10.6": 209, "10.7": 211}
|
||||
|
||||
|
||||
def parameter(fid, field, original=None, original_source=None):
|
||||
c = current(fid, field)
|
||||
approved = APPROVED_VALUES.get(fid, {}).get(field)
|
||||
approval = None
|
||||
if field in APPROVED_VALUES.get(fid, {}):
|
||||
aid = "A04" if fid == "chapel" else "A05"
|
||||
approval = {"value": approved, "decision_id": aid, "certainty": "approved_initial_trial_value", "source": APPROVALS[aid]["source"]}
|
||||
state = "approved_initial_trial_value"
|
||||
comparison = "match" if c["value"] == approved else "mismatch_or_missing"
|
||||
elif original is not None:
|
||||
state = "original_explicit_value"
|
||||
comparison = "match" if c["value"] == original else "mismatch_or_missing"
|
||||
elif field == "maintenance_gold":
|
||||
gold = current(fid, "construction_cost")["value"]
|
||||
calculation = max((gold["gold"] + 199) // 200, 1) if isinstance(gold, dict) else None
|
||||
approval = {"formula": "0.5% of construction Gold per day; preserved integer trial rule: ceil, minimum 1G",
|
||||
"conditional_value_using_current_build_cost": calculation, "decision_id": "A02",
|
||||
"construction_cost_is_individually_approved": fid in APPROVED_VALUES,
|
||||
"source": APPROVALS["A02"]["source"],
|
||||
"integer_rule_source": anchor(UPKEEP, [58], "approved_trial_implementation_record"),
|
||||
"certainty": "approved_formula_current_basis_may_be_legacy"}
|
||||
state = "approved_formula_with_legacy_basis" if calculation is not None else "approved_formula_basis_missing"
|
||||
comparison = "conditional_formula_match" if calculation is not None and c["value"] == calculation else "conditional_formula_mismatch_or_missing"
|
||||
elif c["origin"] == "no_definition":
|
||||
state = "not_set_no_definition"
|
||||
comparison = "no_authoritative_value"
|
||||
elif field == "customer_capacity" and c["value"] == -1 or field == "service_attribute" and c["value"] == "":
|
||||
state = "explicitly_unset_required_contract"
|
||||
comparison = "no_authoritative_value"
|
||||
else:
|
||||
state = "current_value_without_individual_approval"
|
||||
comparison = "no_authoritative_value"
|
||||
return {"field": field, "value_status": state,
|
||||
"original": {"value": original, "source": original_source} if original is not None else None,
|
||||
"individual_or_formula_approval": approval, "current": c,
|
||||
"annex_A_legacy": ANNEX_ROWS.get(fid, {}).get(field),
|
||||
"comparison": comparison, "decision_id": FIELD_DECISIONS.get(field),
|
||||
"acceptance_state": "unverified_static_audit_only"}
|
||||
|
||||
|
||||
def source_function(name):
|
||||
facts = {
|
||||
"一般客室": ("睡眠欲解消・宿泊", [79, 167]),
|
||||
"上級客室": ("上級宿泊", [167]),
|
||||
"VIPスイート": ("VIP宿泊・覗き対応・要高景観", [167, 199]),
|
||||
"ドミトリー": ("地下の宿泊施設", [199]),
|
||||
"大衆食堂": ("食欲解消・厨房とセット運用", [79, 168]),
|
||||
"レストラン": ("飲食・厨房とセット運用", [168]),
|
||||
"酒場": ("飲食・厨房とセット運用・騒音発生", [168, 201]),
|
||||
"屋台": ("飲食施設", [200, 201]),
|
||||
"大浴場": ("衛生・覗き対応", [169, 203]),
|
||||
"カジノ": ("娯楽欲解消・イカサマ率設定をLvUPで解放", [79, 169, 179]),
|
||||
"闘技場": ("娯楽施設", [169, 203]),
|
||||
"礼拝堂": ("礼拝・寄付・完了時信仰加算", [169, 203]),
|
||||
"風俗個室": ("地下サービス・覗き対応・LvUPでハードプレイ設定", [170, 179, 205]),
|
||||
"高級ソープ": ("地下アングラ施設", [170, 205]),
|
||||
"SM拷問室": ("地下アングラ施設", [170, 205]),
|
||||
"闇市場": ("地下アングラ施設", [170, 205]),
|
||||
"厨房": ("表層飲食店の裏で供給", [168, 173]),
|
||||
"倉庫": ("物流施設", [173]),
|
||||
"待機室": ("非番従業員の体力回復・待機", [109, 172]),
|
||||
"従業員寮": ("従業員管理・夜間イベント対応", [172]),
|
||||
"警備員詰所": ("従業員管理・警備員の拠点", [107, 172]),
|
||||
"清掃員室": ("清掃員の裏層拠点", [106, 173]),
|
||||
"医務室": ("物流・救護に関わる裏層施設", [110, 173]),
|
||||
"覗き部屋": ("VIP/浴場/風俗の裏限定・裏通路寸断・記録係配置で脅迫ネタ収集", [52, 53, 174]),
|
||||
"地下牢": ("地下特殊施設", [174, 209]),
|
||||
"マナ炉": ("地下特殊施設・奴隷消費", [174, 209]),
|
||||
"蔓の昇降機": ("客用・定員あり・垂直移動", [86, 176]),
|
||||
"業務用EV": ("裏層・大容量・高速の垂直移動", [86, 176]),
|
||||
"ワープ": ("瞬間移動・マナコスト大", [86, 176]),
|
||||
"魔法砲台": ("外壁の防衛設備", [210, 211]),
|
||||
}
|
||||
text, lines = facts[name]
|
||||
return {"condition": text, "source": anchor(BASE, lines, "specified_original")}
|
||||
|
||||
|
||||
def relation(name):
|
||||
if name in ["大衆食堂", "レストラン", "酒場"]:
|
||||
return {"required": True, "condition": "裏層の厨房とセット運用。関連先は配置の存在だけでなく実稼働を評価する。",
|
||||
"target_kind": "kitchen", "source": anchor(BASE, [168, 173], "specified_original")}
|
||||
if name == "厨房":
|
||||
return {"required": True, "condition": "表層飲食店の裏に配置。供給先との対応を保持。", "target_kind": "front_food_facility",
|
||||
"source": anchor(BASE, [173], "specified_original")}
|
||||
if name == "覗き部屋":
|
||||
return {"required": True, "condition": "VIP/浴場/風俗の裏限定。複数関連条件のAND/ORではなく対象種別の許可集合。",
|
||||
"target_kind": ["VIP", "bath", "adult_room"], "source": anchor(BASE, [52, 174], "specified_original")}
|
||||
return {"required": None, "condition": "個別の関連先は原本/A01〜A05に指定なし。空文字を正式な不要判定としない。",
|
||||
"target_kind": None, "source": None}
|
||||
|
||||
|
||||
mapping = json.loads((ROOT / "docs/plans/original-completion-2026-09-29/execution/original_facility_mapping.json").read_text())
|
||||
records = []
|
||||
for ix, mapped in enumerate(mapping, 1):
|
||||
fid, name = mapped["definition_id"], mapped["name"]
|
||||
source = anchor(BASE, [GROUP_LINES[mapped["section"]]], "specified_original")
|
||||
size = [int(v) for v in mapped["original_size"].split("x")] if "x" in mapped["original_size"] else None
|
||||
layer = 1 if mapped["original_layer_zone"].startswith("BACK") else 0 if mapped["original_layer_zone"].startswith("FRONT") else None
|
||||
zone = 3 if "地下" in mapped["original_layer_zone"] else None
|
||||
original_values = {"footprint": size, "layer": layer, "zone": zone}
|
||||
parms = {field: parameter(fid, field, original_values.get(field), source if field in original_values else None)
|
||||
for fields in F_FIELDS.values() for field in fields}
|
||||
parms.update({field: parameter(fid, field) for field in ["max_level", "upgrade_costs", "unlock_rank", "research_requirements", "policy_schema", "faith_per_service"]})
|
||||
# Lv1 data is recorded as current base data, never assumed approved for every level.
|
||||
levels = []
|
||||
for lv in [1, 2, 3]:
|
||||
levels.append({"level": lv,
|
||||
"authoritative_level_specific_price": None,
|
||||
"authoritative_level_specific_customer_capacity": None,
|
||||
"authoritative_level_specific_staff_min": None,
|
||||
"authoritative_level_specific_staff_max": None,
|
||||
"approved_base_trial_values_initial_level_candidate": APPROVED_VALUES.get(fid) if lv == 1 else None,
|
||||
"base_approval_has_explicit_level_scope": False,
|
||||
"base_approval_use_note": "A04/A05初期試験値をLv1初期値候補に結び付ける。承認本文にLv別変更はない。Lv2/3への複写や不変性の承認は主張しない。" if fid in APPROVED_VALUES else None,
|
||||
"current_level_specific_definition": None,
|
||||
"current_base_parameters_reference": list(parms) if lv == 1 and fid else [],
|
||||
"upgrade_cost_from_previous_current_legacy": current(fid, "upgrade_costs")["value"][lv-2] if lv > 1 and fid and len(current(fid, "upgrade_costs")["value"]) >= lv-1 else None,
|
||||
"unlock_condition": None, "policy_unlock_assignment": None,
|
||||
"status": "no_approved_level_ladder", "decision_ids": ["D02", "D04"],
|
||||
"source": anchor(PLAN, [207], "user_requested_implementation_plan")})
|
||||
r = {"id": f"FVC.original.{ix:02d}", "name": name, "source_section": mapped["section"],
|
||||
"definition_id": fid, "definition_path": DEFINITIONS[fid]["path"] if fid else None,
|
||||
"definition_exists": bool(fid), "normal_registered_static": bool(fid and DEFINITIONS[fid]["path"] in NORMAL_PATHS),
|
||||
"registration_state": "normal_registered" if fid and DEFINITIONS[fid]["path"] in NORMAL_PATHS else "definition_only" if fid else "no_definition",
|
||||
"original_value_source": source,
|
||||
"source_function": source_function(name),
|
||||
"major_items": {group: {"source": anchor(ADDITION, [984 + int(group[1:]) - 1], "retained_user_addition_record"),
|
||||
"parameter_fields": fields, "acceptance_state": "unverified"} for group, fields in F_FIELDS.items()},
|
||||
"parameters": parms,
|
||||
"placement_conditions": {"height": "高層" if "高層" in mapped["original_layer_zone"] else None,
|
||||
"height_threshold": None, "high_beauty_required": name == "VIPスイート", "beauty_threshold": None,
|
||||
"outer_wall_required": name == "魔法砲台", "source": source,
|
||||
"access_and_support": anchor(BASE, [164], "specified_original")},
|
||||
"related_facility": {"source_required_relation": relation(name), "current_definition_field": "not_present",
|
||||
"current_instance_default": {"id": "", "operating": True,
|
||||
"source": anchor(INSTANCE, [49, 50, 84, 85], "current_implementation_only")},
|
||||
"runtime_creation_sets_related_id": False,
|
||||
"runtime_creation_source": anchor(RUNTIME, [findline(RUNTIME, 'func _register_operational_facility('), findline(RUNTIME, '&"customer_capacity": definition.customer_capacity')], "current_implementation_only"),
|
||||
"single_dependency_contract_reference": "common_contracts.related_facility"},
|
||||
"operation_state_contract_reference": "common_contracts.operation_state",
|
||||
"surcharge_contract_reference": "common_contracts.need_match_surcharge",
|
||||
"need_match_inputs": {
|
||||
"attribute": parms["service_attribute"],
|
||||
"deficit_threshold": {"value": None, "status": "required_before_dependent_charging", "decision_id": "D01"},
|
||||
"surcharge_amount_or_rate": {"value": None, "status": "required_before_dependent_charging", "decision_id": "D01"},
|
||||
"source": anchor(DECISIONS, [findline(DECISIONS, '"id": "D01"')], "pending_required_user_value"),
|
||||
"deficit_threshold_not_inferred_from_need_effects": True},
|
||||
"generic_capacity_contract": {
|
||||
"is_customer_capacity": False,
|
||||
"current": parms["capacity"]["current"],
|
||||
"profile_capacity_units": {key: value.get("capacity_units") for key, value in (current(fid, "service_profiles")["value"] or {}).items()},
|
||||
"queue_capacity_is_separate": True,
|
||||
"current_runtime_use": "service slots (definition.capacity clamped to at least 1), not unique active guest count",
|
||||
"source": anchor(INSTANCE, [247, 248, 253, 261, 270], "current_implementation_only")},
|
||||
"level_contracts": levels,
|
||||
"unresolved_value_fields": [field for field, p in parms.items() if p["value_status"] in ["explicitly_unset_required_contract", "not_set_no_definition", "approved_formula_basis_missing"]],
|
||||
"unapproved_current_fields": [field for field, p in parms.items() if p["value_status"] == "current_value_without_individual_approval"],
|
||||
"definition_anomalies": {key: DEFINITIONS[fid][key] for key in ["duplicate_assignments", "unknown_export_fields"]} if fid else None,
|
||||
"runtime_acceptance_state": "unverified_no_engine_run_in_this_audit"}
|
||||
records.append(r)
|
||||
|
||||
supplemental = []
|
||||
matched = {r["definition_id"] for r in records if r["definition_id"]}
|
||||
for fid, definition in DEFINITIONS.items():
|
||||
if fid in matched:
|
||||
continue
|
||||
supplemental.append({"id": f"FVC.supplemental.{fid}", "definition_id": fid,
|
||||
"display_name": definition["fields"]["display_name"]["value"], "definition_path": definition["path"],
|
||||
"normal_registered_static": definition["path"] in NORMAL_PATHS,
|
||||
"chapter_10_mapping": None,
|
||||
"major_item_applicability": "not_finalized_for_facilities_outside_chapter_10",
|
||||
"parameters": {field: parameter(fid, field) for fields in F_FIELDS.values() for field in fields},
|
||||
"annex_A_card": anchor(ANNEX, [ANNEX_CARDS[fid]], "retained_legacy_implementation_reference") if fid in ANNEX_CARDS else None})
|
||||
|
||||
missing_blueprints = []
|
||||
for r in records:
|
||||
if r["definition_exists"]:
|
||||
continue
|
||||
missing_blueprints.append({"contract_id": r["id"], "name": r["name"], "production_id": None,
|
||||
"safe_original_values": {key: r["parameters"][key]["original"] for key in ["footprint", "layer", "zone"] if r["parameters"][key]["original"]},
|
||||
"safe_original_conditions": [r["source_function"], r["placement_conditions"], r["related_facility"]["source_required_relation"]],
|
||||
"unknowns": ["production_id", "construction_cost", "build_minutes", "maintenance_mana", "base_price", "customer_capacity",
|
||||
"staff_min", "staff_max", "service_attribute", "Lv1_to_Lv3_values", "unlock_conditions"],
|
||||
"register_and_allow_build_now": False,
|
||||
"reason": "原本で確定する寸法・層・地域・機能だけを設計用blueprintへ抽出可能。FacilityDefinition既定値で空欄を埋めて営業可能とする承認はない。"})
|
||||
|
||||
common = {
|
||||
"operation_state": {"authoritative_states": ["INACTIVE", "OPERATING", "SUSPENDED"],
|
||||
"persistent_suspension": "破壊/摘発後の休止は関連施設復旧だけでは解除しない。",
|
||||
"source": anchor(ADDITION, [989], "retained_user_addition_record"),
|
||||
"current_code": anchor(INSTANCE, [8, 186, 198, 219, 225, 229], "current_implementation_only"),
|
||||
"mana_zero": {"value": 0, "behavior": "全館機能停止", "source": anchor(BASE, [183], "specified_original")},
|
||||
"resumption_conditions": {"value": None, "status": "explicitly_unset_optional_extension", "decision_id": "D03",
|
||||
"is_mandatory_missing": False}, "acceptance_state": "unverified"},
|
||||
"related_facility": {"cardinality": "single", "flag": "actual_operation_state", "cycles": "reject",
|
||||
"source": anchor(ADDITION, [992], "retained_user_addition_record"),
|
||||
"current_code": anchor(SERVICE, [141, 146, 151, 155, 359, 365], "current_implementation_only"),
|
||||
"multi_relation_AND_OR": {"value": None, "status": "explicitly_unset_optional_extension", "decision_id": "D03", "is_mandatory_missing": False},
|
||||
"individual_assignment": "本文明示の厨房連携や覗き対象を汎用単一関連APIが自動割当済みとは扱わない。", "acceptance_state": "unverified"},
|
||||
"need_match_surcharge": {"attribute_vocabulary": ["appetite", "relaxation", "sexual", "gambling"],
|
||||
"source": anchor(ADDITION, [986, 993, 995], "retained_user_addition_record"),
|
||||
"deficit_threshold": {"value": None, "status": "required_before_dependent_charging", "decision_id": "D01"},
|
||||
"surcharge_amount_or_rate": {"value": None, "status": "required_before_dependent_charging", "decision_id": "D01"},
|
||||
"per_facility_attribute": {"value": None, "status": "required_before_dependent_charging", "decision_id": "D01"},
|
||||
"multiple_attributes_and_duplicate_surcharge": {"value": None, "status": "explicitly_unset_optional_extension", "is_mandatory_missing": False},
|
||||
"nonmatching_utilization": "allowed", "quote_api_supplied_amount_sentinel": -1,
|
||||
"current_quote_code": anchor(PRICING, [findline(PRICING, 'static func quote_service(')], "current_implementation_only"),
|
||||
"current_normal_pricing": "pending_service_readmodel returns unresolved inputs; real surcharge not connected",
|
||||
"current_runtime": anchor(RUNTIME, [findline(RUNTIME, 'func get_facility_service_pricing_readmodel(')], "current_implementation_only"),
|
||||
"guest_need_effects_are_attribute_assignment": False,
|
||||
"guest_need_0_100_is_deficit_threshold": False,
|
||||
"Anri_multiplier": {"percent": 20, "scope": "assigned floor fees; guest payment equals facility revenue", "source": APPROVALS["A03"]["source"]},
|
||||
"composition_order_with_Anri": {"value": None, "status": "requires_contract_before_surcharge_connection", "source": anchor(PLAN, [206], "user_requested_implementation_plan")},
|
||||
"acceptance_state": "unverified"},
|
||||
"generic_capacity": {"is_customer_capacity": False, "waiting_is_customer_count": False,
|
||||
"definition_default": 0, "runtime_minimum_slots": 1,
|
||||
"source": anchor(DEF, [61, 63, 64], "current_implementation_only"),
|
||||
"runtime_source": anchor(RUNTIME, [findline(RUNTIME, '&"capacity": maxi(definition.capacity, 1)')], "current_implementation_only"),
|
||||
"customer_enforcement": anchor(INSTANCE, [247, 248, 253, 261, 270], "current_implementation_only"),
|
||||
"examples": {"kitchen": "generic20 / profile batch4", "warehouse": "generic100; inventory/handling meaning", "staff_dormitory": "generic12 staff support / current customer0", "cleaning_room": "generic0 / approved customer0 / runtime slots clamped1"}},
|
||||
"levels": {"planned_levels": [1, 2, 3], "original_max_level_number": None,
|
||||
"original": anchor(BASE, [165, 179], "specified_original"), "plan": anchor(PLAN, [207], "user_requested_implementation_plan"),
|
||||
"current_max_level_default": 3, "default_is_approved_ladder": False,
|
||||
"current_level_specific_price_staff_customer_data": "none in FacilityDefinition",
|
||||
"unapproved_upgrade_costs": "retained per-facility raw data, never treated as approved prices/capacities",
|
||||
"unlock_rank_default_1_is_approved": False,
|
||||
"normal_build_code_checks_unlock_rank_or_research": False,
|
||||
"normal_build_source": anchor(RUNTIME, [findline(RUNTIME, 'func _execute_build_facility('), findline(RUNTIME, 'definition.build_minutes,')], "current_implementation_only"),
|
||||
"acceptance_state": "unverified"},
|
||||
"upkeep": {"A01": APPROVALS["A01"], "A02": APPROVALS["A02"],
|
||||
"individual_overrides": ["chapel:6G/day", "cleaning_room:2G/day"],
|
||||
"formula_is_approval_of_build_cost": False,
|
||||
"annex_zero_maintenance_values_are_current": False,
|
||||
"acceptance_state": "unverified_in_this_value_audit"},
|
||||
"cleaning_A05": {"cleanliness_range": [0, 100], "initial": 100, "reserve_at_or_below": 75,
|
||||
"onsite_minutes": 8, "restore": 25, "use_only_defined_dirt": True, "source": APPROVALS["A05"]["source"],
|
||||
"not_service_attribute_or_matching_deficit_threshold": True},
|
||||
}
|
||||
|
||||
count_status = Counter(p["value_status"] for r in records for p in r["parameters"].values())
|
||||
summary = {"original_facilities": len(records), "defined": sum(r["definition_exists"] for r in records),
|
||||
"normal_registered_static": sum(r["normal_registered_static"] for r in records),
|
||||
"definition_only": [r["definition_id"] for r in records if r["registration_state"] == "definition_only"],
|
||||
"undefined": len(missing_blueprints), "all_current_definition_files": len(DEFINITIONS), "supplemental_defined_outside_chapter10": len(supplemental),
|
||||
"original_facility_parameter_records": sum(len(r["parameters"]) for r in records),
|
||||
"parameter_value_status_counts": dict(sorted(count_status.items())),
|
||||
"customer_capacity": dict(Counter(r["parameters"]["customer_capacity"]["value_status"] for r in records)),
|
||||
"staff_max": dict(Counter(r["parameters"]["staff_max"]["value_status"] for r in records)),
|
||||
"service_attribute": dict(Counter(r["parameters"]["service_attribute"]["value_status"] for r in records)),
|
||||
"all_current_definition_service_attributes_empty": all(current(fid, "service_attribute")["value"] == "" for fid in DEFINITIONS),
|
||||
"all_original_defined_dimensions_match": all(r["parameters"]["footprint"]["comparison"] == "match" for r in records if r["definition_exists"]),
|
||||
"all_original_defined_layers_match": all(r["parameters"]["layer"]["comparison"] == "match" for r in records if r["definition_exists"]),
|
||||
"approved_runtime_acceptances_counted": 0,
|
||||
"is_final_all_facility_catalog": False,
|
||||
"optional_unset_D03_counted_as_mandatory_missing": 0,
|
||||
"counts_are_field_observations_not_atomic_completion_denominator": True}
|
||||
|
||||
recommendations = [
|
||||
{"id": "P09-safe-01", "action": "A04/A05の承認済み人数・最少/最大職員・料金の契約を通常UIと保存へつなぐ境界検証を先行。current値を変更する前にsource/value確定と表示を区別。",
|
||||
"new_numeric_decision_needed": False, "dependencies": ["existing approved facilities"], "acceptance": "customer_count excludes queued guests; over-limit refusal preserves state; staff_min and staff_max remain distinct; read-only pricing inspection"},
|
||||
{"id": "P09-safe-02", "action": "customer_capacity=-1とservice_attribute空文字をUIで未設定と表示し、現在明示2/0も承認由来の別表示にする。generic容量・人数・待機人数を分ける。",
|
||||
"new_numeric_decision_needed": False, "dependencies": ["existing readmodels"], "acceptance": "unset is never presented as 0/unlimited/approved; fields and save values unchanged"},
|
||||
{"id": "P09-safe-03", "action": "13未定義施設のsource-only blueprint/設計カタログを追加。確定寸法/層/地下/高層/外壁/厨房/覗き制約だけを保持し、未確定の値はnullとする。通常建設へ登録しない。",
|
||||
"new_numeric_decision_needed": False, "dependencies": [], "acceptance": "no fabricated costs/capacities/IDs; cannon footprint remains null; no FacilityDefinition default leakage into playable construction"},
|
||||
{"id": "P09-safe-04", "action": "食堂・酒場の厨房セット運用を現単一関連APIと実稼働判定へ接続する契約/候補検証を用意。厨房停止・破壊・未配置・保存復元時の動作を検証。",
|
||||
"new_numeric_decision_needed": False, "dependencies": ["normal source-confirmed kitchen relationship", "placement selection contract"],
|
||||
"acceptance": "cannot operate solely because a kitchen exists; do not invent multi dependency or automatic suspension recovery"},
|
||||
{"id": "P09-safe-05", "action": "Lv1–3の明示tableと未設定guardを作り、Lv2/3料金や人数をbase値の複写で確定しない。既存upgrade_costsとrank1は旧実装欄に保全。",
|
||||
"new_numeric_decision_needed": False, "dependencies": ["save schema/version policy before persisted model changes"],
|
||||
"acceptance": "unknown level action rejected before charging or mutation; existing saves preserved; known A04/A05 base values reused without claiming level scope"},
|
||||
{"id": "P09-safe-06", "action": "欲求加算はD01確定まで請求を接続せず、nonmatch利用可・未解決見積拒否・財布/収入一致の計算契約と比較例を準備する。",
|
||||
"new_numeric_decision_needed": False, "dependencies": ["D01 only before actual surcharge"], "acceptance": "no threshold copied from cleaning/guest needs; no attribute inferred from need_effects; A03 composition explicitly specified before charge"},
|
||||
]
|
||||
|
||||
for p in [BASE, ANNEX, DECISIONS, ADDITION, PLAN, DEF, CATALOG, INSTANCE, SERVICE, PRICING, RUNTIME, UPKEEP]:
|
||||
pin(p)
|
||||
document = {"schema_version": 1, "audit_id": "P09-facility-value-static-2026-10-02-v1",
|
||||
"source_head": subprocess.check_output(["git", "rev-parse", "HEAD"], cwd=ROOT, text=True).strip(),
|
||||
"source_is_working_tree": True, "method": "read-only text parsing and semantic source audit; no engine, no production mutation",
|
||||
"authority_order": ["specified original", "confirmed user A01-A05 and facility addition", "annex A legacy implementation reference", "current implementation"],
|
||||
"status_definitions": {"original_explicit_value": "原本の明示値。現値一致は静的比較であり受入ではない。",
|
||||
"approved_initial_trial_value": "個別承認の初期試験値。最終バランスや全Lv値の承認を意味しない。",
|
||||
"approved_formula_with_legacy_basis": "率・整数則は承認済み。建築費の現値は個別承認値とは限らない。",
|
||||
"current_value_without_individual_approval": "旧/現在コードに値あり。原本/個別承認の根拠は未確認。勝手に採用値へ格上げしない。",
|
||||
"explicitly_unset_required_contract": "当該機能の本番接続前に必要な未設定値。",
|
||||
"not_set_no_definition": "独立した定義なし。原本の確定項目は別欄へ抽出。",
|
||||
"explicitly_unset_optional_extension": "未採用/未設定維持が許される拡張。必須欠損に数えない。"},
|
||||
"summary": summary, "source_pins": PINS, "approvals": APPROVALS,
|
||||
"common_contracts": common, "original_facilities": records, "missing_definition_blueprints": missing_blueprints,
|
||||
"supplemental_current_definitions": supplemental,
|
||||
"scope_pending": {"named_original_outside_chapter10": ["受付/精算所", "金庫室", "購買部", "闇銀行", "通路", "階段", "従業員ドア", "リネンシュート", "隠し扉"],
|
||||
"source": anchor(BASE, [49, 50, 86, 176, 177, 190, 191, 192, 193], "specified_original"),
|
||||
"contract": "F各施設とLv素材の適用範囲はatomic open expansion registerと同じ未確定状態。30施設/37定義の件数を最終全施設数にしない。"},
|
||||
"safe_followup_candidates": recommendations,
|
||||
"acceptance": {"completed": False, "runtime_runs_in_this_audit": 0, "source_comparison_is_runtime_acceptance": False}}
|
||||
|
||||
(OUT / "facility_value_contracts.json").write_text(json.dumps(document, ensure_ascii=False, indent=2) + "\n")
|
||||
|
||||
def brief(v):
|
||||
return "未定義" if v is None else "未設定" if v == -1 else str(v)
|
||||
|
||||
table = ["| 原本施設 | 定義/通常登録 | 原本サイズ・層 | min/max現値 | 人数専用/汎用capacity現値 | 料金現値 | 根拠 |",
|
||||
"|---|---|---|---|---|---|---|"]
|
||||
for r in records:
|
||||
p = r["parameters"]
|
||||
root_value = mapped_value = "×".join(map(str, p["footprint"]["original"]["value"])) if p["footprint"]["original"] else "未指定"
|
||||
layer = p["layer"]["original"]["value"] if p["layer"]["original"] else None
|
||||
place = root_value + (" 裏" if layer == 1 else " 表" if layer == 0 else " 外壁")
|
||||
approved_label = "A04初期値" if r["definition_id"] == "chapel" else "A05初期値" if r["definition_id"] == "cleaning_room" else "数値は旧/現実装・個別根拠未確認" if r["definition_exists"] else "原本の寸法等のみ確定"
|
||||
table.append(f"| {r['name']} | {r['registration_state']} | {place} | {brief(p['staff_min']['current']['value'])}/{brief(p['staff_max']['current']['value'])} | {brief(p['customer_capacity']['current']['value'])}/{brief(p['capacity']['current']['value'])} | {brief(p['base_price']['current']['value'])} | {approved_label} |")
|
||||
|
||||
blueprint_table = ["| 未定義施設 | 確定できる項目 | 原本行 |",
|
||||
"|---|---|---|"]
|
||||
for bp in missing_blueprints:
|
||||
r = next(r for r in records if r["id"] == bp["contract_id"])
|
||||
dimension = bp["safe_original_values"].get("footprint", {}).get("value")
|
||||
dims = "×".join(map(str, dimension)) if dimension else "寸法未指定"
|
||||
blueprint_table.append(f"| {bp['name']} | {dims}; {r['source_function']['condition']}; {mapping[int(r['id'].split('.')[-1])-1]['original_layer_zone']} | {GROUP_LINES[r['source_section']]} |")
|
||||
|
||||
readme = f"""# P09 施設必須値の再監査(2026-10-02)
|
||||
|
||||
原本30施設について、定義あり{summary['defined']}、通常登録{summary['normal_registered_static']}、定義のみ1(black_market)、未定義{summary['undefined']}を静的に確認した。現在の全`.tres`は{summary['all_current_definition_files']}件で、そのうち原本第10章の対応外は{len(supplemental)}件。これは最終の全施設数でも、実装・受入完了率でもない。今回のエンジン実行は0件、本体変更は0件。
|
||||
|
||||
機械可読台帳は `facility_value_contracts.json`。各値は原本文、A01〜A05、別冊A作成時の値と由来、現`.tres`明示値またはクラス既定値を別欄に保存し、出典行とSHAを保持する。生成は `python3 docs/plans/original-completion-2026-10-02/build_facility_value_contracts.py`。検証は `validate_facility_value_contracts.py`。承認差異の静的一致からRuntime/UI/保存のPASSを作らない。
|
||||
|
||||
原本を最上位とし、A01〜A05と会話追加10項目を重ねる。別冊Aは旧実装の完全一覧であり、数値承認書ではない。冒頭3・10・14〜19・76・106行が既定値/旧状態/仮値/異なる処理系を区別している。参照先 `docs/FACILITY_UNIT_PARAMETERS.md` は現ローカルに存在しないため、その内容を推定補完しなかった。既存の保存版TRACEABILITY 978〜995行を追加指定の記録に用いた。
|
||||
|
||||
## 30施設の現値と根拠
|
||||
|
||||
表のmin/max、人数専用、capacity、料金は現実装の観測値である。礼拝堂・清掃員室以外の数値を承認済みに扱わない。未設定は人数専用-1、未定義は独立定義なしを表す。
|
||||
|
||||
{chr(10).join(table)}
|
||||
|
||||
原本で寸法のある現定義17施設は寸法と層が静的一致する。一般的な「表」は地上専用を意味しない。地下や高層の指定は個別に保存し、魔法砲台の幅・高さ・層は捏造しない。premium_room、staff_dormitory、cleaning_room、chapelの通常登録は今回変更ではなく現基準の既存成果である。
|
||||
|
||||
## 10項目と必須値の区別
|
||||
|
||||
1. 配置コスト: 原本は資金/資材と建設ルールを指定するが、施設別コストと工期の数値はA04/A05以外に個別承認を確認できない。現値は保存しても正式値へ格上げしない。
|
||||
2. 運用コスト: A01の保有按分、休業/休止中計上、00:00精算、不足繰越とA02の0.5%初期試験率は確定。既存ceil/最低1Gを出典付きで保持する。礼拝堂6G/日、清掃員室2G/日は個別上書き。率の承認は建築費自体の個別承認を意味しない。闇市場の現maintenance_gold=既定0は、現1600Gに率を当てた条件付き8Gと異なる。未登録定義を登録する前にこの差異を解消する契約が必要。施設別マナ量は原本では未指定。原本183行のマナ0で全停止を別に保持する。
|
||||
3. 利用料金: 礼拝堂20G寄付、清掃員室利用料なしを承認初期値として保持。他施設の単価や5段階60/80/100/150/250%は旧実装値。D01の不足基準、加算額/率、属性割当は未設定。元の四欲求0〜100や清掃予約75を不足基準に流用しない。A03の20%補正は承認済みだが、新しい一致加算との順序/丸めはP09-02接続前に固定する。
|
||||
4. サイズ: 29施設の原本寸法と30対象の層/地下/高層/外壁指定を追跡。魔法砲台寸法は未指定のまま。
|
||||
5. 役割: 職種とサービス機能の原文、required_rolesとstaff_minを分離。A04聖職者min1/max2、A05清掃員min1/max2は確定。原本の記録係配置などの役割と人数の数値は同一視しない。
|
||||
6. 稼働状態: INACTIVE/OPERATING/SUSPENDEDと破壊/摘発後の持続休止は会話追加の確定契約。復帰条件D03はユーザー指定の未設定拡張であり、必須値欠損に数えない。
|
||||
7. 顧客同時利用最大人数: 承認値は礼拝堂8、清掃員室0のみ。上級客室2・従業員寮0は現明示値だが根拠未確認。残る定義13件は-1継承、未定義13件には値なし。待機者は利用人数とは別。厨房capacity20/profile生産4、倉庫capacity100、従業員寮capacity12などをcustomer_capacityへ転記しない。
|
||||
8. 配置可能従業員最大数: staff_maxはstaff_minと別。2施設の個別承認、15施設の旧/現値、13施設の未定義を保持する。Lv別変更もD02の未確定に残す。
|
||||
9. 関連施設/稼働フラグ: 原本168・173行の食堂/レストラン/酒場と厨房セット、52・174行の覗き対象などを個別追跡。現汎用単一依存APIは循環拒否と実稼働評価を持つが、建設からのcreate_instanceにはrelated_facility_idを渡していない。原本の対応を自動設定済みと認定しない。D03の複数依存AND/ORは未設定維持が許され、必須欠損へ数えない。
|
||||
10. 属性: 許可型appetite/relaxation/sexual/gamblingは確定、施設への割当は全37定義で空文字。Runtimeのhunger/sleep/hygiene/medical/leisure/intimacy効果と提供属性は別の名前空間・用途。効果タグから勝手に属性を選ばない。不一致の利用は許可する。
|
||||
|
||||
## Lv1〜3と解放
|
||||
|
||||
全30施設にLv1/2/3を別レコードとして置き、価格・人数・staff_min/max・解放条件・Policy解放割当を未確定として追跡する。A04/A05の初期値をLv1候補へ参照するが、承認本文にLv指定はなく、Lv2/3で不変とも自動増加とも承認されていない。原本165行のLvMAXに数値はない。計画P09-03のLv1〜3と現既定max_level3を混同しない。
|
||||
|
||||
現FacilityDefinitionにレベル別料金/人数の表はない。厨房などのupgrade_costsは旧実装の費用列であり、レベル別営業値ではない。unlock_rank既定1は正式な個別解放承認ではなく、現通常建設コマンドにunlock_rank/research_requirementsの評価もない。別冊Aの世界樹ランク表や初回Day案内は派生計画/実装欄に保全し、原本施設別解放条件へ昇格しない。原本179行の価格、食材、混浴、ハード設定、イカサマ率のLv解放は機能要件として残り、どのLvでどの値にするかを捏造しない。
|
||||
|
||||
## 未定義13施設から抽出できる範囲
|
||||
|
||||
{chr(10).join(blueprint_table)}
|
||||
|
||||
これらはsource-only blueprintとして先行定義できる。費用・人数・職員上限・マナ量・属性・個別解放・Lv2/3の値が未確定のまま、FacilityDefinitionの既定100G/1資材/60分などを適用して通常建設へ公開しない。既存地下客室をドミトリー、地下VIPサロンをVIPスイート、魔力制御室をマナ炉の完成代替と扱わない。
|
||||
|
||||
## 次に進める低依存作業
|
||||
|
||||
{chr(10).join('- ' + r['action'] for r in recommendations)}
|
||||
|
||||
この監査は値の出典と欠損を整理したもので、P09や各施設の受入完了ではない。通常操作、グループ利用中変更、日跨ぎ、途中保存、修正後同一ソース回帰の証拠は引き続き必要である。第9章経済施設、通路/階段/ドア/リネンシュート/隠し扉へのF各施設/Lv素材適用範囲はatomicのopen expansion registerと同じ未確定状態を維持する。
|
||||
|
||||
静的データの追加観測として、医務室定義には同値のstaff_max/capacity二重代入と、FacilityDefinitionに宣言がないqueue_staff_minがある。台帳のdefinition_anomaliesへ保存し、今回Runtimeへの影響を推測したり本体を修正したりしない。validatorは49出典ファイルのSHA、30施設/780値/90Lvの構造、全出典行、D03除外、架空値拒否を確認する。PASSは台帳の整合性だけを意味する。
|
||||
"""
|
||||
(OUT / "P09_VALUE_CONTRACTS.md").write_text(readme)
|
||||
print(json.dumps(summary, ensure_ascii=False, indent=2))
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
+20
@@ -0,0 +1,20 @@
|
||||
// Diagnostic only. Plain fixed C++ state shared by this DSO's instances.
|
||||
// Single main-thread, one isolated headless tick only; not concurrent-safe.
|
||||
// No Godot object, name, container, allocation or callback in counters.
|
||||
#include <array>
|
||||
#include <chrono>
|
||||
#include <algorithm>
|
||||
namespace tree_diag321 {
|
||||
struct Row { int64_t calls=0, inclusive_us=0, max_us=0; };
|
||||
static std::array<Row,9> rows{};
|
||||
static int64_t active=0;
|
||||
struct Timer {
|
||||
size_t index; std::chrono::steady_clock::time_point started;
|
||||
explicit Timer(size_t i) noexcept : index(i),started(std::chrono::steady_clock::now()) { ++active; }
|
||||
~Timer() noexcept {
|
||||
const auto us=std::max<int64_t>(0,std::chrono::duration_cast<std::chrono::microseconds>(std::chrono::steady_clock::now()-started).count());
|
||||
auto &r=rows[index]; ++r.calls; r.inclusive_us+=us; r.max_us=std::max(r.max_us,us); --active;
|
||||
}
|
||||
Timer(const Timer&)=delete; Timer& operator=(const Timer&)=delete;
|
||||
};
|
||||
}
|
||||
+17
@@ -0,0 +1,17 @@
|
||||
// Called only outside the diagnostic tick. Reset rejects active scopes.
|
||||
bool native_profile_reset() {
|
||||
if (tree_diag321::active != 0) return false;
|
||||
tree_diag321::rows = {}; return true;
|
||||
}
|
||||
Dictionary native_profile_snapshot() {
|
||||
Dictionary result;
|
||||
const char *labels[9] = {"performance_record_sample","project_guest_candidates","facility_access_cells","guest_path_minutes","nearest_guest_access_impl","resolve_ready_guest_point","builtin_walk_edge_rows","search_impl","guest_candidate_batch"};
|
||||
for(size_t i=0;i<9;++i) {
|
||||
Dictionary row;
|
||||
row["calls"]=tree_diag321::rows[i].calls;
|
||||
row["inclusive_us"]=tree_diag321::rows[i].inclusive_us;
|
||||
row["max_us"]=tree_diag321::rows[i].max_us;
|
||||
result[labels[i]]=row;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
+75
@@ -0,0 +1,75 @@
|
||||
#pragma once
|
||||
// Standalone numeric prototype; not connected to Godot or production.
|
||||
#include <algorithm>
|
||||
#include <cstdint>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
#include <limits>
|
||||
#include <map>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
namespace the_tree_native {
|
||||
using I = std::int64_t;
|
||||
struct Edge { I neighbor, cost; };
|
||||
struct Result {
|
||||
std::string status = "usable";
|
||||
std::vector<I> costs, parents, discovered, parent_discovered, settled;
|
||||
};
|
||||
I signed_add(I a, I b) {
|
||||
const auto sum = static_cast<std::uint64_t>(a) + static_cast<std::uint64_t>(b);
|
||||
if (sum <= static_cast<std::uint64_t>(std::numeric_limits<I>::max())) return static_cast<I>(sum);
|
||||
return std::numeric_limits<I>::min() + static_cast<I>(sum - (std::uint64_t(1) << 63));
|
||||
}
|
||||
template <typename EdgeProvider>
|
||||
Result search(I n, I start, const std::vector<I>& goals, EdgeProvider&& edge_provider) {
|
||||
Result r;
|
||||
if (start < 0 || start >= n) { r.status = "unusable"; return r; }
|
||||
r.costs.assign(n, std::numeric_limits<I>::max());
|
||||
r.parents.assign(n, -1);
|
||||
std::vector<unsigned char> finalized(n, 0), pending(n, 0);
|
||||
bool unknown_goal = false;
|
||||
I remaining = 0;
|
||||
for (I goal : goals) {
|
||||
if (goal < 0 || goal >= n) { unknown_goal = true; continue; }
|
||||
if (!pending[goal]) { pending[goal] = 1; ++remaining; }
|
||||
}
|
||||
r.costs[start] = 0; r.discovered.push_back(start);
|
||||
std::map<I, std::vector<I>> frontier;
|
||||
frontier[0].push_back(start);
|
||||
while (!frontier.empty()) {
|
||||
auto it = frontier.begin();
|
||||
const I entry_cost = it->first;
|
||||
auto active = std::move(it->second);
|
||||
frontier.erase(it);
|
||||
std::sort(active.begin(), active.end());
|
||||
for (I current : active) {
|
||||
if (finalized[current] || entry_cost != r.costs[current]) continue;
|
||||
finalized[current] = 1;
|
||||
r.settled.push_back(current);
|
||||
if (pending[current]) { pending[current] = 0; --remaining; }
|
||||
if (!remaining && !unknown_goal) return r;
|
||||
for (const Edge& edge : edge_provider(current)) {
|
||||
if (edge.cost <= 0) { r.status = "unusable"; return r; }
|
||||
const I neighbor = edge.neighbor;
|
||||
if (neighbor < 0 || neighbor >= n) { r.status = "unusable"; return r; }
|
||||
const I cost = signed_add(r.costs[current], edge.cost);
|
||||
if (cost < r.costs[current]) { r.status = "wrapped"; return r; }
|
||||
const I known = r.costs[neighbor];
|
||||
const bool better_parent = cost == known && r.parents[neighbor] >= 0 && current < r.parents[neighbor];
|
||||
if (cost > known || (cost == known && !better_parent)) continue;
|
||||
if (r.parents[neighbor] < 0) {
|
||||
r.parent_discovered.push_back(neighbor);
|
||||
if (neighbor != start) r.discovered.push_back(neighbor);
|
||||
}
|
||||
r.parents[neighbor] = current;
|
||||
r.costs[neighbor] = cost;
|
||||
if (finalized[neighbor]) r.settled.erase(std::remove(r.settled.begin(), r.settled.end(), neighbor), r.settled.end());
|
||||
finalized[neighbor] = 0;
|
||||
frontier[cost].push_back(neighbor);
|
||||
}
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
} // namespace the_tree_native
|
||||
BIN
Binary file not shown.
+49
@@ -0,0 +1,49 @@
|
||||
{
|
||||
"pid": 25510,
|
||||
"argv": [
|
||||
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++",
|
||||
"-o",
|
||||
"/Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p23-02-current-native-diagnostic-build-r322/objects/062-tree_numeric_core.os",
|
||||
"-c",
|
||||
"-std=c++17",
|
||||
"-isystem",
|
||||
"/Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p23-02-native-sdk-lifetime-candidate-r131/sdk/include",
|
||||
"-isystem",
|
||||
"/Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p23-02-native-sdk-lifetime-candidate-r131/sdk/gen/include",
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"-Werror",
|
||||
"-fexceptions",
|
||||
"-arch",
|
||||
"arm64",
|
||||
"-isysroot",
|
||||
"/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk",
|
||||
"-fvisibility=hidden",
|
||||
"-O3",
|
||||
"-fPIC",
|
||||
"-DMACOS_ENABLED",
|
||||
"-DUNIX_ENABLED",
|
||||
"-DTHREADS_ENABLED",
|
||||
"-DDEBUG_ENABLED",
|
||||
"-DNDEBUG",
|
||||
"-DGDEXTENSION",
|
||||
"-I/Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p23-02-current-native-diagnostic-candidate-r321/source",
|
||||
"/Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p23-02-current-native-diagnostic-candidate-r321/source/tree_numeric_core.cpp"
|
||||
],
|
||||
"pgid": 25510,
|
||||
"ended_utc": "2026-10-12T03:27:19.159055+00:00",
|
||||
"exit_code": 0,
|
||||
"duration_seconds": 2.2526393329999994,
|
||||
"cleanup": [
|
||||
{
|
||||
"attempt": 0,
|
||||
"pgid": 25510,
|
||||
"members_before": [],
|
||||
"group_signal": null,
|
||||
"tracked_parent_signal": null
|
||||
}
|
||||
],
|
||||
"remaining": [],
|
||||
"cleanup_errors": [],
|
||||
"foreign_process_or_group_signals": 0
|
||||
}
|
||||
+45
@@ -0,0 +1,45 @@
|
||||
{
|
||||
"pid": 25510,
|
||||
"owner_pid": 25488,
|
||||
"pgid": 25510,
|
||||
"argv": [
|
||||
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++",
|
||||
"-o",
|
||||
"/Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p23-02-current-native-diagnostic-build-r322/objects/062-tree_numeric_core.os",
|
||||
"-c",
|
||||
"-std=c++17",
|
||||
"-isystem",
|
||||
"/Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p23-02-native-sdk-lifetime-candidate-r131/sdk/include",
|
||||
"-isystem",
|
||||
"/Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p23-02-native-sdk-lifetime-candidate-r131/sdk/gen/include",
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"-Werror",
|
||||
"-fexceptions",
|
||||
"-arch",
|
||||
"arm64",
|
||||
"-isysroot",
|
||||
"/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk",
|
||||
"-fvisibility=hidden",
|
||||
"-O3",
|
||||
"-fPIC",
|
||||
"-DMACOS_ENABLED",
|
||||
"-DUNIX_ENABLED",
|
||||
"-DTHREADS_ENABLED",
|
||||
"-DDEBUG_ENABLED",
|
||||
"-DNDEBUG",
|
||||
"-DGDEXTENSION",
|
||||
"-I/Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p23-02-current-native-diagnostic-candidate-r321/source",
|
||||
"/Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p23-02-current-native-diagnostic-candidate-r321/source/tree_numeric_core.cpp"
|
||||
],
|
||||
"started_utc": "2026-10-12T03:27:16.940410+00:00",
|
||||
"initial_members": [
|
||||
{
|
||||
"pid": 25510,
|
||||
"ppid": 25488,
|
||||
"pgid": 25510,
|
||||
"args": "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++ -o /Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p23-02-current-native-diagnostic-build-r322/objects/062-tree_numeric_core.os -c -std=c++17 -isystem /Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p23-02-native-sdk-lifetime-candidate-r131/sdk/include -isystem /Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p23-02-native-sdk-lifetime-candidate-r131/sdk/gen/include -Wall -Wextra -Werror -fexceptions -arch arm64 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk -fvisibility=hidden -O3 -fPIC -DMACOS_ENABLED -DUNIX_ENABLED -DTHREADS_ENABLED -DDEBUG_ENABLED -DNDEBUG -DGDEXTENSION -I/Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p23-02-current-native-diagnostic-candidate-r321/source /Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p23-02-current-native-diagnostic-candidate-r321/source/tree_numeric_core.cpp"
|
||||
}
|
||||
],
|
||||
"owned_new_session": true
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "compile-062",
|
||||
"result": {
|
||||
"pid": 25510,
|
||||
"exit_code": 0,
|
||||
"timed_out": false,
|
||||
"error_lines": [],
|
||||
"duration_seconds": 2.252984541
|
||||
},
|
||||
"capture": {
|
||||
"passed": true,
|
||||
"cleanup": [
|
||||
{
|
||||
"attempt": 0,
|
||||
"pgid": 25510,
|
||||
"members_before": [],
|
||||
"group_signal": null,
|
||||
"tracked_parent_signal": null
|
||||
}
|
||||
],
|
||||
"cleanup_errors": [],
|
||||
"all_captured_ended": true,
|
||||
"foreign_process_or_group_signals": 0
|
||||
},
|
||||
"full_input_inventory_before_verified": true,
|
||||
"full_input_inventory_after_verified": true,
|
||||
"post_input_error": null
|
||||
}
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
+35
@@ -0,0 +1,35 @@
|
||||
{
|
||||
"pid": 25525,
|
||||
"argv": [
|
||||
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++",
|
||||
"-o",
|
||||
"/Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p23-02-current-native-diagnostic-build-r322/libtree_numeric_core.macos.template_debug.dylib",
|
||||
"-arch",
|
||||
"arm64",
|
||||
"-isysroot",
|
||||
"/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk",
|
||||
"-fvisibility=hidden",
|
||||
"-Wl,-S",
|
||||
"-Wl,-x",
|
||||
"-Wl,-dead_strip",
|
||||
"-shared",
|
||||
"/Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p23-02-current-native-diagnostic-build-r322/objects/062-tree_numeric_core.os",
|
||||
"/Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p23-02-native-sdk-lifetime-build-r133/libgodot-cpp.macos.template_debug.arm64.a"
|
||||
],
|
||||
"pgid": 25525,
|
||||
"ended_utc": "2026-10-12T03:27:20.234519+00:00",
|
||||
"exit_code": 0,
|
||||
"duration_seconds": 0.6376524579999998,
|
||||
"cleanup": [
|
||||
{
|
||||
"attempt": 0,
|
||||
"pgid": 25525,
|
||||
"members_before": [],
|
||||
"group_signal": null,
|
||||
"tracked_parent_signal": null
|
||||
}
|
||||
],
|
||||
"remaining": [],
|
||||
"cleanup_errors": [],
|
||||
"foreign_process_or_group_signals": 0
|
||||
}
|
||||
+31
@@ -0,0 +1,31 @@
|
||||
{
|
||||
"pid": 25525,
|
||||
"owner_pid": 25488,
|
||||
"pgid": 25525,
|
||||
"argv": [
|
||||
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++",
|
||||
"-o",
|
||||
"/Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p23-02-current-native-diagnostic-build-r322/libtree_numeric_core.macos.template_debug.dylib",
|
||||
"-arch",
|
||||
"arm64",
|
||||
"-isysroot",
|
||||
"/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk",
|
||||
"-fvisibility=hidden",
|
||||
"-Wl,-S",
|
||||
"-Wl,-x",
|
||||
"-Wl,-dead_strip",
|
||||
"-shared",
|
||||
"/Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p23-02-current-native-diagnostic-build-r322/objects/062-tree_numeric_core.os",
|
||||
"/Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p23-02-native-sdk-lifetime-build-r133/libgodot-cpp.macos.template_debug.arm64.a"
|
||||
],
|
||||
"started_utc": "2026-10-12T03:27:19.633839+00:00",
|
||||
"initial_members": [
|
||||
{
|
||||
"pid": 25525,
|
||||
"ppid": 25488,
|
||||
"pgid": 25525,
|
||||
"args": "/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++ -o /Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p23-02-current-native-diagnostic-build-r322/libtree_numeric_core.macos.template_debug.dylib -arch arm64 -isysroot /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk -fvisibility=hidden -Wl,-S -Wl,-x -Wl,-dead_strip -shared /Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p23-02-current-native-diagnostic-build-r322/objects/062-tree_numeric_core.os /Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p23-02-native-sdk-lifetime-build-r133/libgodot-cpp.macos.template_debug.arm64.a"
|
||||
}
|
||||
],
|
||||
"owned_new_session": true
|
||||
}
|
||||
+28
@@ -0,0 +1,28 @@
|
||||
{
|
||||
"name": "link",
|
||||
"result": {
|
||||
"pid": 25525,
|
||||
"exit_code": 0,
|
||||
"timed_out": false,
|
||||
"error_lines": [],
|
||||
"duration_seconds": 0.637939083
|
||||
},
|
||||
"capture": {
|
||||
"passed": true,
|
||||
"cleanup": [
|
||||
{
|
||||
"attempt": 0,
|
||||
"pgid": 25525,
|
||||
"members_before": [],
|
||||
"group_signal": null,
|
||||
"tracked_parent_signal": null
|
||||
}
|
||||
],
|
||||
"cleanup_errors": [],
|
||||
"all_captured_ended": true,
|
||||
"foreign_process_or_group_signals": 0
|
||||
},
|
||||
"full_input_inventory_before_verified": true,
|
||||
"full_input_inventory_after_verified": true,
|
||||
"post_input_error": null
|
||||
}
|
||||
BIN
Binary file not shown.
+18
@@ -0,0 +1,18 @@
|
||||
{
|
||||
"owned_child_created": true,
|
||||
"engine_ended": true,
|
||||
"actual_process_exit_code": 0,
|
||||
"attempts": [
|
||||
{
|
||||
"attempt": 1,
|
||||
"kill_attempted": false,
|
||||
"wait_timeout_seconds": 5,
|
||||
"wait_completed": true,
|
||||
"errors": [],
|
||||
"poll_before": 0,
|
||||
"wait_returned_code": 0,
|
||||
"poll_after": 0
|
||||
}
|
||||
],
|
||||
"errors": []
|
||||
}
|
||||
+38
@@ -0,0 +1,38 @@
|
||||
{
|
||||
"scope": "isolated diagnostic owned child",
|
||||
"event": "ENGINE_ENDED",
|
||||
"actual_pid": 26808,
|
||||
"owner_pid": 26773,
|
||||
"command": [
|
||||
"/Applications/Godot.app/Contents/MacOS/Godot",
|
||||
"--headless",
|
||||
"--path",
|
||||
"/Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p23-02-native-guest-bulk-local-r315/stage",
|
||||
"--script",
|
||||
"/Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p23-02-current-native-diagnostic-r323/candidate-guarded.gd",
|
||||
"--verbose",
|
||||
"--log-file",
|
||||
"/Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p23-02-current-native-diagnostic-r323/candidate/engine-native.log"
|
||||
],
|
||||
"monotonic_ns": 9705466083,
|
||||
"exit_code": 0,
|
||||
"actual_process_exit_code": 0,
|
||||
"engine_ended": true,
|
||||
"timed_out": false,
|
||||
"lifecycle_error": null,
|
||||
"start_receipt_written": true,
|
||||
"pid": 26808,
|
||||
"argv": [
|
||||
"/Applications/Godot.app/Contents/MacOS/Godot",
|
||||
"--headless",
|
||||
"--path",
|
||||
"/Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p23-02-native-guest-bulk-local-r315/stage",
|
||||
"--script",
|
||||
"/Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p23-02-current-native-diagnostic-r323/candidate-guarded.gd",
|
||||
"--verbose",
|
||||
"--log-file",
|
||||
"/Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p23-02-current-native-diagnostic-r323/candidate/engine-native.log"
|
||||
],
|
||||
"ended_utc": "2026-10-12T03:31:05.619611+00:00",
|
||||
"signal_scope": "tracked Popen child only; no external group signal"
|
||||
}
|
||||
BIN
Binary file not shown.
+39
@@ -0,0 +1,39 @@
|
||||
{
|
||||
"actual_captured_pids": [
|
||||
26808
|
||||
],
|
||||
"cleanup": [
|
||||
{
|
||||
"pid": 26808,
|
||||
"cleanup": {
|
||||
"owned_child_created": true,
|
||||
"engine_ended": true,
|
||||
"actual_process_exit_code": 0,
|
||||
"attempts": [
|
||||
{
|
||||
"attempt": 1,
|
||||
"kill_attempted": false,
|
||||
"wait_timeout_seconds": 5,
|
||||
"wait_completed": true,
|
||||
"errors": [],
|
||||
"poll_before": 0,
|
||||
"wait_returned_code": 0,
|
||||
"poll_after": 0
|
||||
}
|
||||
],
|
||||
"errors": []
|
||||
},
|
||||
"outer_cleanup_errors": [],
|
||||
"engine_ended": true,
|
||||
"outer_maximum_helper_invocations": 3,
|
||||
"helper_maximum_waits": 3,
|
||||
"per_wait_timeout_seconds": 5,
|
||||
"foreign_process_or_group_signals": 0
|
||||
}
|
||||
],
|
||||
"adapter_exception": null,
|
||||
"all_captured_ended": true,
|
||||
"outer_cleanup_errors": [],
|
||||
"foreign_process_or_group_signals": 0,
|
||||
"all_cleanup_before_outer_receipt": true
|
||||
}
|
||||
BIN
Binary file not shown.
+40
@@ -0,0 +1,40 @@
|
||||
{
|
||||
"scope": "isolated diagnostic owned child",
|
||||
"event": "ENGINE_STARTED",
|
||||
"actual_pid": 26808,
|
||||
"owner_pid": 26773,
|
||||
"command": [
|
||||
"/Applications/Godot.app/Contents/MacOS/Godot",
|
||||
"--headless",
|
||||
"--path",
|
||||
"/Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p23-02-native-guest-bulk-local-r315/stage",
|
||||
"--script",
|
||||
"/Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p23-02-current-native-diagnostic-r323/candidate-guarded.gd",
|
||||
"--verbose",
|
||||
"--log-file",
|
||||
"/Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p23-02-current-native-diagnostic-r323/candidate/engine-native.log"
|
||||
],
|
||||
"monotonic_ns": 3473308875,
|
||||
"pid": 26808,
|
||||
"argv": [
|
||||
"/Applications/Godot.app/Contents/MacOS/Godot",
|
||||
"--headless",
|
||||
"--path",
|
||||
"/Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p23-02-native-guest-bulk-local-r315/stage",
|
||||
"--script",
|
||||
"/Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p23-02-current-native-diagnostic-r323/candidate-guarded.gd",
|
||||
"--verbose",
|
||||
"--log-file",
|
||||
"/Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p23-02-current-native-diagnostic-r323/candidate/engine-native.log"
|
||||
],
|
||||
"started_utc": "2026-10-12T03:30:59.382320+00:00",
|
||||
"current_process_identity": {
|
||||
"pid": 26808,
|
||||
"ppid": 26773,
|
||||
"pgid": 26808,
|
||||
"args": "/Applications/Godot.app/Contents/MacOS/Godot --headless --path /Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p23-02-native-guest-bulk-local-r315/stage --script /Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p23-02-current-native-diagnostic-r323/candidate-guarded.gd --verbose --log-file /Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p23-02-current-native-diagnostic-r323/candidate/engine-native.log"
|
||||
},
|
||||
"live_identity_exact": true,
|
||||
"tracked_child_ended_before_initial_probe": false,
|
||||
"signal_scope": "tracked Popen child only; no external group signal"
|
||||
}
|
||||
BIN
Binary file not shown.
+16
@@ -0,0 +1,16 @@
|
||||
extends "/Users/korizatou114/Projects/\u3068\u308a\u306b\u304f\u5de5\u623f\u30b2\u30fc\u30e0\u5236\u4f5c\u90e8/artifacts/original-completion-2026-10-02/p23-02-current-native-diagnostic-r323/whole.gd"
|
||||
const Guard := preload("/Users/korizatou114/Projects/\u3068\u308a\u306b\u304f\u5de5\u623f\u30b2\u30fc\u30e0\u5236\u4f5c\u90e8/artifacts/original-completion-2026-10-02/p23-02-native-cache-public-mutations-r144/profile_guard.gd")
|
||||
func _initialize() -> void:
|
||||
if not Guard.activate(): quit(97); return
|
||||
super._initialize()
|
||||
func run() -> void:
|
||||
var loaded := GDExtensionManager.load_extension("/Users/korizatou114/Projects/\u3068\u308a\u306b\u304f\u5de5\u623f\u30b2\u30fc\u30e0\u5236\u4f5c\u90e8/artifacts/original-completion-2026-10-02/p23-02-current-native-diagnostic-r323/candidate.gdextension")
|
||||
var exact_api := false
|
||||
if loaded == GDExtensionManager.LOAD_STATUS_OK and ClassDB.class_exists(&"TheTreeNumericCore"):
|
||||
var probe: Variant = ClassDB.instantiate(&"TheTreeNumericCore")
|
||||
exact_api = probe != null and probe.has_method(&"guest_tree_edge_rows") == true and probe.has_method(&"native_profile_reset") == true and probe.has_method(&"native_profile_snapshot") == true
|
||||
probe = null
|
||||
check(loaded == GDExtensionManager.LOAD_STATUS_OK and ClassDB.class_exists(&"TheTreeNumericCore") and exact_api, "exact native extension registered before diagnostic")
|
||||
if loaded != GDExtensionManager.LOAD_STATUS_OK or not ClassDB.class_exists(&"TheTreeNumericCore") or not exact_api: quit(96); return
|
||||
print("NATIVE_ADAPTER_LIBRARY_REGISTERED")
|
||||
super.run()
|
||||
BIN
Binary file not shown.
+79
@@ -0,0 +1,79 @@
|
||||
{
|
||||
"Windows_accepted": false,
|
||||
"after647": {
|
||||
"bytes": 246720,
|
||||
"codec": "zstd",
|
||||
"field_deletions": 0,
|
||||
"file_envelope": false,
|
||||
"full_bytes_roundtrip": true,
|
||||
"path": "/Users/korizatou114/Projects/とりにく工房ゲーム制作部/artifacts/original-completion-2026-10-02/p23-02-current-native-diagnostic-r323/candidate-measurement/after647.varbytes.zst",
|
||||
"sha256": "684ba40e9f649b7dd4bfc620185d7cd18bae3111c3b4038e48aaad5a9168d880",
|
||||
"uncompressed_bytes": 11487732,
|
||||
"uncompressed_sha256": "9df3c1b6f65dfd8be944cd5cab8c631f7533530dfef3820d9fa5d74a5b1ac30f"
|
||||
},
|
||||
"cache_hits": 0,
|
||||
"cache_misses": 9311,
|
||||
"candidate": "candidate",
|
||||
"candidate_adopted": false,
|
||||
"checks": 17,
|
||||
"failures": [],
|
||||
"limits": [
|
||||
"One instrumented current313-r02 diagnostic, fixed cold646. Nested inclusive scopes cannot be added/subtracted across scopes or compared to uninstrumented durations. No speedup, percentile, budget or causality claim.",
|
||||
"No Main, rendering, OS input or realtime x3. Timed interval contains builtin production callbacks; snapshot verification and compression occur after timing."
|
||||
],
|
||||
"manual_tick_us": 1110625,
|
||||
"native_profile": {
|
||||
"builtin_walk_edge_rows": {
|
||||
"calls": 8206,
|
||||
"inclusive_us": 25627,
|
||||
"max_us": 33
|
||||
},
|
||||
"facility_access_cells": {
|
||||
"calls": 15000,
|
||||
"inclusive_us": 13555,
|
||||
"max_us": 111
|
||||
},
|
||||
"guest_candidate_batch": {
|
||||
"calls": 50,
|
||||
"inclusive_us": 135549,
|
||||
"max_us": 2955
|
||||
},
|
||||
"guest_path_minutes": {
|
||||
"calls": 7500,
|
||||
"inclusive_us": 72455,
|
||||
"max_us": 307
|
||||
},
|
||||
"nearest_guest_access_impl": {
|
||||
"calls": 7500,
|
||||
"inclusive_us": 391568,
|
||||
"max_us": 35990
|
||||
},
|
||||
"performance_record_sample": {
|
||||
"calls": 7586,
|
||||
"inclusive_us": 101550,
|
||||
"max_us": 45
|
||||
},
|
||||
"project_guest_candidates": {
|
||||
"calls": 50,
|
||||
"inclusive_us": 575267,
|
||||
"max_us": 46509
|
||||
},
|
||||
"resolve_ready_guest_point": {
|
||||
"calls": 9225,
|
||||
"inclusive_us": 81044,
|
||||
"max_us": 58
|
||||
},
|
||||
"search_impl": {
|
||||
"calls": 50,
|
||||
"inclusive_us": 106513,
|
||||
"max_us": 34212
|
||||
}
|
||||
},
|
||||
"normal_OS_accepted": false,
|
||||
"passed": true,
|
||||
"performance_budget_accepted": false,
|
||||
"pid": 26808,
|
||||
"plan_id": "P23-02",
|
||||
"sample": "candidate",
|
||||
"unit_accepted": false
|
||||
}
|
||||
BIN
Binary file not shown.
+20
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"actual_user_data_dir": "/Users/korizatou114/Library/Application Support/TheTree-PCK-tests-263ec52e04fc4bd4999a25b39fafa649-budget-runtime",
|
||||
"before": "/Users/korizatou114/Library/Application Support/TheTree-PCK-tests-263ec52e04fc4bd4999a25b39fafa649-budget-startup",
|
||||
"conditions": [
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
],
|
||||
"conditions_checked": 5,
|
||||
"custom_user_dir_name": "TheTree-PCK-tests-263ec52e04fc4bd4999a25b39fafa649-budget-runtime",
|
||||
"expected_user_data_dir": "/Users/korizatou114/Library/Application Support/TheTree-PCK-tests-263ec52e04fc4bd4999a25b39fafa649-budget-runtime",
|
||||
"fresh_before_parent_initialize": true,
|
||||
"globalized_probe": "/Users/korizatou114/Library/Application Support/TheTree-PCK-tests-263ec52e04fc4bd4999a25b39fafa649-budget-runtime/guard-probe",
|
||||
"profile_writes_before_guard": 0,
|
||||
"project_settings_persisted": false,
|
||||
"runtime_settings_only": true,
|
||||
"verified": true
|
||||
}
|
||||
+7
@@ -0,0 +1,7 @@
|
||||
[configuration]
|
||||
entry_symbol = "tree_numeric_core_init"
|
||||
compatibility_minimum = "4.4"
|
||||
reloadable = false
|
||||
|
||||
[libraries]
|
||||
macos.debug.arm64 = "/private/tmp/thetree-native-diagnostic-r322-3ada932edeab48e2b8d229dfc2a06761/libtree_numeric_core.macos.template_debug.dylib"
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
+59
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"plan": [
|
||||
[
|
||||
"compile-062",
|
||||
[
|
||||
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++",
|
||||
"-o",
|
||||
"/Users/korizatou114/Projects/\u3068\u308a\u306b\u304f\u5de5\u623f\u30b2\u30fc\u30e0\u5236\u4f5c\u90e8/artifacts/original-completion-2026-10-02/p23-02-current-native-diagnostic-build-r322/objects/062-tree_numeric_core.os",
|
||||
"-c",
|
||||
"-std=c++17",
|
||||
"-isystem",
|
||||
"/Users/korizatou114/Projects/\u3068\u308a\u306b\u304f\u5de5\u623f\u30b2\u30fc\u30e0\u5236\u4f5c\u90e8/artifacts/original-completion-2026-10-02/p23-02-native-sdk-lifetime-candidate-r131/sdk/include",
|
||||
"-isystem",
|
||||
"/Users/korizatou114/Projects/\u3068\u308a\u306b\u304f\u5de5\u623f\u30b2\u30fc\u30e0\u5236\u4f5c\u90e8/artifacts/original-completion-2026-10-02/p23-02-native-sdk-lifetime-candidate-r131/sdk/gen/include",
|
||||
"-Wall",
|
||||
"-Wextra",
|
||||
"-Werror",
|
||||
"-fexceptions",
|
||||
"-arch",
|
||||
"arm64",
|
||||
"-isysroot",
|
||||
"/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk",
|
||||
"-fvisibility=hidden",
|
||||
"-O3",
|
||||
"-fPIC",
|
||||
"-DMACOS_ENABLED",
|
||||
"-DUNIX_ENABLED",
|
||||
"-DTHREADS_ENABLED",
|
||||
"-DDEBUG_ENABLED",
|
||||
"-DNDEBUG",
|
||||
"-DGDEXTENSION",
|
||||
"-I/Users/korizatou114/Projects/\u3068\u308a\u306b\u304f\u5de5\u623f\u30b2\u30fc\u30e0\u5236\u4f5c\u90e8/artifacts/original-completion-2026-10-02/p23-02-current-native-diagnostic-candidate-r321/source",
|
||||
"/Users/korizatou114/Projects/\u3068\u308a\u306b\u304f\u5de5\u623f\u30b2\u30fc\u30e0\u5236\u4f5c\u90e8/artifacts/original-completion-2026-10-02/p23-02-current-native-diagnostic-candidate-r321/source/tree_numeric_core.cpp"
|
||||
]
|
||||
],
|
||||
[
|
||||
"link",
|
||||
[
|
||||
"/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/clang++",
|
||||
"-o",
|
||||
"/Users/korizatou114/Projects/\u3068\u308a\u306b\u304f\u5de5\u623f\u30b2\u30fc\u30e0\u5236\u4f5c\u90e8/artifacts/original-completion-2026-10-02/p23-02-current-native-diagnostic-build-r322/libtree_numeric_core.macos.template_debug.dylib",
|
||||
"-arch",
|
||||
"arm64",
|
||||
"-isysroot",
|
||||
"/Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX.sdk",
|
||||
"-fvisibility=hidden",
|
||||
"-Wl,-S",
|
||||
"-Wl,-x",
|
||||
"-Wl,-dead_strip",
|
||||
"-shared",
|
||||
"/Users/korizatou114/Projects/\u3068\u308a\u306b\u304f\u5de5\u623f\u30b2\u30fc\u30e0\u5236\u4f5c\u90e8/artifacts/original-completion-2026-10-02/p23-02-current-native-diagnostic-build-r322/objects/062-tree_numeric_core.os",
|
||||
"/Users/korizatou114/Projects/\u3068\u308a\u306b\u304f\u5de5\u623f\u30b2\u30fc\u30e0\u5236\u4f5c\u90e8/artifacts/original-completion-2026-10-02/p23-02-native-sdk-lifetime-build-r133/libgodot-cpp.macos.template_debug.arm64.a"
|
||||
]
|
||||
]
|
||||
],
|
||||
"planned_bridge_compilations": 1,
|
||||
"SDK_compilations": 0,
|
||||
"Godot_runs": 0
|
||||
}
|
||||
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
BIN
Binary file not shown.
+19
@@ -0,0 +1,19 @@
|
||||
# coding: utf-8
|
||||
from pathlib import Path
|
||||
import importlib.util,json,hashlib,shutil,uuid
|
||||
R=Path('/Users/korizatou114/Projects/とりにく工房ゲーム制作部');A=R/'artifacts/original-completion-2026-10-02';O=A/'p23-02-current-native-diagnostic-r323';C=A/'p23-02-current-native-diagnostic-candidate-r321';B=A/'p23-02-current-native-diagnostic-build-r322'
|
||||
def mod(name,p):
|
||||
s=importlib.util.spec_from_file_location(name,p);m=importlib.util.module_from_spec(s);s.loader.exec_module(m);return m
|
||||
r=mod('prepare323',R/'tools/run_all_tests.py')
|
||||
def pin(p):return dict(path=str(p),bytes=p.stat().st_size,sha256=r.sha(p))
|
||||
assert r.read(B/'execution.json')['passed'] and shutil.disk_usage(A).free>=528*1048576
|
||||
old=r.read(A/'p23-02-native-guest-bulk-impact-whole-r317-r03/input.json');stage=Path(old['stage']);parent=Path(old['parent']);assert r.inventory(stage,stage=True)==old['expected_stage'];assert r.inventory(parent,stage=True)==old['expected_baseline_stage']
|
||||
private=Path('/private/tmp')/('thetree-native-diagnostic-r322-'+uuid.uuid4().hex);private.mkdir();lib=private/'libtree_numeric_core.macos.template_debug.dylib'
|
||||
with lib.open('xb') as f:f.write((B/'libtree_numeric_core.macos.template_debug.dylib').read_bytes())
|
||||
assert lib.read_bytes()==(B/'libtree_numeric_core.macos.template_debug.dylib').read_bytes()
|
||||
with (O/'candidate.gdextension').open('x') as f:f.write('[configuration]\nentry_symbol = "tree_numeric_core_init"\ncompatibility_minimum = "4.4"\nreloadable = false\n\n[libraries]\nmacos.debug.arm64 = "'+str(lib)+'"\n')
|
||||
v=old.copy();fixed={k:q for k,q in old['pins'].items() if k in ['engine','helper','run_all','profile_guard','modified_budget','original_budget','original_path','fixture','oracle','original_clock','QA_base','local_input','local_review','local_root','provider_review','provider_root']}
|
||||
for k,p in dict(config_candidate=O/'candidate.gdextension',new_library=B/'libtree_numeric_core.macos.template_debug.dylib',library_candidate=lib,prepare=Path(__file__),runner=A/'p23-02-current-native-diagnostic-r323.py',QA=O/'whole.gd',QA_original=A/'p23-02-native-guest-bulk-impact-whole-r317/whole.gd',candidate_input=C/'input.json',candidate_guest=C/'scripts/guest_service.gd',candidate_runtime=C/'scripts/simulation_runtime.gd',starter_candidate=O/'candidate-guarded.gd',build_execution=B/'execution.json',build_input=B/'input.json',diagnostic_cpp=C/'source/tree_numeric_core.cpp',diagnostic_profile=C/'source/native_profile.hpp',diagnostic_api=C/'source/native_profile_api.inc',diagnostic_batch=C/'source/guest_candidate_batch.inc',static_review=A/'p23-02-current-native-diagnostic-static-independent-review-r321.json',static_root=A/'p23-02-current-native-diagnostic-static-root-verification-r321.json').items():fixed[k]=pin(p)
|
||||
v.update(pins=fixed,source_before=r.source_refs(R),default_before=r.default_player(R),doc4_before={n:r.sha(R/'docs/plans/original-completion-2026-10-02'/n) for n in old['doc4_before']},support_inventory=r.inventory(r.SUPPORT),engine_processes=1,compiler_processes=0,stage_clones=0,full_regression_runs=0,expected_whole_checks=17,scope='One instrumented current313-r02 native tick646->647 and original whole11487732-byte equality; no pair or acceptance.',expected_total_seconds=[2,30],library_copies=1)
|
||||
with (O/'input.json').open('x') as f:json.dump(v,f,ensure_ascii=False,indent=2);f.write('\n')
|
||||
print(json.dumps(dict(plan=str(O/'input.json'),pins=len(fixed),private_copy=str(lib),free=shutil.disk_usage(A).free)))
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
# coding: utf-8
|
||||
from pathlib import Path
|
||||
import json,hashlib
|
||||
R=Path('/Users/korizatou114/Projects/とりにく工房ゲーム制作部');A=R/'artifacts/original-completion-2026-10-02';O=A/'p23-02-current-native-diagnostic-r323'
|
||||
def pin(p):return dict(path=str(p),bytes=p.stat().st_size,sha256=hashlib.sha256(p.read_bytes()).hexdigest())
|
||||
e=json.loads((O/'execution.json').read_text());assert e['passed'];v=json.loads((O/'candidate-measurement/report.json').read_text());assert v['passed'] and v['checks']==17
|
||||
rows=v['native_profile'];assert len(rows)==9
|
||||
m=json.loads((A/'p23-02-current-progress-disposition-r320.json').read_text());w=json.loads((R/'docs/plans/original-completion-2026-10-02/work_packages.json').read_text());p=next(x for x in w if x['id']=='P23')
|
||||
result=dict(plan_id='P23-02',kind='finite_native_inclusive_diagnostic',candidate='r313-r02 with diagnostic-only r321/library322 additions; unselected/unadopted',report=pin(O/'candidate-measurement/report.json'),execution=pin(O/'execution.json'),sync_instrumented_tick_us=v['manual_tick_us'],scope_rows=rows,conditions=m['conditions'],dependencies=p['depends_on'],condition_source=pin(A/'p23-02-current-progress-disposition-r320.json'),reuse=['r315 originalGuest local finite evidence','r317-r03 original whole11487732-byte equality evidence','existing315/209 stages and unchanged133 SDK archive'],all_after647_bytes_equal_original=True,formal_completed_added=0,unit_accepted=False,performance_accepted=False,next_action='Select a coherent necessary optimization from measured scopes plus direct source inspection; preserve all original callbacks, arithmetic, order and saved state. Then local/impact checks and final exact-version required regression/review.',limits=['Counters include all entries/unsupported/exception exits; no per-call success inference.','Named rows are nested inclusive observations. No cross-row sum/subtraction, pureCPU, causal attribution or uninstrumented speed comparison.','One fixed cold646 tick; no P95/50ms/D10/normalOS/x3/final172/Windows acceptance.','Original seventeen conditions remain unaccepted; only C08/X8 Windows blocked. Held manual input is deferred, not waived.'])
|
||||
with (O/'scope-and-remaining.json').open('x') as f:json.dump(result,f,ensure_ascii=False,indent=2);f.write('\n')
|
||||
print(json.dumps(dict(sync_instrumented_us=v['manual_tick_us'],native_rows=rows),ensure_ascii=False))
|
||||
+3
@@ -0,0 +1,3 @@
|
||||
# Current native diagnostic r321–r323
|
||||
|
||||
Finite diagnostic evidence only. Every saved gzip copy roundtrips to the original full bytes; originals are retained. See manifest for exact source/stored hashes and scope. No unit, performance, Windows or adoption acceptance.
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user