delvewright_dsl/mark.rs
1//! Marks: a point the campaign places relative to an anchor (spec-0066).
2
3use schemars::JsonSchema;
4use serde::{Deserialize, Serialize};
5
6use crate::AnchorId;
7use crate::serde_fields::is_zero3;
8
9#[cfg(doc)]
10use crate::{Actor, CastPlace, Npc, SoundAt, Verb};
11
12/// **A mark** (spec-0066): an anchor and an integer block offset from it, the
13/// one declaration of a point the campaign places relative to a piece.
14///
15/// Its cell is the anchor's resolved cell plus `offset`, in world axes after
16/// the piece's placement. It is what a body stands on ([`Npc::offset`],
17/// [`Actor::offset`]), where a walk ends ([`Verb::MoveNpc`], [`Verb::MoveActor`],
18/// [`Verb::Teleport`]), where the cast ledger says a body is
19/// ([`CastPlace::Mark`]), where a sound plays ([`SoundAt::Anchor`]), and every
20/// camera position in a shot: a dolly waypoint (`path`), an aim target
21/// (`look_at`) and an anchor subject (`subject`). The roles are fields; the
22/// type is one.
23///
24/// A mark's cell lies inside the placed piece its anchor belongs to (`DW0897`):
25/// an offset says *where beside this place*, never *which place*.
26#[derive(
27 Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize, JsonSchema,
28)]
29#[serde(deny_unknown_fields)]
30pub struct Mark {
31 /// The anchor the mark is relative to.
32 pub anchor: AnchorId,
33 /// Integer `[x, y, z]` block offset from the anchor (default `[0, 0, 0]`).
34 #[serde(default, skip_serializing_if = "is_zero3")]
35 pub offset: [i32; 3],
36}
37
38impl Mark {
39 /// A mark at `anchor` with no offset.
40 pub fn at(anchor: AnchorId) -> Self {
41 Mark {
42 anchor,
43 offset: [0, 0, 0],
44 }
45 }
46
47 /// Whether the offset is non-zero.
48 pub fn is_offset(&self) -> bool {
49 !is_zero3(&self.offset)
50 }
51
52 /// The mark's cell, given the cell its anchor resolved to.
53 pub fn cell(&self, anchor_cell: [i32; 3]) -> [i32; 3] {
54 offset_cell(anchor_cell, self.offset)
55 }
56
57 /// The mark as a diagnostic spells it: the anchor alone at a zero offset,
58 /// else `anchor + [x, y, z]`.
59 pub fn display(&self) -> String {
60 if self.is_offset() {
61 format!(
62 "{} + [{}, {}, {}]",
63 self.anchor, self.offset[0], self.offset[1], self.offset[2]
64 )
65 } else {
66 self.anchor.as_str().to_string()
67 }
68 }
69}
70
71/// `cell + offset`, componentwise: the one arithmetic a [`Mark`] adds.
72pub fn offset_cell(cell: [i32; 3], offset: [i32; 3]) -> [i32; 3] {
73 [
74 cell[0] + offset[0],
75 cell[1] + offset[1],
76 cell[2] + offset[2],
77 ]
78}