default()
))
})}))}
+
+ #[throws(IE)]
+ fn held_change_hook(&self,
+ _ig: &InstanceRef,
+ gplayers: &GPlayers,
+ ipieces: &IPieces,
+ goccults: &GOccults,
+ gpieces: &mut GPieces,
+ tpiece: PieceId,
+ was_held: Option<PlayerId>)
+ -> OpHookThunk {
+ let missing_e = || internal_error_bydebug(&(was_held, tpiece));
+
+ let tself = self;
+ let tgpc = gpieces.get(tpiece).ok_or_else(missing_e)?;
+ let tipc = ipieces.get(tpiece).ok_or_else(missing_e)?;
+
+ if_let!{ Some(player) = was_held; else return Ok(default()) }
+ if tgpc.held.is_some() { /*wat*/ return default(); }
+ let gpl = gplayers.get(player);
+
+ // Occultation is not yet supported here. When implementing
+ // occultation, delete this and fix all the things.
+ let show = ShowUnocculted::new_visible();
+
+ let merge_with = gpieces.iter().filter_map(|(mpiece, mgpc)|{
+ if mpiece == tpiece { throw!() }
+ let mipc = ipieces.get(mpiece)?;
+
+ // Our position is within its bbox
+ if ! mipc.show(show).abs_bbox(mgpc).ok()?.contains(tgpc.pos) { throw!() }
+
+ // It's a banknote
+ let mself: &Banknote = mipc.p.show(show)
+ .downcast_piece_fastsplit().ok()?;
+
+ // Of our currency
+ if mself.currency != tself.currency { throw!() }
+ let currency = &mself.currency;
+
+ if mgpc.occult.passive_occid().is_some() {
+ // We don't do occultation yet. But, anyway, we don't want to
+ // deal with this since it might mean we're totally invisible
+ // to our player! When we do support this, call
+ // Occultation::get_kind ?
+ throw!();
+ }
+
+ // We are in the ellipse inscribed in its bbox
+ let delta = (tgpc.pos - mgpc.pos).ok()?.promote();
+ let bbox_sz = mipc.show(show).bbox_approx().ok()?;
+ let dist2: f64 = (0..2).map(|i| {
+ // The bbox may not be centred. We imagine a quarter ellipse
+ // inscribed in each corner, with the centre at the nominal position.
+ let delta = delta.coords[i];
+ let cnr = if delta < 0. { bbox_sz.tl() } else { bbox_sz.br() };
+ let rel = delta / (cnr.coords[i] as f64);
+ rel*rel
+ }).sum();
+ if ! (dist2 <= 1.) { throw!() }
+
+ Some((mpiece,mgpc,currency))
+ });
+
+ if_let!{
+ Some((mpiece,mgpc,currency)) =
+ merge_with.at_most_one().ok().flatten();
+ else return Ok(default());
+ }
+
+ let tqty = tgpc.xdata_exp::<Value>()?.qty;
+ let mqty = mgpc.xdata_exp::<Value>()?.qty;
+ if_let!{
+ Some(new_qty) = mqty.checked_add(tqty);
+ else return Ok(default()); // arithmetic overflow!
+ }
+
+ let logents = vec![ LogEntry { html: hformat!(
+ "{} deposited {} {}{}, giving {}{}",
+ match gpl {
+ Some(gpl) => gpl.nick.to_html(),
+ None => Html::lit("Departing player").into(),
+ },
+ tipc.p.show(show).describe_html(tgpc, goccults)?,
+ tqty, currency,
+ new_qty, currency,
+ )}];
+
+ OpHookThunk::Reborrow(Box::new(move |igg: &mut InstanceGuard, (_player,)| {
+
+ let (puo, uu_d) = igg.fastsplit_delete(show, tpiece, &logents)?;
+ // commitment point
+ Ok(((move ||{
+ let ig = &mut **igg;
+
+ let () = (||{
+ // None of these situations ought to happen, really, but the
+ // callback structure means it isn't 100% possible to rule them out.
+ let mgpc = ig.gs.pieces.get_mut(mpiece).ok_or("tpiece vanished")?;
+ let mvalue = mgpc.xdata_mut_exp::<Value>().map_err(|_|"xdata vanished")?;
+ mvalue.qty = mvalue.qty.checked_add(tqty).ok_or("overflow")?;
+ if mvalue.qty != new_qty { throw!("modified value") }
+ Ok::<_,&'static str>(())
+ })().unwrap_or_else(|m|{
+ warn!("during dorp-and-merge of currency {tpiece:?} into {mpiece:?}: {m}");
+ });
+
+ vec![Box::new(move |prepub: &mut PrepareUpdatesBuffer| {
+ prepub.piece_update_image(mpiece, &None).unwrap_or_else(
+ |e| error!("currency image update failed: {} {:?}", &e, &e));
+ prepub.piece_update(tpiece, &None, puo.into());
+ prepub.log_updates(logents);
+ prepub.add_unprepared(uu_d);
+ }) as _]
+
+ }))()) // <- no ?
+ }))}
}
(t_pu, unprepared)
})()// <- no ?, infallible (to avoid having not completed implementation
}
+
+ #[throws(IE)]
+ pub fn fastsplit_delete(&mut self,
+ show: ShowUnocculted,
+ piece: PieceId,
+ _proof_that_caller_handles_logging: &Vec<LogEntry>)
+ -> (PieceUpdateOp<(),()>, UnpreparedUpdates)
+ {
+ let missing_e = || internal_error_bydebug(&piece);
+
+ // See reasoning in fastsplit_split
+ let modperm = self.modify_pieces_not_necessarily_saving_aux();
+
+ let gpc = self.gs.pieces.get(piece).ok_or_else(missing_e)?;
+ let ipc = self.ipieces.get(piece).ok_or_else(missing_e)?;
+ let _p: &Piece = ipc.p.show(show).downcast_piece()?;
+
+ let _fsid: &FastSplitId = gpc.fastsplit.as_ref()
+ .ok_or_else(|| internal_error_bydebug(gpc))?;
+ // We allow merging things with different FastSplitIds. The
+ // FastSplitId identifies not the "kind of piece", but simply
+ // ancestry. For example, all banknotes split from the same
+ // original note will have the same id, but otherwise, different
+ // banknotes even of the same currency have different ids.
+
+ match ToRecalculate::with(|mut to_permute| {
+ let r = self.delete_piece(modperm,&mut to_permute, piece,|_,_,_,_|());
+ (r, to_permute.implement(self))
+ }) {
+ (Err(e), uu_p) => {
+ PrepareUpdatesBuffer::only_unprepared(self, uu_p);
+ throw!(e);
+ },
+ (Ok(((), puo, uu_d)), uu_p) => {
+ (puo, chain!(uu_d, uu_p).collect())
+ },
+ }
+ }
}
impl IFastSplits {