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