20bool parseDefinition(Value o, std::unordered_map<std::string, CardDefinition> &defs) {
22 if (
id.empty())
return false;
25 def.
name = o.getString(
"name",
id);
26 def.
kind = o.getString(
"kind",
"creature");
27 def.
cost = o.getInt(
"cost");
28 def.
attack = o.getInt(
"attack");
29 def.
health = o.getInt(
"health");
30 auto tint = o.getFloatArray(
"tint");
31 if (tint.size() >= 3) def.
tint = glm::vec3(tint[0], tint[1], tint[2]);
37T *resolve(
const ecs::EntityHandle &
h) {
38 return static_cast<T *
>(ecs::try_get(
h));
41void destroyHandles(std::vector<ecs::EntityHandle> &hs) {
43 if (ecs::Entity *e = ecs::try_get(
h)) ecs::DestroyEntity(e);
49void registerCppEntityClassForScript() {
52 sq_pushobject(
vm, out.getRaw());
53 ecs::Table *table = ecs::current();
54 if (table ==
nullptr) {
58 ecs::IComponentManager &cm = table->getOrCreateManager<T>();
59 auto *reg = cm.getOrCreateRegistryComponentBuffer<T>();
60 std::vector<ecs::IComponentBuffer *> stack;
62 while (!stack.empty()) {
63 ecs::IComponentBuffer *buf = stack.back();
65 auto *r =
dynamic_cast<ecs::IRegistryComponentBuffer *
>(buf);
67 for (uint32_t i = 0; i < r->entity_count(); ++i) {
68 ecs::Entity *ent = r->entity_at(i);
69 if (ent !=
nullptr && ecs::is_entity_visible(ent)) {
70 ssq::detail::pushByPtr<T>(
vm,
static_cast<T *
>(ent));
71 sq_arrayappend(
vm, -2);
75 if (buf->children !=
nullptr) stack.push_back(buf->children);
76 if (buf->next !=
nullptr) stack.push_back(buf->next);
85 destroyHandles(hands_);
86 destroyHandles(decks_);
87 destroyHandles(zones_);
88 destroyHandles(cards_);
89 activeDeck_ =
nullptr;
90 activeConfig_ =
nullptr;
99 if (!doc.
valid())
return 0;
100 const Value root = doc.
root();
102 if (root.isArray()) {
103 for (
size_t i = 0; i < root.size(); ++i)
104 if (parseDefinition(root.at(i), defs_)) ++
n;
105 }
else if (root.isObject()) {
106 if (parseDefinition(root, defs_)) ++
n;
118 auto it = defs_.find(
id);
119 return it == defs_.end() ? nullptr : &it->second;
123 const auto *
d = findDef(
id);
124 return d ?
d->name : std::string{};
128 const auto *
d = findDef(
id);
129 return d ?
d->kind : std::string{};
133 const auto *
d = findDef(
id);
134 return d ?
d->cost : 0;
138 const auto *
d = findDef(
id);
139 return d ?
d->attack : 0;
143 const auto *
d = findDef(
id);
144 return d ?
d->health : 0;
148 const auto *
d = findDef(
id);
149 return d ?
d->tint.r : 0.f;
153 const auto *
d = findDef(
id);
154 return d ?
d->tint.g : 0.f;
158 const auto *
d = findDef(
id);
159 return d ?
d->tint.b : 0.f;
167 auto cfg = std::make_unique<LayoutConfig>();
169 configs_.push_back(std::move(cfg));
170 if (!activeConfig_) activeConfig_ = raw;
176 if (!
d)
return nullptr;
178 c->identity()->id = defId +
"#" + std::to_string(nextInstance_++);
179 c->identity()->name =
d->name;
180 c->identity()->kind =
d->kind;
181 c->stats()->cost =
d->cost;
182 c->stats()->attack =
d->attack;
183 c->stats()->health =
d->health;
184 c->visual()->tint =
d->tint;
185 cards_.push_back(ecs::handle_of(
c));
191 decks_.push_back(ecs::handle_of(
d));
198 auto *R =
z->rect().operator->();
205 zones_.push_back(ecs::handle_of(
z));
211 h->meta()->config = cfg;
212 hands_.push_back(ecs::handle_of(
h));
221 if (cfg) activeConfig_ = cfg;
229 if (index < 0 ||
static_cast<size_t>(index) >= hands_.size())
return nullptr;
230 return resolve<Hand>(hands_[
static_cast<size_t>(index)]);
234 for (
const auto &
h : hands_) {
235 Hand *hand = resolve<Hand>(
h);
236 if (hand && hand->meta()->owner == owner)
return hand;
244 if (index < 0 ||
static_cast<size_t>(index) >= zones_.size())
return nullptr;
245 return resolve<Zone>(zones_[
static_cast<size_t>(index)]);
252 if (!
h || !activeDeck_)
return nullptr;
254 if (!
c)
return nullptr;
256 c->layout()->x = activeConfig_->
deckX;
257 c->layout()->y = activeConfig_->
deckY;
269 std::vector<Zone *> zonePtrs;
270 zonePtrs.reserve(zones_.size());
271 for (
auto &
h : zones_) {
272 if (
Zone *
z = resolve<Zone>(
h)) zonePtrs.push_back(
z);
274 for (
auto &
h : hands_) {
275 if (
Hand *hand = resolve<Hand>(
h)) hand->update(dt, mx, my, down, zonePtrs, events_);
280 if (activeConfig_ && activeConfig_->
showZones) {
281 for (
auto &
h : zones_) {
282 if (
Zone *
z = resolve<Zone>(
h))
z->render(gfx,
true);
285 for (
auto &
h : hands_) {
286 if (
Hand *hand = resolve<Hand>(
h)) hand->render(gfx);
291 if (!activeDeck_ || !activeConfig_)
return;
293 const int c = activeDeck_->
count();
295 const float w = cfg.
cardW * 0.9f;
296 const float h = cfg.
cardH * 0.9f;
297 for (
int i = 0; i < 3 && i <
c; ++i) {
298 const float off =
static_cast<float>(i) * 3.f;
303 glm::vec4(0.95f, 0.9f, 0.8f, 1.f));
315 if (index < 0 ||
static_cast<size_t>(index) >= events_.size())
return {};
316 return events_[
static_cast<size_t>(index)].
type;
320 if (index < 0 ||
static_cast<size_t>(index) >= events_.size())
return {};
321 return events_[
static_cast<size_t>(index)].hand;
325 if (index < 0 ||
static_cast<size_t>(index) >= events_.size())
return {};
326 return events_[
static_cast<size_t>(index)].zoneId;
330 if (index < 0 ||
static_cast<size_t>(index) >= events_.size())
return {};
331 return events_[
static_cast<size_t>(index)].cardId;
335 if (index < 0 ||
static_cast<size_t>(index) >= events_.size())
return {};
336 return events_[
static_cast<size_t>(index)].reason;
343void Card::expose(ssq::Table &table) {
344 auto cls = table.addClass(
name, Card::create,
false);
347 registerCppEntityClassForScript<CardData>();
348 registerCppEntityClassForScript<Hand>();
349 registerCppEntityClassForScript<Deck>();
350 registerCppEntityClassForScript<Zone>();
354 "LayoutConfig", std::function<LayoutConfig *()>([]() ->
LayoutConfig * {
return nullptr; }),
false);
355 cfgCls.addFunc(
"getCardW", [](LayoutConfig *
c) ->
float {
return c ?
c->cardW : 0.f; });
356 cfgCls.addFunc(
"setCardW", [](LayoutConfig *
c,
float v) {
if (
c)
c->cardW =
v; });
357 cfgCls.addFunc(
"getCardH", [](LayoutConfig *
c) ->
float {
return c ?
c->cardH : 0.f; });
358 cfgCls.addFunc(
"setCardH", [](LayoutConfig *
c,
float v) {
if (
c)
c->cardH =
v; });
359 cfgCls.addFunc(
"getSpacing", [](LayoutConfig *
c) ->
float {
return c ?
c->spacing : 0.f; });
360 cfgCls.addFunc(
"setSpacing", [](LayoutConfig *
c,
float v) {
if (
c)
c->spacing =
v; });
361 cfgCls.addFunc(
"getHandX", [](LayoutConfig *
c) ->
float {
return c ?
c->handX : 0.f; });
362 cfgCls.addFunc(
"setHandX", [](LayoutConfig *
c,
float v) {
if (
c)
c->handX =
v; });
363 cfgCls.addFunc(
"getHandY", [](LayoutConfig *
c) ->
float {
return c ?
c->handY : 0.f; });
364 cfgCls.addFunc(
"setHandY", [](LayoutConfig *
c,
float v) {
if (
c)
c->handY =
v; });
365 cfgCls.addFunc(
"getArcHeight", [](LayoutConfig *
c) ->
float {
return c ?
c->arcHeight : 0.f; });
366 cfgCls.addFunc(
"setArcHeight", [](LayoutConfig *
c,
float v) {
if (
c)
c->arcHeight =
v; });
367 cfgCls.addFunc(
"getRotationAngle", [](LayoutConfig *
c) ->
float {
return c ?
c->rotationAngle : 0.f; });
368 cfgCls.addFunc(
"setRotationAngle", [](LayoutConfig *
c,
float v) {
if (
c)
c->rotationAngle =
v; });
369 cfgCls.addFunc(
"getHoverRotation", [](LayoutConfig *
c) ->
bool {
return c ?
c->hoverRotation :
false; });
370 cfgCls.addFunc(
"setHoverRotation", [](LayoutConfig *
c,
bool v) {
if (
c)
c->hoverRotation =
v; });
371 cfgCls.addFunc(
"getHoverScale", [](LayoutConfig *
c) ->
float {
return c ?
c->hoverScale : 0.f; });
372 cfgCls.addFunc(
"setHoverScale", [](LayoutConfig *
c,
float v) {
if (
c)
c->hoverScale =
v; });
373 cfgCls.addFunc(
"getHoverLift", [](LayoutConfig *
c) ->
float {
return c ?
c->hoverLift : 0.f; });
374 cfgCls.addFunc(
"setHoverLift", [](LayoutConfig *
c,
float v) {
if (
c)
c->hoverLift =
v; });
375 cfgCls.addFunc(
"getHoverSpeed", [](LayoutConfig *
c) ->
float {
return c ?
c->hoverSpeed : 0.f; });
376 cfgCls.addFunc(
"setHoverSpeed", [](LayoutConfig *
c,
float v) {
if (
c)
c->hoverSpeed =
v; });
377 cfgCls.addFunc(
"getMotionSpeed", [](LayoutConfig *
c) ->
float {
return c ?
c->motionSpeed : 0.f; });
378 cfgCls.addFunc(
"setMotionSpeed", [](LayoutConfig *
c,
float v) {
if (
c)
c->motionSpeed =
v; });
379 cfgCls.addFunc(
"getDisabledAlpha", [](LayoutConfig *
c) ->
float {
return c ?
c->disabledAlpha : 0.f; });
380 cfgCls.addFunc(
"setDisabledAlpha", [](LayoutConfig *
c,
float v) {
if (
c)
c->disabledAlpha =
v; });
381 cfgCls.addFunc(
"getShowZones", [](LayoutConfig *
c) ->
bool {
return c ?
c->showZones :
false; });
382 cfgCls.addFunc(
"setShowZones", [](LayoutConfig *
c,
bool v) {
if (
c)
c->showZones =
v; });
383 cfgCls.addFunc(
"getDeckX", [](LayoutConfig *
c) ->
float {
return c ?
c->deckX : 0.f; });
384 cfgCls.addFunc(
"setDeckX", [](LayoutConfig *
c,
float v) {
if (
c)
c->deckX =
v; });
385 cfgCls.addFunc(
"getDeckY", [](LayoutConfig *
c) ->
float {
return c ?
c->deckY : 0.f; });
386 cfgCls.addFunc(
"setDeckY", [](LayoutConfig *
c,
float v) {
if (
c)
c->deckY =
v; });
387 cfgCls.addFunc(
"getDragThreshold", [](LayoutConfig *
c) ->
float {
return c ?
c->dragThreshold : 0.f; });
388 cfgCls.addFunc(
"setDragThreshold", [](LayoutConfig *
c,
float v) {
if (
c)
c->dragThreshold =
v; });
391 auto cardCls = table.addClass<CardData>(
392 "CardData", std::function<CardData *()>([]() -> CardData * {
return nullptr; }),
false);
393 cardCls.addFunc(
"getId", [](CardData *
c) -> std::string {
return c ?
c->identity()->
id : std::string{}; });
394 cardCls.addFunc(
"setId", [](CardData *
c,
const std::string &
v) {
if (
c)
c->identity()->id =
v; });
395 cardCls.addFunc(
"getName", [](CardData *
c) -> std::string {
return c ?
c->identity()->name : std::string{}; });
396 cardCls.addFunc(
"setName", [](CardData *
c,
const std::string &
v) {
if (
c)
c->identity()->name =
v; });
397 cardCls.addFunc(
"getKind", [](CardData *
c) -> std::string {
return c ?
c->identity()->kind : std::string{}; });
398 cardCls.addFunc(
"setKind", [](CardData *
c,
const std::string &
v) {
if (
c)
c->identity()->kind =
v; });
399 cardCls.addFunc(
"getCost", [](CardData *
c) ->
int {
return c ?
c->stats()->cost : 0; });
400 cardCls.addFunc(
"setCost", [](CardData *
c,
int v) {
if (
c)
c->stats()->cost =
v; });
401 cardCls.addFunc(
"getAttack", [](CardData *
c) ->
int {
return c ?
c->stats()->attack : 0; });
402 cardCls.addFunc(
"setAttack", [](CardData *
c,
int v) {
if (
c)
c->stats()->attack =
v; });
403 cardCls.addFunc(
"getHealth", [](CardData *
c) ->
int {
return c ?
c->stats()->health : 0; });
404 cardCls.addFunc(
"setHealth", [](CardData *
c,
int v) {
if (
c)
c->stats()->health =
v; });
405 cardCls.addFunc(
"isFaceUp", [](CardData *
c) ->
bool {
return c ?
c->visual()->faceUp :
true; });
406 cardCls.addFunc(
"setFaceUp", [](CardData *
c,
bool v) {
if (
c)
c->visual()->faceUp =
v; });
407 cardCls.addFunc(
"isDisabled", [](CardData *
c) ->
bool {
return c ?
c->visual()->disabled :
false; });
408 cardCls.addFunc(
"setDisabled", [](CardData *
c,
bool v) {
if (
c)
c->visual()->disabled =
v; });
409 cardCls.addFunc(
"getState", [](CardData *
c) -> std::string {
412 cardCls.addFunc(
"setState", [](CardData *
c,
const std::string &
v) {
414 auto *st =
c->state().operator->();
424 cardCls.addFunc(
"getTintR", [](CardData *
c) ->
float {
return c ?
c->visual()->tint.r : 0.f; });
425 cardCls.addFunc(
"getTintG", [](CardData *
c) ->
float {
return c ?
c->visual()->tint.g : 0.f; });
426 cardCls.addFunc(
"getTintB", [](CardData *
c) ->
float {
return c ?
c->visual()->tint.b : 0.f; });
427 cardCls.addFunc(
"setTint", [](CardData *
c,
float r,
float g,
float b) {
428 if (
c)
c->visual()->tint = glm::vec3(r, g,
b);
430 cardCls.addFunc(
"setArt", [](CardData *
c, graphics::Texture *t) {
if (
c)
c->visual()->texture = t; });
431 cardCls.addFunc(
"getArt", [](CardData *
c) -> graphics::Texture * {
return c ?
c->visual()->texture :
nullptr; });
432 cardCls.addFunc(
"getX", [](CardData *
c) ->
float {
return c ?
c->layout()->x : 0.f; });
433 cardCls.addFunc(
"getY", [](CardData *
c) ->
float {
return c ?
c->layout()->y : 0.f; });
434 cardCls.addFunc(
"getW", [](CardData *
c) ->
float {
return c ?
c->layout()->w : 0.f; });
435 cardCls.addFunc(
"getH", [](CardData *
c) ->
float {
return c ?
c->layout()->h : 0.f; });
436 cardCls.addFunc(
"getScale", [](CardData *
c) ->
float {
return c ?
c->layout()->scale : 0.f; });
437 cardCls.addFunc(
"getAlpha", [](CardData *
c) ->
float {
return c ?
c->layout()->alpha : 0.f; });
438 cardCls.addFunc(
"isHovered", [](CardData *
c) ->
bool {
return c ?
c->state()->hovered :
false; });
439 cardCls.addFunc(
"isDragging", [](CardData *
c) ->
bool {
return c ?
c->state()->dragging :
false; });
440 cardCls.addFunc(
"hit", [](CardData *
c,
float px,
float py) ->
bool {
return c &&
c->hit(
px, py); });
441 cardCls.addFunc(
"describe", [](CardData *
c) -> std::string {
return c ?
c->describe() : std::string{}; });
444 auto deckCls = table.addClass<
Deck>(
445 "Deck", std::function<Deck *()>([]() -> Deck * {
return nullptr; }),
false);
456 auto zoneCls = table.addClass<Zone>(
457 "Zone", std::function<Zone *()>([]() -> Zone * {
return nullptr; }),
false);
458 zoneCls.addFunc(
"getId", [](Zone *
z) -> std::string {
return z ?
z->rect()->
id : std::string{}; });
459 zoneCls.addFunc(
"setId", [](Zone *
z,
const std::string &
v) {
if (
z)
z->rect()->id =
v; });
460 zoneCls.addFunc(
"getLabel", [](Zone *
z) -> std::string {
return z ?
z->rect()->label : std::string{}; });
461 zoneCls.addFunc(
"setLabel", [](Zone *
z,
const std::string &
v) {
if (
z)
z->rect()->label =
v; });
462 zoneCls.addFunc(
"getX", [](Zone *
z) ->
float {
return z ?
z->rect()->x : 0.f; });
463 zoneCls.addFunc(
"getY", [](Zone *
z) ->
float {
return z ?
z->rect()->y : 0.f; });
464 zoneCls.addFunc(
"getW", [](Zone *
z) ->
float {
return z ?
z->rect()->w : 0.f; });
465 zoneCls.addFunc(
"getH", [](Zone *
z) ->
float {
return z ?
z->rect()->h : 0.f; });
466 zoneCls.addFunc(
"setRect", [](Zone *
z,
float x,
float y,
float w,
float h) {
468 auto *R =
z->rect().operator->();
469 R->x =
x; R->y =
y; R->w =
w; R->h =
h;
471 zoneCls.addFunc(
"getColorR", [](Zone *
z) ->
float {
return z ?
z->rect()->color.r : 0.f; });
472 zoneCls.addFunc(
"getColorG", [](Zone *
z) ->
float {
return z ?
z->rect()->color.g : 0.f; });
473 zoneCls.addFunc(
"getColorB", [](Zone *
z) ->
float {
return z ?
z->rect()->color.b : 0.f; });
474 zoneCls.addFunc(
"setColor", [](Zone *
z,
float r,
float g,
float b) {
475 if (
z)
z->rect()->color = glm::vec3(r, g,
b);
477 zoneCls.addFunc(
"getAlpha", [](Zone *
z) ->
float {
return z ?
z->rect()->alpha : 0.f; });
478 zoneCls.addFunc(
"setAlpha", [](Zone *
z,
float v) {
if (
z)
z->rect()->alpha =
v; });
479 zoneCls.addFunc(
"isEnabled", [](Zone *
z) ->
bool {
return z ?
z->rect()->enabled :
false; });
480 zoneCls.addFunc(
"setEnabled", [](Zone *
z,
bool v) {
if (
z)
z->rect()->enabled =
v; });
481 zoneCls.addFunc(
"addAcceptKind", [](Zone *
z,
const std::string &k) {
if (
z)
z->filter()->acceptKinds.push_back(k); });
482 zoneCls.addFunc(
"clearAcceptKinds", [](Zone *
z) {
if (
z)
z->filter()->acceptKinds.clear(); });
483 zoneCls.addFunc(
"accepts", [](Zone *
z, CardData *
c) ->
bool {
return z &&
z->accepts(
c); });
484 zoneCls.addFunc(
"contains", [](Zone *
z,
float px,
float py) ->
bool {
return z &&
z->contains(
px, py); });
485 zoneCls.addFunc(
"render", [](Zone *
z, graphics::Graphics *gfx,
bool showLabel) {
486 if (
z)
z->render(gfx, showLabel);
490 auto handCls = table.addClass<
Hand>(
491 "Hand", std::function<Hand *()>([]() -> Hand * {
return nullptr; }),
false);
492 handCls.addFunc(
"getOwner", [](Hand *
h) -> std::string {
return h ?
h->meta()->owner : std::string{}; });
493 handCls.addFunc(
"setOwner", [](Hand *
h,
const std::string &
v) {
if (
h)
h->meta()->owner =
v; });
494 handCls.addFunc(
"getConfig", [](Hand *
h) -> LayoutConfig * {
return h ?
h->meta()->config :
nullptr; });
495 handCls.addFunc(
"setConfig", [](Hand *
h, LayoutConfig *
c) {
if (
h)
h->meta()->config =
c; });
496 handCls.addFunc(
"isFaceDown", [](Hand *
h) ->
bool {
return h ?
h->meta()->faceDown :
false; });
497 handCls.addFunc(
"setFaceDown", [](Hand *
h,
bool v) {
if (
h)
h->meta()->faceDown =
v; });
498 handCls.addFunc(
"isPeek", [](Hand *
h) ->
bool {
return h ?
h->meta()->peek :
false; });
499 handCls.addFunc(
"setPeek", [](Hand *
h,
bool v) {
if (
h)
h->meta()->peek =
v; });
500 handCls.addFunc(
"isInteractive", [](Hand *
h) ->
bool {
return h ?
h->meta()->interactive :
true; });
501 handCls.addFunc(
"setInteractive", [](Hand *
h,
bool v) {
if (
h)
h->meta()->interactive =
v; });
509 handCls.addFunc(
"render", [](Hand *
h, graphics::Graphics *gfx) {
if (
h)
h->render(gfx); });
512void Card::expose(ssq::Class &
cls) {