//! Which keyless primitives have their paint position FORCED by occlusion? //! //! An opaque full-screen quad must sort below every element visible at any //! instant it is opaque. Where that set is *every* other element, its position is //! forced to first — derived from the file, not analogised from a neighbour. //! //! Controls, both measured in the running game and both reproduced here: //! * `palogo_eff0.prm` is measured painting FIRST — and comes out forced first. //! * `pteff00.prm` is measured painting LAST — and is forced below only a //! handful, so the constraint permits it on top. //! //! ⚠️ Assumes straight alpha-over blending. Blend mode is ❔ in //! `ui-prm-primitives.md`; an additive quad at alpha 255 would not occlude. use sylpheed_formats::{pak, ratc, ui_layout}; fn main() { let root = std::env::var("SYLPHEED_DISC").expect("set SYLPHEED_DISC to the extracted disc root"); let mut paks: Vec<_> = std::fs::read_dir(format!("{root}/dat")) .expect("dat/") .flatten() .map(|e| e.path()) .filter(|p| p.extension().and_then(|s| s.to_str()) == Some("pak")) .collect(); paks.sort(); let (mut forced, mut partial, mut free) = (0usize, 0usize, 0usize); let mut names: std::collections::BTreeMap = Default::default(); for p in &paks { let Ok(ar) = pak::PakArchive::open(p) else { continue; }; for e in ar.entries() { let Ok(by) = ar.read(e) else { continue }; if !ratc::is_ratc(&by) { continue; } let Some(b) = ui_layout::parse_build(&by) else { continue; }; let tmax = b .elements .iter() .flat_map(|el| el.keyframes.iter().filter_map(|k| k.time)) .max() .unwrap_or(0); if tmax == 0 { continue; } for el in &b.elements { if ui_layout::sprite_layer_key(&b, &by, el).is_some() { continue; } // full-screen only: a quad that does not cover cannot occlude if el.pivot_x * 2 < 1280 || el.pivot_y * 2 < 720 { continue; } let op: Vec = (0..=tmax) .filter(|&t| el.pose_at(t).map(|k| k.fade >> 24) == Some(255)) .collect(); if op.is_empty() { continue; } let others: Vec<&ui_layout::Element> = b.elements.iter().filter(|o| o.index != el.index).collect(); if others.is_empty() { continue; } let below = others .iter() .filter(|o| { op.iter() .any(|&t| o.pose_at(t).map(|k| k.fade >> 24).unwrap_or(0) > 0) }) .count(); let ent = names.entry(el.name.clone()).or_default(); ent.1 += 1; if below == others.len() { forced += 1; ent.0 += 1 } else if below > 0 { partial += 1 } else { free += 1 } } } } println!("keyless FULL-SCREEN primitives with an opaque interval:"); println!(" position FORCED FIRST (below every other element) : {forced}"); println!(" forced below SOME but not all : {partial}"); println!(" occludes nothing : {free}"); println!("\nby name — instances forced first / total:"); for (n, (f, t)) in &names { println!(" {n:>24} {f:>4} / {t}"); } }