Phase 2: structural/completeness fixes (correctness pass)
- 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>
This commit is contained in:
parent
354dfe34d5
commit
a84b6b1a2b
5 changed files with 111 additions and 68 deletions
51
config.cpp
51
config.cpp
|
|
@ -49,42 +49,55 @@ class CfgMods
|
|||
};
|
||||
|
||||
// ------------------------------------------------------------
|
||||
// Lootbox entity classes - one per box type defined in
|
||||
// box_types.json. The "className" field in that JSON file
|
||||
// MUST exactly match one of these class names.
|
||||
// Lootbox entity classes.
|
||||
//
|
||||
// Swap "ContainerObject" inheritance/model/physics as needed -
|
||||
// these reuse a vanilla static container model purely as a
|
||||
// placeholder. Replace 'model=' with your own p3d if you have one.
|
||||
// BINDING: DayZ binds a CfgVehicles class to a script class by
|
||||
// NAME. The three crate classes below do NOT each need their own
|
||||
// script class - instead they all inherit from a shared config
|
||||
// class "YALootBoxes_Base" whose name matches the script class in
|
||||
// YALootBoxes_Lootbox.c. The engine walks the config inheritance
|
||||
// chain upward until it finds a matching script class, so every
|
||||
// crate resolves to the YALootBoxes_Base script type. The old
|
||||
// "simulationModule" line did NOT do this and has been removed.
|
||||
//
|
||||
// BASE CLASS: these inherit from vanilla "Container_Base" (a real
|
||||
// lootable container base). "ContainerObject" used previously is
|
||||
// not a vanilla class. >>> VERIFY against your SDK <<< and, if the
|
||||
// crate spawns with no cargo space, add a Cargo grid definition
|
||||
// (e.g. itemsCargoSize[] = {x,y};) to YALootBoxes_Base below - the
|
||||
// wooden_crate model + Container_Base may need it to hold loot.
|
||||
//
|
||||
// MODEL: wooden_crate.p3d path is a placeholder; replace with a
|
||||
// real p3d you have. >>> VERIFY model path against your SDK <<<
|
||||
// ------------------------------------------------------------
|
||||
class CfgVehicles
|
||||
{
|
||||
class ContainerObject;
|
||||
class Container_Base;
|
||||
|
||||
class YALootBoxes_Crate_Military : ContainerObject
|
||||
// Shared, non-spawnable base. Name MUST match the script class
|
||||
// "YALootBoxes_Base" in YALootBoxes_Lootbox.c for binding.
|
||||
class YALootBoxes_Base : Container_Base
|
||||
{
|
||||
scope = 0;
|
||||
vehicleClass = "Containers";
|
||||
model = "\dz\structures\wood\wooden_crate.p3d";
|
||||
};
|
||||
|
||||
class YALootBoxes_Crate_Military : YALootBoxes_Base
|
||||
{
|
||||
scope = 2;
|
||||
displayName = "Military Crate (Locked)";
|
||||
model = "\dz\structures\wood\wooden_crate.p3d";
|
||||
vehicleClass = "Containers";
|
||||
simulationModule = "YALootBoxes_Base";
|
||||
};
|
||||
|
||||
class YALootBoxes_Crate_Medical : ContainerObject
|
||||
class YALootBoxes_Crate_Medical : YALootBoxes_Base
|
||||
{
|
||||
scope = 2;
|
||||
displayName = "Medical Crate (Locked)";
|
||||
model = "\dz\structures\wood\wooden_crate.p3d";
|
||||
vehicleClass = "Containers";
|
||||
simulationModule = "YALootBoxes_Base";
|
||||
};
|
||||
|
||||
class YALootBoxes_Crate_Industrial : ContainerObject
|
||||
class YALootBoxes_Crate_Industrial : YALootBoxes_Base
|
||||
{
|
||||
scope = 2;
|
||||
displayName = "Industrial Crate (Locked)";
|
||||
model = "\dz\structures\wood\wooden_crate.p3d";
|
||||
vehicleClass = "Containers";
|
||||
simulationModule = "YALootBoxes_Base";
|
||||
};
|
||||
};
|
||||
|
|
@ -81,29 +81,38 @@
|
|||
// FILE: loot_lists.json
|
||||
// ------------------------------------------------------------
|
||||
{
|
||||
"LootLists": {
|
||||
"military": [
|
||||
{ "classname": "AKM", "chance": 0.10, "minCount": 1, "maxCount": 1 },
|
||||
{ "classname": "Mag_AKM_30Rnd", "chance": 0.40, "minCount": 1, "maxCount": 3 },
|
||||
{ "classname": "PlateCarrierVest","chance": 0.20, "minCount": 1, "maxCount": 1 },
|
||||
{ "classname": "TacticalHelmet", "chance": 0.15, "minCount": 1, "maxCount": 1 },
|
||||
{ "classname": "Ammo_762x39", "chance": 0.50, "minCount": 1, "maxCount": 2 }
|
||||
],
|
||||
"medical": [
|
||||
{ "classname": "Morphine", "chance": 0.30, "minCount": 1, "maxCount": 1 },
|
||||
{ "classname": "SalineBagIV", "chance": 0.25, "minCount": 1, "maxCount": 2 },
|
||||
{ "classname": "VitaminBottle", "chance": 0.50, "minCount": 1, "maxCount": 3 },
|
||||
{ "classname": "Bandage", "chance": 0.60, "minCount": 2, "maxCount": 5 },
|
||||
{ "classname": "TetracyclineAntibiotics", "chance": 0.20, "minCount": 1, "maxCount": 2 }
|
||||
],
|
||||
"industrial": [
|
||||
{ "classname": "WeldingMask", "chance": 0.15, "minCount": 1, "maxCount": 1 },
|
||||
{ "classname": "Battery_Car", "chance": 0.30, "minCount": 1, "maxCount": 1 },
|
||||
{ "classname": "SparkPlug", "chance": 0.40, "minCount": 1, "maxCount": 2 },
|
||||
{ "classname": "MetalPipe", "chance": 0.50, "minCount": 1, "maxCount": 4 },
|
||||
{ "classname": "DuctTape", "chance": 0.45, "minCount": 1, "maxCount": 2 }
|
||||
]
|
||||
}
|
||||
"LootLists": [
|
||||
{
|
||||
"type": "military",
|
||||
"entries": [
|
||||
{ "classname": "AKM", "chance": 0.10, "minCount": 1, "maxCount": 1 },
|
||||
{ "classname": "Mag_AKM_30Rnd", "chance": 0.40, "minCount": 1, "maxCount": 3 },
|
||||
{ "classname": "PlateCarrierVest","chance": 0.20, "minCount": 1, "maxCount": 1 },
|
||||
{ "classname": "TacticalHelmet", "chance": 0.15, "minCount": 1, "maxCount": 1 },
|
||||
{ "classname": "Ammo_762x39", "chance": 0.50, "minCount": 1, "maxCount": 2 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "medical",
|
||||
"entries": [
|
||||
{ "classname": "Morphine", "chance": 0.30, "minCount": 1, "maxCount": 1 },
|
||||
{ "classname": "SalineBagIV", "chance": 0.25, "minCount": 1, "maxCount": 2 },
|
||||
{ "classname": "VitaminBottle", "chance": 0.50, "minCount": 1, "maxCount": 3 },
|
||||
{ "classname": "Bandage", "chance": 0.60, "minCount": 2, "maxCount": 5 },
|
||||
{ "classname": "TetracyclineAntibiotics", "chance": 0.20, "minCount": 1, "maxCount": 2 }
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "industrial",
|
||||
"entries": [
|
||||
{ "classname": "WeldingMask", "chance": 0.15, "minCount": 1, "maxCount": 1 },
|
||||
{ "classname": "Battery_Car", "chance": 0.30, "minCount": 1, "maxCount": 1 },
|
||||
{ "classname": "SparkPlug", "chance": 0.40, "minCount": 1, "maxCount": 2 },
|
||||
{ "classname": "MetalPipe", "chance": 0.50, "minCount": 1, "maxCount": 4 },
|
||||
{ "classname": "DuctTape", "chance": 0.45, "minCount": 1, "maxCount": 2 }
|
||||
]
|
||||
}
|
||||
]
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -3,9 +3,8 @@
|
|||
// Custom UserAction: hold-to-open a locked lootbox.
|
||||
// Lives in: scripts/4_World/YALootBoxes_ActionOpen.c
|
||||
//
|
||||
// Registration of this action onto YALootBoxes_Base happens
|
||||
// via GetActionComponentName/cfgVehicles "actions[]" in config.cpp,
|
||||
// OR (simpler, used here) by overriding GetActions() — see note
|
||||
// Registration of this action onto YALootBoxes_Base happens by
|
||||
// overriding SetActions()/AddAction() on the entity — see note
|
||||
// at bottom of file.
|
||||
// ============================================================
|
||||
|
||||
|
|
@ -144,7 +143,7 @@ void YALB_ApplyRandomFailEffect(PlayerBase player, YALB_BoxTypeDef def, ItemBase
|
|||
break;
|
||||
|
||||
case "HEALTH":
|
||||
player.DecreaseHealth("", "", chosen.damage);
|
||||
player.DecreaseHealth("", "Health", chosen.damage);
|
||||
break;
|
||||
|
||||
case "BLEEDING":
|
||||
|
|
@ -159,9 +158,9 @@ void YALB_ApplyRandomFailEffect(PlayerBase player, YALB_BoxTypeDef def, ItemBase
|
|||
break;
|
||||
|
||||
case "BROKEN_BONE":
|
||||
player.GetHealthDamageManager().SetHealthLimitDamage(true);
|
||||
player.SetHealth("", "Health", player.GetHealth("", "Health") - 1); // nudge to trigger fracture check
|
||||
player.GetHealthDamageManager().SetFracture(true);
|
||||
// Vanilla broken-leg API. The player modifier system drives
|
||||
// the HUD icon / forced-prone reaction off this state.
|
||||
player.SetBrokenLegs(eBrokenLegs.BROKEN_LEGS);
|
||||
break;
|
||||
|
||||
default:
|
||||
|
|
@ -175,7 +174,7 @@ void YALB_ApplyExplosiveDamage(PlayerBase player, float damage, float radius)
|
|||
// Direct damage to the player rather than a real grenade explosion entity,
|
||||
// since we don't want a separate live explosive object to manage/clean up.
|
||||
vector pos = player.GetPosition();
|
||||
player.DecreaseHealth("", "", damage);
|
||||
player.DecreaseHealth("", "Health", damage);
|
||||
|
||||
// Optional: damage nearby players too, to sell the "explosion" effect
|
||||
array<Man> nearbyPlayers = new array<Man>();
|
||||
|
|
@ -188,7 +187,7 @@ void YALB_ApplyExplosiveDamage(PlayerBase player, float damage, float radius)
|
|||
if (dist <= radius)
|
||||
{
|
||||
float falloff = 1.0 - (dist / radius);
|
||||
other.DecreaseHealth("", "", damage * falloff);
|
||||
other.DecreaseHealth("", "Health", damage * falloff);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -223,13 +222,13 @@ void YALB_NotifyPlayer(PlayerBase player, string message)
|
|||
// NOTE: Hooking this action onto the lootbox class
|
||||
// ------------------------------------------------------------
|
||||
// The cleanest approach for a fully custom action without
|
||||
// touching vanilla configs is to override GetActions() directly
|
||||
// touching vanilla configs is to override SetActions() directly
|
||||
// on YALootBoxes_Base:
|
||||
//
|
||||
// override void GetActions(out array<typename> actions)
|
||||
// override void SetActions()
|
||||
// {
|
||||
// actions.Insert(YALB_ActionOpenLootbox);
|
||||
// super.SetActions();
|
||||
// AddAction(YALB_ActionOpenLootbox);
|
||||
// }
|
||||
//
|
||||
// This override is included in YALootBoxes_Lootbox.c via a
|
||||
// partial addition — see the updated lootbox entity file.
|
||||
// This override lives in YALootBoxes_Lootbox.c.
|
||||
|
|
@ -46,7 +46,9 @@ class YALB_RequiredItem
|
|||
class YALB_FailEffect
|
||||
{
|
||||
string type; // EXPLOSIVE, HEALTH, BLEEDING, SICKNESS, BROKEN_BONE
|
||||
float chance; // 0.0 - 1.0
|
||||
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
|
||||
|
|
@ -116,14 +118,29 @@ class YALB_LootEntry
|
|||
int maxCount;
|
||||
};
|
||||
|
||||
// One loot list per box "type". Stored as a flat array of {type, entries}
|
||||
// objects rather than a map<string, array<...>> - 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<ref YALB_LootEntry> entries;
|
||||
|
||||
void YALB_LootList()
|
||||
{
|
||||
entries = new array<ref YALB_LootEntry>();
|
||||
}
|
||||
};
|
||||
|
||||
class YALB_LootListsFile
|
||||
{
|
||||
// Map: box "type" string -> array of loot entries
|
||||
ref map<string, ref array<ref YALB_LootEntry>> LootLists;
|
||||
ref array<ref YALB_LootList> LootLists;
|
||||
|
||||
void YALB_LootListsFile()
|
||||
{
|
||||
LootLists = new map<string, ref array<ref YALB_LootEntry>>();
|
||||
LootLists = new array<ref YALB_LootList>();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -133,7 +150,7 @@ class YALB_LootListsFile
|
|||
class YALB_SpawnLocationDef
|
||||
{
|
||||
string name;
|
||||
array<float> position; // [x, y, z]
|
||||
ref array<float> position; // [x, y, z] - 'ref' so the array member is owned/allocated
|
||||
string boxType;
|
||||
|
||||
vector GetPosition()
|
||||
|
|
@ -246,9 +263,11 @@ class YALB_ConfigLoader
|
|||
static array<ref YALB_LootEntry> GetLootListForType(string type)
|
||||
{
|
||||
if (!LootLists) return null;
|
||||
array<ref YALB_LootEntry> list;
|
||||
if (LootLists.LootLists.Find(type, list))
|
||||
return list;
|
||||
foreach (YALB_LootList list : LootLists.LootLists)
|
||||
{
|
||||
if (list.type == type)
|
||||
return list.entries;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -9,7 +9,7 @@
|
|||
// shared behavior for ALL of them.
|
||||
// ============================================================
|
||||
|
||||
class YALootBoxes_Base extends ContainerObject
|
||||
class YALootBoxes_Base extends Container_Base
|
||||
{
|
||||
protected bool m_IsLocked;
|
||||
protected string m_BoxTypeId; // matches "type" in box_types.json
|
||||
|
|
@ -147,9 +147,12 @@ class YALootBoxes_Base extends ContainerObject
|
|||
|
||||
// Hooks our custom hold-to-open action onto this object without
|
||||
// needing to touch any vanilla cfgVehicles action arrays.
|
||||
override void GetActions(out array<typename> actions)
|
||||
// NOTE: the engine-called override is SetActions()/AddAction(),
|
||||
// NOT GetActions(out array<typename>) - the latter is never
|
||||
// invoked by the action manager and silently registers nothing.
|
||||
override void SetActions()
|
||||
{
|
||||
super.GetActions(actions);
|
||||
actions.Insert(YALB_ActionOpenLootbox);
|
||||
super.SetActions();
|
||||
AddAction(YALB_ActionOpenLootbox);
|
||||
}
|
||||
};
|
||||
Loading…
Add table
Reference in a new issue