Stacking root cause: boxes that persisted from sessions before the
non-persistency change load into the world AFTER MissionServer.OnInit, so the
one-shot init cleanup missed them; they were untracked and never despawned, and
new boxes spawned on top.
- RemoveUntrackedBoxesAt(pos): deletes YALootBoxes_Base near a point that are
NOT in m_ActiveBoxes (skips live boxes via IsTrackedBox), so it is safe to
call any time.
- SpawnOneAtRandomFreeLocation now clears its exact location first, guaranteeing
one box per location and never stacking on a leftover.
- CleanupOrphanedBoxes now runs at startup AND at the start of every
TickLifecycle (self-healing), so ghosts that stream in late are removed within
one check interval. Radius tightened 5m -> 3m.
Despawn timers (per request): split into two main_config keys
- DespawnMinutes: locked/unopened box lifetime (per-type despawnMinutes still
overrides when > 0; default per-type is now 0 = use global).
- OpenedDespawnMinutes: lifetime after a box is opened.
Replaces the single DefaultDespawnMinutes. Lootbox uses GetLockedLifetimeMinutes
/ GetOpenedLifetimeMinutes accordingly. README/configfiles.readme + _comment docs
updated.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Action (the real "no action" cause):
- DayZ does not auto-discover custom actions; ActionConstructor holds a
hardcoded list and AddAction() silently drops anything missing from it.
Added a modded ActionConstructor that registers YALB_ActionOpenLootbox, so
the open action finally attaches to the box.
- Switched the action target condition CCTObject -> CCTCursor (the look-at
pattern vanilla uses for world-object interactions).
Restart stacking:
- Spawn boxes with CreateObjectEx + ECE_NOPERSISTENCY_WORLD so they are never
saved to the world DB and don't survive restarts.
- Added CleanupOrphanedBoxes(): on init, scan each spawn location and delete
any leftover YALootBoxes_Base before spawning (clears boxes persisted before
this change).
Logging:
- DebugLogLevel changed from a string to a numeric field (0=off 1=ERROR
2=WARN 3=INFO 4=DEBUG 5=VERBOSE), clamped at load. Removed the string parser.
NOTE: existing main_config.json has a string value and must be regenerated.
Config docs:
- Each generated JSON now includes a "_comment" array describing every field
and its range (ignored by the loader). README + configfiles.readme updated.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Logging:
- Replace EnableDebugLogging (bool) with DebugLogLevel (string) in
YALB_MainConfig; default "INFO". Stored as a string for admin-friendliness,
parsed once at load into YALB_ConfigLoader.LogLevel (int) for cheap compares.
- Add YALB_LogLevel enum NONE(0)<ERROR(1)<WARN(2)<INFO(3)<DEBUG(4)<VERBOSE(5),
YALB_ParseLogLevel (case-insensitive, falls back to INFO), and leveled
helpers Log_Error/Warn/Info/Debug/Verbose. ERROR also routes via ErrorEx().
- Reclassify every existing log call by content (missing critical config ->
ERROR, recoverable -> WARN, milestones -> INFO, per-tick summary -> DEBUG,
per-player/per-roll detail -> VERBOSE). Add a VERBOSE proximity-send log.
- Update main_config.json example and the default constructor to "INFO".
- Add README.md documenting levels, the config field, and the VERBOSE warning.
Green cleanups (from Phase 2):
- Remove dead OnActionInfoUpdate override (behaviour is inherited).
- Remove unused m_LocationInUse map (the live pool is m_FreeLocationNames).
- Null-guard player identity in the fail-effect log.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- config.cpp: fix script/config binding. Crates now inherit a shared
YALootBoxes_Base config class (name-matches the script class) instead of
relying on the non-binding simulationModule line. Base class changed from
non-existent ContainerObject to vanilla Container_Base.
- Lootbox.c: same base class change; replace never-called GetActions
override with the engine-correct SetActions()/AddAction().
- ActionOpen.c: BROKEN_BONE now uses player.SetBrokenLegs(eBrokenLegs.
BROKEN_LEGS) instead of bogus GetHealthDamageManager().SetFracture();
DecreaseHealth health-type arg empty-string changed to "Health" (3 sites).
- Config.c: restructure loot lists from a string-keyed map to a flat array
of YALB_LootList{type,entries} for reliable JSON deserialization;
GetLootListForType updated to linear scan; position made a ref array;
document failEffects.chance as a weight, not a probability.
- loot_lists.json (configfiles.readme): match new array schema.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Renamed code prefix LB_ -> YALB_ across all four script files
- Renamed source files LootboxSpawner_*.c -> YALootBoxes_*.c
- Deleted three empty 0-byte stub files (_BoxTypes/_LootLists/_SpawnLocations)
- Fixed folder case scripts/4_world -> scripts/4_World to match config.cpp
- Verified box_types.json className values match CfgVehicles class names
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>