@linxin666/dsh-pet 0.3.17 → 0.3.18

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 (55) hide show
  1. package/README.i18n.yaml +2 -2
  2. package/README.md +16 -1
  3. package/README.zh.md +16 -1
  4. package/assets/blue-throated-bee-eater/pet.json +204 -0
  5. package/assets/blue-throated-bee-eater/previews/failed.gif +0 -0
  6. package/assets/blue-throated-bee-eater/previews/idle.gif +0 -0
  7. package/assets/blue-throated-bee-eater/previews/jumping.gif +0 -0
  8. package/assets/blue-throated-bee-eater/previews/review.gif +0 -0
  9. package/assets/blue-throated-bee-eater/previews/running-left.gif +0 -0
  10. package/assets/blue-throated-bee-eater/previews/running-right.gif +0 -0
  11. package/assets/blue-throated-bee-eater/previews/running.gif +0 -0
  12. package/assets/blue-throated-bee-eater/previews/waiting.gif +0 -0
  13. package/assets/blue-throated-bee-eater/previews/waving.gif +0 -0
  14. package/assets/blue-throated-bee-eater/spritesheet.webp +0 -0
  15. package/assets/blue-throated-bee-eater/voice.json +13 -0
  16. package/contracts/pet-manifest-v2.schema.json +84 -0
  17. package/lib/client.js +176 -27
  18. package/lib/client.js.map +1 -1
  19. package/lib/index.js +170 -6
  20. package/lib/types/client/gameplay-hud.d.ts +2 -0
  21. package/lib/types/client/gameplay-hud.d.ts.map +1 -1
  22. package/lib/types/client/gameplay-hud.js +65 -12
  23. package/lib/types/client/index.d.ts +0 -6
  24. package/lib/types/client/index.d.ts.map +1 -1
  25. package/lib/types/client/index.js +25 -2
  26. package/lib/types/client/locales.d.ts +7 -3
  27. package/lib/types/client/locales.d.ts.map +1 -1
  28. package/lib/types/client/locales.js +7 -3
  29. package/lib/types/client/renderers/Frames2dVisualMount.d.ts.map +1 -1
  30. package/lib/types/client/renderers/Frames2dVisualMount.js +5 -1
  31. package/lib/types/client/renderers/frames2d.d.ts +25 -0
  32. package/lib/types/client/renderers/frames2d.d.ts.map +1 -1
  33. package/lib/types/client/renderers/frames2d.js +84 -7
  34. package/lib/types/manifest-v2.d.ts +45 -0
  35. package/lib/types/manifest-v2.d.ts.map +1 -1
  36. package/lib/types/manifest-v2.js +139 -1
  37. package/lib/types/persist.d.ts +1 -1
  38. package/lib/types/persist.d.ts.map +1 -1
  39. package/lib/types/persist.js +1 -1
  40. package/lib/types/registry.d.ts +22 -0
  41. package/lib/types/registry.d.ts.map +1 -1
  42. package/lib/types/registry.js +45 -1
  43. package/package.json +2 -1
  44. package/src/client/gameplay-hud.test.tsx +64 -0
  45. package/src/client/gameplay-hud.tsx +109 -21
  46. package/src/client/index.ts +26 -2
  47. package/src/client/locales.ts +7 -3
  48. package/src/client/pet.module.css +31 -1
  49. package/src/client/renderers/Frames2dVisualMount.tsx +5 -1
  50. package/src/client/renderers/frames2d.test.ts +43 -0
  51. package/src/client/renderers/frames2d.ts +100 -7
  52. package/src/manifest-v2.ts +167 -1
  53. package/src/persist.ts +1 -1
  54. package/src/registry.test.ts +19 -5
  55. package/src/registry.ts +66 -1
package/lib/client.js CHANGED
@@ -201,7 +201,7 @@ window.__ModuleLoader__.load({
201
201
  }
202
202
  //#endregion
203
203
  //#region \0dsh-css:packages/dsh-pet/src/client/pet.module.css.mjs
204
- 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}}.kz2Bea_bubbleUsage{white-space:nowrap;backdrop-filter:blur(8px);letter-spacing:.02em;background:linear-gradient(160deg,#131c36eb,#070b1af2);border:1px solid #7e98ff73;flex-direction:column;align-items:stretch;gap:3px;min-width:128px;max-width:min(280px,100vw - 24px);padding:6px 12px;animation:.28s cubic-bezier(.22,1,.36,1) kz2Bea_pet-usage-in;display:flex;box-shadow:0 4px 12px #02061759,inset 0 1px #e2e8ff1a,0 0 12px #4d6bfe2e}.kz2Bea_bubbleUsageHead{justify-content:space-between;align-items:baseline;gap:10px;display:flex}.kz2Bea_bubbleUsageTitle{text-overflow:ellipsis;opacity:.85;overflow:hidden}.kz2Bea_bubbleUsageValue{font-variant-numeric:tabular-nums;flex-shrink:0;font-weight:600}.kz2Bea_bubbleUsageNote{text-overflow:ellipsis;opacity:.55;font-size:10px;overflow:hidden}.kz2Bea_bubbleUsageMeter{background:#e2e8ff24;border-radius:999px;height:4px;display:block;overflow:hidden}.kz2Bea_bubbleUsageMeterFill{background:#6ee7b7;border-radius:999px;height:100%;transition:width .3s;display:block}.kz2Bea_bubbleUsageOk{border-color:#7e98ff73}.kz2Bea_bubbleUsageWarn{border-color:#fbbf248c}.kz2Bea_bubbleUsageWarn .kz2Bea_bubbleUsageMeterFill{background:#fbbf24}.kz2Bea_bubbleUsageLow{border-color:#f8717199}.kz2Bea_bubbleUsageLow .kz2Bea_bubbleUsageMeterFill{background:#f87171}@keyframes kz2Bea_pet-usage-in{0%{opacity:0;transform:translateY(6px)scale(.96)}to{opacity:1;transform:translateY(0)scale(1)}}@media (prefers-reduced-motion:reduce){.kz2Bea_bubbleUsage{animation:none}}";
204
+ 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_gameplaySkinItems{flex-direction:column;gap:6px;margin-bottom:8px;display:flex}.kz2Bea_gameplaySkinItem{width:100%;color:inherit;cursor:pointer;text-align:left;background:#7e98ff14;border:1px solid #7e98ff40;border-radius:8px;padding:8px 10px;font-size:12px;display:block}.kz2Bea_gameplaySkinItem:hover{background:#7e98ff29;border-color:#7e98ffb3}.kz2Bea_gameplaySkinItemActive{background:#7e98ff47;border-color:#7e98ffe6}.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}}.kz2Bea_bubbleUsage{white-space:nowrap;backdrop-filter:blur(8px);letter-spacing:.02em;background:linear-gradient(160deg,#131c36eb,#070b1af2);border:1px solid #7e98ff73;flex-direction:column;align-items:stretch;gap:3px;min-width:128px;max-width:min(280px,100vw - 24px);padding:6px 12px;animation:.28s cubic-bezier(.22,1,.36,1) kz2Bea_pet-usage-in;display:flex;box-shadow:0 4px 12px #02061759,inset 0 1px #e2e8ff1a,0 0 12px #4d6bfe2e}.kz2Bea_bubbleUsageHead{justify-content:space-between;align-items:baseline;gap:10px;display:flex}.kz2Bea_bubbleUsageTitle{text-overflow:ellipsis;opacity:.85;overflow:hidden}.kz2Bea_bubbleUsageValue{font-variant-numeric:tabular-nums;flex-shrink:0;font-weight:600}.kz2Bea_bubbleUsageNote{text-overflow:ellipsis;opacity:.55;font-size:10px;overflow:hidden}.kz2Bea_bubbleUsageMeter{background:#e2e8ff24;border-radius:999px;height:4px;display:block;overflow:hidden}.kz2Bea_bubbleUsageMeterFill{background:#6ee7b7;border-radius:999px;height:100%;transition:width .3s;display:block}.kz2Bea_bubbleUsageOk{border-color:#7e98ff73}.kz2Bea_bubbleUsageWarn{border-color:#fbbf248c}.kz2Bea_bubbleUsageWarn .kz2Bea_bubbleUsageMeterFill{background:#fbbf24}.kz2Bea_bubbleUsageLow{border-color:#f8717199}.kz2Bea_bubbleUsageLow .kz2Bea_bubbleUsageMeterFill{background:#f87171}@keyframes kz2Bea_pet-usage-in{0%{opacity:0;transform:translateY(6px)scale(.96)}to{opacity:1;transform:translateY(0)scale(1)}}@media (prefers-reduced-motion:reduce){.kz2Bea_bubbleUsage{animation:none}}";
205
205
  const tagId$2 = "@linxin666/dsh-pet/packages/dsh-pet/src/client/pet.module.css";
206
206
  if (typeof document !== "undefined" && document.querySelector("style[data-plugin-css=" + JSON.stringify(tagId$2) + "]") === null) {
207
207
  const tag = document.createElement("style");
@@ -249,6 +249,9 @@ window.__ModuleLoader__.load({
249
249
  "gameplayShopItemLabel": "kz2Bea_gameplayShopItemLabel",
250
250
  "gameplayShopItemPrice": "kz2Bea_gameplayShopItemPrice",
251
251
  "gameplayShopItems": "kz2Bea_gameplayShopItems",
252
+ "gameplaySkinItem": "kz2Bea_gameplaySkinItem",
253
+ "gameplaySkinItemActive": "kz2Bea_gameplaySkinItemActive",
254
+ "gameplaySkinItems": "kz2Bea_gameplaySkinItems",
252
255
  "nameCell": "kz2Bea_nameCell",
253
256
  "nameInput": "kz2Bea_nameInput",
254
257
  "panel": "kz2Bea_panel",
@@ -1089,8 +1092,12 @@ window.__ModuleLoader__.load({
1089
1092
  gameplayBus.setTrack = (track) => {
1090
1093
  handleRef.current?.setState(track);
1091
1094
  };
1095
+ gameplayBus.setIdleTrack = (track) => {
1096
+ handleRef.current?.setIdleTrack(track);
1097
+ };
1092
1098
  cleanups.push(() => {
1093
1099
  gameplayBus.setTrack = void 0;
1100
+ gameplayBus.setIdleTrack = void 0;
1094
1101
  });
1095
1102
  }
1096
1103
  const dragTrack = props.definition.gameplay?.dragState ?? (frames2d.tracks.drag === void 0 ? void 0 : "drag");
@@ -1203,6 +1210,9 @@ window.__ModuleLoader__.load({
1203
1210
  const phase = ui.snapshot?.phase ?? "idle";
1204
1211
  const [open, setOpen] = (0, react.useState)(false);
1205
1212
  const [page, setPage] = (0, react.useState)("root");
1213
+ const [skinId, setSkinId] = (0, react.useState)(void 0);
1214
+ const skinIdRef = (0, react.useRef)(void 0);
1215
+ skinIdRef.current = skinId;
1206
1216
  const hudRef = (0, react.useRef)(null);
1207
1217
  const cardRef = (0, react.useRef)(null);
1208
1218
  const [floats, setFloats] = (0, react.useState)([]);
@@ -1232,6 +1242,24 @@ window.__ModuleLoader__.load({
1232
1242
  };
1233
1243
  (0, react.useEffect)(() => {
1234
1244
  if (def === void 0) return void 0;
1245
+ const holdTrack = (track, holdMs) => {
1246
+ bus.setTrack?.(track);
1247
+ touchLockUntilRef.current = Date.now() + holdMs;
1248
+ window.setTimeout(() => {
1249
+ if (Date.now() >= touchLockUntilRef.current) bus.setTrack?.(void 0);
1250
+ }, holdMs);
1251
+ };
1252
+ const speak = (phrases) => {
1253
+ if (phrases !== void 0 && phrases.length > 0) {
1254
+ const phrase = phrases[Math.floor(Math.random() * phrases.length)];
1255
+ store.actions.setFeedback({
1256
+ text: phrase,
1257
+ kind: "none",
1258
+ at: Date.now()
1259
+ });
1260
+ }
1261
+ };
1262
+ const trackDuration = (track) => definition.frames2d?.tracks[track]?.durations.reduce((sum, ms) => sum + ms, 0) ?? 0;
1235
1263
  bus.tap = (fx, fy) => {
1236
1264
  if (modeRef.current === "sleep") {
1237
1265
  api.setMode(null).then(applyResult, () => void 0);
@@ -1251,19 +1279,31 @@ window.__ModuleLoader__.load({
1251
1279
  api.touch().then(applyResult, () => void 0);
1252
1280
  return;
1253
1281
  }
1282
+ const activeSkin = definition.frames2d?.skins?.find((skin) => skin.id === skinIdRef.current);
1283
+ if (activeSkin !== void 0) {
1284
+ const actions = activeSkin.clickActions ?? [];
1285
+ if (actions.length > 0) {
1286
+ let roll = Math.random();
1287
+ const fired = actions.find((action) => {
1288
+ if (roll < action.probability) return true;
1289
+ roll -= action.probability;
1290
+ return false;
1291
+ });
1292
+ if (fired !== void 0) {
1293
+ holdTrack(fired.track, trackDuration(fired.track) || 3e3);
1294
+ speak(fired.phrases);
1295
+ return;
1296
+ }
1297
+ }
1298
+ api.touch().then(applyResult, () => void 0);
1299
+ return;
1300
+ }
1254
1301
  const zone = def.touch === void 0 ? void 0 : touchZoneAt(def.touch, hy);
1255
1302
  if (zone === void 0) return;
1256
1303
  api.touch(zone.name).then((result) => {
1257
1304
  applyResult(result);
1258
1305
  if (result.hit !== true) return;
1259
- if (result.state !== void 0) {
1260
- bus.setTrack?.(result.state);
1261
- const holdMs = result.stateMs ?? 3e3;
1262
- touchLockUntilRef.current = Date.now() + holdMs;
1263
- window.setTimeout(() => {
1264
- if (Date.now() >= touchLockUntilRef.current) bus.setTrack?.(void 0);
1265
- }, holdMs);
1266
- }
1306
+ if (result.state !== void 0) holdTrack(result.state, result.stateMs ?? 3e3);
1267
1307
  if (result.phrase !== void 0) store.actions.setFeedback({
1268
1308
  text: result.phrase,
1269
1309
  kind: "none",
@@ -1392,12 +1432,14 @@ window.__ModuleLoader__.load({
1392
1432
  (0, react.useEffect)(() => {
1393
1433
  const sleep = def?.sleep;
1394
1434
  if (def === void 0 || sleep === void 0 || view?.mode !== "sleep") return void 0;
1395
- bus.setTrack?.(sleep.state);
1435
+ const hold = (definition.frames2d?.skins?.find((skin) => skin.id === skinIdRef.current)?.gameplayTracks)?.["sleep"] ?? sleep.state;
1436
+ bus.setTrack?.(hold);
1396
1437
  return () => bus.setTrack?.(void 0);
1397
1438
  }, [
1398
1439
  definition.id,
1399
1440
  def,
1400
- view?.mode
1441
+ view?.mode,
1442
+ skinId
1401
1443
  ]);
1402
1444
  if (def === void 0 || view === void 0) return null;
1403
1445
  const mode = view.mode;
@@ -1422,6 +1464,11 @@ window.__ModuleLoader__.load({
1422
1464
  const setMode = (next) => {
1423
1465
  api.setMode(next).then(applyResult, () => void 0);
1424
1466
  };
1467
+ const skins = definition.frames2d?.skins;
1468
+ const selectSkin = (skin) => {
1469
+ setSkinId(skin?.id);
1470
+ bus.setIdleTrack?.(skin?.idleTrack);
1471
+ };
1425
1472
  return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1426
1473
  ref: hudRef,
1427
1474
  className: pet_module_css_default.gameplayHud,
@@ -1462,12 +1509,6 @@ window.__ModuleLoader__.load({
1462
1509
  }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1463
1510
  className: pet_module_css_default.gameplayActions,
1464
1511
  children: [
1465
- def.work !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1466
- type: "button",
1467
- className: pet_module_css_default.action,
1468
- onClick: () => setMode(mode === "work" ? null : "work"),
1469
- children: tr(mode === "work" ? "pet.gameplay.stopWork" : "pet.gameplay.work")
1470
- }),
1471
1512
  def.sleep !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1472
1513
  type: "button",
1473
1514
  className: pet_module_css_default.action,
@@ -1479,6 +1520,18 @@ window.__ModuleLoader__.load({
1479
1520
  className: pet_module_css_default.action,
1480
1521
  onClick: () => setPage("shop"),
1481
1522
  children: tr("pet.gameplay.shop")
1523
+ }),
1524
+ skins !== void 0 && skins.length > 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1525
+ type: "button",
1526
+ className: pet_module_css_default.action,
1527
+ onClick: () => setPage("skins"),
1528
+ children: tr("pet.gameplay.skin")
1529
+ }),
1530
+ def.work !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1531
+ type: "button",
1532
+ className: pet_module_css_default.action,
1533
+ onClick: () => setMode(mode === "work" ? null : "work"),
1534
+ children: tr(mode === "work" ? "pet.gameplay.stopWork" : "pet.gameplay.work")
1482
1535
  })
1483
1536
  ]
1484
1537
  })] }),
@@ -1519,6 +1572,28 @@ window.__ModuleLoader__.load({
1519
1572
  children: tr("pet.gameplay.back")
1520
1573
  })
1521
1574
  })] }),
1575
+ page === "skins" && skins !== void 0 && /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
1576
+ className: pet_module_css_default.gameplaySkinItems,
1577
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1578
+ type: "button",
1579
+ className: skinId === void 0 ? pet_module_css_default.gameplaySkinItem + " " + pet_module_css_default.gameplaySkinItemActive : pet_module_css_default.gameplaySkinItem,
1580
+ onClick: () => selectSkin(void 0),
1581
+ children: tr("pet.gameplay.skinDefault")
1582
+ }), skins.map((skin) => /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1583
+ type: "button",
1584
+ className: skinId === skin.id ? pet_module_css_default.gameplaySkinItem + " " + pet_module_css_default.gameplaySkinItemActive : pet_module_css_default.gameplaySkinItem,
1585
+ onClick: () => selectSkin(skin),
1586
+ children: skin.label
1587
+ }, skin.id))]
1588
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
1589
+ className: pet_module_css_default.gameplayActions,
1590
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1591
+ type: "button",
1592
+ className: pet_module_css_default.action,
1593
+ onClick: () => setPage("root"),
1594
+ children: tr("pet.gameplay.back")
1595
+ })
1596
+ })] }),
1522
1597
  /* @__PURE__ */ (0, react_jsx_runtime.jsx)("button", {
1523
1598
  type: "button",
1524
1599
  className: pet_module_css_default.gameplayClose,
@@ -1955,9 +2030,50 @@ window.__ModuleLoader__.load({
1955
2030
  }
1956
2031
  const phases = config.phases;
1957
2032
  if (typeof phases.idle !== "string" || tracks[phases.idle] === void 0) throw new Error("frames2d phases.idle must name an existing track");
2033
+ let skins;
2034
+ if (Array.isArray(config.skins)) {
2035
+ const resolved = [];
2036
+ for (const skin of config.skins) {
2037
+ if (!isRecord(skin) || typeof skin.id !== "string" || typeof skin.label !== "string" || typeof skin.idleTrack !== "string" || skin.idleTrack === "") continue;
2038
+ const target = tracks[skin.idleTrack];
2039
+ if (target === void 0 || !target.loop) continue;
2040
+ let clickActions;
2041
+ if (Array.isArray(skin.clickActions)) {
2042
+ const kept = [];
2043
+ for (const action of skin.clickActions) {
2044
+ if (!isRecord(action) || typeof action.track !== "string" || action.track === "" || typeof action.probability !== "number" || !(action.probability > 0) || action.probability > 1) continue;
2045
+ if (tracks[action.track] === void 0) continue;
2046
+ kept.push({
2047
+ track: action.track,
2048
+ probability: action.probability,
2049
+ ...Array.isArray(action.phrases) ? { phrases: action.phrases } : {}
2050
+ });
2051
+ }
2052
+ if (kept.length > 0) clickActions = kept;
2053
+ }
2054
+ let gameplayTracks;
2055
+ if (isRecord(skin.gameplayTracks)) {
2056
+ const kept = {};
2057
+ for (const [state, trackName] of Object.entries(skin.gameplayTracks)) {
2058
+ if (typeof trackName !== "string" || trackName === "" || tracks[trackName] === void 0) continue;
2059
+ kept[state] = trackName;
2060
+ }
2061
+ if (Object.keys(kept).length > 0) gameplayTracks = kept;
2062
+ }
2063
+ resolved.push({
2064
+ id: skin.id,
2065
+ label: skin.label,
2066
+ idleTrack: skin.idleTrack,
2067
+ ...clickActions === void 0 ? {} : { clickActions },
2068
+ ...gameplayTracks === void 0 ? {} : { gameplayTracks }
2069
+ });
2070
+ }
2071
+ if (resolved.length > 0) skins = resolved;
2072
+ }
1958
2073
  return {
1959
2074
  tracks,
1960
- phases
2075
+ phases,
2076
+ ...skins === void 0 ? {} : { skins }
1961
2077
  };
1962
2078
  }
1963
2079
  const frames2dRenderer = {
@@ -2046,11 +2162,14 @@ window.__ModuleLoader__.load({
2046
2162
  let frameIndex = 0;
2047
2163
  let lastAdvance = Date.now();
2048
2164
  let override;
2165
+ let baseIdle = config.phases.idle;
2049
2166
  let drawToken = 0;
2050
2167
  let lastDrawnUrl;
2051
2168
  const trackForPhase = (phase) => {
2052
2169
  const mapped = config.phases[phase];
2053
- return mapped !== void 0 && config.tracks[mapped] !== void 0 ? mapped : config.phases.idle;
2170
+ if (mapped === void 0) return baseIdle;
2171
+ const target = mapped === config.phases.idle ? baseIdle : mapped;
2172
+ return config.tracks[target] !== void 0 ? target : baseIdle;
2054
2173
  };
2055
2174
  /** Canvas path: paints the newest requested frame; stale draws drop out. */
2056
2175
  const paintCanvas = (url) => {
@@ -2098,13 +2217,13 @@ window.__ModuleLoader__.load({
2098
2217
  schedule(def.durations[frameIndex] ?? 200);
2099
2218
  return;
2100
2219
  }
2101
- const target = def.fallback !== void 0 && config.tracks[def.fallback] !== void 0 ? def.fallback : config.phases.idle;
2220
+ const target = def.fallback !== void 0 && config.tracks[def.fallback] !== void 0 ? def.fallback === config.phases.idle ? baseIdle : def.fallback : baseIdle;
2102
2221
  if (target === trackForPhase(ctx.phase.get())) override = void 0;
2103
2222
  play(target);
2104
2223
  }
2105
2224
  function play(trackId) {
2106
2225
  if (disposed) return;
2107
- if (config.tracks[trackId] === void 0) trackId = config.phases.idle;
2226
+ if (config.tracks[trackId] === void 0) trackId = baseIdle;
2108
2227
  if (timer !== void 0) clearTimeout(timer);
2109
2228
  track = trackId;
2110
2229
  frameIndex = 0;
@@ -2163,6 +2282,14 @@ window.__ModuleLoader__.load({
2163
2282
  override = next;
2164
2283
  if (next !== track) play(next);
2165
2284
  },
2285
+ setIdleTrack(next) {
2286
+ if (disposed) return;
2287
+ if (next === void 0 || config.tracks[next] !== void 0 && config.tracks[next].loop) baseIdle = next ?? config.phases.idle;
2288
+ if (override === void 0) {
2289
+ const target = trackForPhase(ctx.phase.get());
2290
+ if (target !== track) play(target);
2291
+ }
2292
+ },
2166
2293
  currentTrack() {
2167
2294
  return track;
2168
2295
  }
@@ -3330,9 +3457,11 @@ window.__ModuleLoader__.load({
3330
3457
  "pet.gameplay.menu": "玩法",
3331
3458
  "pet.gameplay.work": "打工",
3332
3459
  "pet.gameplay.stopWork": "收工",
3333
- "pet.gameplay.sleep": "睡觉",
3460
+ "pet.gameplay.sleep": "休息",
3334
3461
  "pet.gameplay.wake": "起床",
3335
3462
  "pet.gameplay.shop": "商店",
3463
+ "pet.gameplay.skin": "皮肤",
3464
+ "pet.gameplay.skinDefault": "默认",
3336
3465
  "pet.gameplay.back": "返回",
3337
3466
  "pet.gameplay.buy": "购买",
3338
3467
  "pet.gameplay.insufficient": "{currency}不足",
@@ -3358,7 +3487,7 @@ window.__ModuleLoader__.load({
3358
3487
  "settings.visible": "显示宠物",
3359
3488
  "settings.visibleHint": "关闭后宠物隐藏,可从聊天输入区重新召唤。",
3360
3489
  "settings.size": "大小(px)",
3361
- "settings.sizeHint": "精灵单元高度,范围 32–512。",
3490
+ "settings.sizeHint": "精灵单元高度,范围 32–1024。",
3362
3491
  "settings.right": "距右侧(px)",
3363
3492
  "settings.rightHint": "距视口右边缘的水平内缩距离。",
3364
3493
  "settings.bottom": "距底部(px)",
@@ -3412,6 +3541,8 @@ window.__ModuleLoader__.load({
3412
3541
  "pet.gameplay.sleep": "Sleep",
3413
3542
  "pet.gameplay.wake": "Wake up",
3414
3543
  "pet.gameplay.shop": "Shop",
3544
+ "pet.gameplay.skin": "Skin",
3545
+ "pet.gameplay.skinDefault": "Default",
3415
3546
  "pet.gameplay.back": "Back",
3416
3547
  "pet.gameplay.buy": "Buy",
3417
3548
  "pet.gameplay.insufficient": "Not enough {currency}",
@@ -3437,7 +3568,7 @@ window.__ModuleLoader__.load({
3437
3568
  "settings.visible": "Show the pet",
3438
3569
  "settings.visibleHint": "When off, the pet hides; summon it again from the input row.",
3439
3570
  "settings.size": "Size (px)",
3440
- "settings.sizeHint": "Sprite cell height, 32–512.",
3571
+ "settings.sizeHint": "Sprite cell height, 32–1024.",
3441
3572
  "settings.right": "Right inset (px)",
3442
3573
  "settings.rightHint": "Horizontal inset from the viewport right edge.",
3443
3574
  "settings.bottom": "Bottom inset (px)",
@@ -3488,7 +3619,7 @@ window.__ModuleLoader__.load({
3488
3619
  /** The building package's version, when the bundle carries it. */
3489
3620
  function bakedVersion() {
3490
3621
  try {
3491
- return "0.3.17";
3622
+ return "0.3.18";
3492
3623
  } catch {
3493
3624
  return;
3494
3625
  }
@@ -3595,6 +3726,16 @@ window.__ModuleLoader__.load({
3595
3726
  * first-level settings section.
3596
3727
  * @param ctx - client root context.
3597
3728
  */
3729
+ /**
3730
+ * Module-wide work-tick throttle (ms). Hot reloads can leave several
3731
+ * GameplayHud instances alive, each running its own 10s work interval;
3732
+ * without a shared gate every interval would call workTick and each stale
3733
+ * call re-rolls, re-grants treats and re-plays the success/fail track, so
3734
+ * the outcome appears to play several times per window. This shared marker
3735
+ * accepts the first adjudication of a window and silently suppresses the
3736
+ * duplicates that follow. Reset when (re-)entering work mode.
3737
+ */
3738
+ let lastWorkTickAt = 0;
3598
3739
  function apply(ctx) {
3599
3740
  reportDailyHeartbeat([{ name: "@linxin666/dsh-pet" }]);
3600
3741
  ctx.effect(() => {
@@ -3756,8 +3897,16 @@ window.__ModuleLoader__.load({
3756
3897
  },
3757
3898
  gameplay: {
3758
3899
  touch: (zone) => petApi.gameplayTouch(zone),
3759
- setMode: (mode) => petApi.gameplaySetMode(mode),
3760
- workTick: () => petApi.gameplayWorkTick(),
3900
+ setMode: async (mode) => {
3901
+ if (mode === "work") lastWorkTickAt = 0;
3902
+ return petApi.gameplaySetMode(mode);
3903
+ },
3904
+ workTick: async () => {
3905
+ const now = Date.now();
3906
+ if (now - lastWorkTickAt < 8500) return { ok: true };
3907
+ lastWorkTickAt = now;
3908
+ return petApi.gameplayWorkTick();
3909
+ },
3761
3910
  buy: (item) => petApi.gameplayBuy(item)
3762
3911
  }
3763
3912
  });