Skip to content

Add image overlay support to possession mode lenses#4506

Merged
Loobinex merged 23 commits intodkfans:masterfrom
Cerwym:feature/lens-overlay
Jan 31, 2026
Merged

Add image overlay support to possession mode lenses#4506
Loobinex merged 23 commits intodkfans:masterfrom
Cerwym:feature/lens-overlay

Conversation

@Cerwym
Copy link
Contributor

@Cerwym Cerwym commented Jan 26, 2026

Implements overlay image rendering for lens effects

Overlays can be used with mist / distortion effects, they will be rendered AFTER such and won't be affected

This PR also as per feedback has been extended to allow for support of loading both overlays and mist effects from data, and fxdata and will allow for fallback into the base game's directory too.

Here's a full mod proving the setup.
test-pr-4506.zip

Test Scenarios

Creature Lens ID Test Case Expected Behavior
Bug 17 Mist from ZIP Loads mist_zip_test from lens_mists.zip
Fly 18 Mist from data folder Loads mist_data_test.raw from mod data/
Hell Hound 19 Overlay from ZIP Loads overlay_zip_test from lens_overlays.zip
Spider 20 Overlay from data folder Loads overlay_data_test.raw from mod data/ (vertical blue stripes)
Tentacle 21 Mist fallback Fails to find in ZIP, falls back to mist_fallback_test.raw in mod data/
Dragon 22 Overlay fallback Fails to find in ZIP, falls back to overlay_fallback_test.raw in mod data/ (checkerboard + red border)

Load Order Priority

The try_load_file_from_mods_with_fallback() helper function searches in this order:

  1. Mod ZIP files - Check each enabled mod's ZIP archives (via JSON registry)
  2. Mod data folders - Check each enabled mod's data/ or fxdata/ directories
  3. Base game data - Fallback to core game data/ or fxdata/ folders
    test-pr-4506.zip

Verification

To test, possess each creature in-game. Expected results:

  • Bug/Fly/Tentacle: Mist effects applied
  • Hell Hound: Overlay from ZIP visible
  • Spider: Blue vertical stripes overlay (pallete will render it as pink)
  • Dragon: Checkerboard pattern with red border overlay

Example of overlay and mist effect on a Dragon

image

Implements overlay image rendering for lens effects, allowing modders to add
custom visual overlays (helmets, visors, HUD elements) during creature possession.

Features:
- RAW (256x256, 8-bit indexed) and PNG/BMP format support
- Automatic mod directory detection and fallback to base game
- Palette index 0 treated as transparent
- Stretch-to-fit scaling for widescreen compatibility
- Overlay configuration via lenses.cfg: Overlay = filename.raw alpha

Files modified:
- src/config_lenses.c/h: Configuration parsing and data structures
- src/lens_api.c: Image loading and rendering implementation
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds support for defining and rendering an image overlay as part of a lens effect, intended to be drawn on top of existing mist/distortion lens rendering (e.g., for helmet/visor visuals in possession mode).

Changes:

  • Extends LensConfig with overlay fields (file, mod dir, in-memory image buffer, dimensions, alpha).
  • Adds config parsing for a new OVERLAY directive in lenses.cfg.
  • Implements overlay loading (RAW + PNG/BMP) and overlay drawing in the lens render pipeline.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 7 comments.

File Description
src/lens_api.c Loads overlay images and draws them after other lens effects.
src/config_lenses.h Extends LensConfig with overlay-related fields and adds LCF_HasOverlay.
src/config_lenses.c Adds parsing support for OVERLAY = <file> <alpha> and tracks mod-relative paths.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@benlp91
Copy link
Contributor

benlp91 commented Jan 26, 2026

Just let me know if I should test it

@benlp91 benlp91 linked an issue Jan 27, 2026 that may be closed by this pull request
@benlp91 benlp91 marked this pull request as draft January 27, 2026 12:29
@benlp91 benlp91 marked this pull request as ready for review January 27, 2026 12:29
@benlp91 benlp91 marked this pull request as draft January 27, 2026 12:29
@benlp91
Copy link
Contributor

benlp91 commented Jan 27, 2026

I forgot how to trigger a Prototype lol @Loobinex please can you do?

@PieterVdc PieterVdc marked this pull request as ready for review January 27, 2026 12:32
@PieterVdc PieterVdc marked this pull request as draft January 27, 2026 12:32
@PieterVdc
Copy link
Member

I forgot how to trigger a Prototype lol @Loobinex please can you do?

it just needed approval because Cerwym isn't maintainer

@Cerwym Cerwym marked this pull request as ready for review January 27, 2026 13:54
@Cerwym
Copy link
Contributor Author

Cerwym commented Jan 27, 2026

I forgot how to trigger a Prototype lol @Loobinex please can you do?

it just needed approval because Cerwym isn't maintainer

What with me being new and untrustworthy and all that

…ook at zip, then mod data, then core data in case of not finding the file
…tion

- Created try_load_file_from_mods_with_fallback() helper function
- Reduces code duplication for mod->base game file loading pattern
- Applied to lens overlay loading as proof of concept
- Helper function can be reused for other mod fallback scenarios
- Mist files now check mods' data directories before base game
- Consistent mod fallback behavior between overlays and mists
- Better logging to show which mod provided the mist file
…ence

- Changed from checking fx_data flag to mod_dir flag (allows any mod directory)
- Added LbFileExists() check before LbFileLoadAt() to avoid error spam
- Fixes crash when loading files that don't exist in all mods
- Prevents access violations from failed file loads
@Loobinex Loobinex marked this pull request as draft January 30, 2026 18:31
@Loobinex Loobinex marked this pull request as ready for review January 30, 2026 18:31
Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 11 out of 12 changed files in this pull request and generated 9 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

src/config.c Outdated
Comment on lines 1050 to 1053
// Set the terminator at the actual count position (not at max_count-1)
if (named_fields_set->names != NULL && named_fields_set->count_field != NULL)
{
named_fields_set->names[*named_fields_set->count_field].name = NULL;
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

parse_named_field_blocks() sets the terminator at names[*count_field], but names is only sized to max_count entries in callers (e.g., lenses_desc[LENS_ITEMS_MAX]). If a config defines block index max_count-1, count_field becomes max_count and this writes one past the end. Either allocate names arrays as max_count+1 (space for sentinel) or clamp the terminator write to max_count-1 and adjust semantics accordingly.

Suggested change
// Set the terminator at the actual count position (not at max_count-1)
if (named_fields_set->names != NULL && named_fields_set->count_field != NULL)
{
named_fields_set->names[*named_fields_set->count_field].name = NULL;
// Set the terminator safely within the allocated range
if (named_fields_set->names != NULL && named_fields_set->count_field != NULL)
{
int terminator_index = *named_fields_set->count_field;
if (terminator_index >= named_fields_set->max_count)
{
terminator_index = named_fields_set->max_count - 1;
}
named_fields_set->names[terminator_index].name = NULL;

Copilot uses AI. Check for mistakes.
src/lens_api.c Outdated

void setup_eye_lens(long nlens)
{
WARNLOG("CONFIG_DEBUG: setup_eye_lens called with nlens=%ld", nlens);
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

setup_eye_lens() currently logs "CONFIG_DEBUG" via WARNLOG on every invocation. Since this can happen frequently during play, please switch to SYNCDBG (or gate behind BFDEBUG_LEVEL) so release logs aren’t polluted with warning-level messages.

Copilot uses AI. Check for mistakes.
Comment on lines +271 to +274
}

if (LbFileLoadAt(fname, cache->data) != size) {
WARNLOG("Failed to load overlay file '%s' from /data directory", lenscfg->overlay_file);
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When loading a ".raw" overlay file, this code calls LbFileLoadAt() directly. LbFileLoadAt() reads the full file length into the buffer, so without checking LbFileLengthRnc(fname) first, an oversized file can overflow cache->data before the return-value check runs. Validate size (or reuse try_load_file_from_mods_with_fallback) before loading.

Copilot uses AI. Check for mistakes.
Comment on lines +4288 to +4290
// Apply lens effect to the viewport area only (not including sidebar)
draw_lens_effect(lbDisplay.WScreen + view_x, lbDisplay.GraphicsScreenWidth,
scrmem + view_x, eye_lens_width, view_width, view_height, game.applied_lens_type);
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

draw_lens_effect() is applied only with a +view_x offset. If player->engine_window_y is ever non-zero (eg centered/shifted view), the lens effect will be applied to the wrong region and the buffer pointers will be incorrect. Include view_y and offset dst/src by (view_y * pitch) + view_x.

Copilot uses AI. Check for mistakes.
Comment on lines 116 to 118
SYNCLOG("value_overlay called: argnum=%d, value='%s', lens=%d", named_field->argnum, value_text, idx);

if (value_text == NULL || value_text[0] == '\0') {
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

value_overlay() uses SYNCLOG for routine parsing traces. This is very noisy during normal config loading; please switch these to SYNCDBG at an appropriate level (or remove once validated) so logs aren’t polluted in non-debug runs.

Copilot uses AI. Check for mistakes.
#endif
/******************************************************************************/
#define LENS_ITEMS_MAX 32
#define LENS_ITEMS_MAX 255
Copy link

Copilot AI Jan 30, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Lenses are indexed starting at 1 (0 is the default entry), and get_lens_config() returns &lenses[lens_idx]. With LENS_ITEMS_MAX set to 255, the highest valid index is 254, but lenses_count can become 255 (from lens254) and an eye_effect of 255 would access lenses[255] out of bounds. If the intent is to support IDs up to 255, LENS_ITEMS_MAX should be 256 (or adjust indexing/count logic to keep indices < LENS_ITEMS_MAX).

Suggested change
#define LENS_ITEMS_MAX 255
#define LENS_ITEMS_MAX 256

Copilot uses AI. Check for mistakes.
@Loobinex Loobinex marked this pull request as draft January 31, 2026 01:14
@Loobinex Loobinex marked this pull request as ready for review January 31, 2026 13:24
@Loobinex Loobinex merged commit 810ea10 into dkfans:master Jan 31, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Allow custom lens decals for a particular creature model

4 participants