Initial commit: YAProxy house-proxy interaction framework (1.29)
Self-contained framework for attaching user actions to furniture proxies inside house models. Detection via geometry-LOD selection scan (works on DayZ 1.29); resolved proxy name + world position synced to server via ActionData. No entities used for detection. - modded House: geometry-LOD proxy position/name maps (client-side) - YAProxyActionBase: cursor->proxy matching + sync + subclass hooks - YAProxyActionData: client->server proxy target sync - YAProxyDebug: validation action + per-house scan logging (DEBUG flag) Credit: Zenarchist (ZenProxyFramework) and ShowMeInfoV2. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
commit
89f42a04b0
9 changed files with 625 additions and 0 deletions
1
$PBOPREFIX$.txt
Normal file
1
$PBOPREFIX$.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
YAProxy
|
||||
66
README.md
Normal file
66
README.md
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
# YAProxy
|
||||
|
||||
A lightweight DayZ framework (1.29+) for attaching user actions to **furniture
|
||||
proxies** baked into house models (sinks, fridges, cabinets, vending machines,
|
||||
…), which are not separate entities.
|
||||
|
||||
Credit: technique combines **Zenarchist's ZenProxyFramework** (action API /
|
||||
client→server position sync, invisible-entity persistence pattern) and the
|
||||
**ShowMeInfoV2** mod (geometry-LOD proxy enumeration that works on 1.29).
|
||||
|
||||
## How it works
|
||||
- `modded House` reads the building's **geometry LOD selections**
|
||||
(`GetLODByName(LOD.NAME_GEOMETRY).GetSelections()`), finds those containing a
|
||||
proxy path, and builds `position → proxy name` maps (client-side).
|
||||
- `YAProxyActionBase` resolves which proxy the cursor is on, and syncs the
|
||||
resolved **name + world position** to the server via `ActionData`.
|
||||
- Detection uses **no entities**. (Spawning an invisible marker entity is only
|
||||
one option a consumer can use for *persistent state*, e.g. "sink is repaired
|
||||
for N days" — handled by the consuming mod, not here.)
|
||||
|
||||
## Writing an action (consumer mods)
|
||||
```c
|
||||
class ActionMySearch : YAProxyActionBase
|
||||
{
|
||||
override string GetProxyKeywords() { return "fridge,lekarnicka"; } // substring match; "*" = any
|
||||
override float GetDuration() { return 3.0; }
|
||||
override string GetText() { return "Search"; }
|
||||
|
||||
// optional extra availability check (client)
|
||||
override bool YA_ExtraCondition(PlayerBase p, ActionTarget t, ItemBase i, string name, vector ws)
|
||||
{ return !p.GetItemInHands(); }
|
||||
|
||||
override void OnFinishProgressServer(ActionData ad)
|
||||
{
|
||||
string proxy = GetProxyName(ad); // synced proxy name
|
||||
vector ws = GetProxyWS(ad); // synced world position
|
||||
// ... your effect ...
|
||||
}
|
||||
}
|
||||
```
|
||||
Register it (required, or `AddAction` dereferences null):
|
||||
```c
|
||||
modded class ActionConstructor { override void RegisterActions(TTypenameArray a){ super.RegisterActions(a); a.Insert(ActionMySearch); } }
|
||||
```
|
||||
Attach it where it should appear:
|
||||
- empty-handed → `modded PlayerBase.SetActions(out TInputActionMap m){ super...; AddAction(ActionMySearch, m); }`
|
||||
- tool/container in hands → `modded Wrench / Bottle_Base .SetActions(){ super...; AddAction(ActionMySearch); }`
|
||||
|
||||
For an item-in-hands action, override `CreateConditionComponents()` to use
|
||||
`CCINonRuined` instead of the default `CCINotPresent`.
|
||||
|
||||
## Debug / discovery
|
||||
`YAProxyConstants.DEBUG` (default **true** during bring-up) enables:
|
||||
- per-house scan logs: `[YAProxy] House 'Land_X' proxies built: map1=N map2=M`
|
||||
- an empty-handed **"Proxy: \<name\>"** action on any proxy that, on execute,
|
||||
dumps the resolved name, local vs world position, the full house proxy map,
|
||||
and the server-synced values.
|
||||
|
||||
Set `DEBUG = false` for production (and remove `YAProxyDebug.c` if desired).
|
||||
|
||||
## Notes / limitations
|
||||
- A building only scans if its script class is `House`. Many vanilla buildings
|
||||
aren't `House` by default; declare them `class Land_X : House {}` (see
|
||||
ZenProxyFramework's `ScriptedBuildings.c` for a curated list) as needed.
|
||||
- Some proxies are missed by the LOD scan and need manual position overrides
|
||||
(also per-building) — a future addition if required.
|
||||
60
config.cpp
Normal file
60
config.cpp
Normal file
|
|
@ -0,0 +1,60 @@
|
|||
// ============================================================================
|
||||
// @YAProxy - a lightweight DayZ house-proxy interaction framework (1.29+)
|
||||
// ----------------------------------------------------------------------------
|
||||
// Lets dependent mods attach user actions to furniture PROXIES baked into
|
||||
// house p3d models (sinks, fridges, cabinets, ...), which are not separate
|
||||
// entities. Detection uses the building's geometry-LOD selections; the
|
||||
// targeted proxy's name + world position are synced to the server via the
|
||||
// action's ActionData.
|
||||
//
|
||||
// Credit: technique adapted from Zenarchist's ZenProxyFramework (action API /
|
||||
// position sync) and the ShowMeInfoV2 mod (geometry-LOD proxy enumeration that
|
||||
// works on DayZ 1.29).
|
||||
// ============================================================================
|
||||
class CfgPatches
|
||||
{
|
||||
class YAProxy
|
||||
{
|
||||
units[] = {};
|
||||
weapons[] = {};
|
||||
requiredVersion = 0.1;
|
||||
requiredAddons[] =
|
||||
{
|
||||
"DZ_Data",
|
||||
"DZ_Scripts"
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
class CfgMods
|
||||
{
|
||||
class YAProxy
|
||||
{
|
||||
dir = "YAProxy";
|
||||
picture = "";
|
||||
action = "";
|
||||
hideName = 1;
|
||||
hidePicture = 1;
|
||||
name = "YAProxy";
|
||||
credits = "drasikas; technique from Zenarchist (ZenProxyFramework) & ShowMeInfoV2";
|
||||
author = "drasikas";
|
||||
authorID = "0";
|
||||
version = "1.0";
|
||||
extra = 0;
|
||||
type = "mod";
|
||||
dependencies[] = { "Game", "World" };
|
||||
class defs
|
||||
{
|
||||
class gameScriptModule
|
||||
{
|
||||
value = "";
|
||||
files[] = { "YAProxy/scripts/3_Game" };
|
||||
};
|
||||
class worldScriptModule
|
||||
{
|
||||
value = "";
|
||||
files[] = { "YAProxy/scripts/4_World" };
|
||||
};
|
||||
};
|
||||
};
|
||||
};
|
||||
13
mod.cpp
Normal file
13
mod.cpp
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
class CfgMods
|
||||
{
|
||||
class YAProxy
|
||||
{
|
||||
name = "YAProxy";
|
||||
author = "drasikas";
|
||||
authorID = "0";
|
||||
version = "1.0";
|
||||
tooltip = "House-proxy interaction framework (1.29+).";
|
||||
overview = "Framework for attaching user actions to furniture proxies inside house models. Detects proxies via geometry-LOD selections and syncs the targeted proxy to the server. Credit: Zenarchist (ZenProxyFramework) and ShowMeInfoV2.";
|
||||
dependencies[] = { "Game", "World" };
|
||||
};
|
||||
};
|
||||
16
scripts/3_Game/YAProxy/YAProxyConstants.c
Normal file
16
scripts/3_Game/YAProxy/YAProxyConstants.c
Normal file
|
|
@ -0,0 +1,16 @@
|
|||
// ============================================================================
|
||||
// YAProxyConstants
|
||||
// ============================================================================
|
||||
class YAProxyConstants
|
||||
{
|
||||
static const string TAG = "[YAProxy]";
|
||||
|
||||
// Matching tolerances (metres). MAIN: cursor-hit vs selection LS to decide a
|
||||
// proxy is "near"; FINE: world-pos match for the fallback map.
|
||||
static const float DIST_MAIN = 5.0;
|
||||
static const float DIST_FINE = 1.15;
|
||||
|
||||
// Toggle verbose proxy-scan logging AND the debug "Proxy: <name>" player
|
||||
// action (validation). Set false for production.
|
||||
static bool DEBUG = true;
|
||||
}
|
||||
238
scripts/4_World/YAProxy/YAProxyActionBase.c
Normal file
238
scripts/4_World/YAProxy/YAProxyActionBase.c
Normal file
|
|
@ -0,0 +1,238 @@
|
|||
// ============================================================================
|
||||
// YAProxyActionBase
|
||||
// ----------------------------------------------------------------------------
|
||||
// Base continuous action for interacting with house furniture proxies.
|
||||
//
|
||||
// Subclass it and override:
|
||||
// GetProxyKeywords() - comma-separated keywords; the proxy matches if its
|
||||
// name contains any keyword (e.g. "case_sink_a").
|
||||
// GetDuration() - progress time in seconds.
|
||||
// GetText() - action label.
|
||||
// YA_ExtraCondition() - extra availability checks (item state, etc.).
|
||||
// OnFinishProgressServer() - the effect; use GetProxyName()/GetProxyWS().
|
||||
// And set the condition components (item requirement) in CreateConditionComponents.
|
||||
//
|
||||
// Resolution (client) uses the modded House proxy maps (geometry-LOD scan);
|
||||
// the resolved proxy name + world pos are synced to the server via ActionData.
|
||||
//
|
||||
// Detection: uses the default ContinuousDefaultActionInput (detect-from-item +
|
||||
// detect-from-player), so the action works whether it is registered on the
|
||||
// player (empty-handed) or on an item (wrench / bottle). The cursor target is
|
||||
// passed to ActionCondition either way.
|
||||
// ============================================================================
|
||||
|
||||
class YAProxyActionBaseCB : ActionContinuousBaseCB
|
||||
{
|
||||
override void CreateActionComponent()
|
||||
{
|
||||
float t = 3.0;
|
||||
YAProxyActionBase a = YAProxyActionBase.Cast(m_ActionData.m_Action);
|
||||
if (a)
|
||||
t = a.GetDuration();
|
||||
m_ActionData.m_ActionComponent = new CAContinuousTime(t);
|
||||
}
|
||||
}
|
||||
|
||||
class YAProxyActionBase : ActionContinuousBase
|
||||
{
|
||||
// Last client-side resolution (for live labels / debug). Transient.
|
||||
protected string m_LastName;
|
||||
protected vector m_LastWS;
|
||||
|
||||
void YAProxyActionBase()
|
||||
{
|
||||
m_CallbackClass = YAProxyActionBaseCB;
|
||||
m_CommandUID = DayZPlayerConstants.CMD_ACTIONFB_INTERACT;
|
||||
m_FullBody = true;
|
||||
m_StanceMask = DayZPlayerConstants.STANCEMASK_ERECT | DayZPlayerConstants.STANCEMASK_CROUCH;
|
||||
}
|
||||
|
||||
// ---------------- subclass hooks ----------------
|
||||
float GetDuration() { return 3.0; }
|
||||
string GetProxyKeywords() { return ""; } // comma-separated; "" = match nothing
|
||||
|
||||
override void CreateConditionComponents()
|
||||
{
|
||||
m_ConditionItem = new CCINotPresent(); // no item by default (override for tools/containers)
|
||||
m_ConditionTarget = new CCTCursorParent(UAMaxDistances.DEFAULT);
|
||||
}
|
||||
|
||||
// Extra availability check after a proxy is matched (client side).
|
||||
bool YA_ExtraCondition(PlayerBase player, ActionTarget target, ItemBase item, string proxyName, vector proxyWS)
|
||||
{
|
||||
return true;
|
||||
}
|
||||
|
||||
override bool IsLockTargetOnUse()
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
// ---------------- custom ActionData ----------------
|
||||
override ActionData CreateActionData()
|
||||
{
|
||||
return new YAProxyActionData;
|
||||
}
|
||||
|
||||
// ---------------- resolution (client) ----------------
|
||||
// Which configured proxy is the cursor on? Returns name ("" if none), fills ws.
|
||||
string YA_Resolve(ActionTarget target, out vector ws)
|
||||
{
|
||||
ws = vector.Zero;
|
||||
if (!target || !target.GetObject())
|
||||
return "";
|
||||
|
||||
House house = House.Cast(target.GetParent());
|
||||
if (!house || !house.YA_ProxiesBuilt())
|
||||
return "";
|
||||
|
||||
string keywords = GetProxyKeywords();
|
||||
if (keywords == "")
|
||||
return "";
|
||||
keywords.ToLower();
|
||||
TStringArray kw = new TStringArray();
|
||||
keywords.Split(",", kw);
|
||||
|
||||
// 1) Map1: the target object's world position is the key for most proxies.
|
||||
string wKey = target.GetObject().GetWorldPosition().ToString(false);
|
||||
string name1;
|
||||
if (house.YA_GetProxyMap1().Find(wKey, name1) && YA_KeywordMatch(name1, kw))
|
||||
{
|
||||
ws = target.GetObject().GetWorldPosition();
|
||||
return name1;
|
||||
}
|
||||
|
||||
// 2) Map2 fallback: nearest local-space proxy to the cursor hit point.
|
||||
vector hitLS = target.GetCursorHitPos() - house.GetPosition();
|
||||
TStringStringMap map2 = house.YA_GetProxyMap2();
|
||||
float best = YAProxyConstants.DIST_FINE;
|
||||
string bestName = "";
|
||||
vector bestLS = vector.Zero;
|
||||
for (int i = 0; i < map2.Count(); i++)
|
||||
{
|
||||
vector lsPos = map2.GetKey(i).ToVector();
|
||||
float d = vector.Distance(hitLS, lsPos);
|
||||
if (d < best && YA_KeywordMatch(map2.GetElement(i), kw))
|
||||
{
|
||||
best = d;
|
||||
bestName = map2.GetElement(i);
|
||||
bestLS = lsPos;
|
||||
}
|
||||
}
|
||||
if (bestName != "")
|
||||
{
|
||||
ws = house.GetPosition() + bestLS;
|
||||
return bestName;
|
||||
}
|
||||
|
||||
return "";
|
||||
}
|
||||
|
||||
protected bool YA_KeywordMatch(string proxyName, TStringArray keywords)
|
||||
{
|
||||
string n = proxyName;
|
||||
n.ToLower();
|
||||
foreach (string k : keywords)
|
||||
{
|
||||
if (k == "*") // wildcard: match any proxy (debug)
|
||||
return true;
|
||||
if (k != "" && n.Contains(k))
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// ---------------- availability ----------------
|
||||
override bool ActionCondition(PlayerBase player, ActionTarget target, ItemBase item)
|
||||
{
|
||||
// Server can't see the cursor target; let it through (validated on finish).
|
||||
if (GetGame().IsDedicatedServer())
|
||||
return true;
|
||||
|
||||
vector ws;
|
||||
string name = YA_Resolve(target, ws);
|
||||
if (name == "")
|
||||
return false;
|
||||
|
||||
m_LastName = name; // remember for live labels / debug
|
||||
m_LastWS = ws;
|
||||
|
||||
return YA_ExtraCondition(player, target, item, name, ws);
|
||||
}
|
||||
|
||||
// ---------------- client -> server sync ----------------
|
||||
override bool SetupAction(PlayerBase player, ActionTarget target, ItemBase item, out ActionData action_data, Param extra_data = NULL)
|
||||
{
|
||||
if (!super.SetupAction(player, target, item, action_data, extra_data))
|
||||
return false;
|
||||
|
||||
if (GetGame().IsClient())
|
||||
{
|
||||
YAProxyActionData pad = YAProxyActionData.Cast(action_data);
|
||||
if (pad)
|
||||
{
|
||||
vector ws;
|
||||
pad.m_ProxyName = YA_Resolve(target, ws);
|
||||
pad.m_ProxyWS = ws;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
override void WriteToContext(ParamsWriteContext ctx, ActionData action_data)
|
||||
{
|
||||
super.WriteToContext(ctx, action_data);
|
||||
YAProxyActionData pad = YAProxyActionData.Cast(action_data);
|
||||
if (pad)
|
||||
{
|
||||
ctx.Write(pad.m_ProxyName);
|
||||
ctx.Write(pad.m_ProxyWS);
|
||||
}
|
||||
}
|
||||
|
||||
override bool ReadFromContext(ParamsReadContext ctx, out ActionReciveData action_recive_data)
|
||||
{
|
||||
if (!action_recive_data)
|
||||
action_recive_data = new YAProxyActionReceiveData;
|
||||
|
||||
super.ReadFromContext(ctx, action_recive_data);
|
||||
|
||||
YAProxyActionReceiveData rd = YAProxyActionReceiveData.Cast(action_recive_data);
|
||||
string name;
|
||||
vector ws;
|
||||
if (!ctx.Read(name)) return false;
|
||||
if (!ctx.Read(ws)) return false;
|
||||
rd.m_ProxyName = name;
|
||||
rd.m_ProxyWS = ws;
|
||||
return true;
|
||||
}
|
||||
|
||||
override void HandleReciveData(ActionReciveData action_recive_data, ActionData action_data)
|
||||
{
|
||||
super.HandleReciveData(action_recive_data, action_data);
|
||||
YAProxyActionReceiveData rd = YAProxyActionReceiveData.Cast(action_recive_data);
|
||||
YAProxyActionData pad = YAProxyActionData.Cast(action_data);
|
||||
if (rd && pad)
|
||||
{
|
||||
pad.m_ProxyName = rd.m_ProxyName;
|
||||
pad.m_ProxyWS = rd.m_ProxyWS;
|
||||
}
|
||||
}
|
||||
|
||||
// ---------------- server-side accessors ----------------
|
||||
string GetProxyName(ActionData action_data)
|
||||
{
|
||||
YAProxyActionData pad = YAProxyActionData.Cast(action_data);
|
||||
if (pad)
|
||||
return pad.m_ProxyName;
|
||||
return "";
|
||||
}
|
||||
|
||||
vector GetProxyWS(ActionData action_data)
|
||||
{
|
||||
YAProxyActionData pad = YAProxyActionData.Cast(action_data);
|
||||
if (pad)
|
||||
return pad.m_ProxyWS;
|
||||
return vector.Zero;
|
||||
}
|
||||
}
|
||||
18
scripts/4_World/YAProxy/YAProxyActionData.c
Normal file
18
scripts/4_World/YAProxy/YAProxyActionData.c
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
// ============================================================================
|
||||
// YAProxyActionData / YAProxyActionReceiveData
|
||||
// ----------------------------------------------------------------------------
|
||||
// Carries the resolved proxy name + world position from client to server so
|
||||
// server-side action code can act on the right proxy (the cursor target itself
|
||||
// is not networked). Pattern adapted from ZenProxyFramework.
|
||||
// ============================================================================
|
||||
class YAProxyActionReceiveData : ActionReciveData
|
||||
{
|
||||
string m_ProxyName;
|
||||
vector m_ProxyWS;
|
||||
}
|
||||
|
||||
class YAProxyActionData : ActionData
|
||||
{
|
||||
string m_ProxyName;
|
||||
vector m_ProxyWS;
|
||||
}
|
||||
103
scripts/4_World/YAProxy/YAProxyDebug.c
Normal file
103
scripts/4_World/YAProxy/YAProxyDebug.c
Normal file
|
|
@ -0,0 +1,103 @@
|
|||
// ============================================================================
|
||||
// YAProxyDebug - validation / discovery action (remove or set DEBUG=false
|
||||
// for production)
|
||||
// ----------------------------------------------------------------------------
|
||||
// Empty-handed action available on ANY recognised house proxy. The action
|
||||
// prompt shows "Proxy: <name>" live; executing it dumps full diagnostics:
|
||||
// * the resolved proxy name,
|
||||
// * target.GetObject() local vs world position (to confirm which keying
|
||||
// matches on 1.29 - ZPF uses local, ShowMeInfo uses world),
|
||||
// * the whole house proxy map (every proxy name + key in that building),
|
||||
// * the server-synced name + world pos.
|
||||
//
|
||||
// Only registered on the player when YAProxyConstants.DEBUG is true.
|
||||
// ============================================================================
|
||||
|
||||
class ActionYAProxyDebug : YAProxyActionBase
|
||||
{
|
||||
override float GetDuration()
|
||||
{
|
||||
return 1.0;
|
||||
}
|
||||
|
||||
override string GetProxyKeywords()
|
||||
{
|
||||
return "*"; // match any proxy
|
||||
}
|
||||
|
||||
override string GetText()
|
||||
{
|
||||
if (m_LastName != "")
|
||||
return "Proxy: " + m_LastName;
|
||||
return "Proxy: ?";
|
||||
}
|
||||
|
||||
override bool YA_ExtraCondition(PlayerBase player, ActionTarget target, ItemBase item, string proxyName, vector proxyWS)
|
||||
{
|
||||
return !player.GetItemInHands(); // empty-handed
|
||||
}
|
||||
|
||||
override void OnFinishProgressClient(ActionData action_data)
|
||||
{
|
||||
ActionTarget t = action_data.m_Target;
|
||||
if (!t || !t.GetObject())
|
||||
return;
|
||||
|
||||
Object obj = t.GetObject();
|
||||
Object parent = t.GetParent();
|
||||
|
||||
Print("[YAProxy][DBG] ===== proxy target dump =====");
|
||||
Print("[YAProxy][DBG] resolvedName = '" + m_LastName + "'");
|
||||
Print("[YAProxy][DBG] obj.GetPosition() (local?) = " + obj.GetPosition().ToString(false));
|
||||
Print("[YAProxy][DBG] obj.GetWorldPosition() = " + obj.GetWorldPosition().ToString(false));
|
||||
Print("[YAProxy][DBG] cursorHitPos = " + t.GetCursorHitPos().ToString(false));
|
||||
|
||||
House h = House.Cast(parent);
|
||||
if (h)
|
||||
{
|
||||
Print("[YAProxy][DBG] house = " + h.GetType() + " pos = " + h.GetPosition().ToString(false) + " built = " + h.YA_ProxiesBuilt());
|
||||
YA_DumpMap("map1(world)", h.YA_GetProxyMap1());
|
||||
YA_DumpMap("map2(local)", h.YA_GetProxyMap2());
|
||||
}
|
||||
else
|
||||
{
|
||||
Print("[YAProxy][DBG] parent is NOT a House script class (" + parent + ") - scan won't run for it");
|
||||
}
|
||||
}
|
||||
|
||||
protected void YA_DumpMap(string label, TStringStringMap m)
|
||||
{
|
||||
Print("[YAProxy][DBG] --- " + label + " (" + m.Count() + ") ---");
|
||||
for (int i = 0; i < m.Count(); i++)
|
||||
Print("[YAProxy][DBG] " + label + "[" + m.GetKey(i) + "] = " + m.GetElement(i));
|
||||
}
|
||||
|
||||
override void OnFinishProgressServer(ActionData action_data)
|
||||
{
|
||||
Print("[YAProxy][DBG] SERVER synced proxy='" + GetProxyName(action_data) + "' ws=" + GetProxyWS(action_data).ToString(false));
|
||||
}
|
||||
}
|
||||
|
||||
// --- registration (debug only) ---
|
||||
modded class ActionConstructor
|
||||
{
|
||||
override void RegisterActions(TTypenameArray actions)
|
||||
{
|
||||
super.RegisterActions(actions);
|
||||
actions.Insert(ActionYAProxyDebug);
|
||||
}
|
||||
}
|
||||
|
||||
modded class PlayerBase
|
||||
{
|
||||
override void SetActions(out TInputActionMap InputActionMap)
|
||||
{
|
||||
super.SetActions(InputActionMap);
|
||||
|
||||
if (YAProxyConstants.DEBUG)
|
||||
{
|
||||
AddAction(ActionYAProxyDebug, InputActionMap);
|
||||
Print("[YAProxy] Debug proxy action registered on player");
|
||||
}
|
||||
}
|
||||
}
|
||||
110
scripts/4_World/YAProxy/YAProxyHouse.c
Normal file
110
scripts/4_World/YAProxy/YAProxyHouse.c
Normal file
|
|
@ -0,0 +1,110 @@
|
|||
// ============================================================================
|
||||
// YAProxyHouse (modded House)
|
||||
// ----------------------------------------------------------------------------
|
||||
// Builds, shortly after each house spawns, a map of furniture-proxy world/
|
||||
// local positions -> proxy name by reading the building's geometry-LOD
|
||||
// selections. Dependent actions look up the targeted proxy in these maps.
|
||||
//
|
||||
// Adapted from the ShowMeInfoV2 mod's GetBuildingMaps() (the geometry-LOD scan
|
||||
// that works on DayZ 1.29). Two maps:
|
||||
// Map1 (worldish): keyed by GetSelectionPositionOld(sel) - matches the
|
||||
// target object's world position for most proxies.
|
||||
// Map2 (local): keyed by GetSelectionPositionLS(sel) - distance fallback
|
||||
// for proxies whose component pos doesn't match Map1.
|
||||
// ============================================================================
|
||||
modded class House
|
||||
{
|
||||
protected ref TStringStringMap ya_ProxyMap1; // world-pos string -> proxy name
|
||||
protected ref TStringStringMap ya_ProxyMap2; // local-pos string -> proxy name
|
||||
protected bool ya_ProxiesBuilt = false;
|
||||
|
||||
void House()
|
||||
{
|
||||
ya_ProxyMap1 = new TStringStringMap;
|
||||
ya_ProxyMap2 = new TStringStringMap;
|
||||
|
||||
// Resolution happens client-side; the server uses the synced world pos and
|
||||
// dedicated servers may not even load geometry LODs. So scan only off the
|
||||
// dedicated server. Geometry isn't ready at construction -> defer.
|
||||
if (!GetGame().IsDedicatedServer())
|
||||
GetGame().GetCallQueue(CALL_CATEGORY_SYSTEM).CallLater(this.YA_BuildProxyMaps, 1000, false);
|
||||
}
|
||||
|
||||
TStringStringMap YA_GetProxyMap1() { return ya_ProxyMap1; }
|
||||
TStringStringMap YA_GetProxyMap2() { return ya_ProxyMap2; }
|
||||
bool YA_ProxiesBuilt() { return ya_ProxiesBuilt; }
|
||||
|
||||
void YA_BuildProxyMaps()
|
||||
{
|
||||
string type = GetType();
|
||||
type.ToLower();
|
||||
if (type.Contains("contaminatedarea")) // no geometry / not relevant
|
||||
return;
|
||||
|
||||
LOD geoLod = GetLODByName(LOD.NAME_GEOMETRY);
|
||||
if (!geoLod)
|
||||
return;
|
||||
|
||||
// ---- Map1: geometry-LOD proxy selections ----
|
||||
array<Selection> geoSelections = new array<Selection>();
|
||||
geoLod.GetSelections(geoSelections);
|
||||
foreach (Selection sel : geoSelections)
|
||||
{
|
||||
if (!sel)
|
||||
continue;
|
||||
|
||||
string selName = sel.GetName();
|
||||
if (!YA_IsProxySelection(selName))
|
||||
continue;
|
||||
|
||||
string posKey = GetSelectionPositionOld(selName).ToString(false);
|
||||
string proxyName = YA_ParseProxyName(selName);
|
||||
if (proxyName != "")
|
||||
ya_ProxyMap1.Set(posKey, proxyName);
|
||||
}
|
||||
|
||||
// ---- Map2: parent selection list (local-space positions) ----
|
||||
TStringArray parentSelections = new TStringArray();
|
||||
GetSelectionList(parentSelections);
|
||||
foreach (string pSel : parentSelections)
|
||||
{
|
||||
if (!YA_IsProxySelection(pSel))
|
||||
continue;
|
||||
|
||||
string lsKey = GetSelectionPositionLS(pSel).ToString(false);
|
||||
string pName = YA_ParseProxyName(pSel);
|
||||
if (pName != "")
|
||||
ya_ProxyMap2.Set(lsKey, pName);
|
||||
}
|
||||
|
||||
ya_ProxiesBuilt = true;
|
||||
if (YAProxyConstants.DEBUG)
|
||||
Print(string.Format("%1 House '%2' proxies built: map1=%3 map2=%4", YAProxyConstants.TAG, GetType(), ya_ProxyMap1.Count(), ya_ProxyMap2.Count()));
|
||||
}
|
||||
|
||||
// A real, targetable proxy selection (not the "proxies" group or shadows).
|
||||
protected bool YA_IsProxySelection(string selection)
|
||||
{
|
||||
string s = selection;
|
||||
s.ToLower();
|
||||
return s.Contains("proxy") && !s.Contains("proxies") && !s.Contains("shadow");
|
||||
}
|
||||
|
||||
// "proxy\furniture\fridge.p3d(.001)" -> "fridge"
|
||||
protected string YA_ParseProxyName(string selection)
|
||||
{
|
||||
string s = selection;
|
||||
s.ToLower();
|
||||
|
||||
TStringArray parts = new TStringArray();
|
||||
s.Split("\\", parts);
|
||||
if (parts.Count() == 0)
|
||||
return "";
|
||||
|
||||
string last = parts[parts.Count() - 1]; // "fridge.p3d" / "fridge.p3d.001"
|
||||
int dot = last.IndexOf(".p3d");
|
||||
if (dot != -1)
|
||||
last = last.Substring(0, dot);
|
||||
return last;
|
||||
}
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue