187 lines
8.8 KiB
C
187 lines
8.8 KiB
C
// ============================================================================
|
|
// YAInteractionConfigLoader
|
|
// ----------------------------------------------------------------------------
|
|
// Owns the loaded configuration (Settings + Data) and the JSON load/save flow.
|
|
//
|
|
// Behaviour:
|
|
// * First server start (no JSON present): write code defaults to
|
|
// <ProfileFolder>/YAInteraction/{settings.json,interactables.json}.
|
|
// * Subsequent starts: read those files; values present in JSON override the
|
|
// inline/code defaults.
|
|
//
|
|
// Init() is called server-side from modded MissionServer.OnInit(), before any
|
|
// player connects (see YAInteractionMissionInit.c).
|
|
//
|
|
// Clients never run MissionServer.OnInit and have no JSON in their profile, so
|
|
// EnsureDefaults() provides the in-code default registry without touching files
|
|
// - enough for client-side action injection (loot/cooldowns are server-only).
|
|
// TODO (v2): sync the registry to clients so admin JSON edits to Interactables
|
|
// are reflected client-side without shipping a new build.
|
|
// ============================================================================
|
|
class YAInteractionConfigLoader
|
|
{
|
|
static ref YAInteractionSettings Settings;
|
|
static ref YAInteractionData Data;
|
|
|
|
private static const string DIR_NAME = "YAInteraction";
|
|
private static const string SETTINGS_FILE = "settings.json";
|
|
private static const string DATA_FILE = "interactables.json";
|
|
|
|
// ------------------------------------------------------------------
|
|
// Full server-side init: build paths, ensure dir, load-or-save each
|
|
// file, then rebuild the manager lookups.
|
|
// ------------------------------------------------------------------
|
|
static void Init()
|
|
{
|
|
// "$profile:" is the engine-mounted virtual path to the server profiles
|
|
// folder. (DayZGame has no GetProfileFolder() in this build; $profile: is
|
|
// the portable idiom used by e.g. ZenRepairWells / TerjeMods.)
|
|
string folder = "$profile:\\" + DIR_NAME + "\\";
|
|
if (!FileExist(folder))
|
|
MakeDirectory(folder);
|
|
|
|
string settingsPath = folder + SETTINGS_FILE;
|
|
string dataPath = folder + DATA_FILE;
|
|
|
|
// --- settings.json (per-key override of inline defaults) ---
|
|
Settings = new YAInteractionSettings(); // inline defaults
|
|
if (FileExist(settingsPath))
|
|
{
|
|
JsonFileLoader<YAInteractionSettings>.JsonLoadFile(settingsPath, Settings);
|
|
Print("[YAInteraction] Loaded settings from " + settingsPath);
|
|
}
|
|
else
|
|
{
|
|
JsonFileLoader<YAInteractionSettings>.JsonSaveFile(settingsPath, Settings);
|
|
Print("[YAInteraction] No settings.json - wrote defaults to " + settingsPath);
|
|
}
|
|
|
|
// --- interactables.json (full registry/loot definition) ---
|
|
if (FileExist(dataPath))
|
|
{
|
|
Data = new YAInteractionData(); // fresh empty arrays, fully defined by JSON
|
|
JsonFileLoader<YAInteractionData>.JsonLoadFile(dataPath, Data);
|
|
Print(string.Format("[YAInteraction] Loaded interactables from %1 (%2 entries, %3 tables)", dataPath, Data.Interactables.Count(), Data.LootTables.Count()));
|
|
}
|
|
else
|
|
{
|
|
Data = BuildDefaultData();
|
|
JsonFileLoader<YAInteractionData>.JsonSaveFile(dataPath, Data);
|
|
Print(string.Format("[YAInteraction] No interactables.json - wrote defaults to %1 (%2 entries, %3 tables)", dataPath, Data.Interactables.Count(), Data.LootTables.Count()));
|
|
}
|
|
|
|
YAInteractionManager.RebuildLookups();
|
|
// TODO (v2): hot-reload - re-run Init() + RebuildLookups() on an admin
|
|
// command or file watch so JSON edits apply without a server restart.
|
|
}
|
|
|
|
// ------------------------------------------------------------------
|
|
// Client-safe fallback: ensure Settings/Data exist from code defaults
|
|
// without any file I/O. Used when Init() hasn't run (i.e. on clients).
|
|
// ------------------------------------------------------------------
|
|
static void EnsureDefaults()
|
|
{
|
|
if (!Settings)
|
|
Settings = new YAInteractionSettings();
|
|
if (!Data)
|
|
{
|
|
Data = BuildDefaultData();
|
|
Print("[YAInteraction] EnsureDefaults: built in-code default registry (no JSON / client side)");
|
|
}
|
|
}
|
|
|
|
// ==================================================================
|
|
// Code defaults - the same items as the original config. This is the
|
|
// single place defaults are defined; edit here, delete the JSON, and
|
|
// restart to regenerate.
|
|
// ==================================================================
|
|
static YAInteractionData BuildDefaultData()
|
|
{
|
|
if (!Settings)
|
|
Settings = new YAInteractionSettings(); // default cooldowns feed the default entries below
|
|
|
|
YAInteractionData d = new YAInteractionData();
|
|
|
|
// --- Interactable registry (keyed by ZenProxyFramework PROXY NAME) ---
|
|
// (proxyName, actionType, lootTableName, cooldownSec)
|
|
// "case_sink_a" is confirmed (it's in ZenProxyConstants.SINKS). The search
|
|
// names are best guesses - confirm via ZPF's proxy dump / debug F-key and
|
|
// correct them in interactables.json (or here + rebuild).
|
|
// Proxy names (from YAProxy scan / DayZ editor): the resolved name carries
|
|
// a ".NNN" instance suffix (e.g. "fridge.001"); the keyword is matched as
|
|
// a substring, so the base name is enough.
|
|
// (proxyName, actionType, lootTable, cooldownSec, searchChance%, searchMin, searchMax)
|
|
d.Interactables.Insert(MakeEntry("case_sink_a", YAInteractionConfig.ACTION_REPAIR_WATER, "", Settings.SinkPlayerCooldownSec));
|
|
d.Interactables.Insert(MakeEntry("sink", YAInteractionConfig.ACTION_REPAIR_WATER, "", Settings.SinkPlayerCooldownSec));
|
|
d.Interactables.Insert(MakeEntry("lekarnicka", YAInteractionConfig.ACTION_SEARCH, YAInteractionConfig.TABLE_MED, Settings.ObjectCooldownSec, 60.0, 1, 2));
|
|
d.Interactables.Insert(MakeEntry("fridge", YAInteractionConfig.ACTION_SEARCH, YAInteractionConfig.TABLE_FOOD, Settings.ObjectCooldownSec, 75.0, 1, 2));
|
|
// Works for vending machines SPAWNED STANDALONE (init.c / CE) - matched as
|
|
// a real entity by classname (StaticObj_Furniture_vending_machine). House-
|
|
// baked vending proxies still can't be detected (no targetable object).
|
|
d.Interactables.Insert(MakeEntry("vending_machine", YAInteractionConfig.ACTION_SEARCH, YAInteractionConfig.TABLE_VENDING, Settings.ObjectCooldownSec, 80.0, 1, 2));
|
|
//For future reference, static objects along patrhways (e.g. crates, barrels, boxes) with various random loot.
|
|
//d.Interactables.Insert(MakeEntry("Wooden_Crate", YAInteractionConfig.ACTION_SEARCH, YAInteractionConfig.TABLE_FOOD, Settings.ObjectCooldownSec, 90.0, 1, 3));
|
|
|
|
// --- Loot tables (classnames verified in P:\DZ) ---
|
|
YALootTable med = MakeTable(YAInteractionConfig.TABLE_MED);
|
|
med.Entries.Insert(MakeLoot("BandageDressing", 40, 1, 2));
|
|
med.Entries.Insert(MakeLoot("CharcoalTablets", 25, 1, 1));
|
|
med.Entries.Insert(MakeLoot("TetracyclineAntibiotics", 15, 1, 1));
|
|
med.Entries.Insert(MakeLoot("SalineBag", 10, 1, 1));
|
|
med.Entries.Insert(MakeLoot("Morphine", 6, 1, 1));
|
|
med.Entries.Insert(MakeLoot("Epinephrine", 4, 1, 1));
|
|
d.LootTables.Insert(med);
|
|
|
|
YALootTable food = MakeTable(YAInteractionConfig.TABLE_FOOD);
|
|
food.Entries.Insert(MakeLoot("Apple", 30, 1, 2));
|
|
food.Entries.Insert(MakeLoot("BakedBeansCan", 20, 1, 1));
|
|
food.Entries.Insert(MakeLoot("SardinesCan", 15, 1, 1));
|
|
food.Entries.Insert(MakeLoot("Rice", 15, 1, 1));
|
|
food.Entries.Insert(MakeLoot("Banana", 12, 1, 2)); // vanilla has no "dried banana"
|
|
food.Entries.Insert(MakeLoot("PowderedMilk", 8, 1, 1));
|
|
d.LootTables.Insert(food);
|
|
|
|
YALootTable vending = MakeTable(YAInteractionConfig.TABLE_VENDING);
|
|
vending.Entries.Insert(MakeLoot("SodaCan_Cola", 20, 0, 0));
|
|
vending.Entries.Insert(MakeLoot("SodaCan_Spite", 20, 0, 0));
|
|
vending.Entries.Insert(MakeLoot("SodaCan_Fronta", 20, 0, 0));
|
|
vending.Entries.Insert(MakeLoot("SodaCan_Pipsi", 15, 0, 0));
|
|
vending.Entries.Insert(MakeLoot("WaterBottle", 25, 0, 0)); // "PET_BottledWater" does not exist
|
|
d.LootTables.Insert(vending);
|
|
|
|
return d;
|
|
}
|
|
|
|
// --- Factory helpers (data classes have no parameterised ctor so the
|
|
// JSON serializer can default-construct them). ---
|
|
|
|
private static YAInteractableEntry MakeEntry(string proxyName, string actionType, string lootTableName, float cooldownSec, float searchChance = 100.0, int searchMin = 1, int searchMax = 1)
|
|
{
|
|
YAInteractableEntry e = new YAInteractableEntry();
|
|
e.ProxyName = proxyName;
|
|
e.ActionType = actionType;
|
|
e.LootTableName = lootTableName;
|
|
e.CooldownSec = cooldownSec;
|
|
e.SearchChance = searchChance;
|
|
e.SearchMin = searchMin;
|
|
e.SearchMax = searchMax;
|
|
return e;
|
|
}
|
|
|
|
private static YALootTable MakeTable(string name)
|
|
{
|
|
YALootTable t = new YALootTable();
|
|
t.Name = name;
|
|
return t;
|
|
}
|
|
|
|
private static YALootEntry MakeLoot(string className, float weight, int minQty, int maxQty)
|
|
{
|
|
YALootEntry e = new YALootEntry();
|
|
e.ClassName = className;
|
|
e.Weight = weight;
|
|
e.MinQty = minQty;
|
|
e.MaxQty = maxQty;
|
|
return e;
|
|
}
|
|
}
|