Olympe Engine 2.0
2D Game Engine with ECS Architecture
Loading...
Searching...
No Matches
DynamicDataPinManager.cpp
Go to the documentation of this file.
1/**
2 * @file DynamicDataPinManager.cpp
3 * @brief Implementation of DynamicDataPinManager (Phase 24.3).
4 * @author Olympe Engine
5 * @date 2026-03-17
6 *
7 * C++14 compliant — no std::optional, structured bindings, std::filesystem.
8 */
9
11
12#include <algorithm>
13#include <sstream>
14#include "../../BlueprintEditor/ConditionRef.h" // For OperandRef mode info (Phase 24)
15
16namespace Olympe {
17
18// ============================================================================
19// Constructor
20// ============================================================================
21
26
27// ============================================================================
28// SyncPins
29// ============================================================================
30
31void DynamicDataPinManager::SyncPins(std::vector<NodeConditionRef>& conditionRefs,
32 const std::vector<ConditionRef>& operandRefs)
33{
34 // Build the set of (conditionIndex, position) pairs that SHOULD exist
35 // after this sync, and map each pair to the condition preview string.
36 struct PinSpec {
37 int conditionIndex;
38 OperandPosition position;
39 std::string condPreview;
40 std::string presetID;
41 };
42
43 std::vector<PinSpec> needed;
44
45 for (int i = 0; i < static_cast<int>(conditionRefs.size()); ++i)
46 {
47 const NodeConditionRef& ref = conditionRefs[i];
49 if (!preset)
50 continue;
51
52 // Phase 24: Build preview from ACTUAL operand data if available
53 // This ensures that when users switch operand modes (e.g., Const -> Pin),
54 // the pin label reflects the CURRENT state, not the static preset.
55 std::string preview;
56 if (i < static_cast<int>(operandRefs.size()) && !operandRefs.empty())
57 {
59 std::ostringstream oss;
60 oss << "[" << (cref.leftOperand.mode == OperandRef::Mode::Variable
61 ? cref.leftOperand.variableName
62 : (cref.leftOperand.mode == OperandRef::Mode::Pin
63 ? "Pin"
64 : cref.leftOperand.constValue))
65 << "] " << cref.operatorStr << " ["
66 << (cref.rightOperand.mode == OperandRef::Mode::Variable
67 ? cref.rightOperand.variableName
68 : (cref.rightOperand.mode == OperandRef::Mode::Pin
69 ? "Pin"
70 : cref.rightOperand.constValue))
71 << "]";
72 preview = oss.str();
73 }
74 else
75 {
76 // Fallback to static preset preview
77 preview = preset->GetPreview();
78 }
79
80 if (preset->NeedsLeftPin())
81 needed.push_back({i, OperandPosition::Left, preview, ref.presetID});
82
83 if (preset->NeedsRightPin())
84 needed.push_back({i, OperandPosition::Right, preview, ref.presetID});
85 }
86
87 // Build new ordered pin list, reusing existing UUIDs where possible.
88 std::vector<DynamicDataPin> newPins;
89 newPins.reserve(needed.size());
90
91 for (const auto& spec : needed)
92 {
93 DynamicDataPin* existing = FindExistingPin(spec.conditionIndex, spec.position);
94
95 DynamicDataPin pin;
96 if (existing)
97 {
98 // Reuse UUID and other immutable fields; refresh label in case
99 // the preset preview changed.
100 pin = *existing;
101 // Rebuild the label with the (possibly updated) preview
102 std::string side = (spec.position == OperandPosition::Left) ? "L" : "R";
103 std::ostringstream oss;
104 oss << "In #" << (spec.conditionIndex + 1) << side
105 << ": " << spec.condPreview;
106 pin.label = oss.str();
107 }
108 else
109 {
110 pin = DynamicDataPin(spec.conditionIndex, spec.position,
111 spec.condPreview);
112 }
113
114 newPins.push_back(pin);
115 }
116
117 // Replace stored pins and rebuild index maps.
118 m_pins = std::move(newPins);
119 m_idIndex.clear();
120 m_keyIndex.clear();
121
122 for (size_t idx = 0; idx < m_pins.size(); ++idx)
123 {
124 DynamicDataPin& p = m_pins[idx];
125 // Assign 1-based sequence number for "Pin-in #N" short labels.
126 p.sequenceNumber = static_cast<int>(idx) + 1;
127 m_idIndex[p.id] = idx;
128 m_keyIndex[MakePinKey(p.conditionIndex, p.position)] = idx;
129 }
130
131 // Write back leftPinID / rightPinID into the condition refs.
132 for (auto& ref : conditionRefs)
133 {
134 ref.leftPinID = "";
135 ref.rightPinID = "";
136 }
137
138 for (const auto& pin : m_pins)
139 {
140 int ci = pin.conditionIndex;
141 if (ci < 0 || ci >= static_cast<int>(conditionRefs.size()))
142 continue;
143
144 if (pin.position == OperandPosition::Left)
145 conditionRefs[ci].leftPinID = pin.id;
146 else
147 conditionRefs[ci].rightPinID = pin.id;
148 }
149}
150
151// ============================================================================
152// RegeneratePinsFromConditions
153// ============================================================================
154
155void DynamicDataPinManager::RegeneratePinsFromConditions(
156 std::vector<NodeConditionRef>& conditionRefs,
157 const std::vector<ConditionRef>& operandRefs)
158{
159 // Phase 24: Pass operandRefs to SyncPins so labels are built from ACTUAL
160 // operand data (not static preset preview). This fixes recursive label
161 // accumulation when users switch operand modes.
162 SyncPins(conditionRefs, operandRefs);
163}
164
165// ============================================================================
166// Query
167// ============================================================================
168
169const std::vector<DynamicDataPin>& DynamicDataPinManager::GetAllPins() const
170{
171 return m_pins;
172}
173
174const DynamicDataPin* DynamicDataPinManager::GetPinByID(const std::string& pinID) const
175{
176 auto it = m_idIndex.find(pinID);
177 if (it == m_idIndex.end())
178 return nullptr;
179 return &m_pins[it->second];
180}
181
182DynamicDataPin* DynamicDataPinManager::GetPinByID(const std::string& pinID)
183{
184 auto it = m_idIndex.find(pinID);
185 if (it == m_idIndex.end())
186 return nullptr;
187 return &m_pins[it->second];
188}
189
190std::vector<const DynamicDataPin*>
191DynamicDataPinManager::GetPinsForCondition(int conditionIndex) const
192{
193 std::vector<const DynamicDataPin*> result;
194 for (const auto& pin : m_pins)
195 {
196 if (pin.conditionIndex == conditionIndex)
197 result.push_back(&pin);
198 }
199 return result;
200}
201
202size_t DynamicDataPinManager::GetPinCount() const
203{
204 return m_pins.size();
205}
206
207// ============================================================================
208// Invalidation
209// ============================================================================
210
211void DynamicDataPinManager::InvalidatePreset(const std::string& deletedPresetID)
212{
213 // Find indices of pins that belong to conditions referencing deletedPresetID.
214 // Since we don't store the presetID per-pin, we remove pins only if the
215 // associated preset's NeedsLeftPin / NeedsRightPin is determined by looking
216 // up the preset — but the preset is being deleted. Instead, we track which
217 // condition indices are affected and remove their pins.
218 //
219 // Because the caller will typically call SyncPins() with the updated
220 // condition list immediately after InvalidatePreset(), we do a best-effort
221 // removal of any pin whose UUID is not found in a valid preset any more.
222 // In practice: remove ALL pins that would no longer be generated.
223
224 // Simple strategy: remove all pins whose id appears in our map and whose
225 // preset is gone from the registry. Since the preset IS being deleted we
226 // can't look it up; instead just clear everything — SyncPins() will rebuild.
228
229 // Remove all pins whose condition-index points to a condition whose preset
230 // ID equals deletedPresetID. Since we don't store presetID in DynamicDataPin
231 // we clear all pins and let the next SyncPins() call rebuild from scratch.
232 Clear();
233}
234
235void DynamicDataPinManager::Clear()
236{
237 m_pins.clear();
238 m_idIndex.clear();
239 m_keyIndex.clear();
240}
241
242// ============================================================================
243// Color helper
244// ============================================================================
245
246/*static*/
247void DynamicDataPinManager::GetDynamicPinColor(float& r, float& g,
248 float& b, float& a)
249{
250 r = 1.0f;
251 g = 1.0f;
252 b = 0.0f;
253 a = 1.0f;
254}
255
256// ============================================================================
257// Internal helpers
258// ============================================================================
259
261DynamicDataPinManager::FindExistingPin(int conditionIndex, OperandPosition pos)
262{
263 auto it = m_keyIndex.find(MakePinKey(conditionIndex, pos));
264 if (it == m_keyIndex.end())
265 return nullptr;
266 return &m_pins[it->second];
267}
268
269/*static*/
270std::string DynamicDataPinManager::MakePinKey(int conditionIndex,
272{
273 std::ostringstream oss;
274 oss << conditionIndex << '_'
275 << ((pos == OperandPosition::Left) ? 'L' : 'R');
276 return oss.str();
277}
278
279} // namespace Olympe
Engine for generating and tracking dynamic data pins (Phase 24.3).
ComponentTypeID GetComponentTypeID_Static()
Definition ECS_Entity.h:56
Manages the global pool of ConditionPreset objects.
ConditionPreset * GetPreset(const std::string &id)
Returns a mutable pointer to the preset, or nullptr if not found.
DynamicDataPinManager(ConditionPresetRegistry &registry)
Constructs the manager bound to the global preset registry.
ConditionPresetRegistry & m_registry
Shared global registry.
void SyncPins(std::vector< NodeConditionRef > &conditionRefs, const std::vector< ConditionRef > &operandRefs=std::vector< ConditionRef >())
Rebuilds the pin set to match the given condition list.
< Provides AssetID and INVALID_ASSET_ID
OperandPosition
Identifies which operand side a DynamicDataPin serves.
@ Right
Pin provides data for the right operand of the condition.
@ Left
Pin provides data for the left operand of the condition.
A globally-stored, reusable condition expression.
Stores the complete reference for one condition including operand-to-DynamicDataPin mapping.
std::string operatorStr
Comparison operator: "==", "<=", ">=", "!=", "<", ">".
A data-input pin created dynamically for a Pin-mode operand.
One entry in a NodeBranch's conditions list.
@ Variable
References a blackboard variable by name.
@ Pin
External data-input pin on the owning node.