Cover Image

[Devlog] Kingdom Come: Deliverance II - Never Lose That Canker's Mace Again!

Alright folks, time for another peek into the modding workshop. This time, our subject is the magnificent, sprawling world of Kingdom Come: Deliverance II (KCD2). My latest adventure wasn't chasing down memory addresses for a TPV toggle, but rather scratching a very practical itch: the eternal struggle of finding loot. And yeah, the specific catalyst was spending an entire, rather frustrating, day combing the Bohemian countryside for that damned "Canker's Mace." 🤣 There had to be a better way.

We've all been there after a hectic skirmish: that crucial sword or valuable shield skitters away, only to vanish into the dense foliage or the dim light of a dungeon floor.

The classic struggle: a dropped weapon nearly invisible in the dense undergrowth.
The classic struggle: a dropped weapon nearly invisible in the dense undergrowth.



This very frustration led to the "Loot Beacon" mod – a little Lua-scripted helper to ensure Henry (and by extension, me) never misses a valuable drop, a hidden corpse, or an important herb again. With a simple keypress, elusive items become clear.

With Loot Beacon enabled, the same weapon is now clearly highlighted and easy to spot.
With Loot Beacon enabled, the same weapon is now clearly highlighted and easy to spot.

This is a different beast from deep C++ DLL hacking; KCD2, like its predecessor, offers a robust Lua scripting system, which is what this mod primarily leverages. Warhorse Studios has even provided some official modding guidance, and for those diving into script binds, I've hosted the extracted WH scriptbind documentation here.

For those interested in trying it out, the Loot Beacon mod can be found on NexusMods and the Steam Workshop:

The Core Idea: Light 'Em Up!

The goal was simple: press a key, and have nearby lootable objects highlight themselves with a visual cue. In CryEngine (and its KCD2 fork), this usually means particle effects. The engine is fantastic at particles, so the plan was to:

  1. Detect nearby entities.
  2. Filter them to find interesting ones (items, corpses, specific custom entities).
  3. Attach a particle effect "beacon" to them.
Light 'Em Up
Light 'Em Up

This sounds straightforward, but as always, the devil is in the details, especially when dealing with entity types and game states.

Mod Structure: A Modular Approach

I decided to structure the mod into several Lua modules for clarity and maintainability, all loaded by a main loot_beacon.lua bootstrap script:

  • core.lua: Handles overall mod initialization, versioning, and acts as a central point.
  • logger.lua: A simple logging utility for debugging (essential!).
  • config.lua: Manages all configurable settings (keybinds, colors, detection radius, etc.), loadable from mod.cfg.
  • entity_detector.lua: The brains of the operation – scans for entities, filters by type, and checks for lootability.
  • highlighter.lua: Attaches and removes the particle effects from detected entities.
  • command_registry.lua: Sets up console commands for controlling the mod.
  • event_handler.lua: Listens to game events (like game pause/resume) to manage highlights safely.
  • ui_manager.lua: Provides on-screen notifications about what's been found.

The bootstrap (loot_beacon.lua) just ensures all these are loaded in the correct order:

Snippet from loot_beacon.lua
function LootBeacon_LoadModules()
    local modPath = "Scripts/LootBeacon"
    local moduleFiles = { -- Order matters for dependencies
        "/core.lua", "/logger.lua", "/config.lua",
        "/entity_detector.lua", "/highlighter.lua",
        "/command_registry.lua", "/event_handler.lua", "/ui_manager.lua"
    }
    for _, file in ipairs(moduleFiles) do
        Script.LoadScript(modPath .. file) -- Error handling omitted for brevity
    end
    return true
end
 
if LootBeacon_LoadModules() then LootBeacon.Core:initialize() end

Detecting What's Worth a Beacon

The EntityDetector is where much of the specific game logic comes in. It uses System.GetEntitiesInSphere(playerPos, radius) to get all nearby entities, then iterates through them.

Snippet from entity_detector.lua (simplified)
function LootBeacon.EntityDetector:detectEntities()
    self:resetResults()
    local playerPos = player:GetPos()
    local radius = LootBeacon.Config.detectionRadius
    local allEntities = System.GetEntitiesInSphere(playerPos, radius)
 
    for _, entity in pairs(allEntities) do
        self:processEntity(entity)
    end
    return self.results
end
 
function LootBeacon.EntityDetector:processEntity(entity)
    if not entity or entity:IsHidden() then return end
 
    -- Metadata for things like if stealing is required, or if corpse is illegal to loot
    self.results.metadata[entity.id] = { requires_stealing = false, illegal_corpse = false, ... }
 
    if self:isCustomEntityClass(entity.class) then
        table.insert(self.results.custom, entity)
    elseif entity.actor then -- NPCs and Animals
        if entity.actor:IsDead() or (Config.treatUnconsciousAsDead and entity.actor:IsUnconscious()) then
            if entity.human then table.insert(self.results.corpses, entity)
            else table.insert(self.results.animals, entity) end
            if entity.soul and not entity.soul:IsLegalToLoot() then
                 self.results.metadata[entity.id].illegal_corpse = true
            end
        end
    elseif entity.class == self.ENTITY_CLASS_PICKABLE then
        local isPickable, requiresStealing = self:getItemPickabilityInfo(entity)
        if isPickable then
            table.insert(self.results.items, entity)
            if requiresStealing then self.results.metadata[entity.id].requires_stealing = true end
        end
    end
end

Getting item pickability involves checking item:CanPickUp(player.id) and item:CanSteal(player.id). A crucial filter was skipping items with empty UI names (often NPC-only inventory items) or items currently in use. Custom entities like "Nest" or "Stash" (for chests) are handled via a configurable list.

Making Things Glow: Particles and Accessibility

The Highlighter module takes the detected entities and uses entity:LoadParticleEffect(-1, effectPath, {}) to attach the visual beacons. These are defined in a loot_beacon.xml particle library.

graph TD
    subgraph "Loot Beacon Activation Flow"
        A[Hotkey Pressed] --> B{Command Registry};
        B --> C["LootBeacon.Highlighter:activateHighlights()"];
        C --> D["LootBeacon.EntityDetector:detectEntities()"];
        D --> E["System.GetEntitiesInSphere()"];
        E --> F["Filter & Classify Entities
(Items, Corpses, Animals, Custom)"]; F --> G["Highlighter applies
Particle Effects (entity:LoadParticleEffect)"]; G --> H["Visual Beacons Appear in Game"]; C --> I["LootBeacon.UIManager:showHighlightResults()"]; I --> J["On-Screen Notification"]; C --> K["Script.SetTimer for Auto-Removal"]; end classDef default fill:#282a36,stroke:#f8f8f2,stroke-width:2px,color:#f8f8f2; class A,B,C,D,E,F,G,H,I,J,K default;

Initially, my go-to color for items was a bright red particle effect (loot_beacon.pillar_red). This seemed fine until I received some valuable feedback:

"Am colorblind red is barely noticeable"
"Am colorblind red is barely noticeable"

This was a classic oversight on my part. Color accessibility is incredibly important. The user pointed out that red is a common problem for colorblind individuals and suggested purple as a more universally visible alternative, mentioning that some developers use gold and purple together. They also shared a helpful PDF on Colorblind Safe Color Schemes.

Based on this feedback and the guide, I decided to expand the available particle effect colors in loot_beacon.xml, creating simple pillar effects for:

  • red, green, blue, yellow, cyan, magenta, orange, purple, white, lightblue, pink, lime, teal.

Then, I updated the mod's default color configuration (in mod.cfg and settable via console commands) to use a more accessible palette based on common advice for visibility across various color vision deficiencies. For version 1.4.2+, the new defaults are:

  • Pickable Items & Custom Entities: Orange (loot_beacon.pillar_orange)
  • Human Corpses: Cyan (loot_beacon.pillar_cyan)
  • Animal Carcasses: Blue (loot_beacon.pillar_blue)

Users can, of course, customize these paths to any of the available "pillar_COLOR" effects or even point to different custom particle effects if they create them. The community feedback was overwhelmingly positive for these changes.

Configuration and Commands

A good mod needs to be configurable. I exposed a range of console commands via CommandRegistry (using System.AddCCommand) to tweak settings like detection radius, highlight duration, which object types to highlight, "Good Citizen Mode" (to not highlight stolen items), and the particle effect paths for each category.

Snippet from command_registry.lua
function LootBeacon.CommandRegistry:registerCommand(name, action, description)
    System.AddCCommand(name, action, description) -- Error handling and logging omitted
end
 
-- Example registration
self:registerCommand("loot_beacon_activate", "LootBeacon.Highlighter:activateHighlights()", "Activate highlights")
self:registerCommand("loot_beacon_set_item_particle_effect_path", "LootBeacon.Config:setItemParticleEffectPath(%line)", "Set item particle color")

These settings are persisted in mod.cfg (KCD2's standard for mod configuration files).

Learning and Next Steps

This Loot Beacon mod was a fun exercise in Lua scripting within KCD2's CryEngine environment. It highlighted the importance of modular design, the power of the existing script binds for entity interaction and particle effects, and crucially, the immense value of community feedback for improving accessibility.

There are always more things to find and tweak. Perhaps refining the particle effects themselves (I'm no particle artist) or finding even more nuanced ways to filter loot could be next. But for now, no more lost Canker's Mace for me!


The official modding tools and the community's willingness to share knowledge are invaluable. If you're curious about KCD2 modding or want to contribute, feel free to check out the source for this and my other tools.

All my Kingdom Come: Deliverance II mods and tools can be found in this GitHub repository.

KCD2 Modding: Making sure Henry sees ALL the shiny things. CryEngine, CryMore!


Aleks

Hi, I'm Aleks, a Software Engineer with a passion for open-source projects and micro startups. This blog is my little corner of the internet where I share thoughts on topics I love, interesting stories, and the occasional deep dive into technical challenges.

Get rekt! I'll send new posts to your inbox.