diff --git a/README.md b/README.md index 583915d..d098d52 100644 --- a/README.md +++ b/README.md @@ -48,8 +48,9 @@ bounded runtime landing surface too: representative descriptors import into `world.hotel_revenue`. The grounded aggregate cargo-economics descriptors now have bounded runtime landing surfaces too: descriptor `105` `All Cargo Prices` plus descriptors `177..179` `All Cargo Production` / `All Factory Production` / `All Farm/Mine Production` import into -event-owned cargo override state, while the named cargo-price and named cargo-production strips -remain explicit `blocked_evidence_blocked_descriptor` parity until descriptor ordering is pinned +event-owned cargo override state, and the grounded named cargo-production strip `180..229` now +imports into named cargo production overrides too. The named cargo-price strip `106..176` +remains explicit `blocked_evidence_blocked_descriptor` parity until descriptor ordering is pinned more strongly. The first grounded condition-side unlock now exists for negative-sentinel `raw_condition_id = -1` company scopes, and the first ordinary nonnegative condition batch now executes too: numeric-threshold company diff --git a/artifacts/exports/rt3-1.06/event-effects-cargo-bindings.json b/artifacts/exports/rt3-1.06/event-effects-cargo-bindings.json new file mode 100644 index 0000000..f3e8f15 --- /dev/null +++ b/artifacts/exports/rt3-1.06/event-effects-cargo-bindings.json @@ -0,0 +1,360 @@ +{ + "binding_catalog_version": 1, + "notes": [ + "Named cargo production bindings are grounded from the checked-in 1.06 CargoTypes corpus and the candidate-loader evidence that live cargo names are materialized in lexicographic order.", + "The named production strip has 50 rows, so the checked-in binding covers the 50-name lexicographic corpus used by the grounded 1.06 runtime baseline and leaves the named cargo price strip unresolved.", + "Rock remains outside the grounded 50-row production strip in this batch." + ], + "bindings": [ + { + "descriptor_id": 180, + "band": "cargo_production_named", + "cargo_name": "Alcohol", + "binding_index": 1, + "binding_source": "static" + }, + { + "descriptor_id": 181, + "band": "cargo_production_named", + "cargo_name": "Aluminum", + "binding_index": 2, + "binding_source": "static" + }, + { + "descriptor_id": 182, + "band": "cargo_production_named", + "cargo_name": "Ammunition", + "binding_index": 3, + "binding_source": "static" + }, + { + "descriptor_id": 183, + "band": "cargo_production_named", + "cargo_name": "Automobiles", + "binding_index": 4, + "binding_source": "static" + }, + { + "descriptor_id": 184, + "band": "cargo_production_named", + "cargo_name": "Bauxite", + "binding_index": 5, + "binding_source": "static" + }, + { + "descriptor_id": 185, + "band": "cargo_production_named", + "cargo_name": "Ceramics", + "binding_index": 6, + "binding_source": "static" + }, + { + "descriptor_id": 186, + "band": "cargo_production_named", + "cargo_name": "Cheese", + "binding_index": 7, + "binding_source": "static" + }, + { + "descriptor_id": 187, + "band": "cargo_production_named", + "cargo_name": "Chemicals", + "binding_index": 8, + "binding_source": "static" + }, + { + "descriptor_id": 188, + "band": "cargo_production_named", + "cargo_name": "Clothing", + "binding_index": 9, + "binding_source": "static" + }, + { + "descriptor_id": 189, + "band": "cargo_production_named", + "cargo_name": "Coal", + "binding_index": 10, + "binding_source": "static" + }, + { + "descriptor_id": 190, + "band": "cargo_production_named", + "cargo_name": "Coffee", + "binding_index": 11, + "binding_source": "static" + }, + { + "descriptor_id": 191, + "band": "cargo_production_named", + "cargo_name": "Concrete", + "binding_index": 12, + "binding_source": "static" + }, + { + "descriptor_id": 192, + "band": "cargo_production_named", + "cargo_name": "Corn", + "binding_index": 13, + "binding_source": "static" + }, + { + "descriptor_id": 193, + "band": "cargo_production_named", + "cargo_name": "Cotton", + "binding_index": 14, + "binding_source": "static" + }, + { + "descriptor_id": 194, + "band": "cargo_production_named", + "cargo_name": "Crystals", + "binding_index": 15, + "binding_source": "static" + }, + { + "descriptor_id": 195, + "band": "cargo_production_named", + "cargo_name": "Diesel", + "binding_index": 16, + "binding_source": "static" + }, + { + "descriptor_id": 196, + "band": "cargo_production_named", + "cargo_name": "Dye", + "binding_index": 17, + "binding_source": "static" + }, + { + "descriptor_id": 197, + "band": "cargo_production_named", + "cargo_name": "Electronics", + "binding_index": 18, + "binding_source": "static" + }, + { + "descriptor_id": 198, + "band": "cargo_production_named", + "cargo_name": "Fertilizer", + "binding_index": 19, + "binding_source": "static" + }, + { + "descriptor_id": 199, + "band": "cargo_production_named", + "cargo_name": "Furniture", + "binding_index": 20, + "binding_source": "static" + }, + { + "descriptor_id": 200, + "band": "cargo_production_named", + "cargo_name": "Goods", + "binding_index": 21, + "binding_source": "static" + }, + { + "descriptor_id": 201, + "band": "cargo_production_named", + "cargo_name": "Grain", + "binding_index": 22, + "binding_source": "static" + }, + { + "descriptor_id": 202, + "band": "cargo_production_named", + "cargo_name": "Ingots", + "binding_index": 23, + "binding_source": "static" + }, + { + "descriptor_id": 203, + "band": "cargo_production_named", + "cargo_name": "Iron", + "binding_index": 24, + "binding_source": "static" + }, + { + "descriptor_id": 204, + "band": "cargo_production_named", + "cargo_name": "Livestock", + "binding_index": 25, + "binding_source": "static" + }, + { + "descriptor_id": 205, + "band": "cargo_production_named", + "cargo_name": "Logs", + "binding_index": 26, + "binding_source": "static" + }, + { + "descriptor_id": 206, + "band": "cargo_production_named", + "cargo_name": "Lumber", + "binding_index": 27, + "binding_source": "static" + }, + { + "descriptor_id": 207, + "band": "cargo_production_named", + "cargo_name": "Machinery", + "binding_index": 28, + "binding_source": "static" + }, + { + "descriptor_id": 208, + "band": "cargo_production_named", + "cargo_name": "Mail", + "binding_index": 29, + "binding_source": "static" + }, + { + "descriptor_id": 209, + "band": "cargo_production_named", + "cargo_name": "Meat", + "binding_index": 30, + "binding_source": "static" + }, + { + "descriptor_id": 210, + "band": "cargo_production_named", + "cargo_name": "Medicine", + "binding_index": 31, + "binding_source": "static" + }, + { + "descriptor_id": 211, + "band": "cargo_production_named", + "cargo_name": "Milk", + "binding_index": 32, + "binding_source": "static" + }, + { + "descriptor_id": 212, + "band": "cargo_production_named", + "cargo_name": "Oil", + "binding_index": 33, + "binding_source": "static" + }, + { + "descriptor_id": 213, + "band": "cargo_production_named", + "cargo_name": "Ore", + "binding_index": 34, + "binding_source": "static" + }, + { + "descriptor_id": 214, + "band": "cargo_production_named", + "cargo_name": "Paper", + "binding_index": 35, + "binding_source": "static" + }, + { + "descriptor_id": 215, + "band": "cargo_production_named", + "cargo_name": "Passengers", + "binding_index": 36, + "binding_source": "static" + }, + { + "descriptor_id": 216, + "band": "cargo_production_named", + "cargo_name": "Plastic", + "binding_index": 37, + "binding_source": "static" + }, + { + "descriptor_id": 217, + "band": "cargo_production_named", + "cargo_name": "Produce", + "binding_index": 38, + "binding_source": "static" + }, + { + "descriptor_id": 218, + "band": "cargo_production_named", + "cargo_name": "Pulpwood", + "binding_index": 39, + "binding_source": "static" + }, + { + "descriptor_id": 219, + "band": "cargo_production_named", + "cargo_name": "Rice", + "binding_index": 40, + "binding_source": "static" + }, + { + "descriptor_id": 220, + "band": "cargo_production_named", + "cargo_name": "Rubber", + "binding_index": 41, + "binding_source": "static" + }, + { + "descriptor_id": 221, + "band": "cargo_production_named", + "cargo_name": "Steel", + "binding_index": 42, + "binding_source": "static" + }, + { + "descriptor_id": 222, + "band": "cargo_production_named", + "cargo_name": "Sugar", + "binding_index": 43, + "binding_source": "static" + }, + { + "descriptor_id": 223, + "band": "cargo_production_named", + "cargo_name": "Tires", + "binding_index": 44, + "binding_source": "static" + }, + { + "descriptor_id": 224, + "band": "cargo_production_named", + "cargo_name": "Toys", + "binding_index": 45, + "binding_source": "static" + }, + { + "descriptor_id": 225, + "band": "cargo_production_named", + "cargo_name": "Troops", + "binding_index": 46, + "binding_source": "static" + }, + { + "descriptor_id": 226, + "band": "cargo_production_named", + "cargo_name": "Uranium", + "binding_index": 47, + "binding_source": "static" + }, + { + "descriptor_id": 227, + "band": "cargo_production_named", + "cargo_name": "Waste", + "binding_index": 48, + "binding_source": "static" + }, + { + "descriptor_id": 228, + "band": "cargo_production_named", + "cargo_name": "Weapons", + "binding_index": 49, + "binding_source": "static" + }, + { + "descriptor_id": 229, + "band": "cargo_production_named", + "cargo_name": "Wool", + "binding_index": 50, + "binding_source": "static" + } + ] +} diff --git a/artifacts/exports/rt3-1.06/event-effects-semantic-catalog.json b/artifacts/exports/rt3-1.06/event-effects-semantic-catalog.json index d0957ec..24dcc9a 100644 --- a/artifacts/exports/rt3-1.06/event-effects-semantic-catalog.json +++ b/artifacts/exports/rt3-1.06/event-effects-semantic-catalog.json @@ -1625,453 +1625,453 @@ }, { "descriptor_id": 180, - "label": "Unknown Cargo Production", + "label": "Alcohol Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 181, - "label": "Unknown Cargo Production", + "label": "Aluminum Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 182, - "label": "Unknown Cargo Production", + "label": "Ammunition Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 183, - "label": "Unknown Cargo Production", + "label": "Automobiles Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 184, - "label": "Unknown Cargo Production", + "label": "Bauxite Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 185, - "label": "Unknown Cargo Production", + "label": "Ceramics Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 186, - "label": "Unknown Cargo Production", + "label": "Cheese Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 187, - "label": "Unknown Cargo Production", + "label": "Chemicals Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 188, - "label": "Unknown Cargo Production", + "label": "Clothing Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 189, - "label": "Unknown Cargo Production", + "label": "Coal Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 190, - "label": "Unknown Cargo Production", + "label": "Coffee Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 191, - "label": "Unknown Cargo Production", + "label": "Concrete Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 192, - "label": "Unknown Cargo Production", + "label": "Corn Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 193, - "label": "Unknown Cargo Production", + "label": "Cotton Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 194, - "label": "Unknown Cargo Production", + "label": "Crystals Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 195, - "label": "Unknown Cargo Production", + "label": "Diesel Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 196, - "label": "Unknown Cargo Production", + "label": "Dye Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 197, - "label": "Unknown Cargo Production", + "label": "Electronics Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 198, - "label": "Unknown Cargo Production", + "label": "Fertilizer Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 199, - "label": "Unknown Cargo Production", + "label": "Furniture Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 200, - "label": "Unknown Cargo Production", + "label": "Goods Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 201, - "label": "Unknown Cargo Production", + "label": "Grain Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 202, - "label": "Unknown Cargo Production", + "label": "Ingots Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 203, - "label": "Unknown Cargo Production", + "label": "Iron Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 204, - "label": "Unknown Cargo Production", + "label": "Livestock Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 205, - "label": "Unknown Cargo Production", + "label": "Logs Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 206, - "label": "Unknown Cargo Production", + "label": "Lumber Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 207, - "label": "Unknown Cargo Production", + "label": "Machinery Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 208, - "label": "Unknown Cargo Production", + "label": "Mail Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 209, - "label": "Unknown Cargo Production", + "label": "Meat Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 210, - "label": "Unknown Cargo Production", + "label": "Medicine Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 211, - "label": "Unknown Cargo Production", + "label": "Milk Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 212, - "label": "Unknown Cargo Production", + "label": "Oil Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 213, - "label": "Unknown Cargo Production", + "label": "Ore Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 214, - "label": "Unknown Cargo Production", + "label": "Paper Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 215, - "label": "Unknown Cargo Production", + "label": "Passengers Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 216, - "label": "Unknown Cargo Production", + "label": "Plastic Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 217, - "label": "Unknown Cargo Production", + "label": "Produce Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 218, - "label": "Unknown Cargo Production", + "label": "Pulpwood Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 219, - "label": "Unknown Cargo Production", + "label": "Rice Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 220, - "label": "Unknown Cargo Production", + "label": "Rubber Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 221, - "label": "Unknown Cargo Production", + "label": "Steel Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 222, - "label": "Unknown Cargo Production", + "label": "Sugar Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 223, - "label": "Unknown Cargo Production", + "label": "Tires Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 224, - "label": "Unknown Cargo Production", + "label": "Toys Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 225, - "label": "Unknown Cargo Production", + "label": "Troops Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 226, - "label": "Unknown Cargo Production", + "label": "Uranium Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 227, - "label": "Unknown Cargo Production", + "label": "Waste Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 228, - "label": "Unknown Cargo Production", + "label": "Weapons Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 229, - "label": "Unknown Cargo Production", + "label": "Wool Production", "target_mask_bits": 15, "parameter_family": "cargo_production_scalar", "runtime_key": null, - "runtime_status": "evidence_blocked", - "executable_in_runtime": false + "runtime_status": "executable", + "executable_in_runtime": true }, { "descriptor_id": 230, diff --git a/crates/rrt-hook/src/lib.rs b/crates/rrt-hook/src/lib.rs index 3563a6f..b3aa3dc 100644 --- a/crates/rrt-hook/src/lib.rs +++ b/crates/rrt-hook/src/lib.rs @@ -119,10 +119,49 @@ pub fn write_indexed_collection_probe( Ok(path) } +#[derive(Debug, Clone, Serialize)] +pub struct CargoCollectionProbeRow { + pub entry_id: usize, + pub live: bool, + pub resolved_ptr: usize, + pub stem: Option, + pub route_style_byte: Option, + pub subtype_byte: Option, + pub class_marker: Option, +} + +#[derive(Debug, Clone, Serialize)] +pub struct CargoCollectionProbe { + pub collection_addr: usize, + pub flat_payload: bool, + pub stride: u32, + pub id_bound: i32, + pub payload_ptr: usize, + pub tombstone_ptr: usize, + pub live_entry_count: usize, + pub rows: Vec, +} + +pub fn write_cargo_collection_probe( + base_dir: &Path, + stem: &str, + probe: &CargoCollectionProbe, +) -> io::Result { + fs::create_dir_all(base_dir)?; + + let path = base_dir.join(format!("rrt_cargo_{stem}_collection_probe.json")); + let json = serde_json::to_vec_pretty(probe) + .map_err(|err| io::Error::other(format!("serialize cargo collection probe: {err}")))?; + fs::write(&path, json)?; + + Ok(path) +} + #[cfg(windows)] mod windows_hook { use super::{ - IndexedCollectionProbe, IndexedCollectionProbeRow, sample_finance_snapshot, + CargoCollectionProbe, CargoCollectionProbeRow, IndexedCollectionProbe, + IndexedCollectionProbeRow, sample_finance_snapshot, write_cargo_collection_probe, write_finance_snapshot_bundle, write_finance_snapshot_only, write_indexed_collection_probe, }; use core::ffi::{c_char, c_void}; @@ -160,6 +199,9 @@ mod windows_hook { static FINANCE_CAPTURE_PROBE_WRITTEN_MESSAGE: &[u8] = b"rrt-hook: finance probe snapshot written\n"; static FINANCE_CAPTURE_TIMEOUT_MESSAGE: &[u8] = b"rrt-hook: finance capture timed out\n"; + static CARGO_CAPTURE_STARTED_MESSAGE: &[u8] = b"rrt-hook: cargo capture thread started\n"; + static CARGO_CAPTURE_WRITTEN_MESSAGE: &[u8] = b"rrt-hook: cargo collection probe written\n"; + static CARGO_CAPTURE_TIMEOUT_MESSAGE: &[u8] = b"rrt-hook: cargo capture timed out\n"; static AUTO_LOAD_STARTED_MESSAGE: &[u8] = b"rrt-hook: auto load hook armed\n"; static AUTO_LOAD_HOOK_INSTALLED_MESSAGE: &[u8] = b"rrt-hook: auto load shell-state hook installed\n"; @@ -284,6 +326,7 @@ mod windows_hook { static FINANCE_TEMPLATE_EMITTED: AtomicBool = AtomicBool::new(false); static FINANCE_CAPTURE_STARTED: AtomicBool = AtomicBool::new(false); static FINANCE_COLLECTION_PROBE_WRITTEN: AtomicBool = AtomicBool::new(false); + static CARGO_CAPTURE_STARTED: AtomicBool = AtomicBool::new(false); static AUTO_LOAD_THREAD_STARTED: AtomicBool = AtomicBool::new(false); static AUTO_LOAD_HOOK_INSTALLED: AtomicBool = AtomicBool::new(false); static AUTO_LOAD_ATTEMPTED: AtomicBool = AtomicBool::new(false); @@ -324,6 +367,7 @@ mod windows_hook { static mut MODE2_TEARDOWN_TRAMPOLINE: usize = 0; const COMPANY_COLLECTION_ADDR: usize = 0x0062be10; + const CARGO_COLLECTION_ADDR: usize = 0x0062ba8c; const SHELL_CONTROLLER_PTR_ADDR: usize = 0x006d4024; const SHELL_STATE_PTR_ADDR: usize = 0x006cec74; const ACTIVE_MODE_PTR_ADDR: usize = 0x006cec78; @@ -360,6 +404,10 @@ mod windows_hook { const INDEXED_COLLECTION_ID_BOUND_OFFSET: usize = 0x14; const INDEXED_COLLECTION_PAYLOAD_OFFSET: usize = 0x30; const INDEXED_COLLECTION_TOMBSTONE_BITSET_OFFSET: usize = 0x34; + const CARGO_STEM_OFFSET: usize = 0x04; + const CARGO_SUBTYPE_OFFSET: usize = 0x32; + const CARGO_ROUTE_STYLE_OFFSET: usize = 0x46; + const CARGO_COLLECTION_CLASS_MARKER_BASE_OFFSET: usize = 0x9a; const COMPANY_ACTIVE_OFFSET: usize = 0x3f; const COMPANY_OUTSTANDING_SHARES_OFFSET: usize = 0x47; const COMPANY_COMPANY_VALUE_OFFSET: usize = 0x57; @@ -493,6 +541,7 @@ mod windows_hook { ) -> i32 { maybe_emit_finance_template_bundle(); maybe_start_finance_capture_thread(); + maybe_start_cargo_capture_thread(); maybe_install_auto_load_hook(); let direct_input8_create = unsafe { load_direct_input8_create() }; @@ -592,6 +641,41 @@ mod windows_hook { }); } + fn maybe_start_cargo_capture_thread() { + if env::var_os("RRT_WRITE_CARGO_CAPTURE").is_none() { + return; + } + if CARGO_CAPTURE_STARTED.swap(true, Ordering::AcqRel) { + return; + } + + append_log_message(CARGO_CAPTURE_STARTED_MESSAGE); + let base_dir = env::current_dir().unwrap_or_else(|_| PathBuf::from(".")); + let _ = thread::Builder::new() + .name("rrt-cargo-capture".to_string()) + .spawn(move || { + let mut last_probe: Option = None; + for _ in 0..MAX_CAPTURE_POLL_ATTEMPTS { + if let Some(probe) = unsafe { capture_cargo_collection_probe() } { + last_probe = Some(probe.clone()); + if probe.live_entry_count > 0 + && write_cargo_collection_probe(&base_dir, "attach_probe", &probe) + .is_ok() + { + append_log_message(CARGO_CAPTURE_WRITTEN_MESSAGE); + return; + } + } + thread::sleep(CAPTURE_POLL_INTERVAL); + } + + if let Some(probe) = last_probe { + let _ = write_cargo_collection_probe(&base_dir, "attach_probe_timeout", &probe); + } + append_log_message(CARGO_CAPTURE_TIMEOUT_MESSAGE); + }); + } + fn maybe_install_auto_load_hook() { let save_stem = match env::var("RRT_AUTO_LOAD_SAVE") { Ok(value) if !value.trim().is_empty() => value, @@ -2751,6 +2835,93 @@ mod windows_hook { }) } + unsafe fn capture_cargo_collection_probe() -> Option { + let collection = CARGO_COLLECTION_ADDR as *const u8; + let id_bound = unsafe { read_i32(collection.add(INDEXED_COLLECTION_ID_BOUND_OFFSET)) }; + if id_bound <= 0 { + return Some(CargoCollectionProbe { + collection_addr: CARGO_COLLECTION_ADDR, + flat_payload: unsafe { + read_u32(collection.add(INDEXED_COLLECTION_FLAT_FLAG_OFFSET)) != 0 + }, + stride: unsafe { read_u32(collection.add(INDEXED_COLLECTION_STRIDE_OFFSET)) }, + id_bound, + payload_ptr: unsafe { + read_ptr(collection.add(INDEXED_COLLECTION_PAYLOAD_OFFSET)) as usize + }, + tombstone_ptr: unsafe { + read_ptr(collection.add(INDEXED_COLLECTION_TOMBSTONE_BITSET_OFFSET)) as usize + }, + live_entry_count: 0, + rows: Vec::new(), + }); + } + + let mut live_entry_count = 0_usize; + let mut rows = Vec::with_capacity(id_bound as usize); + for entry_id in 1..=id_bound as usize { + let live = unsafe { indexed_collection_entry_id_is_live(collection, entry_id) }; + let resolved_ptr = unsafe { + indexed_collection_resolve_live_entry_by_id(collection, entry_id) as usize + }; + if live && resolved_ptr != 0 { + live_entry_count += 1; + } + let stem = if resolved_ptr == 0 { + None + } else { + Some(unsafe { + read_c_string((resolved_ptr as *const u8).add(CARGO_STEM_OFFSET), 0x1e) + }) + }; + let route_style_byte = if resolved_ptr == 0 { + None + } else { + Some(unsafe { read_u8((resolved_ptr as *const u8).add(CARGO_ROUTE_STYLE_OFFSET)) }) + }; + let subtype_byte = if resolved_ptr == 0 { + None + } else { + Some(unsafe { read_u8((resolved_ptr as *const u8).add(CARGO_SUBTYPE_OFFSET)) }) + }; + let class_marker = if live { + Some(unsafe { + read_u32( + collection.add(CARGO_COLLECTION_CLASS_MARKER_BASE_OFFSET + entry_id * 4), + ) + }) + } else { + None + }; + rows.push(CargoCollectionProbeRow { + entry_id, + live, + resolved_ptr, + stem, + route_style_byte, + subtype_byte, + class_marker, + }); + } + + Some(CargoCollectionProbe { + collection_addr: CARGO_COLLECTION_ADDR, + flat_payload: unsafe { + read_u32(collection.add(INDEXED_COLLECTION_FLAT_FLAG_OFFSET)) != 0 + }, + stride: unsafe { read_u32(collection.add(INDEXED_COLLECTION_STRIDE_OFFSET)) }, + id_bound, + payload_ptr: unsafe { + read_ptr(collection.add(INDEXED_COLLECTION_PAYLOAD_OFFSET)) as usize + }, + tombstone_ptr: unsafe { + read_ptr(collection.add(INDEXED_COLLECTION_TOMBSTONE_BITSET_OFFSET)) as usize + }, + live_entry_count, + rows, + }) + } + unsafe fn capture_probe_snapshot_from_company(company: *mut u8) -> FinanceSnapshot { let scenario = unsafe { read_ptr(ACTIVE_MODE_PTR_ADDR as *const u8) } as *const u8; let current_year = unsafe { read_u16(scenario.add(SCENARIO_CURRENT_YEAR_OFFSET)) }; @@ -2905,6 +3076,18 @@ mod windows_hook { unsafe { ptr::read_unaligned(address.cast::<*mut u8>()) } } + unsafe fn read_c_string(address: *const u8, max_len: usize) -> String { + let mut len = 0; + while len < max_len { + let byte = unsafe { read_u8(address.add(len)) }; + if byte == 0 { + break; + } + len += 1; + } + String::from_utf8_lossy(unsafe { std::slice::from_raw_parts(address, len) }).into_owned() + } + unsafe fn load_direct_input8_create() -> Option { if let Some(callback) = unsafe { REAL_DINPUT8_CREATE } { return Some(callback); diff --git a/crates/rrt-model/src/lib.rs b/crates/rrt-model/src/lib.rs index 25e6493..d3fd4db 100644 --- a/crates/rrt-model/src/lib.rs +++ b/crates/rrt-model/src/lib.rs @@ -31,6 +31,7 @@ pub const REQUIRED_EXPORTS: &[&str] = &[ "artifacts/exports/rt3-1.06/pending-template-store-record-kinds.csv", "artifacts/exports/rt3-1.06/pending-template-store-management.md", "artifacts/exports/rt3-1.06/event-effects-table.json", + "artifacts/exports/rt3-1.06/event-effects-cargo-bindings.json", "artifacts/exports/rt3-1.06/event-effects-semantic-catalog.json", ]; diff --git a/crates/rrt-runtime/src/import.rs b/crates/rrt-runtime/src/import.rs index 6c55c5b..cacbbd8 100644 --- a/crates/rrt-runtime/src/import.rs +++ b/crates/rrt-runtime/src/import.rs @@ -158,6 +158,7 @@ enum ImportBlocker { NamedTerritoryBinding, UnmappedOrdinaryCondition, UnmappedWorldCondition, + EvidenceBlockedDescriptor, MissingTrainContext, MissingTrainTerritoryContext, MissingLocomotiveCatalogContext, @@ -1216,6 +1217,7 @@ fn runtime_packed_event_grouped_effect_row_summary_from_smp( .or_else(|| row.recovered_cargo_class.clone()), recovered_cargo_label: cargo_entry .map(|entry| entry.label.clone()) + .or_else(|| row.recovered_cargo_label.clone()) .or_else(|| row.recovered_cargo_slot.map(default_cargo_slot_label)), recovered_cargo_supplied_token_stem: cargo_entry .and_then(|entry| entry.supplied_token_stem.clone()), @@ -1488,6 +1490,15 @@ fn lower_contextual_cargo_production_effect( target: RuntimeCargoProductionTarget::FarmMine, value, })), + 180..=229 => { + let Some(name) = row.recovered_cargo_label.clone() else { + return Err(ImportBlocker::EvidenceBlockedDescriptor); + }; + Ok(Some(RuntimeEffect::SetCargoProductionOverride { + target: RuntimeCargoProductionTarget::Named { name }, + value, + })) + } 230..=240 => { let Some(slot) = row.descriptor_id.checked_sub(229) else { return Ok(None); @@ -2650,6 +2661,9 @@ fn company_target_import_error_message( Some(ImportBlocker::NamedTerritoryBinding) => { "packed condition requires named territory binding".to_string() } + Some(ImportBlocker::EvidenceBlockedDescriptor) => { + "packed descriptor is still evidence-blocked".to_string() + } Some(ImportBlocker::UnmappedOrdinaryCondition) => { "packed ordinary condition is not yet mapped".to_string() } @@ -3107,6 +3121,7 @@ fn company_target_import_outcome(blocker: ImportBlocker) -> &'static str { ImportBlocker::NamedTerritoryBinding => "blocked_named_territory_binding", ImportBlocker::UnmappedOrdinaryCondition => "blocked_unmapped_ordinary_condition", ImportBlocker::UnmappedWorldCondition => "blocked_unmapped_world_condition", + ImportBlocker::EvidenceBlockedDescriptor => "blocked_evidence_blocked_descriptor", ImportBlocker::MissingTrainContext => "blocked_missing_train_context", ImportBlocker::MissingTrainTerritoryContext => "blocked_missing_train_territory_context", ImportBlocker::MissingLocomotiveCatalogContext => { @@ -3832,6 +3847,7 @@ mod tests { semantic_preview: Some("Set Company Cash to 7 with aux [2, 3, 24, 36]".to_string()), recovered_cargo_slot: None, recovered_cargo_class: None, + recovered_cargo_label: None, recovered_locomotive_id: None, locomotive_name: Some("Mikado".to_string()), notes: vec!["grouped effect row carries locomotive-name side string".to_string()], @@ -3866,6 +3882,7 @@ mod tests { )), recovered_cargo_slot: None, recovered_cargo_class: None, + recovered_cargo_label: None, recovered_locomotive_id: None, locomotive_name: None, notes: vec![], @@ -3895,6 +3912,7 @@ mod tests { semantic_preview: Some(format!("Set Company Track Pieces Buildable to {value}")), recovered_cargo_slot: None, recovered_cargo_class: None, + recovered_cargo_label: None, recovered_locomotive_id: None, locomotive_name: None, notes: vec![], @@ -3924,6 +3942,7 @@ mod tests { semantic_preview: Some(format!("Set Credit Rating to {value}")), recovered_cargo_slot: None, recovered_cargo_class: None, + recovered_cargo_label: None, recovered_locomotive_id: None, locomotive_name: None, notes: vec![], @@ -3955,6 +3974,7 @@ mod tests { semantic_preview: Some(format!("Set Merger Premium to {value}")), recovered_cargo_slot: None, recovered_cargo_class: None, + recovered_cargo_label: None, recovered_locomotive_id: None, locomotive_name: None, notes: vec![ @@ -3992,6 +4012,7 @@ mod tests { )), recovered_cargo_slot: None, recovered_cargo_class: None, + recovered_cargo_label: None, recovered_locomotive_id: None, locomotive_name: None, notes: vec![], @@ -4027,6 +4048,7 @@ mod tests { )), recovered_cargo_slot: None, recovered_cargo_class: None, + recovered_cargo_label: None, recovered_locomotive_id: None, locomotive_name: None, notes, @@ -4056,6 +4078,7 @@ mod tests { semantic_preview: Some(format!("Set Economic Status to {value}")), recovered_cargo_slot: None, recovered_cargo_class: None, + recovered_cargo_label: None, recovered_locomotive_id: None, locomotive_name: None, notes: vec![], @@ -4087,6 +4110,7 @@ mod tests { semantic_preview: Some(format!("Set Limited Track Building Amount to {value}")), recovered_cargo_slot: None, recovered_cargo_class: None, + recovered_cargo_label: None, recovered_locomotive_id: None, locomotive_name: None, notes: vec![], @@ -4118,6 +4142,7 @@ mod tests { semantic_preview: Some(format!("Set Use Wartime Cargos to {value}")), recovered_cargo_slot: None, recovered_cargo_class: None, + recovered_cargo_label: None, recovered_locomotive_id: None, locomotive_name: None, notes: vec![], @@ -4149,6 +4174,7 @@ mod tests { semantic_preview: Some(format!("Set Turbo Diesel Availability to {value}")), recovered_cargo_slot: None, recovered_cargo_class: None, + recovered_cargo_label: None, recovered_locomotive_id: None, locomotive_name: None, notes: vec![], @@ -4264,6 +4290,7 @@ mod tests { semantic_preview: Some(format!("Set {descriptor_label} to {value}")), recovered_cargo_slot: None, recovered_cargo_class: None, + recovered_cargo_label: None, recovered_locomotive_id, locomotive_name: None, notes: vec![], @@ -4374,6 +4401,7 @@ mod tests { semantic_preview: Some(format!("Set {descriptor_label} to {value}")), recovered_cargo_slot: None, recovered_cargo_class: None, + recovered_cargo_label: None, recovered_locomotive_id, locomotive_name: None, notes: vec![], @@ -4628,6 +4656,7 @@ mod tests { semantic_preview: Some(format!("Set {descriptor_label} to {value}")), recovered_cargo_slot: Some(slot), recovered_cargo_class, + recovered_cargo_label: None, recovered_locomotive_id: None, locomotive_name: None, notes: vec![], @@ -4657,6 +4686,7 @@ mod tests { semantic_preview: Some(format!("Set All Cargo Prices to {value}")), recovered_cargo_slot: None, recovered_cargo_class: None, + recovered_cargo_label: None, recovered_locomotive_id: None, locomotive_name: None, notes: vec![ @@ -4691,6 +4721,7 @@ mod tests { semantic_preview: Some(format!("Set Unknown Cargo Price to {value}")), recovered_cargo_slot: None, recovered_cargo_class: None, + recovered_cargo_label: None, recovered_locomotive_id: None, locomotive_name: None, notes: vec![ @@ -4731,6 +4762,7 @@ mod tests { semantic_preview: Some(format!("Set {label} to {value}")), recovered_cargo_slot: None, recovered_cargo_class, + recovered_cargo_label: None, recovered_locomotive_id: None, locomotive_name: None, notes: vec![ @@ -4743,11 +4775,19 @@ mod tests { descriptor_id: u32, value: i32, ) -> crate::SmpLoadedPackedEventGroupedEffectRowSummary { + let cargo_label = match descriptor_id { + 180 => Some("Alcohol".to_string()), + _ => None, + }; + let descriptor_label = cargo_label + .as_ref() + .map(|label| format!("{label} Production")) + .unwrap_or_else(|| "Unknown Cargo Production".to_string()); crate::SmpLoadedPackedEventGroupedEffectRowSummary { group_index: 0, row_index: 0, descriptor_id, - descriptor_label: Some("Unknown Cargo Production".to_string()), + descriptor_label: Some(descriptor_label.clone()), target_mask_bits: Some(0x08), parameter_family: Some("cargo_production_scalar".to_string()), grouped_target_subject: None, @@ -4762,9 +4802,10 @@ mod tests { value_word_0x16: 0, row_shape: "scalar_assignment".to_string(), semantic_family: Some("scalar_assignment".to_string()), - semantic_preview: Some(format!("Set Unknown Cargo Production to {value}")), + semantic_preview: Some(format!("Set {descriptor_label} to {value}")), recovered_cargo_slot: None, recovered_cargo_class: None, + recovered_cargo_label: cargo_label, recovered_locomotive_id: None, locomotive_name: None, notes: vec![ @@ -4798,6 +4839,7 @@ mod tests { semantic_preview: Some(format!("Set Territory Access Cost to {value}")), recovered_cargo_slot: None, recovered_cargo_class: None, + recovered_cargo_label: None, recovered_locomotive_id: None, locomotive_name: None, notes: vec![], @@ -4834,6 +4876,7 @@ mod tests { )), recovered_cargo_slot: None, recovered_cargo_class: None, + recovered_cargo_label: None, recovered_locomotive_id: None, locomotive_name: None, notes: vec![], @@ -4868,6 +4911,7 @@ mod tests { )), recovered_cargo_slot: None, recovered_cargo_class: None, + recovered_cargo_label: None, recovered_locomotive_id: None, locomotive_name: None, notes: vec![], @@ -4904,6 +4948,7 @@ mod tests { )), recovered_cargo_slot: None, recovered_cargo_class: None, + recovered_cargo_label: None, recovered_locomotive_id: None, locomotive_name: locomotive_name.map(ToString::to_string), notes, @@ -4933,6 +4978,7 @@ mod tests { semantic_preview: Some("Set Confiscate All to FALSE".to_string()), recovered_cargo_slot: None, recovered_cargo_class: None, + recovered_cargo_label: None, recovered_locomotive_id: None, locomotive_name: None, notes: vec![], @@ -6952,6 +6998,7 @@ mod tests { ), recovered_cargo_slot: None, recovered_cargo_class: None, + recovered_cargo_label: None, recovered_locomotive_id: Some(10), locomotive_name: None, notes: vec![], @@ -8066,7 +8113,7 @@ mod tests { } #[test] - fn keeps_named_cargo_production_rows_evidence_blocked() { + fn imports_named_cargo_production_rows_when_binding_is_grounded() { let save_slice = SmpLoadedSaveSlice { file_extension_hint: Some("gms".to_string()), container_profile_family: Some("rt3-classic-save-container-v1".to_string()), @@ -8096,13 +8143,13 @@ mod tests { live_record_count: 1, live_entry_ids: vec![40], decoded_record_count: 1, - imported_runtime_record_count: 0, + imported_runtime_record_count: 1, records: vec![crate::SmpLoadedPackedEventRecordSummary { record_index: 0, live_entry_id: 40, payload_offset: Some(0x7202), payload_len: Some(96), - decode_status: "parity_only".to_string(), + decode_status: "executable".to_string(), payload_family: "real_packed_v1".to_string(), trigger_kind: Some(7), active: None, @@ -8116,30 +8163,45 @@ mod tests { grouped_effect_row_counts: vec![1, 0, 0, 0], grouped_effect_rows: vec![real_named_cargo_production_row(180, 160)], decoded_conditions: Vec::new(), - decoded_actions: vec![], - executable_import_ready: false, - notes: vec!["named cargo production descriptors remain evidence-blocked until cargo ordering is pinned" + decoded_actions: vec![RuntimeEffect::SetCargoProductionOverride { + target: RuntimeCargoProductionTarget::Named { + name: "Alcohol".to_string(), + }, + value: 160, + }], + executable_import_ready: true, + notes: vec!["named cargo production descriptors now import through named cargo overrides" .to_string()], }], }), notes: vec![], }; - let import = project_save_slice_to_runtime_state_import( + let mut import = project_save_slice_to_runtime_state_import( &save_slice, "packed-events-named-cargo-production-parity", None, ) .expect("save slice should project"); - assert!(import.state.event_runtime_records.is_empty()); + assert_eq!(import.state.event_runtime_records.len(), 1); + execute_step_command( + &mut import.state, + &StepCommand::ServiceTriggerKind { trigger_kind: 7 }, + ) + .expect("named cargo production runtime record should run"); + + assert_eq!( + import.state.named_cargo_production_overrides.get("Alcohol"), + Some(&160) + ); assert_eq!( import .state .packed_event_collection .as_ref() .and_then(|summary| summary.records[0].import_outcome.as_deref()), - Some("blocked_evidence_blocked_descriptor") + Some("imported") ); } @@ -8451,6 +8513,7 @@ mod tests { ), recovered_cargo_slot: None, recovered_cargo_class: None, + recovered_cargo_label: None, recovered_locomotive_id: None, locomotive_name: Some("Mikado".to_string()), notes: vec![ @@ -10080,6 +10143,7 @@ mod tests { semantic_preview: Some("Set Turbo Diesel Availability to 1".to_string()), recovered_cargo_slot: None, recovered_cargo_class: None, + recovered_cargo_label: None, recovered_locomotive_id: None, locomotive_name: None, notes: vec!["checked-in whole-game grouped-effect sample".to_string()], diff --git a/crates/rrt-runtime/src/smp.rs b/crates/rrt-runtime/src/smp.rs index c8b70ae..caba87a 100644 --- a/crates/rrt-runtime/src/smp.rs +++ b/crates/rrt-runtime/src/smp.rs @@ -2189,6 +2189,8 @@ pub struct SmpLoadedPackedEventGroupedEffectRowSummary { #[serde(default)] pub recovered_cargo_class: Option, #[serde(default)] + pub recovered_cargo_label: Option, + #[serde(default)] pub recovered_locomotive_id: Option, #[serde(default)] pub locomotive_name: Option, @@ -3510,6 +3512,11 @@ fn parse_real_grouped_effect_row_summary( "cargo-production descriptor maps to world production slot {cargo_slot}" )); } + if let Some(cargo_label) = grounded_named_cargo_production_label(descriptor_id) { + notes.push(format!( + "named cargo production descriptor maps to cargo {cargo_label}" + )); + } Some(SmpLoadedPackedEventGroupedEffectRowSummary { group_index, @@ -3543,6 +3550,8 @@ fn parse_real_grouped_effect_row_summary( recovered_cargo_class: recovered_cargo_production_slot(descriptor_id) .and_then(known_cargo_slot_definition) .map(|definition| runtime_cargo_class_name(definition.cargo_class).to_string()), + recovered_cargo_label: grounded_named_cargo_production_label(descriptor_id) + .map(ToString::to_string), recovered_locomotive_id: recovered_locomotive_availability_loco_id(descriptor_id) .or_else(|| recovered_locomotive_cost_loco_id(descriptor_id)), locomotive_name, @@ -3789,9 +3798,87 @@ fn recovered_cargo_economics_descriptor_metadata( } } +const GROUNDED_NAMED_CARGO_PRODUCTION_LABELS: [(&str, &str); 50] = [ + ("Alcohol", "Alcohol Production"), + ("Aluminum", "Aluminum Production"), + ("Ammunition", "Ammunition Production"), + ("Automobiles", "Automobiles Production"), + ("Bauxite", "Bauxite Production"), + ("Ceramics", "Ceramics Production"), + ("Cheese", "Cheese Production"), + ("Chemicals", "Chemicals Production"), + ("Clothing", "Clothing Production"), + ("Coal", "Coal Production"), + ("Coffee", "Coffee Production"), + ("Concrete", "Concrete Production"), + ("Corn", "Corn Production"), + ("Cotton", "Cotton Production"), + ("Crystals", "Crystals Production"), + ("Diesel", "Diesel Production"), + ("Dye", "Dye Production"), + ("Electronics", "Electronics Production"), + ("Fertilizer", "Fertilizer Production"), + ("Furniture", "Furniture Production"), + ("Goods", "Goods Production"), + ("Grain", "Grain Production"), + ("Ingots", "Ingots Production"), + ("Iron", "Iron Production"), + ("Livestock", "Livestock Production"), + ("Logs", "Logs Production"), + ("Lumber", "Lumber Production"), + ("Machinery", "Machinery Production"), + ("Mail", "Mail Production"), + ("Meat", "Meat Production"), + ("Medicine", "Medicine Production"), + ("Milk", "Milk Production"), + ("Oil", "Oil Production"), + ("Ore", "Ore Production"), + ("Paper", "Paper Production"), + ("Passengers", "Passengers Production"), + ("Plastic", "Plastic Production"), + ("Produce", "Produce Production"), + ("Pulpwood", "Pulpwood Production"), + ("Rice", "Rice Production"), + ("Rubber", "Rubber Production"), + ("Steel", "Steel Production"), + ("Sugar", "Sugar Production"), + ("Tires", "Tires Production"), + ("Toys", "Toys Production"), + ("Troops", "Troops Production"), + ("Uranium", "Uranium Production"), + ("Waste", "Waste Production"), + ("Weapons", "Weapons Production"), + ("Wool", "Wool Production"), +]; + +fn grounded_named_cargo_production_label(descriptor_id: u32) -> Option<&'static str> { + let index = descriptor_id.checked_sub(180)? as usize; + GROUNDED_NAMED_CARGO_PRODUCTION_LABELS + .get(index) + .map(|(cargo_label, _)| *cargo_label) +} + +fn grounded_named_cargo_production_descriptor_label(descriptor_id: u32) -> Option<&'static str> { + let index = descriptor_id.checked_sub(180)? as usize; + GROUNDED_NAMED_CARGO_PRODUCTION_LABELS + .get(index) + .map(|(_, descriptor_label)| *descriptor_label) +} + fn recovered_cargo_production_descriptor_metadata( descriptor_id: u32, ) -> Option { + if let Some(label) = grounded_named_cargo_production_descriptor_label(descriptor_id) { + return Some(RealGroupedEffectDescriptorMetadata { + descriptor_id, + label, + target_mask_bits: 0x08, + parameter_family: "cargo_production_scalar", + runtime_key: None, + runtime_status: RealGroupedEffectRuntimeStatus::Executable, + executable_in_runtime: true, + }); + } recovered_cargo_production_label(descriptor_id).map(|label| { RealGroupedEffectDescriptorMetadata { descriptor_id, @@ -11493,6 +11580,18 @@ mod tests { assert!(metadata.executable_in_runtime); } + #[test] + fn looks_up_grounded_named_cargo_production_descriptor_metadata() { + let metadata = + real_grouped_effect_descriptor_metadata(180).expect("descriptor metadata should exist"); + + assert_eq!(metadata.label, "Alcohol Production"); + assert_eq!(metadata.target_mask_bits, 0x08); + assert_eq!(metadata.parameter_family, "cargo_production_scalar"); + assert_eq!(metadata.runtime_key, None); + assert!(metadata.executable_in_runtime); + } + #[test] fn looks_up_recovered_lower_band_locomotive_cost_descriptor_metadata() { let metadata = @@ -11556,6 +11655,33 @@ mod tests { ); } + #[test] + fn parses_grounded_named_cargo_production_row_with_label() { + let row_bytes = build_real_grouped_effect_row(RealGroupedEffectRowSpec { + descriptor_id: 180, + raw_scalar_value: 160, + opcode: 3, + value_byte_0x09: 0, + value_dword_0x0d: 0, + value_byte_0x11: 0, + value_byte_0x12: 0, + value_word_0x14: 0, + value_word_0x16: 0, + locomotive_name: None, + }); + + let row = parse_real_grouped_effect_row_summary(&row_bytes, 0, 0, None) + .expect("row should parse"); + + assert_eq!(row.descriptor_id, 180); + assert_eq!(row.descriptor_label.as_deref(), Some("Alcohol Production")); + assert_eq!(row.recovered_cargo_label.as_deref(), Some("Alcohol")); + assert_eq!( + row.parameter_family.as_deref(), + Some("cargo_production_scalar") + ); + } + #[test] fn looks_up_recovered_locomotive_policy_descriptor_metadata() { let metadata = diff --git a/docs/README.md b/docs/README.md index 7759055..3e135d0 100644 --- a/docs/README.md +++ b/docs/README.md @@ -115,8 +115,9 @@ The highest-value next passes are now: `world.track_maintenance_cost` - the grounded aggregate cargo-economics descriptors now execute too: descriptor `105` `All Cargo Prices` and descriptors `177..179` `All Cargo Production` / `All Factory Production` - / `All Farm/Mine Production` land on bounded event-owned cargo override state, while the named - cargo-price and named cargo-production strips remain explicit + / `All Farm/Mine Production` land on bounded event-owned cargo override state, and the grounded + named cargo-production strip `180..229` now lands on named cargo production overrides too +- the named cargo-price strip `106..176` remains explicit `blocked_evidence_blocked_descriptor` parity until descriptor ordering is pinned more strongly - widen real packed-event executable coverage descriptor by descriptor after identity, target mask, and normalized effect semantics are all grounded, not just after row framing is parsed diff --git a/docs/runtime-rehost-plan.md b/docs/runtime-rehost-plan.md index 4432147..51635a1 100644 --- a/docs/runtime-rehost-plan.md +++ b/docs/runtime-rehost-plan.md @@ -77,8 +77,9 @@ Implemented today: `RuntimeState.world_scalar_overrides` - the grounded aggregate cargo-economics descriptors now execute too: descriptor `105` `All Cargo Prices` and descriptors `177..179` `All Cargo Production` / `All Factory Production` - / `All Farm/Mine Production` import through bounded cargo override surfaces, while the named - cargo-price and named cargo-production strips now sit on explicit + / `All Farm/Mine Production` import through bounded cargo override surfaces, and the grounded + named cargo-production strip `180..229` now imports through named cargo production overrides too +- the named cargo-price strip `106..176` now sits on explicit `blocked_evidence_blocked_descriptor` parity instead of generic unmapped-descriptor frontier - a minimal event-owned train surface and an opaque economic-status lane now exist in runtime state, and real descriptors `8` = `Economic Status`, `9` = `Confiscate All`, and `15` = diff --git a/fixtures/runtime/packed-event-cargo-economics-parity-save-slice-fixture.json b/fixtures/runtime/packed-event-cargo-economics-parity-save-slice-fixture.json index 2992bea..2a9d9cf 100644 --- a/fixtures/runtime/packed-event-cargo-economics-parity-save-slice-fixture.json +++ b/fixtures/runtime/packed-event-cargo-economics-parity-save-slice-fixture.json @@ -3,7 +3,7 @@ "fixture_id": "packed-event-cargo-economics-parity-save-slice-fixture", "source": { "kind": "captured-runtime", - "description": "Fixture pinning the remaining named cargo-economics descriptor strips on explicit evidence-blocked parity." + "description": "Fixture pinning the remaining named cargo-price descriptor strip on explicit evidence-blocked parity." }, "state_save_slice_path": "packed-event-cargo-economics-parity-save-slice.json", "commands": [ @@ -34,12 +34,6 @@ "descriptor_label": "Unknown Cargo Price", "parameter_family": "cargo_price_scalar", "semantic_family": "scalar_assignment" - }, - { - "descriptor_id": 180, - "descriptor_label": "Unknown Cargo Production", - "parameter_family": "cargo_production_scalar", - "semantic_family": "scalar_assignment" } ] } diff --git a/fixtures/runtime/packed-event-cargo-economics-parity-save-slice.json b/fixtures/runtime/packed-event-cargo-economics-parity-save-slice.json index 5d12397..dc9a1d1 100644 --- a/fixtures/runtime/packed-event-cargo-economics-parity-save-slice.json +++ b/fixtures/runtime/packed-event-cargo-economics-parity-save-slice.json @@ -2,12 +2,12 @@ "format_version": 1, "save_slice_id": "packed-event-cargo-economics-parity-save-slice", "source": { - "description": "Tracked save-slice document pinning the remaining named cargo-economics descriptor strips on explicit evidence-blocked parity.", + "description": "Tracked save-slice document pinning the remaining named cargo-price descriptor strip on explicit evidence-blocked parity.", "original_save_filename": "captured-cargo-economics-parity.gms", "original_save_sha256": "cargo-economics-parity-sample-sha256", "notes": [ "tracked as JSON save-slice document rather than raw .smp", - "pins named cargo price and named cargo production rows until exact descriptor-to-cargo ordering is grounded strongly enough" + "pins the named cargo-price strip until exact descriptor-to-cargo ordering is grounded strongly enough" ] }, "save_slice": { @@ -45,7 +45,7 @@ "record_index": 0, "live_entry_id": 65, "payload_offset": 33696, - "payload_len": 128, + "payload_len": 96, "decode_status": "parity_only", "payload_family": "real_packed_v1", "trigger_kind": 7, @@ -65,7 +65,7 @@ "text_bands": [], "standalone_condition_row_count": 0, "standalone_condition_rows": [], - "grouped_effect_row_counts": [2, 0, 0, 0], + "grouped_effect_row_counts": [1, 0, 0, 0], "grouped_effect_rows": [ { "group_index": 0, @@ -93,45 +93,18 @@ "descriptor recovered from checked-in EventEffects semantic catalog", "exact named cargo ordering for the price strip is not yet pinned" ] - }, - { - "group_index": 0, - "row_index": 1, - "descriptor_id": 180, - "descriptor_label": "Unknown Cargo Production", - "target_mask_bits": 8, - "parameter_family": "cargo_production_scalar", - "opcode": 3, - "raw_scalar_value": 160, - "value_byte_0x09": 0, - "value_dword_0x0d": 0, - "value_byte_0x11": 0, - "value_byte_0x12": 0, - "value_word_0x14": 0, - "value_word_0x16": 0, - "row_shape": "scalar_assignment", - "semantic_family": "scalar_assignment", - "semantic_preview": "Set Unknown Cargo Production to 160", - "recovered_cargo_slot": null, - "recovered_cargo_class": null, - "recovered_locomotive_id": null, - "locomotive_name": null, - "notes": [ - "descriptor recovered from checked-in EventEffects semantic catalog", - "exact named cargo ordering for the production strip is not yet pinned" - ] } ], "decoded_actions": [], "executable_import_ready": false, "notes": [ - "named cargo-economics descriptor strips remain explicit evidence-blocked parity" + "named cargo-price descriptors remain explicit evidence-blocked parity" ] } ] }, "notes": [ - "named cargo economics evidence-blocked parity sample" + "named cargo price evidence-blocked parity sample" ] } } diff --git a/fixtures/runtime/packed-event-cargo-economics-save-slice-fixture.json b/fixtures/runtime/packed-event-cargo-economics-save-slice-fixture.json index 355e6a1..e32b547 100644 --- a/fixtures/runtime/packed-event-cargo-economics-save-slice-fixture.json +++ b/fixtures/runtime/packed-event-cargo-economics-save-slice-fixture.json @@ -3,7 +3,7 @@ "fixture_id": "packed-event-cargo-economics-save-slice-fixture", "source": { "kind": "captured-runtime", - "description": "Fixture proving the grounded aggregate cargo-economics descriptors execute through bounded cargo override surfaces." + "description": "Fixture proving the grounded aggregate cargo-economics descriptors plus the grounded named production strip execute through bounded cargo override surfaces." }, "state_save_slice_path": "packed-event-cargo-economics-save-slice.json", "commands": [ @@ -28,6 +28,9 @@ "all_cargo_production_override": 210, "factory_cargo_production_override": 225, "farm_mine_cargo_production_override": 175, + "named_cargo_production_overrides": { + "Alcohol": 160 + }, "packed_event_collection": { "records": [ { @@ -67,6 +70,14 @@ "kind": "farm_mine" }, "value": 175 + }, + { + "kind": "set_cargo_production_override", + "target": { + "kind": "named", + "name": "Alcohol" + }, + "value": 160 } ] } diff --git a/fixtures/runtime/packed-event-cargo-economics-save-slice.json b/fixtures/runtime/packed-event-cargo-economics-save-slice.json index 825ae3b..78da771 100644 --- a/fixtures/runtime/packed-event-cargo-economics-save-slice.json +++ b/fixtures/runtime/packed-event-cargo-economics-save-slice.json @@ -2,12 +2,12 @@ "format_version": 1, "save_slice_id": "packed-event-cargo-economics-save-slice", "source": { - "description": "Tracked save-slice document proving the grounded aggregate cargo-economics descriptors execute through bounded cargo override surfaces.", + "description": "Tracked save-slice document proving the grounded aggregate and named-production cargo-economics descriptors execute through bounded cargo override surfaces.", "original_save_filename": "captured-cargo-economics.gms", "original_save_sha256": "cargo-economics-sample-sha256", "notes": [ "tracked as JSON save-slice document rather than raw .smp", - "pins the grounded aggregate descriptors 105 and 177..179 while named cargo strips remain evidence-blocked" + "pins the grounded aggregate descriptors 105 and 177..179 plus the grounded named production strip 180..229 while named cargo price remains evidence-blocked" ] }, "save_slice": { @@ -45,7 +45,7 @@ "record_index": 0, "live_entry_id": 64, "payload_offset": 32800, - "payload_len": 176, + "payload_len": 208, "decode_status": "executable", "payload_family": "real_packed_v1", "trigger_kind": 7, @@ -65,7 +65,7 @@ "text_bands": [], "standalone_condition_row_count": 0, "standalone_condition_rows": [], - "grouped_effect_row_counts": [4, 0, 0, 0], + "grouped_effect_row_counts": [5, 0, 0, 0], "grouped_effect_rows": [ { "group_index": 0, @@ -170,6 +170,34 @@ "notes": [ "descriptor recovered from checked-in EventEffects semantic catalog" ] + }, + { + "group_index": 0, + "row_index": 4, + "descriptor_id": 180, + "descriptor_label": "Alcohol Production", + "target_mask_bits": 8, + "parameter_family": "cargo_production_scalar", + "opcode": 3, + "raw_scalar_value": 160, + "value_byte_0x09": 0, + "value_dword_0x0d": 0, + "value_byte_0x11": 0, + "value_byte_0x12": 0, + "value_word_0x14": 0, + "value_word_0x16": 0, + "row_shape": "scalar_assignment", + "semantic_family": "scalar_assignment", + "semantic_preview": "Set Alcohol Production to 160", + "recovered_cargo_slot": null, + "recovered_cargo_class": null, + "recovered_cargo_label": "Alcohol", + "recovered_locomotive_id": null, + "locomotive_name": null, + "notes": [ + "descriptor recovered from checked-in EventEffects semantic catalog", + "named cargo production descriptor maps to cargo Alcohol" + ] } ], "decoded_actions": [ @@ -200,17 +228,25 @@ "kind": "farm_mine" }, "value": 175 + }, + { + "kind": "set_cargo_production_override", + "target": { + "kind": "named", + "name": "Alcohol" + }, + "value": 160 } ], "executable_import_ready": true, "notes": [ - "grounded aggregate cargo economics descriptors execute through bounded cargo override surfaces" + "grounded aggregate cargo economics descriptors plus the grounded named production strip execute through bounded cargo override surfaces" ] } ] }, "notes": [ - "aggregate cargo economics executable sample" + "aggregate cargo economics plus named production executable sample" ] } } diff --git a/tools/py/build_event_effect_semantic_catalog.py b/tools/py/build_event_effect_semantic_catalog.py index f17b870..831d87f 100644 --- a/tools/py/build_event_effect_semantic_catalog.py +++ b/tools/py/build_event_effect_semantic_catalog.py @@ -95,7 +95,20 @@ def locomotive_cost_label(locomotive_id: int) -> str: return f"Lower-Band Locomotive Cost Slot {locomotive_id}" -def classify(row: dict[str, object]) -> dict[str, object]: +def load_cargo_bindings(raw_table_path: Path) -> dict[int, dict[str, object]]: + bindings_path = raw_table_path.parent / "event-effects-cargo-bindings.json" + if not bindings_path.exists(): + return {} + artifact = json.loads(bindings_path.read_text(encoding="utf-8")) + return { + int(binding["descriptor_id"]): binding + for binding in artifact.get("bindings", []) + } + + +def classify( + row: dict[str, object], cargo_bindings: dict[int, dict[str, object]] +) -> dict[str, object]: descriptor_id = int(row["descriptor_id"]) label = str(row["label"]) signature_byte_0x63 = int(row["signature_byte_0x63"]) @@ -142,6 +155,11 @@ def classify(row: dict[str, object]) -> dict[str, object]: executable_in_runtime = True elif 180 <= descriptor_id <= 229: parameter_family = "cargo_production_scalar" + binding = cargo_bindings.get(descriptor_id) + if binding is not None: + label = f"{binding['cargo_name']} Production" + runtime_status = "executable" + executable_in_runtime = True elif 230 <= descriptor_id <= 240: parameter_family = "cargo_production_scalar" runtime_status = "executable" @@ -213,7 +231,8 @@ def main() -> None: args = parser.parse_args() raw_artifact = json.loads(args.raw_table.read_text(encoding="utf-8")) - descriptors = [classify(row) for row in raw_artifact["descriptors"]] + cargo_bindings = load_cargo_bindings(args.raw_table) + descriptors = [classify(row, cargo_bindings) for row in raw_artifact["descriptors"]] artifact = { "descriptor_count": len(descriptors), "raw_table_binary_sha256": raw_artifact.get("binary_sha256"),