From d23f89183a4513cf90f70e0febeff7f0908b06f0 Mon Sep 17 00:00:00 2001 From: Ian Jackson Date: Fri, 12 Feb 2021 01:01:25 +0000 Subject: [PATCH] hidden: Provide recalculate_occultation Signed-off-by: Ian Jackson --- src/hidden.rs | 169 +++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 168 insertions(+), 1 deletion(-) diff --git a/src/hidden.rs b/src/hidden.rs index e4833c79..dfce8d16 100644 --- a/src/hidden.rs +++ b/src/hidden.rs @@ -7,6 +7,7 @@ use crate::imports::*; use slotmap::secondary; type OccK = OccultationKind; +type ONI = OldNewIndex; visible_slotmap_key!{ OccId(b'H') } @@ -70,7 +71,7 @@ impl PartialOrd for OccultationKind { } impl OccultationKind { - fn _at_all_visible(&self) -> bool { + fn at_all_visible(&self) -> bool { match self { OccK::Visible | OccK::Scrambled | @@ -204,3 +205,169 @@ pub fn massage_prep_piecestate( ) { // xxx hidden position involves adjusting pos and z and ??? here } + +// xxx prevent addpiece and removepiece in places that would be occulted +// xxx this means this only happens on ungrab I think ? + +#[throws(InternalError)] +pub fn recalculate_occultation Vec + Sync> + ( + gs: &mut GameState, + who_by: Html, + ipieces: &PiecesLoaded, + piece: PieceId, + vanilla_wrc: WhatResponseToClientOp, + vanilla_uo: PieceUpdateOp<(),()>, + vanilla_log: LF, + ) -> PieceUpdate +{ + // fallible part + let (update, occids): (_, OldNew>) = { + let nopiece = || internal_logic_error("piece vanished"); + let ipc = ipieces.get(piece).ok_or_else(nopiece)?; + let gpc = gs.pieces.get(piece).ok_or_else(nopiece)?; + struct Occulted<'o> { occid: OccId, occ: &'o Occultation } + + let occulteds: OldNew> = [ + gpc.occult.passive.map(|occid| Ok::<_,IE>( + Occulted { + occid, + occ: gs.occults.occults.get(occid).ok_or_else( + || internal_logic_error("uccultation vanished"))?, + } + )).transpose()?, + gs.occults.occults.iter().find_map(|(occid, occ)| { + if gpc.occult.active.is_some() { + // prevent occulting pieces being occulted + // (also prevents reflexive occultation) + return None + } else if occ.in_region(gpc.pos) { + Some(Occulted { occid, occ }) + } else { + None + } + }), + ].into(); + + let occids = occulteds.map(|h| h.as_ref().map(|occ| occ.occid)); + if occids.old() == occids.new() { + return (vanilla_wrc, vanilla_uo, vanilla_log()).into() + } + + /* + #[throws(IE)] + fn get_kind(gs: &GameState, occid: Option, player: PlayerId) + -> Option<(OccultationId, OccultationKind)> { + };*/ + + let mut situations: HashMap< + OldNew<&OccultationKind>, + Vec, + > = default(); + for (player, _gpl) in &gs.players { + situations + .entry( + occulteds.as_refs().map(|occulted| { + if let Some(occulted) = occulted { + occulted.occ.get_kind(player) + } else { + &OccK::Visible + } + })) + .or_default() + .push(player); + } + + let mut puos = SecondarySlotMap::new(); + let mut most_obscure = None; + + for (kinds, players) in &situations { + // For each player, the message obscuration is the least obscure. + // Then the overall obscuration is that from most afflicted player. + most_obscure = cmp::max( + most_obscure, + kinds.iter().map(Deref::deref).min() + ); + + let puo = match ( + kinds.old().at_all_visible(), + kinds.new().at_all_visible(), + ) { + (false, false) => None, + (false, true ) => Some(PUO::Insert(())), + (true, false) => Some(PUO::Delete()), + (true, true ) => Some(PUO::Modify(())), + }; + + if let Some(puo) = puo { + for player in players { + puos.insert(*player, puo); + } + } + } + + let describe_occulter = |oni| { + let h = occulteds[oni].as_ref().ok_or_else( + || internal_logic_error("most obscure not obscure"))?; + let piece = h.occ.occulter; + let ipc = ipieces.get(h.occ.occulter).ok_or_else( + || internal_logic_error( + format!("missing occulter piece {:?} for occid {:?}", + piece, h.occid) + ))?; + Ok::<_,IE>(ipc.describe_html(None)) + }; + + let most_obscure = most_obscure.unwrap_or(&OccK::Visible); // no players! + + let log = match most_obscure { + OccK::Visible => { + vanilla_log() + } + OccK::Scrambled | OccK::Displaced{..} => { + let face = ipc.nfaces() - 1; + let show = ipc.describe_html(Some(face.into())); + vec![ LogEntry { html: Html(format!( + "{} moved {} from {} to {}", + who_by.0, &show.0, + describe_occulter(ONI::Old)?.0, + describe_occulter(ONI::New)?.0, + ))}] + }, + OccK::Invisible => vec![ LogEntry { html: Html(format!( + "{} moved something from {} to {}", + who_by.0, + describe_occulter(ONI::Old)?.0, + describe_occulter(ONI::New)?.0, + ))}], + }; + + let update = PieceUpdate { + wrc: WRC::Unpredictable, + ops: PieceUpdateOps::PerPlayer(puos), + log, + }; + + let occids = occulteds.map(|h| h.as_ref().map(|h| h.occid)); + + (update, occids) + }; + + // point of no return + + // xxx shuffle some players' ids + // xxx and/or shuffle locations + + (||{ + let mut update_pieces = |oni, upd: &dyn Fn(&mut _)| { + if let Some(occid) = occids[oni] { + upd(&mut gs.occults.occults.get_mut(occid).unwrap().pieces); + } + }; + update_pieces(ONI::Old, &|opcs|{ opcs.remove(&piece); }); + update_pieces(ONI::New, &|opcs|{ opcs.insert(piece); }); + gs.pieces.byid_mut(piece).unwrap().occult.passive = *occids.new(); + })(); // <- no ?, infallible commitment + + update +} -- 2.30.2