// ============================================================ // YALootBoxes_Config.c // Typed data classes for all JSON files + load/save/defaults. // Lives in: scripts/4_World/YALootBoxes_Config.c // ============================================================ const string YALB_CONFIG_DIR = "$profile:YALootBoxes\\"; const string YALB_MAIN_CONFIG_FILE = YALB_CONFIG_DIR + "main_config.json"; const string YALB_BOX_TYPES_FILE = YALB_CONFIG_DIR + "box_types.json"; const string YALB_LOOT_LISTS_FILE = YALB_CONFIG_DIR + "loot_lists.json"; const string YALB_SPAWN_LOCATIONS_FILE = YALB_CONFIG_DIR + "spawn_locations.json"; // ------------------------------------------------------------ // MAIN CONFIG // ------------------------------------------------------------ class YALB_MainConfig { int MaxActiveLootboxes; int GlobalCheckIntervalSeconds; int DefaultDespawnMinutes; int OpenChannelDurationSeconds; float ProximityMessageRangeMeters; bool EnableDebugLogging; void YALB_MainConfig() { // Sane defaults if file is missing/corrupt MaxActiveLootboxes = 30; GlobalCheckIntervalSeconds = 60; DefaultDespawnMinutes = 15; OpenChannelDurationSeconds = 5; ProximityMessageRangeMeters = 3.0; EnableDebugLogging = true; } }; // ------------------------------------------------------------ // BOX TYPES // ------------------------------------------------------------ class YALB_RequiredItem { string classname; string displayName; }; class YALB_FailEffect { string type; // EXPLOSIVE, HEALTH, BLEEDING, SICKNESS, BROKEN_BONE float chance; // RELATIVE WEIGHT for the single weighted pick - NOT an // independent probability. Exactly one effect is chosen // per failure; an effect's odds = its chance / sum(chances). float damage; // used by EXPLOSIVE / HEALTH float radius; // used by EXPLOSIVE int bleedSources; // used by BLEEDING string agent; // used by SICKNESS }; class YALB_BoxTypeDef { string className; string type; ref array requiredItems; float openChance; float itemDamageOnOpen; int despawnMinutes; ref array failEffects; void YALB_BoxTypeDef() { requiredItems = new array(); failEffects = new array(); openChance = 0.5; itemDamageOnOpen = 5.0; despawnMinutes = 15; } // Builds the player-facing "requires: X, Y" string string GetRequiredItemsDisplayString() { string result = ""; for (int i = 0; i < requiredItems.Count(); i++) { if (i > 0) result += ", "; result += requiredItems[i].displayName; } return result; } bool IsValidKeyItem(string itemClassName) { for (int i = 0; i < requiredItems.Count(); i++) { if (requiredItems[i].classname == itemClassName) return true; } return false; } }; class YALB_BoxTypesFile { ref array BoxTypes; void YALB_BoxTypesFile() { BoxTypes = new array(); } }; // ------------------------------------------------------------ // LOOT LISTS // ------------------------------------------------------------ class YALB_LootEntry { string classname; float chance; int minCount; int maxCount; }; // One loot list per box "type". Stored as a flat array of {type, entries} // objects rather than a map> - DayZ's JsonFileLoader is // unreliable at deserializing nested generic containers (a map whose values // are themselves ref arrays), whereas an array of typed objects deserializes // dependably. Lookup is a small linear scan (only a handful of types). class YALB_LootList { string type; ref array entries; void YALB_LootList() { entries = new array(); } }; class YALB_LootListsFile { ref array LootLists; void YALB_LootListsFile() { LootLists = new array(); } }; // ------------------------------------------------------------ // SPAWN LOCATIONS // ------------------------------------------------------------ class YALB_SpawnLocationDef { string name; ref array position; // [x, y, z] - 'ref' so the array member is owned/allocated string boxType; vector GetPosition() { if (position && position.Count() == 3) return Vector(position[0], position[1], position[2]); return vector.Zero; } }; class YALB_SpawnLocationsFile { ref array SpawnLocations; void YALB_SpawnLocationsFile() { SpawnLocations = new array(); } }; // ------------------------------------------------------------ // LOADER / SAVER // ------------------------------------------------------------ class YALB_ConfigLoader { static ref YALB_MainConfig MainConfig; static ref YALB_BoxTypesFile BoxTypes; static ref YALB_LootListsFile LootLists; static ref YALB_SpawnLocationsFile SpawnLocations; static void EnsureDirectoryExists() { if (!FileExist(YALB_CONFIG_DIR)) { MakeDirectory(YALB_CONFIG_DIR); } } static void LoadAll() { EnsureDirectoryExists(); MainConfig = new YALB_MainConfig(); if (FileExist(YALB_MAIN_CONFIG_FILE)) { JsonFileLoader.JsonLoadFile(YALB_MAIN_CONFIG_FILE, MainConfig); } else { JsonFileLoader.JsonSaveFile(YALB_MAIN_CONFIG_FILE, MainConfig); YALB_Log("main_config.json not found - created with defaults."); } BoxTypes = new YALB_BoxTypesFile(); if (FileExist(YALB_BOX_TYPES_FILE)) { JsonFileLoader.JsonLoadFile(YALB_BOX_TYPES_FILE, BoxTypes); } else { YALB_Log("box_types.json not found - no lootbox types loaded! Mod will not spawn anything until this exists."); } LootLists = new YALB_LootListsFile(); if (FileExist(YALB_LOOT_LISTS_FILE)) { JsonFileLoader.JsonLoadFile(YALB_LOOT_LISTS_FILE, LootLists); } else { YALB_Log("loot_lists.json not found - lootboxes will open empty!"); } SpawnLocations = new YALB_SpawnLocationsFile(); if (FileExist(YALB_SPAWN_LOCATIONS_FILE)) { JsonFileLoader.JsonLoadFile(YALB_SPAWN_LOCATIONS_FILE, SpawnLocations); } else { YALB_Log("spawn_locations.json not found - no spawn points available!"); } YALB_Log(string.Format("Config loaded: %1 box types, %2 spawn locations, %3 loot lists.", BoxTypes.BoxTypes.Count(), SpawnLocations.SpawnLocations.Count(), LootLists.LootLists.Count())); } static YALB_BoxTypeDef GetBoxTypeByClassName(string className) { if (!BoxTypes) return null; foreach (YALB_BoxTypeDef def : BoxTypes.BoxTypes) { if (def.className == className) return def; } return null; } static YALB_BoxTypeDef GetBoxTypeByType(string type) { if (!BoxTypes) return null; foreach (YALB_BoxTypeDef def : BoxTypes.BoxTypes) { if (def.type == type) return def; } return null; } static array GetLootListForType(string type) { if (!LootLists) return null; foreach (YALB_LootList list : LootLists.LootLists) { if (list.type == type) return list.entries; } return null; } }; // ------------------------------------------------------------ // LOGGING HELPER // ------------------------------------------------------------ void YALB_Log(string msg) { if (YALB_ConfigLoader.MainConfig && !YALB_ConfigLoader.MainConfig.EnableDebugLogging) return; Print("[YALootBoxes] " + msg); }