castle-web-cli 0.4.86 → 0.4.87

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.
@@ -93,7 +93,20 @@ export function ScenePlayer({ sceneData, sprites, files, behaviorClasses, onFirs
93
93
  <canvas
94
94
  ref={canvasRef}
95
95
  tabIndex={0}
96
- style={{ width: '100%', height: '100%', display: 'block', outline: 'none' }}
96
+ style={{
97
+ width: '100%',
98
+ height: '100%',
99
+ display: 'block',
100
+ outline: 'none',
101
+ // Claim touches for the game: without this, a mobile WebView
102
+ // (castle-client) treats a drag as a scroll/pan gesture and fires
103
+ // `pointercancel` mid-drag, so a held Draggable/Slingshot loses its
104
+ // grip. Also suppress long-press text selection / callout.
105
+ touchAction: 'none',
106
+ userSelect: 'none',
107
+ WebkitUserSelect: 'none',
108
+ WebkitTouchCallout: 'none',
109
+ }}
97
110
  />
98
111
  <SceneUI getRuntime={getRuntime} />
99
112
  {error ? <PlayErrorOverlay error={error} onRestart={onRestart} /> : null}
@@ -13,6 +13,7 @@ import {
13
13
  faCode,
14
14
  faCodeBranch,
15
15
  faEraser,
16
+ faExpand,
16
17
  faExternalLinkAlt,
17
18
  faEyeDropper,
18
19
  faFile,
@@ -27,6 +28,8 @@ import {
27
28
  faPlay,
28
29
  faPlus,
29
30
  faRedo,
31
+ faSearchMinus,
32
+ faSearchPlus,
30
33
  faShapes,
31
34
  faSlash,
32
35
  faVectorSquare,
@@ -180,6 +183,7 @@ const icons = {
180
183
  // FA5 has no faCodeFork (that's the FA6 name); faCodeBranch is the fork glyph.
181
184
  'code-fork': faCodeBranch,
182
185
  eraser: faEraser,
186
+ expand: faExpand,
183
187
  external: faExternalLinkAlt,
184
188
  eyedropper: faEyeDropper,
185
189
  fill: faFillDrip,
@@ -199,6 +203,9 @@ const icons = {
199
203
  plus: faPlus,
200
204
  redo: faRedo,
201
205
  rotate: faSyncAlt,
206
+ 'zoom-in': faSearchPlus,
207
+ 'zoom-out': faSearchMinus,
208
+ fit: faExpand,
202
209
  shapes: faShapes,
203
210
  slash: faSlash,
204
211
  square: faSquare,
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import { Panel, SelectField } from '../../engine/ui';
3
3
  import { AutoFields, overrideProps } from '../../engine/autoInspector';
4
- import { analogVelocity, inActorWorldSpace, stickVector } from '../controls';
4
+ import { analogVelocity, inActorWorldSpace, pressOnDraggable, stickVector } from '../controls';
5
5
 
6
6
  // AnalogStick: an on-screen virtual joystick that drives the actor it's on --
7
7
  // the look and feel of castle-client's Analog Stick. Press anywhere to place
@@ -29,7 +29,9 @@ export class AnalogStick {
29
29
  const rt = actor.runtime;
30
30
  const pressed = p.down && !rt._stickWasDown;
31
31
  rt._stickWasDown = p.down;
32
- if (pressed) rt._stickOrigin = { x: p.x, y: p.y };
32
+ // A press on a draggable is consumed by the drag; every other press drives
33
+ // the stick.
34
+ if (pressed && !pressOnDraggable(scene)) rt._stickOrigin = { x: p.x, y: p.y };
33
35
 
34
36
  // Only drive velocity while the stick is actually held.
35
37
  if (p.down && rt._stickOrigin) {
@@ -1,7 +1,7 @@
1
1
  import React from 'react';
2
2
  import { Panel } from '../../engine/ui';
3
3
  import { AutoFields } from '../../engine/autoInspector';
4
- import { inActorWorldSpace, slingLaunch, slingPull } from '../controls';
4
+ import { inActorWorldSpace, pressOnDraggable, slingLaunch, slingPull } from '../controls';
5
5
 
6
6
  // Slingshot: pull back and release to fling the actor the opposite way
7
7
  // (angry-birds style) -- the physics feel of castle-client's Sling. While
@@ -31,8 +31,14 @@ export class Slingshot {
31
31
  const pressed = p.down && !rt._slingWasDown;
32
32
  rt._slingWasDown = p.down;
33
33
 
34
+ // A press on a draggable is consumed by the drag; otherwise the sling grabs
35
+ // (anywhere, or on its own actor when grabAnywhere is off).
34
36
  const grabAnywhere = this.props.grabAnywhere ?? true;
35
- if (pressed && (grabAnywhere || scene.actorAt(p.x, p.y)?.id === actor.id)) {
37
+ if (
38
+ pressed &&
39
+ !pressOnDraggable(scene) &&
40
+ (grabAnywhere || scene.actorAt(p.x, p.y)?.id === actor.id)
41
+ ) {
36
42
  rt._slinging = true;
37
43
  // Anchor at the press point (like castle-client): the aim/launch depends
38
44
  // on the drag gesture, not where the press landed relative to the actor.
@@ -29,6 +29,20 @@ export function chaseVelocity(target, layout, props = {}) {
29
29
  return { x: (target.x - c.x) * stiffness, y: (target.y - c.y) * stiffness };
30
30
  }
31
31
 
32
+ // --- Touch ownership -------------------------------------------------------
33
+ // Hierarchy: a press that lands ON a Draggable object is CONSUMED by that drag,
34
+ // so Slingshot/AnalogStick ignore it. A press anywhere else is shared -- both
35
+ // Slingshot and AnalogStick act (no fair way to rank them). So the only gate the
36
+ // non-drag controls need is this: did the current press land on a draggable?
37
+ // Stateless -- a pure per-press query (the controls only ask on their press
38
+ // edge), so there's no owner to latch and nothing to reset.
39
+ export function pressOnDraggable(scene) {
40
+ const p = scene.pointer;
41
+ if (!p.down) return false;
42
+ const hit = scene.actorAt(p.x, p.y);
43
+ return !!hit && !!hit.components.Draggable;
44
+ }
45
+
32
46
  // Run `draw(ctx)` in world space, undoing the actor's Layout rotation that the
33
47
  // runtime applies about the actor's center. Overlay controls (Slingshot,
34
48
  // AnalogStick) use this so their on-screen UI stays anchored where the user
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "castle-web-cli",
3
- "version": "0.4.86",
3
+ "version": "0.4.87",
4
4
  "type": "module",
5
5
  "bin": {
6
6
  "castle-web": "./dist/index.js"