// @generated by `cargo run -p xtask -- codegen` — do not edit by hand.
// Source of truth: crates/wh40kdc/schemas/bundled.schema.json
// (regenerate that first via `cd tools && npm run bundle:schemas`).
/// Error types.
pub mod error {
/// Error from a `TryFrom` or `FromStr` implementation.
pub struct ConversionError(::std::borrow::Cow<'static, str>);
impl ::std::error::Error for ConversionError {}
impl ::std::fmt::Display for ConversionError {
fn fmt(
&self,
f: &mut ::std::fmt::Formatter<'_>,
) -> Result<(), ::std::fmt::Error> {
::std::fmt::Display::fmt(&self.0, f)
}
}
impl ::std::fmt::Debug for ConversionError {
fn fmt(
&self,
f: &mut ::std::fmt::Formatter<'_>,
) -> Result<(), ::std::fmt::Error> {
::std::fmt::Debug::fmt(&self.0, f)
}
}
impl From<&'static str> for ConversionError {
fn from(value: &'static str) -> Self {
Self(value.into())
}
}
impl From<String> for ConversionError {
fn from(value: String) -> Self {
Self(value.into())
}
}
}
///Community-authored structured representation of what a game ability does. NOT GW text.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Ability DSL Entry",
/// "description": "Community-authored structured representation of what a game ability does. NOT GW text.",
/// "type": "object",
/// "required": [
/// "ability_id",
/// "authored_by",
/// "effect",
/// "game_version",
/// "name",
/// "scope"
/// ],
/// "properties": {
/// "ability_id": {
/// "$ref": "#/$defs/entity-id"
/// },
/// "ability_type": {
/// "type": "string",
/// "enum": [
/// "core",
/// "faction",
/// "detachment",
/// "unit",
/// "enhancement",
/// "stratagem"
/// ]
/// },
/// "authored_by": {
/// "$ref": "#/$defs/contributor-ref"
/// },
/// "behavior": {
/// "description": "How this ability interacts with the game flow — not a runtime predicate",
/// "type": "string",
/// "enum": [
/// "passive",
/// "activated",
/// "reactive",
/// "aura"
/// ]
/// },
/// "community_notes": {
/// "type": "string"
/// },
/// "detachment_id": {
/// "description": "For detachment/enhancement/stratagem-type abilities, the associated detachment",
/// "oneOf": [
/// {
/// "$ref": "#/$defs/entity-id"
/// },
/// {
/// "type": "null"
/// }
/// ]
/// },
/// "dispute_notes": {
/// "type": "string"
/// },
/// "disputed": {
/// "default": false,
/// "type": "boolean"
/// },
/// "effect": {
/// "$ref": "#/$defs/effect"
/// },
/// "faction_id": {
/// "description": "For faction-type abilities, the faction this rule belongs to",
/// "oneOf": [
/// {
/// "$ref": "#/$defs/entity-id"
/// },
/// {
/// "type": "null"
/// }
/// ]
/// },
/// "game_version": {
/// "$ref": "#/$defs/game-version-ref"
/// },
/// "interactions": {
/// "type": "array",
/// "items": {
/// "type": "object",
/// "required": [
/// "ability_ref",
/// "type"
/// ],
/// "properties": {
/// "ability_ref": {
/// "$ref": "#/$defs/entity-id"
/// },
/// "notes": {
/// "type": "string"
/// },
/// "type": {
/// "type": "string",
/// "enum": [
/// "conflicts-with",
/// "combos-with",
/// "superseded-by",
/// "requires",
/// "replaces"
/// ]
/// }
/// }
/// }
/// },
/// "name": {
/// "type": "string"
/// },
/// "scope": {
/// "$ref": "#/$defs/scope"
/// },
/// "supersedes": {
/// "oneOf": [
/// {
/// "$ref": "#/$defs/dataslate-version"
/// },
/// {
/// "type": "null"
/// }
/// ]
/// },
/// "unit_ids": {
/// "type": "array",
/// "items": {
/// "$ref": "#/$defs/entity-id"
/// }
/// },
/// "version": {
/// "$ref": "#/$defs/dataslate-version"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct Ability {
pub ability_id: EntityId,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub ability_type: ::std::option::Option<AbilityAbilityType>,
pub authored_by: ContributorRef,
///How this ability interacts with the game flow — not a runtime predicate
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub behavior: ::std::option::Option<AbilityBehavior>,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub community_notes: ::std::option::Option<::std::string::String>,
///For detachment/enhancement/stratagem-type abilities, the associated detachment
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub detachment_id: ::std::option::Option<EntityId>,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub dispute_notes: ::std::option::Option<::std::string::String>,
#[serde(default)]
pub disputed: bool,
pub effect: Effect,
///For faction-type abilities, the faction this rule belongs to
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub faction_id: ::std::option::Option<EntityId>,
pub game_version: GameVersionRef,
#[serde(default, skip_serializing_if = "::std::vec::Vec::is_empty")]
pub interactions: ::std::vec::Vec<AbilityInteractionsItem>,
pub name: ::std::string::String,
pub scope: Scope,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub supersedes: ::std::option::Option<DataslateVersion>,
#[serde(default, skip_serializing_if = "::std::vec::Vec::is_empty")]
pub unit_ids: ::std::vec::Vec<EntityId>,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub version: ::std::option::Option<DataslateVersion>,
}
///`AbilityAbilityType`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "enum": [
/// "core",
/// "faction",
/// "detachment",
/// "unit",
/// "enhancement",
/// "stratagem"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum AbilityAbilityType {
#[serde(rename = "core")]
Core,
#[serde(rename = "faction")]
Faction,
#[serde(rename = "detachment")]
Detachment,
#[serde(rename = "unit")]
Unit,
#[serde(rename = "enhancement")]
Enhancement,
#[serde(rename = "stratagem")]
Stratagem,
}
impl ::std::fmt::Display for AbilityAbilityType {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Core => f.write_str("core"),
Self::Faction => f.write_str("faction"),
Self::Detachment => f.write_str("detachment"),
Self::Unit => f.write_str("unit"),
Self::Enhancement => f.write_str("enhancement"),
Self::Stratagem => f.write_str("stratagem"),
}
}
}
impl ::std::str::FromStr for AbilityAbilityType {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"core" => Ok(Self::Core),
"faction" => Ok(Self::Faction),
"detachment" => Ok(Self::Detachment),
"unit" => Ok(Self::Unit),
"enhancement" => Ok(Self::Enhancement),
"stratagem" => Ok(Self::Stratagem),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for AbilityAbilityType {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for AbilityAbilityType {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for AbilityAbilityType {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///How this ability interacts with the game flow — not a runtime predicate
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "How this ability interacts with the game flow — not a runtime predicate",
/// "type": "string",
/// "enum": [
/// "passive",
/// "activated",
/// "reactive",
/// "aura"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum AbilityBehavior {
#[serde(rename = "passive")]
Passive,
#[serde(rename = "activated")]
Activated,
#[serde(rename = "reactive")]
Reactive,
#[serde(rename = "aura")]
Aura,
}
impl ::std::fmt::Display for AbilityBehavior {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Passive => f.write_str("passive"),
Self::Activated => f.write_str("activated"),
Self::Reactive => f.write_str("reactive"),
Self::Aura => f.write_str("aura"),
}
}
}
impl ::std::str::FromStr for AbilityBehavior {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"passive" => Ok(Self::Passive),
"activated" => Ok(Self::Activated),
"reactive" => Ok(Self::Reactive),
"aura" => Ok(Self::Aura),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for AbilityBehavior {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for AbilityBehavior {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for AbilityBehavior {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///`AbilityInteractionsItem`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "object",
/// "required": [
/// "ability_ref",
/// "type"
/// ],
/// "properties": {
/// "ability_ref": {
/// "$ref": "#/$defs/entity-id"
/// },
/// "notes": {
/// "type": "string"
/// },
/// "type": {
/// "type": "string",
/// "enum": [
/// "conflicts-with",
/// "combos-with",
/// "superseded-by",
/// "requires",
/// "replaces"
/// ]
/// }
/// }
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
pub struct AbilityInteractionsItem {
pub ability_ref: EntityId,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub notes: ::std::option::Option<::std::string::String>,
#[serde(rename = "type")]
pub type_: AbilityInteractionsItemType,
}
///`AbilityInteractionsItemType`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "enum": [
/// "conflicts-with",
/// "combos-with",
/// "superseded-by",
/// "requires",
/// "replaces"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum AbilityInteractionsItemType {
#[serde(rename = "conflicts-with")]
ConflictsWith,
#[serde(rename = "combos-with")]
CombosWith,
#[serde(rename = "superseded-by")]
SupersededBy,
#[serde(rename = "requires")]
Requires,
#[serde(rename = "replaces")]
Replaces,
}
impl ::std::fmt::Display for AbilityInteractionsItemType {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::ConflictsWith => f.write_str("conflicts-with"),
Self::CombosWith => f.write_str("combos-with"),
Self::SupersededBy => f.write_str("superseded-by"),
Self::Requires => f.write_str("requires"),
Self::Replaces => f.write_str("replaces"),
}
}
}
impl ::std::str::FromStr for AbilityInteractionsItemType {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"conflicts-with" => Ok(Self::ConflictsWith),
"combos-with" => Ok(Self::CombosWith),
"superseded-by" => Ok(Self::SupersededBy),
"requires" => Ok(Self::Requires),
"replaces" => Ok(Self::Replaces),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for AbilityInteractionsItemType {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for AbilityInteractionsItemType {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for AbilityInteractionsItemType {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///A draw-time predicate over an army list (not runtime board state, so deliberately NOT the Ability DSL condition). Used to gate when_drawn operations such as redraws. Example: a card that is void unless the opponent fields a large unit (10e 'Cull the Horde' redrew when the opponent had no unit of 14+ models) is { subject: 'opponent', quantifier: 'none', unit_filter: { model_count_min: 14 } } with operation 'redraw'.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "A draw-time predicate over an army list (not runtime board state, so deliberately NOT the Ability DSL condition). Used to gate when_drawn operations such as redraws. Example: a card that is void unless the opponent fields a large unit (10e 'Cull the Horde' redrew when the opponent had no unit of 14+ models) is { subject: 'opponent', quantifier: 'none', unit_filter: { model_count_min: 14 } } with operation 'redraw'.",
/// "type": "object",
/// "required": [
/// "quantifier",
/// "subject",
/// "unit_filter"
/// ],
/// "properties": {
/// "quantifier": {
/// "description": "Whether the army must contain ('any') or lack ('none') a unit matching unit_filter for the predicate to hold.",
/// "type": "string",
/// "enum": [
/// "any",
/// "none"
/// ]
/// },
/// "subject": {
/// "description": "Whose army list the predicate inspects.",
/// "type": "string",
/// "enum": [
/// "self",
/// "opponent"
/// ]
/// },
/// "unit_filter": {
/// "description": "Criteria a unit in the army must satisfy to match. All present criteria must hold (logical AND).",
/// "type": "object",
/// "minProperties": 1,
/// "properties": {
/// "keywords": {
/// "$ref": "#/$defs/keyword-list"
/// },
/// "model_count_max": {
/// "type": "integer",
/// "minimum": 1.0
/// },
/// "model_count_min": {
/// "type": "integer",
/// "minimum": 1.0
/// },
/// "wounds_min": {
/// "type": "integer",
/// "minimum": 1.0
/// }
/// },
/// "additionalProperties": false
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct ArmyCompositionPredicate {
///Whether the army must contain ('any') or lack ('none') a unit matching unit_filter for the predicate to hold.
pub quantifier: ArmyCompositionPredicateQuantifier,
///Whose army list the predicate inspects.
pub subject: ArmyCompositionPredicateSubject,
pub unit_filter: ArmyCompositionPredicateUnitFilter,
}
///Whether the army must contain ('any') or lack ('none') a unit matching unit_filter for the predicate to hold.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Whether the army must contain ('any') or lack ('none') a unit matching unit_filter for the predicate to hold.",
/// "type": "string",
/// "enum": [
/// "any",
/// "none"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum ArmyCompositionPredicateQuantifier {
#[serde(rename = "any")]
Any,
#[serde(rename = "none")]
None,
}
impl ::std::fmt::Display for ArmyCompositionPredicateQuantifier {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Any => f.write_str("any"),
Self::None => f.write_str("none"),
}
}
}
impl ::std::str::FromStr for ArmyCompositionPredicateQuantifier {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"any" => Ok(Self::Any),
"none" => Ok(Self::None),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for ArmyCompositionPredicateQuantifier {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String>
for ArmyCompositionPredicateQuantifier {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String>
for ArmyCompositionPredicateQuantifier {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///Whose army list the predicate inspects.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Whose army list the predicate inspects.",
/// "type": "string",
/// "enum": [
/// "self",
/// "opponent"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum ArmyCompositionPredicateSubject {
#[serde(rename = "self")]
Self_,
#[serde(rename = "opponent")]
Opponent,
}
impl ::std::fmt::Display for ArmyCompositionPredicateSubject {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Self_ => f.write_str("self"),
Self::Opponent => f.write_str("opponent"),
}
}
}
impl ::std::str::FromStr for ArmyCompositionPredicateSubject {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"self" => Ok(Self::Self_),
"opponent" => Ok(Self::Opponent),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for ArmyCompositionPredicateSubject {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String>
for ArmyCompositionPredicateSubject {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for ArmyCompositionPredicateSubject {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///Criteria a unit in the army must satisfy to match. All present criteria must hold (logical AND).
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Criteria a unit in the army must satisfy to match. All present criteria must hold (logical AND).",
/// "type": "object",
/// "minProperties": 1,
/// "properties": {
/// "keywords": {
/// "$ref": "#/$defs/keyword-list"
/// },
/// "model_count_max": {
/// "type": "integer",
/// "minimum": 1.0
/// },
/// "model_count_min": {
/// "type": "integer",
/// "minimum": 1.0
/// },
/// "wounds_min": {
/// "type": "integer",
/// "minimum": 1.0
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct ArmyCompositionPredicateUnitFilter {
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub keywords: ::std::option::Option<KeywordList>,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub model_count_max: ::std::option::Option<::std::num::NonZeroU64>,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub model_count_min: ::std::option::Option<::std::num::NonZeroU64>,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub wounds_min: ::std::option::Option<::std::num::NonZeroU64>,
}
impl ::std::default::Default for ArmyCompositionPredicateUnitFilter {
fn default() -> Self {
Self {
keywords: Default::default(),
model_count_max: Default::default(),
model_count_min: Default::default(),
wounds_min: Default::default(),
}
}
}
///A model's base. 'round' carries 'diameter'; 'oval' carries 'width'+'length'. 'flying-base' (with 'size': small/large), 'hull', and 'unique' are categories the GW base-size guide gives without standard millimetre dimensions; entries carrying such a category, or any millimetre value not taken from an authoritative source, set 'draft': true to mark them for later hand-authoring.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "A model's base. 'round' carries 'diameter'; 'oval' carries 'width'+'length'. 'flying-base' (with 'size': small/large), 'hull', and 'unique' are categories the GW base-size guide gives without standard millimetre dimensions; entries carrying such a category, or any millimetre value not taken from an authoritative source, set 'draft': true to mark them for later hand-authoring.",
/// "type": "object",
/// "required": [
/// "shape"
/// ],
/// "properties": {
/// "diameter": {
/// "type": "number",
/// "exclusiveMinimum": 0.0
/// },
/// "draft": {
/// "description": "True when the entry is provisional/guessed (e.g. a category without authoritative dimensions) and should be revisited.",
/// "default": false,
/// "type": "boolean"
/// },
/// "length": {
/// "type": "number",
/// "exclusiveMinimum": 0.0
/// },
/// "shape": {
/// "enum": [
/// "round",
/// "oval",
/// "flying-base",
/// "hull",
/// "unique"
/// ]
/// },
/// "size": {
/// "description": "Flying-base size class, when 'shape' is 'flying-base'.",
/// "enum": [
/// "small",
/// "large"
/// ]
/// },
/// "width": {
/// "type": "number",
/// "exclusiveMinimum": 0.0
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct BaseSize {
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub diameter: ::std::option::Option<f64>,
///True when the entry is provisional/guessed (e.g. a category without authoritative dimensions) and should be revisited.
#[serde(default)]
pub draft: bool,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub length: ::std::option::Option<f64>,
pub shape: BaseSizeShape,
///Flying-base size class, when 'shape' is 'flying-base'.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub size: ::std::option::Option<BaseSizeSize>,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub width: ::std::option::Option<f64>,
}
///`BaseSizeShape`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "enum": [
/// "round",
/// "oval",
/// "flying-base",
/// "hull",
/// "unique"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum BaseSizeShape {
#[serde(rename = "round")]
Round,
#[serde(rename = "oval")]
Oval,
#[serde(rename = "flying-base")]
FlyingBase,
#[serde(rename = "hull")]
Hull,
#[serde(rename = "unique")]
Unique,
}
impl ::std::fmt::Display for BaseSizeShape {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Round => f.write_str("round"),
Self::Oval => f.write_str("oval"),
Self::FlyingBase => f.write_str("flying-base"),
Self::Hull => f.write_str("hull"),
Self::Unique => f.write_str("unique"),
}
}
}
impl ::std::str::FromStr for BaseSizeShape {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"round" => Ok(Self::Round),
"oval" => Ok(Self::Oval),
"flying-base" => Ok(Self::FlyingBase),
"hull" => Ok(Self::Hull),
"unique" => Ok(Self::Unique),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for BaseSizeShape {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for BaseSizeShape {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for BaseSizeShape {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///Flying-base size class, when 'shape' is 'flying-base'.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Flying-base size class, when 'shape' is 'flying-base'.",
/// "enum": [
/// "small",
/// "large"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum BaseSizeSize {
#[serde(rename = "small")]
Small,
#[serde(rename = "large")]
Large,
}
impl ::std::fmt::Display for BaseSizeSize {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Small => f.write_str("small"),
Self::Large => f.write_str("large"),
}
}
}
impl ::std::str::FromStr for BaseSizeSize {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"small" => Ok(Self::Small),
"large" => Ok(Self::Large),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for BaseSizeSize {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for BaseSizeSize {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for BaseSizeSize {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///11e battle size, which sets the army's points limit and detachment-point budget: 'incursion' = 1000 pts / 2 detachment points; 'strike-force' = 2000 pts / 3 detachment points.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "11e battle size, which sets the army's points limit and detachment-point budget: 'incursion' = 1000 pts / 2 detachment points; 'strike-force' = 2000 pts / 3 detachment points.",
/// "type": "string",
/// "enum": [
/// "incursion",
/// "strike-force"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum BattleSize {
#[serde(rename = "incursion")]
Incursion,
#[serde(rename = "strike-force")]
StrikeForce,
}
impl ::std::fmt::Display for BattleSize {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Incursion => f.write_str("incursion"),
Self::StrikeForce => f.write_str("strike-force"),
}
}
}
impl ::std::str::FromStr for BattleSize {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"incursion" => Ok(Self::Incursion),
"strike-force" => Ok(Self::StrikeForce),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for BattleSize {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for BattleSize {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for BattleSize {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///`ChoiceEffect`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "object",
/// "required": [
/// "options",
/// "type"
/// ],
/// "properties": {
/// "choice_label": {
/// "type": "string"
/// },
/// "options": {
/// "type": "array",
/// "items": {
/// "$ref": "#/$defs/effect-node"
/// },
/// "minItems": 2
/// },
/// "type": {
/// "const": "choice"
/// }
/// }
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
pub struct ChoiceEffect {
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub choice_label: ::std::option::Option<::std::string::String>,
pub options: ::std::vec::Vec<EffectNode>,
#[serde(rename = "type")]
pub type_: ::serde_json::Value,
}
///A feature placed on an area template, positioned in the area's centroid-local frame (y-down inches). When the area is placed, rotated, or mirrored, its composed features are carried along.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "A feature placed on an area template, positioned in the area's centroid-local frame (y-down inches). When the area is placed, rotated, or mirrored, its composed features are carried along.",
/// "type": "object",
/// "required": [
/// "position",
/// "template"
/// ],
/// "properties": {
/// "floor": {
/// "description": "Ruin floor this feature occupies (0 = ground level).",
/// "default": 0,
/// "type": "integer",
/// "minimum": 0.0
/// },
/// "id": {
/// "description": "Composition-local id for this feature instance.",
/// "$ref": "#/$defs/entity-id"
/// },
/// "mirror": {
/// "default": "none",
/// "type": "string",
/// "enum": [
/// "none",
/// "horizontal",
/// "vertical"
/// ]
/// },
/// "position": {
/// "description": "The feature's centroid in the area's centroid-local frame (origin at the area centroid, y-down inches).",
/// "$ref": "#/$defs/vec2"
/// },
/// "rotation_degrees": {
/// "description": "Clockwise rotation of the feature about its own centroid, within the area-local frame.",
/// "type": "number",
/// "exclusiveMaximum": 360.0,
/// "minimum": 0.0
/// },
/// "template": {
/// "description": "Id of the feature-kind terrain-template to place.",
/// "$ref": "#/$defs/entity-id"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct ComposedFeature {
///Ruin floor this feature occupies (0 = ground level).
#[serde(default)]
pub floor: u64,
///Composition-local id for this feature instance.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub id: ::std::option::Option<EntityId>,
#[serde(default = "defaults::composed_feature_mirror")]
pub mirror: ComposedFeatureMirror,
///The feature's centroid in the area's centroid-local frame (origin at the area centroid, y-down inches).
pub position: Vec2,
///Clockwise rotation of the feature about its own centroid, within the area-local frame.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub rotation_degrees: ::std::option::Option<f64>,
///Id of the feature-kind terrain-template to place.
pub template: EntityId,
}
///`ComposedFeatureMirror`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "default": "none",
/// "type": "string",
/// "enum": [
/// "none",
/// "horizontal",
/// "vertical"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum ComposedFeatureMirror {
#[serde(rename = "none")]
None,
#[serde(rename = "horizontal")]
Horizontal,
#[serde(rename = "vertical")]
Vertical,
}
impl ::std::fmt::Display for ComposedFeatureMirror {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::None => f.write_str("none"),
Self::Horizontal => f.write_str("horizontal"),
Self::Vertical => f.write_str("vertical"),
}
}
}
impl ::std::str::FromStr for ComposedFeatureMirror {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"none" => Ok(Self::None),
"horizontal" => Ok(Self::Horizontal),
"vertical" => Ok(Self::Vertical),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for ComposedFeatureMirror {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for ComposedFeatureMirror {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for ComposedFeatureMirror {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::default::Default for ComposedFeatureMirror {
fn default() -> Self {
ComposedFeatureMirror::None
}
}
///`CompoundCondition`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "object",
/// "required": [
/// "operands",
/// "operator"
/// ],
/// "properties": {
/// "operands": {
/// "type": "array",
/// "items": {
/// "$ref": "#/$defs/condition-node"
/// },
/// "minItems": 1
/// },
/// "operator": {
/// "type": "string",
/// "enum": [
/// "and",
/// "or",
/// "not"
/// ]
/// }
/// }
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
pub struct CompoundCondition {
pub operands: ::std::vec::Vec<ConditionNode>,
pub operator: CompoundConditionOperator,
}
///`CompoundConditionOperator`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "enum": [
/// "and",
/// "or",
/// "not"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum CompoundConditionOperator {
#[serde(rename = "and")]
And,
#[serde(rename = "or")]
Or,
#[serde(rename = "not")]
Not,
}
impl ::std::fmt::Display for CompoundConditionOperator {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::And => f.write_str("and"),
Self::Or => f.write_str("or"),
Self::Not => f.write_str("not"),
}
}
}
impl ::std::str::FromStr for CompoundConditionOperator {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"and" => Ok(Self::And),
"or" => Ok(Self::Or),
"not" => Ok(Self::Not),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for CompoundConditionOperator {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for CompoundConditionOperator {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for CompoundConditionOperator {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///`Condition`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Ability Condition",
/// "$ref": "#/$defs/condition-node"
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(transparent)]
pub struct Condition(pub ConditionNode);
impl ::std::ops::Deref for Condition {
type Target = ConditionNode;
fn deref(&self) -> &ConditionNode {
&self.0
}
}
impl ::std::convert::From<Condition> for ConditionNode {
fn from(value: Condition) -> Self {
value.0
}
}
impl ::std::convert::From<ConditionNode> for Condition {
fn from(value: ConditionNode) -> Self {
Self(value)
}
}
///`ConditionNode`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "oneOf": [
/// {
/// "$ref": "#/$defs/simple-condition"
/// },
/// {
/// "$ref": "#/$defs/compound-condition"
/// }
/// ]
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(untagged)]
pub enum ConditionNode {
SimpleCondition(SimpleCondition),
CompoundCondition(CompoundCondition),
}
impl ::std::convert::From<SimpleCondition> for ConditionNode {
fn from(value: SimpleCondition) -> Self {
Self::SimpleCondition(value)
}
}
impl ::std::convert::From<CompoundCondition> for ConditionNode {
fn from(value: CompoundCondition) -> Self {
Self::CompoundCondition(value)
}
}
///`ConditionalEffect`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "object",
/// "required": [
/// "condition",
/// "effect",
/// "type"
/// ],
/// "properties": {
/// "condition": {
/// "$ref": "#/$defs/condition"
/// },
/// "effect": {
/// "$ref": "#/$defs/effect-node"
/// },
/// "type": {
/// "const": "conditional"
/// }
/// }
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
pub struct ConditionalEffect {
pub condition: Condition,
pub effect: ::std::boxed::Box<EffectNode>,
#[serde(rename = "type")]
pub type_: ::serde_json::Value,
}
///GitHub handle or '40kdc-community'
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "GitHub handle or '40kdc-community'",
/// "type": "string"
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
#[serde(transparent)]
pub struct ContributorRef(pub ::std::string::String);
impl ::std::ops::Deref for ContributorRef {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<ContributorRef> for ::std::string::String {
fn from(value: ContributorRef) -> Self {
value.0
}
}
impl ::std::convert::From<::std::string::String> for ContributorRef {
fn from(value: ::std::string::String) -> Self {
Self(value)
}
}
impl ::std::str::FromStr for ContributorRef {
type Err = ::std::convert::Infallible;
fn from_str(value: &str) -> ::std::result::Result<Self, Self::Err> {
Ok(Self(value.to_string()))
}
}
impl ::std::fmt::Display for ContributorRef {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
self.0.fmt(f)
}
}
///Dataslate version: a quarterly tag (e.g. '2025-q3') or a named kebab-case slug for non-quarterly slates (e.g. 'pre-launch-provisional')
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Dataslate version: a quarterly tag (e.g. '2025-q3') or a named kebab-case slug for non-quarterly slates (e.g. 'pre-launch-provisional')",
/// "type": "string",
/// "pattern": "^(\\d{4}-q[1-4]|[a-z0-9][a-z0-9-]*[a-z0-9])$"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct DataslateVersion(::std::string::String);
impl ::std::ops::Deref for DataslateVersion {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<DataslateVersion> for ::std::string::String {
fn from(value: DataslateVersion) -> Self {
value.0
}
}
impl ::std::str::FromStr for DataslateVersion {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
static PATTERN: ::std::sync::LazyLock<::regress::Regex> = ::std::sync::LazyLock::new(||
{
::regress::Regex::new("^(\\d{4}-q[1-4]|[a-z0-9][a-z0-9-]*[a-z0-9])$")
.unwrap()
});
if PATTERN.find(value).is_none() {
return Err(
"doesn't match pattern \"^(\\d{4}-q[1-4]|[a-z0-9][a-z0-9-]*[a-z0-9])$\""
.into(),
);
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for DataslateVersion {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for DataslateVersion {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for DataslateVersion {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for DataslateVersion {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///A deployment map: per-side deployment zones, objective positions, and (11e) per-side territory polygons. Pattern geometry carries forward unchanged from 10th edition; downstream tooling (e.g. bevy-deploy-helper) consumes this as the canonical encoding.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Deployment Pattern",
/// "description": "A deployment map: per-side deployment zones, objective positions, and (11e) per-side territory polygons. Pattern geometry carries forward unchanged from 10th edition; downstream tooling (e.g. bevy-deploy-helper) consumes this as the canonical encoding.",
/// "type": "object",
/// "required": [
/// "game_version",
/// "id",
/// "name",
/// "zones"
/// ],
/// "properties": {
/// "description": {
/// "type": "string"
/// },
/// "game_version": {
/// "$ref": "#/$defs/game-version-ref"
/// },
/// "id": {
/// "$ref": "#/$defs/entity-id"
/// },
/// "name": {
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
/// },
/// "objectives": {
/// "description": "Objective-marker positions on the board.",
/// "type": "array",
/// "items": {
/// "$ref": "#/$defs/vec2"
/// }
/// },
/// "recommended_terrain_layout_ids": {
/// "description": "Ids of recommended terrain-layout entities (resolved once terrain-layout data is authored).",
/// "type": "array",
/// "items": {
/// "$ref": "#/$defs/entity-id"
/// },
/// "uniqueItems": true
/// },
/// "source": {
/// "description": "Mission pack or source the pattern originates from (e.g. 'leviathan').",
/// "type": "string",
/// "maxLength": 64,
/// "minLength": 1
/// },
/// "territories": {
/// "description": "11e per-side territory polygons, mirroring the deployment-zone shape (e.g. the band between a deployment zone and the midline). Empty until authored.",
/// "type": "array",
/// "items": {
/// "type": "object",
/// "required": [
/// "player",
/// "position",
/// "shape"
/// ],
/// "properties": {
/// "player": {
/// "$ref": "#/$defs/side"
/// },
/// "position": {
/// "$ref": "#/$defs/vec2"
/// },
/// "shape": {
/// "$ref": "#/$defs/zone-shape"
/// }
/// },
/// "additionalProperties": false
/// }
/// },
/// "zones": {
/// "description": "Per-side deployment zones.",
/// "type": "array",
/// "items": {
/// "type": "object",
/// "required": [
/// "player",
/// "position",
/// "shape"
/// ],
/// "properties": {
/// "color": {
/// "description": "Hex render color for the zone overlay.",
/// "type": "string",
/// "pattern": "^#[0-9a-fA-F]{6}$"
/// },
/// "name": {
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
/// },
/// "player": {
/// "$ref": "#/$defs/side"
/// },
/// "position": {
/// "$ref": "#/$defs/vec2"
/// },
/// "shape": {
/// "$ref": "#/$defs/zone-shape"
/// }
/// },
/// "additionalProperties": false
/// },
/// "minItems": 1
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct DeploymentPattern {
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub description: ::std::option::Option<::std::string::String>,
pub game_version: GameVersionRef,
pub id: EntityId,
pub name: DeploymentPatternName,
///Objective-marker positions on the board.
#[serde(default, skip_serializing_if = "::std::vec::Vec::is_empty")]
pub objectives: ::std::vec::Vec<Vec2>,
///Ids of recommended terrain-layout entities (resolved once terrain-layout data is authored).
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub recommended_terrain_layout_ids: ::std::option::Option<Vec<EntityId>>,
///Mission pack or source the pattern originates from (e.g. 'leviathan').
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub source: ::std::option::Option<DeploymentPatternSource>,
///11e per-side territory polygons, mirroring the deployment-zone shape (e.g. the band between a deployment zone and the midline). Empty until authored.
#[serde(default, skip_serializing_if = "::std::vec::Vec::is_empty")]
pub territories: ::std::vec::Vec<DeploymentPatternTerritoriesItem>,
///Per-side deployment zones.
pub zones: ::std::vec::Vec<DeploymentPatternZonesItem>,
}
///`DeploymentPatternName`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct DeploymentPatternName(::std::string::String);
impl ::std::ops::Deref for DeploymentPatternName {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<DeploymentPatternName> for ::std::string::String {
fn from(value: DeploymentPatternName) -> Self {
value.0
}
}
impl ::std::str::FromStr for DeploymentPatternName {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 128usize {
return Err("longer than 128 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for DeploymentPatternName {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for DeploymentPatternName {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for DeploymentPatternName {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for DeploymentPatternName {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///Mission pack or source the pattern originates from (e.g. 'leviathan').
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Mission pack or source the pattern originates from (e.g. 'leviathan').",
/// "type": "string",
/// "maxLength": 64,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct DeploymentPatternSource(::std::string::String);
impl ::std::ops::Deref for DeploymentPatternSource {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<DeploymentPatternSource> for ::std::string::String {
fn from(value: DeploymentPatternSource) -> Self {
value.0
}
}
impl ::std::str::FromStr for DeploymentPatternSource {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 64usize {
return Err("longer than 64 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for DeploymentPatternSource {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for DeploymentPatternSource {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for DeploymentPatternSource {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for DeploymentPatternSource {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///`DeploymentPatternTerritoriesItem`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "object",
/// "required": [
/// "player",
/// "position",
/// "shape"
/// ],
/// "properties": {
/// "player": {
/// "$ref": "#/$defs/side"
/// },
/// "position": {
/// "$ref": "#/$defs/vec2"
/// },
/// "shape": {
/// "$ref": "#/$defs/zone-shape"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct DeploymentPatternTerritoriesItem {
pub player: Side,
pub position: Vec2,
pub shape: ZoneShape,
}
///`DeploymentPatternZonesItem`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "object",
/// "required": [
/// "player",
/// "position",
/// "shape"
/// ],
/// "properties": {
/// "color": {
/// "description": "Hex render color for the zone overlay.",
/// "type": "string",
/// "pattern": "^#[0-9a-fA-F]{6}$"
/// },
/// "name": {
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
/// },
/// "player": {
/// "$ref": "#/$defs/side"
/// },
/// "position": {
/// "$ref": "#/$defs/vec2"
/// },
/// "shape": {
/// "$ref": "#/$defs/zone-shape"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct DeploymentPatternZonesItem {
///Hex render color for the zone overlay.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub color: ::std::option::Option<DeploymentPatternZonesItemColor>,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub name: ::std::option::Option<DeploymentPatternZonesItemName>,
pub player: Side,
pub position: Vec2,
pub shape: ZoneShape,
}
///Hex render color for the zone overlay.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Hex render color for the zone overlay.",
/// "type": "string",
/// "pattern": "^#[0-9a-fA-F]{6}$"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct DeploymentPatternZonesItemColor(::std::string::String);
impl ::std::ops::Deref for DeploymentPatternZonesItemColor {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<DeploymentPatternZonesItemColor> for ::std::string::String {
fn from(value: DeploymentPatternZonesItemColor) -> Self {
value.0
}
}
impl ::std::str::FromStr for DeploymentPatternZonesItemColor {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
static PATTERN: ::std::sync::LazyLock<::regress::Regex> = ::std::sync::LazyLock::new(||
{ ::regress::Regex::new("^#[0-9a-fA-F]{6}$").unwrap() });
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^#[0-9a-fA-F]{6}$\"".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for DeploymentPatternZonesItemColor {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String>
for DeploymentPatternZonesItemColor {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for DeploymentPatternZonesItemColor {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for DeploymentPatternZonesItemColor {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///`DeploymentPatternZonesItemName`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct DeploymentPatternZonesItemName(::std::string::String);
impl ::std::ops::Deref for DeploymentPatternZonesItemName {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<DeploymentPatternZonesItemName> for ::std::string::String {
fn from(value: DeploymentPatternZonesItemName) -> Self {
value.0
}
}
impl ::std::str::FromStr for DeploymentPatternZonesItemName {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 128usize {
return Err("longer than 128 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for DeploymentPatternZonesItemName {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for DeploymentPatternZonesItemName {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for DeploymentPatternZonesItemName {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for DeploymentPatternZonesItemName {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///A detachment option within a faction, providing a detachment rule, enhancements, and stratagems.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Detachment",
/// "description": "A detachment option within a faction, providing a detachment rule, enhancements, and stratagems.",
/// "type": "object",
/// "required": [
/// "faction_id",
/// "game_version",
/// "id",
/// "name"
/// ],
/// "properties": {
/// "detachment_points": {
/// "description": "11e: the detachment-point cost (1–3) charged against the army's detachment-point budget. null when not yet assigned.",
/// "oneOf": [
/// {
/// "type": "integer",
/// "maximum": 3.0,
/// "minimum": 1.0
/// },
/// {
/// "type": "null"
/// }
/// ]
/// },
/// "detachment_rule_id": {
/// "oneOf": [
/// {
/// "$ref": "#/$defs/entity-id"
/// },
/// {
/// "type": "null"
/// }
/// ]
/// },
/// "enhancement_ids": {
/// "type": "array",
/// "items": {
/// "$ref": "#/$defs/entity-id"
/// }
/// },
/// "faction_id": {
/// "$ref": "#/$defs/entity-id"
/// },
/// "force_dispositions": {
/// "description": "11e: ids of the Force Disposition entities this detachment grants. Empty until assigned.",
/// "type": "array",
/// "items": {
/// "$ref": "#/$defs/entity-id"
/// },
/// "uniqueItems": true
/// },
/// "game_version": {
/// "$ref": "#/$defs/game-version-ref"
/// },
/// "id": {
/// "$ref": "#/$defs/entity-id"
/// },
/// "name": {
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
/// },
/// "restrictions": {
/// "oneOf": [
/// {
/// "type": "object",
/// "properties": {
/// "excluded_keywords": {
/// "$ref": "#/$defs/keyword-list"
/// },
/// "notes": {
/// "type": "string"
/// },
/// "required_keywords": {
/// "$ref": "#/$defs/keyword-list"
/// }
/// },
/// "additionalProperties": false
/// },
/// {
/// "type": "null"
/// }
/// ]
/// },
/// "stratagem_ids": {
/// "type": "array",
/// "items": {
/// "$ref": "#/$defs/entity-id"
/// }
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct Detachment {
///11e: the detachment-point cost (1–3) charged against the army's detachment-point budget. null when not yet assigned.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub detachment_points: ::std::option::Option<::std::num::NonZeroU64>,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub detachment_rule_id: ::std::option::Option<EntityId>,
#[serde(default, skip_serializing_if = "::std::vec::Vec::is_empty")]
pub enhancement_ids: ::std::vec::Vec<EntityId>,
pub faction_id: EntityId,
///11e: ids of the Force Disposition entities this detachment grants. Empty until assigned.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub force_dispositions: ::std::option::Option<Vec<EntityId>>,
pub game_version: GameVersionRef,
pub id: EntityId,
pub name: DetachmentName,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub restrictions: ::std::option::Option<DetachmentRestrictions>,
#[serde(default, skip_serializing_if = "::std::vec::Vec::is_empty")]
pub stratagem_ids: ::std::vec::Vec<EntityId>,
}
///`DetachmentName`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct DetachmentName(::std::string::String);
impl ::std::ops::Deref for DetachmentName {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<DetachmentName> for ::std::string::String {
fn from(value: DetachmentName) -> Self {
value.0
}
}
impl ::std::str::FromStr for DetachmentName {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 128usize {
return Err("longer than 128 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for DetachmentName {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for DetachmentName {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for DetachmentName {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for DetachmentName {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///`DetachmentRestrictions`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "object",
/// "properties": {
/// "excluded_keywords": {
/// "$ref": "#/$defs/keyword-list"
/// },
/// "notes": {
/// "type": "string"
/// },
/// "required_keywords": {
/// "$ref": "#/$defs/keyword-list"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct DetachmentRestrictions {
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub excluded_keywords: ::std::option::Option<KeywordList>,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub notes: ::std::option::Option<::std::string::String>,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub required_keywords: ::std::option::Option<KeywordList>,
}
impl ::std::default::Default for DetachmentRestrictions {
fn default() -> Self {
Self {
excluded_keywords: Default::default(),
notes: Default::default(),
required_keywords: Default::default(),
}
}
}
///`DiceGatedEffect`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "object",
/// "required": [
/// "dice",
/// "threshold",
/// "type"
/// ],
/// "properties": {
/// "comparison": {
/// "default": "gte",
/// "type": "string",
/// "enum": [
/// "gte",
/// "lte",
/// "gt",
/// "lt",
/// "eq"
/// ]
/// },
/// "dice": {
/// "description": "Dice expression, e.g. 'D6', '2D6'",
/// "type": "string"
/// },
/// "on_fail": {
/// "oneOf": [
/// {
/// "$ref": "#/$defs/effect-node"
/// },
/// {
/// "type": "null"
/// }
/// ]
/// },
/// "on_success": {
/// "oneOf": [
/// {
/// "$ref": "#/$defs/effect-node"
/// },
/// {
/// "type": "null"
/// }
/// ]
/// },
/// "threshold": {
/// "description": "Fixed threshold or model characteristic to compare against",
/// "oneOf": [
/// {
/// "type": "integer"
/// },
/// {
/// "type": "string",
/// "enum": [
/// "leadership",
/// "toughness",
/// "save"
/// ]
/// }
/// ]
/// },
/// "type": {
/// "const": "dice-gated"
/// }
/// }
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
pub struct DiceGatedEffect {
#[serde(default = "defaults::dice_gated_effect_comparison")]
pub comparison: DiceGatedEffectComparison,
///Dice expression, e.g. 'D6', '2D6'
pub dice: ::std::string::String,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub on_fail: ::std::option::Option<::std::boxed::Box<EffectNode>>,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub on_success: ::std::option::Option<::std::boxed::Box<EffectNode>>,
///Fixed threshold or model characteristic to compare against
pub threshold: DiceGatedEffectThreshold,
#[serde(rename = "type")]
pub type_: ::serde_json::Value,
}
///`DiceGatedEffectComparison`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "default": "gte",
/// "type": "string",
/// "enum": [
/// "gte",
/// "lte",
/// "gt",
/// "lt",
/// "eq"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum DiceGatedEffectComparison {
#[serde(rename = "gte")]
Gte,
#[serde(rename = "lte")]
Lte,
#[serde(rename = "gt")]
Gt,
#[serde(rename = "lt")]
Lt,
#[serde(rename = "eq")]
Eq,
}
impl ::std::fmt::Display for DiceGatedEffectComparison {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Gte => f.write_str("gte"),
Self::Lte => f.write_str("lte"),
Self::Gt => f.write_str("gt"),
Self::Lt => f.write_str("lt"),
Self::Eq => f.write_str("eq"),
}
}
}
impl ::std::str::FromStr for DiceGatedEffectComparison {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"gte" => Ok(Self::Gte),
"lte" => Ok(Self::Lte),
"gt" => Ok(Self::Gt),
"lt" => Ok(Self::Lt),
"eq" => Ok(Self::Eq),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for DiceGatedEffectComparison {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for DiceGatedEffectComparison {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for DiceGatedEffectComparison {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::default::Default for DiceGatedEffectComparison {
fn default() -> Self {
DiceGatedEffectComparison::Gte
}
}
///Fixed threshold or model characteristic to compare against
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Fixed threshold or model characteristic to compare against",
/// "oneOf": [
/// {
/// "type": "integer"
/// },
/// {
/// "type": "string",
/// "enum": [
/// "leadership",
/// "toughness",
/// "save"
/// ]
/// }
/// ]
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(untagged)]
pub enum DiceGatedEffectThreshold {
Integer(i64),
String(DiceGatedEffectThresholdString),
}
impl ::std::str::FromStr for DiceGatedEffectThreshold {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
if let Ok(v) = value.parse() {
Ok(Self::Integer(v))
} else if let Ok(v) = value.parse() {
Ok(Self::String(v))
} else {
Err("string conversion failed for all variants".into())
}
}
}
impl ::std::convert::TryFrom<&str> for DiceGatedEffectThreshold {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for DiceGatedEffectThreshold {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for DiceGatedEffectThreshold {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::fmt::Display for DiceGatedEffectThreshold {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match self {
Self::Integer(x) => x.fmt(f),
Self::String(x) => x.fmt(f),
}
}
}
impl ::std::convert::From<i64> for DiceGatedEffectThreshold {
fn from(value: i64) -> Self {
Self::Integer(value)
}
}
impl ::std::convert::From<DiceGatedEffectThresholdString> for DiceGatedEffectThreshold {
fn from(value: DiceGatedEffectThresholdString) -> Self {
Self::String(value)
}
}
///`DiceGatedEffectThresholdString`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "enum": [
/// "leadership",
/// "toughness",
/// "save"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum DiceGatedEffectThresholdString {
#[serde(rename = "leadership")]
Leadership,
#[serde(rename = "toughness")]
Toughness,
#[serde(rename = "save")]
Save,
}
impl ::std::fmt::Display for DiceGatedEffectThresholdString {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Leadership => f.write_str("leadership"),
Self::Toughness => f.write_str("toughness"),
Self::Save => f.write_str("save"),
}
}
}
impl ::std::str::FromStr for DiceGatedEffectThresholdString {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"leadership" => Ok(Self::Leadership),
"toughness" => Ok(Self::Toughness),
"save" => Ok(Self::Save),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for DiceGatedEffectThresholdString {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for DiceGatedEffectThresholdString {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for DiceGatedEffectThresholdString {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///`DicePoolAllocationEffect`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "object",
/// "required": [
/// "max_activations",
/// "options",
/// "pool",
/// "type"
/// ],
/// "properties": {
/// "max_activations": {
/// "type": "integer",
/// "minimum": 1.0
/// },
/// "options": {
/// "type": "array",
/// "items": {
/// "type": "object",
/// "required": [
/// "effect",
/// "name",
/// "requirement"
/// ],
/// "properties": {
/// "effect": {
/// "$ref": "#/$defs/effect-node"
/// },
/// "name": {
/// "type": "string"
/// },
/// "requirement": {
/// "type": "object",
/// "required": [
/// "min_value",
/// "type"
/// ],
/// "properties": {
/// "min_value": {
/// "type": "integer",
/// "maximum": 6.0,
/// "minimum": 1.0
/// },
/// "type": {
/// "type": "string",
/// "enum": [
/// "pair",
/// "triple",
/// "single",
/// "run"
/// ]
/// }
/// }
/// }
/// }
/// },
/// "minItems": 1
/// },
/// "pool": {
/// "type": "object",
/// "required": [
/// "count",
/// "die"
/// ],
/// "properties": {
/// "count": {
/// "type": "integer",
/// "minimum": 1.0
/// },
/// "die": {
/// "type": "string"
/// }
/// }
/// },
/// "type": {
/// "const": "dice-pool-allocation"
/// }
/// }
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
pub struct DicePoolAllocationEffect {
pub max_activations: ::std::num::NonZeroU64,
pub options: ::std::vec::Vec<DicePoolAllocationEffectOptionsItem>,
pub pool: DicePoolAllocationEffectPool,
#[serde(rename = "type")]
pub type_: ::serde_json::Value,
}
///`DicePoolAllocationEffectOptionsItem`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "object",
/// "required": [
/// "effect",
/// "name",
/// "requirement"
/// ],
/// "properties": {
/// "effect": {
/// "$ref": "#/$defs/effect-node"
/// },
/// "name": {
/// "type": "string"
/// },
/// "requirement": {
/// "type": "object",
/// "required": [
/// "min_value",
/// "type"
/// ],
/// "properties": {
/// "min_value": {
/// "type": "integer",
/// "maximum": 6.0,
/// "minimum": 1.0
/// },
/// "type": {
/// "type": "string",
/// "enum": [
/// "pair",
/// "triple",
/// "single",
/// "run"
/// ]
/// }
/// }
/// }
/// }
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
pub struct DicePoolAllocationEffectOptionsItem {
pub effect: EffectNode,
pub name: ::std::string::String,
pub requirement: DicePoolAllocationEffectOptionsItemRequirement,
}
///`DicePoolAllocationEffectOptionsItemRequirement`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "object",
/// "required": [
/// "min_value",
/// "type"
/// ],
/// "properties": {
/// "min_value": {
/// "type": "integer",
/// "maximum": 6.0,
/// "minimum": 1.0
/// },
/// "type": {
/// "type": "string",
/// "enum": [
/// "pair",
/// "triple",
/// "single",
/// "run"
/// ]
/// }
/// }
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
pub struct DicePoolAllocationEffectOptionsItemRequirement {
pub min_value: ::std::num::NonZeroU64,
#[serde(rename = "type")]
pub type_: DicePoolAllocationEffectOptionsItemRequirementType,
}
///`DicePoolAllocationEffectOptionsItemRequirementType`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "enum": [
/// "pair",
/// "triple",
/// "single",
/// "run"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum DicePoolAllocationEffectOptionsItemRequirementType {
#[serde(rename = "pair")]
Pair,
#[serde(rename = "triple")]
Triple,
#[serde(rename = "single")]
Single,
#[serde(rename = "run")]
Run,
}
impl ::std::fmt::Display for DicePoolAllocationEffectOptionsItemRequirementType {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Pair => f.write_str("pair"),
Self::Triple => f.write_str("triple"),
Self::Single => f.write_str("single"),
Self::Run => f.write_str("run"),
}
}
}
impl ::std::str::FromStr for DicePoolAllocationEffectOptionsItemRequirementType {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"pair" => Ok(Self::Pair),
"triple" => Ok(Self::Triple),
"single" => Ok(Self::Single),
"run" => Ok(Self::Run),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str>
for DicePoolAllocationEffectOptionsItemRequirementType {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String>
for DicePoolAllocationEffectOptionsItemRequirementType {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String>
for DicePoolAllocationEffectOptionsItemRequirementType {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///`DicePoolAllocationEffectPool`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "object",
/// "required": [
/// "count",
/// "die"
/// ],
/// "properties": {
/// "count": {
/// "type": "integer",
/// "minimum": 1.0
/// },
/// "die": {
/// "type": "string"
/// }
/// }
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
pub struct DicePoolAllocationEffectPool {
pub count: ::std::num::NonZeroU64,
pub die: ::std::string::String,
}
///Game edition, e.g. '10th' or '11'
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Game edition, e.g. '10th' or '11'",
/// "type": "string",
/// "pattern": "^\\d{1,2}(th)?$"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct Edition(::std::string::String);
impl ::std::ops::Deref for Edition {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<Edition> for ::std::string::String {
fn from(value: Edition) -> Self {
value.0
}
}
impl ::std::str::FromStr for Edition {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
static PATTERN: ::std::sync::LazyLock<::regress::Regex> = ::std::sync::LazyLock::new(||
{ ::regress::Regex::new("^\\d{1,2}(th)?$").unwrap() });
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^\\d{1,2}(th)?$\"".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for Edition {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for Edition {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for Edition {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for Edition {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///`Effect`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Ability Effect",
/// "$ref": "#/$defs/effect-node"
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(transparent)]
pub struct Effect(pub EffectNode);
impl ::std::ops::Deref for Effect {
type Target = EffectNode;
fn deref(&self) -> &EffectNode {
&self.0
}
}
impl ::std::convert::From<Effect> for EffectNode {
fn from(value: Effect) -> Self {
value.0
}
}
impl ::std::convert::From<EffectNode> for Effect {
fn from(value: EffectNode) -> Self {
Self(value)
}
}
///`EffectNode`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "oneOf": [
/// {
/// "$ref": "#/$defs/single-effect"
/// },
/// {
/// "$ref": "#/$defs/choice-effect"
/// },
/// {
/// "$ref": "#/$defs/sequence-effect"
/// },
/// {
/// "$ref": "#/$defs/dice-gated-effect"
/// },
/// {
/// "$ref": "#/$defs/conditional-effect"
/// },
/// {
/// "$ref": "#/$defs/dice-pool-allocation-effect"
/// }
/// ]
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(untagged)]
pub enum EffectNode {
SingleEffect(SingleEffect),
ChoiceEffect(ChoiceEffect),
SequenceEffect(SequenceEffect),
DiceGatedEffect(DiceGatedEffect),
ConditionalEffect(ConditionalEffect),
DicePoolAllocationEffect(DicePoolAllocationEffect),
}
impl ::std::convert::From<SingleEffect> for EffectNode {
fn from(value: SingleEffect) -> Self {
Self::SingleEffect(value)
}
}
impl ::std::convert::From<ChoiceEffect> for EffectNode {
fn from(value: ChoiceEffect) -> Self {
Self::ChoiceEffect(value)
}
}
impl ::std::convert::From<SequenceEffect> for EffectNode {
fn from(value: SequenceEffect) -> Self {
Self::SequenceEffect(value)
}
}
impl ::std::convert::From<DiceGatedEffect> for EffectNode {
fn from(value: DiceGatedEffect) -> Self {
Self::DiceGatedEffect(value)
}
}
impl ::std::convert::From<ConditionalEffect> for EffectNode {
fn from(value: ConditionalEffect) -> Self {
Self::ConditionalEffect(value)
}
}
impl ::std::convert::From<DicePoolAllocationEffect> for EffectNode {
fn from(value: DicePoolAllocationEffect) -> Self {
Self::DicePoolAllocationEffect(value)
}
}
///A purchasable upgrade for a character unit, provided by a detachment.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Enhancement",
/// "description": "A purchasable upgrade for a character unit, provided by a detachment.",
/// "type": "object",
/// "required": [
/// "cost",
/// "detachment_id",
/// "game_version",
/// "id",
/// "name"
/// ],
/// "properties": {
/// "ability_id": {
/// "oneOf": [
/// {
/// "$ref": "#/$defs/entity-id"
/// },
/// {
/// "type": "null"
/// }
/// ]
/// },
/// "cost": {
/// "type": "integer",
/// "minimum": 0.0
/// },
/// "detachment_id": {
/// "$ref": "#/$defs/entity-id"
/// },
/// "exclusion_keywords": {
/// "oneOf": [
/// {
/// "$ref": "#/$defs/keyword-list"
/// },
/// {
/// "type": "null"
/// }
/// ]
/// },
/// "game_version": {
/// "$ref": "#/$defs/game-version-ref"
/// },
/// "id": {
/// "$ref": "#/$defs/entity-id"
/// },
/// "is_unique": {
/// "default": true,
/// "type": "boolean"
/// },
/// "keyword_restrictions": {
/// "$ref": "#/$defs/keyword-list"
/// },
/// "max_targets": {
/// "description": "Number of units this enhancement may be applied to. Only meaningful when `upgrade_tag` is true; defaults to 1.",
/// "default": 1,
/// "type": "integer",
/// "minimum": 1.0
/// },
/// "name": {
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
/// },
/// "points_provisional": {
/// "description": "True when the cost is carried over provisionally (e.g. seeded from a prior edition during migration) and not yet confirmed against the current dataslate.",
/// "default": false,
/// "type": "boolean"
/// },
/// "upgrade_tag": {
/// "description": "11e: when true, this enhancement applies to up to `max_targets` non-character units while counting as a single Enhancement choice.",
/// "default": false,
/// "type": "boolean"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct Enhancement {
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub ability_id: ::std::option::Option<EntityId>,
pub cost: u64,
pub detachment_id: EntityId,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub exclusion_keywords: ::std::option::Option<KeywordList>,
pub game_version: GameVersionRef,
pub id: EntityId,
#[serde(default = "defaults::default_bool::<true>")]
pub is_unique: bool,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub keyword_restrictions: ::std::option::Option<KeywordList>,
///Number of units this enhancement may be applied to. Only meaningful when `upgrade_tag` is true; defaults to 1.
#[serde(default = "defaults::default_nzu64::<::std::num::NonZeroU64, 1>")]
pub max_targets: ::std::num::NonZeroU64,
pub name: EnhancementName,
///True when the cost is carried over provisionally (e.g. seeded from a prior edition during migration) and not yet confirmed against the current dataslate.
#[serde(default)]
pub points_provisional: bool,
///11e: when true, this enhancement applies to up to `max_targets` non-character units while counting as a single Enhancement choice.
#[serde(default)]
pub upgrade_tag: bool,
}
///`EnhancementName`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct EnhancementName(::std::string::String);
impl ::std::ops::Deref for EnhancementName {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<EnhancementName> for ::std::string::String {
fn from(value: EnhancementName) -> Self {
value.0
}
}
impl ::std::str::FromStr for EnhancementName {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 128usize {
return Err("longer than 128 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for EnhancementName {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for EnhancementName {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for EnhancementName {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for EnhancementName {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///Kebab-case identifier
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Kebab-case identifier",
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 2,
/// "pattern": "^[a-z0-9][a-z0-9-]*[a-z0-9]$"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct EntityId(::std::string::String);
impl ::std::ops::Deref for EntityId {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<EntityId> for ::std::string::String {
fn from(value: EntityId) -> Self {
value.0
}
}
impl ::std::str::FromStr for EntityId {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 128usize {
return Err("longer than 128 characters".into());
}
if value.chars().count() < 2usize {
return Err("shorter than 2 characters".into());
}
static PATTERN: ::std::sync::LazyLock<::regress::Regex> = ::std::sync::LazyLock::new(||
{ ::regress::Regex::new("^[a-z0-9][a-z0-9-]*[a-z0-9]$").unwrap() });
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^[a-z0-9][a-z0-9-]*[a-z0-9]$\"".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for EntityId {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for EntityId {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for EntityId {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for EntityId {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///A playable faction or sub-faction.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Faction",
/// "description": "A playable faction or sub-faction.",
/// "type": "object",
/// "required": [
/// "game_version",
/// "id",
/// "name"
/// ],
/// "properties": {
/// "aliases": {
/// "type": "array",
/// "items": {
/// "type": "string"
/// }
/// },
/// "faction_rule_id": {
/// "description": "Reference to the faction-wide ability (e.g., Oath of Moment)",
/// "oneOf": [
/// {
/// "$ref": "#/$defs/entity-id"
/// },
/// {
/// "type": "null"
/// }
/// ]
/// },
/// "game_version": {
/// "$ref": "#/$defs/game-version-ref"
/// },
/// "id": {
/// "$ref": "#/$defs/entity-id"
/// },
/// "keywords": {
/// "$ref": "#/$defs/keyword-list"
/// },
/// "name": {
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
/// },
/// "parent_faction_id": {
/// "oneOf": [
/// {
/// "$ref": "#/$defs/entity-id"
/// },
/// {
/// "type": "null"
/// }
/// ]
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct Faction {
#[serde(default, skip_serializing_if = "::std::vec::Vec::is_empty")]
pub aliases: ::std::vec::Vec<::std::string::String>,
///Reference to the faction-wide ability (e.g., Oath of Moment)
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub faction_rule_id: ::std::option::Option<EntityId>,
pub game_version: GameVersionRef,
pub id: EntityId,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub keywords: ::std::option::Option<KeywordList>,
pub name: FactionName,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub parent_faction_id: ::std::option::Option<EntityId>,
}
///`FactionName`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct FactionName(::std::string::String);
impl ::std::ops::Deref for FactionName {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<FactionName> for ::std::string::String {
fn from(value: FactionName) -> Self {
value.0
}
}
impl ::std::str::FromStr for FactionName {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 128usize {
return Err("longer than 128 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for FactionName {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for FactionName {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for FactionName {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for FactionName {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///A terrain piece's 2D footprint in local inches (y-down): an axis-aligned rectangle with its min corner at the local origin, a right triangle with the right angle at the local origin and legs along +x/+y, or an explicit polygon (>= 3 points). The placement resolver re-centers the footprint on its polygon area centroid, so the local-origin convention does not affect where the piece lands — only its shape matters.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "A terrain piece's 2D footprint in local inches (y-down): an axis-aligned rectangle with its min corner at the local origin, a right triangle with the right angle at the local origin and legs along +x/+y, or an explicit polygon (>= 3 points). The placement resolver re-centers the footprint on its polygon area centroid, so the local-origin convention does not affect where the piece lands — only its shape matters.",
/// "oneOf": [
/// {
/// "type": "object",
/// "required": [
/// "height",
/// "type",
/// "width"
/// ],
/// "properties": {
/// "height": {
/// "type": "number",
/// "exclusiveMinimum": 0.0
/// },
/// "type": {
/// "const": "rectangle"
/// },
/// "width": {
/// "type": "number",
/// "exclusiveMinimum": 0.0
/// }
/// },
/// "additionalProperties": false
/// },
/// {
/// "type": "object",
/// "required": [
/// "height",
/// "type",
/// "width"
/// ],
/// "properties": {
/// "height": {
/// "type": "number",
/// "exclusiveMinimum": 0.0
/// },
/// "type": {
/// "const": "right-triangle"
/// },
/// "width": {
/// "type": "number",
/// "exclusiveMinimum": 0.0
/// }
/// },
/// "additionalProperties": false
/// },
/// {
/// "type": "object",
/// "required": [
/// "points",
/// "type"
/// ],
/// "properties": {
/// "points": {
/// "type": "array",
/// "items": {
/// "$ref": "#/$defs/vec2"
/// },
/// "minItems": 3
/// },
/// "type": {
/// "const": "polygon"
/// }
/// },
/// "additionalProperties": false
/// }
/// ]
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(tag = "type", deny_unknown_fields)]
pub enum Footprint {
#[serde(rename = "rectangle")]
Rectangle { height: f64, width: f64 },
#[serde(rename = "right-triangle")]
RightTriangle { height: f64, width: f64 },
#[serde(rename = "polygon")]
Polygon { points: ::std::vec::Vec<Vec2> },
}
///A 11e strategic-intent tag granted by detachments. Players compare dispositions at game start to determine the shared mission; asymmetric primary objectives result.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Force Disposition",
/// "description": "A 11e strategic-intent tag granted by detachments. Players compare dispositions at game start to determine the shared mission; asymmetric primary objectives result.",
/// "type": "object",
/// "required": [
/// "game_version",
/// "id",
/// "name"
/// ],
/// "properties": {
/// "game_version": {
/// "$ref": "#/$defs/game-version-ref"
/// },
/// "id": {
/// "description": "One of the five confirmed launch Force Dispositions.",
/// "$ref": "#/$defs/force-disposition-id"
/// },
/// "name": {
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
/// },
/// "text": {
/// "description": "Community-authored description of the disposition's effect (original prose only — no reproduced rules text).",
/// "type": "string",
/// "minLength": 1
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct ForceDisposition {
pub game_version: GameVersionRef,
///One of the five confirmed launch Force Dispositions.
pub id: ForceDispositionId,
pub name: ForceDispositionName,
///Community-authored description of the disposition's effect (original prose only — no reproduced rules text).
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub text: ::std::option::Option<ForceDispositionText>,
}
///One of the five confirmed 11e launch Force Dispositions. Shared by force-disposition entities and the mission-matchup matrix.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "One of the five confirmed 11e launch Force Dispositions. Shared by force-disposition entities and the mission-matchup matrix.",
/// "type": "string",
/// "enum": [
/// "take-and-hold",
/// "disruption",
/// "purge-the-foe",
/// "priority-assets",
/// "reconnaissance"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum ForceDispositionId {
#[serde(rename = "take-and-hold")]
TakeAndHold,
#[serde(rename = "disruption")]
Disruption,
#[serde(rename = "purge-the-foe")]
PurgeTheFoe,
#[serde(rename = "priority-assets")]
PriorityAssets,
#[serde(rename = "reconnaissance")]
Reconnaissance,
}
impl ::std::fmt::Display for ForceDispositionId {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::TakeAndHold => f.write_str("take-and-hold"),
Self::Disruption => f.write_str("disruption"),
Self::PurgeTheFoe => f.write_str("purge-the-foe"),
Self::PriorityAssets => f.write_str("priority-assets"),
Self::Reconnaissance => f.write_str("reconnaissance"),
}
}
}
impl ::std::str::FromStr for ForceDispositionId {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"take-and-hold" => Ok(Self::TakeAndHold),
"disruption" => Ok(Self::Disruption),
"purge-the-foe" => Ok(Self::PurgeTheFoe),
"priority-assets" => Ok(Self::PriorityAssets),
"reconnaissance" => Ok(Self::Reconnaissance),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for ForceDispositionId {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for ForceDispositionId {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for ForceDispositionId {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///`ForceDispositionName`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct ForceDispositionName(::std::string::String);
impl ::std::ops::Deref for ForceDispositionName {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<ForceDispositionName> for ::std::string::String {
fn from(value: ForceDispositionName) -> Self {
value.0
}
}
impl ::std::str::FromStr for ForceDispositionName {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 128usize {
return Err("longer than 128 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for ForceDispositionName {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for ForceDispositionName {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for ForceDispositionName {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for ForceDispositionName {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///Community-authored description of the disposition's effect (original prose only — no reproduced rules text).
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Community-authored description of the disposition's effect (original prose only — no reproduced rules text).",
/// "type": "string",
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct ForceDispositionText(::std::string::String);
impl ::std::ops::Deref for ForceDispositionText {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<ForceDispositionText> for ::std::string::String {
fn from(value: ForceDispositionText) -> Self {
value.0
}
}
impl ::std::str::FromStr for ForceDispositionText {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for ForceDispositionText {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for ForceDispositionText {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for ForceDispositionText {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for ForceDispositionText {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///`GameVersion`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Game Version",
/// "type": "object",
/// "required": [
/// "dataslate",
/// "edition",
/// "effective_date"
/// ],
/// "properties": {
/// "dataslate": {
/// "$ref": "#/$defs/dataslate-version"
/// },
/// "edition": {
/// "$ref": "#/$defs/edition"
/// },
/// "effective_date": {
/// "type": "string",
/// "format": "date"
/// },
/// "label": {
/// "type": "string"
/// },
/// "supersedes": {
/// "oneOf": [
/// {
/// "$ref": "#/$defs/dataslate-version"
/// },
/// {
/// "type": "null"
/// }
/// ]
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct GameVersion {
pub dataslate: DataslateVersion,
pub edition: Edition,
pub effective_date: ::chrono::naive::NaiveDate,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub label: ::std::option::Option<::std::string::String>,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub supersedes: ::std::option::Option<DataslateVersion>,
}
///`GameVersionRef`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Game Version Reference",
/// "type": "object",
/// "required": [
/// "dataslate",
/// "edition"
/// ],
/// "properties": {
/// "dataslate": {
/// "$ref": "#/$defs/dataslate-version"
/// },
/// "edition": {
/// "$ref": "#/$defs/edition"
/// }
/// }
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
pub struct GameVersionRef {
pub dataslate: DataslateVersion,
pub edition: Edition,
}
///`InteractionFlag`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Interaction Flag",
/// "type": "object",
/// "required": [
/// "ability_a",
/// "ability_b",
/// "game_version",
/// "interaction_type"
/// ],
/// "properties": {
/// "ability_a": {
/// "$ref": "#/$defs/entity-id"
/// },
/// "ability_b": {
/// "$ref": "#/$defs/entity-id"
/// },
/// "authored_by": {
/// "$ref": "#/$defs/contributor-ref"
/// },
/// "disputed": {
/// "default": false,
/// "type": "boolean"
/// },
/// "faq_reference": {
/// "type": "string"
/// },
/// "game_version": {
/// "$ref": "#/$defs/game-version-ref"
/// },
/// "interaction_type": {
/// "type": "string",
/// "enum": [
/// "conflicts",
/// "combos",
/// "sequencing-dependent",
/// "stacks",
/// "does-not-stack",
/// "replaces"
/// ]
/// },
/// "resolution": {
/// "type": "string"
/// }
/// }
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
pub struct InteractionFlag {
pub ability_a: EntityId,
pub ability_b: EntityId,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub authored_by: ::std::option::Option<ContributorRef>,
#[serde(default)]
pub disputed: bool,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub faq_reference: ::std::option::Option<::std::string::String>,
pub game_version: GameVersionRef,
pub interaction_type: InteractionFlagInteractionType,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub resolution: ::std::option::Option<::std::string::String>,
}
///`InteractionFlagInteractionType`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "enum": [
/// "conflicts",
/// "combos",
/// "sequencing-dependent",
/// "stacks",
/// "does-not-stack",
/// "replaces"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum InteractionFlagInteractionType {
#[serde(rename = "conflicts")]
Conflicts,
#[serde(rename = "combos")]
Combos,
#[serde(rename = "sequencing-dependent")]
SequencingDependent,
#[serde(rename = "stacks")]
Stacks,
#[serde(rename = "does-not-stack")]
DoesNotStack,
#[serde(rename = "replaces")]
Replaces,
}
impl ::std::fmt::Display for InteractionFlagInteractionType {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Conflicts => f.write_str("conflicts"),
Self::Combos => f.write_str("combos"),
Self::SequencingDependent => f.write_str("sequencing-dependent"),
Self::Stacks => f.write_str("stacks"),
Self::DoesNotStack => f.write_str("does-not-stack"),
Self::Replaces => f.write_str("replaces"),
}
}
}
impl ::std::str::FromStr for InteractionFlagInteractionType {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"conflicts" => Ok(Self::Conflicts),
"combos" => Ok(Self::Combos),
"sequencing-dependent" => Ok(Self::SequencingDependent),
"stacks" => Ok(Self::Stacks),
"does-not-stack" => Ok(Self::DoesNotStack),
"replaces" => Ok(Self::Replaces),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for InteractionFlagInteractionType {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for InteractionFlagInteractionType {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for InteractionFlagInteractionType {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///`Keyword`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "maxLength": 64,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct Keyword(::std::string::String);
impl ::std::ops::Deref for Keyword {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<Keyword> for ::std::string::String {
fn from(value: Keyword) -> Self {
value.0
}
}
impl ::std::str::FromStr for Keyword {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 64usize {
return Err("longer than 64 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for Keyword {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for Keyword {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for Keyword {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for Keyword {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///`KeywordList`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "array",
/// "items": {
/// "$ref": "#/$defs/keyword"
/// },
/// "uniqueItems": true
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(transparent)]
pub struct KeywordList(pub Vec<Keyword>);
impl ::std::ops::Deref for KeywordList {
type Target = Vec<Keyword>;
fn deref(&self) -> &Vec<Keyword> {
&self.0
}
}
impl ::std::convert::From<KeywordList> for Vec<Keyword> {
fn from(value: KeywordList) -> Self {
value.0
}
}
impl ::std::convert::From<Vec<Keyword>> for KeywordList {
fn from(value: Vec<Keyword>) -> Self {
Self(value)
}
}
///Defines which character units can attach to which bodyguard units.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Leader Attachment",
/// "description": "Defines which character units can attach to which bodyguard units.",
/// "type": "object",
/// "required": [
/// "eligible_bodyguard_ids",
/// "game_version",
/// "leader_id"
/// ],
/// "properties": {
/// "eligible_bodyguard_ids": {
/// "type": "array",
/// "items": {
/// "$ref": "#/$defs/entity-id"
/// },
/// "minItems": 1
/// },
/// "game_version": {
/// "$ref": "#/$defs/game-version-ref"
/// },
/// "leader_id": {
/// "$ref": "#/$defs/entity-id"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct LeaderAttachment {
pub eligible_bodyguard_ids: ::std::vec::Vec<EntityId>,
pub game_version: GameVersionRef,
pub leader_id: EntityId,
}
///An 11e primary mission (the objective a player scores). Which mission a player plays is selected by the Force Disposition matchup matrix (see mission-matchup), keyed on the player's own disposition and their opponent's. Victory points are capped per game and per battle round.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Mission",
/// "description": "An 11e primary mission (the objective a player scores). Which mission a player plays is selected by the Force Disposition matchup matrix (see mission-matchup), keyed on the player's own disposition and their opponent's. Victory points are capped per game and per battle round.",
/// "type": "object",
/// "required": [
/// "game_version",
/// "id",
/// "name"
/// ],
/// "properties": {
/// "deployment_pattern_ids": {
/// "description": "Ids of the deployment-pattern entities (maps) this mission can be played on. Empty until the per-mission maps are confirmed.",
/// "type": "array",
/// "items": {
/// "$ref": "#/$defs/entity-id"
/// },
/// "uniqueItems": true
/// },
/// "description": {
/// "description": "Community-authored mission/objective summary (original prose only — no reproduced rules text).",
/// "type": "string"
/// },
/// "game_version": {
/// "$ref": "#/$defs/game-version-ref"
/// },
/// "id": {
/// "$ref": "#/$defs/entity-id"
/// },
/// "name": {
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
/// },
/// "source": {
/// "description": "Mission pack or source the mission originates from.",
/// "type": "string",
/// "maxLength": 64,
/// "minLength": 1
/// },
/// "vp_per_game_cap": {
/// "description": "Maximum primary VP scorable across the whole game. 11e default is 45.",
/// "default": 45,
/// "type": "integer",
/// "minimum": 0.0
/// },
/// "vp_per_round_cap": {
/// "description": "Maximum primary VP scorable in a single battle round. 11e default is 15.",
/// "default": 15,
/// "type": "integer",
/// "minimum": 0.0
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct Mission {
///Ids of the deployment-pattern entities (maps) this mission can be played on. Empty until the per-mission maps are confirmed.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub deployment_pattern_ids: ::std::option::Option<Vec<EntityId>>,
///Community-authored mission/objective summary (original prose only — no reproduced rules text).
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub description: ::std::option::Option<::std::string::String>,
pub game_version: GameVersionRef,
pub id: EntityId,
pub name: MissionName,
///Mission pack or source the mission originates from.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub source: ::std::option::Option<MissionSource>,
///Maximum primary VP scorable across the whole game. 11e default is 45.
#[serde(default = "defaults::default_u64::<u64, 45>")]
pub vp_per_game_cap: u64,
///Maximum primary VP scorable in a single battle round. 11e default is 15.
#[serde(default = "defaults::default_u64::<u64, 15>")]
pub vp_per_round_cap: u64,
}
///One cell of the 11e Force Disposition matrix: given the player's own Force Disposition and their opponent's, the mission that player plays. Mirrors a single row on a physical Force Disposition card. The (disposition, opponent_disposition) pair is the conceptual key; compound uniqueness across entries is a data convention, not enforced by this schema.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Mission Matchup",
/// "description": "One cell of the 11e Force Disposition matrix: given the player's own Force Disposition and their opponent's, the mission that player plays. Mirrors a single row on a physical Force Disposition card. The (disposition, opponent_disposition) pair is the conceptual key; compound uniqueness across entries is a data convention, not enforced by this schema.",
/// "type": "object",
/// "required": [
/// "disposition",
/// "game_version",
/// "id",
/// "mission_id",
/// "opponent_disposition"
/// ],
/// "properties": {
/// "disposition": {
/// "description": "The player's own Force Disposition.",
/// "$ref": "#/$defs/force-disposition-id"
/// },
/// "game_version": {
/// "$ref": "#/$defs/game-version-ref"
/// },
/// "id": {
/// "$ref": "#/$defs/entity-id"
/// },
/// "mission_id": {
/// "description": "Id of the mission entity the player plays for this disposition pairing.",
/// "$ref": "#/$defs/entity-id"
/// },
/// "opponent_disposition": {
/// "description": "The opponent's Force Disposition.",
/// "$ref": "#/$defs/force-disposition-id"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct MissionMatchup {
///The player's own Force Disposition.
pub disposition: ForceDispositionId,
pub game_version: GameVersionRef,
pub id: EntityId,
///Id of the mission entity the player plays for this disposition pairing.
pub mission_id: EntityId,
///The opponent's Force Disposition.
pub opponent_disposition: ForceDispositionId,
}
///`MissionName`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct MissionName(::std::string::String);
impl ::std::ops::Deref for MissionName {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<MissionName> for ::std::string::String {
fn from(value: MissionName) -> Self {
value.0
}
}
impl ::std::str::FromStr for MissionName {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 128usize {
return Err("longer than 128 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for MissionName {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for MissionName {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for MissionName {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for MissionName {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///Mission pack or source the mission originates from.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Mission pack or source the mission originates from.",
/// "type": "string",
/// "maxLength": 64,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct MissionSource(::std::string::String);
impl ::std::ops::Deref for MissionSource {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<MissionSource> for ::std::string::String {
fn from(value: MissionSource) -> Self {
value.0
}
}
impl ::std::str::FromStr for MissionSource {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 64usize {
return Err("longer than 64 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for MissionSource {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for MissionSource {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for MissionSource {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for MissionSource {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///The five official game phases. Unchanged between 10th and 11th edition — 11e reorders Pile In timing within the Fight phase but adds no top-level phase.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "The five official game phases. Unchanged between 10th and 11th edition — 11e reorders Pile In timing within the Fight phase but adds no top-level phase.",
/// "type": "string",
/// "enum": [
/// "command",
/// "movement",
/// "shooting",
/// "charge",
/// "fight"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum Phase {
#[serde(rename = "command")]
Command,
#[serde(rename = "movement")]
Movement,
#[serde(rename = "shooting")]
Shooting,
#[serde(rename = "charge")]
Charge,
#[serde(rename = "fight")]
Fight,
}
impl ::std::fmt::Display for Phase {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Command => f.write_str("command"),
Self::Movement => f.write_str("movement"),
Self::Shooting => f.write_str("shooting"),
Self::Charge => f.write_str("charge"),
Self::Fight => f.write_str("fight"),
}
}
}
impl ::std::str::FromStr for Phase {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"command" => Ok(Self::Command),
"movement" => Ok(Self::Movement),
"shooting" => Ok(Self::Shooting),
"charge" => Ok(Self::Charge),
"fight" => Ok(Self::Fight),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for Phase {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for Phase {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for Phase {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///`PhaseList`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "array",
/// "items": {
/// "$ref": "#/$defs/phase"
/// },
/// "minItems": 1,
/// "uniqueItems": true
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(transparent)]
pub struct PhaseList(pub Vec<Phase>);
impl ::std::ops::Deref for PhaseList {
type Target = Vec<Phase>;
fn deref(&self) -> &Vec<Phase> {
&self.0
}
}
impl ::std::convert::From<PhaseList> for Vec<Phase> {
fn from(value: PhaseList) -> Self {
value.0
}
}
impl ::std::convert::From<Vec<Phase>> for PhaseList {
fn from(value: Vec<Phase>) -> Self {
Self(value)
}
}
///`PhaseMapping`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Phase Mapping",
/// "type": "object",
/// "required": [
/// "game_version",
/// "phases",
/// "source_id",
/// "source_type"
/// ],
/// "properties": {
/// "authored_by": {
/// "$ref": "#/$defs/contributor-ref"
/// },
/// "game_version": {
/// "$ref": "#/$defs/game-version-ref"
/// },
/// "phases": {
/// "$ref": "#/$defs/phase-list"
/// },
/// "source_id": {
/// "$ref": "#/$defs/entity-id"
/// },
/// "source_type": {
/// "$ref": "#/$defs/source-type"
/// }
/// }
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
pub struct PhaseMapping {
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub authored_by: ::std::option::Option<ContributorRef>,
pub game_version: GameVersionRef,
pub phases: PhaseList,
pub source_id: EntityId,
pub source_type: SourceType,
}
///One terrain piece placed on the board. Geometry comes from a catalog `template` or an inline `footprint` (if both are present, `footprint` is authoritative and `template` is provenance).
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "One terrain piece placed on the board. Geometry comes from a catalog `template` or an inline `footprint` (if both are present, `footprint` is authoritative and `template` is provenance).",
/// "type": "object",
/// "required": [
/// "position"
/// ],
/// "properties": {
/// "floor": {
/// "description": "Ruin floor this piece occupies (0 = ground level).",
/// "default": 0,
/// "type": "integer",
/// "minimum": 0.0
/// },
/// "footprint": {
/// "description": "Inline geometry, standing in for or overriding a template footprint. Authoritative when present.",
/// "$ref": "#/$defs/footprint"
/// },
/// "height_inches": {
/// "description": "Height of the piece in inches; overrides the template default. Gates Plunging Fire (a piece 3\" or taller confers +1 BS on ground-level targets).",
/// "type": "number",
/// "minimum": 0.0
/// },
/// "id": {
/// "description": "Layout-local id. Required for any piece referenced by another piece's `parent_area_id`.",
/// "$ref": "#/$defs/entity-id"
/// },
/// "is_objective": {
/// "description": "Whether this piece carries an objective marker.",
/// "default": false,
/// "type": "boolean"
/// },
/// "keystones": {
/// "description": "Measurement keystones: the author-selected dimension lines a reference card prints so a player can place this piece with a tape measure (board edge → a feature of the placed piece). Only the selection is stored — the distance is always DERIVED from the resolved geometry by the shared keystone resolver (pinned by the conformance corpus), so a keystone can never disagree with the layout. Vertex indices follow the resolver's pinned vertex order; re-authoring a template's footprint invalidates them, so review keystones when geometry changes.",
/// "type": "array",
/// "items": {
/// "type": "object",
/// "required": [
/// "edge",
/// "ref"
/// ],
/// "properties": {
/// "edge": {
/// "description": "The board edge the measurement runs from, in the y-down board frame (left/right pin x against board width; top/bottom pin y against board height).",
/// "type": "string",
/// "enum": [
/// "left",
/// "right",
/// "top",
/// "bottom"
/// ]
/// },
/// "ref": {
/// "description": "Which feature of the placed piece the measurement reaches: a footprint vertex (by resolver vertex order) or an axis-aligned bounding face of the placed footprint.",
/// "oneOf": [
/// {
/// "type": "object",
/// "required": [
/// "index",
/// "kind"
/// ],
/// "properties": {
/// "index": {
/// "type": "integer",
/// "minimum": 0.0
/// },
/// "kind": {
/// "const": "vertex"
/// }
/// },
/// "additionalProperties": false
/// },
/// {
/// "type": "object",
/// "required": [
/// "kind",
/// "side"
/// ],
/// "properties": {
/// "kind": {
/// "const": "face"
/// },
/// "side": {
/// "type": "string",
/// "enum": [
/// "min-x",
/// "max-x",
/// "min-y",
/// "max-y"
/// ]
/// }
/// },
/// "additionalProperties": false
/// }
/// ]
/// }
/// },
/// "additionalProperties": false
/// }
/// },
/// "link_group": {
/// "description": "Pieces sharing a `link_group` value are linked terrain — treated as a single terrain feature (and, where an objective sits among them, a single objective).",
/// "type": "string",
/// "maxLength": 64,
/// "minLength": 1
/// },
/// "mirror": {
/// "description": "Reflection applied in the centroid-local frame before rotation: `horizontal` negates local x (left-right flip), `vertical` negates local y.",
/// "default": "none",
/// "type": "string",
/// "enum": [
/// "none",
/// "horizontal",
/// "vertical"
/// ]
/// },
/// "name": {
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
/// },
/// "objective": {
/// "description": "Objective-marker metadata. Only meaningful when `is_objective` is true.",
/// "type": "object",
/// "properties": {
/// "control_range_inches": {
/// "description": "Range from the marker within which models contribute to control.",
/// "type": "number",
/// "exclusiveMinimum": 0.0
/// },
/// "position": {
/// "description": "Board-inch position of the marker. Absent means the piece's `position`.",
/// "$ref": "#/$defs/vec2"
/// }
/// },
/// "additionalProperties": false
/// },
/// "objective_role": {
/// "description": "Designates this terrain area — or, when `link_group`'d, the union of linked areas (one objective for the set) — as carrying an objective of the given 11e role: `home` (inside a deployment zone), `center` (board middle), or `expansion` (no-man's-land). Implies `is_objective`.",
/// "type": "string",
/// "enum": [
/// "home",
/// "expansion",
/// "center"
/// ]
/// },
/// "parent_area_id": {
/// "description": "For a feature: the layout-local id of the area it sits on. The feature's `position`/`rotation_degrees`/`mirror` are composed with the parent area's placement, so moving, rotating, or mirroring the area carries the feature with it.",
/// "$ref": "#/$defs/entity-id"
/// },
/// "piece_type": {
/// "description": "An `area` is a gameplay terrain zone (the 11e 'terrain area'); a `feature` is physical scenery (walls, containers, pipes) placed on an area.",
/// "default": "area",
/// "type": "string",
/// "enum": [
/// "area",
/// "feature"
/// ]
/// },
/// "position": {
/// "description": "Placement of the piece's CENTROID (the polygon area centroid of its footprint). Rotation- and mirror-invariant: changing `rotation_degrees` or `mirror` never moves this point. In board inches, unless the piece is a feature with `parent_area_id`, in which case it is in the parent area's centroid-local frame.",
/// "$ref": "#/$defs/vec2"
/// },
/// "rotation_degrees": {
/// "description": "Clockwise rotation about the centroid in the y-down board frame. Absent or 0 means the template's natural orientation.",
/// "type": "number",
/// "exclusiveMaximum": 360.0,
/// "minimum": 0.0
/// },
/// "template": {
/// "description": "Id of the terrain-template this piece instances. Footprint and defaults (height, blocking, keywords) are taken from that template unless overridden here.",
/// "$ref": "#/$defs/entity-id"
/// },
/// "terrain_area_keywords": {
/// "description": "Terrain-area keywords this piece's area carries; overrides the template default.",
/// "type": "array",
/// "items": {
/// "$ref": "#/$defs/terrain-area-keyword"
/// },
/// "uniqueItems": true
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct Piece {
///Ruin floor this piece occupies (0 = ground level).
#[serde(default)]
pub floor: u64,
///Inline geometry, standing in for or overriding a template footprint. Authoritative when present.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub footprint: ::std::option::Option<Footprint>,
///Height of the piece in inches; overrides the template default. Gates Plunging Fire (a piece 3" or taller confers +1 BS on ground-level targets).
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub height_inches: ::std::option::Option<f64>,
///Layout-local id. Required for any piece referenced by another piece's `parent_area_id`.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub id: ::std::option::Option<EntityId>,
///Whether this piece carries an objective marker.
#[serde(default)]
pub is_objective: bool,
///Measurement keystones: the author-selected dimension lines a reference card prints so a player can place this piece with a tape measure (board edge → a feature of the placed piece). Only the selection is stored — the distance is always DERIVED from the resolved geometry by the shared keystone resolver (pinned by the conformance corpus), so a keystone can never disagree with the layout. Vertex indices follow the resolver's pinned vertex order; re-authoring a template's footprint invalidates them, so review keystones when geometry changes.
#[serde(default, skip_serializing_if = "::std::vec::Vec::is_empty")]
pub keystones: ::std::vec::Vec<PieceKeystonesItem>,
///Pieces sharing a `link_group` value are linked terrain — treated as a single terrain feature (and, where an objective sits among them, a single objective).
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub link_group: ::std::option::Option<PieceLinkGroup>,
///Reflection applied in the centroid-local frame before rotation: `horizontal` negates local x (left-right flip), `vertical` negates local y.
#[serde(default = "defaults::piece_mirror")]
pub mirror: PieceMirror,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub name: ::std::option::Option<PieceName>,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub objective: ::std::option::Option<PieceObjective>,
///Designates this terrain area — or, when `link_group`'d, the union of linked areas (one objective for the set) — as carrying an objective of the given 11e role: `home` (inside a deployment zone), `center` (board middle), or `expansion` (no-man's-land). Implies `is_objective`.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub objective_role: ::std::option::Option<PieceObjectiveRole>,
///For a feature: the layout-local id of the area it sits on. The feature's `position`/`rotation_degrees`/`mirror` are composed with the parent area's placement, so moving, rotating, or mirroring the area carries the feature with it.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub parent_area_id: ::std::option::Option<EntityId>,
///An `area` is a gameplay terrain zone (the 11e 'terrain area'); a `feature` is physical scenery (walls, containers, pipes) placed on an area.
#[serde(default = "defaults::piece_piece_type")]
pub piece_type: PiecePieceType,
///Placement of the piece's CENTROID (the polygon area centroid of its footprint). Rotation- and mirror-invariant: changing `rotation_degrees` or `mirror` never moves this point. In board inches, unless the piece is a feature with `parent_area_id`, in which case it is in the parent area's centroid-local frame.
pub position: Vec2,
///Clockwise rotation about the centroid in the y-down board frame. Absent or 0 means the template's natural orientation.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub rotation_degrees: ::std::option::Option<f64>,
///Id of the terrain-template this piece instances. Footprint and defaults (height, blocking, keywords) are taken from that template unless overridden here.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub template: ::std::option::Option<EntityId>,
///Terrain-area keywords this piece's area carries; overrides the template default.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub terrain_area_keywords: ::std::option::Option<Vec<TerrainAreaKeyword>>,
}
///`PieceKeystonesItem`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "object",
/// "required": [
/// "edge",
/// "ref"
/// ],
/// "properties": {
/// "edge": {
/// "description": "The board edge the measurement runs from, in the y-down board frame (left/right pin x against board width; top/bottom pin y against board height).",
/// "type": "string",
/// "enum": [
/// "left",
/// "right",
/// "top",
/// "bottom"
/// ]
/// },
/// "ref": {
/// "description": "Which feature of the placed piece the measurement reaches: a footprint vertex (by resolver vertex order) or an axis-aligned bounding face of the placed footprint.",
/// "oneOf": [
/// {
/// "type": "object",
/// "required": [
/// "index",
/// "kind"
/// ],
/// "properties": {
/// "index": {
/// "type": "integer",
/// "minimum": 0.0
/// },
/// "kind": {
/// "const": "vertex"
/// }
/// },
/// "additionalProperties": false
/// },
/// {
/// "type": "object",
/// "required": [
/// "kind",
/// "side"
/// ],
/// "properties": {
/// "kind": {
/// "const": "face"
/// },
/// "side": {
/// "type": "string",
/// "enum": [
/// "min-x",
/// "max-x",
/// "min-y",
/// "max-y"
/// ]
/// }
/// },
/// "additionalProperties": false
/// }
/// ]
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct PieceKeystonesItem {
///The board edge the measurement runs from, in the y-down board frame (left/right pin x against board width; top/bottom pin y against board height).
pub edge: PieceKeystonesItemEdge,
///Which feature of the placed piece the measurement reaches: a footprint vertex (by resolver vertex order) or an axis-aligned bounding face of the placed footprint.
#[serde(rename = "ref")]
pub ref_: PieceKeystonesItemRef,
}
///The board edge the measurement runs from, in the y-down board frame (left/right pin x against board width; top/bottom pin y against board height).
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "The board edge the measurement runs from, in the y-down board frame (left/right pin x against board width; top/bottom pin y against board height).",
/// "type": "string",
/// "enum": [
/// "left",
/// "right",
/// "top",
/// "bottom"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum PieceKeystonesItemEdge {
#[serde(rename = "left")]
Left,
#[serde(rename = "right")]
Right,
#[serde(rename = "top")]
Top,
#[serde(rename = "bottom")]
Bottom,
}
impl ::std::fmt::Display for PieceKeystonesItemEdge {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Left => f.write_str("left"),
Self::Right => f.write_str("right"),
Self::Top => f.write_str("top"),
Self::Bottom => f.write_str("bottom"),
}
}
}
impl ::std::str::FromStr for PieceKeystonesItemEdge {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"left" => Ok(Self::Left),
"right" => Ok(Self::Right),
"top" => Ok(Self::Top),
"bottom" => Ok(Self::Bottom),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for PieceKeystonesItemEdge {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for PieceKeystonesItemEdge {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for PieceKeystonesItemEdge {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///Which feature of the placed piece the measurement reaches: a footprint vertex (by resolver vertex order) or an axis-aligned bounding face of the placed footprint.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Which feature of the placed piece the measurement reaches: a footprint vertex (by resolver vertex order) or an axis-aligned bounding face of the placed footprint.",
/// "oneOf": [
/// {
/// "type": "object",
/// "required": [
/// "index",
/// "kind"
/// ],
/// "properties": {
/// "index": {
/// "type": "integer",
/// "minimum": 0.0
/// },
/// "kind": {
/// "const": "vertex"
/// }
/// },
/// "additionalProperties": false
/// },
/// {
/// "type": "object",
/// "required": [
/// "kind",
/// "side"
/// ],
/// "properties": {
/// "kind": {
/// "const": "face"
/// },
/// "side": {
/// "type": "string",
/// "enum": [
/// "min-x",
/// "max-x",
/// "min-y",
/// "max-y"
/// ]
/// }
/// },
/// "additionalProperties": false
/// }
/// ]
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(tag = "kind", deny_unknown_fields)]
pub enum PieceKeystonesItemRef {
#[serde(rename = "vertex")]
Vertex { index: u64 },
#[serde(rename = "face")]
Face { side: PieceKeystonesItemRefSide },
}
///`PieceKeystonesItemRefSide`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "enum": [
/// "min-x",
/// "max-x",
/// "min-y",
/// "max-y"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum PieceKeystonesItemRefSide {
#[serde(rename = "min-x")]
MinX,
#[serde(rename = "max-x")]
MaxX,
#[serde(rename = "min-y")]
MinY,
#[serde(rename = "max-y")]
MaxY,
}
impl ::std::fmt::Display for PieceKeystonesItemRefSide {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::MinX => f.write_str("min-x"),
Self::MaxX => f.write_str("max-x"),
Self::MinY => f.write_str("min-y"),
Self::MaxY => f.write_str("max-y"),
}
}
}
impl ::std::str::FromStr for PieceKeystonesItemRefSide {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"min-x" => Ok(Self::MinX),
"max-x" => Ok(Self::MaxX),
"min-y" => Ok(Self::MinY),
"max-y" => Ok(Self::MaxY),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for PieceKeystonesItemRefSide {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for PieceKeystonesItemRefSide {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for PieceKeystonesItemRefSide {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///Pieces sharing a `link_group` value are linked terrain — treated as a single terrain feature (and, where an objective sits among them, a single objective).
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Pieces sharing a `link_group` value are linked terrain — treated as a single terrain feature (and, where an objective sits among them, a single objective).",
/// "type": "string",
/// "maxLength": 64,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct PieceLinkGroup(::std::string::String);
impl ::std::ops::Deref for PieceLinkGroup {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<PieceLinkGroup> for ::std::string::String {
fn from(value: PieceLinkGroup) -> Self {
value.0
}
}
impl ::std::str::FromStr for PieceLinkGroup {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 64usize {
return Err("longer than 64 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for PieceLinkGroup {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for PieceLinkGroup {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for PieceLinkGroup {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for PieceLinkGroup {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///Reflection applied in the centroid-local frame before rotation: `horizontal` negates local x (left-right flip), `vertical` negates local y.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Reflection applied in the centroid-local frame before rotation: `horizontal` negates local x (left-right flip), `vertical` negates local y.",
/// "default": "none",
/// "type": "string",
/// "enum": [
/// "none",
/// "horizontal",
/// "vertical"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum PieceMirror {
#[serde(rename = "none")]
None,
#[serde(rename = "horizontal")]
Horizontal,
#[serde(rename = "vertical")]
Vertical,
}
impl ::std::fmt::Display for PieceMirror {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::None => f.write_str("none"),
Self::Horizontal => f.write_str("horizontal"),
Self::Vertical => f.write_str("vertical"),
}
}
}
impl ::std::str::FromStr for PieceMirror {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"none" => Ok(Self::None),
"horizontal" => Ok(Self::Horizontal),
"vertical" => Ok(Self::Vertical),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for PieceMirror {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for PieceMirror {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for PieceMirror {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::default::Default for PieceMirror {
fn default() -> Self {
PieceMirror::None
}
}
///`PieceName`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct PieceName(::std::string::String);
impl ::std::ops::Deref for PieceName {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<PieceName> for ::std::string::String {
fn from(value: PieceName) -> Self {
value.0
}
}
impl ::std::str::FromStr for PieceName {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 128usize {
return Err("longer than 128 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for PieceName {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for PieceName {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for PieceName {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for PieceName {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///Objective-marker metadata. Only meaningful when `is_objective` is true.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Objective-marker metadata. Only meaningful when `is_objective` is true.",
/// "type": "object",
/// "properties": {
/// "control_range_inches": {
/// "description": "Range from the marker within which models contribute to control.",
/// "type": "number",
/// "exclusiveMinimum": 0.0
/// },
/// "position": {
/// "description": "Board-inch position of the marker. Absent means the piece's `position`.",
/// "$ref": "#/$defs/vec2"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct PieceObjective {
///Range from the marker within which models contribute to control.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub control_range_inches: ::std::option::Option<f64>,
///Board-inch position of the marker. Absent means the piece's `position`.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub position: ::std::option::Option<Vec2>,
}
impl ::std::default::Default for PieceObjective {
fn default() -> Self {
Self {
control_range_inches: Default::default(),
position: Default::default(),
}
}
}
///Designates this terrain area — or, when `link_group`'d, the union of linked areas (one objective for the set) — as carrying an objective of the given 11e role: `home` (inside a deployment zone), `center` (board middle), or `expansion` (no-man's-land). Implies `is_objective`.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Designates this terrain area — or, when `link_group`'d, the union of linked areas (one objective for the set) — as carrying an objective of the given 11e role: `home` (inside a deployment zone), `center` (board middle), or `expansion` (no-man's-land). Implies `is_objective`.",
/// "type": "string",
/// "enum": [
/// "home",
/// "expansion",
/// "center"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum PieceObjectiveRole {
#[serde(rename = "home")]
Home,
#[serde(rename = "expansion")]
Expansion,
#[serde(rename = "center")]
Center,
}
impl ::std::fmt::Display for PieceObjectiveRole {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Home => f.write_str("home"),
Self::Expansion => f.write_str("expansion"),
Self::Center => f.write_str("center"),
}
}
}
impl ::std::str::FromStr for PieceObjectiveRole {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"home" => Ok(Self::Home),
"expansion" => Ok(Self::Expansion),
"center" => Ok(Self::Center),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for PieceObjectiveRole {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for PieceObjectiveRole {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for PieceObjectiveRole {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///An `area` is a gameplay terrain zone (the 11e 'terrain area'); a `feature` is physical scenery (walls, containers, pipes) placed on an area.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "An `area` is a gameplay terrain zone (the 11e 'terrain area'); a `feature` is physical scenery (walls, containers, pipes) placed on an area.",
/// "default": "area",
/// "type": "string",
/// "enum": [
/// "area",
/// "feature"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum PiecePieceType {
#[serde(rename = "area")]
Area,
#[serde(rename = "feature")]
Feature,
}
impl ::std::fmt::Display for PiecePieceType {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Area => f.write_str("area"),
Self::Feature => f.write_str("feature"),
}
}
}
impl ::std::str::FromStr for PiecePieceType {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"area" => Ok(Self::Area),
"feature" => Ok(Self::Feature),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for PiecePieceType {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for PiecePieceType {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for PiecePieceType {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::default::Default for PiecePieceType {
fn default() -> Self {
PiecePieceType::Area
}
}
///Which player's turn this applies during
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Which player's turn this applies during",
/// "type": "string",
/// "enum": [
/// "your-turn",
/// "opponent-turn",
/// "either"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum PlayerTurn {
#[serde(rename = "your-turn")]
YourTurn,
#[serde(rename = "opponent-turn")]
OpponentTurn,
#[serde(rename = "either")]
Either,
}
impl ::std::fmt::Display for PlayerTurn {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::YourTurn => f.write_str("your-turn"),
Self::OpponentTurn => f.write_str("opponent-turn"),
Self::Either => f.write_str("either"),
}
}
}
impl ::std::str::FromStr for PlayerTurn {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"your-turn" => Ok(Self::YourTurn),
"opponent-turn" => Ok(Self::OpponentTurn),
"either" => Ok(Self::Either),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for PlayerTurn {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for PlayerTurn {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for PlayerTurn {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///A faction's resource system (Miracle Dice, Pain tokens, Blessings dice pool, etc.).
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Resource Pool",
/// "description": "A faction's resource system (Miracle Dice, Pain tokens, Blessings dice pool, etc.).",
/// "type": "object",
/// "required": [
/// "faction_id",
/// "game_version",
/// "id",
/// "name",
/// "pool_type"
/// ],
/// "properties": {
/// "faction_id": {
/// "$ref": "#/$defs/entity-id"
/// },
/// "game_version": {
/// "$ref": "#/$defs/game-version-ref"
/// },
/// "generation": {
/// "type": "array",
/// "items": {
/// "type": "object",
/// "required": [
/// "amount",
/// "condition"
/// ],
/// "properties": {
/// "amount": {
/// "$ref": "#/$defs/stat-value"
/// },
/// "condition": {
/// "$ref": "#/$defs/condition"
/// }
/// }
/// }
/// },
/// "id": {
/// "$ref": "#/$defs/entity-id"
/// },
/// "max_size": {
/// "oneOf": [
/// {
/// "type": "integer",
/// "minimum": 1.0
/// },
/// {
/// "type": "null"
/// }
/// ]
/// },
/// "name": {
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
/// },
/// "pool_type": {
/// "type": "string",
/// "enum": [
/// "token",
/// "dice-pool",
/// "counter"
/// ]
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct ResourcePool {
pub faction_id: EntityId,
pub game_version: GameVersionRef,
#[serde(default, skip_serializing_if = "::std::vec::Vec::is_empty")]
pub generation: ::std::vec::Vec<ResourcePoolGenerationItem>,
pub id: EntityId,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub max_size: ::std::option::Option<::std::num::NonZeroU64>,
pub name: ResourcePoolName,
pub pool_type: ResourcePoolPoolType,
}
///`ResourcePoolGenerationItem`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "object",
/// "required": [
/// "amount",
/// "condition"
/// ],
/// "properties": {
/// "amount": {
/// "$ref": "#/$defs/stat-value"
/// },
/// "condition": {
/// "$ref": "#/$defs/condition"
/// }
/// }
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
pub struct ResourcePoolGenerationItem {
pub amount: StatValue,
pub condition: Condition,
}
///`ResourcePoolName`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct ResourcePoolName(::std::string::String);
impl ::std::ops::Deref for ResourcePoolName {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<ResourcePoolName> for ::std::string::String {
fn from(value: ResourcePoolName) -> Self {
value.0
}
}
impl ::std::str::FromStr for ResourcePoolName {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 128usize {
return Err("longer than 128 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for ResourcePoolName {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for ResourcePoolName {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for ResourcePoolName {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for ResourcePoolName {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///`ResourcePoolPoolType`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "enum": [
/// "token",
/// "dice-pool",
/// "counter"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum ResourcePoolPoolType {
#[serde(rename = "token")]
Token,
#[serde(rename = "dice-pool")]
DicePool,
#[serde(rename = "counter")]
Counter,
}
impl ::std::fmt::Display for ResourcePoolPoolType {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Token => f.write_str("token"),
Self::DicePool => f.write_str("dice-pool"),
Self::Counter => f.write_str("counter"),
}
}
}
impl ::std::str::FromStr for ResourcePoolPoolType {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"token" => Ok(Self::Token),
"dice-pool" => Ok(Self::DicePool),
"counter" => Ok(Self::Counter),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for ResourcePoolPoolType {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for ResourcePoolPoolType {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for ResourcePoolPoolType {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///`Scope`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Ability Scope",
/// "type": "object",
/// "required": [
/// "duration",
/// "range"
/// ],
/// "properties": {
/// "duration": {
/// "type": "string",
/// "enum": [
/// "phase",
/// "turn",
/// "battle-round",
/// "battle",
/// "until-next-command-phase",
/// "one-use",
/// "permanent"
/// ]
/// },
/// "range": {
/// "type": "string",
/// "enum": [
/// "self",
/// "unit",
/// "attached",
/// "aura-6",
/// "aura-9",
/// "aura-12",
/// "aura-custom",
/// "engagement-range",
/// "any-visible",
/// "any-on-battlefield",
/// "terrain-within-range"
/// ]
/// },
/// "range_inches": {
/// "type": "number"
/// }
/// }
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
pub struct Scope {
pub duration: ScopeDuration,
pub range: ScopeRange,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub range_inches: ::std::option::Option<f64>,
}
///`ScopeDuration`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "enum": [
/// "phase",
/// "turn",
/// "battle-round",
/// "battle",
/// "until-next-command-phase",
/// "one-use",
/// "permanent"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum ScopeDuration {
#[serde(rename = "phase")]
Phase,
#[serde(rename = "turn")]
Turn,
#[serde(rename = "battle-round")]
BattleRound,
#[serde(rename = "battle")]
Battle,
#[serde(rename = "until-next-command-phase")]
UntilNextCommandPhase,
#[serde(rename = "one-use")]
OneUse,
#[serde(rename = "permanent")]
Permanent,
}
impl ::std::fmt::Display for ScopeDuration {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Phase => f.write_str("phase"),
Self::Turn => f.write_str("turn"),
Self::BattleRound => f.write_str("battle-round"),
Self::Battle => f.write_str("battle"),
Self::UntilNextCommandPhase => f.write_str("until-next-command-phase"),
Self::OneUse => f.write_str("one-use"),
Self::Permanent => f.write_str("permanent"),
}
}
}
impl ::std::str::FromStr for ScopeDuration {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"phase" => Ok(Self::Phase),
"turn" => Ok(Self::Turn),
"battle-round" => Ok(Self::BattleRound),
"battle" => Ok(Self::Battle),
"until-next-command-phase" => Ok(Self::UntilNextCommandPhase),
"one-use" => Ok(Self::OneUse),
"permanent" => Ok(Self::Permanent),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for ScopeDuration {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for ScopeDuration {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for ScopeDuration {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///`ScopeRange`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "enum": [
/// "self",
/// "unit",
/// "attached",
/// "aura-6",
/// "aura-9",
/// "aura-12",
/// "aura-custom",
/// "engagement-range",
/// "any-visible",
/// "any-on-battlefield",
/// "terrain-within-range"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum ScopeRange {
#[serde(rename = "self")]
Self_,
#[serde(rename = "unit")]
Unit,
#[serde(rename = "attached")]
Attached,
#[serde(rename = "aura-6")]
Aura6,
#[serde(rename = "aura-9")]
Aura9,
#[serde(rename = "aura-12")]
Aura12,
#[serde(rename = "aura-custom")]
AuraCustom,
#[serde(rename = "engagement-range")]
EngagementRange,
#[serde(rename = "any-visible")]
AnyVisible,
#[serde(rename = "any-on-battlefield")]
AnyOnBattlefield,
#[serde(rename = "terrain-within-range")]
TerrainWithinRange,
}
impl ::std::fmt::Display for ScopeRange {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Self_ => f.write_str("self"),
Self::Unit => f.write_str("unit"),
Self::Attached => f.write_str("attached"),
Self::Aura6 => f.write_str("aura-6"),
Self::Aura9 => f.write_str("aura-9"),
Self::Aura12 => f.write_str("aura-12"),
Self::AuraCustom => f.write_str("aura-custom"),
Self::EngagementRange => f.write_str("engagement-range"),
Self::AnyVisible => f.write_str("any-visible"),
Self::AnyOnBattlefield => f.write_str("any-on-battlefield"),
Self::TerrainWithinRange => f.write_str("terrain-within-range"),
}
}
}
impl ::std::str::FromStr for ScopeRange {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"self" => Ok(Self::Self_),
"unit" => Ok(Self::Unit),
"attached" => Ok(Self::Attached),
"aura-6" => Ok(Self::Aura6),
"aura-9" => Ok(Self::Aura9),
"aura-12" => Ok(Self::Aura12),
"aura-custom" => Ok(Self::AuraCustom),
"engagement-range" => Ok(Self::EngagementRange),
"any-visible" => Ok(Self::AnyVisible),
"any-on-battlefield" => Ok(Self::AnyOnBattlefield),
"terrain-within-range" => Ok(Self::TerrainWithinRange),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for ScopeRange {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for ScopeRange {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for ScopeRange {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///When a VP award is evaluated. A bare `phase` is the legacy shorthand for 'during this phase'; richer triggers add `timing` (the moment within a phase/turn/game), `player_turn`, and a `battle_round` window. A card's section headers map onto these: 'ANY BATTLE ROUND' omits `battle_round`; 'SECOND BATTLE ROUND ONWARDS' is { min: 2 }; 'END OF THE BATTLE' is timing: end-of-battle.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "When a VP award is evaluated. A bare `phase` is the legacy shorthand for 'during this phase'; richer triggers add `timing` (the moment within a phase/turn/game), `player_turn`, and a `battle_round` window. A card's section headers map onto these: 'ANY BATTLE ROUND' omits `battle_round`; 'SECOND BATTLE ROUND ONWARDS' is { min: 2 }; 'END OF THE BATTLE' is timing: end-of-battle.",
/// "type": "object",
/// "minProperties": 1,
/// "properties": {
/// "battle_round": {
/// "description": "Battle-round window in which the trigger is active. Absent means any battle round (1-5). 'Second battle round onwards' is { min: 2 }.",
/// "type": "object",
/// "minProperties": 1,
/// "properties": {
/// "max": {
/// "type": "integer",
/// "maximum": 5.0,
/// "minimum": 1.0
/// },
/// "min": {
/// "type": "integer",
/// "maximum": 5.0,
/// "minimum": 1.0
/// }
/// },
/// "additionalProperties": false
/// },
/// "phase": {
/// "description": "The phase the trigger is relative to. Required when `timing` is start-of-phase or end-of-phase; omitted for turn- or battle-level timings.",
/// "$ref": "#/$defs/phase"
/// },
/// "player_turn": {
/// "$ref": "#/$defs/player-turn"
/// },
/// "timing": {
/// "description": "The moment the award is checked. 'End of your turn' = end-of-turn; 'End of your Command phase' = end-of-phase with phase: command; 'End of the battle' = end-of-battle.",
/// "type": "string",
/// "enum": [
/// "start-of-turn",
/// "end-of-turn",
/// "start-of-phase",
/// "end-of-phase",
/// "end-of-battle"
/// ]
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct ScoringTrigger {
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub battle_round: ::std::option::Option<ScoringTriggerBattleRound>,
///The phase the trigger is relative to. Required when `timing` is start-of-phase or end-of-phase; omitted for turn- or battle-level timings.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub phase: ::std::option::Option<Phase>,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub player_turn: ::std::option::Option<PlayerTurn>,
///The moment the award is checked. 'End of your turn' = end-of-turn; 'End of your Command phase' = end-of-phase with phase: command; 'End of the battle' = end-of-battle.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub timing: ::std::option::Option<ScoringTriggerTiming>,
}
impl ::std::default::Default for ScoringTrigger {
fn default() -> Self {
Self {
battle_round: Default::default(),
phase: Default::default(),
player_turn: Default::default(),
timing: Default::default(),
}
}
}
///Battle-round window in which the trigger is active. Absent means any battle round (1-5). 'Second battle round onwards' is { min: 2 }.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Battle-round window in which the trigger is active. Absent means any battle round (1-5). 'Second battle round onwards' is { min: 2 }.",
/// "type": "object",
/// "minProperties": 1,
/// "properties": {
/// "max": {
/// "type": "integer",
/// "maximum": 5.0,
/// "minimum": 1.0
/// },
/// "min": {
/// "type": "integer",
/// "maximum": 5.0,
/// "minimum": 1.0
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct ScoringTriggerBattleRound {
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub max: ::std::option::Option<::std::num::NonZeroU64>,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub min: ::std::option::Option<::std::num::NonZeroU64>,
}
impl ::std::default::Default for ScoringTriggerBattleRound {
fn default() -> Self {
Self {
max: Default::default(),
min: Default::default(),
}
}
}
///The moment the award is checked. 'End of your turn' = end-of-turn; 'End of your Command phase' = end-of-phase with phase: command; 'End of the battle' = end-of-battle.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "The moment the award is checked. 'End of your turn' = end-of-turn; 'End of your Command phase' = end-of-phase with phase: command; 'End of the battle' = end-of-battle.",
/// "type": "string",
/// "enum": [
/// "start-of-turn",
/// "end-of-turn",
/// "start-of-phase",
/// "end-of-phase",
/// "end-of-battle"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum ScoringTriggerTiming {
#[serde(rename = "start-of-turn")]
StartOfTurn,
#[serde(rename = "end-of-turn")]
EndOfTurn,
#[serde(rename = "start-of-phase")]
StartOfPhase,
#[serde(rename = "end-of-phase")]
EndOfPhase,
#[serde(rename = "end-of-battle")]
EndOfBattle,
}
impl ::std::fmt::Display for ScoringTriggerTiming {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::StartOfTurn => f.write_str("start-of-turn"),
Self::EndOfTurn => f.write_str("end-of-turn"),
Self::StartOfPhase => f.write_str("start-of-phase"),
Self::EndOfPhase => f.write_str("end-of-phase"),
Self::EndOfBattle => f.write_str("end-of-battle"),
}
}
}
impl ::std::str::FromStr for ScoringTriggerTiming {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"start-of-turn" => Ok(Self::StartOfTurn),
"end-of-turn" => Ok(Self::EndOfTurn),
"start-of-phase" => Ok(Self::StartOfPhase),
"end-of-phase" => Ok(Self::EndOfPhase),
"end-of-battle" => Ok(Self::EndOfBattle),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for ScoringTriggerTiming {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for ScoringTriggerTiming {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for ScoringTriggerTiming {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///An 11e mission card. The deck-level rule (draw 2 per turn, keep unscored cards) is separate and not modelled here. This is the per-card shape: an optional on-draw deck operation, an optional player action, and zero or more VP-award blocks. Primary mission cards reuse this shape via card_type. Mechanic blocks reference the Ability DSL; prose is community-authored (no reproduced rules text).
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Secondary Card",
/// "description": "An 11e mission card. The deck-level rule (draw 2 per turn, keep unscored cards) is separate and not modelled here. This is the per-card shape: an optional on-draw deck operation, an optional player action, and zero or more VP-award blocks. Primary mission cards reuse this shape via card_type. Mechanic blocks reference the Ability DSL; prose is community-authored (no reproduced rules text).",
/// "type": "object",
/// "required": [
/// "game_version",
/// "id",
/// "name"
/// ],
/// "properties": {
/// "actions": {
/// "description": "Optional player actions the card enables. Most cards have a single action; a few (e.g. Observe Enemy, with separate Baited-removal and Spotted actions) have two distinct actions on the same card.",
/// "type": "array",
/// "items": {
/// "type": "object",
/// "properties": {
/// "action_id": {
/// "description": "Optional kebab-case identifier used to reference this action from `action-completed` conditions in `awards[].when`.",
/// "type": "string",
/// "maxLength": 64,
/// "minLength": 1
/// },
/// "battle_round": {
/// "description": "Battle-round window in which the action can be started. Absent means any battle round. 'From the second battle round onwards' (Triangulate, Extract Intelligence) is { min: 2 }.",
/// "type": "object",
/// "minProperties": 1,
/// "properties": {
/// "max": {
/// "type": "integer",
/// "maximum": 5.0,
/// "minimum": 1.0
/// },
/// "min": {
/// "type": "integer",
/// "maximum": 5.0,
/// "minimum": 1.0
/// }
/// },
/// "additionalProperties": false
/// },
/// "completes": {
/// "description": "Predicate for when the action is considered complete.",
/// "$ref": "#/$defs/condition"
/// },
/// "effect": {
/// "description": "Effect applied when the action completes (e.g. terrain-area-tag, objective-tag, or unit-tag to mark transient state).",
/// "$ref": "#/$defs/effect"
/// },
/// "player_turn": {
/// "$ref": "#/$defs/player-turn"
/// },
/// "restrictions": {
/// "description": "Predicate that BLOCKS starting the action while it holds (Sensor Sweep: a unit cannot start this action if there is only one operation marker on the battlefield).",
/// "$ref": "#/$defs/condition"
/// },
/// "starts": {
/// "description": "Phase in which the action can be started.",
/// "$ref": "#/$defs/phase"
/// },
/// "timing": {
/// "description": "Non-phase moment the action happens, for card rules that are not started in a phase (Locate and Deny's start-of-battle marker placement, Punishment's start-of-turn condemnation, Consecrate's end-of-turn objective selection). Mutually informative with `starts` — a card action uses one or the other.",
/// "type": "string",
/// "enum": [
/// "start-of-battle",
/// "start-of-turn",
/// "end-of-turn"
/// ]
/// },
/// "units": {
/// "description": "Eligibility predicate for which units may perform the action.",
/// "$ref": "#/$defs/condition"
/// },
/// "use_limit": {
/// "description": "Maximum number of times the action may be performed (per turn unless `use_limit_scope` says otherwise).",
/// "type": "integer",
/// "minimum": 1.0
/// },
/// "use_limit_scope": {
/// "description": "Whether `use_limit` is enforced per turn or once per game (e.g. Recover the Relics / Find and Deny 'Overwhelming Force' is once per game).",
/// "default": "per-turn",
/// "type": "string",
/// "enum": [
/// "per-turn",
/// "per-game"
/// ]
/// }
/// },
/// "additionalProperties": false
/// },
/// "minItems": 1
/// },
/// "awards": {
/// "description": "VP-award blocks: each scores when `trigger` fires and the optional `when` condition holds. An award scores either a flat `vp` or a count-scaled `vp_per` (VP per instance of the thing named by `per`). Awards accrue independently and sum; a card's '+ ... CUMULATIVE' rows are modelled as separate awards flagged `cumulative` for faithful round-trip. Awards sharing the same `exclusive_group` value within a card resolve as the highest-scoring single award fires (the card's literal 'OR' rows between tier breakpoints, e.g. Record-Breaking Mission's 3-Fronts vs 4-Fronts).",
/// "type": "array",
/// "items": {
/// "type": "object",
/// "oneOf": [
/// {
/// "required": [
/// "vp"
/// ]
/// },
/// {
/// "required": [
/// "per",
/// "vp_per"
/// ]
/// }
/// ],
/// "required": [
/// "trigger"
/// ],
/// "properties": {
/// "cumulative": {
/// "description": "Marks an award the card shows as an additive '+' bonus to the preceding award in the same trigger block (the card's CUMULATIVE rows). Purely descriptive — all awards accrue independently and are summed.",
/// "default": false,
/// "type": "boolean"
/// },
/// "exclusive_group": {
/// "description": "Awards sharing this kebab-case group key resolve as 'score only the highest, not the sum' (the card's literal OR between tier rows). Awards with different `exclusive_group` values, or no value, accrue independently.",
/// "type": "string",
/// "maxLength": 64,
/// "minLength": 1
/// },
/// "mode": {
/// "description": "Which scoring track this award belongs to on cards that print both. Fixed missions are chosen for the whole game and score the (usually lower) `fixed` values; Tactical missions are drawn each turn and score the `tactical` values. Omitted on cards that score the same regardless of approach. A card may carry parallel `fixed` and `tactical` awards for the same condition; a consumer scores only the awards matching the player's chosen approach.",
/// "type": "string",
/// "enum": [
/// "fixed",
/// "tactical"
/// ]
/// },
/// "per": {
/// "description": "What `vp_per` counts, as a kebab-case descriptor (e.g. 'operation-marker-within-range-of-controlled-central-objective'). Required when `vp_per` is present.",
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
/// },
/// "per_max": {
/// "description": "Optional cap on how many instances `vp_per` counts.",
/// "type": "integer",
/// "minimum": 1.0
/// },
/// "trigger": {
/// "$ref": "#/$defs/scoring-trigger"
/// },
/// "vp": {
/// "description": "Flat VP scored when the award fires.",
/// "type": "integer",
/// "minimum": 0.0
/// },
/// "vp_max": {
/// "description": "Optional cap on the total VP this award can contribute over the game — the card's 'UP TO N VP' ceiling. Distinct from `per_max`, which caps the instance count; use `vp_max` when the printed ceiling is not a multiple of `vp_per` (e.g. Burden of Trust's '2VP per guarded objective, up to 9VP').",
/// "type": "integer",
/// "minimum": 1.0
/// },
/// "vp_per": {
/// "description": "VP scored per instance of the thing named by `per` (e.g. 1 VP per operation marker within range of a controlled objective).",
/// "type": "integer",
/// "minimum": 0.0
/// },
/// "when": {
/// "$ref": "#/$defs/condition"
/// }
/// },
/// "additionalProperties": false
/// },
/// "minItems": 1
/// },
/// "card_type": {
/// "description": "Whether this is a secondary card or a primary mission card (which reuses this shape).",
/// "default": "secondary",
/// "type": "string",
/// "enum": [
/// "secondary",
/// "primary"
/// ]
/// },
/// "game_version": {
/// "$ref": "#/$defs/game-version-ref"
/// },
/// "id": {
/// "$ref": "#/$defs/entity-id"
/// },
/// "name": {
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
/// },
/// "subtype": {
/// "description": "Finer classification within the deck (e.g. a category or tactical/fixed split). Free-form — not enum-locked until 11e categories are confirmed.",
/// "type": "string",
/// "maxLength": 64,
/// "minLength": 1
/// },
/// "text": {
/// "description": "Community-authored card description (original prose only — no reproduced rules text).",
/// "type": "string"
/// },
/// "when_drawn": {
/// "description": "Optional deck operation performed when this card is drawn (e.g. redraw, swap). Distinct from combat effects — deck operations have no combat target, so they are not modelled via the Ability DSL effect language. If `condition` is present, the operation fires only when the predicate holds.",
/// "type": "object",
/// "required": [
/// "operation"
/// ],
/// "properties": {
/// "battle_round": {
/// "description": "Battle-round window in which the draw operation is eligible (e.g. { max: 1 } means 'only when drawn in the first battle round'). Absent means the operation fires regardless of round.",
/// "type": "object",
/// "minProperties": 1,
/// "properties": {
/// "max": {
/// "type": "integer",
/// "maximum": 5.0,
/// "minimum": 1.0
/// },
/// "min": {
/// "type": "integer",
/// "maximum": 5.0,
/// "minimum": 1.0
/// }
/// },
/// "additionalProperties": false
/// },
/// "card_ids": {
/// "description": "Other cards this operation references, by id.",
/// "type": "array",
/// "items": {
/// "$ref": "#/$defs/entity-id"
/// },
/// "uniqueItems": true
/// },
/// "condition": {
/// "description": "Draw-time army-composition predicate gating the operation (e.g. redraw when the opponent lacks a qualifying unit).",
/// "$ref": "#/$defs/army-composition-predicate"
/// },
/// "operation": {
/// "description": "The deck manipulation this card triggers on draw.",
/// "type": "string",
/// "enum": [
/// "reshuffle",
/// "replace",
/// "redraw",
/// "draw-extra",
/// "swap"
/// ]
/// }
/// },
/// "additionalProperties": false
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct SecondaryCard {
///Optional player actions the card enables. Most cards have a single action; a few (e.g. Observe Enemy, with separate Baited-removal and Spotted actions) have two distinct actions on the same card.
#[serde(default, skip_serializing_if = "::std::vec::Vec::is_empty")]
pub actions: ::std::vec::Vec<SecondaryCardActionsItem>,
///VP-award blocks: each scores when `trigger` fires and the optional `when` condition holds. An award scores either a flat `vp` or a count-scaled `vp_per` (VP per instance of the thing named by `per`). Awards accrue independently and sum; a card's '+ ... CUMULATIVE' rows are modelled as separate awards flagged `cumulative` for faithful round-trip. Awards sharing the same `exclusive_group` value within a card resolve as the highest-scoring single award fires (the card's literal 'OR' rows between tier breakpoints, e.g. Record-Breaking Mission's 3-Fronts vs 4-Fronts).
#[serde(default, skip_serializing_if = "::std::vec::Vec::is_empty")]
pub awards: ::std::vec::Vec<SecondaryCardAwardsItem>,
///Whether this is a secondary card or a primary mission card (which reuses this shape).
#[serde(default = "defaults::secondary_card_card_type")]
pub card_type: SecondaryCardCardType,
pub game_version: GameVersionRef,
pub id: EntityId,
pub name: SecondaryCardName,
///Finer classification within the deck (e.g. a category or tactical/fixed split). Free-form — not enum-locked until 11e categories are confirmed.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub subtype: ::std::option::Option<SecondaryCardSubtype>,
///Community-authored card description (original prose only — no reproduced rules text).
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub text: ::std::option::Option<::std::string::String>,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub when_drawn: ::std::option::Option<SecondaryCardWhenDrawn>,
}
///`SecondaryCardActionsItem`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "object",
/// "properties": {
/// "action_id": {
/// "description": "Optional kebab-case identifier used to reference this action from `action-completed` conditions in `awards[].when`.",
/// "type": "string",
/// "maxLength": 64,
/// "minLength": 1
/// },
/// "battle_round": {
/// "description": "Battle-round window in which the action can be started. Absent means any battle round. 'From the second battle round onwards' (Triangulate, Extract Intelligence) is { min: 2 }.",
/// "type": "object",
/// "minProperties": 1,
/// "properties": {
/// "max": {
/// "type": "integer",
/// "maximum": 5.0,
/// "minimum": 1.0
/// },
/// "min": {
/// "type": "integer",
/// "maximum": 5.0,
/// "minimum": 1.0
/// }
/// },
/// "additionalProperties": false
/// },
/// "completes": {
/// "description": "Predicate for when the action is considered complete.",
/// "$ref": "#/$defs/condition"
/// },
/// "effect": {
/// "description": "Effect applied when the action completes (e.g. terrain-area-tag, objective-tag, or unit-tag to mark transient state).",
/// "$ref": "#/$defs/effect"
/// },
/// "player_turn": {
/// "$ref": "#/$defs/player-turn"
/// },
/// "restrictions": {
/// "description": "Predicate that BLOCKS starting the action while it holds (Sensor Sweep: a unit cannot start this action if there is only one operation marker on the battlefield).",
/// "$ref": "#/$defs/condition"
/// },
/// "starts": {
/// "description": "Phase in which the action can be started.",
/// "$ref": "#/$defs/phase"
/// },
/// "timing": {
/// "description": "Non-phase moment the action happens, for card rules that are not started in a phase (Locate and Deny's start-of-battle marker placement, Punishment's start-of-turn condemnation, Consecrate's end-of-turn objective selection). Mutually informative with `starts` — a card action uses one or the other.",
/// "type": "string",
/// "enum": [
/// "start-of-battle",
/// "start-of-turn",
/// "end-of-turn"
/// ]
/// },
/// "units": {
/// "description": "Eligibility predicate for which units may perform the action.",
/// "$ref": "#/$defs/condition"
/// },
/// "use_limit": {
/// "description": "Maximum number of times the action may be performed (per turn unless `use_limit_scope` says otherwise).",
/// "type": "integer",
/// "minimum": 1.0
/// },
/// "use_limit_scope": {
/// "description": "Whether `use_limit` is enforced per turn or once per game (e.g. Recover the Relics / Find and Deny 'Overwhelming Force' is once per game).",
/// "default": "per-turn",
/// "type": "string",
/// "enum": [
/// "per-turn",
/// "per-game"
/// ]
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct SecondaryCardActionsItem {
///Optional kebab-case identifier used to reference this action from `action-completed` conditions in `awards[].when`.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub action_id: ::std::option::Option<SecondaryCardActionsItemActionId>,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub battle_round: ::std::option::Option<SecondaryCardActionsItemBattleRound>,
///Predicate for when the action is considered complete.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub completes: ::std::option::Option<Condition>,
///Effect applied when the action completes (e.g. terrain-area-tag, objective-tag, or unit-tag to mark transient state).
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub effect: ::std::option::Option<Effect>,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub player_turn: ::std::option::Option<PlayerTurn>,
///Predicate that BLOCKS starting the action while it holds (Sensor Sweep: a unit cannot start this action if there is only one operation marker on the battlefield).
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub restrictions: ::std::option::Option<Condition>,
///Phase in which the action can be started.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub starts: ::std::option::Option<Phase>,
///Non-phase moment the action happens, for card rules that are not started in a phase (Locate and Deny's start-of-battle marker placement, Punishment's start-of-turn condemnation, Consecrate's end-of-turn objective selection). Mutually informative with `starts` — a card action uses one or the other.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub timing: ::std::option::Option<SecondaryCardActionsItemTiming>,
///Eligibility predicate for which units may perform the action.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub units: ::std::option::Option<Condition>,
///Maximum number of times the action may be performed (per turn unless `use_limit_scope` says otherwise).
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub use_limit: ::std::option::Option<::std::num::NonZeroU64>,
///Whether `use_limit` is enforced per turn or once per game (e.g. Recover the Relics / Find and Deny 'Overwhelming Force' is once per game).
#[serde(default = "defaults::secondary_card_actions_item_use_limit_scope")]
pub use_limit_scope: SecondaryCardActionsItemUseLimitScope,
}
impl ::std::default::Default for SecondaryCardActionsItem {
fn default() -> Self {
Self {
action_id: Default::default(),
battle_round: Default::default(),
completes: Default::default(),
effect: Default::default(),
player_turn: Default::default(),
restrictions: Default::default(),
starts: Default::default(),
timing: Default::default(),
units: Default::default(),
use_limit: Default::default(),
use_limit_scope: defaults::secondary_card_actions_item_use_limit_scope(),
}
}
}
///Optional kebab-case identifier used to reference this action from `action-completed` conditions in `awards[].when`.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Optional kebab-case identifier used to reference this action from `action-completed` conditions in `awards[].when`.",
/// "type": "string",
/// "maxLength": 64,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct SecondaryCardActionsItemActionId(::std::string::String);
impl ::std::ops::Deref for SecondaryCardActionsItemActionId {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<SecondaryCardActionsItemActionId> for ::std::string::String {
fn from(value: SecondaryCardActionsItemActionId) -> Self {
value.0
}
}
impl ::std::str::FromStr for SecondaryCardActionsItemActionId {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 64usize {
return Err("longer than 64 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for SecondaryCardActionsItemActionId {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String>
for SecondaryCardActionsItemActionId {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String>
for SecondaryCardActionsItemActionId {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for SecondaryCardActionsItemActionId {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///Battle-round window in which the action can be started. Absent means any battle round. 'From the second battle round onwards' (Triangulate, Extract Intelligence) is { min: 2 }.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Battle-round window in which the action can be started. Absent means any battle round. 'From the second battle round onwards' (Triangulate, Extract Intelligence) is { min: 2 }.",
/// "type": "object",
/// "minProperties": 1,
/// "properties": {
/// "max": {
/// "type": "integer",
/// "maximum": 5.0,
/// "minimum": 1.0
/// },
/// "min": {
/// "type": "integer",
/// "maximum": 5.0,
/// "minimum": 1.0
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct SecondaryCardActionsItemBattleRound {
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub max: ::std::option::Option<::std::num::NonZeroU64>,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub min: ::std::option::Option<::std::num::NonZeroU64>,
}
impl ::std::default::Default for SecondaryCardActionsItemBattleRound {
fn default() -> Self {
Self {
max: Default::default(),
min: Default::default(),
}
}
}
///Non-phase moment the action happens, for card rules that are not started in a phase (Locate and Deny's start-of-battle marker placement, Punishment's start-of-turn condemnation, Consecrate's end-of-turn objective selection). Mutually informative with `starts` — a card action uses one or the other.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Non-phase moment the action happens, for card rules that are not started in a phase (Locate and Deny's start-of-battle marker placement, Punishment's start-of-turn condemnation, Consecrate's end-of-turn objective selection). Mutually informative with `starts` — a card action uses one or the other.",
/// "type": "string",
/// "enum": [
/// "start-of-battle",
/// "start-of-turn",
/// "end-of-turn"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum SecondaryCardActionsItemTiming {
#[serde(rename = "start-of-battle")]
StartOfBattle,
#[serde(rename = "start-of-turn")]
StartOfTurn,
#[serde(rename = "end-of-turn")]
EndOfTurn,
}
impl ::std::fmt::Display for SecondaryCardActionsItemTiming {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::StartOfBattle => f.write_str("start-of-battle"),
Self::StartOfTurn => f.write_str("start-of-turn"),
Self::EndOfTurn => f.write_str("end-of-turn"),
}
}
}
impl ::std::str::FromStr for SecondaryCardActionsItemTiming {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"start-of-battle" => Ok(Self::StartOfBattle),
"start-of-turn" => Ok(Self::StartOfTurn),
"end-of-turn" => Ok(Self::EndOfTurn),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for SecondaryCardActionsItemTiming {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for SecondaryCardActionsItemTiming {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for SecondaryCardActionsItemTiming {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///Whether `use_limit` is enforced per turn or once per game (e.g. Recover the Relics / Find and Deny 'Overwhelming Force' is once per game).
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Whether `use_limit` is enforced per turn or once per game (e.g. Recover the Relics / Find and Deny 'Overwhelming Force' is once per game).",
/// "default": "per-turn",
/// "type": "string",
/// "enum": [
/// "per-turn",
/// "per-game"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum SecondaryCardActionsItemUseLimitScope {
#[serde(rename = "per-turn")]
PerTurn,
#[serde(rename = "per-game")]
PerGame,
}
impl ::std::fmt::Display for SecondaryCardActionsItemUseLimitScope {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::PerTurn => f.write_str("per-turn"),
Self::PerGame => f.write_str("per-game"),
}
}
}
impl ::std::str::FromStr for SecondaryCardActionsItemUseLimitScope {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"per-turn" => Ok(Self::PerTurn),
"per-game" => Ok(Self::PerGame),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for SecondaryCardActionsItemUseLimitScope {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String>
for SecondaryCardActionsItemUseLimitScope {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String>
for SecondaryCardActionsItemUseLimitScope {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::default::Default for SecondaryCardActionsItemUseLimitScope {
fn default() -> Self {
SecondaryCardActionsItemUseLimitScope::PerTurn
}
}
///`SecondaryCardAwardsItem`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "object",
/// "oneOf": [
/// {
/// "required": [
/// "vp"
/// ]
/// },
/// {
/// "required": [
/// "per",
/// "vp_per"
/// ]
/// }
/// ],
/// "required": [
/// "trigger"
/// ],
/// "properties": {
/// "cumulative": {
/// "description": "Marks an award the card shows as an additive '+' bonus to the preceding award in the same trigger block (the card's CUMULATIVE rows). Purely descriptive — all awards accrue independently and are summed.",
/// "default": false,
/// "type": "boolean"
/// },
/// "exclusive_group": {
/// "description": "Awards sharing this kebab-case group key resolve as 'score only the highest, not the sum' (the card's literal OR between tier rows). Awards with different `exclusive_group` values, or no value, accrue independently.",
/// "type": "string",
/// "maxLength": 64,
/// "minLength": 1
/// },
/// "mode": {
/// "description": "Which scoring track this award belongs to on cards that print both. Fixed missions are chosen for the whole game and score the (usually lower) `fixed` values; Tactical missions are drawn each turn and score the `tactical` values. Omitted on cards that score the same regardless of approach. A card may carry parallel `fixed` and `tactical` awards for the same condition; a consumer scores only the awards matching the player's chosen approach.",
/// "type": "string",
/// "enum": [
/// "fixed",
/// "tactical"
/// ]
/// },
/// "per": {
/// "description": "What `vp_per` counts, as a kebab-case descriptor (e.g. 'operation-marker-within-range-of-controlled-central-objective'). Required when `vp_per` is present.",
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
/// },
/// "per_max": {
/// "description": "Optional cap on how many instances `vp_per` counts.",
/// "type": "integer",
/// "minimum": 1.0
/// },
/// "trigger": {
/// "$ref": "#/$defs/scoring-trigger"
/// },
/// "vp": {
/// "description": "Flat VP scored when the award fires.",
/// "type": "integer",
/// "minimum": 0.0
/// },
/// "vp_max": {
/// "description": "Optional cap on the total VP this award can contribute over the game — the card's 'UP TO N VP' ceiling. Distinct from `per_max`, which caps the instance count; use `vp_max` when the printed ceiling is not a multiple of `vp_per` (e.g. Burden of Trust's '2VP per guarded objective, up to 9VP').",
/// "type": "integer",
/// "minimum": 1.0
/// },
/// "vp_per": {
/// "description": "VP scored per instance of the thing named by `per` (e.g. 1 VP per operation marker within range of a controlled objective).",
/// "type": "integer",
/// "minimum": 0.0
/// },
/// "when": {
/// "$ref": "#/$defs/condition"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(untagged, deny_unknown_fields)]
pub enum SecondaryCardAwardsItem {
Variant0 {
///Marks an award the card shows as an additive '+' bonus to the preceding award in the same trigger block (the card's CUMULATIVE rows). Purely descriptive — all awards accrue independently and are summed.
#[serde(default)]
cumulative: bool,
///Awards sharing this kebab-case group key resolve as 'score only the highest, not the sum' (the card's literal OR between tier rows). Awards with different `exclusive_group` values, or no value, accrue independently.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
exclusive_group: ::std::option::Option<
SecondaryCardAwardsItemVariant0ExclusiveGroup,
>,
///Which scoring track this award belongs to on cards that print both. Fixed missions are chosen for the whole game and score the (usually lower) `fixed` values; Tactical missions are drawn each turn and score the `tactical` values. Omitted on cards that score the same regardless of approach. A card may carry parallel `fixed` and `tactical` awards for the same condition; a consumer scores only the awards matching the player's chosen approach.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
mode: ::std::option::Option<SecondaryCardAwardsItemVariant0Mode>,
///Optional cap on how many instances `vp_per` counts.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
per_max: ::std::option::Option<::std::num::NonZeroU64>,
trigger: ScoringTrigger,
///Flat VP scored when the award fires.
vp: u64,
///Optional cap on the total VP this award can contribute over the game — the card's 'UP TO N VP' ceiling. Distinct from `per_max`, which caps the instance count; use `vp_max` when the printed ceiling is not a multiple of `vp_per` (e.g. Burden of Trust's '2VP per guarded objective, up to 9VP').
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
vp_max: ::std::option::Option<::std::num::NonZeroU64>,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
when: ::std::option::Option<Condition>,
},
Variant1 {
///Marks an award the card shows as an additive '+' bonus to the preceding award in the same trigger block (the card's CUMULATIVE rows). Purely descriptive — all awards accrue independently and are summed.
#[serde(default)]
cumulative: bool,
///Awards sharing this kebab-case group key resolve as 'score only the highest, not the sum' (the card's literal OR between tier rows). Awards with different `exclusive_group` values, or no value, accrue independently.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
exclusive_group: ::std::option::Option<
SecondaryCardAwardsItemVariant1ExclusiveGroup,
>,
///Which scoring track this award belongs to on cards that print both. Fixed missions are chosen for the whole game and score the (usually lower) `fixed` values; Tactical missions are drawn each turn and score the `tactical` values. Omitted on cards that score the same regardless of approach. A card may carry parallel `fixed` and `tactical` awards for the same condition; a consumer scores only the awards matching the player's chosen approach.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
mode: ::std::option::Option<SecondaryCardAwardsItemVariant1Mode>,
///What `vp_per` counts, as a kebab-case descriptor (e.g. 'operation-marker-within-range-of-controlled-central-objective'). Required when `vp_per` is present.
per: SecondaryCardAwardsItemVariant1Per,
///Optional cap on how many instances `vp_per` counts.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
per_max: ::std::option::Option<::std::num::NonZeroU64>,
trigger: ScoringTrigger,
///Optional cap on the total VP this award can contribute over the game — the card's 'UP TO N VP' ceiling. Distinct from `per_max`, which caps the instance count; use `vp_max` when the printed ceiling is not a multiple of `vp_per` (e.g. Burden of Trust's '2VP per guarded objective, up to 9VP').
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
vp_max: ::std::option::Option<::std::num::NonZeroU64>,
///VP scored per instance of the thing named by `per` (e.g. 1 VP per operation marker within range of a controlled objective).
vp_per: u64,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
when: ::std::option::Option<Condition>,
},
}
///Awards sharing this kebab-case group key resolve as 'score only the highest, not the sum' (the card's literal OR between tier rows). Awards with different `exclusive_group` values, or no value, accrue independently.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Awards sharing this kebab-case group key resolve as 'score only the highest, not the sum' (the card's literal OR between tier rows). Awards with different `exclusive_group` values, or no value, accrue independently.",
/// "type": "string",
/// "maxLength": 64,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct SecondaryCardAwardsItemVariant0ExclusiveGroup(::std::string::String);
impl ::std::ops::Deref for SecondaryCardAwardsItemVariant0ExclusiveGroup {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<SecondaryCardAwardsItemVariant0ExclusiveGroup>
for ::std::string::String {
fn from(value: SecondaryCardAwardsItemVariant0ExclusiveGroup) -> Self {
value.0
}
}
impl ::std::str::FromStr for SecondaryCardAwardsItemVariant0ExclusiveGroup {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 64usize {
return Err("longer than 64 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for SecondaryCardAwardsItemVariant0ExclusiveGroup {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String>
for SecondaryCardAwardsItemVariant0ExclusiveGroup {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String>
for SecondaryCardAwardsItemVariant0ExclusiveGroup {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for SecondaryCardAwardsItemVariant0ExclusiveGroup {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///Which scoring track this award belongs to on cards that print both. Fixed missions are chosen for the whole game and score the (usually lower) `fixed` values; Tactical missions are drawn each turn and score the `tactical` values. Omitted on cards that score the same regardless of approach. A card may carry parallel `fixed` and `tactical` awards for the same condition; a consumer scores only the awards matching the player's chosen approach.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Which scoring track this award belongs to on cards that print both. Fixed missions are chosen for the whole game and score the (usually lower) `fixed` values; Tactical missions are drawn each turn and score the `tactical` values. Omitted on cards that score the same regardless of approach. A card may carry parallel `fixed` and `tactical` awards for the same condition; a consumer scores only the awards matching the player's chosen approach.",
/// "type": "string",
/// "enum": [
/// "fixed",
/// "tactical"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum SecondaryCardAwardsItemVariant0Mode {
#[serde(rename = "fixed")]
Fixed,
#[serde(rename = "tactical")]
Tactical,
}
impl ::std::fmt::Display for SecondaryCardAwardsItemVariant0Mode {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Fixed => f.write_str("fixed"),
Self::Tactical => f.write_str("tactical"),
}
}
}
impl ::std::str::FromStr for SecondaryCardAwardsItemVariant0Mode {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"fixed" => Ok(Self::Fixed),
"tactical" => Ok(Self::Tactical),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for SecondaryCardAwardsItemVariant0Mode {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String>
for SecondaryCardAwardsItemVariant0Mode {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String>
for SecondaryCardAwardsItemVariant0Mode {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///Awards sharing this kebab-case group key resolve as 'score only the highest, not the sum' (the card's literal OR between tier rows). Awards with different `exclusive_group` values, or no value, accrue independently.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Awards sharing this kebab-case group key resolve as 'score only the highest, not the sum' (the card's literal OR between tier rows). Awards with different `exclusive_group` values, or no value, accrue independently.",
/// "type": "string",
/// "maxLength": 64,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct SecondaryCardAwardsItemVariant1ExclusiveGroup(::std::string::String);
impl ::std::ops::Deref for SecondaryCardAwardsItemVariant1ExclusiveGroup {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<SecondaryCardAwardsItemVariant1ExclusiveGroup>
for ::std::string::String {
fn from(value: SecondaryCardAwardsItemVariant1ExclusiveGroup) -> Self {
value.0
}
}
impl ::std::str::FromStr for SecondaryCardAwardsItemVariant1ExclusiveGroup {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 64usize {
return Err("longer than 64 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for SecondaryCardAwardsItemVariant1ExclusiveGroup {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String>
for SecondaryCardAwardsItemVariant1ExclusiveGroup {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String>
for SecondaryCardAwardsItemVariant1ExclusiveGroup {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for SecondaryCardAwardsItemVariant1ExclusiveGroup {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///Which scoring track this award belongs to on cards that print both. Fixed missions are chosen for the whole game and score the (usually lower) `fixed` values; Tactical missions are drawn each turn and score the `tactical` values. Omitted on cards that score the same regardless of approach. A card may carry parallel `fixed` and `tactical` awards for the same condition; a consumer scores only the awards matching the player's chosen approach.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Which scoring track this award belongs to on cards that print both. Fixed missions are chosen for the whole game and score the (usually lower) `fixed` values; Tactical missions are drawn each turn and score the `tactical` values. Omitted on cards that score the same regardless of approach. A card may carry parallel `fixed` and `tactical` awards for the same condition; a consumer scores only the awards matching the player's chosen approach.",
/// "type": "string",
/// "enum": [
/// "fixed",
/// "tactical"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum SecondaryCardAwardsItemVariant1Mode {
#[serde(rename = "fixed")]
Fixed,
#[serde(rename = "tactical")]
Tactical,
}
impl ::std::fmt::Display for SecondaryCardAwardsItemVariant1Mode {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Fixed => f.write_str("fixed"),
Self::Tactical => f.write_str("tactical"),
}
}
}
impl ::std::str::FromStr for SecondaryCardAwardsItemVariant1Mode {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"fixed" => Ok(Self::Fixed),
"tactical" => Ok(Self::Tactical),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for SecondaryCardAwardsItemVariant1Mode {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String>
for SecondaryCardAwardsItemVariant1Mode {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String>
for SecondaryCardAwardsItemVariant1Mode {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///What `vp_per` counts, as a kebab-case descriptor (e.g. 'operation-marker-within-range-of-controlled-central-objective'). Required when `vp_per` is present.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "What `vp_per` counts, as a kebab-case descriptor (e.g. 'operation-marker-within-range-of-controlled-central-objective'). Required when `vp_per` is present.",
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct SecondaryCardAwardsItemVariant1Per(::std::string::String);
impl ::std::ops::Deref for SecondaryCardAwardsItemVariant1Per {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<SecondaryCardAwardsItemVariant1Per> for ::std::string::String {
fn from(value: SecondaryCardAwardsItemVariant1Per) -> Self {
value.0
}
}
impl ::std::str::FromStr for SecondaryCardAwardsItemVariant1Per {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 128usize {
return Err("longer than 128 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for SecondaryCardAwardsItemVariant1Per {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String>
for SecondaryCardAwardsItemVariant1Per {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String>
for SecondaryCardAwardsItemVariant1Per {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for SecondaryCardAwardsItemVariant1Per {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///Whether this is a secondary card or a primary mission card (which reuses this shape).
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Whether this is a secondary card or a primary mission card (which reuses this shape).",
/// "default": "secondary",
/// "type": "string",
/// "enum": [
/// "secondary",
/// "primary"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum SecondaryCardCardType {
#[serde(rename = "secondary")]
Secondary,
#[serde(rename = "primary")]
Primary,
}
impl ::std::fmt::Display for SecondaryCardCardType {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Secondary => f.write_str("secondary"),
Self::Primary => f.write_str("primary"),
}
}
}
impl ::std::str::FromStr for SecondaryCardCardType {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"secondary" => Ok(Self::Secondary),
"primary" => Ok(Self::Primary),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for SecondaryCardCardType {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for SecondaryCardCardType {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for SecondaryCardCardType {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::default::Default for SecondaryCardCardType {
fn default() -> Self {
SecondaryCardCardType::Secondary
}
}
///`SecondaryCardName`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct SecondaryCardName(::std::string::String);
impl ::std::ops::Deref for SecondaryCardName {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<SecondaryCardName> for ::std::string::String {
fn from(value: SecondaryCardName) -> Self {
value.0
}
}
impl ::std::str::FromStr for SecondaryCardName {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 128usize {
return Err("longer than 128 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for SecondaryCardName {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for SecondaryCardName {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for SecondaryCardName {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for SecondaryCardName {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///Finer classification within the deck (e.g. a category or tactical/fixed split). Free-form — not enum-locked until 11e categories are confirmed.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Finer classification within the deck (e.g. a category or tactical/fixed split). Free-form — not enum-locked until 11e categories are confirmed.",
/// "type": "string",
/// "maxLength": 64,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct SecondaryCardSubtype(::std::string::String);
impl ::std::ops::Deref for SecondaryCardSubtype {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<SecondaryCardSubtype> for ::std::string::String {
fn from(value: SecondaryCardSubtype) -> Self {
value.0
}
}
impl ::std::str::FromStr for SecondaryCardSubtype {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 64usize {
return Err("longer than 64 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for SecondaryCardSubtype {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for SecondaryCardSubtype {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for SecondaryCardSubtype {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for SecondaryCardSubtype {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///Optional deck operation performed when this card is drawn (e.g. redraw, swap). Distinct from combat effects — deck operations have no combat target, so they are not modelled via the Ability DSL effect language. If `condition` is present, the operation fires only when the predicate holds.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Optional deck operation performed when this card is drawn (e.g. redraw, swap). Distinct from combat effects — deck operations have no combat target, so they are not modelled via the Ability DSL effect language. If `condition` is present, the operation fires only when the predicate holds.",
/// "type": "object",
/// "required": [
/// "operation"
/// ],
/// "properties": {
/// "battle_round": {
/// "description": "Battle-round window in which the draw operation is eligible (e.g. { max: 1 } means 'only when drawn in the first battle round'). Absent means the operation fires regardless of round.",
/// "type": "object",
/// "minProperties": 1,
/// "properties": {
/// "max": {
/// "type": "integer",
/// "maximum": 5.0,
/// "minimum": 1.0
/// },
/// "min": {
/// "type": "integer",
/// "maximum": 5.0,
/// "minimum": 1.0
/// }
/// },
/// "additionalProperties": false
/// },
/// "card_ids": {
/// "description": "Other cards this operation references, by id.",
/// "type": "array",
/// "items": {
/// "$ref": "#/$defs/entity-id"
/// },
/// "uniqueItems": true
/// },
/// "condition": {
/// "description": "Draw-time army-composition predicate gating the operation (e.g. redraw when the opponent lacks a qualifying unit).",
/// "$ref": "#/$defs/army-composition-predicate"
/// },
/// "operation": {
/// "description": "The deck manipulation this card triggers on draw.",
/// "type": "string",
/// "enum": [
/// "reshuffle",
/// "replace",
/// "redraw",
/// "draw-extra",
/// "swap"
/// ]
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct SecondaryCardWhenDrawn {
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub battle_round: ::std::option::Option<SecondaryCardWhenDrawnBattleRound>,
///Other cards this operation references, by id.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub card_ids: ::std::option::Option<Vec<EntityId>>,
///Draw-time army-composition predicate gating the operation (e.g. redraw when the opponent lacks a qualifying unit).
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub condition: ::std::option::Option<ArmyCompositionPredicate>,
///The deck manipulation this card triggers on draw.
pub operation: SecondaryCardWhenDrawnOperation,
}
///Battle-round window in which the draw operation is eligible (e.g. { max: 1 } means 'only when drawn in the first battle round'). Absent means the operation fires regardless of round.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Battle-round window in which the draw operation is eligible (e.g. { max: 1 } means 'only when drawn in the first battle round'). Absent means the operation fires regardless of round.",
/// "type": "object",
/// "minProperties": 1,
/// "properties": {
/// "max": {
/// "type": "integer",
/// "maximum": 5.0,
/// "minimum": 1.0
/// },
/// "min": {
/// "type": "integer",
/// "maximum": 5.0,
/// "minimum": 1.0
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct SecondaryCardWhenDrawnBattleRound {
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub max: ::std::option::Option<::std::num::NonZeroU64>,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub min: ::std::option::Option<::std::num::NonZeroU64>,
}
impl ::std::default::Default for SecondaryCardWhenDrawnBattleRound {
fn default() -> Self {
Self {
max: Default::default(),
min: Default::default(),
}
}
}
///The deck manipulation this card triggers on draw.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "The deck manipulation this card triggers on draw.",
/// "type": "string",
/// "enum": [
/// "reshuffle",
/// "replace",
/// "redraw",
/// "draw-extra",
/// "swap"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum SecondaryCardWhenDrawnOperation {
#[serde(rename = "reshuffle")]
Reshuffle,
#[serde(rename = "replace")]
Replace,
#[serde(rename = "redraw")]
Redraw,
#[serde(rename = "draw-extra")]
DrawExtra,
#[serde(rename = "swap")]
Swap,
}
impl ::std::fmt::Display for SecondaryCardWhenDrawnOperation {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Reshuffle => f.write_str("reshuffle"),
Self::Replace => f.write_str("replace"),
Self::Redraw => f.write_str("redraw"),
Self::DrawExtra => f.write_str("draw-extra"),
Self::Swap => f.write_str("swap"),
}
}
}
impl ::std::str::FromStr for SecondaryCardWhenDrawnOperation {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"reshuffle" => Ok(Self::Reshuffle),
"replace" => Ok(Self::Replace),
"redraw" => Ok(Self::Redraw),
"draw-extra" => Ok(Self::DrawExtra),
"swap" => Ok(Self::Swap),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for SecondaryCardWhenDrawnOperation {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String>
for SecondaryCardWhenDrawnOperation {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for SecondaryCardWhenDrawnOperation {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///`SequenceEffect`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "object",
/// "required": [
/// "steps",
/// "type"
/// ],
/// "properties": {
/// "steps": {
/// "type": "array",
/// "items": {
/// "$ref": "#/$defs/effect-node"
/// },
/// "minItems": 1
/// },
/// "type": {
/// "const": "sequence"
/// }
/// }
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
pub struct SequenceEffect {
pub steps: ::std::vec::Vec<EffectNode>,
#[serde(rename = "type")]
pub type_: ::serde_json::Value,
}
///Which player a zone or territory belongs to.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Which player a zone or territory belongs to.",
/// "type": "string",
/// "enum": [
/// "attacker",
/// "defender"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum Side {
#[serde(rename = "attacker")]
Attacker,
#[serde(rename = "defender")]
Defender,
}
impl ::std::fmt::Display for Side {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Attacker => f.write_str("attacker"),
Self::Defender => f.write_str("defender"),
}
}
}
impl ::std::str::FromStr for Side {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"attacker" => Ok(Self::Attacker),
"defender" => Ok(Self::Defender),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for Side {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for Side {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for Side {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///`SimpleCondition`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "object",
/// "required": [
/// "type"
/// ],
/// "properties": {
/// "negated": {
/// "default": false,
/// "type": "boolean"
/// },
/// "parameters": {
/// "type": "object",
/// "additionalProperties": true
/// },
/// "type": {
/// "type": "string",
/// "enum": [
/// "phase-is",
/// "timing-is",
/// "player-turn-is",
/// "unit-below-starting-strength",
/// "unit-below-half-strength",
/// "unit-has-keyword",
/// "unit-within-range-of",
/// "model-is-leader",
/// "target-has-keyword",
/// "charged-this-turn",
/// "advanced-this-turn",
/// "remained-stationary",
/// "is-battle-shocked",
/// "has-lost-wounds",
/// "was-hit-by-attack",
/// "opponent-unit-within-range",
/// "within-range-of-objective",
/// "attack-is-type",
/// "has-fought-this-phase",
/// "destroyed-by-attack-type",
/// "controls-objective",
/// "is-attached",
/// "terrain-area-control",
/// "engagement-state",
/// "territory-control",
/// "fights-first",
/// "disposition-matches",
/// "units-destroyed",
/// "units-destroyed-comparison",
/// "objective-majority",
/// "action-completed",
/// "objective-has-tag",
/// "unit-has-tag",
/// "terrain-has-tag",
/// "new-objective-controlled",
/// "engagement-fronts",
/// "destroyed-while-on-objective",
/// "destroyed-in-tagged-terrain",
/// "operation-markers"
/// ]
/// }
/// },
/// "$comment": "Board/meta-state and scoring predicates. `parameters` is intentionally open (additionalProperties: true); each type documents its own param convention. Combat-reactive predicate: `was-hit-by-attack` { subject?: 'self'|'target' (default 'self'), attack_type?: 'melee'|'ranged', weapon_name?: string, count_min?: int (default 1) } — the named unit was hit by at least count_min attacks this phase (distinct from `has-lost-wounds`, which fires only when a wound got through — a hit that is saved or shrugged still satisfies this). `subject:'self'` = the bearer was hit (reactive defensive triggers); `subject:'target'` = the unit the bearer attacked was hit (offensive follow-ups, e.g. a debuff applied to a unit the bearer's named weapon hit). Optional `attack_type`/`weapon_name` narrow which attacks count. Scoring predicates added for mission cards: `units-destroyed` { side: 'enemy'|'friendly', window: 'this-turn'|'previous-turn', count_min: int } — at least count_min units of `side` were destroyed in `window`. `units-destroyed-comparison` { subject: {side, window}, comparator: 'greater-than'|'greater-or-equal', reference: {side, window} } — compares two destruction tallies (e.g. more enemy units destroyed this turn than friendly last turn). `objective-majority` { relative_to: 'opponent' } — you control more objectives than the named party. `controls-objective` params: { count_min: int, objective_role?: 'central'|'expansion'|'non-home'|'home', exclude?: 'home', objective?: 'opponent-home'|'your-home', scope?: 'enemy-territory'|'your-territory' }. Mission-card extensions (11e primary deck): `action-completed` { action_id?: string, target_kind?: 'objective'|'terrain'|'enemy-unit'|'self', target_filter?: { in_enemy_territory?: bool, objective_role?: 'central'|'non-home', exclude?: 'home' }, count_min: int, window?: 'this-turn'|'previous-turn'|'cumulative' } — at least count_min instances of a named action were completed in the window. `objective-has-tag` { tag: 'baited'|'decoyed'|'cleansed'|'triangulated'|'consecrated'|'sabotaged'|'marked'|'vanguard'|'spotted', count_min: int, count_max?: int, objective?: 'opponent-home'|'your-home', scope?: 'enemy-territory'|'your-territory' } — at least count_min objectives carry the named transient tag. `unit-has-tag` { tag: 'doomed'|'condemned'|'spotted'|'surveilled', side: 'enemy'|'friendly', count_min: int, window?: 'destroyed-this-turn'|'left-battlefield-this-turn'|'this-turn'|'still-on-board' } — at least count_min units of `side` carry the tag (optionally with an event window — Punishment scores when a condemned unit was destroyed or left the battlefield this turn; Surveil the Foe scores on units tagged this turn). `terrain-has-tag` { tag: 'mined'|'trapped'|'marked'|'vanguard'|'plundered', friendly_units_min?: int, enemy_units_max?: int, last_marked?: bool, in_enemy_dz?: bool } — terrain piece state predicate; `last_marked` selects the most-recently-marked piece (Find and Deny / Recover the Relics' Overwhelming Force trigger). `new-objective-controlled` { count_min: int } — at least count_min objectives are controlled this turn that were not controlled in the previous command phase. `engagement-fronts` { count_min: int } — friendly units engage enemies in at least count_min distinct fronts; a 'front' is one of the four table quarters (board quadrants about the board's centre — each of the four areas formed by dividing the table along both centre lines). `destroyed-while-on-objective` { destroyer_on_objective?: bool, victim_on_objective?: bool, victim_started_turn_on_objective?: bool, objective_role?: 'central', count_min: int } — count_min enemy units were destroyed this turn under the named spatial condition (the destroying friendly unit, the destroyed enemy unit, or both were within range of an objective at the moment of the kill; `victim_started_turn_on_objective` instead tests the victim's position at the start of the turn, and `objective_role` narrows which objectives count — Secure Asset's central-objective kill row). `destroyed-in-tagged-terrain` { tag?: 'mined'|'trapped'|'marked'|'vanguard'|'plundered', at_start_of_turn?: bool, count_min: int } — count_min enemy units were destroyed this turn while in terrain carrying the named tag; with `at_start_of_turn` the victim must have been in that terrain at the start of the turn (Death Trap's kill bonus), otherwise the spatial test is at the moment of the kill (parallels `destroyed-while-on-objective`). With `tag` omitted, any terrain area qualifies (Search and Scour). `operation-markers` { side?: 'friendly'|'opponent', count_min?: int, count_max?: int, within_range_of?: 'opponent-home-objective', friendly_unit_in_same_terrain_area?: bool, no_enemy_in_terrain_area?: bool } — counts operation markers on the battlefield (side omitted counts both sides' markers); count_max: 0 is 'none remain', count_min == count_max == 1 is 'exactly one'; the terrain-area flags add the co-location proviso used by Locate and Deny / Extract Relic ('one of your units is within the same terrain area as that marker, and no enemy units are within it')."
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
pub struct SimpleCondition {
#[serde(default)]
pub negated: bool,
#[serde(default, skip_serializing_if = "::serde_json::Map::is_empty")]
pub parameters: ::serde_json::Map<::std::string::String, ::serde_json::Value>,
#[serde(rename = "type")]
pub type_: SimpleConditionType,
}
///`SimpleConditionType`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "enum": [
/// "phase-is",
/// "timing-is",
/// "player-turn-is",
/// "unit-below-starting-strength",
/// "unit-below-half-strength",
/// "unit-has-keyword",
/// "unit-within-range-of",
/// "model-is-leader",
/// "target-has-keyword",
/// "charged-this-turn",
/// "advanced-this-turn",
/// "remained-stationary",
/// "is-battle-shocked",
/// "has-lost-wounds",
/// "was-hit-by-attack",
/// "opponent-unit-within-range",
/// "within-range-of-objective",
/// "attack-is-type",
/// "has-fought-this-phase",
/// "destroyed-by-attack-type",
/// "controls-objective",
/// "is-attached",
/// "terrain-area-control",
/// "engagement-state",
/// "territory-control",
/// "fights-first",
/// "disposition-matches",
/// "units-destroyed",
/// "units-destroyed-comparison",
/// "objective-majority",
/// "action-completed",
/// "objective-has-tag",
/// "unit-has-tag",
/// "terrain-has-tag",
/// "new-objective-controlled",
/// "engagement-fronts",
/// "destroyed-while-on-objective",
/// "destroyed-in-tagged-terrain",
/// "operation-markers"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum SimpleConditionType {
#[serde(rename = "phase-is")]
PhaseIs,
#[serde(rename = "timing-is")]
TimingIs,
#[serde(rename = "player-turn-is")]
PlayerTurnIs,
#[serde(rename = "unit-below-starting-strength")]
UnitBelowStartingStrength,
#[serde(rename = "unit-below-half-strength")]
UnitBelowHalfStrength,
#[serde(rename = "unit-has-keyword")]
UnitHasKeyword,
#[serde(rename = "unit-within-range-of")]
UnitWithinRangeOf,
#[serde(rename = "model-is-leader")]
ModelIsLeader,
#[serde(rename = "target-has-keyword")]
TargetHasKeyword,
#[serde(rename = "charged-this-turn")]
ChargedThisTurn,
#[serde(rename = "advanced-this-turn")]
AdvancedThisTurn,
#[serde(rename = "remained-stationary")]
RemainedStationary,
#[serde(rename = "is-battle-shocked")]
IsBattleShocked,
#[serde(rename = "has-lost-wounds")]
HasLostWounds,
#[serde(rename = "was-hit-by-attack")]
WasHitByAttack,
#[serde(rename = "opponent-unit-within-range")]
OpponentUnitWithinRange,
#[serde(rename = "within-range-of-objective")]
WithinRangeOfObjective,
#[serde(rename = "attack-is-type")]
AttackIsType,
#[serde(rename = "has-fought-this-phase")]
HasFoughtThisPhase,
#[serde(rename = "destroyed-by-attack-type")]
DestroyedByAttackType,
#[serde(rename = "controls-objective")]
ControlsObjective,
#[serde(rename = "is-attached")]
IsAttached,
#[serde(rename = "terrain-area-control")]
TerrainAreaControl,
#[serde(rename = "engagement-state")]
EngagementState,
#[serde(rename = "territory-control")]
TerritoryControl,
#[serde(rename = "fights-first")]
FightsFirst,
#[serde(rename = "disposition-matches")]
DispositionMatches,
#[serde(rename = "units-destroyed")]
UnitsDestroyed,
#[serde(rename = "units-destroyed-comparison")]
UnitsDestroyedComparison,
#[serde(rename = "objective-majority")]
ObjectiveMajority,
#[serde(rename = "action-completed")]
ActionCompleted,
#[serde(rename = "objective-has-tag")]
ObjectiveHasTag,
#[serde(rename = "unit-has-tag")]
UnitHasTag,
#[serde(rename = "terrain-has-tag")]
TerrainHasTag,
#[serde(rename = "new-objective-controlled")]
NewObjectiveControlled,
#[serde(rename = "engagement-fronts")]
EngagementFronts,
#[serde(rename = "destroyed-while-on-objective")]
DestroyedWhileOnObjective,
#[serde(rename = "destroyed-in-tagged-terrain")]
DestroyedInTaggedTerrain,
#[serde(rename = "operation-markers")]
OperationMarkers,
}
impl ::std::fmt::Display for SimpleConditionType {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::PhaseIs => f.write_str("phase-is"),
Self::TimingIs => f.write_str("timing-is"),
Self::PlayerTurnIs => f.write_str("player-turn-is"),
Self::UnitBelowStartingStrength => {
f.write_str("unit-below-starting-strength")
}
Self::UnitBelowHalfStrength => f.write_str("unit-below-half-strength"),
Self::UnitHasKeyword => f.write_str("unit-has-keyword"),
Self::UnitWithinRangeOf => f.write_str("unit-within-range-of"),
Self::ModelIsLeader => f.write_str("model-is-leader"),
Self::TargetHasKeyword => f.write_str("target-has-keyword"),
Self::ChargedThisTurn => f.write_str("charged-this-turn"),
Self::AdvancedThisTurn => f.write_str("advanced-this-turn"),
Self::RemainedStationary => f.write_str("remained-stationary"),
Self::IsBattleShocked => f.write_str("is-battle-shocked"),
Self::HasLostWounds => f.write_str("has-lost-wounds"),
Self::WasHitByAttack => f.write_str("was-hit-by-attack"),
Self::OpponentUnitWithinRange => f.write_str("opponent-unit-within-range"),
Self::WithinRangeOfObjective => f.write_str("within-range-of-objective"),
Self::AttackIsType => f.write_str("attack-is-type"),
Self::HasFoughtThisPhase => f.write_str("has-fought-this-phase"),
Self::DestroyedByAttackType => f.write_str("destroyed-by-attack-type"),
Self::ControlsObjective => f.write_str("controls-objective"),
Self::IsAttached => f.write_str("is-attached"),
Self::TerrainAreaControl => f.write_str("terrain-area-control"),
Self::EngagementState => f.write_str("engagement-state"),
Self::TerritoryControl => f.write_str("territory-control"),
Self::FightsFirst => f.write_str("fights-first"),
Self::DispositionMatches => f.write_str("disposition-matches"),
Self::UnitsDestroyed => f.write_str("units-destroyed"),
Self::UnitsDestroyedComparison => f.write_str("units-destroyed-comparison"),
Self::ObjectiveMajority => f.write_str("objective-majority"),
Self::ActionCompleted => f.write_str("action-completed"),
Self::ObjectiveHasTag => f.write_str("objective-has-tag"),
Self::UnitHasTag => f.write_str("unit-has-tag"),
Self::TerrainHasTag => f.write_str("terrain-has-tag"),
Self::NewObjectiveControlled => f.write_str("new-objective-controlled"),
Self::EngagementFronts => f.write_str("engagement-fronts"),
Self::DestroyedWhileOnObjective => {
f.write_str("destroyed-while-on-objective")
}
Self::DestroyedInTaggedTerrain => f.write_str("destroyed-in-tagged-terrain"),
Self::OperationMarkers => f.write_str("operation-markers"),
}
}
}
impl ::std::str::FromStr for SimpleConditionType {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"phase-is" => Ok(Self::PhaseIs),
"timing-is" => Ok(Self::TimingIs),
"player-turn-is" => Ok(Self::PlayerTurnIs),
"unit-below-starting-strength" => Ok(Self::UnitBelowStartingStrength),
"unit-below-half-strength" => Ok(Self::UnitBelowHalfStrength),
"unit-has-keyword" => Ok(Self::UnitHasKeyword),
"unit-within-range-of" => Ok(Self::UnitWithinRangeOf),
"model-is-leader" => Ok(Self::ModelIsLeader),
"target-has-keyword" => Ok(Self::TargetHasKeyword),
"charged-this-turn" => Ok(Self::ChargedThisTurn),
"advanced-this-turn" => Ok(Self::AdvancedThisTurn),
"remained-stationary" => Ok(Self::RemainedStationary),
"is-battle-shocked" => Ok(Self::IsBattleShocked),
"has-lost-wounds" => Ok(Self::HasLostWounds),
"was-hit-by-attack" => Ok(Self::WasHitByAttack),
"opponent-unit-within-range" => Ok(Self::OpponentUnitWithinRange),
"within-range-of-objective" => Ok(Self::WithinRangeOfObjective),
"attack-is-type" => Ok(Self::AttackIsType),
"has-fought-this-phase" => Ok(Self::HasFoughtThisPhase),
"destroyed-by-attack-type" => Ok(Self::DestroyedByAttackType),
"controls-objective" => Ok(Self::ControlsObjective),
"is-attached" => Ok(Self::IsAttached),
"terrain-area-control" => Ok(Self::TerrainAreaControl),
"engagement-state" => Ok(Self::EngagementState),
"territory-control" => Ok(Self::TerritoryControl),
"fights-first" => Ok(Self::FightsFirst),
"disposition-matches" => Ok(Self::DispositionMatches),
"units-destroyed" => Ok(Self::UnitsDestroyed),
"units-destroyed-comparison" => Ok(Self::UnitsDestroyedComparison),
"objective-majority" => Ok(Self::ObjectiveMajority),
"action-completed" => Ok(Self::ActionCompleted),
"objective-has-tag" => Ok(Self::ObjectiveHasTag),
"unit-has-tag" => Ok(Self::UnitHasTag),
"terrain-has-tag" => Ok(Self::TerrainHasTag),
"new-objective-controlled" => Ok(Self::NewObjectiveControlled),
"engagement-fronts" => Ok(Self::EngagementFronts),
"destroyed-while-on-objective" => Ok(Self::DestroyedWhileOnObjective),
"destroyed-in-tagged-terrain" => Ok(Self::DestroyedInTaggedTerrain),
"operation-markers" => Ok(Self::OperationMarkers),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for SimpleConditionType {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for SimpleConditionType {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for SimpleConditionType {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///`SingleEffect`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "object",
/// "required": [
/// "target",
/// "type"
/// ],
/// "properties": {
/// "modifier": {
/// "type": "object",
/// "additionalProperties": true
/// },
/// "target": {
/// "type": "string",
/// "enum": [
/// "self",
/// "bearer",
/// "unit",
/// "attached-unit",
/// "attacker",
/// "defender",
/// "friendly-within-aura",
/// "enemy-within-aura",
/// "all-friendly",
/// "all-enemy"
/// ]
/// },
/// "type": {
/// "type": "string",
/// "enum": [
/// "stat-modifier",
/// "roll-modifier",
/// "re-roll",
/// "mortal-wounds",
/// "feel-no-pain",
/// "invulnerable-save",
/// "ward",
/// "keyword-grant",
/// "movement-modifier",
/// "deep-strike",
/// "fallback-and-act",
/// "fight-first",
/// "fight-last",
/// "shoot-on-death",
/// "fight-on-death",
/// "objective-control-modifier",
/// "leadership-modifier",
/// "damage-reduction",
/// "attack-restriction",
/// "ability-grant",
/// "cp-gain",
/// "cp-refund",
/// "model-destruction",
/// "resurrection",
/// "resource-gain",
/// "resource-spend",
/// "charge-roll-modifier",
/// "terrain-area-tag",
/// "objective-tag",
/// "unit-tag",
/// "bs-modifier",
/// "engagement-passthrough"
/// ]
/// }
/// },
/// "$comment": "When `type` is `re-roll`, `modifier` must carry `roll` (string) and `subset` (`ones` | `all-failures`). Rerolls always target failures; the subset decides whether only 1s are rerolled or every failed die. The constraint is enforced by AJV at validation time and stripped from the codegen bundle (typify can't model if/then/else) — the generated TS/Rust types therefore see `modifier` as an open object, matching its other-`type` callers. When `type` is `feel-no-pain`, `modifier` carries `threshold` (the FNP save target) and optionally `scope` ∈ {`all`, `mortal`}; an absent scope defaults to `all` (fires on every unsaved wound). The two scopes compose independently against the mortal-wound stream. Tag effects (`terrain-area-tag`, `objective-tag`, `unit-tag`) set a transient marker on the named subject; `modifier` carries `tag` (string) and optionally `source` ('this-action'|'destroying-unit') and `clears_on` ('turn-rollover'|'never'). `target` for tag effects names the kind of entity the tag is applied to ('unit', 'self') — a placeholder, since the marker target is the objective/terrain/unit specified by the action context, not a combat target."
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
pub struct SingleEffect {
#[serde(default, skip_serializing_if = "::serde_json::Map::is_empty")]
pub modifier: ::serde_json::Map<::std::string::String, ::serde_json::Value>,
pub target: SingleEffectTarget,
#[serde(rename = "type")]
pub type_: SingleEffectType,
}
///`SingleEffectTarget`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "enum": [
/// "self",
/// "bearer",
/// "unit",
/// "attached-unit",
/// "attacker",
/// "defender",
/// "friendly-within-aura",
/// "enemy-within-aura",
/// "all-friendly",
/// "all-enemy"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum SingleEffectTarget {
#[serde(rename = "self")]
Self_,
#[serde(rename = "bearer")]
Bearer,
#[serde(rename = "unit")]
Unit,
#[serde(rename = "attached-unit")]
AttachedUnit,
#[serde(rename = "attacker")]
Attacker,
#[serde(rename = "defender")]
Defender,
#[serde(rename = "friendly-within-aura")]
FriendlyWithinAura,
#[serde(rename = "enemy-within-aura")]
EnemyWithinAura,
#[serde(rename = "all-friendly")]
AllFriendly,
#[serde(rename = "all-enemy")]
AllEnemy,
}
impl ::std::fmt::Display for SingleEffectTarget {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Self_ => f.write_str("self"),
Self::Bearer => f.write_str("bearer"),
Self::Unit => f.write_str("unit"),
Self::AttachedUnit => f.write_str("attached-unit"),
Self::Attacker => f.write_str("attacker"),
Self::Defender => f.write_str("defender"),
Self::FriendlyWithinAura => f.write_str("friendly-within-aura"),
Self::EnemyWithinAura => f.write_str("enemy-within-aura"),
Self::AllFriendly => f.write_str("all-friendly"),
Self::AllEnemy => f.write_str("all-enemy"),
}
}
}
impl ::std::str::FromStr for SingleEffectTarget {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"self" => Ok(Self::Self_),
"bearer" => Ok(Self::Bearer),
"unit" => Ok(Self::Unit),
"attached-unit" => Ok(Self::AttachedUnit),
"attacker" => Ok(Self::Attacker),
"defender" => Ok(Self::Defender),
"friendly-within-aura" => Ok(Self::FriendlyWithinAura),
"enemy-within-aura" => Ok(Self::EnemyWithinAura),
"all-friendly" => Ok(Self::AllFriendly),
"all-enemy" => Ok(Self::AllEnemy),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for SingleEffectTarget {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for SingleEffectTarget {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for SingleEffectTarget {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///`SingleEffectType`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "enum": [
/// "stat-modifier",
/// "roll-modifier",
/// "re-roll",
/// "mortal-wounds",
/// "feel-no-pain",
/// "invulnerable-save",
/// "ward",
/// "keyword-grant",
/// "movement-modifier",
/// "deep-strike",
/// "fallback-and-act",
/// "fight-first",
/// "fight-last",
/// "shoot-on-death",
/// "fight-on-death",
/// "objective-control-modifier",
/// "leadership-modifier",
/// "damage-reduction",
/// "attack-restriction",
/// "ability-grant",
/// "cp-gain",
/// "cp-refund",
/// "model-destruction",
/// "resurrection",
/// "resource-gain",
/// "resource-spend",
/// "charge-roll-modifier",
/// "terrain-area-tag",
/// "objective-tag",
/// "unit-tag",
/// "bs-modifier",
/// "engagement-passthrough"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum SingleEffectType {
#[serde(rename = "stat-modifier")]
StatModifier,
#[serde(rename = "roll-modifier")]
RollModifier,
#[serde(rename = "re-roll")]
ReRoll,
#[serde(rename = "mortal-wounds")]
MortalWounds,
#[serde(rename = "feel-no-pain")]
FeelNoPain,
#[serde(rename = "invulnerable-save")]
InvulnerableSave,
#[serde(rename = "ward")]
Ward,
#[serde(rename = "keyword-grant")]
KeywordGrant,
#[serde(rename = "movement-modifier")]
MovementModifier,
#[serde(rename = "deep-strike")]
DeepStrike,
#[serde(rename = "fallback-and-act")]
FallbackAndAct,
#[serde(rename = "fight-first")]
FightFirst,
#[serde(rename = "fight-last")]
FightLast,
#[serde(rename = "shoot-on-death")]
ShootOnDeath,
#[serde(rename = "fight-on-death")]
FightOnDeath,
#[serde(rename = "objective-control-modifier")]
ObjectiveControlModifier,
#[serde(rename = "leadership-modifier")]
LeadershipModifier,
#[serde(rename = "damage-reduction")]
DamageReduction,
#[serde(rename = "attack-restriction")]
AttackRestriction,
#[serde(rename = "ability-grant")]
AbilityGrant,
#[serde(rename = "cp-gain")]
CpGain,
#[serde(rename = "cp-refund")]
CpRefund,
#[serde(rename = "model-destruction")]
ModelDestruction,
#[serde(rename = "resurrection")]
Resurrection,
#[serde(rename = "resource-gain")]
ResourceGain,
#[serde(rename = "resource-spend")]
ResourceSpend,
#[serde(rename = "charge-roll-modifier")]
ChargeRollModifier,
#[serde(rename = "terrain-area-tag")]
TerrainAreaTag,
#[serde(rename = "objective-tag")]
ObjectiveTag,
#[serde(rename = "unit-tag")]
UnitTag,
#[serde(rename = "bs-modifier")]
BsModifier,
#[serde(rename = "engagement-passthrough")]
EngagementPassthrough,
}
impl ::std::fmt::Display for SingleEffectType {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::StatModifier => f.write_str("stat-modifier"),
Self::RollModifier => f.write_str("roll-modifier"),
Self::ReRoll => f.write_str("re-roll"),
Self::MortalWounds => f.write_str("mortal-wounds"),
Self::FeelNoPain => f.write_str("feel-no-pain"),
Self::InvulnerableSave => f.write_str("invulnerable-save"),
Self::Ward => f.write_str("ward"),
Self::KeywordGrant => f.write_str("keyword-grant"),
Self::MovementModifier => f.write_str("movement-modifier"),
Self::DeepStrike => f.write_str("deep-strike"),
Self::FallbackAndAct => f.write_str("fallback-and-act"),
Self::FightFirst => f.write_str("fight-first"),
Self::FightLast => f.write_str("fight-last"),
Self::ShootOnDeath => f.write_str("shoot-on-death"),
Self::FightOnDeath => f.write_str("fight-on-death"),
Self::ObjectiveControlModifier => f.write_str("objective-control-modifier"),
Self::LeadershipModifier => f.write_str("leadership-modifier"),
Self::DamageReduction => f.write_str("damage-reduction"),
Self::AttackRestriction => f.write_str("attack-restriction"),
Self::AbilityGrant => f.write_str("ability-grant"),
Self::CpGain => f.write_str("cp-gain"),
Self::CpRefund => f.write_str("cp-refund"),
Self::ModelDestruction => f.write_str("model-destruction"),
Self::Resurrection => f.write_str("resurrection"),
Self::ResourceGain => f.write_str("resource-gain"),
Self::ResourceSpend => f.write_str("resource-spend"),
Self::ChargeRollModifier => f.write_str("charge-roll-modifier"),
Self::TerrainAreaTag => f.write_str("terrain-area-tag"),
Self::ObjectiveTag => f.write_str("objective-tag"),
Self::UnitTag => f.write_str("unit-tag"),
Self::BsModifier => f.write_str("bs-modifier"),
Self::EngagementPassthrough => f.write_str("engagement-passthrough"),
}
}
}
impl ::std::str::FromStr for SingleEffectType {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"stat-modifier" => Ok(Self::StatModifier),
"roll-modifier" => Ok(Self::RollModifier),
"re-roll" => Ok(Self::ReRoll),
"mortal-wounds" => Ok(Self::MortalWounds),
"feel-no-pain" => Ok(Self::FeelNoPain),
"invulnerable-save" => Ok(Self::InvulnerableSave),
"ward" => Ok(Self::Ward),
"keyword-grant" => Ok(Self::KeywordGrant),
"movement-modifier" => Ok(Self::MovementModifier),
"deep-strike" => Ok(Self::DeepStrike),
"fallback-and-act" => Ok(Self::FallbackAndAct),
"fight-first" => Ok(Self::FightFirst),
"fight-last" => Ok(Self::FightLast),
"shoot-on-death" => Ok(Self::ShootOnDeath),
"fight-on-death" => Ok(Self::FightOnDeath),
"objective-control-modifier" => Ok(Self::ObjectiveControlModifier),
"leadership-modifier" => Ok(Self::LeadershipModifier),
"damage-reduction" => Ok(Self::DamageReduction),
"attack-restriction" => Ok(Self::AttackRestriction),
"ability-grant" => Ok(Self::AbilityGrant),
"cp-gain" => Ok(Self::CpGain),
"cp-refund" => Ok(Self::CpRefund),
"model-destruction" => Ok(Self::ModelDestruction),
"resurrection" => Ok(Self::Resurrection),
"resource-gain" => Ok(Self::ResourceGain),
"resource-spend" => Ok(Self::ResourceSpend),
"charge-roll-modifier" => Ok(Self::ChargeRollModifier),
"terrain-area-tag" => Ok(Self::TerrainAreaTag),
"objective-tag" => Ok(Self::ObjectiveTag),
"unit-tag" => Ok(Self::UnitTag),
"bs-modifier" => Ok(Self::BsModifier),
"engagement-passthrough" => Ok(Self::EngagementPassthrough),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for SingleEffectType {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for SingleEffectType {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for SingleEffectType {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///Type of game element that is the source of an enrichment entry
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Type of game element that is the source of an enrichment entry",
/// "type": "string",
/// "enum": [
/// "ability",
/// "stratagem",
/// "enhancement",
/// "detachment-rule",
/// "faction-rule"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum SourceType {
#[serde(rename = "ability")]
Ability,
#[serde(rename = "stratagem")]
Stratagem,
#[serde(rename = "enhancement")]
Enhancement,
#[serde(rename = "detachment-rule")]
DetachmentRule,
#[serde(rename = "faction-rule")]
FactionRule,
}
impl ::std::fmt::Display for SourceType {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Ability => f.write_str("ability"),
Self::Stratagem => f.write_str("stratagem"),
Self::Enhancement => f.write_str("enhancement"),
Self::DetachmentRule => f.write_str("detachment-rule"),
Self::FactionRule => f.write_str("faction-rule"),
}
}
}
impl ::std::str::FromStr for SourceType {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"ability" => Ok(Self::Ability),
"stratagem" => Ok(Self::Stratagem),
"enhancement" => Ok(Self::Enhancement),
"detachment-rule" => Ok(Self::DetachmentRule),
"faction-rule" => Ok(Self::FactionRule),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for SourceType {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for SourceType {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for SourceType {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///A stat that can be a fixed number or a dice expression
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "A stat that can be a fixed number or a dice expression",
/// "oneOf": [
/// {
/// "type": "integer",
/// "minimum": 0.0
/// },
/// {
/// "type": "string",
/// "pattern": "^\\d*[Dd]\\d+(\\+\\d+)?$"
/// }
/// ]
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(untagged)]
pub enum StatValue {
Integer(u64),
String(StatValueString),
}
impl ::std::str::FromStr for StatValue {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
if let Ok(v) = value.parse() {
Ok(Self::Integer(v))
} else if let Ok(v) = value.parse() {
Ok(Self::String(v))
} else {
Err("string conversion failed for all variants".into())
}
}
}
impl ::std::convert::TryFrom<&str> for StatValue {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for StatValue {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for StatValue {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::fmt::Display for StatValue {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match self {
Self::Integer(x) => x.fmt(f),
Self::String(x) => x.fmt(f),
}
}
}
impl ::std::convert::From<u64> for StatValue {
fn from(value: u64) -> Self {
Self::Integer(value)
}
}
impl ::std::convert::From<StatValueString> for StatValue {
fn from(value: StatValueString) -> Self {
Self::String(value)
}
}
///`StatValueString`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "pattern": "^\\d*[Dd]\\d+(\\+\\d+)?$"
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct StatValueString(::std::string::String);
impl ::std::ops::Deref for StatValueString {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<StatValueString> for ::std::string::String {
fn from(value: StatValueString) -> Self {
value.0
}
}
impl ::std::str::FromStr for StatValueString {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
static PATTERN: ::std::sync::LazyLock<::regress::Regex> = ::std::sync::LazyLock::new(||
{ ::regress::Regex::new("^\\d*[Dd]\\d+(\\+\\d+)?$").unwrap() });
if PATTERN.find(value).is_none() {
return Err("doesn't match pattern \"^\\d*[Dd]\\d+(\\+\\d+)?$\"".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for StatValueString {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for StatValueString {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for StatValueString {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for StatValueString {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///A CP-costed ability usable during specific game phases.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Stratagem",
/// "description": "A CP-costed ability usable during specific game phases.",
/// "type": "object",
/// "required": [
/// "category",
/// "cp_cost",
/// "game_version",
/// "id",
/// "name",
/// "phases",
/// "player_turn",
/// "timing",
/// "type"
/// ],
/// "properties": {
/// "ability_id": {
/// "oneOf": [
/// {
/// "$ref": "#/$defs/entity-id"
/// },
/// {
/// "type": "null"
/// }
/// ]
/// },
/// "category": {
/// "description": "Whether this is a universal core stratagem or tied to a specific detachment",
/// "type": "string",
/// "enum": [
/// "core",
/// "detachment"
/// ]
/// },
/// "cp_cost": {
/// "type": "integer",
/// "maximum": 4.0,
/// "minimum": 0.0
/// },
/// "detachment_id": {
/// "description": "Null for core stratagems",
/// "oneOf": [
/// {
/// "$ref": "#/$defs/entity-id"
/// },
/// {
/// "type": "null"
/// }
/// ]
/// },
/// "game_version": {
/// "$ref": "#/$defs/game-version-ref"
/// },
/// "id": {
/// "$ref": "#/$defs/entity-id"
/// },
/// "name": {
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
/// },
/// "phases": {
/// "$ref": "#/$defs/phase-list"
/// },
/// "player_turn": {
/// "$ref": "#/$defs/player-turn"
/// },
/// "target_restrictions": {
/// "oneOf": [
/// {
/// "type": "object",
/// "properties": {
/// "excluded_keywords": {
/// "$ref": "#/$defs/keyword-list"
/// },
/// "notes": {
/// "type": "string"
/// },
/// "required_keywords": {
/// "$ref": "#/$defs/keyword-list"
/// }
/// },
/// "additionalProperties": false
/// },
/// {
/// "type": "null"
/// }
/// ]
/// },
/// "timing": {
/// "type": "string",
/// "enum": [
/// "once-per-phase",
/// "once-per-turn",
/// "once-per-battle",
/// "unlimited"
/// ]
/// },
/// "type": {
/// "description": "GW-printed stratagem category from the card",
/// "type": "string",
/// "enum": [
/// "battle-tactic",
/// "strategic-ploy",
/// "epic-deed",
/// "wargear"
/// ]
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct Stratagem {
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub ability_id: ::std::option::Option<EntityId>,
///Whether this is a universal core stratagem or tied to a specific detachment
pub category: StratagemCategory,
pub cp_cost: i64,
///Null for core stratagems
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub detachment_id: ::std::option::Option<EntityId>,
pub game_version: GameVersionRef,
pub id: EntityId,
pub name: StratagemName,
pub phases: PhaseList,
pub player_turn: PlayerTurn,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub target_restrictions: ::std::option::Option<StratagemTargetRestrictions>,
pub timing: StratagemTiming,
///GW-printed stratagem category from the card
#[serde(rename = "type")]
pub type_: StratagemType,
}
///Whether this is a universal core stratagem or tied to a specific detachment
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Whether this is a universal core stratagem or tied to a specific detachment",
/// "type": "string",
/// "enum": [
/// "core",
/// "detachment"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum StratagemCategory {
#[serde(rename = "core")]
Core,
#[serde(rename = "detachment")]
Detachment,
}
impl ::std::fmt::Display for StratagemCategory {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Core => f.write_str("core"),
Self::Detachment => f.write_str("detachment"),
}
}
}
impl ::std::str::FromStr for StratagemCategory {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"core" => Ok(Self::Core),
"detachment" => Ok(Self::Detachment),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for StratagemCategory {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for StratagemCategory {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for StratagemCategory {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///`StratagemName`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct StratagemName(::std::string::String);
impl ::std::ops::Deref for StratagemName {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<StratagemName> for ::std::string::String {
fn from(value: StratagemName) -> Self {
value.0
}
}
impl ::std::str::FromStr for StratagemName {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 128usize {
return Err("longer than 128 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for StratagemName {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for StratagemName {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for StratagemName {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for StratagemName {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///`StratagemTargetRestrictions`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "object",
/// "properties": {
/// "excluded_keywords": {
/// "$ref": "#/$defs/keyword-list"
/// },
/// "notes": {
/// "type": "string"
/// },
/// "required_keywords": {
/// "$ref": "#/$defs/keyword-list"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct StratagemTargetRestrictions {
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub excluded_keywords: ::std::option::Option<KeywordList>,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub notes: ::std::option::Option<::std::string::String>,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub required_keywords: ::std::option::Option<KeywordList>,
}
impl ::std::default::Default for StratagemTargetRestrictions {
fn default() -> Self {
Self {
excluded_keywords: Default::default(),
notes: Default::default(),
required_keywords: Default::default(),
}
}
}
///`StratagemTiming`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "enum": [
/// "once-per-phase",
/// "once-per-turn",
/// "once-per-battle",
/// "unlimited"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum StratagemTiming {
#[serde(rename = "once-per-phase")]
OncePerPhase,
#[serde(rename = "once-per-turn")]
OncePerTurn,
#[serde(rename = "once-per-battle")]
OncePerBattle,
#[serde(rename = "unlimited")]
Unlimited,
}
impl ::std::fmt::Display for StratagemTiming {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::OncePerPhase => f.write_str("once-per-phase"),
Self::OncePerTurn => f.write_str("once-per-turn"),
Self::OncePerBattle => f.write_str("once-per-battle"),
Self::Unlimited => f.write_str("unlimited"),
}
}
}
impl ::std::str::FromStr for StratagemTiming {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"once-per-phase" => Ok(Self::OncePerPhase),
"once-per-turn" => Ok(Self::OncePerTurn),
"once-per-battle" => Ok(Self::OncePerBattle),
"unlimited" => Ok(Self::Unlimited),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for StratagemTiming {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for StratagemTiming {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for StratagemTiming {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///GW-printed stratagem category from the card
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "GW-printed stratagem category from the card",
/// "type": "string",
/// "enum": [
/// "battle-tactic",
/// "strategic-ploy",
/// "epic-deed",
/// "wargear"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum StratagemType {
#[serde(rename = "battle-tactic")]
BattleTactic,
#[serde(rename = "strategic-ploy")]
StrategicPloy,
#[serde(rename = "epic-deed")]
EpicDeed,
#[serde(rename = "wargear")]
Wargear,
}
impl ::std::fmt::Display for StratagemType {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::BattleTactic => f.write_str("battle-tactic"),
Self::StrategicPloy => f.write_str("strategic-ploy"),
Self::EpicDeed => f.write_str("epic-deed"),
Self::Wargear => f.write_str("wargear"),
}
}
}
impl ::std::str::FromStr for StratagemType {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"battle-tactic" => Ok(Self::BattleTactic),
"strategic-ploy" => Ok(Self::StrategicPloy),
"epic-deed" => Ok(Self::EpicDeed),
"wargear" => Ok(Self::Wargear),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for StratagemType {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for StratagemType {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for StratagemType {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///An 11e terrain-area keyword. Confirmed launch set; extend as further keywords publish on dataslate.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "An 11e terrain-area keyword. Confirmed launch set; extend as further keywords publish on dataslate.",
/// "type": "string",
/// "enum": [
/// "obscuring",
/// "hidden",
/// "plunging-fire"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum TerrainAreaKeyword {
#[serde(rename = "obscuring")]
Obscuring,
#[serde(rename = "hidden")]
Hidden,
#[serde(rename = "plunging-fire")]
PlungingFire,
}
impl ::std::fmt::Display for TerrainAreaKeyword {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Obscuring => f.write_str("obscuring"),
Self::Hidden => f.write_str("hidden"),
Self::PlungingFire => f.write_str("plunging-fire"),
}
}
}
impl ::std::str::FromStr for TerrainAreaKeyword {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"obscuring" => Ok(Self::Obscuring),
"hidden" => Ok(Self::Hidden),
"plunging-fire" => Ok(Self::PlungingFire),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for TerrainAreaKeyword {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for TerrainAreaKeyword {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for TerrainAreaKeyword {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///A recommended arrangement of terrain pieces on the board, independent of the deployment map (a deployment-pattern references the layouts it recommends via recommended_terrain_layout_ids). Each piece draws its geometry from a catalog `template` (a terrain-template entity) or an inline `footprint`; geometry is the source of truth. Placement is template-centroid-anchored: `position` is the piece's centroid, which is invariant under rotation and mirror, so orientation and location are decoupled. Resolved board-space vertices are derived by the shared terrain resolver (pinned by the conformance corpus), never stored here. No layout data is authored yet beyond migrated examples.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Terrain Layout",
/// "description": "A recommended arrangement of terrain pieces on the board, independent of the deployment map (a deployment-pattern references the layouts it recommends via recommended_terrain_layout_ids). Each piece draws its geometry from a catalog `template` (a terrain-template entity) or an inline `footprint`; geometry is the source of truth. Placement is template-centroid-anchored: `position` is the piece's centroid, which is invariant under rotation and mirror, so orientation and location are decoupled. Resolved board-space vertices are derived by the shared terrain resolver (pinned by the conformance corpus), never stored here. No layout data is authored yet beyond migrated examples.",
/// "type": "object",
/// "required": [
/// "game_version",
/// "id",
/// "name"
/// ],
/// "properties": {
/// "deployment_pattern_id": {
/// "description": "Id of the deployment-pattern (map) this layout is built on (e.g. `search-and-destroy`). Optional until confirmed.",
/// "$ref": "#/$defs/entity-id"
/// },
/// "description": {
/// "type": "string"
/// },
/// "game_version": {
/// "$ref": "#/$defs/game-version-ref"
/// },
/// "id": {
/// "$ref": "#/$defs/entity-id"
/// },
/// "mission_matchup_id": {
/// "description": "The 11e Force Disposition matchup this layout's card is built for, named in the card's printed order (e.g. `take-and-hold-vs-purge-the-foe`). One of the enumerated mission-matchup ids. Optional: many cards are not yet classified.",
/// "$ref": "#/$defs/entity-id"
/// },
/// "name": {
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
/// },
/// "pieces": {
/// "description": "Terrain pieces composing the layout. May be empty while a layout is registered by name ahead of its confirmed geometry.",
/// "type": "array",
/// "items": {
/// "$ref": "#/$defs/piece"
/// }
/// },
/// "source": {
/// "description": "Mission pack or source the layout originates from.",
/// "type": "string",
/// "maxLength": 64,
/// "minLength": 1
/// },
/// "variant": {
/// "description": "The card's trailing variant number within its mission matchup (1–3 at launch, since three layouts share each pairing). No hard maximum, to avoid a breaking change if more variants ship.",
/// "type": "integer",
/// "minimum": 1.0
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct TerrainLayout {
///Id of the deployment-pattern (map) this layout is built on (e.g. `search-and-destroy`). Optional until confirmed.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub deployment_pattern_id: ::std::option::Option<EntityId>,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub description: ::std::option::Option<::std::string::String>,
pub game_version: GameVersionRef,
pub id: EntityId,
///The 11e Force Disposition matchup this layout's card is built for, named in the card's printed order (e.g. `take-and-hold-vs-purge-the-foe`). One of the enumerated mission-matchup ids. Optional: many cards are not yet classified.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub mission_matchup_id: ::std::option::Option<EntityId>,
pub name: TerrainLayoutName,
///Terrain pieces composing the layout. May be empty while a layout is registered by name ahead of its confirmed geometry.
#[serde(default, skip_serializing_if = "::std::vec::Vec::is_empty")]
pub pieces: ::std::vec::Vec<Piece>,
///Mission pack or source the layout originates from.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub source: ::std::option::Option<TerrainLayoutSource>,
///The card's trailing variant number within its mission matchup (1–3 at launch, since three layouts share each pairing). No hard maximum, to avoid a breaking change if more variants ship.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub variant: ::std::option::Option<::std::num::NonZeroU64>,
}
///`TerrainLayoutName`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct TerrainLayoutName(::std::string::String);
impl ::std::ops::Deref for TerrainLayoutName {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<TerrainLayoutName> for ::std::string::String {
fn from(value: TerrainLayoutName) -> Self {
value.0
}
}
impl ::std::str::FromStr for TerrainLayoutName {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 128usize {
return Err("longer than 128 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for TerrainLayoutName {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for TerrainLayoutName {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for TerrainLayoutName {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for TerrainLayoutName {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///Mission pack or source the layout originates from.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Mission pack or source the layout originates from.",
/// "type": "string",
/// "maxLength": 64,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct TerrainLayoutSource(::std::string::String);
impl ::std::ops::Deref for TerrainLayoutSource {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<TerrainLayoutSource> for ::std::string::String {
fn from(value: TerrainLayoutSource) -> Self {
value.0
}
}
impl ::std::str::FromStr for TerrainLayoutSource {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 64usize {
return Err("longer than 64 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for TerrainLayoutSource {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for TerrainLayoutSource {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for TerrainLayoutSource {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for TerrainLayoutSource {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///A reusable terrain piece in the standard catalog: a gameplay area (the 11e terrain-area templates) or a scenery feature (walls, containers, pipes, floor segments). Footprints are authored in natural local inches; the terrain resolver derives each footprint's polygon area centroid and re-centers on it, so a layout piece that instances a template places its centroid via the layout's `position`. An `area` template may carry an embedded `features` list — scenery placed in the area's centroid-local frame — making the template a reusable composition (e.g. a ruin with its walls). Placing such a template places all of its features, transformed by the area's own placement.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Terrain Template",
/// "description": "A reusable terrain piece in the standard catalog: a gameplay area (the 11e terrain-area templates) or a scenery feature (walls, containers, pipes, floor segments). Footprints are authored in natural local inches; the terrain resolver derives each footprint's polygon area centroid and re-centers on it, so a layout piece that instances a template places its centroid via the layout's `position`. An `area` template may carry an embedded `features` list — scenery placed in the area's centroid-local frame — making the template a reusable composition (e.g. a ruin with its walls). Placing such a template places all of its features, transformed by the area's own placement.",
/// "type": "object",
/// "required": [
/// "footprint",
/// "game_version",
/// "id",
/// "kind",
/// "name"
/// ],
/// "properties": {
/// "default_blocking": {
/// "description": "Whether the template blocks line of sight / movement by default.",
/// "type": "boolean"
/// },
/// "default_height_inches": {
/// "description": "Default height in inches for pieces instancing this template. Gates Plunging Fire (>= 3\").",
/// "type": "number",
/// "minimum": 0.0
/// },
/// "default_terrain_area_keywords": {
/// "description": "Terrain-area keywords areas of this template carry by default. Meaningful for `kind: \"area\"`.",
/// "type": "array",
/// "items": {
/// "$ref": "#/$defs/terrain-area-keyword"
/// },
/// "uniqueItems": true
/// },
/// "features": {
/// "description": "Composed scenery features, in the area's centroid-local frame. Only meaningful for `kind: \"area\"`.",
/// "type": "array",
/// "items": {
/// "$ref": "#/$defs/composed-feature"
/// }
/// },
/// "footprint": {
/// "$ref": "#/$defs/footprint"
/// },
/// "game_version": {
/// "$ref": "#/$defs/game-version-ref"
/// },
/// "ground_accessible": {
/// "description": "Whether models may be placed on the ground footprint. `false` marks an elevated-only piece (a platform reachable only on its `upper_floor`, e.g. a gantry/catwalk) or a solid obstacle with no valid placement (e.g. a generator). Meaningful for `kind: \"feature\"`.",
/// "default": true,
/// "type": "boolean"
/// },
/// "id": {
/// "$ref": "#/$defs/entity-id"
/// },
/// "kind": {
/// "description": "`area` = a gameplay terrain zone; `feature` = physical scenery placed on an area.",
/// "type": "string",
/// "enum": [
/// "area",
/// "feature"
/// ]
/// },
/// "name": {
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
/// },
/// "source": {
/// "description": "Catalog or mission pack the template originates from.",
/// "type": "string",
/// "maxLength": 64,
/// "minLength": 1
/// },
/// "upper_floor": {
/// "description": "An elevated platform carried by this feature (e.g. a ruin's second storey). Its footprint is authored in the SAME local frame as `footprint` and re-centered on the GROUND footprint's polygon area centroid, so the two floors stay registered when the piece is placed, rotated, or mirrored. Non-resolved metadata: the terrain resolver does not emit it; authoring/visualization tools render it as an overlay. Meaningful for `kind: \"feature\"`.",
/// "type": "object",
/// "required": [
/// "footprint"
/// ],
/// "properties": {
/// "floor": {
/// "description": "Ruin floor this platform occupies (1 = first floor above ground).",
/// "default": 1,
/// "type": "integer",
/// "minimum": 1.0
/// },
/// "footprint": {
/// "$ref": "#/$defs/footprint"
/// }
/// },
/// "additionalProperties": false
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct TerrainTemplate {
///Whether the template blocks line of sight / movement by default.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub default_blocking: ::std::option::Option<bool>,
///Default height in inches for pieces instancing this template. Gates Plunging Fire (>= 3").
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub default_height_inches: ::std::option::Option<f64>,
///Terrain-area keywords areas of this template carry by default. Meaningful for `kind: "area"`.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub default_terrain_area_keywords: ::std::option::Option<Vec<TerrainAreaKeyword>>,
///Composed scenery features, in the area's centroid-local frame. Only meaningful for `kind: "area"`.
#[serde(default, skip_serializing_if = "::std::vec::Vec::is_empty")]
pub features: ::std::vec::Vec<ComposedFeature>,
pub footprint: Footprint,
pub game_version: GameVersionRef,
///Whether models may be placed on the ground footprint. `false` marks an elevated-only piece (a platform reachable only on its `upper_floor`, e.g. a gantry/catwalk) or a solid obstacle with no valid placement (e.g. a generator). Meaningful for `kind: "feature"`.
#[serde(default = "defaults::default_bool::<true>")]
pub ground_accessible: bool,
pub id: EntityId,
///`area` = a gameplay terrain zone; `feature` = physical scenery placed on an area.
pub kind: TerrainTemplateKind,
pub name: TerrainTemplateName,
///Catalog or mission pack the template originates from.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub source: ::std::option::Option<TerrainTemplateSource>,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub upper_floor: ::std::option::Option<TerrainTemplateUpperFloor>,
}
///`area` = a gameplay terrain zone; `feature` = physical scenery placed on an area.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "`area` = a gameplay terrain zone; `feature` = physical scenery placed on an area.",
/// "type": "string",
/// "enum": [
/// "area",
/// "feature"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum TerrainTemplateKind {
#[serde(rename = "area")]
Area,
#[serde(rename = "feature")]
Feature,
}
impl ::std::fmt::Display for TerrainTemplateKind {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Area => f.write_str("area"),
Self::Feature => f.write_str("feature"),
}
}
}
impl ::std::str::FromStr for TerrainTemplateKind {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"area" => Ok(Self::Area),
"feature" => Ok(Self::Feature),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for TerrainTemplateKind {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for TerrainTemplateKind {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for TerrainTemplateKind {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///`TerrainTemplateName`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct TerrainTemplateName(::std::string::String);
impl ::std::ops::Deref for TerrainTemplateName {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<TerrainTemplateName> for ::std::string::String {
fn from(value: TerrainTemplateName) -> Self {
value.0
}
}
impl ::std::str::FromStr for TerrainTemplateName {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 128usize {
return Err("longer than 128 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for TerrainTemplateName {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for TerrainTemplateName {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for TerrainTemplateName {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for TerrainTemplateName {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///Catalog or mission pack the template originates from.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Catalog or mission pack the template originates from.",
/// "type": "string",
/// "maxLength": 64,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct TerrainTemplateSource(::std::string::String);
impl ::std::ops::Deref for TerrainTemplateSource {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<TerrainTemplateSource> for ::std::string::String {
fn from(value: TerrainTemplateSource) -> Self {
value.0
}
}
impl ::std::str::FromStr for TerrainTemplateSource {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 64usize {
return Err("longer than 64 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for TerrainTemplateSource {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for TerrainTemplateSource {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for TerrainTemplateSource {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for TerrainTemplateSource {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///An elevated platform carried by this feature (e.g. a ruin's second storey). Its footprint is authored in the SAME local frame as `footprint` and re-centered on the GROUND footprint's polygon area centroid, so the two floors stay registered when the piece is placed, rotated, or mirrored. Non-resolved metadata: the terrain resolver does not emit it; authoring/visualization tools render it as an overlay. Meaningful for `kind: "feature"`.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "An elevated platform carried by this feature (e.g. a ruin's second storey). Its footprint is authored in the SAME local frame as `footprint` and re-centered on the GROUND footprint's polygon area centroid, so the two floors stay registered when the piece is placed, rotated, or mirrored. Non-resolved metadata: the terrain resolver does not emit it; authoring/visualization tools render it as an overlay. Meaningful for `kind: \"feature\"`.",
/// "type": "object",
/// "required": [
/// "footprint"
/// ],
/// "properties": {
/// "floor": {
/// "description": "Ruin floor this platform occupies (1 = first floor above ground).",
/// "default": 1,
/// "type": "integer",
/// "minimum": 1.0
/// },
/// "footprint": {
/// "$ref": "#/$defs/footprint"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct TerrainTemplateUpperFloor {
///Ruin floor this platform occupies (1 = first floor above ground).
#[serde(default = "defaults::default_nzu64::<::std::num::NonZeroU64, 1>")]
pub floor: ::std::num::NonZeroU64,
pub footprint: Footprint,
}
///`TimingFlag`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Timing Flag",
/// "type": "object",
/// "required": [
/// "game_version",
/// "source_id",
/// "source_type",
/// "timing"
/// ],
/// "properties": {
/// "authored_by": {
/// "$ref": "#/$defs/contributor-ref"
/// },
/// "game_version": {
/// "$ref": "#/$defs/game-version-ref"
/// },
/// "source_id": {
/// "$ref": "#/$defs/entity-id"
/// },
/// "source_type": {
/// "$ref": "#/$defs/source-type"
/// },
/// "timing": {
/// "type": "string",
/// "enum": [
/// "start-of-phase",
/// "end-of-phase",
/// "before-hit-roll",
/// "after-hit-roll",
/// "before-wound-roll",
/// "after-wound-roll",
/// "before-save-roll",
/// "after-save-roll",
/// "before-damage-roll",
/// "after-damage-roll",
/// "before-charge-roll",
/// "after-charge-roll",
/// "before-advance-roll",
/// "after-advance-roll",
/// "before-battle-shock",
/// "after-battle-shock",
/// "on-unit-selected",
/// "on-unit-destroyed",
/// "on-model-destroyed",
/// "on-damage-allocated"
/// ]
/// }
/// }
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
pub struct TimingFlag {
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub authored_by: ::std::option::Option<ContributorRef>,
pub game_version: GameVersionRef,
pub source_id: EntityId,
pub source_type: SourceType,
pub timing: TimingFlagTiming,
}
///`TimingFlagTiming`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "enum": [
/// "start-of-phase",
/// "end-of-phase",
/// "before-hit-roll",
/// "after-hit-roll",
/// "before-wound-roll",
/// "after-wound-roll",
/// "before-save-roll",
/// "after-save-roll",
/// "before-damage-roll",
/// "after-damage-roll",
/// "before-charge-roll",
/// "after-charge-roll",
/// "before-advance-roll",
/// "after-advance-roll",
/// "before-battle-shock",
/// "after-battle-shock",
/// "on-unit-selected",
/// "on-unit-destroyed",
/// "on-model-destroyed",
/// "on-damage-allocated"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum TimingFlagTiming {
#[serde(rename = "start-of-phase")]
StartOfPhase,
#[serde(rename = "end-of-phase")]
EndOfPhase,
#[serde(rename = "before-hit-roll")]
BeforeHitRoll,
#[serde(rename = "after-hit-roll")]
AfterHitRoll,
#[serde(rename = "before-wound-roll")]
BeforeWoundRoll,
#[serde(rename = "after-wound-roll")]
AfterWoundRoll,
#[serde(rename = "before-save-roll")]
BeforeSaveRoll,
#[serde(rename = "after-save-roll")]
AfterSaveRoll,
#[serde(rename = "before-damage-roll")]
BeforeDamageRoll,
#[serde(rename = "after-damage-roll")]
AfterDamageRoll,
#[serde(rename = "before-charge-roll")]
BeforeChargeRoll,
#[serde(rename = "after-charge-roll")]
AfterChargeRoll,
#[serde(rename = "before-advance-roll")]
BeforeAdvanceRoll,
#[serde(rename = "after-advance-roll")]
AfterAdvanceRoll,
#[serde(rename = "before-battle-shock")]
BeforeBattleShock,
#[serde(rename = "after-battle-shock")]
AfterBattleShock,
#[serde(rename = "on-unit-selected")]
OnUnitSelected,
#[serde(rename = "on-unit-destroyed")]
OnUnitDestroyed,
#[serde(rename = "on-model-destroyed")]
OnModelDestroyed,
#[serde(rename = "on-damage-allocated")]
OnDamageAllocated,
}
impl ::std::fmt::Display for TimingFlagTiming {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::StartOfPhase => f.write_str("start-of-phase"),
Self::EndOfPhase => f.write_str("end-of-phase"),
Self::BeforeHitRoll => f.write_str("before-hit-roll"),
Self::AfterHitRoll => f.write_str("after-hit-roll"),
Self::BeforeWoundRoll => f.write_str("before-wound-roll"),
Self::AfterWoundRoll => f.write_str("after-wound-roll"),
Self::BeforeSaveRoll => f.write_str("before-save-roll"),
Self::AfterSaveRoll => f.write_str("after-save-roll"),
Self::BeforeDamageRoll => f.write_str("before-damage-roll"),
Self::AfterDamageRoll => f.write_str("after-damage-roll"),
Self::BeforeChargeRoll => f.write_str("before-charge-roll"),
Self::AfterChargeRoll => f.write_str("after-charge-roll"),
Self::BeforeAdvanceRoll => f.write_str("before-advance-roll"),
Self::AfterAdvanceRoll => f.write_str("after-advance-roll"),
Self::BeforeBattleShock => f.write_str("before-battle-shock"),
Self::AfterBattleShock => f.write_str("after-battle-shock"),
Self::OnUnitSelected => f.write_str("on-unit-selected"),
Self::OnUnitDestroyed => f.write_str("on-unit-destroyed"),
Self::OnModelDestroyed => f.write_str("on-model-destroyed"),
Self::OnDamageAllocated => f.write_str("on-damage-allocated"),
}
}
}
impl ::std::str::FromStr for TimingFlagTiming {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"start-of-phase" => Ok(Self::StartOfPhase),
"end-of-phase" => Ok(Self::EndOfPhase),
"before-hit-roll" => Ok(Self::BeforeHitRoll),
"after-hit-roll" => Ok(Self::AfterHitRoll),
"before-wound-roll" => Ok(Self::BeforeWoundRoll),
"after-wound-roll" => Ok(Self::AfterWoundRoll),
"before-save-roll" => Ok(Self::BeforeSaveRoll),
"after-save-roll" => Ok(Self::AfterSaveRoll),
"before-damage-roll" => Ok(Self::BeforeDamageRoll),
"after-damage-roll" => Ok(Self::AfterDamageRoll),
"before-charge-roll" => Ok(Self::BeforeChargeRoll),
"after-charge-roll" => Ok(Self::AfterChargeRoll),
"before-advance-roll" => Ok(Self::BeforeAdvanceRoll),
"after-advance-roll" => Ok(Self::AfterAdvanceRoll),
"before-battle-shock" => Ok(Self::BeforeBattleShock),
"after-battle-shock" => Ok(Self::AfterBattleShock),
"on-unit-selected" => Ok(Self::OnUnitSelected),
"on-unit-destroyed" => Ok(Self::OnUnitDestroyed),
"on-model-destroyed" => Ok(Self::OnModelDestroyed),
"on-damage-allocated" => Ok(Self::OnDamageAllocated),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for TimingFlagTiming {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for TimingFlagTiming {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for TimingFlagTiming {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///A unit datasheet entry with stat profiles and point costs.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Unit",
/// "description": "A unit datasheet entry with stat profiles and point costs.",
/// "type": "object",
/// "required": [
/// "faction_id",
/// "game_version",
/// "id",
/// "name",
/// "profiles"
/// ],
/// "properties": {
/// "ability_ids": {
/// "type": "array",
/// "items": {
/// "$ref": "#/$defs/entity-id"
/// }
/// },
/// "attachment_role": {
/// "description": "Character attachment role (11e). 'support' implies the unit is only legal when attached to a host unit (cannot be taken solo); 'leader' is valid as a standalone list entry. null/absent for non-attaching units.",
/// "oneOf": [
/// {
/// "enum": [
/// "leader",
/// "support"
/// ]
/// },
/// {
/// "type": "null"
/// }
/// ]
/// },
/// "base_size_mm": {
/// "description": "The unit's representative base (the most-numerous model's base). Mixed-model units carry the full per-model breakdown in unit-composition; this top-level value is a convenience for consumers that need a single base.",
/// "oneOf": [
/// {
/// "$ref": "#/$defs/base-size"
/// },
/// {
/// "type": "null"
/// }
/// ]
/// },
/// "faction_id": {
/// "$ref": "#/$defs/entity-id"
/// },
/// "faction_keywords": {
/// "$ref": "#/$defs/keyword-list"
/// },
/// "game_version": {
/// "$ref": "#/$defs/game-version-ref"
/// },
/// "id": {
/// "$ref": "#/$defs/entity-id"
/// },
/// "is_legend": {
/// "default": false,
/// "type": "boolean"
/// },
/// "keywords": {
/// "$ref": "#/$defs/keyword-list"
/// },
/// "model_count": {
/// "type": "object",
/// "required": [
/// "max",
/// "min"
/// ],
/// "properties": {
/// "max": {
/// "type": "integer",
/// "minimum": 1.0
/// },
/// "min": {
/// "type": "integer",
/// "minimum": 1.0
/// }
/// }
/// },
/// "name": {
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
/// },
/// "points": {
/// "type": "array",
/// "items": {
/// "type": "object",
/// "required": [
/// "cost",
/// "models"
/// ],
/// "properties": {
/// "cost": {
/// "type": "integer",
/// "minimum": 0.0
/// },
/// "models": {
/// "type": "integer",
/// "minimum": 1.0
/// }
/// }
/// }
/// },
/// "points_provisional": {
/// "description": "True when point costs are carried over provisionally (e.g. seeded from a prior edition during migration) and not yet confirmed against the current dataslate.",
/// "default": false,
/// "type": "boolean"
/// },
/// "profiles": {
/// "type": "array",
/// "items": {
/// "type": "object",
/// "required": [
/// "Ld",
/// "M",
/// "OC",
/// "Sv",
/// "T",
/// "W"
/// ],
/// "properties": {
/// "Ld": {
/// "type": "integer",
/// "maximum": 10.0,
/// "minimum": 2.0
/// },
/// "M": {
/// "$ref": "#/$defs/stat-value"
/// },
/// "OC": {
/// "type": "integer",
/// "minimum": 0.0
/// },
/// "Sv": {
/// "type": "integer",
/// "maximum": 7.0,
/// "minimum": 2.0
/// },
/// "T": {
/// "type": "integer",
/// "minimum": 1.0
/// },
/// "W": {
/// "type": "integer",
/// "minimum": 1.0
/// },
/// "invuln_sv": {
/// "oneOf": [
/// {
/// "type": "integer",
/// "maximum": 6.0,
/// "minimum": 2.0
/// },
/// {
/// "type": "null"
/// }
/// ]
/// },
/// "name": {
/// "description": "Profile name (e.g., 'Wounded' for degrading)",
/// "type": "string"
/// }
/// }
/// },
/// "minItems": 1
/// },
/// "role": {
/// "description": "Battlefield role from the datasheet header. Unit types (Infantry, Vehicle, etc.) belong in keywords.",
/// "type": "string",
/// "enum": [
/// "character",
/// "battleline",
/// "dedicated-transport",
/// "fortification",
/// "allied",
/// "epic-hero"
/// ]
/// },
/// "transport_capacity": {
/// "oneOf": [
/// {
/// "type": "object",
/// "required": [
/// "capacity"
/// ],
/// "properties": {
/// "capacity": {
/// "type": "integer",
/// "minimum": 1.0
/// },
/// "exclusion_keywords": {
/// "oneOf": [
/// {
/// "$ref": "#/$defs/keyword-list"
/// },
/// {
/// "type": "null"
/// }
/// ]
/// },
/// "keyword_restrictions": {
/// "oneOf": [
/// {
/// "$ref": "#/$defs/keyword-list"
/// },
/// {
/// "type": "null"
/// }
/// ]
/// }
/// },
/// "additionalProperties": false
/// },
/// {
/// "type": "null"
/// }
/// ]
/// },
/// "weapon_ids": {
/// "type": "array",
/// "items": {
/// "$ref": "#/$defs/entity-id"
/// }
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct Unit {
#[serde(default, skip_serializing_if = "::std::vec::Vec::is_empty")]
pub ability_ids: ::std::vec::Vec<EntityId>,
///Character attachment role (11e). 'support' implies the unit is only legal when attached to a host unit (cannot be taken solo); 'leader' is valid as a standalone list entry. null/absent for non-attaching units.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub attachment_role: ::std::option::Option<UnitAttachmentRole>,
///The unit's representative base (the most-numerous model's base). Mixed-model units carry the full per-model breakdown in unit-composition; this top-level value is a convenience for consumers that need a single base.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub base_size_mm: ::std::option::Option<BaseSize>,
pub faction_id: EntityId,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub faction_keywords: ::std::option::Option<KeywordList>,
pub game_version: GameVersionRef,
pub id: EntityId,
#[serde(default)]
pub is_legend: bool,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub keywords: ::std::option::Option<KeywordList>,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub model_count: ::std::option::Option<UnitModelCount>,
pub name: UnitName,
#[serde(default, skip_serializing_if = "::std::vec::Vec::is_empty")]
pub points: ::std::vec::Vec<UnitPointsItem>,
///True when point costs are carried over provisionally (e.g. seeded from a prior edition during migration) and not yet confirmed against the current dataslate.
#[serde(default)]
pub points_provisional: bool,
pub profiles: ::std::vec::Vec<UnitProfilesItem>,
///Battlefield role from the datasheet header. Unit types (Infantry, Vehicle, etc.) belong in keywords.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub role: ::std::option::Option<UnitRole>,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub transport_capacity: ::std::option::Option<UnitTransportCapacity>,
#[serde(default, skip_serializing_if = "::std::vec::Vec::is_empty")]
pub weapon_ids: ::std::vec::Vec<EntityId>,
}
///`UnitAttachmentRole`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "enum": [
/// "leader",
/// "support"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum UnitAttachmentRole {
#[serde(rename = "leader")]
Leader,
#[serde(rename = "support")]
Support,
}
impl ::std::fmt::Display for UnitAttachmentRole {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Leader => f.write_str("leader"),
Self::Support => f.write_str("support"),
}
}
}
impl ::std::str::FromStr for UnitAttachmentRole {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"leader" => Ok(Self::Leader),
"support" => Ok(Self::Support),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for UnitAttachmentRole {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for UnitAttachmentRole {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for UnitAttachmentRole {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///Describes the internal model-type breakdown of a unit.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Unit Composition",
/// "description": "Describes the internal model-type breakdown of a unit.",
/// "type": "object",
/// "required": [
/// "game_version",
/// "models",
/// "unit_id"
/// ],
/// "properties": {
/// "game_version": {
/// "$ref": "#/$defs/game-version-ref"
/// },
/// "models": {
/// "type": "array",
/// "items": {
/// "type": "object",
/// "required": [
/// "max",
/// "min",
/// "name"
/// ],
/// "properties": {
/// "base_size_mm": {
/// "description": "This model's base. Absent when no base could be resolved for the model.",
/// "$ref": "#/$defs/base-size"
/// },
/// "default_weapon_ids": {
/// "type": "array",
/// "items": {
/// "$ref": "#/$defs/entity-id"
/// }
/// },
/// "is_leader_model": {
/// "default": false,
/// "type": "boolean"
/// },
/// "max": {
/// "type": "integer",
/// "minimum": 1.0
/// },
/// "min": {
/// "type": "integer",
/// "minimum": 0.0
/// },
/// "name": {
/// "type": "string",
/// "minLength": 1
/// },
/// "profile_name": {
/// "oneOf": [
/// {
/// "type": "string",
/// "minLength": 1
/// },
/// {
/// "type": "null"
/// }
/// ]
/// }
/// },
/// "additionalProperties": false
/// },
/// "minItems": 1
/// },
/// "unit_id": {
/// "$ref": "#/$defs/entity-id"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct UnitComposition {
pub game_version: GameVersionRef,
pub models: ::std::vec::Vec<UnitCompositionModelsItem>,
pub unit_id: EntityId,
}
///`UnitCompositionModelsItem`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "object",
/// "required": [
/// "max",
/// "min",
/// "name"
/// ],
/// "properties": {
/// "base_size_mm": {
/// "description": "This model's base. Absent when no base could be resolved for the model.",
/// "$ref": "#/$defs/base-size"
/// },
/// "default_weapon_ids": {
/// "type": "array",
/// "items": {
/// "$ref": "#/$defs/entity-id"
/// }
/// },
/// "is_leader_model": {
/// "default": false,
/// "type": "boolean"
/// },
/// "max": {
/// "type": "integer",
/// "minimum": 1.0
/// },
/// "min": {
/// "type": "integer",
/// "minimum": 0.0
/// },
/// "name": {
/// "type": "string",
/// "minLength": 1
/// },
/// "profile_name": {
/// "oneOf": [
/// {
/// "type": "string",
/// "minLength": 1
/// },
/// {
/// "type": "null"
/// }
/// ]
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct UnitCompositionModelsItem {
///This model's base. Absent when no base could be resolved for the model.
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub base_size_mm: ::std::option::Option<BaseSize>,
#[serde(default, skip_serializing_if = "::std::vec::Vec::is_empty")]
pub default_weapon_ids: ::std::vec::Vec<EntityId>,
#[serde(default)]
pub is_leader_model: bool,
pub max: ::std::num::NonZeroU64,
pub min: u64,
pub name: UnitCompositionModelsItemName,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub profile_name: ::std::option::Option<UnitCompositionModelsItemProfileName>,
}
///`UnitCompositionModelsItemName`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct UnitCompositionModelsItemName(::std::string::String);
impl ::std::ops::Deref for UnitCompositionModelsItemName {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<UnitCompositionModelsItemName> for ::std::string::String {
fn from(value: UnitCompositionModelsItemName) -> Self {
value.0
}
}
impl ::std::str::FromStr for UnitCompositionModelsItemName {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for UnitCompositionModelsItemName {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for UnitCompositionModelsItemName {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for UnitCompositionModelsItemName {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for UnitCompositionModelsItemName {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///`UnitCompositionModelsItemProfileName`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct UnitCompositionModelsItemProfileName(::std::string::String);
impl ::std::ops::Deref for UnitCompositionModelsItemProfileName {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<UnitCompositionModelsItemProfileName>
for ::std::string::String {
fn from(value: UnitCompositionModelsItemProfileName) -> Self {
value.0
}
}
impl ::std::str::FromStr for UnitCompositionModelsItemProfileName {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for UnitCompositionModelsItemProfileName {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String>
for UnitCompositionModelsItemProfileName {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String>
for UnitCompositionModelsItemProfileName {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for UnitCompositionModelsItemProfileName {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///`UnitModelCount`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "object",
/// "required": [
/// "max",
/// "min"
/// ],
/// "properties": {
/// "max": {
/// "type": "integer",
/// "minimum": 1.0
/// },
/// "min": {
/// "type": "integer",
/// "minimum": 1.0
/// }
/// }
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
pub struct UnitModelCount {
pub max: ::std::num::NonZeroU64,
pub min: ::std::num::NonZeroU64,
}
///`UnitName`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct UnitName(::std::string::String);
impl ::std::ops::Deref for UnitName {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<UnitName> for ::std::string::String {
fn from(value: UnitName) -> Self {
value.0
}
}
impl ::std::str::FromStr for UnitName {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 128usize {
return Err("longer than 128 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for UnitName {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for UnitName {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for UnitName {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for UnitName {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///`UnitPointsItem`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "object",
/// "required": [
/// "cost",
/// "models"
/// ],
/// "properties": {
/// "cost": {
/// "type": "integer",
/// "minimum": 0.0
/// },
/// "models": {
/// "type": "integer",
/// "minimum": 1.0
/// }
/// }
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
pub struct UnitPointsItem {
pub cost: u64,
pub models: ::std::num::NonZeroU64,
}
///`UnitProfilesItem`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "object",
/// "required": [
/// "Ld",
/// "M",
/// "OC",
/// "Sv",
/// "T",
/// "W"
/// ],
/// "properties": {
/// "Ld": {
/// "type": "integer",
/// "maximum": 10.0,
/// "minimum": 2.0
/// },
/// "M": {
/// "$ref": "#/$defs/stat-value"
/// },
/// "OC": {
/// "type": "integer",
/// "minimum": 0.0
/// },
/// "Sv": {
/// "type": "integer",
/// "maximum": 7.0,
/// "minimum": 2.0
/// },
/// "T": {
/// "type": "integer",
/// "minimum": 1.0
/// },
/// "W": {
/// "type": "integer",
/// "minimum": 1.0
/// },
/// "invuln_sv": {
/// "oneOf": [
/// {
/// "type": "integer",
/// "maximum": 6.0,
/// "minimum": 2.0
/// },
/// {
/// "type": "null"
/// }
/// ]
/// },
/// "name": {
/// "description": "Profile name (e.g., 'Wounded' for degrading)",
/// "type": "string"
/// }
/// }
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
pub struct UnitProfilesItem {
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub invuln_sv: ::std::option::Option<i64>,
#[serde(rename = "Ld")]
pub ld: i64,
#[serde(rename = "M")]
pub m: StatValue,
///Profile name (e.g., 'Wounded' for degrading)
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub name: ::std::option::Option<::std::string::String>,
#[serde(rename = "OC")]
pub oc: u64,
#[serde(rename = "Sv")]
pub sv: i64,
#[serde(rename = "T")]
pub t: ::std::num::NonZeroU64,
#[serde(rename = "W")]
pub w: ::std::num::NonZeroU64,
}
///Battlefield role from the datasheet header. Unit types (Infantry, Vehicle, etc.) belong in keywords.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Battlefield role from the datasheet header. Unit types (Infantry, Vehicle, etc.) belong in keywords.",
/// "type": "string",
/// "enum": [
/// "character",
/// "battleline",
/// "dedicated-transport",
/// "fortification",
/// "allied",
/// "epic-hero"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum UnitRole {
#[serde(rename = "character")]
Character,
#[serde(rename = "battleline")]
Battleline,
#[serde(rename = "dedicated-transport")]
DedicatedTransport,
#[serde(rename = "fortification")]
Fortification,
#[serde(rename = "allied")]
Allied,
#[serde(rename = "epic-hero")]
EpicHero,
}
impl ::std::fmt::Display for UnitRole {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Character => f.write_str("character"),
Self::Battleline => f.write_str("battleline"),
Self::DedicatedTransport => f.write_str("dedicated-transport"),
Self::Fortification => f.write_str("fortification"),
Self::Allied => f.write_str("allied"),
Self::EpicHero => f.write_str("epic-hero"),
}
}
}
impl ::std::str::FromStr for UnitRole {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"character" => Ok(Self::Character),
"battleline" => Ok(Self::Battleline),
"dedicated-transport" => Ok(Self::DedicatedTransport),
"fortification" => Ok(Self::Fortification),
"allied" => Ok(Self::Allied),
"epic-hero" => Ok(Self::EpicHero),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for UnitRole {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for UnitRole {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for UnitRole {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///`UnitTransportCapacity`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "object",
/// "required": [
/// "capacity"
/// ],
/// "properties": {
/// "capacity": {
/// "type": "integer",
/// "minimum": 1.0
/// },
/// "exclusion_keywords": {
/// "oneOf": [
/// {
/// "$ref": "#/$defs/keyword-list"
/// },
/// {
/// "type": "null"
/// }
/// ]
/// },
/// "keyword_restrictions": {
/// "oneOf": [
/// {
/// "$ref": "#/$defs/keyword-list"
/// },
/// {
/// "type": "null"
/// }
/// ]
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct UnitTransportCapacity {
pub capacity: ::std::num::NonZeroU64,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub exclusion_keywords: ::std::option::Option<KeywordList>,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub keyword_restrictions: ::std::option::Option<KeywordList>,
}
///A 2D point in board inches. Origin at a board corner; JSON uses y-down (downstream renderers may flip to y-up).
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "A 2D point in board inches. Origin at a board corner; JSON uses y-down (downstream renderers may flip to y-up).",
/// "type": "object",
/// "required": [
/// "x",
/// "y"
/// ],
/// "properties": {
/// "x": {
/// "type": "number"
/// },
/// "y": {
/// "type": "number"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct Vec2 {
pub x: f64,
pub y: f64,
}
///A non-weapon item a model may carry — an icon, attachment, or other piece of equipment with no weapon profile. Weapons live in weapon.schema.json; this entity exists so wargear-option swaps and add-ons can reference equipment that is not a weapon.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Wargear",
/// "description": "A non-weapon item a model may carry — an icon, attachment, or other piece of equipment with no weapon profile. Weapons live in weapon.schema.json; this entity exists so wargear-option swaps and add-ons can reference equipment that is not a weapon.",
/// "type": "object",
/// "required": [
/// "game_version",
/// "id",
/// "name"
/// ],
/// "properties": {
/// "category": {
/// "oneOf": [
/// {
/// "description": "Free-form grouping such as 'icon', 'upgrade', or 'equipment'.",
/// "type": "string",
/// "minLength": 1
/// },
/// {
/// "type": "null"
/// }
/// ]
/// },
/// "game_version": {
/// "$ref": "#/$defs/game-version-ref"
/// },
/// "id": {
/// "$ref": "#/$defs/entity-id"
/// },
/// "name": {
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct Wargear {
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub category: ::std::option::Option<WargearCategory>,
pub game_version: GameVersionRef,
pub id: EntityId,
pub name: WargearName,
}
///Free-form grouping such as 'icon', 'upgrade', or 'equipment'.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Free-form grouping such as 'icon', 'upgrade', or 'equipment'.",
/// "type": "string",
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct WargearCategory(::std::string::String);
impl ::std::ops::Deref for WargearCategory {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<WargearCategory> for ::std::string::String {
fn from(value: WargearCategory) -> Self {
value.0
}
}
impl ::std::str::FromStr for WargearCategory {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for WargearCategory {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for WargearCategory {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for WargearCategory {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for WargearCategory {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///`WargearName`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct WargearName(::std::string::String);
impl ::std::ops::Deref for WargearName {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<WargearName> for ::std::string::String {
fn from(value: WargearName) -> Self {
value.0
}
}
impl ::std::str::FromStr for WargearName {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 128usize {
return Err("longer than 128 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for WargearName {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for WargearName {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for WargearName {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for WargearName {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///A wargear option available to models within a unit: a weapon/wargear swap, a pure add-on, or a choice between alternatives. Models start with the unit's base loadout; an option modifies that loadout for the number of models its `model_constraint` permits.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Wargear Option",
/// "description": "A wargear option available to models within a unit: a weapon/wargear swap, a pure add-on, or a choice between alternatives. Models start with the unit's base loadout; an option modifies that loadout for the number of models its `model_constraint` permits.",
/// "type": "object",
/// "required": [
/// "game_version",
/// "id",
/// "unit_id"
/// ],
/// "properties": {
/// "additional_cost": {
/// "oneOf": [
/// {
/// "type": "integer",
/// "minimum": 0.0
/// },
/// {
/// "type": "null"
/// }
/// ]
/// },
/// "game_version": {
/// "$ref": "#/$defs/game-version-ref"
/// },
/// "id": {
/// "$ref": "#/$defs/entity-id"
/// },
/// "is_free": {
/// "default": true,
/// "type": "boolean"
/// },
/// "model_constraint": {
/// "oneOf": [
/// {
/// "description": "Limits how many models may take this option. The eligible-model count is: `any_number` ? model_count : `per_n_models` ? floor(model_count / per_n_models) : (`max_count` ?? 1); then clamped by `max_count` when both are set. `model_name` scopes the option to a specific model profile (e.g. the unit's champion); omit for single-profile units.",
/// "type": "object",
/// "properties": {
/// "any_number": {
/// "description": "When true, every model in the unit may take the option ('Any number of models can each ...'). Mutually exclusive in spirit with `per_n_models`.",
/// "default": false,
/// "type": "boolean"
/// },
/// "max_count": {
/// "type": "integer",
/// "minimum": 1.0
/// },
/// "model_name": {
/// "type": "string",
/// "minLength": 1
/// },
/// "per_n_models": {
/// "type": "integer",
/// "minimum": 1.0
/// }
/// },
/// "additionalProperties": false
/// },
/// {
/// "type": "null"
/// }
/// ]
/// },
/// "replacement": {
/// "description": "Weapon or wargear IDs added to the model — all of them. Exactly one of `replacement` / `replacement_choice` is present.",
/// "type": "array",
/// "items": {
/// "$ref": "#/$defs/entity-id"
/// },
/// "minItems": 1
/// },
/// "replacement_choice": {
/// "description": "A choice of replacements ('one of the following'): pick exactly one inner group; each group's IDs are all added together. Exactly one of `replacement` / `replacement_choice` is present.",
/// "type": "array",
/// "items": {
/// "type": "array",
/// "items": {
/// "$ref": "#/$defs/entity-id"
/// },
/// "minItems": 1
/// },
/// "minItems": 2
/// },
/// "replaces": {
/// "description": "Weapon or wargear IDs removed from the model. Omit for a pure add-on (the option only equips new wargear).",
/// "type": "array",
/// "items": {
/// "$ref": "#/$defs/entity-id"
/// },
/// "minItems": 1
/// },
/// "unit_id": {
/// "$ref": "#/$defs/entity-id"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct WargearOption {
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub additional_cost: ::std::option::Option<u64>,
pub game_version: GameVersionRef,
pub id: EntityId,
#[serde(default = "defaults::default_bool::<true>")]
pub is_free: bool,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub model_constraint: ::std::option::Option<WargearOptionModelConstraint>,
///Weapon or wargear IDs added to the model — all of them. Exactly one of `replacement` / `replacement_choice` is present.
#[serde(default, skip_serializing_if = "::std::vec::Vec::is_empty")]
pub replacement: ::std::vec::Vec<EntityId>,
///A choice of replacements ('one of the following'): pick exactly one inner group; each group's IDs are all added together. Exactly one of `replacement` / `replacement_choice` is present.
#[serde(default, skip_serializing_if = "::std::vec::Vec::is_empty")]
pub replacement_choice: ::std::vec::Vec<::std::vec::Vec<EntityId>>,
///Weapon or wargear IDs removed from the model. Omit for a pure add-on (the option only equips new wargear).
#[serde(default, skip_serializing_if = "::std::vec::Vec::is_empty")]
pub replaces: ::std::vec::Vec<EntityId>,
pub unit_id: EntityId,
}
///Limits how many models may take this option. The eligible-model count is: `any_number` ? model_count : `per_n_models` ? floor(model_count / per_n_models) : (`max_count` ?? 1); then clamped by `max_count` when both are set. `model_name` scopes the option to a specific model profile (e.g. the unit's champion); omit for single-profile units.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Limits how many models may take this option. The eligible-model count is: `any_number` ? model_count : `per_n_models` ? floor(model_count / per_n_models) : (`max_count` ?? 1); then clamped by `max_count` when both are set. `model_name` scopes the option to a specific model profile (e.g. the unit's champion); omit for single-profile units.",
/// "type": "object",
/// "properties": {
/// "any_number": {
/// "description": "When true, every model in the unit may take the option ('Any number of models can each ...'). Mutually exclusive in spirit with `per_n_models`.",
/// "default": false,
/// "type": "boolean"
/// },
/// "max_count": {
/// "type": "integer",
/// "minimum": 1.0
/// },
/// "model_name": {
/// "type": "string",
/// "minLength": 1
/// },
/// "per_n_models": {
/// "type": "integer",
/// "minimum": 1.0
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct WargearOptionModelConstraint {
///When true, every model in the unit may take the option ('Any number of models can each ...'). Mutually exclusive in spirit with `per_n_models`.
#[serde(default)]
pub any_number: bool,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub max_count: ::std::option::Option<::std::num::NonZeroU64>,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub model_name: ::std::option::Option<WargearOptionModelConstraintModelName>,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub per_n_models: ::std::option::Option<::std::num::NonZeroU64>,
}
impl ::std::default::Default for WargearOptionModelConstraint {
fn default() -> Self {
Self {
any_number: Default::default(),
max_count: Default::default(),
model_name: Default::default(),
per_n_models: Default::default(),
}
}
}
///`WargearOptionModelConstraintModelName`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct WargearOptionModelConstraintModelName(::std::string::String);
impl ::std::ops::Deref for WargearOptionModelConstraintModelName {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<WargearOptionModelConstraintModelName>
for ::std::string::String {
fn from(value: WargearOptionModelConstraintModelName) -> Self {
value.0
}
}
impl ::std::str::FromStr for WargearOptionModelConstraintModelName {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for WargearOptionModelConstraintModelName {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String>
for WargearOptionModelConstraintModelName {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String>
for WargearOptionModelConstraintModelName {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for WargearOptionModelConstraintModelName {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///A weapon entry with one or more stat profiles (e.g., standard and overcharge modes).
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Weapon",
/// "description": "A weapon entry with one or more stat profiles (e.g., standard and overcharge modes).",
/// "type": "object",
/// "required": [
/// "game_version",
/// "id",
/// "name",
/// "profiles",
/// "type"
/// ],
/// "properties": {
/// "game_version": {
/// "$ref": "#/$defs/game-version-ref"
/// },
/// "id": {
/// "$ref": "#/$defs/entity-id"
/// },
/// "name": {
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
/// },
/// "profiles": {
/// "type": "array",
/// "items": {
/// "type": "object",
/// "required": [
/// "name",
/// "stats"
/// ],
/// "properties": {
/// "keywords": {
/// "description": "References into the weapon-keyword catalog. Each entry names the catalog id and supplies parameter values (e.g. `Sustained Hits 1` → `{keyword_id: 'sustained-hits', parameters: {value: 1}}`).",
/// "type": "array",
/// "items": {
/// "type": "object",
/// "required": [
/// "keyword_id"
/// ],
/// "properties": {
/// "keyword_id": {
/// "$ref": "#/$defs/entity-id"
/// },
/// "parameters": {
/// "description": "Reference-site parameters conforming to the catalog entry's required_parameters. Only the three documented keys are accepted; any other key is invalid.",
/// "type": "object",
/// "properties": {
/// "target_keyword": {
/// "type": "string",
/// "maxLength": 64,
/// "minLength": 1
/// },
/// "threshold": {
/// "type": "integer",
/// "maximum": 6.0,
/// "minimum": 2.0
/// },
/// "value": {
/// "$ref": "#/$defs/stat-value"
/// }
/// },
/// "additionalProperties": false
/// }
/// },
/// "additionalProperties": false
/// }
/// },
/// "name": {
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
/// },
/// "range": {
/// "oneOf": [
/// {
/// "type": "integer",
/// "minimum": 0.0
/// },
/// {
/// "type": "string",
/// "const": "Melee"
/// }
/// ]
/// },
/// "stats": {
/// "type": "object",
/// "required": [
/// "A",
/// "AP",
/// "D",
/// "S"
/// ],
/// "properties": {
/// "A": {
/// "$ref": "#/$defs/stat-value"
/// },
/// "AP": {
/// "type": "integer"
/// },
/// "BS": {
/// "oneOf": [
/// {
/// "type": "integer",
/// "maximum": 6.0,
/// "minimum": 2.0
/// },
/// {
/// "type": "null"
/// }
/// ]
/// },
/// "D": {
/// "$ref": "#/$defs/stat-value"
/// },
/// "S": {
/// "$ref": "#/$defs/stat-value"
/// },
/// "WS": {
/// "oneOf": [
/// {
/// "type": "integer",
/// "maximum": 6.0,
/// "minimum": 2.0
/// },
/// {
/// "type": "null"
/// }
/// ]
/// }
/// }
/// }
/// },
/// "additionalProperties": false
/// },
/// "minItems": 1
/// },
/// "type": {
/// "enum": [
/// "ranged",
/// "melee"
/// ]
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct Weapon {
pub game_version: GameVersionRef,
pub id: EntityId,
pub name: WeaponName,
pub profiles: ::std::vec::Vec<WeaponProfilesItem>,
#[serde(rename = "type")]
pub type_: WeaponType,
}
///Catalog entry for a weapon keyword (Lethal Hits, Sustained Hits N, Anti-X N+, etc.). Each weapon profile references entries here via {keyword_id, parameters?} instead of carrying free-text strings. The optional `effect` describes the keyword's game mechanic in the Ability DSL; null when the behaviour is faction-specific flavour not yet modelled.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "title": "Weapon Keyword",
/// "description": "Catalog entry for a weapon keyword (Lethal Hits, Sustained Hits N, Anti-X N+, etc.). Each weapon profile references entries here via {keyword_id, parameters?} instead of carrying free-text strings. The optional `effect` describes the keyword's game mechanic in the Ability DSL; null when the behaviour is faction-specific flavour not yet modelled.",
/// "type": "object",
/// "required": [
/// "effect",
/// "game_version",
/// "id",
/// "name",
/// "required_parameters"
/// ],
/// "properties": {
/// "effect": {
/// "description": "Mechanical effect of this keyword. Null when the behaviour is faction-specific flavour not yet expressible in the DSL — engines treat such references as no-op buffs and may surface them as 'cannot auto-apply'.",
/// "oneOf": [
/// {
/// "$ref": "#/$defs/effect"
/// },
/// {
/// "type": "null"
/// }
/// ]
/// },
/// "game_version": {
/// "$ref": "#/$defs/game-version-ref"
/// },
/// "id": {
/// "$ref": "#/$defs/entity-id"
/// },
/// "name": {
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
/// },
/// "required_parameters": {
/// "description": "Parameter keys that must be supplied at each reference site, in the order they would appear in a printed datasheet (e.g. Anti-INFANTRY 4+ → ['target_keyword', 'threshold']).",
/// "type": "array",
/// "items": {
/// "type": "string",
/// "enum": [
/// "value",
/// "target_keyword",
/// "threshold"
/// ]
/// },
/// "maxItems": 3,
/// "uniqueItems": true
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct WeaponKeyword {
///Mechanical effect of this keyword. Null when the behaviour is faction-specific flavour not yet expressible in the DSL — engines treat such references as no-op buffs and may surface them as 'cannot auto-apply'.
pub effect: ::std::option::Option<Effect>,
pub game_version: GameVersionRef,
pub id: EntityId,
pub name: WeaponKeywordName,
///Parameter keys that must be supplied at each reference site, in the order they would appear in a printed datasheet (e.g. Anti-INFANTRY 4+ → ['target_keyword', 'threshold']).
pub required_parameters: Vec<WeaponKeywordRequiredParametersItem>,
}
///`WeaponKeywordName`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct WeaponKeywordName(::std::string::String);
impl ::std::ops::Deref for WeaponKeywordName {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<WeaponKeywordName> for ::std::string::String {
fn from(value: WeaponKeywordName) -> Self {
value.0
}
}
impl ::std::str::FromStr for WeaponKeywordName {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 128usize {
return Err("longer than 128 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for WeaponKeywordName {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for WeaponKeywordName {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for WeaponKeywordName {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for WeaponKeywordName {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///`WeaponKeywordRequiredParametersItem`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "enum": [
/// "value",
/// "target_keyword",
/// "threshold"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum WeaponKeywordRequiredParametersItem {
#[serde(rename = "value")]
Value,
#[serde(rename = "target_keyword")]
TargetKeyword,
#[serde(rename = "threshold")]
Threshold,
}
impl ::std::fmt::Display for WeaponKeywordRequiredParametersItem {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Value => f.write_str("value"),
Self::TargetKeyword => f.write_str("target_keyword"),
Self::Threshold => f.write_str("threshold"),
}
}
}
impl ::std::str::FromStr for WeaponKeywordRequiredParametersItem {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"value" => Ok(Self::Value),
"target_keyword" => Ok(Self::TargetKeyword),
"threshold" => Ok(Self::Threshold),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for WeaponKeywordRequiredParametersItem {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String>
for WeaponKeywordRequiredParametersItem {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String>
for WeaponKeywordRequiredParametersItem {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///`WeaponName`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct WeaponName(::std::string::String);
impl ::std::ops::Deref for WeaponName {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<WeaponName> for ::std::string::String {
fn from(value: WeaponName) -> Self {
value.0
}
}
impl ::std::str::FromStr for WeaponName {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 128usize {
return Err("longer than 128 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for WeaponName {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for WeaponName {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for WeaponName {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for WeaponName {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///`WeaponProfilesItem`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "object",
/// "required": [
/// "name",
/// "stats"
/// ],
/// "properties": {
/// "keywords": {
/// "description": "References into the weapon-keyword catalog. Each entry names the catalog id and supplies parameter values (e.g. `Sustained Hits 1` → `{keyword_id: 'sustained-hits', parameters: {value: 1}}`).",
/// "type": "array",
/// "items": {
/// "type": "object",
/// "required": [
/// "keyword_id"
/// ],
/// "properties": {
/// "keyword_id": {
/// "$ref": "#/$defs/entity-id"
/// },
/// "parameters": {
/// "description": "Reference-site parameters conforming to the catalog entry's required_parameters. Only the three documented keys are accepted; any other key is invalid.",
/// "type": "object",
/// "properties": {
/// "target_keyword": {
/// "type": "string",
/// "maxLength": 64,
/// "minLength": 1
/// },
/// "threshold": {
/// "type": "integer",
/// "maximum": 6.0,
/// "minimum": 2.0
/// },
/// "value": {
/// "$ref": "#/$defs/stat-value"
/// }
/// },
/// "additionalProperties": false
/// }
/// },
/// "additionalProperties": false
/// }
/// },
/// "name": {
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
/// },
/// "range": {
/// "oneOf": [
/// {
/// "type": "integer",
/// "minimum": 0.0
/// },
/// {
/// "type": "string",
/// "const": "Melee"
/// }
/// ]
/// },
/// "stats": {
/// "type": "object",
/// "required": [
/// "A",
/// "AP",
/// "D",
/// "S"
/// ],
/// "properties": {
/// "A": {
/// "$ref": "#/$defs/stat-value"
/// },
/// "AP": {
/// "type": "integer"
/// },
/// "BS": {
/// "oneOf": [
/// {
/// "type": "integer",
/// "maximum": 6.0,
/// "minimum": 2.0
/// },
/// {
/// "type": "null"
/// }
/// ]
/// },
/// "D": {
/// "$ref": "#/$defs/stat-value"
/// },
/// "S": {
/// "$ref": "#/$defs/stat-value"
/// },
/// "WS": {
/// "oneOf": [
/// {
/// "type": "integer",
/// "maximum": 6.0,
/// "minimum": 2.0
/// },
/// {
/// "type": "null"
/// }
/// ]
/// }
/// }
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct WeaponProfilesItem {
///References into the weapon-keyword catalog. Each entry names the catalog id and supplies parameter values (e.g. `Sustained Hits 1` → `{keyword_id: 'sustained-hits', parameters: {value: 1}}`).
#[serde(default, skip_serializing_if = "::std::vec::Vec::is_empty")]
pub keywords: ::std::vec::Vec<WeaponProfilesItemKeywordsItem>,
pub name: WeaponProfilesItemName,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub range: ::std::option::Option<WeaponProfilesItemRange>,
pub stats: WeaponProfilesItemStats,
}
///`WeaponProfilesItemKeywordsItem`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "object",
/// "required": [
/// "keyword_id"
/// ],
/// "properties": {
/// "keyword_id": {
/// "$ref": "#/$defs/entity-id"
/// },
/// "parameters": {
/// "description": "Reference-site parameters conforming to the catalog entry's required_parameters. Only the three documented keys are accepted; any other key is invalid.",
/// "type": "object",
/// "properties": {
/// "target_keyword": {
/// "type": "string",
/// "maxLength": 64,
/// "minLength": 1
/// },
/// "threshold": {
/// "type": "integer",
/// "maximum": 6.0,
/// "minimum": 2.0
/// },
/// "value": {
/// "$ref": "#/$defs/stat-value"
/// }
/// },
/// "additionalProperties": false
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct WeaponProfilesItemKeywordsItem {
pub keyword_id: EntityId,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub parameters: ::std::option::Option<WeaponProfilesItemKeywordsItemParameters>,
}
///Reference-site parameters conforming to the catalog entry's required_parameters. Only the three documented keys are accepted; any other key is invalid.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "Reference-site parameters conforming to the catalog entry's required_parameters. Only the three documented keys are accepted; any other key is invalid.",
/// "type": "object",
/// "properties": {
/// "target_keyword": {
/// "type": "string",
/// "maxLength": 64,
/// "minLength": 1
/// },
/// "threshold": {
/// "type": "integer",
/// "maximum": 6.0,
/// "minimum": 2.0
/// },
/// "value": {
/// "$ref": "#/$defs/stat-value"
/// }
/// },
/// "additionalProperties": false
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(deny_unknown_fields)]
pub struct WeaponProfilesItemKeywordsItemParameters {
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub target_keyword: ::std::option::Option<
WeaponProfilesItemKeywordsItemParametersTargetKeyword,
>,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub threshold: ::std::option::Option<i64>,
#[serde(default, skip_serializing_if = "::std::option::Option::is_none")]
pub value: ::std::option::Option<StatValue>,
}
impl ::std::default::Default for WeaponProfilesItemKeywordsItemParameters {
fn default() -> Self {
Self {
target_keyword: Default::default(),
threshold: Default::default(),
value: Default::default(),
}
}
}
///`WeaponProfilesItemKeywordsItemParametersTargetKeyword`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "maxLength": 64,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct WeaponProfilesItemKeywordsItemParametersTargetKeyword(::std::string::String);
impl ::std::ops::Deref for WeaponProfilesItemKeywordsItemParametersTargetKeyword {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<WeaponProfilesItemKeywordsItemParametersTargetKeyword>
for ::std::string::String {
fn from(value: WeaponProfilesItemKeywordsItemParametersTargetKeyword) -> Self {
value.0
}
}
impl ::std::str::FromStr for WeaponProfilesItemKeywordsItemParametersTargetKeyword {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 64usize {
return Err("longer than 64 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str>
for WeaponProfilesItemKeywordsItemParametersTargetKeyword {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String>
for WeaponProfilesItemKeywordsItemParametersTargetKeyword {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String>
for WeaponProfilesItemKeywordsItemParametersTargetKeyword {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de>
for WeaponProfilesItemKeywordsItemParametersTargetKeyword {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///`WeaponProfilesItemName`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "string",
/// "maxLength": 128,
/// "minLength": 1
///}
/// ```
/// </details>
#[derive(::serde::Serialize, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
#[serde(transparent)]
pub struct WeaponProfilesItemName(::std::string::String);
impl ::std::ops::Deref for WeaponProfilesItemName {
type Target = ::std::string::String;
fn deref(&self) -> &::std::string::String {
&self.0
}
}
impl ::std::convert::From<WeaponProfilesItemName> for ::std::string::String {
fn from(value: WeaponProfilesItemName) -> Self {
value.0
}
}
impl ::std::str::FromStr for WeaponProfilesItemName {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
if value.chars().count() > 128usize {
return Err("longer than 128 characters".into());
}
if value.chars().count() < 1usize {
return Err("shorter than 1 characters".into());
}
Ok(Self(value.to_string()))
}
}
impl ::std::convert::TryFrom<&str> for WeaponProfilesItemName {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for WeaponProfilesItemName {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for WeaponProfilesItemName {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl<'de> ::serde::Deserialize<'de> for WeaponProfilesItemName {
fn deserialize<D>(deserializer: D) -> ::std::result::Result<Self, D::Error>
where
D: ::serde::Deserializer<'de>,
{
::std::string::String::deserialize(deserializer)?
.parse()
.map_err(|e: self::error::ConversionError| {
<D::Error as ::serde::de::Error>::custom(e.to_string())
})
}
}
///`WeaponProfilesItemRange`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "oneOf": [
/// {
/// "type": "integer",
/// "minimum": 0.0
/// },
/// {
/// "type": "string",
/// "const": "Melee"
/// }
/// ]
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(untagged)]
pub enum WeaponProfilesItemRange {
Integer(u64),
String(::std::string::String),
}
impl ::std::fmt::Display for WeaponProfilesItemRange {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match self {
Self::Integer(x) => x.fmt(f),
Self::String(x) => x.fmt(f),
}
}
}
impl ::std::convert::From<u64> for WeaponProfilesItemRange {
fn from(value: u64) -> Self {
Self::Integer(value)
}
}
///`WeaponProfilesItemStats`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "type": "object",
/// "required": [
/// "A",
/// "AP",
/// "D",
/// "S"
/// ],
/// "properties": {
/// "A": {
/// "$ref": "#/$defs/stat-value"
/// },
/// "AP": {
/// "type": "integer"
/// },
/// "BS": {
/// "oneOf": [
/// {
/// "type": "integer",
/// "maximum": 6.0,
/// "minimum": 2.0
/// },
/// {
/// "type": "null"
/// }
/// ]
/// },
/// "D": {
/// "$ref": "#/$defs/stat-value"
/// },
/// "S": {
/// "$ref": "#/$defs/stat-value"
/// },
/// "WS": {
/// "oneOf": [
/// {
/// "type": "integer",
/// "maximum": 6.0,
/// "minimum": 2.0
/// },
/// {
/// "type": "null"
/// }
/// ]
/// }
/// }
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
pub struct WeaponProfilesItemStats {
#[serde(rename = "A")]
pub a: StatValue,
#[serde(rename = "AP")]
pub ap: i64,
#[serde(
rename = "BS",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub bs: ::std::option::Option<i64>,
#[serde(rename = "D")]
pub d: StatValue,
#[serde(rename = "S")]
pub s: StatValue,
#[serde(
rename = "WS",
default,
skip_serializing_if = "::std::option::Option::is_none"
)]
pub ws: ::std::option::Option<i64>,
}
///`WeaponType`
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "enum": [
/// "ranged",
/// "melee"
/// ]
///}
/// ```
/// </details>
#[derive(
::serde::Deserialize,
::serde::Serialize,
Clone,
Copy,
Debug,
Eq,
Hash,
Ord,
PartialEq,
PartialOrd
)]
pub enum WeaponType {
#[serde(rename = "ranged")]
Ranged,
#[serde(rename = "melee")]
Melee,
}
impl ::std::fmt::Display for WeaponType {
fn fmt(&self, f: &mut ::std::fmt::Formatter<'_>) -> ::std::fmt::Result {
match *self {
Self::Ranged => f.write_str("ranged"),
Self::Melee => f.write_str("melee"),
}
}
}
impl ::std::str::FromStr for WeaponType {
type Err = self::error::ConversionError;
fn from_str(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
match value {
"ranged" => Ok(Self::Ranged),
"melee" => Ok(Self::Melee),
_ => Err("invalid value".into()),
}
}
}
impl ::std::convert::TryFrom<&str> for WeaponType {
type Error = self::error::ConversionError;
fn try_from(
value: &str,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<&::std::string::String> for WeaponType {
type Error = self::error::ConversionError;
fn try_from(
value: &::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
impl ::std::convert::TryFrom<::std::string::String> for WeaponType {
type Error = self::error::ConversionError;
fn try_from(
value: ::std::string::String,
) -> ::std::result::Result<Self, self::error::ConversionError> {
value.parse()
}
}
///Auto-generated by tools/src/bundle-schemas.ts. Single self-contained schema for Rust codegen — do not edit by hand.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "$id": "https://40kdc.dev/schemas/bundled.schema.json",
/// "title": "40kdc Bundled Schemas",
/// "description": "Auto-generated by tools/src/bundle-schemas.ts. Single self-contained schema for Rust codegen — do not edit by hand."
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(transparent)]
pub struct X40kdcBundledSchemas(pub ::serde_json::Value);
impl ::std::ops::Deref for X40kdcBundledSchemas {
type Target = ::serde_json::Value;
fn deref(&self) -> &::serde_json::Value {
&self.0
}
}
impl ::std::convert::From<X40kdcBundledSchemas> for ::serde_json::Value {
fn from(value: X40kdcBundledSchemas) -> Self {
value.0
}
}
impl ::std::convert::From<::serde_json::Value> for X40kdcBundledSchemas {
fn from(value: ::serde_json::Value) -> Self {
Self(value)
}
}
///A zone footprint, expressed as an axis-aligned rectangle or an explicit polygon. Vertices/extent are relative to the owning element's position.
///
/// <details><summary>JSON schema</summary>
///
/// ```json
///{
/// "description": "A zone footprint, expressed as an axis-aligned rectangle or an explicit polygon. Vertices/extent are relative to the owning element's position.",
/// "oneOf": [
/// {
/// "type": "object",
/// "required": [
/// "height",
/// "type",
/// "width"
/// ],
/// "properties": {
/// "height": {
/// "type": "number",
/// "exclusiveMinimum": 0.0
/// },
/// "type": {
/// "const": "rectangle"
/// },
/// "width": {
/// "type": "number",
/// "exclusiveMinimum": 0.0
/// }
/// },
/// "additionalProperties": false
/// },
/// {
/// "type": "object",
/// "required": [
/// "points",
/// "type"
/// ],
/// "properties": {
/// "points": {
/// "type": "array",
/// "items": {
/// "$ref": "#/$defs/vec2"
/// },
/// "minItems": 3
/// },
/// "type": {
/// "const": "polygon"
/// }
/// },
/// "additionalProperties": false
/// }
/// ]
///}
/// ```
/// </details>
#[derive(::serde::Deserialize, ::serde::Serialize, Clone, Debug, PartialEq)]
#[serde(tag = "type", deny_unknown_fields)]
pub enum ZoneShape {
#[serde(rename = "rectangle")]
Rectangle { height: f64, width: f64 },
#[serde(rename = "polygon")]
Polygon { points: ::std::vec::Vec<Vec2> },
}
/// Generation of default values for serde.
pub mod defaults {
pub(super) fn default_bool<const V: bool>() -> bool {
V
}
pub(super) fn default_u64<T, const V: u64>() -> T
where
T: ::std::convert::TryFrom<u64>,
<T as ::std::convert::TryFrom<u64>>::Error: ::std::fmt::Debug,
{
T::try_from(V).unwrap()
}
pub(super) fn default_nzu64<T, const V: u64>() -> T
where
T: ::std::convert::TryFrom<::std::num::NonZeroU64>,
<T as ::std::convert::TryFrom<::std::num::NonZeroU64>>::Error: ::std::fmt::Debug,
{
T::try_from(::std::num::NonZeroU64::try_from(V).unwrap()).unwrap()
}
pub(super) fn composed_feature_mirror() -> super::ComposedFeatureMirror {
super::ComposedFeatureMirror::None
}
pub(super) fn dice_gated_effect_comparison() -> super::DiceGatedEffectComparison {
super::DiceGatedEffectComparison::Gte
}
pub(super) fn piece_mirror() -> super::PieceMirror {
super::PieceMirror::None
}
pub(super) fn piece_piece_type() -> super::PiecePieceType {
super::PiecePieceType::Area
}
pub(super) fn secondary_card_card_type() -> super::SecondaryCardCardType {
super::SecondaryCardCardType::Secondary
}
pub(super) fn secondary_card_actions_item_use_limit_scope() -> super::SecondaryCardActionsItemUseLimitScope {
super::SecondaryCardActionsItemUseLimitScope::PerTurn
}
}