@linxin666/dsh-pet 0.3.3 → 0.3.5
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.
- package/README.i18n.yaml +2 -2
- package/README.md +30 -13
- package/README.zh.md +30 -13
- package/assets/ouo-neko/pet.json +31 -0
- package/assets/ouo-neko/previews/failed.gif +0 -0
- package/assets/ouo-neko/previews/idle.gif +0 -0
- package/assets/ouo-neko/previews/jumping.gif +0 -0
- package/assets/ouo-neko/previews/review.gif +0 -0
- package/assets/ouo-neko/previews/running-left.gif +0 -0
- package/assets/ouo-neko/previews/running-right.gif +0 -0
- package/assets/ouo-neko/previews/running.gif +0 -0
- package/assets/ouo-neko/previews/waiting.gif +0 -0
- package/assets/ouo-neko/previews/waving.gif +0 -0
- package/assets/ouo-neko/spritesheet.webp +0 -0
- package/contracts/pet-manifest-v2.schema.json +102 -1
- package/contracts/voice-pack-v1.schema.json +23 -28
- package/lib/client.js +781 -40
- package/lib/client.js.map +1 -1
- package/lib/index.js +1567 -482
- package/lib/types/chatter.d.ts +68 -46
- package/lib/types/chatter.d.ts.map +1 -1
- package/lib/types/chatter.js +243 -301
- package/lib/types/client/PetDockEntry.d.ts +3 -0
- package/lib/types/client/PetDockEntry.d.ts.map +1 -1
- package/lib/types/client/PetDockEntry.js +18 -2
- package/lib/types/client/PetSprite.d.ts +23 -0
- package/lib/types/client/PetSprite.d.ts.map +1 -1
- package/lib/types/client/PetSprite.js +49 -45
- package/lib/types/client/PluginSettingsCard.d.ts +2 -0
- package/lib/types/client/PluginSettingsCard.d.ts.map +1 -1
- package/lib/types/client/PluginSettingsCard.js +2 -2
- package/lib/types/client/drag-stream.d.ts +19 -0
- package/lib/types/client/drag-stream.d.ts.map +1 -0
- package/lib/types/client/drag-stream.js +26 -0
- package/lib/types/client/gameplay-hud.d.ts +50 -0
- package/lib/types/client/gameplay-hud.d.ts.map +1 -0
- package/lib/types/client/gameplay-hud.js +274 -0
- package/lib/types/client/index.d.ts.map +1 -1
- package/lib/types/client/index.js +38 -9
- package/lib/types/client/locales.d.ts +34 -0
- package/lib/types/client/locales.d.ts.map +1 -1
- package/lib/types/client/locales.js +36 -0
- package/lib/types/client/pet-store.d.ts +7 -1
- package/lib/types/client/pet-store.d.ts.map +1 -1
- package/lib/types/client/pet-store.js +4 -0
- package/lib/types/client/renderers/Frames2dVisualMount.d.ts +29 -0
- package/lib/types/client/renderers/Frames2dVisualMount.d.ts.map +1 -0
- package/lib/types/client/renderers/Frames2dVisualMount.js +83 -0
- package/lib/types/client/renderers/PetRendererSwitch.d.ts +6 -0
- package/lib/types/client/renderers/PetRendererSwitch.d.ts.map +1 -1
- package/lib/types/client/renderers/PetRendererSwitch.js +14 -1
- package/lib/types/client/renderers/frames2d.d.ts +36 -0
- package/lib/types/client/renderers/frames2d.d.ts.map +1 -0
- package/lib/types/client/renderers/frames2d.js +196 -0
- package/lib/types/defaults.d.ts +6 -0
- package/lib/types/defaults.d.ts.map +1 -0
- package/lib/types/defaults.js +5 -0
- package/lib/types/dsh-home.d.ts.map +1 -1
- package/lib/types/dsh-home.js +9 -3
- package/lib/types/event-projection.d.ts +9 -4
- package/lib/types/event-projection.d.ts.map +1 -1
- package/lib/types/event-projection.js +46 -14
- package/lib/types/gameplay.d.ts +182 -0
- package/lib/types/gameplay.d.ts.map +1 -0
- package/lib/types/gameplay.js +618 -0
- package/lib/types/ledger.d.ts +14 -0
- package/lib/types/ledger.d.ts.map +1 -1
- package/lib/types/ledger.js +30 -0
- package/lib/types/manifest-v2.d.ts +38 -1
- package/lib/types/manifest-v2.d.ts.map +1 -1
- package/lib/types/manifest-v2.js +182 -2
- package/lib/types/persist.d.ts +4 -4
- package/lib/types/persist.d.ts.map +1 -1
- package/lib/types/persist.js +41 -4
- package/lib/types/registry.d.ts +29 -0
- package/lib/types/registry.d.ts.map +1 -1
- package/lib/types/registry.js +170 -1
- package/lib/types/routes.d.ts.map +1 -1
- package/lib/types/routes.js +30 -3
- package/lib/types/service.d.ts +63 -11
- package/lib/types/service.d.ts.map +1 -1
- package/lib/types/service.js +208 -19
- package/lib/types/voice-pack.d.ts +6 -7
- package/lib/types/voice-pack.d.ts.map +1 -1
- package/lib/types/voice-pack.js +44 -47
- package/package.json +5 -2
- package/src/chatter.test.ts +68 -38
- package/src/chatter.ts +269 -316
- package/src/client/PetDockEntry.test.tsx +6 -0
- package/src/client/PetDockEntry.tsx +32 -1
- package/src/client/PetSprite.test.tsx +68 -21
- package/src/client/PetSprite.tsx +63 -45
- package/src/client/PluginSettingsCard.tsx +4 -2
- package/src/client/drag-stream.ts +35 -0
- package/src/client/gameplay-hud.test.tsx +338 -0
- package/src/client/gameplay-hud.tsx +403 -0
- package/src/client/index.test.tsx +10 -1
- package/src/client/index.ts +46 -11
- package/src/client/locales.ts +36 -0
- package/src/client/pet-store.ts +10 -1
- package/src/client/pet.module.css +192 -29
- package/src/client/renderers/Frames2dVisualMount.tsx +111 -0
- package/src/client/renderers/PetRendererSwitch.tsx +28 -1
- package/src/client/renderers/frames2d.test.ts +137 -0
- package/src/client/renderers/frames2d.ts +216 -0
- package/src/client/settings-card.module.css +5 -0
- package/src/defaults.ts +7 -0
- package/src/dsh-home.ts +9 -3
- package/src/event-projection.ts +57 -16
- package/src/gameplay.test.ts +239 -0
- package/src/gameplay.ts +692 -0
- package/src/ledger.test.ts +16 -0
- package/src/ledger.ts +31 -0
- package/src/manifest-v2.test.ts +130 -1
- package/src/manifest-v2.ts +181 -2
- package/src/persist.test.ts +3 -0
- package/src/persist.ts +43 -6
- package/src/registry.test.ts +168 -1
- package/src/registry.ts +199 -1
- package/src/routes.ts +28 -4
- package/src/service.ts +240 -29
- package/src/voice-pack.test.ts +48 -25
- package/src/voice-pack.ts +50 -47
package/lib/client.js
CHANGED
|
@@ -42,6 +42,12 @@ window.__ModuleLoader__.load({
|
|
|
42
42
|
},
|
|
43
43
|
setFeedback: (draft, feedback) => {
|
|
44
44
|
draft.feedback = feedback;
|
|
45
|
+
},
|
|
46
|
+
setGameplayView: (draft, view) => {
|
|
47
|
+
if (draft.snapshot !== null) draft.snapshot = {
|
|
48
|
+
...draft.snapshot,
|
|
49
|
+
gameplay: view
|
|
50
|
+
};
|
|
45
51
|
}
|
|
46
52
|
}
|
|
47
53
|
});
|
|
@@ -165,7 +171,7 @@ window.__ModuleLoader__.load({
|
|
|
165
171
|
}
|
|
166
172
|
//#endregion
|
|
167
173
|
//#region \0dsh-css:packages/dsh-pet/src/client/pet.module.css.mjs
|
|
168
|
-
const css$2 = ".kz2Bea_float{pointer-events:auto;-webkit-user-select:none;user-select:none;will-change:transform;contain:layout style;flex-direction:column;align-items:center;display:flex;position:fixed}.kz2Bea_sprite{image-rendering:auto;touch-action:none;contain:paint}.kz2Bea_spriteWrap{flex:none;position:relative}.
|
|
174
|
+
const css$2 = ".kz2Bea_float{pointer-events:auto;-webkit-user-select:none;user-select:none;will-change:transform;contain:layout style;flex-direction:column;align-items:center;display:flex;position:fixed}.kz2Bea_sprite{image-rendering:auto;touch-action:none;contain:paint}.kz2Bea_spriteWrap{flex:none;position:relative}.kz2Bea_bubble{white-space:nowrap;color:#f4f7ff;pointer-events:none;border-radius:999px;margin-bottom:6px;padding:4px 10px;font-size:12px;line-height:1.4;animation:2.6s ease-out forwards kz2Bea_pet-bubble-pop;position:absolute;bottom:100%;box-shadow:0 2px 8px #00000040}.kz2Bea_bubblePet{background:#4d6bfef2}.kz2Bea_bubbleFeed{background:#0369a1f2}.kz2Bea_bubbleStatus{text-overflow:ellipsis;backdrop-filter:blur(8px);letter-spacing:.02em;background:linear-gradient(160deg,#131c36e6,#070b1af0);border:1px solid #7e98ff73;max-width:min(280px,100vw - 24px);padding:5px 12px;animation:.24s ease-out kz2Bea_pet-bubble-in;overflow:hidden;box-shadow:0 4px 12px #02061759,inset 0 1px #e2e8ff1a,0 0 12px #4d6bfe2e}.kz2Bea_bubbleWhisper{letter-spacing:.03em;animation:.32s cubic-bezier(.22,1,.36,1) kz2Bea_pet-whisper-in}.kz2Bea_bubbleWhisper:before{content:\"「\";opacity:.7;margin-right:2px}.kz2Bea_bubbleWhisper:after{content:\"」\";opacity:.7;margin-left:2px}.kz2Bea_bubbleStack{pointer-events:auto;flex-direction:column-reverse;align-items:center;gap:4px;display:flex;position:absolute;bottom:100%}.kz2Bea_bubbleAnchor{display:inline-flex;position:relative}.kz2Bea_bubbleMore{z-index:1;color:#eef2ff;cursor:pointer;background:linear-gradient(160deg,#4a68f5f2,#2f44b8f2);border:1px solid #7e98ff8c;border-radius:999px;justify-content:center;align-items:center;min-width:18px;height:18px;padding:0 5px;font-size:10px;font-weight:600;line-height:1;transition:filter .12s;display:inline-flex;position:absolute;top:-9px;right:-10px;box-shadow:0 2px 6px #02061766}.kz2Bea_bubbleMore:hover{filter:brightness(1.15)}.kz2Bea_bubbleMore:focus-visible{outline:none;box-shadow:0 0 0 2px #7e98ffe6}.kz2Bea_bubbleStack .kz2Bea_bubble{pointer-events:auto;margin-bottom:0;position:relative;bottom:auto}.kz2Bea_bubbleClickable{cursor:pointer;font:inherit;text-align:center;appearance:none;transition:filter .12s,box-shadow .12s}.kz2Bea_bubbleClickable:hover{filter:brightness(1.12)}.kz2Bea_bubbleClickable:focus-visible{outline:none;box-shadow:0 0 0 2px #7e98ffe6}@keyframes kz2Bea_pet-bubble-in{0%{opacity:0;transform:translateY(6px)scale(.96)}to{opacity:1;transform:translateY(0)scale(1)}}@keyframes kz2Bea_pet-whisper-in{0%{opacity:0;transform:translateY(8px)scale(.9)}60%{transform:translateY(-1px)scale(1.02)}to{opacity:1;transform:translateY(0)scale(1)}}@keyframes kz2Bea_pet-panel-in{0%{opacity:0;transform:translateY(4px)scale(.97)}to{opacity:1;transform:translateY(0)scale(1)}}@keyframes kz2Bea_pet-bubble-pop{0%{opacity:0;transform:translateY(6px)scale(.85)}15%{opacity:1;transform:translateY(0)scale(1.05)}25%{transform:translateY(0)scale(1)}75%{opacity:1}to{opacity:0;transform:translateY(-8px)scale(.95)}}.kz2Bea_panel{color:#e6ebf8;backdrop-filter:blur(10px);transform-origin:50% 0;background:linear-gradient(165deg,#131c36f2,#070b1af7);border:1px solid #7e98ff4d;border-radius:12px;flex-direction:column;gap:7px;min-width:148px;margin-top:8px;padding:10px 12px;font-size:12px;animation:.2s cubic-bezier(.22,1,.36,1) kz2Bea_pet-panel-in;display:flex;position:absolute;top:100%;box-shadow:0 8px 24px #02061773,0 0 0 1px #4d6bfe1a,inset 0 1px #e2e8ff14}.kz2Bea_panel:after{content:\"\";height:14px;position:absolute;bottom:100%;left:0;right:0}.kz2Bea_panelAbove{transform-origin:50% 100%;margin-top:0;margin-bottom:8px;top:auto;bottom:100%}.kz2Bea_panelAbove:after{top:100%;bottom:auto}.kz2Bea_rankRow{white-space:nowrap;justify-content:space-between;gap:10px;display:flex}.kz2Bea_nameCell{letter-spacing:.02em;background:linear-gradient(90deg,#a9c1ff,#6c8bff 60%,#4d6bfe);color:#0000;-webkit-background-clip:text;background-clip:text;font-weight:600}.kz2Bea_statRank{color:#9db4ff;font-weight:600}.kz2Bea_statTreats{color:#7dd3fc;font-weight:600}.kz2Bea_statPoints{color:#fcd34d;font-weight:600}.kz2Bea_renameRow{align-items:center;gap:6px;display:flex}.kz2Bea_nameInput{color:#e6ebf8;background:#131c36e6;border:1px solid #7e98ff80;border-radius:6px;outline:none;flex:1;min-width:0;padding:3px 6px;font-size:12px}.kz2Bea_nameInput:focus{border-color:#4d6bfe;box-shadow:0 0 0 2px #4d6bfe73}.kz2Bea_actions{gap:6px;display:flex}.kz2Bea_action{cursor:pointer;color:#fff;background:linear-gradient(#4a68f5,#3a55e0);border:none;border-radius:6px;flex:1;padding:4px 8px;font-size:12px;font-weight:600;transition:filter .12s,box-shadow .12s,transform .12s;box-shadow:0 2px 6px #4d6bfe4d}.kz2Bea_action:hover{filter:brightness(1.08);transform:translateY(-1px);box-shadow:0 4px 10px #4d6bfe66}.kz2Bea_action:active{filter:brightness(.94);transform:translateY(0);box-shadow:0 1px 4px #4d6bfe4d}.kz2Bea_action:focus-visible{outline:none;box-shadow:0 0 0 2px #7e98ffe6}.kz2Bea_summon{color:#8ea6ff;cursor:pointer;background:#070b1abf;border:1px dashed #7e98ff99;border-radius:999px;padding:2px 10px;font-size:11px;transition:border-color .12s,color .12s,background .12s,box-shadow .12s}.kz2Bea_summon:hover{color:#c3d3ff;background:#070b1ae6;border-color:#7e98fff2}.kz2Bea_summon:active{color:#8ea6ff;border-color:#7e98ffcc}.kz2Bea_summon:focus-visible{outline:none;box-shadow:0 0 0 2px #7e98ffe6}@media (prefers-reduced-motion:reduce){.kz2Bea_bubble,.kz2Bea_bubbleStatus,.kz2Bea_bubbleWhisper,.kz2Bea_panel{opacity:1;animation:none}.kz2Bea_action,.kz2Bea_summon,.kz2Bea_bubbleMore{transition:none}}.kz2Bea_gameplayHud{width:0;height:0;position:absolute;bottom:0;left:0}.kz2Bea_gameplayModeChip{color:#cdd7ff;white-space:nowrap;pointer-events:none;background:#7e98ff2e;border-radius:999px;padding:1px 8px;font-size:10px;position:absolute;top:0;left:0;transform:translateY(-100%)translateY(-4px)}.kz2Bea_gameplayCard{color:#dfe6ff;background:#070b1aeb;border:1px solid #7e98ff40;border-radius:10px;min-width:180px;max-height:340px;padding:10px 12px;font-size:12px;position:absolute;bottom:0;left:0;overflow-y:auto;transform:none;box-shadow:0 8px 24px #0006}.kz2Bea_gameplayBars{flex-direction:column;gap:4px;margin-bottom:8px;display:flex}.kz2Bea_gameplayBarRow{align-items:center;gap:6px;display:flex}.kz2Bea_gameplayBarLabel{color:#9aa8d8;flex-shrink:0;width:34px}.kz2Bea_gameplayBarTrack{background:#7e98ff26;border-radius:3px;flex:1;height:6px;overflow:hidden}.kz2Bea_gameplayBarFill{background:linear-gradient(90deg,#7e98ff,#9db4ff);border-radius:3px;height:100%;transition:width .3s;display:block}.kz2Bea_gameplayActions{flex-wrap:wrap;gap:6px;display:flex}.kz2Bea_gameplayShopItems{grid-template-columns:repeat(2,minmax(0,1fr));gap:8px;margin-bottom:8px;display:grid}.kz2Bea_gameplayShopItem{color:inherit;cursor:pointer;background:#7e98ff14;border:1px solid #7e98ff40;border-radius:8px;flex-direction:column;align-items:center;gap:4px;padding:8px 6px;display:flex}.kz2Bea_gameplayShopItem:hover{background:#7e98ff29;border-color:#7e98ffb3}.kz2Bea_gameplayShopItemImage{object-fit:contain;width:36px;height:36px;image-rendering:pixelated;pointer-events:none}.kz2Bea_gameplayShopItemLabel{text-align:center;font-size:11px}.kz2Bea_gameplayShopItemPrice{color:#ffd27e;font-size:10px}.kz2Bea_gameplayClose{color:#9aa8d8;cursor:pointer;background:0 0;border:none;padding:0 2px;font-size:14px;position:absolute;top:4px;right:8px}.kz2Bea_gameplayClose:hover{color:#fff}.kz2Bea_gameplayFloat{color:#3a2a00;white-space:nowrap;pointer-events:none;background:#ffd27eeb;border-radius:999px;padding:2px 10px;font-size:11px;animation:1.1s ease-out forwards kz2Bea_pet-gameplay-float;position:absolute;top:-8px;left:50%;transform:translate(-50%)}@keyframes kz2Bea_pet-gameplay-float{0%{opacity:0;transform:translate(-50%)translateY(6px)}20%{opacity:1}to{opacity:0;transform:translate(-50%)translateY(-18px)}}@media (prefers-reduced-motion:reduce){.kz2Bea_gameplayFloat{opacity:1;animation:none}.kz2Bea_gameplayBarFill{transition:none}}";
|
|
169
175
|
const tagId$2 = "@linxin666/dsh-pet/pet.module.css";
|
|
170
176
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
|
|
171
177
|
const tag = document.createElement("style");
|
|
@@ -186,14 +192,30 @@ window.__ModuleLoader__.load({
|
|
|
186
192
|
"bubbleStack": "kz2Bea_bubbleStack",
|
|
187
193
|
"bubbleStatus": "kz2Bea_bubbleStatus",
|
|
188
194
|
"bubbleWhisper": "kz2Bea_bubbleWhisper",
|
|
189
|
-
"closeButton": "kz2Bea_closeButton",
|
|
190
195
|
"float": "kz2Bea_float",
|
|
196
|
+
"gameplayActions": "kz2Bea_gameplayActions",
|
|
197
|
+
"gameplayBarFill": "kz2Bea_gameplayBarFill",
|
|
198
|
+
"gameplayBarLabel": "kz2Bea_gameplayBarLabel",
|
|
199
|
+
"gameplayBarRow": "kz2Bea_gameplayBarRow",
|
|
200
|
+
"gameplayBarTrack": "kz2Bea_gameplayBarTrack",
|
|
201
|
+
"gameplayBars": "kz2Bea_gameplayBars",
|
|
202
|
+
"gameplayCard": "kz2Bea_gameplayCard",
|
|
203
|
+
"gameplayClose": "kz2Bea_gameplayClose",
|
|
204
|
+
"gameplayFloat": "kz2Bea_gameplayFloat",
|
|
205
|
+
"gameplayHud": "kz2Bea_gameplayHud",
|
|
206
|
+
"gameplayModeChip": "kz2Bea_gameplayModeChip",
|
|
207
|
+
"gameplayShopItem": "kz2Bea_gameplayShopItem",
|
|
208
|
+
"gameplayShopItemImage": "kz2Bea_gameplayShopItemImage",
|
|
209
|
+
"gameplayShopItemLabel": "kz2Bea_gameplayShopItemLabel",
|
|
210
|
+
"gameplayShopItemPrice": "kz2Bea_gameplayShopItemPrice",
|
|
211
|
+
"gameplayShopItems": "kz2Bea_gameplayShopItems",
|
|
191
212
|
"nameCell": "kz2Bea_nameCell",
|
|
192
213
|
"nameInput": "kz2Bea_nameInput",
|
|
193
214
|
"panel": "kz2Bea_panel",
|
|
194
215
|
"panelAbove": "kz2Bea_panelAbove",
|
|
195
216
|
"pet-bubble-in": "kz2Bea_pet-bubble-in",
|
|
196
217
|
"pet-bubble-pop": "kz2Bea_pet-bubble-pop",
|
|
218
|
+
"pet-gameplay-float": "kz2Bea_pet-gameplay-float",
|
|
197
219
|
"pet-panel-in": "kz2Bea_pet-panel-in",
|
|
198
220
|
"pet-whisper-in": "kz2Bea_pet-whisper-in",
|
|
199
221
|
"rankRow": "kz2Bea_rankRow",
|
|
@@ -448,6 +470,7 @@ window.__ModuleLoader__.load({
|
|
|
448
470
|
};
|
|
449
471
|
(0, react.useEffect)(() => () => clearHideTimer(), []);
|
|
450
472
|
const onPointerDown = (e) => {
|
|
473
|
+
if (props.dragDisabled === true) return;
|
|
451
474
|
e.preventDefault();
|
|
452
475
|
e.target.setPointerCapture?.(e.pointerId);
|
|
453
476
|
const current = dragPos ?? {
|
|
@@ -467,7 +490,10 @@ window.__ModuleLoader__.load({
|
|
|
467
490
|
if (drag === null) return;
|
|
468
491
|
const dx = e.clientX - drag.startX;
|
|
469
492
|
const dy = e.clientY - drag.startY;
|
|
470
|
-
if (Math.abs(dx) > 4 || Math.abs(dy) > 4)
|
|
493
|
+
if (Math.abs(dx) > 4 || Math.abs(dy) > 4) {
|
|
494
|
+
if (!draggedRef.current) props.onDraggingChange?.(true);
|
|
495
|
+
draggedRef.current = true;
|
|
496
|
+
}
|
|
471
497
|
const right = clampOffset(drag.right - dx, window.innerWidth - 40);
|
|
472
498
|
const bottom = clampOffset(drag.bottom - dy, window.innerHeight - 40);
|
|
473
499
|
setDragPos({
|
|
@@ -478,6 +504,7 @@ window.__ModuleLoader__.load({
|
|
|
478
504
|
const onPointerUp = () => {
|
|
479
505
|
if (dragRef.current === null) return;
|
|
480
506
|
dragRef.current = null;
|
|
507
|
+
if (draggedRef.current) props.onDraggingChange?.(false);
|
|
481
508
|
if (dragPos !== null) props.onDragEnd(dragPos.right, dragPos.bottom);
|
|
482
509
|
};
|
|
483
510
|
const pos = dragPos ?? {
|
|
@@ -490,8 +517,7 @@ window.__ModuleLoader__.load({
|
|
|
490
517
|
const stackOpen = stackPeek || stackPinned;
|
|
491
518
|
const visibleSessions = !stackOpen && sessionBubbles.length > 1 ? sessionBubbles.slice(0, 1) : sessionBubbles;
|
|
492
519
|
const statusBubble = feedback === null && sessionBubbles.length === 0 ? snapshot?.bubble : void 0;
|
|
493
|
-
const
|
|
494
|
-
const bubblePresent = feedback !== null || sessionBubbles.length > 0 || statusBubble !== void 0 || whisper !== void 0;
|
|
520
|
+
const bubblePresent = feedback !== null || sessionBubbles.length > 0 || statusBubble !== void 0;
|
|
495
521
|
const displayName = snapshot?.name ?? definition.displayName;
|
|
496
522
|
const decoration = snapshot?.decoration;
|
|
497
523
|
(0, react.useEffect)(() => {
|
|
@@ -546,13 +572,13 @@ window.__ModuleLoader__.load({
|
|
|
546
572
|
hideTimerRef.current = window.setTimeout(() => setHovered(false), 300);
|
|
547
573
|
},
|
|
548
574
|
children: [
|
|
549
|
-
/* @__PURE__ */ (0, react_jsx_runtime.
|
|
575
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
550
576
|
className: pet_module_css_default.spriteWrap,
|
|
551
577
|
style: {
|
|
552
578
|
width: spriteWidth,
|
|
553
579
|
height: spriteHeight
|
|
554
580
|
},
|
|
555
|
-
children:
|
|
581
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
556
582
|
ref: spriteRef,
|
|
557
583
|
className: pet_module_css_default.sprite,
|
|
558
584
|
style: {
|
|
@@ -569,41 +595,32 @@ window.__ModuleLoader__.load({
|
|
|
569
595
|
onPointerDown,
|
|
570
596
|
onPointerMove,
|
|
571
597
|
onPointerUp,
|
|
572
|
-
onClick: () => {
|
|
598
|
+
onClick: (e) => {
|
|
573
599
|
if (draggedRef.current) return;
|
|
600
|
+
if (props.onGameplayTap !== void 0 && spriteRef.current !== null) {
|
|
601
|
+
const rect = spriteRef.current.getBoundingClientRect();
|
|
602
|
+
if (rect.width > 0 && rect.height > 0) props.onGameplayTap((e.clientX - rect.left) / rect.width, (e.clientY - rect.top) / rect.height);
|
|
603
|
+
}
|
|
574
604
|
props.onPet();
|
|
575
605
|
},
|
|
576
606
|
role: "button",
|
|
577
607
|
"aria-label": definition.displayName,
|
|
578
608
|
children: props.visual
|
|
579
|
-
})
|
|
580
|
-
type: "button",
|
|
581
|
-
className: pet_module_css_default.closeButton,
|
|
582
|
-
"aria-label": panelLabel("hide", props.t("pet.hide")),
|
|
583
|
-
title: panelLabel("hide", props.t("pet.hide")),
|
|
584
|
-
"data-testid": "pet-close",
|
|
585
|
-
onPointerDown: (e) => {
|
|
586
|
-
e.stopPropagation();
|
|
587
|
-
},
|
|
588
|
-
onClick: (e) => {
|
|
589
|
-
e.stopPropagation();
|
|
590
|
-
props.onHide();
|
|
591
|
-
},
|
|
592
|
-
children: "×"
|
|
593
|
-
})]
|
|
609
|
+
})
|
|
594
610
|
}),
|
|
611
|
+
props.hud,
|
|
595
612
|
feedback !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
596
613
|
ref: bubbleRef,
|
|
597
614
|
className: clsx(pet_module_css_default.bubble, feedback.kind === "feed" ? pet_module_css_default.bubbleFeed : pet_module_css_default.bubblePet),
|
|
598
615
|
children: feedback.text
|
|
599
616
|
}, feedback.at),
|
|
600
|
-
feedback === null && (sessionBubbles.length > 0 || statusBubble !== void 0
|
|
617
|
+
feedback === null && (sessionBubbles.length > 0 || statusBubble !== void 0) && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
601
618
|
ref: bubbleRef,
|
|
602
619
|
className: pet_module_css_default.bubbleStack,
|
|
603
620
|
onPointerEnter: () => setStackPeek(true),
|
|
604
621
|
onPointerLeave: () => setStackPeek(false),
|
|
605
622
|
children: [visibleSessions.map((session, index) => {
|
|
606
|
-
const speaksWhisper =
|
|
623
|
+
const speaksWhisper = session.whisper !== void 0;
|
|
607
624
|
const bubble = /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
608
625
|
type: "button",
|
|
609
626
|
className: clsx(pet_module_css_default.bubble, pet_module_css_default.bubbleStatus, pet_module_css_default.bubbleClickable, speaksWhisper && pet_module_css_default.bubbleWhisper),
|
|
@@ -614,8 +631,8 @@ window.__ModuleLoader__.load({
|
|
|
614
631
|
children: [index === 0 && !speaksWhisper && decoration !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatusOrnament, {
|
|
615
632
|
decoration,
|
|
616
633
|
phase
|
|
617
|
-
}),
|
|
618
|
-
}, speaksWhisper ? "whisper:" + whisper : session.sessionId);
|
|
634
|
+
}), session.whisper ?? session.bubble]
|
|
635
|
+
}, speaksWhisper ? "whisper:" + session.whisper : session.sessionId);
|
|
619
636
|
if (index !== 0 || sessionBubbles.length <= 1) return bubble;
|
|
620
637
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
621
638
|
className: pet_module_css_default.bubbleAnchor,
|
|
@@ -632,15 +649,15 @@ window.__ModuleLoader__.load({
|
|
|
632
649
|
children: stackOpen ? "×" : "+" + String(sessionBubbles.length - 1)
|
|
633
650
|
})]
|
|
634
651
|
}, "primary");
|
|
635
|
-
}), sessionBubbles.length === 0 &&
|
|
636
|
-
className: clsx(pet_module_css_default.bubble, pet_module_css_default.bubbleStatus
|
|
652
|
+
}), sessionBubbles.length === 0 && statusBubble !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
653
|
+
className: clsx(pet_module_css_default.bubble, pet_module_css_default.bubbleStatus),
|
|
637
654
|
role: "status",
|
|
638
655
|
"aria-live": "polite",
|
|
639
|
-
children: [
|
|
656
|
+
children: [decoration !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)(StatusOrnament, {
|
|
640
657
|
decoration,
|
|
641
658
|
phase
|
|
642
|
-
}),
|
|
643
|
-
},
|
|
659
|
+
}), statusBubble]
|
|
660
|
+
}, "status")]
|
|
644
661
|
}),
|
|
645
662
|
hovered && dragRef.current === null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
646
663
|
ref: panelRef,
|
|
@@ -732,6 +749,12 @@ window.__ModuleLoader__.load({
|
|
|
732
749
|
className: pet_module_css_default.action,
|
|
733
750
|
onClick: props.onHide,
|
|
734
751
|
children: panelLabel("hide", props.t("pet.hide"))
|
|
752
|
+
}),
|
|
753
|
+
props.onGameplayMenu !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
754
|
+
type: "button",
|
|
755
|
+
className: pet_module_css_default.action,
|
|
756
|
+
onClick: props.onGameplayMenu,
|
|
757
|
+
children: props.t("pet.gameplay.menu")
|
|
735
758
|
})
|
|
736
759
|
]
|
|
737
760
|
})
|
|
@@ -741,6 +764,27 @@ window.__ModuleLoader__.load({
|
|
|
741
764
|
}), document.body);
|
|
742
765
|
}
|
|
743
766
|
//#endregion
|
|
767
|
+
//#region src/client/drag-stream.ts
|
|
768
|
+
/** Create the stream (one per pet activation, owned by the renderer switch). */
|
|
769
|
+
function createDragStream() {
|
|
770
|
+
let current = false;
|
|
771
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
772
|
+
return {
|
|
773
|
+
get: () => current,
|
|
774
|
+
subscribe(listener) {
|
|
775
|
+
listeners.add(listener);
|
|
776
|
+
return () => {
|
|
777
|
+
listeners.delete(listener);
|
|
778
|
+
};
|
|
779
|
+
},
|
|
780
|
+
push(dragging) {
|
|
781
|
+
if (dragging === current) return;
|
|
782
|
+
current = dragging;
|
|
783
|
+
for (const listener of [...listeners]) listener(dragging);
|
|
784
|
+
}
|
|
785
|
+
};
|
|
786
|
+
}
|
|
787
|
+
//#endregion
|
|
744
788
|
//#region src/client/renderers/registry.ts
|
|
745
789
|
/** Renderer dispatch table. */
|
|
746
790
|
var RendererRegistry = class {
|
|
@@ -888,6 +932,93 @@ window.__ModuleLoader__.load({
|
|
|
888
932
|
});
|
|
889
933
|
}
|
|
890
934
|
//#endregion
|
|
935
|
+
//#region src/client/renderers/Frames2dVisualMount.tsx
|
|
936
|
+
/**
|
|
937
|
+
* Frames2d visual mount — the React bridge between the pet center chrome
|
|
938
|
+
* and the imperative frames2d renderer, mirroring the live2d mount. The
|
|
939
|
+
* bridge owns the contract context (asset base, phase stream, interaction
|
|
940
|
+
* write-back, activation cleanups), feeds the polled phase into the stream,
|
|
941
|
+
* forwards the chrome's drag gesture onto the conventional 'drag' track
|
|
942
|
+
* (when the pet declares one), and renders the localized fallback card when
|
|
943
|
+
* the served config is invalid.
|
|
944
|
+
* @module @linxin666/dsh-pet/client/renderers/Frames2dVisualMount
|
|
945
|
+
*/
|
|
946
|
+
/** Mount the frames2d renderer as the sprite's visual (inside the chrome). */
|
|
947
|
+
function Frames2dVisualMount(props) {
|
|
948
|
+
const containerRef = (0, react.useRef)(null);
|
|
949
|
+
const streamRef = (0, react.useRef)(null);
|
|
950
|
+
const handleRef = (0, react.useRef)(null);
|
|
951
|
+
const [invalid, setInvalid] = (0, react.useState)(false);
|
|
952
|
+
(0, react.useEffect)(() => {
|
|
953
|
+
setInvalid(false);
|
|
954
|
+
const container = containerRef.current;
|
|
955
|
+
const frames2d = props.definition.frames2d;
|
|
956
|
+
if (container === null || frames2d === void 0) return void 0;
|
|
957
|
+
streamRef.current ??= createPhaseStream(props.phase);
|
|
958
|
+
const cleanups = [];
|
|
959
|
+
const ctx = {
|
|
960
|
+
petId: props.definition.id,
|
|
961
|
+
assetBase: "/pet/" + encodeURIComponent(props.definition.id),
|
|
962
|
+
container,
|
|
963
|
+
phase: streamRef.current,
|
|
964
|
+
interact: props.onPet,
|
|
965
|
+
onCleanup: (fn) => {
|
|
966
|
+
cleanups.push(fn);
|
|
967
|
+
}
|
|
968
|
+
};
|
|
969
|
+
let handle;
|
|
970
|
+
try {
|
|
971
|
+
handle = defaultPetRendererRegistry.mount("frames2d", ctx, frames2d);
|
|
972
|
+
} catch {
|
|
973
|
+
setInvalid(true);
|
|
974
|
+
return () => {
|
|
975
|
+
for (const fn of cleanups.splice(0)) fn();
|
|
976
|
+
};
|
|
977
|
+
}
|
|
978
|
+
handleRef.current = handle;
|
|
979
|
+
if (props.bus !== void 0) {
|
|
980
|
+
const gameplayBus = props.bus;
|
|
981
|
+
gameplayBus.setTrack = (track) => {
|
|
982
|
+
handleRef.current?.setState(track);
|
|
983
|
+
};
|
|
984
|
+
cleanups.push(() => {
|
|
985
|
+
gameplayBus.setTrack = void 0;
|
|
986
|
+
});
|
|
987
|
+
}
|
|
988
|
+
const dragTrack = props.definition.gameplay?.dragState ?? (frames2d.tracks.drag === void 0 ? void 0 : "drag");
|
|
989
|
+
const offDrag = props.drag.subscribe((dragging) => {
|
|
990
|
+
if (dragTrack === void 0) return;
|
|
991
|
+
if (dragging) {
|
|
992
|
+
handle.setState(dragTrack);
|
|
993
|
+
return;
|
|
994
|
+
}
|
|
995
|
+
handle.setState(props.definition.gameplay?.dragEndState);
|
|
996
|
+
});
|
|
997
|
+
cleanups.push(offDrag);
|
|
998
|
+
return () => {
|
|
999
|
+
handleRef.current = null;
|
|
1000
|
+
for (const fn of cleanups.splice(0)) fn();
|
|
1001
|
+
handle.dispose();
|
|
1002
|
+
};
|
|
1003
|
+
}, [props.definition]);
|
|
1004
|
+
(0, react.useEffect)(() => {
|
|
1005
|
+
streamRef.current?.push(props.phase);
|
|
1006
|
+
}, [props.phase]);
|
|
1007
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1008
|
+
ref: containerRef,
|
|
1009
|
+
"data-dsh-pet-frames2d": props.definition.id,
|
|
1010
|
+
style: {
|
|
1011
|
+
width: "100%",
|
|
1012
|
+
height: "100%",
|
|
1013
|
+
pointerEvents: "none"
|
|
1014
|
+
},
|
|
1015
|
+
children: invalid && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1016
|
+
"data-dsh-pet-frames2d-error": "invalid-config",
|
|
1017
|
+
children: props.t("pet.renderer.unavailable", { renderer: "frames2d" })
|
|
1018
|
+
})
|
|
1019
|
+
});
|
|
1020
|
+
}
|
|
1021
|
+
//#endregion
|
|
891
1022
|
//#region src/client/renderers/PetRendererSwitch.tsx
|
|
892
1023
|
/**
|
|
893
1024
|
* Renderer switch — the client dispatch seam of the pet center (issue #623,
|
|
@@ -901,7 +1032,27 @@ window.__ModuleLoader__.load({
|
|
|
901
1032
|
/** Dispatch one pet definition to its renderer; unknown kinds get a card. */
|
|
902
1033
|
function PetRendererSwitch(props) {
|
|
903
1034
|
const renderer = props.definition.renderer ?? "sprite2d";
|
|
1035
|
+
const dragRef = (0, react.useRef)(null);
|
|
1036
|
+
if (dragRef.current === null || dragRef.current.id !== props.definition.id) dragRef.current = {
|
|
1037
|
+
id: props.definition.id,
|
|
1038
|
+
stream: createDragStream()
|
|
1039
|
+
};
|
|
1040
|
+
const drag = props.drag ?? dragRef.current.stream;
|
|
904
1041
|
if (renderer === "sprite2d") return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(react_jsx_runtime.Fragment, { children: props.children });
|
|
1042
|
+
if (renderer === "frames2d" && defaultPetRendererRegistry.has("frames2d") && (0, react.isValidElement)(props.children)) {
|
|
1043
|
+
const visual = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Frames2dVisualMount, {
|
|
1044
|
+
definition: props.definition,
|
|
1045
|
+
phase: props.phase,
|
|
1046
|
+
onPet: props.onPet,
|
|
1047
|
+
drag,
|
|
1048
|
+
...props.bus === void 0 ? {} : { bus: props.bus },
|
|
1049
|
+
t: props.t
|
|
1050
|
+
});
|
|
1051
|
+
return (0, react.cloneElement)(props.children, {
|
|
1052
|
+
visual,
|
|
1053
|
+
onDraggingChange: (dragging) => drag.push(dragging)
|
|
1054
|
+
});
|
|
1055
|
+
}
|
|
905
1056
|
if (renderer === "live2d" && defaultPetRendererRegistry.has("live2d") && (0, react.isValidElement)(props.children)) {
|
|
906
1057
|
const visual = /* @__PURE__ */ (0, react_jsx_runtime.jsx)(Live2dVisualMount, {
|
|
907
1058
|
definition: props.definition,
|
|
@@ -917,6 +1068,362 @@ window.__ModuleLoader__.load({
|
|
|
917
1068
|
});
|
|
918
1069
|
}
|
|
919
1070
|
//#endregion
|
|
1071
|
+
//#region src/gameplay.ts
|
|
1072
|
+
/** The zone one normalized hit-box point lands in, if any. */
|
|
1073
|
+
function touchZoneAt(touch, yFraction) {
|
|
1074
|
+
return touch.zones.find((zone) => yFraction >= zone.y0 && yFraction < zone.y1);
|
|
1075
|
+
}
|
|
1076
|
+
//#endregion
|
|
1077
|
+
//#region src/client/gameplay-hud.tsx
|
|
1078
|
+
/**
|
|
1079
|
+
* Gameplay HUD — the client half of the manifest 'gameplay' block (miku-pet
|
|
1080
|
+
* generalization). One component owns everything the block needs: the stat
|
|
1081
|
+
* bars and shop card (menu / shop pages), the touch-zone tap
|
|
1082
|
+
* handling, the idle director rolls, the work and sleep loops, and the
|
|
1083
|
+
* float-text toasts. It talks to the host through the injected verb API,
|
|
1084
|
+
* writes results straight back into the store (the 2 s poll stays the
|
|
1085
|
+
* backstop), and steers the frames2d renderer through the per-pet bus.
|
|
1086
|
+
* @module @linxin666/dsh-pet/client/GameplayHud
|
|
1087
|
+
*/
|
|
1088
|
+
let floatSeq = 0;
|
|
1089
|
+
/** The gameplay overlay for one frames2d pet that declares 'gameplay'. */
|
|
1090
|
+
function GameplayHud(props) {
|
|
1091
|
+
const { definition, store, api, bus } = props;
|
|
1092
|
+
const ui = (0, react.useSyncExternalStore)(store.subscribe, store.getSnapshot);
|
|
1093
|
+
const def = definition.gameplay;
|
|
1094
|
+
const view = ui.snapshot?.gameplay;
|
|
1095
|
+
const phase = ui.snapshot?.phase ?? "idle";
|
|
1096
|
+
const [open, setOpen] = (0, react.useState)(false);
|
|
1097
|
+
const [page, setPage] = (0, react.useState)("root");
|
|
1098
|
+
const hudRef = (0, react.useRef)(null);
|
|
1099
|
+
const cardRef = (0, react.useRef)(null);
|
|
1100
|
+
const [floats, setFloats] = (0, react.useState)([]);
|
|
1101
|
+
const modeRef = (0, react.useRef)(view?.mode ?? null);
|
|
1102
|
+
modeRef.current = view?.mode ?? null;
|
|
1103
|
+
const phaseRef = (0, react.useRef)(phase);
|
|
1104
|
+
phaseRef.current = phase;
|
|
1105
|
+
const draggingRef = (0, react.useRef)(false);
|
|
1106
|
+
const touchLockUntilRef = (0, react.useRef)(0);
|
|
1107
|
+
const missRef = (0, react.useRef)(0);
|
|
1108
|
+
const busyRef = (0, react.useRef)(false);
|
|
1109
|
+
const tr = props.t;
|
|
1110
|
+
const statLabel = (name) => tr("pet.gameplay.stat." + name);
|
|
1111
|
+
const currencyLabel = (name) => tr("pet.gameplay.currency." + name);
|
|
1112
|
+
const pushFloat = (text) => {
|
|
1113
|
+
const id = ++floatSeq;
|
|
1114
|
+
setFloats((list) => [...list.slice(-3), {
|
|
1115
|
+
id,
|
|
1116
|
+
text
|
|
1117
|
+
}]);
|
|
1118
|
+
window.setTimeout(() => {
|
|
1119
|
+
setFloats((list) => list.filter((entry) => entry.id !== id));
|
|
1120
|
+
}, 1100);
|
|
1121
|
+
};
|
|
1122
|
+
const applyResult = (result) => {
|
|
1123
|
+
if (result.view !== void 0) store.actions.setGameplayView(result.view);
|
|
1124
|
+
};
|
|
1125
|
+
(0, react.useEffect)(() => {
|
|
1126
|
+
if (def === void 0) return void 0;
|
|
1127
|
+
bus.tap = (fx, fy) => {
|
|
1128
|
+
if (modeRef.current === "sleep") {
|
|
1129
|
+
api.setMode(null).then(applyResult, () => void 0);
|
|
1130
|
+
return;
|
|
1131
|
+
}
|
|
1132
|
+
if (modeRef.current === "work") return;
|
|
1133
|
+
const box = def.hitBox ?? {
|
|
1134
|
+
x0: 0,
|
|
1135
|
+
y0: 0,
|
|
1136
|
+
x1: 1,
|
|
1137
|
+
y1: 1
|
|
1138
|
+
};
|
|
1139
|
+
const hx = (fx - box.x0) / (box.x1 - box.x0);
|
|
1140
|
+
const hy = (fy - box.y0) / (box.y1 - box.y0);
|
|
1141
|
+
if (hx < 0 || hx > 1 || hy < 0 || hy > 1) return;
|
|
1142
|
+
if (Date.now() < touchLockUntilRef.current) {
|
|
1143
|
+
api.touch().then(applyResult, () => void 0);
|
|
1144
|
+
return;
|
|
1145
|
+
}
|
|
1146
|
+
const zone = def.touch === void 0 ? void 0 : touchZoneAt(def.touch, hy);
|
|
1147
|
+
if (zone === void 0) return;
|
|
1148
|
+
api.touch(zone.name).then((result) => {
|
|
1149
|
+
applyResult(result);
|
|
1150
|
+
if (result.hit !== true) return;
|
|
1151
|
+
if (result.state !== void 0) {
|
|
1152
|
+
bus.setTrack?.(result.state);
|
|
1153
|
+
const holdMs = result.stateMs ?? 3e3;
|
|
1154
|
+
touchLockUntilRef.current = Date.now() + holdMs;
|
|
1155
|
+
window.setTimeout(() => {
|
|
1156
|
+
if (Date.now() >= touchLockUntilRef.current) bus.setTrack?.(void 0);
|
|
1157
|
+
}, holdMs);
|
|
1158
|
+
}
|
|
1159
|
+
if (result.phrase !== void 0) store.actions.setFeedback({
|
|
1160
|
+
text: result.phrase,
|
|
1161
|
+
kind: "none",
|
|
1162
|
+
at: Date.now()
|
|
1163
|
+
});
|
|
1164
|
+
}, () => void 0);
|
|
1165
|
+
};
|
|
1166
|
+
return () => {
|
|
1167
|
+
bus.tap = void 0;
|
|
1168
|
+
};
|
|
1169
|
+
}, [definition.id, def]);
|
|
1170
|
+
(0, react.useEffect)(() => {
|
|
1171
|
+
bus.openCard = (next) => {
|
|
1172
|
+
setOpen((prev) => next ?? !prev);
|
|
1173
|
+
setPage("root");
|
|
1174
|
+
};
|
|
1175
|
+
return () => {
|
|
1176
|
+
bus.openCard = void 0;
|
|
1177
|
+
};
|
|
1178
|
+
}, [bus]);
|
|
1179
|
+
(0, react.useLayoutEffect)(() => {
|
|
1180
|
+
if (!open) return void 0;
|
|
1181
|
+
const hud = hudRef.current;
|
|
1182
|
+
const card = cardRef.current;
|
|
1183
|
+
if (hud === null || card === null) return void 0;
|
|
1184
|
+
const place = () => {
|
|
1185
|
+
const box = hud.parentElement?.getBoundingClientRect();
|
|
1186
|
+
if (box === void 0) return;
|
|
1187
|
+
const gap = 8;
|
|
1188
|
+
const width = card.getBoundingClientRect().width;
|
|
1189
|
+
const x = window.innerWidth - box.right >= width + gap ? box.width + gap : -(width + gap);
|
|
1190
|
+
card.style.transform = "translate(" + Math.round(x) + "px, " + Math.round(-box.height / 2) + "px) translateY(50%)";
|
|
1191
|
+
card.style.maxHeight = Math.round(box.height) + "px";
|
|
1192
|
+
};
|
|
1193
|
+
place();
|
|
1194
|
+
window.addEventListener("resize", place);
|
|
1195
|
+
return () => {
|
|
1196
|
+
window.removeEventListener("resize", place);
|
|
1197
|
+
};
|
|
1198
|
+
}, [open, page]);
|
|
1199
|
+
(0, react.useEffect)(() => {
|
|
1200
|
+
return props.drag.subscribe((dragging) => {
|
|
1201
|
+
draggingRef.current = dragging;
|
|
1202
|
+
if (dragging && modeRef.current === "sleep") api.setMode(null).then(applyResult, () => void 0);
|
|
1203
|
+
});
|
|
1204
|
+
}, [props.drag]);
|
|
1205
|
+
(0, react.useEffect)(() => {
|
|
1206
|
+
const director = def?.idleDirector;
|
|
1207
|
+
if (def === void 0 || director === void 0) return void 0;
|
|
1208
|
+
const total = director.idleWeight + director.acts.reduce((sum, act) => sum + act.weight, 0);
|
|
1209
|
+
if (total <= 0) return void 0;
|
|
1210
|
+
const timer = window.setInterval(() => {
|
|
1211
|
+
if (phaseRef.current !== "idle") return;
|
|
1212
|
+
if (modeRef.current !== null || draggingRef.current) return;
|
|
1213
|
+
if (Date.now() < touchLockUntilRef.current) return;
|
|
1214
|
+
let pickedAct;
|
|
1215
|
+
if (missRef.current >= director.maxMiss) {
|
|
1216
|
+
const actTotal = director.acts.reduce((sum, act) => sum + act.weight, 0);
|
|
1217
|
+
let actRoll = Math.random() * actTotal;
|
|
1218
|
+
for (const act of director.acts) {
|
|
1219
|
+
actRoll -= act.weight;
|
|
1220
|
+
if (actRoll < 0) {
|
|
1221
|
+
pickedAct = act;
|
|
1222
|
+
break;
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
} else {
|
|
1226
|
+
let roll = Math.random() * total;
|
|
1227
|
+
for (const act of director.acts) {
|
|
1228
|
+
roll -= act.weight;
|
|
1229
|
+
if (roll < 0) {
|
|
1230
|
+
pickedAct = act;
|
|
1231
|
+
break;
|
|
1232
|
+
}
|
|
1233
|
+
}
|
|
1234
|
+
}
|
|
1235
|
+
if (pickedAct === void 0) {
|
|
1236
|
+
missRef.current += 1;
|
|
1237
|
+
return;
|
|
1238
|
+
}
|
|
1239
|
+
missRef.current = 0;
|
|
1240
|
+
bus.setTrack?.(pickedAct.track);
|
|
1241
|
+
if (pickedAct.phrases !== void 0 && pickedAct.phrases.length > 0) {
|
|
1242
|
+
const phrase = pickedAct.phrases[Math.floor(Math.random() * pickedAct.phrases.length)];
|
|
1243
|
+
store.actions.setFeedback({
|
|
1244
|
+
text: phrase,
|
|
1245
|
+
kind: "none",
|
|
1246
|
+
at: Date.now()
|
|
1247
|
+
});
|
|
1248
|
+
}
|
|
1249
|
+
}, director.intervalMs);
|
|
1250
|
+
return () => window.clearInterval(timer);
|
|
1251
|
+
}, [definition.id, def]);
|
|
1252
|
+
(0, react.useEffect)(() => {
|
|
1253
|
+
const work = def?.work;
|
|
1254
|
+
if (def === void 0 || work === void 0 || view?.mode !== "work") return void 0;
|
|
1255
|
+
bus.setTrack?.(work.state);
|
|
1256
|
+
let resultTimer = 0;
|
|
1257
|
+
const timer = window.setInterval(() => {
|
|
1258
|
+
if (busyRef.current) return;
|
|
1259
|
+
busyRef.current = true;
|
|
1260
|
+
api.workTick().then((result) => {
|
|
1261
|
+
busyRef.current = false;
|
|
1262
|
+
applyResult(result);
|
|
1263
|
+
if (result.ok !== true || result.outcome === void 0) return;
|
|
1264
|
+
const resultTrack = result.outcome === "success" ? work.successState : work.failState;
|
|
1265
|
+
const hold = result.outcome === "success" ? work.resultMs?.success ?? 1300 : work.resultMs?.fail ?? 1900;
|
|
1266
|
+
bus.setTrack?.(resultTrack);
|
|
1267
|
+
resultTimer = window.setTimeout(() => {
|
|
1268
|
+
if (modeRef.current === "work") bus.setTrack?.(work.state);
|
|
1269
|
+
}, hold);
|
|
1270
|
+
}, () => {
|
|
1271
|
+
busyRef.current = false;
|
|
1272
|
+
});
|
|
1273
|
+
}, work.tickMs);
|
|
1274
|
+
return () => {
|
|
1275
|
+
window.clearInterval(timer);
|
|
1276
|
+
window.clearTimeout(resultTimer);
|
|
1277
|
+
bus.setTrack?.(void 0);
|
|
1278
|
+
};
|
|
1279
|
+
}, [
|
|
1280
|
+
definition.id,
|
|
1281
|
+
def,
|
|
1282
|
+
view?.mode
|
|
1283
|
+
]);
|
|
1284
|
+
(0, react.useEffect)(() => {
|
|
1285
|
+
const sleep = def?.sleep;
|
|
1286
|
+
if (def === void 0 || sleep === void 0 || view?.mode !== "sleep") return void 0;
|
|
1287
|
+
bus.setTrack?.(sleep.state);
|
|
1288
|
+
return () => bus.setTrack?.(void 0);
|
|
1289
|
+
}, [
|
|
1290
|
+
definition.id,
|
|
1291
|
+
def,
|
|
1292
|
+
view?.mode
|
|
1293
|
+
]);
|
|
1294
|
+
if (def === void 0 || view === void 0) return null;
|
|
1295
|
+
const mode = view.mode;
|
|
1296
|
+
const stats = def.stats ?? {};
|
|
1297
|
+
const shop = def.shop;
|
|
1298
|
+
const buy = (itemId) => {
|
|
1299
|
+
api.buy(itemId).then((result) => {
|
|
1300
|
+
applyResult(result);
|
|
1301
|
+
if (result.ok !== true) {
|
|
1302
|
+
if (result.error === "insufficient-funds") {
|
|
1303
|
+
const item = shop?.items.find((entry) => entry.id === itemId);
|
|
1304
|
+
pushFloat(tr("pet.gameplay.insufficient", { currency: currencyLabel(item?.currency ?? "treats") }));
|
|
1305
|
+
}
|
|
1306
|
+
return;
|
|
1307
|
+
}
|
|
1308
|
+
if (result.prize !== void 0) pushFloat(tr("pet.gameplay.prize", {
|
|
1309
|
+
amount: result.prize.amount,
|
|
1310
|
+
currency: currencyLabel(result.prize.currency)
|
|
1311
|
+
}));
|
|
1312
|
+
}, () => void 0);
|
|
1313
|
+
};
|
|
1314
|
+
const setMode = (next) => {
|
|
1315
|
+
api.setMode(next).then(applyResult, () => void 0);
|
|
1316
|
+
};
|
|
1317
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1318
|
+
ref: hudRef,
|
|
1319
|
+
className: pet_module_css_default.gameplayHud,
|
|
1320
|
+
"data-dsh-pet-gameplay": definition.id,
|
|
1321
|
+
children: [
|
|
1322
|
+
floats.map((entry) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1323
|
+
className: pet_module_css_default.gameplayFloat,
|
|
1324
|
+
children: entry.text
|
|
1325
|
+
}, entry.id)),
|
|
1326
|
+
mode !== null && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1327
|
+
className: pet_module_css_default.gameplayModeChip,
|
|
1328
|
+
children: tr(mode === "work" ? "pet.gameplay.working" : "pet.gameplay.sleeping")
|
|
1329
|
+
}),
|
|
1330
|
+
open && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1331
|
+
ref: cardRef,
|
|
1332
|
+
className: pet_module_css_default.gameplayCard,
|
|
1333
|
+
"data-page": page,
|
|
1334
|
+
children: [
|
|
1335
|
+
page === "root" && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1336
|
+
className: pet_module_css_default.gameplayBars,
|
|
1337
|
+
children: Object.entries(stats).map(([name, stat]) => {
|
|
1338
|
+
const value = view.stats[name] ?? 0;
|
|
1339
|
+
return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1340
|
+
className: pet_module_css_default.gameplayBarRow,
|
|
1341
|
+
title: statLabel(name) + " " + String(value) + "/" + String(stat.max),
|
|
1342
|
+
children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1343
|
+
className: pet_module_css_default.gameplayBarLabel,
|
|
1344
|
+
children: statLabel(name)
|
|
1345
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1346
|
+
className: pet_module_css_default.gameplayBarTrack,
|
|
1347
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1348
|
+
className: pet_module_css_default.gameplayBarFill,
|
|
1349
|
+
style: { width: Math.round(value / stat.max * 100) + "%" }
|
|
1350
|
+
})
|
|
1351
|
+
})]
|
|
1352
|
+
}, name);
|
|
1353
|
+
})
|
|
1354
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
|
|
1355
|
+
className: pet_module_css_default.gameplayActions,
|
|
1356
|
+
children: [
|
|
1357
|
+
def.work !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1358
|
+
type: "button",
|
|
1359
|
+
className: pet_module_css_default.action,
|
|
1360
|
+
onClick: () => setMode(mode === "work" ? null : "work"),
|
|
1361
|
+
children: tr(mode === "work" ? "pet.gameplay.stopWork" : "pet.gameplay.work")
|
|
1362
|
+
}),
|
|
1363
|
+
def.sleep !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1364
|
+
type: "button",
|
|
1365
|
+
className: pet_module_css_default.action,
|
|
1366
|
+
onClick: () => setMode(mode === "sleep" ? null : "sleep"),
|
|
1367
|
+
children: tr(mode === "sleep" ? "pet.gameplay.wake" : "pet.gameplay.sleep")
|
|
1368
|
+
}),
|
|
1369
|
+
shop !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1370
|
+
type: "button",
|
|
1371
|
+
className: pet_module_css_default.action,
|
|
1372
|
+
onClick: () => setPage("shop"),
|
|
1373
|
+
children: tr("pet.gameplay.shop")
|
|
1374
|
+
})
|
|
1375
|
+
]
|
|
1376
|
+
})] }),
|
|
1377
|
+
page === "shop" && shop !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1378
|
+
className: pet_module_css_default.gameplayShopItems,
|
|
1379
|
+
children: shop.items.map((item) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("button", {
|
|
1380
|
+
type: "button",
|
|
1381
|
+
className: pet_module_css_default.gameplayShopItem,
|
|
1382
|
+
onClick: () => buy(item.id),
|
|
1383
|
+
title: item.label + " — " + String(item.price) + " " + currencyLabel(item.currency),
|
|
1384
|
+
children: [
|
|
1385
|
+
item.image !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("img", {
|
|
1386
|
+
className: pet_module_css_default.gameplayShopItemImage,
|
|
1387
|
+
src: item.image,
|
|
1388
|
+
alt: "",
|
|
1389
|
+
draggable: false
|
|
1390
|
+
}),
|
|
1391
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1392
|
+
className: pet_module_css_default.gameplayShopItemLabel,
|
|
1393
|
+
children: item.label
|
|
1394
|
+
}),
|
|
1395
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("span", {
|
|
1396
|
+
className: pet_module_css_default.gameplayShopItemPrice,
|
|
1397
|
+
children: [
|
|
1398
|
+
item.price,
|
|
1399
|
+
" ",
|
|
1400
|
+
currencyLabel(item.currency)
|
|
1401
|
+
]
|
|
1402
|
+
})
|
|
1403
|
+
]
|
|
1404
|
+
}, item.id))
|
|
1405
|
+
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
1406
|
+
className: pet_module_css_default.gameplayActions,
|
|
1407
|
+
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1408
|
+
type: "button",
|
|
1409
|
+
className: pet_module_css_default.action,
|
|
1410
|
+
onClick: () => setPage("root"),
|
|
1411
|
+
children: tr("pet.gameplay.back")
|
|
1412
|
+
})
|
|
1413
|
+
})] }),
|
|
1414
|
+
/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
|
|
1415
|
+
type: "button",
|
|
1416
|
+
className: pet_module_css_default.gameplayClose,
|
|
1417
|
+
"aria-label": tr("pet.gameplay.back"),
|
|
1418
|
+
onClick: () => setOpen(false),
|
|
1419
|
+
children: "×"
|
|
1420
|
+
})
|
|
1421
|
+
]
|
|
1422
|
+
})
|
|
1423
|
+
]
|
|
1424
|
+
});
|
|
1425
|
+
}
|
|
1426
|
+
//#endregion
|
|
920
1427
|
//#region src/client/PetDockEntry.tsx
|
|
921
1428
|
/**
|
|
922
1429
|
* Global floating pet entry. The pet is host-global (its state, display and
|
|
@@ -953,6 +1460,14 @@ window.__ModuleLoader__.load({
|
|
|
953
1460
|
(0, react.useEffect)(() => {
|
|
954
1461
|
ensure();
|
|
955
1462
|
}, [ensure]);
|
|
1463
|
+
const auxRef = (0, react.useRef)(null);
|
|
1464
|
+
if (definition !== null && (auxRef.current === null || auxRef.current.id !== definition.id)) auxRef.current = {
|
|
1465
|
+
id: definition.id,
|
|
1466
|
+
bus: {},
|
|
1467
|
+
drag: createDragStream()
|
|
1468
|
+
};
|
|
1469
|
+
const aux = auxRef.current;
|
|
1470
|
+
const gameplay = definition?.gameplay;
|
|
956
1471
|
if (visible) return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
957
1472
|
"data-pet-dock": true,
|
|
958
1473
|
"data-testid": "pet-dock",
|
|
@@ -960,6 +1475,10 @@ window.__ModuleLoader__.load({
|
|
|
960
1475
|
definition,
|
|
961
1476
|
phase: snapshot?.phase ?? "idle",
|
|
962
1477
|
onPet: props.pet,
|
|
1478
|
+
...aux === null ? {} : {
|
|
1479
|
+
drag: aux.drag,
|
|
1480
|
+
bus: aux.bus
|
|
1481
|
+
},
|
|
963
1482
|
t: props.t,
|
|
964
1483
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(PetSprite, {
|
|
965
1484
|
snapshot,
|
|
@@ -973,6 +1492,19 @@ window.__ModuleLoader__.load({
|
|
|
973
1492
|
onRename: props.rename,
|
|
974
1493
|
onOpenSession: props.openSession,
|
|
975
1494
|
onFeedbackDone: props.feedbackDone,
|
|
1495
|
+
dragDisabled: snapshot.gameplay?.mode === "work",
|
|
1496
|
+
...gameplay === void 0 || aux === null ? {} : {
|
|
1497
|
+
onGameplayTap: (fx, fy) => aux.bus.tap?.(fx, fy),
|
|
1498
|
+
onGameplayMenu: () => aux.bus.openCard?.(),
|
|
1499
|
+
hud: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(GameplayHud, {
|
|
1500
|
+
definition,
|
|
1501
|
+
store,
|
|
1502
|
+
api: props.gameplay,
|
|
1503
|
+
bus: aux.bus,
|
|
1504
|
+
drag: aux.drag,
|
|
1505
|
+
t: props.t
|
|
1506
|
+
})
|
|
1507
|
+
},
|
|
976
1508
|
t: props.t
|
|
977
1509
|
})
|
|
978
1510
|
})
|
|
@@ -1268,6 +1800,161 @@ window.__ModuleLoader__.load({
|
|
|
1268
1800
|
}
|
|
1269
1801
|
};
|
|
1270
1802
|
//#endregion
|
|
1803
|
+
//#region src/client/renderers/frames2d.ts
|
|
1804
|
+
/**
|
|
1805
|
+
* frames2d renderer — plays the free-form named frame-sequence tracks of a
|
|
1806
|
+
* frames2d pet (manifest v2 'frames2d' block). The pet center picks the
|
|
1807
|
+
* track from the ActivityPhase stream (phase -> track map, idle fallback);
|
|
1808
|
+
* the gameplay driver may force a track through the handle's setState
|
|
1809
|
+
* override (drag/work/sleep/shop...), and a finished non-loop track settles
|
|
1810
|
+
* into its fallback, releasing the override when the fallback matches the
|
|
1811
|
+
* phase-mapped track. Rendering never throws: a broken track list degrades
|
|
1812
|
+
* to the first decodable frame, and the 1.2 s stall watchdog re-kicks the
|
|
1813
|
+
* playback chain after timer throttling.
|
|
1814
|
+
* @module @linxin666/dsh-pet/client/renderers/frames2d
|
|
1815
|
+
*/
|
|
1816
|
+
/** Stall watchdog period: a looping track stuck longer re-kicks its chain. */
|
|
1817
|
+
const WATCHDOG_MS = 1200;
|
|
1818
|
+
function isRecord(value) {
|
|
1819
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1820
|
+
}
|
|
1821
|
+
/** Fail-closed client-side validation of the served frames2d block. */
|
|
1822
|
+
function validateFrames2dConfig(config) {
|
|
1823
|
+
if (!isRecord(config) || !isRecord(config.tracks) || !isRecord(config.phases)) throw new Error("frames2d config requires tracks and phases objects");
|
|
1824
|
+
const tracks = {};
|
|
1825
|
+
for (const [name, raw] of Object.entries(config.tracks)) {
|
|
1826
|
+
if (!isRecord(raw) || !Array.isArray(raw.frames) || raw.frames.length === 0 || raw.frames.some((f) => typeof f !== "string" || f === "") || !Array.isArray(raw.durations) || raw.durations.length !== raw.frames.length || raw.durations.some((d) => typeof d !== "number" || !(d > 0))) throw new Error("frames2d track " + JSON.stringify(name) + " needs same-length frames/durations");
|
|
1827
|
+
tracks[name] = {
|
|
1828
|
+
frames: raw.frames,
|
|
1829
|
+
durations: raw.durations,
|
|
1830
|
+
loop: raw.loop !== false,
|
|
1831
|
+
...typeof raw.fallback === "string" ? { fallback: raw.fallback } : {}
|
|
1832
|
+
};
|
|
1833
|
+
}
|
|
1834
|
+
const phases = config.phases;
|
|
1835
|
+
if (typeof phases.idle !== "string" || tracks[phases.idle] === void 0) throw new Error("frames2d phases.idle must name an existing track");
|
|
1836
|
+
return {
|
|
1837
|
+
tracks,
|
|
1838
|
+
phases
|
|
1839
|
+
};
|
|
1840
|
+
}
|
|
1841
|
+
const frames2dRenderer = {
|
|
1842
|
+
id: "frames2d",
|
|
1843
|
+
apiVersion: PET_RENDERER_API_VERSION,
|
|
1844
|
+
validateConfig: validateFrames2dConfig,
|
|
1845
|
+
mount(ctx, config) {
|
|
1846
|
+
const reducedMotion = typeof window !== "undefined" && typeof window.matchMedia === "function" && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
1847
|
+
const img = document.createElement("img");
|
|
1848
|
+
img.dataset.dshPetFrames2d = ctx.petId;
|
|
1849
|
+
img.alt = "";
|
|
1850
|
+
img.draggable = false;
|
|
1851
|
+
img.style.width = "100%";
|
|
1852
|
+
img.style.height = "100%";
|
|
1853
|
+
img.style.objectFit = "contain";
|
|
1854
|
+
img.style.pointerEvents = "none";
|
|
1855
|
+
ctx.container.appendChild(img);
|
|
1856
|
+
for (const track of Object.values(config.tracks)) for (const url of track.frames) {
|
|
1857
|
+
const pre = new Image();
|
|
1858
|
+
pre.src = url;
|
|
1859
|
+
}
|
|
1860
|
+
let disposed = false;
|
|
1861
|
+
let timer;
|
|
1862
|
+
let watchdog;
|
|
1863
|
+
let track = config.phases.idle;
|
|
1864
|
+
let frameIndex = 0;
|
|
1865
|
+
let lastAdvance = Date.now();
|
|
1866
|
+
let override;
|
|
1867
|
+
const trackForPhase = (phase) => {
|
|
1868
|
+
const mapped = config.phases[phase];
|
|
1869
|
+
return mapped !== void 0 && config.tracks[mapped] !== void 0 ? mapped : config.phases.idle;
|
|
1870
|
+
};
|
|
1871
|
+
const show = (trackId, index) => {
|
|
1872
|
+
const url = config.tracks[trackId]?.frames[index];
|
|
1873
|
+
if (url !== void 0 && img.getAttribute("src") !== url) img.src = url;
|
|
1874
|
+
};
|
|
1875
|
+
const schedule = (ms) => {
|
|
1876
|
+
if (disposed) return;
|
|
1877
|
+
timer = setTimeout(tick, ms);
|
|
1878
|
+
};
|
|
1879
|
+
function tick() {
|
|
1880
|
+
if (disposed) return;
|
|
1881
|
+
const def = config.tracks[track];
|
|
1882
|
+
if (def === void 0 || def.frames.length === 0) return;
|
|
1883
|
+
lastAdvance = Date.now();
|
|
1884
|
+
const next = frameIndex + 1;
|
|
1885
|
+
if (next < def.frames.length) {
|
|
1886
|
+
frameIndex = next;
|
|
1887
|
+
show(track, frameIndex);
|
|
1888
|
+
schedule(def.durations[frameIndex] ?? 200);
|
|
1889
|
+
return;
|
|
1890
|
+
}
|
|
1891
|
+
if (def.loop) {
|
|
1892
|
+
frameIndex = 0;
|
|
1893
|
+
show(track, frameIndex);
|
|
1894
|
+
schedule(def.durations[frameIndex] ?? 200);
|
|
1895
|
+
return;
|
|
1896
|
+
}
|
|
1897
|
+
const target = def.fallback !== void 0 && config.tracks[def.fallback] !== void 0 ? def.fallback : config.phases.idle;
|
|
1898
|
+
if (target === trackForPhase(ctx.phase.get())) override = void 0;
|
|
1899
|
+
play(target);
|
|
1900
|
+
}
|
|
1901
|
+
function play(trackId) {
|
|
1902
|
+
if (disposed) return;
|
|
1903
|
+
if (config.tracks[trackId] === void 0) trackId = config.phases.idle;
|
|
1904
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
1905
|
+
track = trackId;
|
|
1906
|
+
frameIndex = 0;
|
|
1907
|
+
lastAdvance = Date.now();
|
|
1908
|
+
show(track, frameIndex);
|
|
1909
|
+
if (reducedMotion) return;
|
|
1910
|
+
const def = config.tracks[track];
|
|
1911
|
+
schedule(def.durations[0] ?? 200);
|
|
1912
|
+
}
|
|
1913
|
+
const unsubscribe = ctx.phase.subscribe((phase) => {
|
|
1914
|
+
if (override !== void 0) return;
|
|
1915
|
+
const target = trackForPhase(phase);
|
|
1916
|
+
if (target !== track) play(target);
|
|
1917
|
+
});
|
|
1918
|
+
if (!reducedMotion) watchdog = setInterval(() => {
|
|
1919
|
+
if (disposed) return;
|
|
1920
|
+
const def = config.tracks[track];
|
|
1921
|
+
if (def === void 0 || !def.loop) return;
|
|
1922
|
+
const expected = (def.durations[frameIndex] ?? 200) + WATCHDOG_MS;
|
|
1923
|
+
if (Date.now() - lastAdvance > expected) tick();
|
|
1924
|
+
}, WATCHDOG_MS);
|
|
1925
|
+
play(track);
|
|
1926
|
+
let disposedOnce = false;
|
|
1927
|
+
const dispose = () => {
|
|
1928
|
+
if (disposedOnce) return;
|
|
1929
|
+
disposedOnce = true;
|
|
1930
|
+
disposed = true;
|
|
1931
|
+
unsubscribe();
|
|
1932
|
+
if (timer !== void 0) clearTimeout(timer);
|
|
1933
|
+
if (watchdog !== void 0) clearInterval(watchdog);
|
|
1934
|
+
img.remove();
|
|
1935
|
+
};
|
|
1936
|
+
ctx.onCleanup(dispose);
|
|
1937
|
+
return {
|
|
1938
|
+
dispose,
|
|
1939
|
+
setState(next) {
|
|
1940
|
+
if (disposed) return;
|
|
1941
|
+
if (next === void 0) {
|
|
1942
|
+
override = void 0;
|
|
1943
|
+
const target = trackForPhase(ctx.phase.get());
|
|
1944
|
+
if (target !== track) play(target);
|
|
1945
|
+
return;
|
|
1946
|
+
}
|
|
1947
|
+
if (config.tracks[next] === void 0) return;
|
|
1948
|
+
override = next;
|
|
1949
|
+
if (next !== track) play(next);
|
|
1950
|
+
},
|
|
1951
|
+
currentTrack() {
|
|
1952
|
+
return track;
|
|
1953
|
+
}
|
|
1954
|
+
};
|
|
1955
|
+
}
|
|
1956
|
+
};
|
|
1957
|
+
//#endregion
|
|
1271
1958
|
//#region src/client/ui-teardown.ts
|
|
1272
1959
|
/**
|
|
1273
1960
|
* Cross-bundle-instance teardown slot for the page-global pet root
|
|
@@ -1311,7 +1998,7 @@ window.__ModuleLoader__.load({
|
|
|
1311
1998
|
}
|
|
1312
1999
|
//#endregion
|
|
1313
2000
|
//#region \0dsh-css:packages/dsh-pet/src/client/settings-card.module.css.mjs
|
|
1314
|
-
const css$1 = ".kKk9aW_card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}.kKk9aW_card:hover{border-color:var(--dsw-alias-label-dimmed)}.kKk9aW_cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.kKk9aW_header{appearance:none;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.kKk9aW_header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.kKk9aW_headerStatic{border-radius:12px;align-items:center;gap:12px;width:100%;padding:14px 16px;display:flex}.kKk9aW_headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.kKk9aW_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.kKk9aW_description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.kKk9aW_pending{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.kKk9aW_chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.kKk9aW_chevronOpen{transform:rotate(180deg)}.kKk9aW_body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}.kKk9aW_readOnly{color:var(--dsw-alias-label-tertiary);margin:12px 0 0;font-size:12px;line-height:1.5}.kKk9aW_notExposed{color:var(--dsw-alias-state-warn-primary);margin:12px 0 0;font-size:12px;line-height:1.5}.kKk9aW_footer{border-top:1px solid var(--dsw-alias-border-l2);justify-content:flex-end;align-items:center;gap:8px;padding:12px 0 4px;display:flex}.kKk9aW_failed{min-width:0;color:var(--dsw-alias-label-error);text-overflow:ellipsis;white-space:nowrap;flex:1;margin:0;font-size:12px;line-height:1.5;overflow:hidden}.kKk9aW_discard,.kKk9aW_save{appearance:none;font:inherit;cursor:pointer;border:1px solid #0000;border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5}.kKk9aW_discard{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:0 0}.kKk9aW_discard:hover:not(:disabled){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}.kKk9aW_save{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}.kKk9aW_discard:disabled,.kKk9aW_save:disabled{opacity:.4;cursor:default}.kKk9aW_discard:focus-visible,.kKk9aW_save:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.kKk9aW_field{flex-direction:column;gap:6px;padding:12px 0;display:flex}.kKk9aW_field+.kKk9aW_field{border-top:1px solid var(--dsw-alias-border-l2)}.kKk9aW_head{align-items:center;gap:8px;display:flex}.kKk9aW_label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}.kKk9aW_badges{align-items:center;gap:8px;display:inline-flex}.kKk9aW_badge{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.kKk9aW_reset{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;padding:0;font-size:12px;line-height:1.5}.kKk9aW_reset:hover:not(:disabled){color:var(--dsw-alias-label-primary)}.kKk9aW_reset:disabled{cursor:default}.kKk9aW_reset:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px;outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}.kKk9aW_input,.kKk9aW_select{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.kKk9aW_input:focus-visible,.kKk9aW_select:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}.kKk9aW_input:disabled,.kKk9aW_select:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.kKk9aW_inputInvalid{border:1px solid var(--dsw-alias-label-error);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.kKk9aW_inputInvalid:focus-visible{outline:2px solid var(--dsw-alias-label-error);outline-offset:1px;border-color:var(--dsw-alias-label-error)}.kKk9aW_selectWrap{position:relative}.kKk9aW_selectButton{appearance:none;text-align:left;cursor:pointer;justify-content:space-between;align-items:center;gap:8px;width:100%;display:flex}.kKk9aW_selectLabel{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.kKk9aW_selectChevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.kKk9aW_selectChevronOpen{transform:rotate(180deg)}.kKk9aW_selectPopup{z-index:40;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);max-height:240px;box-shadow:0 8px 24px var(--dsw-alias-bg-mask-2);opacity:0;border-radius:8px;flex-direction:column;padding:4px;transition:opacity .1s,transform .1s;display:flex;position:absolute;top:calc(100% + 4px);left:0;right:0;overflow-y:auto;transform:translateY(-4px)}.kKk9aW_selectPopupOpen{opacity:1;transform:none}.kKk9aW_selectPopupClose{opacity:0;pointer-events:none;transform:translateY(-4px)}.kKk9aW_selectOption{color:var(--dsw-alias-label-primary);cursor:pointer;white-space:nowrap;text-overflow:ellipsis;border-radius:6px;flex-shrink:0;padding:6px 10px;font-size:13px;line-height:1.5;overflow:hidden}.kKk9aW_selectOption:hover,.kKk9aW_selectOptionActive{background:var(--dsw-alias-interactive-bg-hover)}.kKk9aW_selectOptionSelected{color:var(--dsw-alias-brand-primary);background:color-mix(in srgb, var(--dsw-alias-brand-primary-new-colorprimary-new-color) 10%, transparent);font-weight:500}.kKk9aW_invalid{color:var(--dsw-alias-label-error);margin:0;font-size:12px;line-height:1.5}.kKk9aW_hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}@media (prefers-reduced-motion:reduce){.kKk9aW_card,.kKk9aW_header,.kKk9aW_chevron,.kKk9aW_chevronOpen,.kKk9aW_discard,.kKk9aW_save,.kKk9aW_selectChevron,.kKk9aW_selectChevronOpen,.kKk9aW_selectPopup{transition:none}}";
|
|
2001
|
+
const css$1 = ".kKk9aW_card{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);border-radius:12px;list-style:none;transition:border-color .16s,background .16s}.kKk9aW_card:hover{border-color:var(--dsw-alias-label-dimmed)}.kKk9aW_cardOpen{background:var(--dsw-alias-bg-layer-2);border-color:var(--dsw-alias-label-dimmed)}.kKk9aW_header{appearance:none;box-sizing:border-box;width:100%;font:inherit;color:inherit;text-align:left;cursor:pointer;background:0 0;border:0;border-radius:12px;align-items:center;gap:12px;padding:14px 16px;display:flex}.kKk9aW_header:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:-2px}.kKk9aW_headerStatic{box-sizing:border-box;border-radius:12px;align-items:center;gap:12px;width:100%;padding:14px 16px;display:flex}.kKk9aW_headText{flex-direction:column;flex:1;gap:4px;min-width:0;display:flex}.kKk9aW_name{color:var(--dsw-alias-label-primary);font-size:15px;font-weight:600;line-height:1.4}.kKk9aW_description{color:var(--dsw-alias-label-tertiary);font-size:13px;line-height:1.5}.kKk9aW_pending{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;flex:none;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.kKk9aW_chevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.kKk9aW_chevronOpen{transform:rotate(180deg)}.kKk9aW_body{border-top:1px solid var(--dsw-alias-border-l2);margin:0 16px;padding-bottom:8px}.kKk9aW_readOnly{color:var(--dsw-alias-label-tertiary);margin:12px 0 0;font-size:12px;line-height:1.5}.kKk9aW_notExposed{color:var(--dsw-alias-state-warn-primary);margin:12px 0 0;font-size:12px;line-height:1.5}.kKk9aW_footer{border-top:1px solid var(--dsw-alias-border-l2);justify-content:flex-end;align-items:center;gap:8px;padding:12px 0 4px;display:flex}.kKk9aW_failed{min-width:0;color:var(--dsw-alias-label-error);text-overflow:ellipsis;white-space:nowrap;flex:1;margin:0;font-size:12px;line-height:1.5;overflow:hidden}.kKk9aW_discard,.kKk9aW_save{appearance:none;font:inherit;cursor:pointer;border:1px solid #0000;border-radius:8px;padding:5px 14px;font-size:13px;line-height:1.5}.kKk9aW_discard{border-color:var(--dsw-alias-border-l2);color:var(--dsw-alias-label-secondary);background:0 0}.kKk9aW_discard:hover:not(:disabled){color:var(--dsw-alias-label-primary);border-color:var(--dsw-alias-label-dimmed)}.kKk9aW_save{background:var(--dsw-alias-label-primary);color:var(--dsw-alias-bg-layer-3)}.kKk9aW_discard:disabled,.kKk9aW_save:disabled{opacity:.4;cursor:default}.kKk9aW_discard:focus-visible,.kKk9aW_save:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:1px}.kKk9aW_field{flex-direction:column;gap:6px;padding:12px 0;display:flex}.kKk9aW_field+.kKk9aW_field{border-top:1px solid var(--dsw-alias-border-l2)}.kKk9aW_head{align-items:center;gap:8px;display:flex}.kKk9aW_label{min-width:0;color:var(--dsw-alias-label-primary);flex:1;font-size:13px;font-weight:500;line-height:1.5}.kKk9aW_badges{align-items:center;gap:8px;display:inline-flex}.kKk9aW_badge{white-space:nowrap;background:var(--dsw-alias-bg-module-platform);color:var(--dsw-alias-label-secondary);border-radius:999px;padding:1px 8px;font-size:11px;font-weight:500;line-height:17px}.kKk9aW_reset{font:inherit;color:var(--dsw-alias-label-secondary);cursor:pointer;background:0 0;border:none;padding:0;font-size:12px;line-height:1.5}.kKk9aW_reset:hover:not(:disabled){color:var(--dsw-alias-label-primary)}.kKk9aW_reset:disabled{cursor:default}.kKk9aW_reset:focus-visible{outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px;outline:2px solid var(--dsw-alias-brand-primary);outline-offset:2px}.kKk9aW_input,.kKk9aW_select{border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.kKk9aW_input:focus-visible,.kKk9aW_select:focus-visible{border-color:var(--dsw-alias-brand-primary);outline:none}.kKk9aW_input:disabled,.kKk9aW_select:disabled{color:var(--dsw-alias-label-tertiary);cursor:default}.kKk9aW_inputInvalid{border:1px solid var(--dsw-alias-label-error);background:var(--dsw-alias-bg-layer-3);height:34px;font:inherit;color:var(--dsw-alias-label-primary);border-radius:8px;padding:0 12px;font-size:13px;line-height:1.5}.kKk9aW_inputInvalid:focus-visible{outline:2px solid var(--dsw-alias-label-error);outline-offset:1px;border-color:var(--dsw-alias-label-error)}.kKk9aW_selectWrap{position:relative}.kKk9aW_selectButton{appearance:none;text-align:left;cursor:pointer;justify-content:space-between;align-items:center;gap:8px;width:100%;display:flex}.kKk9aW_selectLabel{text-overflow:ellipsis;white-space:nowrap;min-width:0;overflow:hidden}.kKk9aW_selectChevron{color:var(--dsw-alias-label-tertiary);flex:none;transition:transform .16s}.kKk9aW_selectChevronOpen{transform:rotate(180deg)}.kKk9aW_selectPopup{z-index:40;border:1px solid var(--dsw-alias-border-l2);background:var(--dsw-alias-bg-layer-3);max-height:240px;box-shadow:0 8px 24px var(--dsw-alias-bg-mask-2);opacity:0;border-radius:8px;flex-direction:column;padding:4px;transition:opacity .1s,transform .1s;display:flex;position:absolute;top:calc(100% + 4px);left:0;right:0;overflow-y:auto;transform:translateY(-4px)}.kKk9aW_selectPopupOpen{opacity:1;transform:none}.kKk9aW_selectPopupClose{opacity:0;pointer-events:none;transform:translateY(-4px)}.kKk9aW_selectOption{color:var(--dsw-alias-label-primary);cursor:pointer;white-space:nowrap;text-overflow:ellipsis;border-radius:6px;flex-shrink:0;padding:6px 10px;font-size:13px;line-height:1.5;overflow:hidden}.kKk9aW_selectOption:hover,.kKk9aW_selectOptionActive{background:var(--dsw-alias-interactive-bg-hover)}.kKk9aW_selectOptionSelected{color:var(--dsw-alias-brand-primary);background:color-mix(in srgb, var(--dsw-alias-brand-primary-new-colorprimary-new-color) 10%, transparent);font-weight:500}.kKk9aW_invalid{color:var(--dsw-alias-label-error);margin:0;font-size:12px;line-height:1.5}.kKk9aW_hint{color:var(--dsw-alias-label-tertiary);margin:0;font-size:12px;line-height:1.5}@media (prefers-reduced-motion:reduce){.kKk9aW_card,.kKk9aW_header,.kKk9aW_chevron,.kKk9aW_chevronOpen,.kKk9aW_discard,.kKk9aW_save,.kKk9aW_selectChevron,.kKk9aW_selectChevronOpen,.kKk9aW_selectPopup{transition:none}}";
|
|
1315
2002
|
const tagId$1 = "@linxin666/dsh-pet/settings-card.module.css";
|
|
1316
2003
|
if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$1) + "]") === null) {
|
|
1317
2004
|
const tag = document.createElement("style");
|
|
@@ -1396,7 +2083,7 @@ window.__ModuleLoader__.load({
|
|
|
1396
2083
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1397
2084
|
className: settings_card_module_css_default.description,
|
|
1398
2085
|
title: description,
|
|
1399
|
-
children: description
|
|
2086
|
+
children: props.descriptionNode ?? description
|
|
1400
2087
|
})]
|
|
1401
2088
|
}), state.dirty ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1402
2089
|
className: settings_card_module_css_default.pending,
|
|
@@ -1421,7 +2108,7 @@ window.__ModuleLoader__.load({
|
|
|
1421
2108
|
}), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
1422
2109
|
className: settings_card_module_css_default.description,
|
|
1423
2110
|
title: description,
|
|
1424
|
-
children: description
|
|
2111
|
+
children: props.descriptionNode ?? description
|
|
1425
2112
|
})]
|
|
1426
2113
|
}),
|
|
1427
2114
|
state.dirty ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
|
|
@@ -2403,6 +3090,23 @@ window.__ModuleLoader__.load({
|
|
|
2403
3090
|
"pet.live2d.vendor-missing": "Live2D 组件缺失,请升级宠物插件。",
|
|
2404
3091
|
"pet.live2d.load-failed": "Live2D 模型加载失败,请检查该宠物目录的完整性。",
|
|
2405
3092
|
"pet.openSessionHint": "点击跳转到对应会话",
|
|
3093
|
+
"pet.gameplay.menu": "玩法",
|
|
3094
|
+
"pet.gameplay.work": "打工",
|
|
3095
|
+
"pet.gameplay.stopWork": "收工",
|
|
3096
|
+
"pet.gameplay.sleep": "睡觉",
|
|
3097
|
+
"pet.gameplay.wake": "起床",
|
|
3098
|
+
"pet.gameplay.shop": "商店",
|
|
3099
|
+
"pet.gameplay.back": "返回",
|
|
3100
|
+
"pet.gameplay.buy": "购买",
|
|
3101
|
+
"pet.gameplay.insufficient": "{currency}不足",
|
|
3102
|
+
"pet.gameplay.prize": "中奖 +{amount} {currency}",
|
|
3103
|
+
"pet.gameplay.working": "打工中",
|
|
3104
|
+
"pet.gameplay.sleeping": "睡觉中",
|
|
3105
|
+
"pet.gameplay.stat.hunger": "饱食",
|
|
3106
|
+
"pet.gameplay.stat.mood": "心情",
|
|
3107
|
+
"pet.gameplay.stat.energy": "精力",
|
|
3108
|
+
"pet.gameplay.stat.affection": "好感",
|
|
3109
|
+
"pet.gameplay.currency.treats": "小鱼干",
|
|
2406
3110
|
"pet.moreSessions": "展开其余 {n} 个会话的气泡",
|
|
2407
3111
|
"pet.collapseSessions": "收起会话气泡",
|
|
2408
3112
|
"settings.title": "宠物",
|
|
@@ -2456,6 +3160,23 @@ window.__ModuleLoader__.load({
|
|
|
2456
3160
|
"pet.live2d.vendor-missing": "The Live2D component is missing; please update the pet plugin.",
|
|
2457
3161
|
"pet.live2d.load-failed": "The Live2D model failed to load; check the pet directory is complete.",
|
|
2458
3162
|
"pet.openSessionHint": "Click to jump to this session",
|
|
3163
|
+
"pet.gameplay.menu": "Play",
|
|
3164
|
+
"pet.gameplay.work": "Work",
|
|
3165
|
+
"pet.gameplay.stopWork": "Stop work",
|
|
3166
|
+
"pet.gameplay.sleep": "Sleep",
|
|
3167
|
+
"pet.gameplay.wake": "Wake up",
|
|
3168
|
+
"pet.gameplay.shop": "Shop",
|
|
3169
|
+
"pet.gameplay.back": "Back",
|
|
3170
|
+
"pet.gameplay.buy": "Buy",
|
|
3171
|
+
"pet.gameplay.insufficient": "Not enough {currency}",
|
|
3172
|
+
"pet.gameplay.prize": "Prize +{amount} {currency}",
|
|
3173
|
+
"pet.gameplay.working": "Working",
|
|
3174
|
+
"pet.gameplay.sleeping": "Sleeping",
|
|
3175
|
+
"pet.gameplay.stat.hunger": "Hunger",
|
|
3176
|
+
"pet.gameplay.stat.mood": "Mood",
|
|
3177
|
+
"pet.gameplay.stat.energy": "Energy",
|
|
3178
|
+
"pet.gameplay.stat.affection": "Affection",
|
|
3179
|
+
"pet.gameplay.currency.treats": "Treats",
|
|
2459
3180
|
"pet.moreSessions": "Expand {n} more session bubbles",
|
|
2460
3181
|
"pet.collapseSessions": "Collapse session bubbles",
|
|
2461
3182
|
"settings.title": "Pet",
|
|
@@ -2521,7 +3242,7 @@ window.__ModuleLoader__.load({
|
|
|
2521
3242
|
/** The building package's version, when the bundle carries it. */
|
|
2522
3243
|
function bakedVersion() {
|
|
2523
3244
|
try {
|
|
2524
|
-
return "0.3.
|
|
3245
|
+
return "0.3.5";
|
|
2525
3246
|
} catch {
|
|
2526
3247
|
return;
|
|
2527
3248
|
}
|
|
@@ -2597,13 +3318,17 @@ window.__ModuleLoader__.load({
|
|
|
2597
3318
|
}
|
|
2598
3319
|
/** The live host API instance (always defined; failures surface per call). */
|
|
2599
3320
|
const petApi = {
|
|
2600
|
-
state: () => petFetch("/api/pet/state"),
|
|
3321
|
+
state: (currentSessionId) => petFetch("/api/pet/state" + (currentSessionId === void 0 ? "" : "?current=" + encodeURIComponent(currentSessionId))),
|
|
2601
3322
|
pets: () => petFetch("/api/pet/pets"),
|
|
2602
3323
|
interact: (kind) => petFetch("/api/pet/interact", { kind }),
|
|
2603
3324
|
setVisible: (visible) => petFetch("/api/pet/set-visible", { visible }),
|
|
2604
3325
|
setConfig: (patch) => petFetch("/api/pet/set-config", patch),
|
|
2605
3326
|
setName: (name) => petFetch("/api/pet/set-name", { name }),
|
|
2606
|
-
setPet: (petId) => petFetch("/api/pet/set-pet", { petId })
|
|
3327
|
+
setPet: (petId) => petFetch("/api/pet/set-pet", { petId }),
|
|
3328
|
+
gameplayTouch: (zone) => petFetch("/api/pet/gameplay/touch", zone === void 0 ? {} : { zone }),
|
|
3329
|
+
gameplaySetMode: (mode) => petFetch("/api/pet/gameplay/mode", { mode }),
|
|
3330
|
+
gameplayWorkTick: () => petFetch("/api/pet/gameplay/work-tick", {}),
|
|
3331
|
+
gameplayBuy: (item) => petFetch("/api/pet/gameplay/buy", { item })
|
|
2607
3332
|
};
|
|
2608
3333
|
/** Poll interval for the host snapshot. */
|
|
2609
3334
|
const POLL_MS = 2e3;
|
|
@@ -2637,6 +3362,7 @@ window.__ModuleLoader__.load({
|
|
|
2637
3362
|
}
|
|
2638
3363
|
}, "pet: dictionaries");
|
|
2639
3364
|
defaultPetRendererRegistry.register(live2dRenderer);
|
|
3365
|
+
defaultPetRendererRegistry.register(frames2dRenderer);
|
|
2640
3366
|
const settingsScope = (ctx.get("webUiSettings") ?? ctx.settingsScope).bind({ namespace: PET_SETTINGS_NS });
|
|
2641
3367
|
const enabled = () => {
|
|
2642
3368
|
const snapshot = settingsScope.getSnapshot();
|
|
@@ -2679,6 +3405,11 @@ window.__ModuleLoader__.load({
|
|
|
2679
3405
|
const setPets = petStore.actions.setPets;
|
|
2680
3406
|
const setState = petStore.actions.setState;
|
|
2681
3407
|
const setFeedback = petStore.actions.setFeedback;
|
|
3408
|
+
const sessions = ctx.sessions;
|
|
3409
|
+
const currentSessionId = () => {
|
|
3410
|
+
const current = sessions.list.getSnapshot().current;
|
|
3411
|
+
return current === void 0 ? void 0 : String(current);
|
|
3412
|
+
};
|
|
2682
3413
|
let petsLoaded = false;
|
|
2683
3414
|
let stateSeq = 0;
|
|
2684
3415
|
const pollNow = () => {
|
|
@@ -2688,7 +3419,7 @@ window.__ModuleLoader__.load({
|
|
|
2688
3419
|
}, () => {});
|
|
2689
3420
|
const seq = stateSeq + 1;
|
|
2690
3421
|
stateSeq = seq;
|
|
2691
|
-
petApi.state().then((snapshot) => {
|
|
3422
|
+
petApi.state(currentSessionId()).then((snapshot) => {
|
|
2692
3423
|
if (seq !== stateSeq) return;
|
|
2693
3424
|
setSnapshot(snapshot);
|
|
2694
3425
|
}, () => {
|
|
@@ -2720,7 +3451,11 @@ window.__ModuleLoader__.load({
|
|
|
2720
3451
|
document.removeEventListener("visibilitychange", onVisibility);
|
|
2721
3452
|
};
|
|
2722
3453
|
}, "pet: poll");
|
|
2723
|
-
|
|
3454
|
+
ctx.effect(() => {
|
|
3455
|
+
return sessions.list.subscribe(() => {
|
|
3456
|
+
if (document.visibilityState === "visible") pollNow();
|
|
3457
|
+
});
|
|
3458
|
+
}, "pet: current-session watch");
|
|
2724
3459
|
const openSession = (sessionId) => {
|
|
2725
3460
|
if (sessions.list.getSnapshot().byId[sessionId] === void 0) return;
|
|
2726
3461
|
sessions.open(sessionId);
|
|
@@ -2772,6 +3507,12 @@ window.__ModuleLoader__.load({
|
|
|
2772
3507
|
},
|
|
2773
3508
|
feedbackDone: () => {
|
|
2774
3509
|
setFeedback(null);
|
|
3510
|
+
},
|
|
3511
|
+
gameplay: {
|
|
3512
|
+
touch: (zone) => petApi.gameplayTouch(zone),
|
|
3513
|
+
setMode: (mode) => petApi.gameplaySetMode(mode),
|
|
3514
|
+
workTick: () => petApi.gameplayWorkTick(),
|
|
3515
|
+
buy: (item) => petApi.gameplayBuy(item)
|
|
2775
3516
|
}
|
|
2776
3517
|
});
|
|
2777
3518
|
takeoverPetUiTeardown();
|