diff --git a/crates/benilla-app/assets/ui/OptionsFrame.xml b/crates/benilla-app/assets/ui/OptionsFrame.xml index d9ad6c90..77843a0c 100644 --- a/crates/benilla-app/assets/ui/OptionsFrame.xml +++ b/crates/benilla-app/assets/ui/OptionsFrame.xml @@ -693,9 +693,7 @@ DAYLIGHT_WINDOW_SPLIT = "Large city buildings take daylight through each window on its own instead of " .. "treating a whole wall of windows as one opening, so halls like the cathedral " - .. "catch the sun pane by pane. - -Applies to buildings loaded after the change. " + .. "catch the sun pane by pane.\n\nApplies to buildings loaded after the change. " .. "Off is the earlier look.", -- MONKEY (lampfog) LAMP_FOG = @@ -921,9 +919,7 @@ Applies to buildings loaded after the change. " DAYLIGHT_WINDOW_SPLIT = "Большие городские здания пропускают дневной свет через каждое окно отдельно, " .. "а не через всю стену окон как один проём, поэтому залы вроде собора " - .. "освещаются солнцем окно за окном. - -Действует для зданий, загруженных после " + .. "освещаются солнцем окно за окном.\n\nДействует для зданий, загруженных после " .. "изменения. Выключено — прежний вид.", -- MONKEY (lampfog) LAMP_FOG = diff --git a/crates/benilla-app/src/entities/spell_fx/lifecycle.rs b/crates/benilla-app/src/entities/spell_fx/lifecycle.rs index 3f4fbbfa..01e2c3ca 100644 --- a/crates/benilla-app/src/entities/spell_fx/lifecycle.rs +++ b/crates/benilla-app/src/entities/spell_fx/lifecycle.rs @@ -639,7 +639,8 @@ mod tests { // The ONSET: a firework's fuse. Dark for the whole flight, up at the detonation. let fuse = || SpellLight::new(1.0, 1.5, SpellLightMode::Burst { span: 0.6 }); assert_eq!(at(fuse(), 1.4), 0.0); - assert_eq!(at(fuse(), 1.5 + SPELL_LIGHT_RAMP), 1.0); + // MONKEY (integration): (1.5 + ramp) - 1.5 is not exactly `ramp` in f32. + assert!((at(fuse(), 1.5 + SPELL_LIGHT_RAMP) - 1.0).abs() < 1e-6); // The reap fade: a kit light goes dark on its instance's `FxDecay`, quicker than the // model's own Decay sequence — the aura is over the moment the server said so. diff --git a/crates/benilla-app/src/game_plugins.rs b/crates/benilla-app/src/game_plugins.rs index 65d3b818..06ab6b77 100644 --- a/crates/benilla-app/src/game_plugins.rs +++ b/crates/benilla-app/src/game_plugins.rs @@ -1089,7 +1089,15 @@ pub(crate) mod schedule_tests { /// the net drain's. Same tree, the class dropped, nothing else moved: 5,052 + 556. /// **5,554 (decision 2345)** — lowered: the TAB scan stopped reading `Visibility` (the draw /// election's verdict), which ended its pairs with every `Visibility` writer. - const UPDATE_ACTIONABLE_CEILING: usize = 5_554; + /// **5,582 (MONKEY integration round 3, graphics programme)** — measured on the merged tree + /// after declaring the orders the lanes had left out (wind → wetness → fog-model writers of + /// `MonkeyFrame`; the viewer publish before its new readers; the sky clock and sky tier before + /// the consume set; the grass benders on `GlobalTransform` and after the camera-pose copy). + /// What remains, read off the dump: 13 pairs of `skybox::animate_skyboxes` against the + /// `MatAnimTable` allocators (attach/spawn/doodad systems) — the allocator case of 2300, the + /// skybox writes only the rows it owns — and one pair each of the new systems against the two + /// exclusive systems (`finish_colliders`, `apply_net_updates`), the 2343 class. + const UPDATE_ACTIONABLE_CEILING: usize = 5_582; const UPDATE_ACTIONABLE_SLACK: usize = 40; fn ratchet(what: &str, n: usize, ceiling: usize, slack: usize) { diff --git a/crates/benilla-app/src/player.rs b/crates/benilla-app/src/player.rs index 3f69801e..4eb4830e 100644 --- a/crates/benilla-app/src/player.rs +++ b/crates/benilla-app/src/player.rs @@ -328,7 +328,11 @@ impl Plugin for PlayerPlugin { Update, (world_focus::publish_viewer, world_focus::publish_view_focus) .after(benilla_world::schedule::WorldStage::Input) - .before(benilla_world::schedule::WorldStage::Stream), + .before(benilla_world::schedule::WorldStage::Stream) + // MONKEY (integration): the `Viewer` readers the programme added (grass benders, + // fog model, zone skybox) run after this frame's publish. + .before(benilla_world::wind::WindTick) + .before(benilla_world::lighting::LightingResolveSet), ) .add_systems( Update, diff --git a/crates/benilla-app/src/shaders.rs b/crates/benilla-app/src/shaders.rs index 55ab58a5..26f0fb24 100644 --- a/crates/benilla-app/src/shaders.rs +++ b/crates/benilla-app/src/shaders.rs @@ -26,6 +26,8 @@ pub(crate) fn plugin(app: &mut App) { bevy::asset::embedded_asset!(app, "shaders/ui_slice_gamma.wgsl"); bevy::asset::embedded_asset!(app, "shaders/shadow_caster.wgsl"); bevy::asset::embedded_asset!(app, "shaders/shadow_caster_cutout_prepass.wgsl"); + // MONKEY (ao): the contact-shadow pass. + bevy::asset::embedded_asset!(app, "shaders/ssao.wgsl"); } #[cfg(test)] @@ -61,6 +63,6 @@ mod tests { `shaders::plugin`, or was registered from a file that is not directly under src/" ); } - assert_eq!(found, 7, "the game's shader set changed size"); + assert_eq!(found, 8, "the game's shader set changed size"); } } diff --git a/crates/benilla-app/src/sky_quality.rs b/crates/benilla-app/src/sky_quality.rs index e56503e7..0e54c03d 100644 --- a/crates/benilla-app/src/sky_quality.rs +++ b/crates/benilla-app/src/sky_quality.rs @@ -13,7 +13,11 @@ pub(crate) struct SkyQualityPlugin; impl Plugin for SkyQualityPlugin { fn build(&self, app: &mut App) { - app.init_resource::().add_systems(Update, bridge); + app.init_resource::().add_systems( + Update, + // MONKEY (integration): the tier lands before the sky and cloud readers. + bridge.before(benilla_world::lighting::LightingConsumeSet), + ); } } diff --git a/crates/benilla-app/src/ssao.rs b/crates/benilla-app/src/ssao.rs index 489ea9a7..c997cc96 100644 --- a/crates/benilla-app/src/ssao.rs +++ b/crates/benilla-app/src/ssao.rs @@ -112,10 +112,11 @@ impl Plugin for AmbientOcclusionPlugin { if !app.is_plugin_added::() { return; } + // MONKEY (integration): the crate's shader convention — embedded by `shaders::plugin`. let shader = app - .world_mut() - .resource_mut::>() - .add(Shader::from_wgsl(include_str!("shaders/ssao.wgsl"), "ssao.wgsl")); + .world() + .resource::() + .load("embedded://benilla_app/shaders/ssao.wgsl"); let Some(render_app) = app.get_sub_app_mut(RenderApp) else { return; }; diff --git a/crates/benilla-app/src/ui_script/verb_event_gate.rs b/crates/benilla-app/src/ui_script/verb_event_gate.rs index 8fa3f854..790fc0e1 100644 --- a/crates/benilla-app/src/ui_script/verb_event_gate.rs +++ b/crates/benilla-app/src/ui_script/verb_event_gate.rs @@ -166,6 +166,20 @@ const GAP: &[(&str, &str, &str)] = &[ nothing moves and `ui_char.rs`'s feed has nothing to announce — the stock SkillFrame \ would only repaint under its own Close", ), + // MONKEY (integration): the two temp-point verbs, the same case as `CancelSkillUps` (pre-existing + // upstream gap, surfaced once the Windows path fix let this gate run). + ( + "AddSkillUp", + "SKILL_LINES_CHANGED", + "the reference's helper 0x4d32d0 moves a temp point and fires; the temp-point table stays \ + empty in this model (`skills.rs`), and `ui_char.rs`'s feed fires on any real block change", + ), + ( + "RemoveSkillUp", + "SKILL_LINES_CHANGED", + "the reference's helper 0x4d3480, the same as AddSkillUp: no temp points to move here, and \ + `ui_char.rs`'s feed fires on any real block change", + ), ( "ClickTargetTradeButton", "TRADE_REPLACE_ENCHANT", @@ -279,7 +293,8 @@ fn quoted(text: &str, name: &str) -> bool { } fn short(p: &std::path::Path) -> String { - let s = p.to_string_lossy(); + // MONKEY (integration): Windows paths use a backslash; the rows are written with `/`. + let s = p.to_string_lossy().replace('\\', "/"); s.split_once("/crates/") .map_or(s.to_string(), |(_, t)| t.to_string()) } @@ -370,7 +385,7 @@ fn the_declared_rows_are_still_true() { } let named: Vec<&(PathBuf, String)> = src .iter() - .filter(|(p, _)| p.to_string_lossy().ends_with(file)) + .filter(|(p, _)| short(p).ends_with(file)) // MONKEY (integration): separator-agnostic .collect(); if named.is_empty() { wrong.push(format!( diff --git a/crates/benilla-app/src/ui_session.rs b/crates/benilla-app/src/ui_session.rs index 3ef4700b..a496e5d5 100644 --- a/crates/benilla-app/src/ui_session.rs +++ b/crates/benilla-app/src/ui_session.rs @@ -514,7 +514,8 @@ mod tests { // The chain's own source is the authority on what is wired — not a hand-copied list here, // which would be the very drift this test exists to catch. - let this_file = include_str!("ui_session.rs"); + // MONKEY (integration): a CRLF checkout must still find the function's closing brace. + let this_file = include_str!("ui_session.rs").replace("\r\n", "\n"); let chain = this_file .split_once("pub(crate) fn feed_interact_npc") .expect("feed_interact_npc") diff --git a/crates/benilla-world/src/sky_fx.rs b/crates/benilla-world/src/sky_fx.rs index e8462579..c76ec84e 100644 --- a/crates/benilla-world/src/sky_fx.rs +++ b/crates/benilla-world/src/sky_fx.rs @@ -91,7 +91,8 @@ impl Plugin for SkyFxPlugin { }); let quality = SkyQuality(SkyQuality::env_override().unwrap_or(0)); app.insert_resource(quality) - .add_systems(Update, tick_sky_clock); + // MONKEY (integration): the clock ticks before its readers (sky colours, cloud FX). + .add_systems(Update, tick_sky_clock.before(crate::lighting::LightingConsumeSet)); } } diff --git a/crates/benilla-world/src/skybox.rs b/crates/benilla-world/src/skybox.rs index bd750db4..f14b946b 100644 --- a/crates/benilla-world/src/skybox.rs +++ b/crates/benilla-world/src/skybox.rs @@ -196,6 +196,8 @@ impl Plugin for SkyboxPlugin { // After the lighting resolve, so the weight is this frame's crossfade, as the // fog's is. .after(crate::lighting::LightingResolveSet) + // MONKEY (integration): a `WowLighting` reader joins the consume set. + .in_set(crate::lighting::LightingConsumeSet) .in_set(SkyboxResolve), ) // Camera-anchored placement runs after propagation, off this frame's camera pose. diff --git a/crates/benilla-world/src/weather/wetness.rs b/crates/benilla-world/src/weather/wetness.rs index ebcf73df..1dcca38b 100644 --- a/crates/benilla-world/src/weather/wetness.rs +++ b/crates/benilla-world/src/weather/wetness.rs @@ -124,7 +124,15 @@ fn wetness_tick( pub(super) fn register(app: &mut App) { app.init_resource::() .init_resource::() - .add_systems(Update, wetness_tick.after(WeatherTick)); + // MONKEY (integration): after the wind rows and before the resolve, so the three + // `MonkeyFrame` writers run in one declared order. + .add_systems( + Update, + wetness_tick + .after(WeatherTick) + .after(crate::wind::WindTick) + .before(crate::lighting::LightingResolveSet), + ); } #[cfg(test)] diff --git a/crates/benilla-world/src/wind/mod.rs b/crates/benilla-world/src/wind/mod.rs index f1e811cf..47676254 100644 --- a/crates/benilla-world/src/wind/mod.rs +++ b/crates/benilla-world/src/wind/mod.rs @@ -207,8 +207,10 @@ fn update_wind( fn update_benders( quality: Res, viewer: Res, - viewer_unit: Query<&Transform, (With, With)>, - units: Query<(&Transform, &WorldUnit), Without>, + // MONKEY (integration): `GlobalTransform` (last propagation), not `Transform`, so the scan is + // not an undeclared order against every unit mover in `Update`. + viewer_unit: Query<&GlobalTransform, (With, With)>, + units: Query<(&GlobalTransform, &WorldUnit), Without>, mut frame: ResMut, ) { frame.bender_count = 0; @@ -220,7 +222,7 @@ fn update_benders( // no-avatar fallback that lets the parting instrument exercise the same receiver. let Some(player) = viewer .at - .or_else(|| viewer_unit.single().ok().map(|t| t.translation)) + .or_else(|| viewer_unit.single().ok().map(|t| t.translation())) else { return; }; @@ -231,7 +233,7 @@ fn update_benders( // unit just to retain seven entries. let mut nearby = [(f32::INFINITY, Vec3::ZERO, 1.5); 7]; for (transform, unit) in &units { - let at = transform.translation; + let at = transform.translation(); let d2 = (at.xz() - player.xz()).length_squared(); if d2 > 40.0 * 40.0 || d2 >= nearby[6].0 { continue; @@ -253,6 +255,11 @@ fn update_benders( } } +/// MONKEY (integration): the wind writers' set, so other `MonkeyFrame` writers (wetness) and the +/// viewer publish can order against it. +#[derive(SystemSet, Debug, Clone, Copy, PartialEq, Eq, Hash)] +pub struct WindTick; + /// Installs the shared field after this frame's weather ramp has resolved. pub struct WindPlugin; @@ -260,7 +267,18 @@ impl Plugin for WindPlugin { fn build(&self, app: &mut App) { app.init_resource::() .init_resource::() - .add_systems(Update, (update_wind, update_benders).after(WeatherTick)); + // MONKEY (integration): one ordered writer run of `MonkeyFrame`'s wind rows, before + // the lighting resolve (the fog model writes the same resource there). + .add_systems( + Update, + (update_wind, update_benders) + .chain() + .in_set(WindTick) + .after(WeatherTick) + // The camera pose copy also writes `GlobalTransform` (on the camera only). + .after(crate::view::publish_camera_pose) + .before(crate::lighting::LightingResolveSet), + ); } }