Fine, ig)
},
- AddPieces(PiecesSpec{ pos,posd,count,face,pinned,info }) => {
+ AddPieces(PiecesSpec{ pos,posd,count,face,pinned,angle,info }) => {
let (ig_g, modperm, _) = cs.check_acl_modify_pieces(ag, ig)?;
let ig = &mut **ig_g;
let gs = &mut ig.gs;
lastclient: Default::default(),
gen_before_lastclient: Generation(0),
pinned: pinned.unwrap_or(false),
+ angle,
gen: gs.gen,
pos, face,
};
struct SessionPieceContext {
id: VisiblePieceId,
pos: Pos,
+ transform: VisibleAngleTransform,
info: String, // SessionPieceLoadJson as JSON
}
else { continue /* was deleted */ };
let defs = p.make_defs(&pri)?;
alldefs.push((pri.id, defs));
+ let transform = make_angle_visible(pr.angle, pr.pos);
let for_info = SessionPieceLoadJson {
held : &pr.held,
};
let for_piece = SessionPieceContext {
+ transform,
id: pri.id,
- pos : pr.pos,
- info : serde_json::to_string(&for_info)
+ pos: pr.pos,
+ info: serde_json::to_string(&for_info)
.map_err(|e| InternalError::JSONEncode(e))?,
};
uses.push(for_piece);
[[pieces]]
pos = [160,40]
type = "Lib"
+angle.Compass = 1
lib = "wikimedia"
item = "chess-b-N"
count: Some(to_add as u32),
face: None,
pinned: Some(false),
+ angle: default(),
info: Box::new(spec),
};
Poor(vec![ MGI::AddPieces(spec) ],
let spec = PiecesSpec {
pos: Some(pos),
posd: None, count: Some(1), face: None, pinned: Some(false),
- info: Box::new(spec),
+ angle: default(), info: Box::new(spec),
};
let insn = MGI::AddPieces(spec);
insns.push(insn);
visible_slotmap_key!{ VisiblePieceId('.') }
+#[derive(Clone,Debug)]
+#[derive(Serialize,Deserialize)]
+#[serde(transparent)]
+pub struct VisibleAngleTransform(String);
+
#[derive(Clone,Serialize,Deserialize,Hash,Eq,Ord,PartialEq,PartialOrd)]
#[serde(transparent)]
pub struct Html(pub String);
pub held: Option<PlayerId>,
pub zlevel: ZLevel,
pub pinned: bool,
+ #[serde(default)] pub angle: PieceAngle,
pub gen: Generation,
pub lastclient: ClientId,
pub gen_before_lastclient: Generation,
let kd: slotmap::KeyData = p.into();
VisiblePieceId(kd)
}
+
+pub fn make_angle_visible(angle: PieceAngle, pos: Pos)
+ -> VisibleAngleTransform {
+ // todo-lens need to do censorship mapping here
+ VisibleAngleTransform(angle.to_transform(pos))
+}
InconsistentPieceCount,
BadUrlSyntax,
UrlTooLong,
+ CompassAngleInvalid,
}
display_as_debug!{SpecError}
pub count: Option<u32>,
pub face: Option<FaceId>,
pub pinned: Option<bool>,
+ #[serde(default)] pub angle: PieceAngle,
#[serde(flatten)]
pub info: Box<dyn PieceSpec>,
}
+#[derive(Debug,Copy,Clone,Serialize,Deserialize)]
+pub enum PieceAngle {
+ Compass(CompassAngle),
+}
+
+#[derive(Debug,Copy,Clone,Eq,PartialEq)]
+#[derive(Default,Serialize,Deserialize)]
+#[serde(try_from="u8")]
+#[serde(into="u8")]
+/// 0 = unrotated, +ve is anticlockwise, units of 45deg
+pub struct CompassAngle(u8);
+
//---------- Piece specs ----------
// the implementations are in pieces.rs
type SE = SpecError;
type TPS = TablePlayerSpec;
+ impl Default for PieceAngle {
+ fn default() -> Self { PieceAngle::Compass(default()) }
+ }
+
+ impl TryFrom<u8> for CompassAngle {
+ type Error = SpecError;
+ #[throws(SpecError)]
+ fn try_from(v: u8) -> Self {
+ if v < 8 { Self(v) }
+ else { throw!(SE::CompassAngleInvalid) }
+ }
+ }
+
+ impl From<CompassAngle> for u8 {
+ fn from(a: CompassAngle) -> u8 {
+ a.0
+ }
+ }
+
+ impl PieceAngle {
+ pub fn to_transform(self, pos: Pos) -> String {
+ match self {
+ PieceAngle::Compass(CompassAngle(angle)) => {
+ if angle == 0 { default() }
+ else { format!("rotate({}, {}, {})",
+ -45 * (angle as i16),
+ pos.0[0], pos.0[1]) }
+ }
+ }
+ }
+ }
+
impl<P: Eq + Hash> Default for Acl<P> {
fn default() -> Self { Acl { ents: default() } }
}
{%- for piece in uses %}
<use id="use{{ piece.id }}" href="#piece{{ piece.id }}"
x="{{ piece.pos[0] }}" y="{{ piece.pos[1] }}"
+ transform="{{ piece.transform }}"
data-piece="{{ piece.id }}" data-info="{{ piece.info | escape }}" />
{%- endfor %}
<g id="defs_marker"></g>