Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions crates/workshop-rs/src/catalog/data/catalog.json
Original file line number Diff line number Diff line change
Expand Up @@ -7995,7 +7995,7 @@
]
}
],
"digest": "b436f6107f2c77cc39abc9dba74780a9a85facebd7aaaec4f87437a89b9d4655",
"digest": "2d98f50b523e38bfbff59a952c0e4c3c80e8c7f972bb9b30a81fdabc0c46de19",
"enums": [
{
"domain": "Impulse",
Expand Down Expand Up @@ -23793,7 +23793,7 @@
"array"
],
"paramTypes": [
"Array|EntityId"
"Any"
],
"params": [
"Array"
Expand Down
30 changes: 30 additions & 0 deletions crates/workshop-rs/tests/contextual_semantics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -150,6 +150,36 @@ fn boolean_parameters_accept_any_value_but_keep_structural_checks() {
.expect_err("the accepted value itself is still validated structurally");
}

#[test]
fn first_of_wrapper_accepts_any_value_and_round_trips() {
let values = [
"Vector(1, 2, 3)",
"Hero(Ana)",
"Team Of(Event Player)",
"Map(Hanamura)",
"Color(Red)",
"Button(Jump)",
"Game Mode(Skirmish)",
];
for value in values {
let source = format!(
"variables\n{{\n global:\n 0: probe\n}}\nrule (\"wrapper\")\n{{\n event {{ Ongoing - Global; }}\n actions {{ Wait Until(First Of({value}), 2); }}\n}}"
);
let reparsed = roundtrip::round_trip_with_context(
&source,
&catalog(),
&Locale::new("en-US"),
&catalog(),
);
assert!(reparsed.equivalent, "First Of({value}) must round-trip");
validate_program(&program(&format!("Wait Until(First Of({value}), 2);")));
}

let arity = program("Wait Until(First Of(Vector(1, 2, 3), 1), 2);");
validate::validate_canonical_ids_wir(&arity, &catalog())
.expect_err("First Of arity is still checked");
}

#[test]
fn null_and_empty_string_contexts_normalize_at_their_positions() {
let teleport = program("Teleport(Event Player, 0);");
Expand Down
2 changes: 1 addition & 1 deletion crates/workshop-rs/tests/identity.rs
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ use workshop_rs::catalog::{Catalog, Locale};
/// (`workshop-catalog-gen build`) recomputes it and the pin is updated
/// deliberately together with the data.
const PINNED_CATALOG_DIGEST: &str =
"b436f6107f2c77cc39abc9dba74780a9a85facebd7aaaec4f87437a89b9d4655";
"2d98f50b523e38bfbff59a952c0e4c3c80e8c7f972bb9b30a81fdabc0c46de19";

#[test]
fn committed_catalog_digest_is_pinned() {
Expand Down
2 changes: 1 addition & 1 deletion docs/language-support/values.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ This document inventories canonical Workshop values and expressions in `workshop
| `Farthest Player From` | ✅ Supported | Returns: `Player`; Parameters: (Position: Vector, Team: Team). |
| `False` | ✅ Supported | Returns: `BoolLiteral`. |
| `Filtered Array` | ✅ Supported | Returns: `Array`; Parameters: (Array: Array, Condition: Boolean). |
| `First Of` | ✅ Supported | Returns: `Object|Array`; Parameters: (Array: Array|EntityId). |
| `First Of` | ✅ Supported | Returns: `Object|Array`; Parameters: (Array: Any). |
| `Flag Position` | ✅ Supported | Returns: `Vector`; Parameters: (Team: Team). |
| `Forward` | ✅ Supported | Returns: `Array`. |
| `Game Mode` | ✅ Supported | Returns: `Gamemode`; Parameters: (Gamemode: Gamemode). |
Expand Down
1 change: 1 addition & 0 deletions docs/source-attribution/catalog.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ is embedded in the dataset and surfaced by `workshop-rs-cli version --json`.
| OSTW-exercised params/spellings and enum domains (CreateEffect, Workshop Setting, Hero/Map/Button/Icon/Operation/Rounding/InworldTextRev, …) | Pinned OSTW v3.4.0 reference probe emissions (P4/P5/P6/P6b) and the protect-ban entry-point reachable closure. |
| Parameter metadata (`paramDomains`, `paramDefaults`, `paramCoercions`) | Pinned-reference probe data, never copied from upstream game data. Contextual literal substitutions are recorded per parameter position and cross-checked against [OverPy's replacement metadata](https://github.com/Zezombye/overpy/blob/2002431649cbdd7ddc3aa70bd184b598ec2820e5/src/types.d.ts), [Workshop emission path](https://github.com/Zezombye/overpy/blob/2002431649cbdd7ddc3aa70bd184b598ec2820e5/src/compiler/astToWorkshop.ts), and [Wait Until handling](https://github.com/Zezombye/overpy/blob/2002431649cbdd7ddc3aa70bd184b598ec2820e5/src/compiler/functions/waitUntil.ts); they do not establish global type coercions. |
| Boolean parameter acceptance (typed rule in `values/validate.rs`, not catalog data) | [ADR-0014](../adr/0014-validation-evidence-for-slot-acceptance.md): OverPy wraps values in `First Of` keyed on the `Boolean` parameter type ([`astToWorkshop.ts`](https://github.com/Zezombye/overpy/blob/1e2688954302a402d076944b46db07efb14d7b61/src/compiler/astToWorkshop.ts)), and the fix for Zezombye/overpy#182 states every value can be put in a Boolean field. The values the client rejects there are not modelled. |
| `First Of` argument (`paramTypes[0]`: `Any`) | [ADR-0014](../adr/0014-validation-evidence-for-slot-acceptance.md): pinned OverPy 9.7.10 wraps a value the client rejects in a Boolean parameter as `First Of(<value>)` ([`astToWorkshop.ts`](https://github.com/Zezombye/overpy/blob/1e2688954302a402d076944b46db07efb14d7b61/src/compiler/astToWorkshop.ts)), so the wrapper's argument is any value (Vector, Hero, Team, Map, Color, Button, Game Mode, …). The rule is keyed on the wrapped form, not on an array; the `Array|EntityId` declaration was a presumption only. |
| Localized parameter labels (`paramAliases`) | Separately reviewed owner mappings, stored per declared parameter position and resolved only through the catalog API. The initial `Wait` `zh-CN` labels are `时间` and `等待行为`, as identified by the documented Chinese Workshop action reference at https://overwatch.huijiwiki.com/wiki/%E5%9C%B0%E5%9B%BE%E5%B7%A5%E5%9D%8A/%E5%8A%A8%E4%BD%9C (accessed 2026-09-11); the canonical parameter order and descriptions are cross-checked against the pinned `workshop-data` action definition. They are not generated by the general zh-CN corpus pipeline. |
| Action/Value parameter and return signatures | Workshop.codes structured article properties (Returns, Parameters, Type, and Default), cross-checked against the pinned OverPy metadata and static OSTW data; entries without convergent sources remain explicitly unresolved. Representative article links are recorded with the catalog entries. |
| `Create Dummy Bot` hero parameter union (`Hero|Array`) | Workshop behavior accepts a single Hero or Hero array and selects randomly when multiple heroes are provided; pinned OverPy 9.7.10 texture-tag setup emits `getAllHeroes()` into `Create Dummy Bot`. |
Expand Down
Loading