fn op(&self, a: ApiPieceOpArgs) -> PieceUpdate {
let ApiPieceOpArgs { gs,piece, .. } = a;
let gpc = gs.pieces.byid_mut(piece)?;
- op_do_set_z(gpc, gs.gen, &self.z)?;
+ api_op_set_z(gpc, gs.gen, &self.z)?.implement(gpc);
let update = PieceUpdateOp::SetZLevel(());
(WhatResponseToClientOp::Predictable,
update, vec![]).into()
let gpc = a.gs.pieces.byid_mut(a.piece)?;
if gpc.held != None { throw!(Ia::PieceHeld) }
if ! (self.z > gpc.zlevel.z) { throw!(Ia::BadPieceStateForOperation); }
- op_do_set_z(gpc, a.gs.gen, &self.z)?;
- a.ipc.show(y).op_multigrab(a, y, self.n, &self.z).map_err(|e| match e {
- // TODO: The error handling is wrong, here. If op_multigrab
- // returns a deferred thunk, the APOE::PartiallyProcessed will
- // not be applked.
- APOE::Inapplicable(ia) => APOE::PartiallyProcessed(ia, vec![]),
- other => other,
- })?
+ let new_z = api_op_set_z(gpc, a.gs.gen, &self.z)?;
+ a.ipc.show(y).op_multigrab(a, y, self.n, new_z)?
}
}
}
#[throws(ApiPieceOpError)]
fn op_multigrab(&self, _: ApiPieceOpArgs, show: ShowUnocculted,
- take: MultigrabQty, new_z: &ZCoord) -> OpOutcomeThunk {
+ take: MultigrabQty, new_z: ShouldSetZLevel) -> OpOutcomeThunk {
let currency = self.currency.clone();
- let new_z = new_z.clone();
OpOutcomeThunk::Reborrow(Box::new(
move |ig: &mut InstanceGuard, player: PlayerId, tpiece: PieceId|
{
#[throws(ApiPieceOpError)]
pub fn fastsplit_split<I>(
&mut self, player: PlayerId,
- tpiece: PieceId, show: ShowUnocculted, new_z: ZCoord,
+ tpiece: PieceId, show: ShowUnocculted, new_z: ShouldSetZLevel,
implementation: I
) -> UpdateFromOpComplex
where I: FnOnce(&IOccults, &GameOccults, &GPlayer,
pos: tgpc.pos,
face: tgpc.face,
held: None,
- zlevel: ZLevel { z: new_z, zg: ig.gs.gen },
+ zlevel: tgpc.zlevel.clone() /* placeholder, overwritten below */,
pinned: tgpc.pinned,
occult: default(),
angle: tgpc.angle,
rotateable: tgpc.rotateable,
fastsplit: tgpc.fastsplit,
};
+ new_z.implement(&mut ngpc);
let tipc_p = (||{
let p = tipc.p.show(show);
//
// So the multigrab operation specifies a ZCoord.
fn op_multigrab(&self, _a: ApiPieceOpArgs, _show: ShowUnocculted,
- _qty: MultigrabQty, _new_z: &ZCoord)
+ _qty: MultigrabQty, _new_z: ShouldSetZLevel)
-> Result<OpOutcomeThunk,ApiPieceOpError> {
Err(Ia::BadPieceStateForOperation)?
}
// ---------- 2-phase Z level setting ----------
+/// Drop this only on errors; don't just forget it
+#[derive(Debug, Clone)]
+pub struct ShouldSetZLevel(ZLevel);
+
+/// Prepare to set the z level of gpc to z
+///
+/// If this is a good idea, returns a ShouldSetZLevel.
+/// That should be [`implement`](ShouldSetZLevel::implement)ed
+/// along with the the rest of the operation, when committing.
+///
+/// This allows us to do all of an operation's checks and preparation,
+/// including Z level setting, first, and then only set the Z level
+/// infallibly at the end.
#[throws(ApiPieceOpError)]
-pub fn op_do_set_z(gpc: &mut GPiece, gen: Generation, z: &ZCoord) {
- if gpc.occult.is_active() {
- if z >= &gpc.zlevel.z { throw!(Ia::Occultation) }
- }
- gpc.zlevel = ZLevel { z: z.clone(), zg: gen };
+pub fn api_op_set_z(gpc: &mut GPiece, gen: Generation, z: &ZCoord)
+ -> ShouldSetZLevel
+{
+ if gpc.occult.is_active() {
+ if z >= &gpc.zlevel.z { throw!(Ia::Occultation) }
+ }
+ ShouldSetZLevel(ZLevel { z: z.clone(), zg: gen })
+}
+
+impl ShouldSetZLevel {
+ pub fn implement(self, gpc: &mut GPiece) {
+ gpc.zlevel = self.0;
+ }
+
+ pub fn inspect(&self) -> &ZLevel { &self.0 }
}
// ---------- log expiry ----------