载入中...
搜索中...
未找到
ClimbingBindings.cpp
浏览该文件的文档.
1#include "climbing/Climbing.h"
3
6#include "physics/Body3D.h"
7#include "physics/World3D.h"
8
9#include <functional>
10#include <optional>
11#include <simplesquirrel/simplesquirrel.hpp>
12#include <utility>
13
14namespace eve::climbing {
15namespace {
16
17eve::Value vecValue(Vec3 value) { return eve::Value::object({{"x", value.x}, {"y", value.y}, {"z", value.z}}); }
18
19eve::Value candidateValue(ClimbingCandidate candidate) {
20 return eve::Value::object({
21 {"actionId", std::move(candidate.actionId)},
22 {"definitionGeneration", std::to_string(candidate.definitionGeneration)},
23 {"world", static_cast<std::int64_t>(candidate.world.packed())},
24 {"obstacleBody", static_cast<std::int64_t>(candidate.obstacleBody.packed())},
25 {"obstacleShape", static_cast<std::int64_t>(candidate.obstacleShape.packed())},
26 {"obstacleBodyId", candidate.obstacleBodyId},
27 {"obstacleShapeId", candidate.obstacleShapeId},
28 {"ignoredBodyId", candidate.ignoredBodyId},
29 {"frontPoint", vecValue(candidate.frontPoint)},
30 {"topPoint", vecValue(candidate.topPoint)},
31 {"landingFeet", vecValue(candidate.landingFeet)},
32 {"surfaceNormal", vecValue(candidate.surfaceNormal)},
33 {"surfaceTangent", vecValue(candidate.surfaceTangent)},
34 {"leftHandAnchor", vecValue(candidate.leftHandAnchor)},
35 {"rightHandAnchor", vecValue(candidate.rightHandAnchor)},
36 {"bodyLocalTop", vecValue(candidate.bodyLocalTop)},
37 {"bodyLocalLanding", vecValue(candidate.bodyLocalLanding)},
38 {"obstacleHeight", candidate.obstacleHeight},
39 {"obstacleDepth", candidate.obstacleDepth},
40 {"score", candidate.score},
41 {"kind", static_cast<std::int64_t>(candidate.kind)},
42 {"support", static_cast<std::int64_t>(candidate.support)},
43 });
44}
45
46const char* phaseName(ClimbingPhase phase) {
47 switch (phase) {
48 case ClimbingPhase::Idle: return "idle";
49 case ClimbingPhase::Requested: return "requested";
50 case ClimbingPhase::Aligning: return "aligning";
51 case ClimbingPhase::Launching: return "launching";
52 case ClimbingPhase::Climbing: return "climbing";
53 case ClimbingPhase::Landing: return "landing";
54 case ClimbingPhase::Recovering: return "recovering";
55 case ClimbingPhase::Hanging: return "hanging";
56 case ClimbingPhase::Dropping: return "dropping";
57 case ClimbingPhase::Completed: return "completed";
58 case ClimbingPhase::Cancelled: return "cancelled";
59 case ClimbingPhase::Failed: return "failed";
60 case ClimbingPhase::Balanced: return "balanced";
61 case ClimbingPhase::Swinging: return "swinging";
62 }
63 return "failed";
64}
65
67 switch (kind) {
68 case ClimbingEventKind::Started: return "started";
69 case ClimbingEventKind::AnchorTransitionStarted: return "anchor_transition_started";
70 case ClimbingEventKind::AnchorReached: return "anchor_reached";
71 case ClimbingEventKind::ContactLeftHand: return "contact_left_hand";
72 case ClimbingEventKind::ContactRightHand: return "contact_right_hand";
73 case ClimbingEventKind::Landed: return "landed";
74 case ClimbingEventKind::Hanging: return "hanging";
75 case ClimbingEventKind::Dropped: return "dropped";
76 case ClimbingEventKind::Completed: return "completed";
77 case ClimbingEventKind::Cancelled: return "cancelled";
78 case ClimbingEventKind::Failed: return "failed";
79 }
80 return "failed";
81}
82
83eve::Value eventValue(ClimbingEvent event) {
84 eve::Value::Array metadata;
85 metadata.reserve(event.metadata.size());
86 for (std::string& value : event.metadata) metadata.emplace_back(std::move(value));
87 return eve::Value::object({
88 {"kind", eventKindName(event.kind)},
89 {"actionId", std::move(event.actionId)},
90 {"tick", std::to_string(event.tick.value())},
91 {"executionId", std::to_string(event.executionId.value())},
92 {"metadata", eve::Value(std::move(metadata))},
93 });
94}
95
96eve::Value eventBatchValue(std::vector<ClimbingEvent> events) {
97 eve::Value::Array result;
98 result.reserve(events.size());
99 for (auto& event : events) result.push_back(eventValue(std::move(event)));
100 return eve::Value(std::move(result));
101}
102
103eve::Value advanceValue(ClimbingAdvance advance) {
104 return eve::Value::object({
105 {"phase", phaseName(advance.phase)},
106 {"actionId", std::move(advance.actionId)},
107 {"feet", vecValue(advance.feet)},
108 {"desiredDelta", vecValue(advance.desiredDelta)},
109 {"actualDelta", vecValue(advance.actualDelta)},
110 {"warpResidual", vecValue(advance.warpResidual)},
111 {"normalizedTime", advance.normalizedTime},
112 {"constrained", advance.constrained},
113 {"grounded", advance.grounded},
114 {"support", static_cast<std::int64_t>(advance.support)},
115 {"leftHandAnchor", vecValue(advance.leftHandAnchor)},
116 {"rightHandAnchor", vecValue(advance.rightHandAnchor)},
117 {"contactWeight", advance.contactWeight},
118 {"leftHandWeight", advance.leftHandWeight},
119 {"rightHandWeight", advance.rightHandWeight},
120 {"leftFootWeight", advance.leftFootWeight},
121 {"rightFootWeight", advance.rightFootWeight},
122 {"pelvisWeight", advance.pelvisWeight},
123 {"compactCollisionRequested", advance.compactCollisionRequested},
124 {"compactCollisionActive", advance.compactCollisionActive},
125 {"branchWindowOpen", advance.branchWindowOpen},
126 {"branchComboTag", std::move(advance.branchComboTag)},
127 {"executionId", std::to_string(advance.executionId.value())},
128 {"appliedWarp", vecValue(advance.appliedWarp)},
129 {"desiredYawDelta", advance.desiredYawDelta},
130 {"cameraCueProfile", std::move(advance.cameraCueProfile)},
131 {"cameraCue", std::move(advance.cameraCue)},
132 {"animationClipId", std::move(advance.animationClipId)},
133 {"animationGraphNodeId", std::move(advance.animationGraphNodeId)},
134 {"animationMirrored", advance.animationMirrored},
135 {"terminalVelocity", vecValue(advance.terminalVelocity)},
136 {"hasTerminalVelocity", advance.hasTerminalVelocity},
137 });
138}
139
140eve::Value debugValue(ClimbingDebugSnapshot snapshot) {
141 eve::Value::Array candidates;
142 candidates.reserve(snapshot.candidates.size());
143 for (auto& candidate : snapshot.candidates) candidates.push_back(candidateValue(std::move(candidate)));
144 eve::Value::Array queries;
145 queries.reserve(snapshot.queries.size());
146 for (auto& query : snapshot.queries)
147 queries.push_back(eve::Value::object({{"actionId", std::move(query.actionId)},
148 {"code", std::move(query.code)},
149 {"start", vecValue(query.start)},
150 {"end", vecValue(query.end)},
151 {"radius", query.radius},
152 {"height", query.height}}));
153 eve::Value::Array evidence;
154 evidence.reserve(snapshot.evidence.size());
155 for (auto& item : snapshot.evidence)
156 evidence.push_back(eve::Value::object({{"actionId", std::move(item.actionId)},
157 {"code", std::move(item.code)},
158 {"biasCost", item.biasCost},
159 {"heightCost", item.heightCost},
160 {"distanceCost", item.distanceCost},
161 {"totalCost", item.totalCost}}));
162 eve::Value::Array motion;
163 motion.reserve(snapshot.motion.size());
164 for (const auto& item : snapshot.motion)
165 motion.push_back(eve::Value::object({{"tick", std::to_string(item.tick.value())},
166 {"plannedFeet", vecValue(item.plannedFeet)},
167 {"actualFeet", vecValue(item.actualFeet)},
168 {"residual", vecValue(item.residual)},
169 {"capsuleHeight", item.capsuleHeight},
170 {"constrained", item.constrained}}));
171 return eve::Value::object({
172 {"phase", phaseName(snapshot.phase)},
173 {"candidates", eve::Value(std::move(candidates))},
174 {"accumulatedWarpResidual", vecValue(snapshot.accumulatedWarpResidual)},
175 {"broadPhaseQueryCount", static_cast<std::int64_t>(snapshot.broadPhaseQueryCount)},
176 {"broadPhaseHitCount", static_cast<std::int64_t>(snapshot.broadPhaseHitCount)},
177 {"queryCount", static_cast<std::int64_t>(snapshot.queryCount)},
178 {"terminalCode", std::move(snapshot.terminalCode)},
179 {"executionId", static_cast<std::int64_t>(snapshot.executionId.value())},
180 {"queries", eve::Value(std::move(queries))},
181 {"evidence", eve::Value(std::move(evidence))},
182 {"motion", eve::Value(std::move(motion))},
183 });
184}
185
186std::optional<ClimbingActionKind> parseActionKind(std::string_view value) {
187 if (value == "vault") return ClimbingActionKind::Vault;
188 if (value == "mantle") return ClimbingActionKind::Mantle;
189 if (value == "ledge_grab") return ClimbingActionKind::LedgeGrab;
190 if (value == "climb_up") return ClimbingActionKind::ClimbUp;
191 if (value == "shimmy") return ClimbingActionKind::Shimmy;
192 if (value == "corner_inner") return ClimbingActionKind::CornerInner;
193 if (value == "corner_outer") return ClimbingActionKind::CornerOuter;
194 if (value == "ledge_jump") return ClimbingActionKind::LedgeJump;
195 if (value == "climb_down") return ClimbingActionKind::ClimbDown;
196 if (value == "ladder_mount") return ClimbingActionKind::LadderMount;
197 if (value == "ladder_climb") return ClimbingActionKind::LadderClimb;
198 if (value == "ladder_dismount") return ClimbingActionKind::LadderDismount;
199 if (value == "wall_run") return ClimbingActionKind::WallRun;
200 if (value == "slide") return ClimbingActionKind::Slide;
201 if (value == "beam_balance") return ClimbingActionKind::BeamBalance;
202 if (value == "pole_swing") return ClimbingActionKind::PoleSwing;
203 if (value == "bar_swing") return ClimbingActionKind::BarSwing;
204 return std::nullopt;
205}
206
207std::optional<ClimbingAnchorEdgeKind> parseAnchorEdgeKind(std::string_view value) {
208 if (value == "shimmy") return ClimbingAnchorEdgeKind::Shimmy;
209 if (value == "corner") return ClimbingAnchorEdgeKind::Corner;
210 if (value == "jump") return ClimbingAnchorEdgeKind::Jump;
211 if (value == "drop") return ClimbingAnchorEdgeKind::Drop;
212 if (value == "mount") return ClimbingAnchorEdgeKind::Mount;
213 if (value == "dismount") return ClimbingAnchorEdgeKind::Dismount;
214 if (value == "climb") return ClimbingAnchorEdgeKind::Climb;
215 if (value == "balance") return ClimbingAnchorEdgeKind::Balance;
216 if (value == "swing") return ClimbingAnchorEdgeKind::Swing;
217 return std::nullopt;
218}
219
220eve::Value anchorNodeValue(ClimbingAnchorNodeRef reference) {
221 return eve::Value::object({
222 {"graphId", std::move(reference.graphId)},
223 {"nodeId", std::move(reference.nodeId)},
224 {"graphGeneration", std::to_string(reference.graphGeneration)},
225 });
226}
227
228eve::Value graphReloadValue(ClimbingAnchorGraphReload reload) {
229 eve::Value::Array invalidated;
230 invalidated.reserve(reload.invalidatedOccupants.size());
231 for (const auto& occupant : reload.invalidatedOccupants)
232 invalidated.push_back(eve::Value::object({
233 {"agentId", std::to_string(occupant.agentId.value())},
234 {"executionId", std::to_string(occupant.executionId.value())},
235 }));
236 return eve::Value::object({
237 {"oldGeneration", std::to_string(reload.oldGeneration)},
238 {"newGeneration", std::to_string(reload.newGeneration)},
239 {"invalidatedOccupants", eve::Value(std::move(invalidated))},
240 });
241}
242
243const char* anchorEdgeKindName(ClimbingAnchorEdgeKind kind);
244
245eve::Value anchorRouteValue(ClimbingAnchorRoute route) {
247 nodes.reserve(route.nodes.size());
248 for (auto& node : route.nodes) nodes.push_back(anchorNodeValue(std::move(node)));
250 steps.reserve(route.steps.size());
251 for (auto& step : route.steps)
252 steps.push_back(eve::Value::object({
253 {"from", anchorNodeValue(std::move(step.from))},
254 {"to", anchorNodeValue(std::move(step.to))},
255 {"kind", anchorEdgeKindName(step.kind)},
256 }));
257 return eve::Value::object({
258 {"graphId", std::move(route.graphId)},
259 {"graphGeneration", std::to_string(route.graphGeneration)},
260 {"nodes", eve::Value(std::move(nodes))},
261 {"steps", eve::Value(std::move(steps))},
262 });
263}
264
265const char* anchorKindName(ClimbingAnchorKind kind) {
266 switch (kind) {
267 case ClimbingAnchorKind::Ledge: return "ledge";
268 case ClimbingAnchorKind::CornerInner: return "corner_inner";
269 case ClimbingAnchorKind::CornerOuter: return "corner_outer";
270 case ClimbingAnchorKind::LadderRung: return "ladder_rung";
271 case ClimbingAnchorKind::Pole: return "pole";
272 case ClimbingAnchorKind::Beam: return "beam";
273 case ClimbingAnchorKind::Bar: return "bar";
274 }
275 return "unknown";
276}
277
278const char* anchorEdgeKindName(ClimbingAnchorEdgeKind kind) {
279 switch (kind) {
280 case ClimbingAnchorEdgeKind::Shimmy: return "shimmy";
281 case ClimbingAnchorEdgeKind::Corner: return "corner";
282 case ClimbingAnchorEdgeKind::Jump: return "jump";
283 case ClimbingAnchorEdgeKind::Drop: return "drop";
284 case ClimbingAnchorEdgeKind::Mount: return "mount";
285 case ClimbingAnchorEdgeKind::Dismount: return "dismount";
286 case ClimbingAnchorEdgeKind::Climb: return "climb";
287 case ClimbingAnchorEdgeKind::Balance: return "balance";
288 case ClimbingAnchorEdgeKind::Swing: return "swing";
289 }
290 return "unknown";
291}
292
293eve::Value anchorOverlayValue(ClimbingAnchorAuthoringOverlay overlay) {
295 nodes.reserve(overlay.nodes.size());
296 for (auto& node : overlay.nodes)
297 nodes.push_back(eve::Value::object({
298 {"id", std::move(node.id)},
299 {"kind", anchorKindName(node.kind)},
300 {"position", vecValue(node.position)},
301 {"normal", vecValue(node.normal)},
302 {"tangent", vecValue(node.tangent)},
303 {"leftHandSocket", vecValue(node.leftHandSocket)},
304 {"rightHandSocket", vecValue(node.rightHandSocket)},
305 {"feetSocket", vecValue(node.feetSocket)},
306 {"occupancySlots", static_cast<std::int64_t>(node.occupancySlots)},
307 }));
309 edges.reserve(overlay.edges.size());
310 for (auto& edge : overlay.edges)
311 edges.push_back(eve::Value::object({
312 {"from", std::move(edge.from)},
313 {"to", std::move(edge.to)},
314 {"kind", anchorEdgeKindName(edge.kind)},
315 {"bidirectional", edge.bidirectional},
316 }));
317 return eve::Value::object({
318 {"graphId", std::move(overlay.graphId)},
319 {"buildSettingsHash", std::move(overlay.buildSettingsHash)},
320 {"nodes", eve::Value(std::move(nodes))},
321 {"edges", eve::Value(std::move(edges))},
322 });
323}
324
325eve::Result<eve::Value> anchorBakeResultValue(ClimbingAnchorBakeResult result) {
326 auto encoded = encodeClimbingAnchorGraphDefinition(result.graph);
327 if (!encoded) return eve::Result<eve::Value>::failure(encoded.status());
328 auto json = encoded.value().toJson();
329 if (!json) return eve::Result<eve::Value>::failure(json.status());
331 {"graphJson", std::move(json).takeValue()},
332 {"buildSettingsHash", std::move(result.graph.buildSettingsHash)},
333 {"ledgeNodeCount", static_cast<std::int64_t>(result.ledgeNodeCount)},
334 {"ladderNodeCount", static_cast<std::int64_t>(result.ladderNodeCount)},
335 }));
336}
337
338eve::Value identityValue(eve::Value value) { return value; }
339
340std::vector<std::string> requiredNotifiesForKind(ClimbingActionKind kind) {
341 switch (kind) {
342 case ClimbingActionKind::Vault: return {"contact.left_hand", "land"};
344 case ClimbingActionKind::ClimbUp: return {"contact.left_hand", "contact.right_hand", "land"};
345 case ClimbingActionKind::LedgeGrab: return {"contact.left_hand", "contact.right_hand"};
352 case ClimbingActionKind::LadderClimb: return {"contact.left_hand", "contact.right_hand"};
354 return {"contact.left_hand", "contact.right_hand", "land"};
355 case ClimbingActionKind::Slide: return {"collision.compact", "land"};
357 case ClimbingActionKind::BarSwing: return {"contact.left_hand", "contact.right_hand"};
359 case ClimbingActionKind::BeamBalance: return {};
360 }
361 return {};
362}
363
364std::optional<ClimbingCancelReason> parseCancelReason(std::string_view value) {
365 if (value == "player_request") return ClimbingCancelReason::PlayerRequest;
366 if (value == "drop_requested") return ClimbingCancelReason::DropRequested;
367 if (value == "link_stale") return ClimbingCancelReason::LinkStale;
368 if (value == "anchor_stale") return ClimbingCancelReason::AnchorStale;
369 if (value == "motion_blocked") return ClimbingCancelReason::MotionBlocked;
370 if (value == "warp_budget_exceeded") return ClimbingCancelReason::WarpBudgetExceeded;
371 if (value == "definition_reloaded") return ClimbingCancelReason::DefinitionReloaded;
372 return std::nullopt;
373}
374
375template <class Ref, class Proxy, class Release>
376ssq::Table makeOwnedProxy(HSQUIRRELVM vm, eve::Result<Ref>&& reference, Release&& release) {
378 const Ref ref = std::move(reference).takeValue();
379 auto object = eve::script::makeOwnedSquirrelInstance<Proxy>(vm, std::make_unique<Proxy>(ref));
380 if (!object) {
381 const eve::Status status = object.status();
382 object.ignore("failed to create owned climbing proxy");
383 std::invoke(std::forward<Release>(release), ref).ignore("rollback failed climbing allocation");
385 }
386 ssq::Object owned = std::move(object).takeValue();
388 result.set("value", owned);
389 result.set("ownership", std::string("owned"));
390 result.set("ownerEpoch", static_cast<std::int64_t>(ref.ownerEpoch));
391 result.set("handle", static_cast<std::int64_t>(ref.packed()));
392 return result;
393}
394
395
396struct ScriptClimbingAnchorGraph {
397 explicit ScriptClimbingAnchorGraph(ClimbingAnchorGraphHandleRef value) : reference(value) {}
398 ~ScriptClimbingAnchorGraph() noexcept {
399 Climbing::releaseAnchorGraph(reference).ignore("script climbing anchor graph proxy destruction");
400 }
402};
403
404} // namespace
405
406eve::Value projectClimbingAdvance(ClimbingAdvance value) { return advanceValue(std::move(value)); }
407
409
410void Climbing::expose(ssq::Table& table) {
411 const HSQUIRRELVM vm = table.getHandle();
412 auto graph = table.addClass<ScriptClimbingAnchorGraph>(
413 "ClimbingAnchorGraph", std::function<ScriptClimbingAnchorGraph*()>([]() { return nullptr; }), false);
414 graph.addFunc("ownership", [](ScriptClimbingAnchorGraph*) { return std::string("owned"); });
415 graph.addFunc("isStale", [](ScriptClimbingAnchorGraph* value) {
416 return !value || Climbing::isAnchorGraphStale(value->reference);
417 });
418 graph.addFunc("release", [vm](ScriptClimbingAnchorGraph* value) {
419 if (!value)
422 "climbing anchor graph proxy must not be null",
423 "anchorGraph", {}, "climbing.binding")));
425 });
426 graph.addFunc("node", [vm](ScriptClimbingAnchorGraph* value, const std::string& nodeId) {
427 if (!value)
429 vm,
431 eve::DiagnosticCode::InvalidArgument, "climbing anchor graph proxy must not be null", "anchorGraph",
432 {}, "climbing.binding")),
433 anchorNodeValue);
434 auto resolved = Climbing::resolveAnchorGraph(value->reference);
436 vm,
437 resolved.isBound() ? resolved->nodeRef(nodeId)
438 : eve::Result<ClimbingAnchorNodeRef>::failure(eve::Diagnostic::error(
439 eve::DiagnosticCode::StaleHandle, "climbing anchor graph handle is stale",
440 "anchorGraph", {}, "climbing.binding")),
441 anchorNodeValue);
442 });
443 graph.addFunc("reloadJson", [vm](ScriptClimbingAnchorGraph* value, const std::string& json) {
444 if (!value)
446 vm,
448 eve::DiagnosticCode::InvalidArgument, "climbing anchor graph proxy must not be null", "anchorGraph",
449 {}, "climbing.binding")),
450 graphReloadValue);
451 auto parsed = eve::Value::fromJson(json);
452 if (!parsed)
454 vm, eve::Result<ClimbingAnchorGraphReload>::failure(parsed.status()), graphReloadValue);
455 auto definition = decodeClimbingAnchorGraphDefinition(parsed.value());
456 if (!definition)
458 vm, eve::Result<ClimbingAnchorGraphReload>::failure(definition.status()), graphReloadValue);
459 auto resolved = Climbing::resolveAnchorGraph(value->reference);
461 vm,
462 resolved.isBound() ? resolved->reload(std::move(definition).takeValue())
463 : eve::Result<ClimbingAnchorGraphReload>::failure(eve::Diagnostic::error(
464 eve::DiagnosticCode::StaleHandle, "climbing anchor graph handle is stale",
465 "anchorGraph", {}, "climbing.binding")),
466 graphReloadValue);
467 });
468 graph.addFunc("planRoute", [vm](ScriptClimbingAnchorGraph* value, const std::string& startNodeId,
469 const std::string& goalNodeId, bool avoidFull, std::int64_t agentId,
470 std::int64_t executionId, std::int64_t maxVisitedNodes) {
471 if (!value || agentId < 0 || executionId < 0 || (agentId == 0) != (executionId == 0) ||
472 maxVisitedNodes <= 0 || maxVisitedNodes > 65536)
476 "graph, paired non-negative owner ids, and route bound are required",
477 "planRoute", {}, "climbing.binding")),
478 anchorRouteValue);
479 auto resolved = Climbing::resolveAnchorGraph(value->reference);
480 if (!resolved.isBound())
482 vm,
484 eve::Diagnostic::error(eve::DiagnosticCode::StaleHandle, "climbing anchor graph handle is stale",
485 "anchorGraph", {}, "climbing.binding")),
486 anchorRouteValue);
487 auto start = resolved->nodeRef(startNodeId);
488 if (!start)
490 anchorRouteValue);
491 auto goal = resolved->nodeRef(goalNodeId);
492 if (!goal)
494 anchorRouteValue);
495 ClimbingAnchorRouteRequest request;
496 request.start = std::move(start).takeValue();
497 request.goal = std::move(goal).takeValue();
498 request.occupancyPolicy = avoidFull ? ClimbingRouteOccupancyPolicy::AvoidFull
500 request.requester = {
501 ClimbingAnchorAgentId(static_cast<std::uint64_t>(agentId)),
502 ClimbingExecutionId(static_cast<std::uint64_t>(executionId)),
503 };
504 request.maxVisitedNodes = static_cast<std::uint32_t>(maxVisitedNodes);
505 return eve::script::projectResult(vm, resolved->planRoute(request), anchorRouteValue);
506 });
507 graph.addFunc("generation", [](ScriptClimbingAnchorGraph* value) -> std::string {
508 if (!value) return {};
509 auto resolved = Climbing::resolveAnchorGraph(value->reference);
510 return resolved.isBound() ? std::to_string(resolved->generation()) : std::string{};
511 });
512 graph.addFunc("reservationCount", [](ScriptClimbingAnchorGraph* value) -> std::int64_t {
513 if (!value) return 0;
514 auto resolved = Climbing::resolveAnchorGraph(value->reference);
515 return resolved.isBound() ? static_cast<std::int64_t>(resolved->reservationCount()) : 0;
516 });
517 auto runtime = table.addClass<ScriptClimbingRuntime>(
518 "ClimbingRuntime", std::function<ScriptClimbingRuntime*()>([]() { return nullptr; }), false);
519 runtime.addFunc("ownership", [](ScriptClimbingRuntime*) { return std::string("owned"); });
520 runtime.addFunc("isStale",
521 [](ScriptClimbingRuntime* value) { return !value || Climbing::isStale(value->reference); });
522 runtime.addFunc("release", [vm](ScriptClimbingRuntime* value) {
523 if (!value)
526 "climbing runtime proxy must not be null",
527 "runtime", {}, "climbing.binding")));
529 });
530 runtime.addFunc("setProfileJson", [vm](ScriptClimbingRuntime* value, const std::string& json) {
531 if (!value)
534 "climbing runtime proxy must not be null",
535 "runtime", {}, "climbing.binding")));
536 auto resolved = Climbing::resolve(value->reference);
538 vm, resolved.isBound() ? resolved->setProfileJson(json)
539 : eve::Result<void>::failure(eve::Diagnostic::error(
540 eve::DiagnosticCode::StaleHandle, "climbing runtime handle is stale",
541 "runtime", {}, "climbing.binding")));
542 });
543 runtime.addFunc("reloadProfileJson", [vm](ScriptClimbingRuntime* value, const std::string& json) {
544 if (!value)
547 "climbing runtime proxy must not be null",
548 "runtime", {}, "climbing.binding")));
549 auto resolved = Climbing::resolve(value->reference);
551 vm, resolved.isBound() ? resolved->reloadProfileJson(json)
552 : eve::Result<void>::failure(eve::Diagnostic::error(
553 eve::DiagnosticCode::StaleHandle, "climbing runtime handle is stale",
554 "runtime", {}, "climbing.binding")));
555 });
556 runtime.addFunc("setProfile", [vm](ScriptClimbingRuntime* value, float radius, float height, float skin,
557 float probeDistance, float obstacleHeight, float minTopNormalY,
558 float maxWarpResidual, int maskBits) {
559 if (!value)
562 "climbing runtime proxy must not be null",
563 "runtime", {}, "climbing.binding")));
564 auto resolved = Climbing::resolve(value->reference);
565 if (!resolved.isBound())
568 "climbing runtime handle is stale", "runtime", {},
569 "climbing.binding")));
570 ClimbingProfile profile;
571 profile.capsuleRadius = radius;
572 profile.capsuleHeight = height;
573 profile.skin = skin;
574 profile.maxProbeDistance = probeDistance;
575 profile.maxObstacleHeight = obstacleHeight;
576 profile.minTopNormalY = minTopNormalY;
577 profile.maxWarpResidual = maxWarpResidual;
578 profile.queryFilter.maskBits = static_cast<std::uint32_t>(maskBits);
579 return eve::script::projectResult(vm, resolved->setProfile(std::move(profile)));
580 });
581 runtime.addFunc("upsertAction", [vm](ScriptClimbingRuntime* value, const std::string& id, float minHeight,
582 float maxHeight, float minSpeed, float durationSeconds, float landingForward,
583 float apexHeight, int selectionBias) {
584 if (!value)
587 "climbing runtime proxy must not be null",
588 "runtime", {}, "climbing.binding")));
589 auto resolved = Climbing::resolve(value->reference);
590 if (!resolved.isBound())
593 "climbing runtime handle is stale", "runtime", {},
594 "climbing.binding")));
595 auto duration = eve::Duration::fromSeconds(durationSeconds);
598 vm, resolved->upsertAction({id, minHeight, maxHeight, minSpeed, std::move(duration).takeValue(),
599 landingForward, apexHeight, selectionBias}));
600 });
601 runtime.addFunc("upsertActionKind", [vm](ScriptClimbingRuntime* value, const std::string& id,
602 const std::string& kind, float minHeight, float maxHeight, float minSpeed,
603 float durationSeconds, float landingForward, float apexHeight,
604 int selectionBias, float hangBodyOffset, float hangFeetBelowLedge,
605 float handSpacing, float cancelStart, float cancelEnd) {
606 if (!value)
609 "climbing runtime proxy must not be null",
610 "runtime", {}, "climbing.binding")));
611 auto resolved = Climbing::resolve(value->reference);
612 if (!resolved.isBound())
615 "climbing runtime handle is stale", "runtime", {},
616 "climbing.binding")));
617 const auto parsedKind = parseActionKind(kind);
618 if (!parsedKind)
621 "unknown climbing action kind", "kind", {}, "climbing.binding")));
622 auto duration = eve::Duration::fromSeconds(durationSeconds);
624 ClimbingActionDefinition action{
625 id, minHeight, maxHeight, minSpeed, std::move(duration).takeValue(), landingForward,
626 apexHeight, selectionBias};
627 action.kind = *parsedKind;
628 action.hangBodyOffset = hangBodyOffset;
629 action.hangFeetBelowLedge = hangFeetBelowLedge;
630 action.handSpacing = handSpacing;
631 action.cancelWindowStart = cancelStart;
632 action.cancelWindowEnd = cancelEnd;
633 action.requiredNotifies = requiredNotifiesForKind(*parsedKind);
634 return eve::script::projectResult(vm, resolved->upsertAction(std::move(action)));
635 });
636 runtime.addFunc("validateActionClip", [vm](ScriptClimbingRuntime* value, const std::string& actionId,
637 animation::AnimClip* clip) {
638 if (!value || !clip)
641 "runtime and animation clip are required",
642 "validateActionClip", {}, "climbing.binding")));
643 auto resolved = Climbing::resolve(value->reference);
645 vm, resolved.isBound() ? resolved->validateAnimationBinding(actionId, *clip)
646 : eve::Result<void>::failure(eve::Diagnostic::error(
647 eve::DiagnosticCode::StaleHandle, "climbing runtime handle is stale",
648 "runtime", {}, "climbing.binding")));
649 });
650 runtime.addFunc("probeMode", [vm](ScriptClimbingRuntime* value, physics::World3D* world, float x, float y, float z,
651 float forwardX, float forwardZ, float speed, float verticalSpeed, bool grounded) {
652 if (!value || !world)
654 vm,
656 "runtime and world are required",
657 "probeMode", {}, "climbing.binding")),
658 candidateValue);
659 auto resolved = Climbing::resolve(value->reference);
660 if (!resolved.isBound())
663 eve::DiagnosticCode::StaleHandle, "climbing runtime handle is stale",
664 "runtime", {}, "climbing.binding")),
665 candidateValue);
666 auto candidates =
667 resolved->probe(*world, {{x, y, z}, {forwardX, 0.f, forwardZ}, speed, -1, verticalSpeed, grounded});
668 if (!candidates.ok())
670 candidateValue);
671 if (candidates.value().empty())
673 vm,
675 eve::DiagnosticCode::NotFound, "no traversable obstacle", "probeMode", {}, "climbing.binding")),
676 candidateValue);
678 candidateValue);
679 });
680 runtime.addFunc("tryBegin", [vm](ScriptClimbingRuntime* value, physics::World3D* world, float x, float y, float z,
681 float forwardX, float forwardZ, float speed, int ignoredBodyId,
682 std::int64_t tick) {
683 if (!value || !world || tick < 0)
685 vm,
687 eve::DiagnosticCode::InvalidArgument, "runtime, world, and non-negative tick are required",
688 "tryBegin", {}, "climbing.binding")),
689 candidateValue);
690 auto resolved = Climbing::resolve(value->reference);
691 if (!resolved.isBound())
694 eve::DiagnosticCode::StaleHandle, "climbing runtime handle is stale",
695 "runtime", {}, "climbing.binding")),
696 candidateValue);
698 vm,
699 resolved->tryBegin(*world, {{x, y, z}, {forwardX, 0.f, forwardZ}, speed, ignoredBodyId},
700 eve::SimulationTick(static_cast<std::uint64_t>(tick))),
701 candidateValue);
702 });
703 runtime.addFunc("tryBeginMode", [vm](ScriptClimbingRuntime* value, physics::World3D* world, float x, float y,
704 float z, float forwardX, float forwardZ, float speed, int ignoredBodyId,
705 float verticalSpeed, bool grounded, std::int64_t tick) {
706 if (!value || !world || tick < 0)
708 vm,
710 eve::DiagnosticCode::InvalidArgument, "runtime, world, and non-negative tick are required",
711 "tryBeginMode", {}, "climbing.binding")),
712 candidateValue);
713 auto resolved = Climbing::resolve(value->reference);
714 if (!resolved.isBound())
717 eve::DiagnosticCode::StaleHandle, "climbing runtime handle is stale",
718 "runtime", {}, "climbing.binding")),
719 candidateValue);
721 vm,
722 resolved->tryBegin(*world,
723 {{x, y, z}, {forwardX, 0.f, forwardZ}, speed, ignoredBodyId, verticalSpeed, grounded},
724 eve::SimulationTick(static_cast<std::uint64_t>(tick))),
725 candidateValue);
726 });
727 runtime.addFunc("tryBeginAnchor", [vm](ScriptClimbingRuntime* value, ScriptClimbingAnchorGraph* graphValue,
728 physics::World3D* world, const std::string& nodeId,
729 std::int64_t agentId, const std::string& actionId, float x, float y,
730 float z, float forwardX, float forwardZ, float speed, int ignoredBodyId,
731 float verticalSpeed, bool grounded, std::int64_t tick) {
732 if (!value || !graphValue || !world || agentId <= 0 || tick < 0)
736 "runtime, graph, world, positive agent id, and tick are required",
737 "tryBeginAnchor", {}, "climbing.binding")),
738 candidateValue);
739 auto runtimeResolved = Climbing::resolve(value->reference);
740 auto graphResolved = Climbing::resolveAnchorGraph(graphValue->reference);
741 if (!runtimeResolved.isBound() || !graphResolved.isBound())
743 vm,
745 eve::DiagnosticCode::StaleHandle, "climbing runtime or anchor graph handle is stale", "runtime", {},
746 "climbing.binding")),
747 candidateValue);
748 auto node = graphResolved->nodeRef(nodeId);
749 if (!node)
751 candidateValue);
753 vm,
754 runtimeResolved->tryBeginAnchor(
755 *world, graphValue->reference, node.value(),
756 ClimbingAnchorAgentId(static_cast<std::uint64_t>(agentId)), actionId,
757 {{x, y, z}, {forwardX, 0.f, forwardZ}, speed, ignoredBodyId, verticalSpeed, grounded},
758 eve::SimulationTick(static_cast<std::uint64_t>(tick))),
759 candidateValue);
760 });
761 runtime.addFunc("transitionAnchor", [vm](ScriptClimbingRuntime* value,
762 ScriptClimbingAnchorGraph* graphValue,
763 physics::World3D* world, const std::string& targetNodeId,
764 const std::string& edgeKind, const std::string& actionId,
765 std::int64_t tick) {
766 if (!value || !graphValue || !world || tick < 0)
769 "runtime, graph, world, and non-negative tick are required",
770 "transitionAnchor", {}, "climbing.binding")));
771 const auto parsedEdge = parseAnchorEdgeKind(edgeKind);
772 if (!parsedEdge)
775 "unknown climbing anchor edge kind", "edgeKind",
776 {}, "climbing.binding")));
777 auto runtimeResolved = Climbing::resolve(value->reference);
778 auto graphResolved = Climbing::resolveAnchorGraph(graphValue->reference);
779 if (!runtimeResolved.isBound() || !graphResolved.isBound())
782 eve::DiagnosticCode::StaleHandle, "climbing runtime or anchor graph handle is stale", "runtime",
783 {}, "climbing.binding")));
784 auto node = graphResolved->nodeRef(targetNodeId);
787 vm, runtimeResolved->transitionAnchor(*world, node.value(), *parsedEdge, actionId,
788 eve::SimulationTick(static_cast<std::uint64_t>(tick))));
789 });
790 runtime.addFunc("currentAnchor", [vm](ScriptClimbingRuntime* value) {
791 if (!value)
793 vm,
795 eve::DiagnosticCode::InvalidArgument, "climbing runtime proxy must not be null", "runtime", {},
796 "climbing.binding")),
797 anchorNodeValue);
798 auto resolved = Climbing::resolve(value->reference);
800 vm,
801 resolved.isBound() ? resolved->currentAnchor()
802 : eve::Result<ClimbingAnchorNodeRef>::failure(eve::Diagnostic::error(
803 eve::DiagnosticCode::StaleHandle, "climbing runtime handle is stale", "runtime",
804 {}, "climbing.binding")),
805 anchorNodeValue);
806 });
808 runtime.addFunc("cancel", [vm](ScriptClimbingRuntime* value, const std::string& reason, std::int64_t tick) {
809 if (!value || tick < 0)
812 "runtime and non-negative tick are required",
813 "cancel", {}, "climbing.binding")));
814 auto resolved = Climbing::resolve(value->reference);
815 if (!resolved.isBound())
818 "climbing runtime handle is stale", "runtime", {},
819 "climbing.binding")));
820 const auto parsed = parseCancelReason(reason);
821 if (!parsed)
824 "unknown climbing cancellation reason", "reason",
825 {}, "climbing.binding")));
827 vm, resolved->cancel(*parsed, eve::SimulationTick(static_cast<std::uint64_t>(tick))));
828 });
829 runtime.addFunc("drainEvents", [vm](ScriptClimbingRuntime* value) {
830 if (!value)
832 vm,
833 eve::Result<std::vector<ClimbingEvent>>::failure(eve::Diagnostic::error(
834 eve::DiagnosticCode::InvalidArgument, "climbing runtime proxy must not be null", "runtime", {},
835 "climbing.binding")),
836 eventBatchValue);
837 auto resolved = Climbing::resolve(value->reference);
839 vm,
840 resolved.isBound() ? resolved->drainEvents()
841 : eve::Result<std::vector<ClimbingEvent>>::failure(eve::Diagnostic::error(
842 eve::DiagnosticCode::StaleHandle, "climbing runtime handle is stale", "runtime",
843 {}, "climbing.binding")),
844 eventBatchValue);
845 });
846 runtime.addFunc("drop", [vm](ScriptClimbingRuntime* value, std::int64_t tick) {
847 if (!value || tick < 0)
850 "runtime and non-negative tick are required",
851 "drop", {}, "climbing.binding")));
852 auto resolved = Climbing::resolve(value->reference);
854 vm, resolved.isBound() ? resolved->drop(eve::SimulationTick(static_cast<std::uint64_t>(tick)))
855 : eve::Result<void>::failure(eve::Diagnostic::error(
856 eve::DiagnosticCode::StaleHandle, "climbing runtime handle is stale",
857 "runtime", {}, "climbing.binding")));
858 });
859 runtime.addFunc("climbUp", [vm](ScriptClimbingRuntime* value, std::int64_t tick) {
860 if (!value || tick < 0)
863 "runtime and non-negative tick are required",
864 "climbUp", {}, "climbing.binding")));
865 auto resolved = Climbing::resolve(value->reference);
867 vm, resolved.isBound() ? resolved->climbUp(eve::SimulationTick(static_cast<std::uint64_t>(tick)))
868 : eve::Result<void>::failure(eve::Diagnostic::error(
869 eve::DiagnosticCode::StaleHandle, "climbing runtime handle is stale",
870 "runtime", {}, "climbing.binding")));
871 });
872 runtime.addFunc("snapshotJson", [vm](ScriptClimbingRuntime* value) {
873 if (!value)
875 vm,
877 "climbing runtime proxy must not be null",
878 "runtime", {}, "climbing.binding")),
879 [](std::string text) { return eve::Value(std::move(text)); });
880 auto resolved = Climbing::resolve(value->reference);
882 vm,
883 resolved.isBound() ? resolved->snapshotJson()
884 : eve::Result<std::string>::failure(eve::Diagnostic::error(
885 eve::DiagnosticCode::StaleHandle, "climbing runtime handle is stale", "runtime",
886 {}, "climbing.binding")),
887 [](std::string text) { return eve::Value(std::move(text)); });
888 });
889 runtime.addFunc("restoreJson", [vm](ScriptClimbingRuntime* value, const std::string& json,
890 physics::World3D* world) {
891 if (!value || !world)
894 "runtime and world are required", "restoreJson",
895 {}, "climbing.binding")));
896 auto resolved = Climbing::resolve(value->reference);
898 vm, resolved.isBound() ? resolved->restoreJson(json, *world)
899 : eve::Result<void>::failure(eve::Diagnostic::error(
900 eve::DiagnosticCode::StaleHandle, "climbing runtime handle is stale",
901 "runtime", {}, "climbing.binding")));
902 });
903 runtime.addFunc("definitionGeneration", [](ScriptClimbingRuntime* value) -> std::string {
904 if (!value) return {};
905 auto resolved = Climbing::resolve(value->reference);
906 return resolved.isBound() ? std::to_string(resolved->definitionGeneration()) : std::string{};
907 });
908 runtime.addFunc("setDebugCapture", [vm](ScriptClimbingRuntime* value, bool enabled) {
909 if (!value)
912 "climbing runtime proxy must not be null",
913 "runtime", {}, "climbing.binding")));
914 auto resolved = Climbing::resolve(value->reference);
915 if (!resolved.isBound())
918 "climbing runtime handle is stale", "runtime", {},
919 "climbing.binding")));
920 resolved->setDebugCapture(enabled ? ClimbingDebugCapture::Enabled : ClimbingDebugCapture::Disabled);
922 });
923 runtime.addFunc("inspect", [vm](ScriptClimbingRuntime* value) {
924 if (!value)
926 vm,
928 eve::DiagnosticCode::InvalidArgument, "climbing runtime proxy must not be null", "runtime", {},
929 "climbing.binding")),
930 debugValue);
931 auto resolved = Climbing::resolve(value->reference);
932 if (!resolved.isBound())
935 eve::DiagnosticCode::StaleHandle, "climbing runtime handle is stale",
936 "runtime", {}, "climbing.binding")),
937 debugValue);
939 debugValue);
940 });
941
942 auto module = table.addClass(name, Climbing::create, false);
943 expose(module);
944}
945
946void Climbing::expose(ssq::Class& cls) {
947 cls.addFunc("getName", &Climbing::getName);
948 cls.addFunc("newRuntime", [vm = cls.getHandle()](Climbing*) {
949 return makeOwnedProxy<ClimbingRuntimeHandleRef, ScriptClimbingRuntime>(
950 vm, Climbing::newRuntime(),
951 [](ClimbingRuntimeHandleRef reference) { return Climbing::release(reference); });
952 });
953 cls.addFunc("newAnchorGraphJson", [vm = cls.getHandle()](Climbing*, const std::string& json,
954 physics::World3D* world, physics::Body3D* body) {
955 if (!world || !body)
956 return makeOwnedProxy<ClimbingAnchorGraphHandleRef, ScriptClimbingAnchorGraph>(
957 vm,
960 "newAnchorGraphJson", {}, "climbing.binding")),
961 [](ClimbingAnchorGraphHandleRef reference) { return Climbing::releaseAnchorGraph(reference); });
962 auto parsed = eve::Value::fromJson(json);
963 if (!parsed)
964 return makeOwnedProxy<ClimbingAnchorGraphHandleRef, ScriptClimbingAnchorGraph>(
966 [](ClimbingAnchorGraphHandleRef reference) { return Climbing::releaseAnchorGraph(reference); });
967 auto definition = decodeClimbingAnchorGraphDefinition(parsed.value());
968 if (!definition)
969 return makeOwnedProxy<ClimbingAnchorGraphHandleRef, ScriptClimbingAnchorGraph>(
971 [](ClimbingAnchorGraphHandleRef reference) { return Climbing::releaseAnchorGraph(reference); });
972 return makeOwnedProxy<ClimbingAnchorGraphHandleRef, ScriptClimbingAnchorGraph>(
973 vm, Climbing::newAnchorGraph(std::move(definition).takeValue(), *world, body->runtimeHandle()),
974 [](ClimbingAnchorGraphHandleRef reference) { return Climbing::releaseAnchorGraph(reference); });
975 });
976 cls.addFunc("bakeAnchorGraphJson", [vm = cls.getHandle()](Climbing*, const std::string& json) {
977 auto parsed = eve::Value::fromJson(json);
978 if (!parsed)
980 vm, eve::Result<eve::Value>::failure(parsed.status()), identityValue);
981 auto request = decodeClimbingAnchorBakeRequest(parsed.value());
982 if (!request)
984 vm, eve::Result<eve::Value>::failure(request.status()), identityValue);
985 auto baked = bakeClimbingAnchorGraph(std::move(request).takeValue());
986 if (!baked)
988 vm, eve::Result<eve::Value>::failure(baked.status()), identityValue);
989 return eve::script::projectResult(vm, anchorBakeResultValue(std::move(baked).takeValue()), identityValue);
990 });
991 cls.addFunc("inspectAnchorGraphJson", [vm = cls.getHandle()](Climbing*, const std::string& json) {
992 auto parsed = eve::Value::fromJson(json);
993 if (!parsed)
995 vm, eve::Result<ClimbingAnchorAuthoringOverlay>::failure(parsed.status()), anchorOverlayValue);
996 auto graph = decodeClimbingAnchorGraphDefinition(parsed.value());
997 if (!graph)
999 vm, eve::Result<ClimbingAnchorAuthoringOverlay>::failure(graph.status()), anchorOverlayValue);
1001 vm, inspectClimbingAnchorGraphAuthoring(graph.value()), anchorOverlayValue);
1002 });
1003}
1004
1005} // namespace eve::climbing
double value
Duration start
float y
Definition AnimClip.cpp:738
float x
Definition AnimClip.cpp:738
float z
Definition AnimClip.cpp:738
float duration
eve::action::ActionExecutionId executionId
ActiveSource owned
AuthorityStoreHandleRef reference
Definition Authority.cpp:24
float phase
Definition CaveMesh.cpp:58
Deterministic climbing/parkour planning and capsule-constrained execution.
struct SQVM * HSQUIRRELVM
std::string nodeId
HSQUIRRELVM vm
Definition ECS.cpp:20
HSQOBJECT cls
Definition ECS.cpp:21
const GltfImportRequest & request
wgpu::PopErrorScopeStatus status
glm::vec4 clip
std::uint32_t height
std::string text
TokenKind kind
std::vector< BvhNode > nodes
#define Module_IMPL(ModuleName, newExpr)
Definition Module.h:26
std::string error
Definition Package.cpp:60
std::map< std::string, std::vector< std::string > > graph
Definition Package.cpp:59
World3D * world
float radius
std::string action
Definition PlayHost.cpp:117
std::string id
Definition PlayHost.cpp:108
std::function< eve::Result< void >()> release
Definition Procgen.cpp:86
int steps
const RoadNode * node
const RoadEdge * edge
std::string string
The single Squirrel projection for common Result, Status and Value.
Battle::Events events
SimulationTick tick
Json object
float step
Definition TreeMesh.cpp:314
std::string body
std::vector< int > edges
static Diagnostic error(DiagnosticCode code, std::string message, std::string path={}, DiagnosticDetails details={}, std::string source={})
Construct an error diagnostic with the standard error severity.
Definition Diagnostic.h:125
static Result< Duration > fromSeconds(double seconds)
Convert finite seconds to the nearest nanosecond.
Definition Time.cpp:16
void ignore(std::string_view reason={}) const noexcept
Explicitly discard this result after documenting the reason.
Definition Result.h:537
Move-only operation result carrying either a value or Status.
Definition Result.h:155
static Result success(T value)
Construct a successful result owning value.
Definition Result.h:164
static Result failure(Status status)
Construct a failed result from a structured status.
Definition Result.h:175
Structured status and zero or more diagnostics for an operation.
Definition Status.h:68
static Status success(StatusCode code=StatusCode::Ok)
Construct a successful status with an explicit non-error outcome.
Definition Status.h:81
The canonical owning dynamic value used by data-facing protocols.
Definition Value.h:31
static Result< Value > fromJson(std::string_view json)
Parse one strict JSON value into an owning Value.
Definition Value.cpp:57
std::vector< Value > Array
Definition Value.h:33
static Value object(Object value)
Compatibility factory for an object value.
Definition Value.h:114
Script-facing factory and owner for independent climbing runtimes.
Definition Climbing.h:891
static eve::Result< void > release(ClimbingRuntimeHandleRef reference)
Releases a module-owned runtime and invalidates its reference.
static eve::Result< void > releaseAnchorGraph(ClimbingAnchorGraphHandleRef reference)
Releases a module-owned graph instance and all of its reservations.
static bool isStale(ClimbingRuntimeHandleRef reference) noexcept
Reports whether a runtime reference is stale.
static eve::script::Borrowed< ClimbingAnchorGraphInstance > resolveAnchorGraph(ClimbingAnchorGraphHandleRef reference) noexcept
Resolves a live graph instance as a synchronous non-owning observation.
static bool isAnchorGraphStale(ClimbingAnchorGraphHandleRef reference) noexcept
Reports whether a graph instance reference is stale.
static eve::script::Borrowed< ClimbingRuntime > resolve(ClimbingRuntimeHandleRef reference) noexcept
Resolves a live runtime as a non-owning observation.
std::string eventKindName(EventKind kind)
Returns the stable lowercase name of an event kind.
I * query()
Queries .
Definition Capability.h:88
eve::Result< ClimbingAnchorBakeResult > bakeClimbingAnchorGraph(const ClimbingAnchorBakeRequest &request)
Bakes deterministic ledge and ladder topology from owning semantic geometry samples.
eve::detail::StrongUint64< detail::ClimbingExecutionIdTag > ClimbingExecutionId
Stable non-zero identity assigned when a climbing selection transaction commits.
ClimbingAnchorEdgeKind
Allowed authored transition between two explicit anchors.
eve::Result< ClimbingAnchorGraphDefinition > decodeClimbingAnchorGraphDefinition(const eve::Value &value)
Decodes and validates a complete owning graph candidate transactionally.
ClimbingAnchorKind
Authored semantic of one explicit climbing anchor.
ClimbingPhase
Execution lifecycle visible to gameplay and animation adapters.
Definition Climbing.h:470
eve::Result< ClimbingAnchorAuthoringOverlay > inspectClimbingAnchorGraphAuthoring(const ClimbingAnchorGraphDefinition &graph)
Builds a validated owning snapshot for an editor or debug-draw adapter.
void exposeClimbingMotionBindings(ssq::Class &runtime, HSQUIRRELVM vm)
Expose climbing motion bindings.
ClimbingDebugCapture
Runtime policy controlling optional owning diagnostic capture.
Definition Climbing.h:600
eve::script::RuntimeHandleRef< ClimbingAnchorGraphHandleTag > ClimbingAnchorGraphHandleRef
Generation- and module-epoch-qualified anchor graph instance reference.
ClimbingEventKind
Stable semantic event emitted by the authoritative climbing lifecycle.
Definition Climbing.h:488
ClimbingActionKind
Data-driven action family; stable action identity remains the definition id.
Definition Climbing.h:46
eve::detail::StrongUint64< detail::ClimbingAnchorAgentIdTag > ClimbingAnchorAgentId
Stable non-zero identity of the character/agent that owns graph occupancy.
eve::Result< ClimbingAnchorBakeRequest > decodeClimbingAnchorBakeRequest(const eve::Value &value)
Decodes all known bake request fields without publishing a graph.
eve::Result< eve::Value > encodeClimbingAnchorGraphDefinition(const ClimbingAnchorGraphDefinition &graph)
Encodes a validated graph to its canonical owning Value representation.
ClimbingProfileDefinition ClimbingProfile
Compatibility-only spelling; ClimbingProfileDefinition is canonical.
Definition Climbing.h:272
eve::Value projectClimbingAdvance(ClimbingAdvance value)
Project climbing advance.
std::variant< std::monostate, std::int64_t, double, std::string, bool > Value
Definition Database.h:26
eve::Diagnostic Diagnostic
eve::Result< T > Result
std::unordered_map< std::string, SkillDefinition > & table()
Definition Skill.cpp:65
ssq::Table projectStatusResult(HSQUIRRELVM vm, const Status &status)
Project a checked native status that carries no payload.
ssq::Table projectResult(HSQUIRRELVM vm, Result< void > &&result)
Consume and project a void native Result using the common schema.
std::string_view phaseName(BattlePhase phase) noexcept
Return the stable protocol spelling of a battle phase.
Build metadata (engine git commit, build time, third-party version).
Definition Build.cpp:16
DiagnosticCode
Stable machine-readable diagnostic codes.
Definition Diagnostic.h:47
detail::StrongUint64< detail::SimulationTickTag > SimulationTick
Deterministic simulation time step; it is not wall-clock time.
Definition Time.h:31
bool enabled
Owning output of one execution tick.
Definition Climbing.h:553