incanto 0.4.2 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (43) hide show
  1. package/dist/2d.d.ts +74 -6
  2. package/dist/2d.js +4 -4
  3. package/dist/3d.d.ts +174 -16
  4. package/dist/3d.js +4 -4
  5. package/dist/{audio-player-DqUR3XFs.d.ts → audio-player-DkBqRTs4.d.ts} +1 -1
  6. package/dist/{behavior-BAQq7HGM.d.ts → behavior-CWhW3oa6.d.ts} +54 -0
  7. package/dist/{create-game-CZHROKcT.js → create-game-BFESHv1X.js} +37 -25
  8. package/dist/{create-game-CMS7DiPi.js → create-game-D8UvwXme.js} +231 -8
  9. package/dist/debug.d.ts +1 -1
  10. package/dist/{duplicate-DP2WPYom.js → duplicate-BEvGBtb_.js} +1 -1
  11. package/dist/{gameplay-Ccruc3Wd.js → gameplay-CDFgSG6z.js} +293 -24
  12. package/dist/gameplay.d.ts +113 -2
  13. package/dist/gameplay.js +2 -2
  14. package/dist/index.d.ts +139 -5
  15. package/dist/index.js +59 -6
  16. package/dist/{loader-CGs_G-r0.js → loader-B4OEXDZ8.js} +60 -0
  17. package/dist/{loader-Mo0KghCv.d.ts → loader-D2u0fVW2.d.ts} +1 -1
  18. package/dist/net.d.ts +1 -1
  19. package/dist/net.js +1 -1
  20. package/dist/{particle-sim-DYuSUxvK.js → particle-sim-CFkILGwh.js} +84 -3
  21. package/dist/{particle-sim-CbN4YUuH.d.ts → particle-sim-CwJ5rI_P.d.ts} +3 -0
  22. package/dist/{physics-2d-KuMWPTf6.js → physics-2d-KXn1N1jF.js} +95 -10
  23. package/dist/{physics-3d-CXOBOUKG.js → physics-3d-C58oQzTX.js} +121 -43
  24. package/dist/react.d.ts +1 -1
  25. package/dist/react.js +1 -1
  26. package/dist/{register-B0gq63VW.js → register-C35HDZpm.js} +2 -2
  27. package/dist/{register-DPEV9_9t.js → register-D71C4rDC.js} +76 -5
  28. package/dist/{register-BuUV1_KB.js → register-Dl_ixIJe.js} +275 -2
  29. package/dist/{register-02aSywx2.js → register-DvPHJdVj.js} +390 -59
  30. package/dist/test.d.ts +2 -2
  31. package/dist/test.js +10 -10
  32. package/editor/assets/{agent8-DUVZGcuO.js → agent8-DruKhR22.js} +1 -1
  33. package/editor/assets/index-D2qufqUo.js +7417 -0
  34. package/editor/index.html +1 -1
  35. package/package.json +1 -1
  36. package/schemas/scene.schema.json +847 -133
  37. package/skills/incanto-3d-models.md +24 -1
  38. package/skills/incanto-building-3d-games.md +35 -3
  39. package/skills/incanto-gameplay-behaviors.md +60 -0
  40. package/skills/incanto-hud.md +58 -0
  41. package/skills/incanto-node-reference.md +113 -0
  42. package/skills/incanto-physics-and-input.md +47 -0
  43. package/editor/assets/index-DsM2Yp9F.js +0 -7365
@@ -1,4 +1,4 @@
1
- import { f as Node } from "./loader-CGs_G-r0.js";
1
+ import { f as Node } from "./loader-B4OEXDZ8.js";
2
2
  import { t as IncantoError } from "./errors-BpWbnbb_.js";
3
3
  import { t as Rng } from "./rng-DP-SR7eg.js";
4
4
  import { l as registerNode } from "./registry-BVJ2HbCn.js";
@@ -500,6 +500,275 @@ var AudioPlayer = class extends Node {
500
500
  }
501
501
  };
502
502
  //#endregion
503
+ //#region src/core/nodes/hud.ts
504
+ const ANCHOR_CSS = {
505
+ topLeft: "left:16px;top:16px;align-items:flex-start;",
506
+ top: "left:50%;top:16px;transform:translateX(-50%);align-items:center;",
507
+ topRight: "right:16px;top:16px;align-items:flex-end;",
508
+ left: "left:16px;top:50%;transform:translateY(-50%);align-items:flex-start;",
509
+ center: "left:50%;top:50%;transform:translate(-50%,-50%);align-items:center;",
510
+ right: "right:16px;top:50%;transform:translateY(-50%);align-items:flex-end;",
511
+ bottomLeft: "left:16px;bottom:16px;align-items:flex-start;",
512
+ bottom: "left:50%;bottom:16px;transform:translateX(-50%);align-items:center;",
513
+ bottomRight: "right:16px;bottom:16px;align-items:flex-end;"
514
+ };
515
+ const FONT = "600 14px system-ui, -apple-system, 'Segoe UI', sans-serif";
516
+ function hasDom() {
517
+ return typeof document !== "undefined";
518
+ }
519
+ /**
520
+ * The overlay container. One per scene is plenty; widgets mount into its
521
+ * anchor slots. `zIndex` lifts it above game canvases; the layer never eats
522
+ * pointer events (widgets that need clicks opt in individually).
523
+ */
524
+ var HudLayer = class extends Node {
525
+ static typeName = "HudLayer";
526
+ static props = {
527
+ zIndex: { default: 100 },
528
+ visible: { default: true }
529
+ };
530
+ zIndex = 100;
531
+ visible = true;
532
+ /** @internal root overlay element (null headless). */
533
+ _element = null;
534
+ slots = /* @__PURE__ */ new Map();
535
+ onEnterTree() {
536
+ if (!hasDom()) return;
537
+ const el = document.createElement("div");
538
+ el.dataset.incantoHud = this.name;
539
+ el.style.cssText = `position:fixed;inset:0;pointer-events:none;z-index:${this.zIndex};font:${FONT};`;
540
+ document.body.appendChild(el);
541
+ this._element = el;
542
+ }
543
+ onExitTree() {
544
+ this._element?.remove();
545
+ this._element = null;
546
+ this.slots.clear();
547
+ }
548
+ update() {
549
+ if (this._element) this._element.style.display = this.visible ? "" : "none";
550
+ }
551
+ /** @internal Widgets mount into per-anchor flex columns. */
552
+ _slot(anchor) {
553
+ if (!this._element) return null;
554
+ let slot = this.slots.get(anchor);
555
+ if (!slot) {
556
+ slot = document.createElement("div");
557
+ slot.style.cssText = `position:absolute;display:flex;flex-direction:column;gap:8px;${ANCHOR_CSS[anchor] ?? ANCHOR_CSS.topLeft}`;
558
+ this._element.appendChild(slot);
559
+ this.slots.set(anchor, slot);
560
+ }
561
+ return slot;
562
+ }
563
+ };
564
+ /** Shared plumbing: mount into the parent HudLayer's anchor slot. */
565
+ var HudWidget = class extends Node {
566
+ static props = {
567
+ anchor: {
568
+ default: "topLeft",
569
+ options: [
570
+ "topLeft",
571
+ "top",
572
+ "topRight",
573
+ "left",
574
+ "center",
575
+ "right",
576
+ "bottomLeft",
577
+ "bottom",
578
+ "bottomRight"
579
+ ]
580
+ },
581
+ visible: { default: true }
582
+ };
583
+ anchor = "topLeft";
584
+ visible = true;
585
+ /** @internal */
586
+ _element = null;
587
+ layer() {
588
+ for (let p = this.parent; p; p = p.parent) if (p instanceof HudLayer) return p;
589
+ return null;
590
+ }
591
+ onReady() {
592
+ if (!hasDom()) return;
593
+ const slot = this.layer()?._slot(this.anchor);
594
+ if (!slot) return;
595
+ this._element = this._build();
596
+ slot.appendChild(this._element);
597
+ }
598
+ onExitTree() {
599
+ this._element?.remove();
600
+ this._element = null;
601
+ }
602
+ update(_dt) {
603
+ if (!this._element) return;
604
+ this._element.style.display = this.visible ? "" : "none";
605
+ this._sync();
606
+ }
607
+ _sync() {}
608
+ };
609
+ /** A text line (score, timer, hints). Set `.text` from behaviors. */
610
+ var UiText = class extends HudWidget {
611
+ static typeName = "UiText";
612
+ static props = {
613
+ ...HudWidget.props,
614
+ text: { default: "" },
615
+ size: { default: 16 },
616
+ color: { default: "#ffffff" },
617
+ shadow: { default: true }
618
+ };
619
+ text = "";
620
+ size = 16;
621
+ color = "#ffffff";
622
+ shadow = true;
623
+ last = "\0";
624
+ _build() {
625
+ const el = document.createElement("div");
626
+ el.style.cssText = `font-size:${this.size}px;color:${this.color};${this.shadow ? "text-shadow:0 1px 3px rgba(0,0,0,.7);" : ""}white-space:pre;`;
627
+ return el;
628
+ }
629
+ _sync() {
630
+ if (this.text !== this.last && this._element) {
631
+ this.last = this.text;
632
+ this._element.textContent = this.text;
633
+ }
634
+ }
635
+ };
636
+ /** A labeled progress bar (health, stamina, reload, boss HP). */
637
+ var UiBar = class extends HudWidget {
638
+ static typeName = "UiBar";
639
+ static props = {
640
+ ...HudWidget.props,
641
+ value: { default: 100 },
642
+ max: { default: 100 },
643
+ width: { default: 180 },
644
+ height: { default: 14 },
645
+ color: { default: "#4ade80" },
646
+ lowColor: { default: "#ef4444" },
647
+ lowThreshold: { default: .3 },
648
+ background: { default: "rgba(0,0,0,0.5)" },
649
+ label: { default: "" }
650
+ };
651
+ value = 100;
652
+ max = 100;
653
+ width = 180;
654
+ height = 14;
655
+ color = "#4ade80";
656
+ lowColor = "#ef4444";
657
+ lowThreshold = .3;
658
+ background = "rgba(0,0,0,0.5)";
659
+ label = "";
660
+ fill = null;
661
+ lastRatio = -1;
662
+ /** Current fill ratio 0..1 (what the bar shows). */
663
+ get ratio() {
664
+ return this.max > 0 ? Math.min(1, Math.max(0, this.value / this.max)) : 0;
665
+ }
666
+ _build() {
667
+ const wrap = document.createElement("div");
668
+ wrap.style.cssText = "display:flex;align-items:center;gap:6px;";
669
+ if (this.label) {
670
+ const tag = document.createElement("span");
671
+ tag.textContent = this.label;
672
+ tag.style.cssText = "color:#fff;font-size:12px;text-shadow:0 1px 3px rgba(0,0,0,.7);";
673
+ wrap.appendChild(tag);
674
+ }
675
+ const track = document.createElement("div");
676
+ track.style.cssText = `width:${this.width}px;height:${this.height}px;background:${this.background};border-radius:${this.height / 2}px;overflow:hidden;`;
677
+ this.fill = document.createElement("div");
678
+ this.fill.style.cssText = `height:100%;width:100%;background:${this.color};border-radius:inherit;transition:width .15s;`;
679
+ track.appendChild(this.fill);
680
+ wrap.appendChild(track);
681
+ return wrap;
682
+ }
683
+ _sync() {
684
+ const r = this.ratio;
685
+ if (r === this.lastRatio || !this.fill) return;
686
+ this.lastRatio = r;
687
+ this.fill.style.width = `${r * 100}%`;
688
+ this.fill.style.background = r <= this.lowThreshold ? this.lowColor : this.color;
689
+ }
690
+ };
691
+ /**
692
+ * Center-screen announcements ("WAVE 2", "YOU DIED", "LEVEL UP") with fade
693
+ * in/out and a queue — call `.show(text, { color, seconds })`; `sticky:true`
694
+ * (seconds: 0) keeps it until the next show(). Emits `bannerShown(text)`.
695
+ */
696
+ var UiBanner = class extends HudWidget {
697
+ static typeName = "UiBanner";
698
+ static signals = ["bannerShown"];
699
+ static props = {
700
+ ...HudWidget.props,
701
+ anchor: {
702
+ default: "center",
703
+ options: [
704
+ "topLeft",
705
+ "top",
706
+ "topRight",
707
+ "left",
708
+ "center",
709
+ "right",
710
+ "bottomLeft",
711
+ "bottom",
712
+ "bottomRight"
713
+ ]
714
+ },
715
+ size: { default: 42 },
716
+ seconds: { default: 2 }
717
+ };
718
+ anchor = "center";
719
+ size = 42;
720
+ /** Default display time (per-show override via options). */
721
+ seconds = 2;
722
+ queue = [];
723
+ current = null;
724
+ remaining = 0;
725
+ /** What the banner is showing right now ('' when idle) — test-friendly. */
726
+ get showing() {
727
+ return this.current?.text ?? "";
728
+ }
729
+ show(text, opts) {
730
+ this.queue.push({
731
+ text,
732
+ color: opts?.color ?? "#ffffff",
733
+ seconds: opts?.seconds ?? this.seconds
734
+ });
735
+ }
736
+ /** Drop everything (scene transitions). */
737
+ clear() {
738
+ this.queue.length = 0;
739
+ this.current = null;
740
+ this.remaining = 0;
741
+ if (this._element) this._element.style.opacity = "0";
742
+ }
743
+ _build() {
744
+ const el = document.createElement("div");
745
+ el.style.cssText = `font-size:${this.size}px;font-weight:800;color:#fff;text-shadow:0 2px 12px rgba(0,0,0,.8);opacity:0;transition:opacity .25s;text-align:center;`;
746
+ return el;
747
+ }
748
+ update(dt) {
749
+ super.update(dt);
750
+ if (this.current) {
751
+ if (this.current.seconds > 0) {
752
+ this.remaining -= dt;
753
+ if (this.remaining <= 0) {
754
+ this.current = null;
755
+ if (this._element) this._element.style.opacity = "0";
756
+ }
757
+ } else if (this.queue.length > 0) this.current = null;
758
+ }
759
+ if (!this.current && this.queue.length > 0) {
760
+ this.current = this.queue.shift();
761
+ this.remaining = this.current.seconds;
762
+ if (this._element) {
763
+ this._element.textContent = this.current.text;
764
+ this._element.style.color = this.current.color;
765
+ this._element.style.opacity = "1";
766
+ }
767
+ this.emit("bannerShown", this.current.text);
768
+ }
769
+ }
770
+ };
771
+ //#endregion
503
772
  //#region src/core/nodes/timer.ts
504
773
  /**
505
774
  * The canonical serializable game clock — never `setTimeout` in game logic.
@@ -556,6 +825,10 @@ function registerCoreNodes() {
556
825
  registerNode(Node);
557
826
  registerNode(Timer);
558
827
  registerNode(AudioPlayer);
828
+ registerNode(HudLayer);
829
+ registerNode(UiText);
830
+ registerNode(UiBar);
831
+ registerNode(UiBanner);
559
832
  }
560
833
  //#endregion
561
- export { spatialGain as a, SFX_PRESET_NAMES as c, ROLLOFF_MODELS as i, synthSfx as l, Timer as n, spatialPan as o, AudioPlayer as r, SFX_PRESETS as s, registerCoreNodes as t };
834
+ export { UiBar as a, ROLLOFF_MODELS as c, SFX_PRESETS as d, SFX_PRESET_NAMES as f, UiBanner as i, spatialGain as l, Timer as n, UiText as o, synthSfx as p, HudLayer as r, AudioPlayer as s, registerCoreNodes as t, spatialPan as u };