The latest update for the indie game *Shogun Exodus* introduces two major visual features designed to enhance graphical depth without compromising performance: building parallax and floor reflections. Update 139 adds these toggleable, lightweight systems, allowing players to experience enhanced environmental detail through a dynamic front-shell parallax effect for castle structures and stylized, fake reflections for pawns and placeables. Both features are highly optimized with shared materials, off-screen culling, and cached textures to ensure smooth gameplay even with high object counts.
In addition to the visual upgrades, this update brings robust settings configuration, enabling players to toggle both options independently from the Start screen or Pause menu. Modders can also customize these features via JSON configuration files. The release includes several hotfixes and fine-tuning passes aimed at perfecting the reflection shader, ensuring reflections scale properly, fade correctly within floor boundaries, and disable dynamically when characters climb stairs or become airborne.
Raw Patch Notes
UPDATE 2026-06-30 #139 - Building Parallax + Floor Reflections - Added two visual-only, toggleable, moddable graphics features. Both default ON and are independent on/off toggles in Settings (Start screen + Pause menu). Built lightweight for large pawn/placeable counts (shared materials, reused cached textures, off-screen culling). - Castle parallax: generated buildings now route their front structural-shell VISUALS (separators/pillars, door art, end-wall art, roof, underground) into a new visual-only `FrontParallaxLayer`; collisions, knobs, rooms, and all gameplay nodes stay put. A per-building `BuildingParallaxController` (created at runtime, idle in the editor) drifts that one layer horizontally (and slightly vertically) relative to the camera, so the shell reads as a touch closer than the room interiors. Camera-relative, so it survives the in-progress camera zoom/anchor fix and a future free-moving camera. Strength is conservative and clamped; live toggle (no restart needed). Per-theme override via `visuals.parallax` in `building_style_themes.json`. - Floor reflections: stylized fake reflections for pawns and placeables. A shared `Art/Effects/FloorReflection.gdshader` does the low-opacity vertical fade + perspective corner-skew (trapezoid) on a `CanvasGroup`; the vertical flip/squash is done by the node transform. Pawns use `CharacterReflection`, which builds a mirror `CharacterVisual` from the same appearance recipe (reusing the appearance service's cached render plan/textures — no extra GPU memory) and copies the source's live animation transforms each frame via new `CharacterVisual.copy_animation_state_to()`. Placeables use `PlaceableReflection`, mirroring the baked sprite beneath its floor-contact point and refreshing with the placeable's visual. Reflections are culled when their owner is well off-screen and freed of per-frame cost when the setting is off; they never affect clicks, collision, saves, AI, or placement. - New reusable base `FloorReflectionBase` centralizes the surface/material/culling/settings plumbing so future reflective types can reuse it. Reflection shader params (opacity, near/far alpha, fade_invert, perspective_narrow) and shape (squash, z) are Inspector-tunable per node; if a setup's fade lands on the wrong end, flip `fade_invert` instead of editing the shader. - Settings: new `parallax_enabled` key in `SettingsManager` (default true) with `is/set_parallax_enabled`; the pre-existing `reflections_enabled` placeholder is now wired to the live reflection nodes. - Files: `Art/Effects/FloorReflection.gdshader` (new), `Scripts/Visual/FloorReflectionBase.gd` (new), `Scripts/Visual/CharacterReflection.gd` (new), `Scripts/Visual/PlaceableReflection.gd` (new), `Scripts/Buildings/BuildingParallaxController.gd` (new), `Scripts/Buildings/BuildingSpawner.gd`, `Scripts/Characters/CharacterVisual.gd`, `Scripts/Placeables/PlaceableBase.gd`, `Autoload/SettingsManager.gd`, `Scripts/UI/StartScreenMenu.gd`, `Scripts/UI/PauseMenu.gd`, `Scenes/Actors/Player.tscn`, `Scenes/Actors/AI/BasicAI.tscn`, `Scenes/Placeables/PlaceableBase.tscn`, `Scenes/StartScreen.tscn`, `Scenes/UI/PauseMenu.tscn`, `Config/Buildings/Styles/building_style_themes.json`, `Data/LoadingScreenText.json`, `current-game-setup.txt`, `mod-guide.txt`, `update-log.txt` - Fix (reflections rework): the initial CanvasGroup-composited reflection rendered as a solid white box (the custom shader sampled an unset texture, not the composite) and extended far past the floor. Reworked to apply the shared shader PER mirrored sprite (each samples its own real texture), fade in WORLD space anchored at the floor contact line (`contact_world_y` + `fade_span`, set per reflection each frame) so it dies out within the floor band, and use stronger default squash/perspective. Removed the `CanvasGroup` and the `fade_invert` uniform; added `fade_span`. `CharacterVisual.apply_material_to_sprites()` shares one reflection material across a mirror's group sprites. - Fix (reflections tuning pass): (1) shortened the default fade so the reflection dies within the floor band instead of spilling into rooms below (`fade_span` 320->190); (2) reversed the perspective — it now WIDENS with depth (spreading outward from the object's center in the vertex shader via `center_world_x`) so it grows toward the viewer like the faked floor, instead of the previous UV-remap that narrowed and cropped (replaced `perspective_narrow` with `perspective_widen`); (3) added an optional per-placeable `reflection` override block in `placeables.json` (enabled/opacity/fade_span/perspective_widen/squash/alpha) so pseudo-3D or asymmetrical placeables can be toned down or disabled — base `large_container` now uses a subtle, nearly-straight, fainter reflection. `update_anchor()` replaces `update_contact_world_y()`. - Tuning: tightened reflection defaults so it stays on the floor plating and reads more strongly toward the viewer — `fade_span` 190->130, `perspective_widen` 0.7->1.2 (in `Scripts/Visual/FloorReflectionBase.gd`). Per-placeable overrides (e.g. `large_container`) still win. - Fix (reflections, floor clamp + stairs): the fade was computed as a vertex varying, so it was clamped only at the quad corners and always stretched across the full squashed reflection height — `fade_span` had no visible effect and the reflection reached the floor below. Moved the fade to a PER-FRAGMENT world-position calc so `fade_span` now sets the exact depth it dies at. Also reflections now hide while an actor is climbing stairs or airborne (read from `get_animation_context()` climb_active/on_floor), so they only show on the floor. Bumped `perspective_widen` default to 2.5 (cap 4.0) for a stronger grow-toward-viewer. - Polish (reflections): added `contact_offset_y` (default -5) on `FloorReflectionBase` to nudge the reflection up so it connects with the owner's feet/base; and fixed a diagonal-smear bug where a walking pawn's body lean (rotation) was amplified by `perspective_widen` into a sideways skew — `CharacterVisual.copy_animation_state_to()` now takes `zero_rotation` (used by pawn reflections) so the reflection keeps its bob/scale but stays upright beneath the pawn. - Fix (reflection lean, real cause): the sideways skew was mainly the perspective widen being centered on the feet/collision origin (center_world_x) rather than the body's true horizontal center — so the body sat off-axis and skewed, flipping direction with facing (not primarily the walking lean). `CharacterReflection` now anchors center_world_x on the body group's world X (bottom-center anchor, tracks facing) while keeping contact Y at the feet; combined with the upright (zero_rotation) copy the reflection now sits straight beneath the pawn regardless of facing/motion. - Fix (reflection skew, robust): the widen was spreading content about a world-space center X (center_world_x) that was hard to pin to the real body center, so the reflection sheared to one side and flipped with facing. Rewrote the widen to spread each mirrored sprite about ITS OWN texture center (via TEXTURE_PIXEL_SIZE) in the vertex shader — symmetric and straight beneath the owner with no world-center guess. Removed the center_world_x uniform/plumbing; `update_anchor()` now only sets contact_world_y. Also silenced an unrelated unused-parameter warning in CharacterAppearanceService `_resolve_body_part_attachment` (slots_cfg -> _slots_cfg). - Fix (reflection shader robustness): the previous shader used vertex-stage `TEXTURE_PIXEL_SIZE`/`inverse(MODEL_MATRIX)` for the widen, which can fail to compile in the vertex function — when it does, Godot silently keeps the last-good shader, so edits appeared to do "nothing." Rewrote to a minimal shader that only uses always-available built-ins: the widen is now a symmetric horizontal magnify about each sprite's UV center in the fragment stage. `perspective_widen = 0` gives a plain flip+squash+fade mirror.
*This is an automated post, using Python and LLMs to summarize game updates. Mistakes, while rare, may happen.

Reply