@rpgjs/client 5.0.0-beta.23 → 5.0.0-beta.25

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 (47) hide show
  1. package/CHANGELOG.md +29 -0
  2. package/dist/Game/Object.d.ts +5 -0
  3. package/dist/Game/Object.js +48 -2
  4. package/dist/Game/Object.js.map +1 -1
  5. package/dist/Gui/Gui.js +5 -0
  6. package/dist/Gui/Gui.js.map +1 -1
  7. package/dist/components/character.ce.js +9 -2
  8. package/dist/components/character.ce.js.map +1 -1
  9. package/dist/components/gui/dialogbox/index.ce.js +54 -4
  10. package/dist/components/gui/dialogbox/index.ce.js.map +1 -1
  11. package/dist/components/gui/hud/hud.ce.js +2 -2
  12. package/dist/components/gui/hud/hud.ce.js.map +1 -1
  13. package/dist/components/gui/index.d.ts +3 -1
  14. package/dist/components/gui/index.js +2 -0
  15. package/dist/components/gui/input-field.ce.js +123 -0
  16. package/dist/components/gui/input-field.ce.js.map +1 -0
  17. package/dist/components/gui/input.ce.js +40 -0
  18. package/dist/components/gui/input.ce.js.map +1 -0
  19. package/dist/components/gui/menu/items-menu.ce.js +27 -4
  20. package/dist/components/gui/menu/items-menu.ce.js.map +1 -1
  21. package/dist/components/gui/menu/main-menu.ce.js +210 -155
  22. package/dist/components/gui/menu/main-menu.ce.js.map +1 -1
  23. package/dist/components/gui/save-load.ce.js +33 -4
  24. package/dist/components/gui/save-load.ce.js.map +1 -1
  25. package/dist/components/gui/shop/shop.ce.js +47 -4
  26. package/dist/components/gui/shop/shop.ce.js.map +1 -1
  27. package/dist/i18n.d.ts +11 -0
  28. package/dist/i18n.js +11 -0
  29. package/dist/i18n.js.map +1 -1
  30. package/dist/index.js +13 -11
  31. package/package.json +4 -4
  32. package/src/Game/Object.spec.ts +26 -2
  33. package/src/Game/Object.ts +71 -2
  34. package/src/Gui/Gui.spec.ts +5 -0
  35. package/src/Gui/Gui.ts +5 -1
  36. package/src/components/character.ce +12 -6
  37. package/src/components/gui/dialogbox/index.ce +82 -41
  38. package/src/components/gui/hud/hud.ce +2 -2
  39. package/src/components/gui/index.ts +5 -1
  40. package/src/components/gui/input-field.ce +106 -0
  41. package/src/components/gui/input.ce +26 -0
  42. package/src/components/gui/menu/items-menu.ce +12 -2
  43. package/src/components/gui/menu/main-menu.ce +37 -6
  44. package/src/components/gui/save-load.ce +48 -32
  45. package/src/components/gui/shop/shop.ce +28 -3
  46. package/src/i18n.ts +11 -0
  47. package/vite.config.ts +5 -2
@@ -177,6 +177,24 @@ function component($$props) {
177
177
  quantityDialogOpen.set(false);
178
178
  };
179
179
  }
180
+ const stopEvent = (event) => {
181
+ event?.stopPropagation?.();
182
+ };
183
+ const closeQuantityDialogFromPointer = (event) => {
184
+ stopEvent(event);
185
+ quantityDialogOpen.set(false);
186
+ };
187
+ const closeShop = () => {
188
+ if (quantityDialogOpen()) {
189
+ quantityDialogOpen.set(false);
190
+ return;
191
+ }
192
+ onFinish();
193
+ };
194
+ const closeShopFromPointer = (event) => {
195
+ stopEvent(event);
196
+ closeShop();
197
+ };
180
198
  function confirmTrade() {
181
199
  return function() {
182
200
  const item = currentItem();
@@ -312,7 +330,26 @@ function component($$props) {
312
330
  height: "100%"
313
331
  }, h(DOMElement, {
314
332
  element: "div",
315
- attrs: { class: "rpg-shop-container rpg-anim-fade-in" }
333
+ attrs: {
334
+ class: "rpg-shop-layer",
335
+ click: closeShopFromPointer
336
+ }
337
+ }, [h(DOMElement, {
338
+ element: "button",
339
+ attrs: {
340
+ class: "rpg-ui-close-button",
341
+ type: "button",
342
+ title: "Close",
343
+ "aria-label": "Close shop",
344
+ click: closeShopFromPointer
345
+ },
346
+ textContent: "x"
347
+ }), h(DOMElement, {
348
+ element: "div",
349
+ attrs: {
350
+ class: "rpg-shop-container rpg-anim-fade-in",
351
+ click: stopEvent
352
+ }
316
353
  }, [h(DOMElement, {
317
354
  element: "div",
318
355
  attrs: { class: "rpg-shop-header" }
@@ -527,10 +564,16 @@ function component($$props) {
527
564
  })
528
565
  ])])])), cond(quantityDialogOpen, () => h(DOMElement, {
529
566
  element: "div",
530
- attrs: { class: "rpg-shop-modal" }
567
+ attrs: {
568
+ class: "rpg-shop-modal",
569
+ click: closeQuantityDialogFromPointer
570
+ }
531
571
  }, h(DOMElement, {
532
572
  element: "div",
533
- attrs: { class: "rpg-shop-modal-card" }
573
+ attrs: {
574
+ class: "rpg-shop-modal-card",
575
+ click: stopEvent
576
+ }
534
577
  }, [
535
578
  h(DOMElement, {
536
579
  element: "div",
@@ -608,7 +651,7 @@ function component($$props) {
608
651
  },
609
652
  textContent: computed(() => actionLabel() + " x" + quantity())
610
653
  })])
611
- ])))]))]));
654
+ ])))]))])]));
612
655
  }
613
656
  var __ce_component = component;
614
657
  //#endregion
@@ -1 +1 @@
1
- {"version":3,"file":"shop.ce.js","names":[],"sources":["../../../../src/components/gui/shop/shop.ce"],"sourcesContent":[" <DOMContainer width=\"100%\" height=\"100%\">\n <div class=\"rpg-shop-container rpg-anim-fade-in\">\n <div class=\"rpg-shop-header\">\n <div class=\"rpg-shop-merchant\">\n <div>\n @if (hasFace) {\n <div class=\"rpg-shop-merchant-avatar\">\n <DOMSprite \n sheet={faceSheet(face().id, face().expression)} \n width=\"50px\"\n height=\"50px\"\n objectFit=\"contain\"\n /> \n </div>\n }\n </div>\n <div class=\"rpg-shop-merchant-info\">\n <p>{{ shopMessage() }}</p>\n </div>\n </div>\n <div class=\"rpg-shop-gold\">\n {{ gold() }} {{ goldTerm }}\n </div>\n </div>\n\n <div class=\"rpg-shop-body\">\n <div class=\"rpg-shop-left\">\n @if (tradeView() === 'mode') {\n <div class=\"rpg-shop-content rpg-shop-content-mode\">\n <div class=\"rpg-shop-details rpg-shop-details-mode\">\n <div class=\"rpg-shop-details-header\">\n <div class=\"rpg-shop-details-icon\">🛒</div>\n <h2 style=\"margin: 0;\">{t(\"rpg.shop.choose-action\")}</h2>\n </div>\n <div class=\"rpg-shop-trade\">\n <Navigation tabindex={selectedModeIndex} controls={modeControls}>\n <div class=\"rpg-shop-tabs rpg-shop-trade-tabs\">\n <div\n class=\"rpg-shop-tab\"\n class={{active: selectedModeIndex() === 0}}\n click={selectMode('buy')}\n >{t(\"rpg.shop.buy\")}</div>\n <div\n class=\"rpg-shop-tab\"\n class={{active: selectedModeIndex() === 1}}\n click={selectMode('sell')}\n >{t(\"rpg.shop.sell\")}</div>\n </div>\n </Navigation>\n </div>\n </div>\n </div>\n }\n @else {\n <div>\n <Navigation tabindex={selectedTab} controls={tabControls}> \n <div class=\"rpg-shop-tabs\">\n @for ((tab,index) of tabs) {\n <div \n class=\"rpg-shop-tab\" \n class={{active: selectedTab() === index}} \n tabindex={index} \n click={selectTab(index)}\n >{{ tab.label }}</div>\n }\n </div>\n </Navigation>\n\n <div class=\"rpg-shop-content\">\n <div class=\"rpg-shop-grid\">\n <Navigation tabindex={selectedItem} controls={itemControls}> \n @for ((item,index) of filteredItems) { \n <div class=\"rpg-shop-card\" class={{disabled: isItemDisabled(item), selected: selectedItem() === index}} tabindex={index} click={selectItem(index)}>\n <div class=\"rpg-shop-card-icon\">\n @if (item.icon) {\n <DOMSprite \n sheet={iconSheet(item.icon)} \n playing=\"default\" \n width=\"48px\"\n height=\"48px\"\n objectFit=\"contain\"\n />\n }\n </div>\n <div class=\"rpg-shop-card-name\">{{ item.name }}</div>\n <div class=\"rpg-shop-card-price\">{{ item.price }} {{ goldTerm }}</div>\n @if (item.quantity !== undefined) {\n <div class=\"rpg-shop-card-qty\">x{{ item.quantity }}</div>\n }\n @if (item.equipped) {\n <div class=\"rpg-shop-card-tag\">{t(\"rpg.shop.equipped\")}</div>\n }\n </div>\n }\n </Navigation>\n </div>\n\n <div class=\"rpg-shop-details\">\n <div class=\"rpg-shop-details-header\">\n <div class=\"rpg-shop-details-icon\">\n <DOMSprite \n sheet={iconSheet(currentItem()?.icon)} \n playing=\"default\" \n width=\"80px\"\n height=\"80px\"\n objectFit=\"contain\"\n />\n </div>\n <h2 style=\"margin: 0;\">{{ currentItem()?.name || \"\" }}</h2>\n <p style=\"color: #ffd700; font-weight: bold; margin: 8px 0;\">{{ currentItem()?.price ?? 0 }} {{ goldTerm }}</p>\n @if (currentItem()?.quantity !== undefined) {\n <div class=\"rpg-shop-details-qty\">{t(\"rpg.shop.qty\")}: x{{ currentItem()?.quantity }}</div>\n }\n </div>\n <div>\n @if (currentItem()?.equipped) {\n <div class=\"rpg-shop-equipped\">{t(\"rpg.shop.already-equipped\")}</div>\n }\n </div>\n <div class=\"rpg-shop-details-desc\">\n {{ currentItem()?.description || \"\" }}\n </div>\n <div>\n @if (displayStats().length > 0) {\n <div class=\"rpg-shop-stats\">\n @for ((stat,index) of displayStats) {\n <div class=\"rpg-shop-stat\" class={{positive: stat.delta > 0, negative: stat.delta < 0}}>\n <div class=\"rpg-shop-stat-key\">{{ stat.label }}</div>\n <div class=\"rpg-shop-stat-value\">\n {{ stat.delta > 0 ? '+' : '' }}{{ stat.delta }}\n </div>\n @if (stat.current !== undefined) {\n <div class=\"rpg-shop-stat-current\">{{ stat.current }} → {{ stat.next }}</div>\n }\n </div>\n }\n </div>\n }\n </div>\n <button class=\"rpg-shop-btn\" click={backToMode()}>{t(\"rpg.shop.back\")}</button>\n </div>\n </div>\n </div>\n }\n @if (quantityDialogOpen) {\n <div class=\"rpg-shop-modal\">\n <div class=\"rpg-shop-modal-card\">\n <div class=\"rpg-shop-modal-title\">{{ actionLabel }}</div>\n <div class=\"rpg-shop-modal-item\">{{ currentItem()?.name || \"\" }}</div>\n @if (currentItem()?.quantity !== undefined) {\n <div class=\"rpg-shop-modal-qty\">{t(\"rpg.shop.available\")}: x{{ currentItem()?.quantity }}</div>\n }\n <div class=\"rpg-shop-quantity\">\n <div class=\"rpg-shop-quantity-label\">{t(\"rpg.shop.quantity\")}</div>\n <div class=\"rpg-shop-quantity-controls\">\n <button class=\"rpg-shop-btn\" click={changeQuantity(-1)}>-</button>\n <div class=\"rpg-shop-quantity-value\">{{ quantity }}</div>\n <button class=\"rpg-shop-btn\" click={changeQuantity(1)}>+</button>\n </div>\n </div>\n <div class=\"rpg-shop-modal-total\">\n <span>{t(\"rpg.shop.total\")}</span>\n <span>{{ totalPrice() }} {{ goldTerm }}</span>\n </div>\n <div class=\"rpg-shop-modal-actions\">\n <button class=\"rpg-shop-btn rpg-shop-btn-secondary\" click={closeQuantityDialog()}>{t(\"rpg.menu.cancel\")}</button>\n <button class=\"rpg-shop-btn\" click={confirmTrade()}>\n {{ actionLabel() }} x{{ quantity() }}\n </button>\n </div>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n </DOMContainer>\n\n<script>\n import { mount, signal, computed, createTabindexNavigator, effect } from \"canvasengine\";\n import { inject } from \"../../../core/inject\";\n import { RpgClientEngine } from \"../../../RpgClientEngine\";\n import { getKeyboardControlBind } from \"../../../services/actionInput\";\n\n const engine = inject(RpgClientEngine)\n const { t } = engine.i18n()\n const currentPlayer = engine.scene.currentPlayer\n const keyboardControls = engine.globalConfig.keyboardControls\n const iconSheet = (iconId) => ({\n definition: engine.getSpriteSheet(iconId),\n playing: \"default\"\n })\n const goldTerm = engine.globalConfig.goldTerm || 'G'\n\n const selectedItem = signal(0)\n const selectedTab = signal(0)\n const tradeMode = signal('buy')\n const tradeView = signal('mode')\n const selectedModeIndex = signal(0)\n const quantity = signal(1)\n const quantityDialogOpen = signal(false)\n const defaultMessage = t(\"rpg.shop.default-message\")\n const tabs = [\n { id: 'item', label: t(\"rpg.menu.items\") },\n { id: 'weapon', label: t(\"rpg.menu.weapons\") },\n { id: 'armor', label: t(\"rpg.menu.armor\") }\n ]\n\n const { data, onInteraction , onFinish } = defineProps()\n\n const resolveProp = (value) => typeof value === \"function\" ? value() : value\n const guiData = computed(() => resolveProp(data) || {})\n const shopMessage = computed(() => {\n const message = resolveProp(guiData().message)\n return message ? String(message) : defaultMessage\n })\n const face = computed(() => resolveProp(guiData().face))\n const hasFace = computed(() => {\n const value = face()\n return value && value.id\n })\n const buyItems = computed(() => guiData().items || [])\n const sellItems = computed(() => guiData().sellItems || [])\n const activeItems = computed(() => tradeMode() === 'buy' ? buyItems() : sellItems())\n const filteredItems = computed(() => {\n const tab = tabs[selectedTab()]\n const items = activeItems()\n if (!tab) return []\n return items.filter((item) => item?.type === tab.id)\n })\n const currentItem = computed(() => filteredItems()[selectedItem()])\n const gold = computed(() => currentPlayer()._gold())\n const actionLabel = computed(() => tradeMode() === 'buy' ? t(\"rpg.shop.buy\") : t(\"rpg.shop.sell\"))\n const faceSheet = (graphicId, animationName) => ({\n definition: engine.getSpriteSheet(graphicId),\n playing: animationName || \"default\"\n })\n const maxQuantity = computed(() => {\n const item = currentItem()\n if (!item) return 0\n const price = item?.price || 0\n if (price <= 0) return 0\n if (tradeMode() === 'sell') {\n const qty = item?.quantity ?? 0\n return Math.max(0, qty)\n }\n return Math.max(1, Math.floor(gold() / price))\n })\n const totalPrice = computed(() => {\n const price = currentItem()?.price || 0\n return price * quantity()\n })\n const displayStats = computed(() => {\n const stats = currentItem()?.stats || {}\n const params = guiData().playerParams || {}\n const order = ['atk', 'def', 'pdef', 'sdef', 'str', 'dex', 'int', 'agi', 'maxHp', 'maxSp']\n const labels = {\n atk: 'ATK',\n def: 'DEF',\n pdef: 'PDEF',\n sdef: 'SDEF',\n str: 'STR',\n dex: 'DEX',\n int: 'INT',\n agi: 'AGI',\n maxHp: 'MAX HP',\n maxSp: 'MAX SP'\n }\n const orderedKeys = order.filter((key) => stats[key] !== undefined)\n const extraKeys = Object.keys(stats).filter((key) => !order.includes(key))\n const keys = orderedKeys.concat(extraKeys)\n const list = []\n for (const key of keys) {\n const delta = stats[key]\n if (delta === undefined || delta === 0) continue\n let current = params[key]\n if (current === undefined && key === 'def') current = params.pdef\n const next = current !== undefined ? current + delta : undefined\n list.push({\n key,\n label: labels[key] || key.toUpperCase(),\n delta,\n current,\n next\n })\n }\n return list\n })\n const nav = createTabindexNavigator(selectedItem, { count: () => filteredItems().length }, 'wrap')\n const navTab = createTabindexNavigator(selectedTab, { count: () => tabs.length }, 'wrap')\n const navMode = createTabindexNavigator(selectedModeIndex, { count: () => 2 }, 'wrap')\n\n function selectItem(index) {\n return function() {\n selectedItem.set(index)\n quantity.set(1)\n if (!isItemDisabled(filteredItems()[index])) {\n quantityDialogOpen.set(true)\n }\n }\n }\n\n function selectTab(index) {\n return function() {\n selectedTab.set(index)\n selectedItem.set(0)\n }\n }\n\n function selectMode(mode) {\n return function() {\n tradeMode.set(mode)\n selectedModeIndex.set(mode === 'buy' ? 0 : 1)\n selectedItem.set(0)\n quantity.set(1)\n tradeView.set('items')\n }\n }\n\n function isItemDisabled(item) {\n if (!item) return true\n const price = item?.price || 0\n if (price <= 0) return true\n if (tradeMode() === 'sell') {\n const qty = item?.quantity ?? 0\n return qty <= 0\n }\n return gold() < price\n }\n\n function backToMode() {\n return function() {\n tradeView.set('mode')\n }\n }\n\n function changeQuantity(delta) {\n return function() {\n const limit = maxQuantity()\n if (limit <= 0) return\n const nextValue = Math.max(1, Math.min(limit, quantity() + delta))\n if (nextValue !== quantity()) {\n quantity.set(nextValue)\n }\n }\n }\n\n function closeQuantityDialog() {\n return function() {\n quantityDialogOpen.set(false)\n }\n }\n\n function confirmTrade() {\n return function() {\n const item = currentItem()\n if (!item || isItemDisabled(item)) {\n quantityDialogOpen.set(false)\n return\n }\n const action = tradeMode() === 'buy' ? 'buyItem' : 'sellItem'\n onInteraction(action, { id: item.id, nb: quantity() })\n quantityDialogOpen.set(false)\n }\n }\n\n effect(() => {\n const count = filteredItems().length\n if (selectedItem() >= count) {\n selectedItem.set(Math.max(0, count - 1))\n }\n })\n\n const modeControls = signal({\n left: {\n repeat: true,\n bind: keyboardControls.left,\n throttle: 150,\n keyDown() {\n navMode.next(-1)\n }\n },\n right: {\n repeat: true,\n bind: keyboardControls.right,\n throttle: 150,\n keyDown() {\n navMode.next(1)\n }\n },\n action: {\n bind: getKeyboardControlBind(keyboardControls.action),\n keyDown() {\n const mode = selectedModeIndex() === 0 ? 'buy' : 'sell'\n tradeMode.set(mode)\n selectedItem.set(0)\n quantity.set(1)\n tradeView.set('items')\n }\n },\n escape: {\n bind: keyboardControls.escape,\n keyDown() {\n onFinish()\n }\n },\n gamepad: {\n enabled: true\n }\n });\n\n const itemControls = signal({\n up: {\n repeat: true,\n bind: keyboardControls.up,\n throttle: 150,\n keyDown() {\n if (quantityDialogOpen()) {\n changeQuantity(1)()\n return\n }\n nav.next(-1)\n }\n },\n down: {\n repeat: true,\n bind: keyboardControls.down,\n throttle: 150,\n keyDown() {\n if (quantityDialogOpen()) {\n changeQuantity(-1)()\n return\n }\n nav.next(1)\n }\n },\n action: {\n bind: getKeyboardControlBind(keyboardControls.action),\n keyDown() {\n if (quantityDialogOpen()) {\n const item = currentItem()\n if (!item || isItemDisabled(item)) {\n quantityDialogOpen.set(false)\n return\n }\n const action = tradeMode() === 'buy' ? 'buyItem' : 'sellItem'\n onInteraction(action, { id: item.id, nb: quantity() })\n quantityDialogOpen.set(false)\n return\n }\n const item = currentItem()\n if (!isItemDisabled(item)) {\n quantityDialogOpen.set(true)\n }\n }\n },\n escape: {\n bind: keyboardControls.escape,\n keyDown() {\n if (quantityDialogOpen()) {\n quantityDialogOpen.set(false)\n return\n }\n tradeView.set('mode')\n }\n },\n gamepad: {\n enabled: true\n }\n });\n\n const tabControls = signal({\n left: {\n repeat: true,\n bind: keyboardControls.left,\n throttle: 150,\n keyDown() {\n if (quantityDialogOpen()) return\n navTab.next(-1)\n }\n },\n right: {\n repeat: true,\n bind: keyboardControls.right,\n throttle: 150,\n keyDown() {\n if (quantityDialogOpen()) return\n navTab.next(1)\n }\n },\n gamepad: {\n enabled: true\n }\n })\n</script>\n"],"mappings":"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"}
1
+ {"version":3,"file":"shop.ce.js","names":[],"sources":["../../../../src/components/gui/shop/shop.ce"],"sourcesContent":[" <DOMContainer width=\"100%\" height=\"100%\">\n <div class=\"rpg-shop-layer\" click={closeShopFromPointer}>\n <button class=\"rpg-ui-close-button\" type=\"button\" title=\"Close\" aria-label=\"Close shop\" click={closeShopFromPointer}>x</button>\n <div class=\"rpg-shop-container rpg-anim-fade-in\" click={stopEvent}>\n <div class=\"rpg-shop-header\">\n <div class=\"rpg-shop-merchant\">\n <div>\n @if (hasFace) {\n <div class=\"rpg-shop-merchant-avatar\">\n <DOMSprite \n sheet={faceSheet(face().id, face().expression)} \n width=\"50px\"\n height=\"50px\"\n objectFit=\"contain\"\n /> \n </div>\n }\n </div>\n <div class=\"rpg-shop-merchant-info\">\n <p>{{ shopMessage() }}</p>\n </div>\n </div>\n <div class=\"rpg-shop-gold\">\n {{ gold() }} {{ goldTerm }}\n </div>\n </div>\n\n <div class=\"rpg-shop-body\">\n <div class=\"rpg-shop-left\">\n @if (tradeView() === 'mode') {\n <div class=\"rpg-shop-content rpg-shop-content-mode\">\n <div class=\"rpg-shop-details rpg-shop-details-mode\">\n <div class=\"rpg-shop-details-header\">\n <div class=\"rpg-shop-details-icon\">🛒</div>\n <h2 style=\"margin: 0;\">{t(\"rpg.shop.choose-action\")}</h2>\n </div>\n <div class=\"rpg-shop-trade\">\n <Navigation tabindex={selectedModeIndex} controls={modeControls}>\n <div class=\"rpg-shop-tabs rpg-shop-trade-tabs\">\n <div\n class=\"rpg-shop-tab\"\n class={{active: selectedModeIndex() === 0}}\n click={selectMode('buy')}\n >{t(\"rpg.shop.buy\")}</div>\n <div\n class=\"rpg-shop-tab\"\n class={{active: selectedModeIndex() === 1}}\n click={selectMode('sell')}\n >{t(\"rpg.shop.sell\")}</div>\n </div>\n </Navigation>\n </div>\n </div>\n </div>\n }\n @else {\n <div>\n <Navigation tabindex={selectedTab} controls={tabControls}> \n <div class=\"rpg-shop-tabs\">\n @for ((tab,index) of tabs) {\n <div \n class=\"rpg-shop-tab\" \n class={{active: selectedTab() === index}} \n tabindex={index} \n click={selectTab(index)}\n >{{ tab.label }}</div>\n }\n </div>\n </Navigation>\n\n <div class=\"rpg-shop-content\">\n <div class=\"rpg-shop-grid\">\n <Navigation tabindex={selectedItem} controls={itemControls}> \n @for ((item,index) of filteredItems) { \n <div class=\"rpg-shop-card\" class={{disabled: isItemDisabled(item), selected: selectedItem() === index}} tabindex={index} click={selectItem(index)}>\n <div class=\"rpg-shop-card-icon\">\n @if (item.icon) {\n <DOMSprite \n sheet={iconSheet(item.icon)} \n playing=\"default\" \n width=\"48px\"\n height=\"48px\"\n objectFit=\"contain\"\n />\n }\n </div>\n <div class=\"rpg-shop-card-name\">{{ item.name }}</div>\n <div class=\"rpg-shop-card-price\">{{ item.price }} {{ goldTerm }}</div>\n @if (item.quantity !== undefined) {\n <div class=\"rpg-shop-card-qty\">x{{ item.quantity }}</div>\n }\n @if (item.equipped) {\n <div class=\"rpg-shop-card-tag\">{t(\"rpg.shop.equipped\")}</div>\n }\n </div>\n }\n </Navigation>\n </div>\n\n <div class=\"rpg-shop-details\">\n <div class=\"rpg-shop-details-header\">\n <div class=\"rpg-shop-details-icon\">\n <DOMSprite \n sheet={iconSheet(currentItem()?.icon)} \n playing=\"default\" \n width=\"80px\"\n height=\"80px\"\n objectFit=\"contain\"\n />\n </div>\n <h2 style=\"margin: 0;\">{{ currentItem()?.name || \"\" }}</h2>\n <p style=\"color: #ffd700; font-weight: bold; margin: 8px 0;\">{{ currentItem()?.price ?? 0 }} {{ goldTerm }}</p>\n @if (currentItem()?.quantity !== undefined) {\n <div class=\"rpg-shop-details-qty\">{t(\"rpg.shop.qty\")}: x{{ currentItem()?.quantity }}</div>\n }\n </div>\n <div>\n @if (currentItem()?.equipped) {\n <div class=\"rpg-shop-equipped\">{t(\"rpg.shop.already-equipped\")}</div>\n }\n </div>\n <div class=\"rpg-shop-details-desc\">\n {{ currentItem()?.description || \"\" }}\n </div>\n <div>\n @if (displayStats().length > 0) {\n <div class=\"rpg-shop-stats\">\n @for ((stat,index) of displayStats) {\n <div class=\"rpg-shop-stat\" class={{positive: stat.delta > 0, negative: stat.delta < 0}}>\n <div class=\"rpg-shop-stat-key\">{{ stat.label }}</div>\n <div class=\"rpg-shop-stat-value\">\n {{ stat.delta > 0 ? '+' : '' }}{{ stat.delta }}\n </div>\n @if (stat.current !== undefined) {\n <div class=\"rpg-shop-stat-current\">{{ stat.current }} → {{ stat.next }}</div>\n }\n </div>\n }\n </div>\n }\n </div>\n <button class=\"rpg-shop-btn\" click={backToMode()}>{t(\"rpg.shop.back\")}</button>\n </div>\n </div>\n </div>\n }\n @if (quantityDialogOpen) {\n <div class=\"rpg-shop-modal\" click={closeQuantityDialogFromPointer}>\n <div class=\"rpg-shop-modal-card\" click={stopEvent}>\n <div class=\"rpg-shop-modal-title\">{{ actionLabel }}</div>\n <div class=\"rpg-shop-modal-item\">{{ currentItem()?.name || \"\" }}</div>\n @if (currentItem()?.quantity !== undefined) {\n <div class=\"rpg-shop-modal-qty\">{t(\"rpg.shop.available\")}: x{{ currentItem()?.quantity }}</div>\n }\n <div class=\"rpg-shop-quantity\">\n <div class=\"rpg-shop-quantity-label\">{t(\"rpg.shop.quantity\")}</div>\n <div class=\"rpg-shop-quantity-controls\">\n <button class=\"rpg-shop-btn\" click={changeQuantity(-1)}>-</button>\n <div class=\"rpg-shop-quantity-value\">{{ quantity }}</div>\n <button class=\"rpg-shop-btn\" click={changeQuantity(1)}>+</button>\n </div>\n </div>\n <div class=\"rpg-shop-modal-total\">\n <span>{t(\"rpg.shop.total\")}</span>\n <span>{{ totalPrice() }} {{ goldTerm }}</span>\n </div>\n <div class=\"rpg-shop-modal-actions\">\n <button class=\"rpg-shop-btn rpg-shop-btn-secondary\" click={closeQuantityDialog()}>{t(\"rpg.menu.cancel\")}</button>\n <button class=\"rpg-shop-btn\" click={confirmTrade()}>\n {{ actionLabel() }} x{{ quantity() }}\n </button>\n </div>\n </div>\n </div>\n }\n </div>\n </div>\n </div>\n </div>\n </DOMContainer>\n\n<script>\n import { mount, signal, computed, createTabindexNavigator, effect } from \"canvasengine\";\n import { inject } from \"../../../core/inject\";\n import { RpgClientEngine } from \"../../../RpgClientEngine\";\n import { getKeyboardControlBind } from \"../../../services/actionInput\";\n\n const engine = inject(RpgClientEngine)\n const { t } = engine.i18n()\n const currentPlayer = engine.scene.currentPlayer\n const keyboardControls = engine.globalConfig.keyboardControls\n const iconSheet = (iconId) => ({\n definition: engine.getSpriteSheet(iconId),\n playing: \"default\"\n })\n const goldTerm = engine.globalConfig.goldTerm || 'G'\n\n const selectedItem = signal(0)\n const selectedTab = signal(0)\n const tradeMode = signal('buy')\n const tradeView = signal('mode')\n const selectedModeIndex = signal(0)\n const quantity = signal(1)\n const quantityDialogOpen = signal(false)\n const defaultMessage = t(\"rpg.shop.default-message\")\n const tabs = [\n { id: 'item', label: t(\"rpg.menu.items\") },\n { id: 'weapon', label: t(\"rpg.menu.weapons\") },\n { id: 'armor', label: t(\"rpg.menu.armor\") }\n ]\n\n const { data, onInteraction , onFinish } = defineProps()\n\n const resolveProp = (value) => typeof value === \"function\" ? value() : value\n const guiData = computed(() => resolveProp(data) || {})\n const shopMessage = computed(() => {\n const message = resolveProp(guiData().message)\n return message ? String(message) : defaultMessage\n })\n const face = computed(() => resolveProp(guiData().face))\n const hasFace = computed(() => {\n const value = face()\n return value && value.id\n })\n const buyItems = computed(() => guiData().items || [])\n const sellItems = computed(() => guiData().sellItems || [])\n const activeItems = computed(() => tradeMode() === 'buy' ? buyItems() : sellItems())\n const filteredItems = computed(() => {\n const tab = tabs[selectedTab()]\n const items = activeItems()\n if (!tab) return []\n return items.filter((item) => item?.type === tab.id)\n })\n const currentItem = computed(() => filteredItems()[selectedItem()])\n const gold = computed(() => currentPlayer()._gold())\n const actionLabel = computed(() => tradeMode() === 'buy' ? t(\"rpg.shop.buy\") : t(\"rpg.shop.sell\"))\n const faceSheet = (graphicId, animationName) => ({\n definition: engine.getSpriteSheet(graphicId),\n playing: animationName || \"default\"\n })\n const maxQuantity = computed(() => {\n const item = currentItem()\n if (!item) return 0\n const price = item?.price || 0\n if (price <= 0) return 0\n if (tradeMode() === 'sell') {\n const qty = item?.quantity ?? 0\n return Math.max(0, qty)\n }\n return Math.max(1, Math.floor(gold() / price))\n })\n const totalPrice = computed(() => {\n const price = currentItem()?.price || 0\n return price * quantity()\n })\n const displayStats = computed(() => {\n const stats = currentItem()?.stats || {}\n const params = guiData().playerParams || {}\n const order = ['atk', 'def', 'pdef', 'sdef', 'str', 'dex', 'int', 'agi', 'maxHp', 'maxSp']\n const labels = {\n atk: 'ATK',\n def: 'DEF',\n pdef: 'PDEF',\n sdef: 'SDEF',\n str: 'STR',\n dex: 'DEX',\n int: 'INT',\n agi: 'AGI',\n maxHp: 'MAX HP',\n maxSp: 'MAX SP'\n }\n const orderedKeys = order.filter((key) => stats[key] !== undefined)\n const extraKeys = Object.keys(stats).filter((key) => !order.includes(key))\n const keys = orderedKeys.concat(extraKeys)\n const list = []\n for (const key of keys) {\n const delta = stats[key]\n if (delta === undefined || delta === 0) continue\n let current = params[key]\n if (current === undefined && key === 'def') current = params.pdef\n const next = current !== undefined ? current + delta : undefined\n list.push({\n key,\n label: labels[key] || key.toUpperCase(),\n delta,\n current,\n next\n })\n }\n return list\n })\n const nav = createTabindexNavigator(selectedItem, { count: () => filteredItems().length }, 'wrap')\n const navTab = createTabindexNavigator(selectedTab, { count: () => tabs.length }, 'wrap')\n const navMode = createTabindexNavigator(selectedModeIndex, { count: () => 2 }, 'wrap')\n\n function selectItem(index) {\n return function() {\n selectedItem.set(index)\n quantity.set(1)\n if (!isItemDisabled(filteredItems()[index])) {\n quantityDialogOpen.set(true)\n }\n }\n }\n\n function selectTab(index) {\n return function() {\n selectedTab.set(index)\n selectedItem.set(0)\n }\n }\n\n function selectMode(mode) {\n return function() {\n tradeMode.set(mode)\n selectedModeIndex.set(mode === 'buy' ? 0 : 1)\n selectedItem.set(0)\n quantity.set(1)\n tradeView.set('items')\n }\n }\n\n function isItemDisabled(item) {\n if (!item) return true\n const price = item?.price || 0\n if (price <= 0) return true\n if (tradeMode() === 'sell') {\n const qty = item?.quantity ?? 0\n return qty <= 0\n }\n return gold() < price\n }\n\n function backToMode() {\n return function() {\n tradeView.set('mode')\n }\n }\n\n function changeQuantity(delta) {\n return function() {\n const limit = maxQuantity()\n if (limit <= 0) return\n const nextValue = Math.max(1, Math.min(limit, quantity() + delta))\n if (nextValue !== quantity()) {\n quantity.set(nextValue)\n }\n }\n }\n\n function closeQuantityDialog() {\n return function() {\n quantityDialogOpen.set(false)\n }\n }\n\n const stopEvent = (event) => {\n event?.stopPropagation?.()\n }\n\n const closeQuantityDialogFromPointer = (event) => {\n stopEvent(event)\n quantityDialogOpen.set(false)\n }\n\n const closeShop = () => {\n if (quantityDialogOpen()) {\n quantityDialogOpen.set(false)\n return\n }\n onFinish()\n }\n\n const closeShopFromPointer = (event) => {\n stopEvent(event)\n closeShop()\n }\n\n function confirmTrade() {\n return function() {\n const item = currentItem()\n if (!item || isItemDisabled(item)) {\n quantityDialogOpen.set(false)\n return\n }\n const action = tradeMode() === 'buy' ? 'buyItem' : 'sellItem'\n onInteraction(action, { id: item.id, nb: quantity() })\n quantityDialogOpen.set(false)\n }\n }\n\n effect(() => {\n const count = filteredItems().length\n if (selectedItem() >= count) {\n selectedItem.set(Math.max(0, count - 1))\n }\n })\n\n const modeControls = signal({\n left: {\n repeat: true,\n bind: keyboardControls.left,\n throttle: 150,\n keyDown() {\n navMode.next(-1)\n }\n },\n right: {\n repeat: true,\n bind: keyboardControls.right,\n throttle: 150,\n keyDown() {\n navMode.next(1)\n }\n },\n action: {\n bind: getKeyboardControlBind(keyboardControls.action),\n keyDown() {\n const mode = selectedModeIndex() === 0 ? 'buy' : 'sell'\n tradeMode.set(mode)\n selectedItem.set(0)\n quantity.set(1)\n tradeView.set('items')\n }\n },\n escape: {\n bind: keyboardControls.escape,\n keyDown() {\n onFinish()\n }\n },\n gamepad: {\n enabled: true\n }\n });\n\n const itemControls = signal({\n up: {\n repeat: true,\n bind: keyboardControls.up,\n throttle: 150,\n keyDown() {\n if (quantityDialogOpen()) {\n changeQuantity(1)()\n return\n }\n nav.next(-1)\n }\n },\n down: {\n repeat: true,\n bind: keyboardControls.down,\n throttle: 150,\n keyDown() {\n if (quantityDialogOpen()) {\n changeQuantity(-1)()\n return\n }\n nav.next(1)\n }\n },\n action: {\n bind: getKeyboardControlBind(keyboardControls.action),\n keyDown() {\n if (quantityDialogOpen()) {\n const item = currentItem()\n if (!item || isItemDisabled(item)) {\n quantityDialogOpen.set(false)\n return\n }\n const action = tradeMode() === 'buy' ? 'buyItem' : 'sellItem'\n onInteraction(action, { id: item.id, nb: quantity() })\n quantityDialogOpen.set(false)\n return\n }\n const item = currentItem()\n if (!isItemDisabled(item)) {\n quantityDialogOpen.set(true)\n }\n }\n },\n escape: {\n bind: keyboardControls.escape,\n keyDown() {\n if (quantityDialogOpen()) {\n quantityDialogOpen.set(false)\n return\n }\n tradeView.set('mode')\n }\n },\n gamepad: {\n enabled: true\n }\n });\n\n const tabControls = signal({\n left: {\n repeat: true,\n bind: keyboardControls.left,\n throttle: 150,\n keyDown() {\n if (quantityDialogOpen()) return\n navTab.next(-1)\n }\n },\n right: {\n repeat: true,\n bind: keyboardControls.right,\n throttle: 150,\n keyDown() {\n if (quantityDialogOpen()) return\n navTab.next(1)\n }\n },\n gamepad: {\n enabled: true\n }\n })\n</script>\n"],"mappings":"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"}
package/dist/i18n.d.ts CHANGED
@@ -39,6 +39,17 @@ export declare const RpgClientBuiltinI18n: {
39
39
  "rpg.gameover.title": string;
40
40
  "rpg.gameover.title-screen": string;
41
41
  "rpg.gameover.load-game": string;
42
+ "rpg.input.confirm": string;
43
+ "rpg.input.cancel": string;
44
+ "rpg.input.error.invalid": string;
45
+ "rpg.input.error.required": string;
46
+ "rpg.input.error.number": string;
47
+ "rpg.input.error.min": string;
48
+ "rpg.input.error.max": string;
49
+ "rpg.input.error.step": string;
50
+ "rpg.input.error.min-length": string;
51
+ "rpg.input.error.max-length": string;
52
+ "rpg.input.error.email": string;
42
53
  "rpg.shop.default-message": string;
43
54
  "rpg.shop.choose-action": string;
44
55
  "rpg.shop.buy": string;
package/dist/i18n.js CHANGED
@@ -39,6 +39,17 @@ var RpgClientBuiltinI18n = { en: {
39
39
  "rpg.gameover.title": "Game Over",
40
40
  "rpg.gameover.title-screen": "Title Screen",
41
41
  "rpg.gameover.load-game": "Load Game",
42
+ "rpg.input.confirm": "Confirm",
43
+ "rpg.input.cancel": "Cancel",
44
+ "rpg.input.error.invalid": "Invalid value.",
45
+ "rpg.input.error.required": "A value is required.",
46
+ "rpg.input.error.number": "Enter a valid number.",
47
+ "rpg.input.error.min": "The value must be at least {min}.",
48
+ "rpg.input.error.max": "The value must be at most {max}.",
49
+ "rpg.input.error.step": "The value must use increments of {step}.",
50
+ "rpg.input.error.min-length": "Enter at least {minLength} characters.",
51
+ "rpg.input.error.max-length": "Enter no more than {maxLength} characters.",
52
+ "rpg.input.error.email": "Enter a valid email address.",
42
53
  "rpg.shop.default-message": "Welcome to my shop!",
43
54
  "rpg.shop.choose-action": "Choose an action",
44
55
  "rpg.shop.buy": "Buy",
package/dist/i18n.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"i18n.js","names":[],"sources":["../src/i18n.ts"],"sourcesContent":["import { createI18nProvider, type I18nConfig } from \"@rpgjs/common\";\n\nexport const RpgClientBuiltinI18n = {\n en: {\n \"rpg.menu.title\": \"Menu\",\n \"rpg.menu.status\": \"Status\",\n \"rpg.menu.level\": \"Level\",\n \"rpg.menu.gold\": \"Gold\",\n \"rpg.menu.parameters\": \"Parameters\",\n \"rpg.menu.items\": \"Items\",\n \"rpg.menu.skills\": \"Skills\",\n \"rpg.menu.equip\": \"Equip\",\n \"rpg.menu.options\": \"Options\",\n \"rpg.menu.save\": \"Save\",\n \"rpg.menu.exit\": \"Exit\",\n \"rpg.menu.weapons\": \"Weapons\",\n \"rpg.menu.armor\": \"Armor\",\n \"rpg.menu.use\": \"Use\",\n \"rpg.menu.cancel\": \"Cancel\",\n \"rpg.menu.unequip\": \"Unequip\",\n \"rpg.menu.remove-equipment\": \"Remove the current equipment\",\n \"rpg.menu.empty\": \"Empty\",\n \"rpg.menu.leave-game\": \"Leave the game?\",\n \"rpg.menu.exit-help\": \"Press Action to confirm or Escape to go back.\",\n \"rpg.menu.options-help\": \"Configure your preferences here.\",\n \"rpg.save.title\": \"Save Game\",\n \"rpg.save.subtitle\": \"Choose a slot to overwrite or create.\",\n \"rpg.load.title\": \"Load Game\",\n \"rpg.load.subtitle\": \"Select a slot to load your progress.\",\n \"rpg.save.auto\": \"Auto Save\",\n \"rpg.save.slot\": \"Slot {index}\",\n \"rpg.save.empty-slot\": \"Empty Slot\",\n \"rpg.save.level\": \"Level\",\n \"rpg.save.exp\": \"Exp\",\n \"rpg.save.map\": \"Map\",\n \"rpg.save.date\": \"Date\",\n \"rpg.title.default\": \"RPG\",\n \"rpg.title.start\": \"Start\",\n \"rpg.title.load\": \"Load\",\n \"rpg.gameover.title\": \"Game Over\",\n \"rpg.gameover.title-screen\": \"Title Screen\",\n \"rpg.gameover.load-game\": \"Load Game\",\n \"rpg.shop.default-message\": \"Welcome to my shop!\",\n \"rpg.shop.choose-action\": \"Choose an action\",\n \"rpg.shop.buy\": \"Buy\",\n \"rpg.shop.sell\": \"Sell\",\n \"rpg.shop.back\": \"Back\",\n \"rpg.shop.equipped\": \"Equipped\",\n \"rpg.shop.already-equipped\": \"Already equipped\",\n \"rpg.shop.qty\": \"Qty\",\n \"rpg.shop.available\": \"Available\",\n \"rpg.shop.quantity\": \"Quantity\",\n \"rpg.shop.total\": \"Total\",\n },\n};\n\nexport function provideI18n(config: I18nConfig = {}) {\n return createI18nProvider(config);\n}\n"],"mappings":";;AAEA,IAAa,uBAAuB,EAClC,IAAI;CACF,kBAAkB;CAClB,mBAAmB;CACnB,kBAAkB;CAClB,iBAAiB;CACjB,uBAAuB;CACvB,kBAAkB;CAClB,mBAAmB;CACnB,kBAAkB;CAClB,oBAAoB;CACpB,iBAAiB;CACjB,iBAAiB;CACjB,oBAAoB;CACpB,kBAAkB;CAClB,gBAAgB;CAChB,mBAAmB;CACnB,oBAAoB;CACpB,6BAA6B;CAC7B,kBAAkB;CAClB,uBAAuB;CACvB,sBAAsB;CACtB,yBAAyB;CACzB,kBAAkB;CAClB,qBAAqB;CACrB,kBAAkB;CAClB,qBAAqB;CACrB,iBAAiB;CACjB,iBAAiB;CACjB,uBAAuB;CACvB,kBAAkB;CAClB,gBAAgB;CAChB,gBAAgB;CAChB,iBAAiB;CACjB,qBAAqB;CACrB,mBAAmB;CACnB,kBAAkB;CAClB,sBAAsB;CACtB,6BAA6B;CAC7B,0BAA0B;CAC1B,4BAA4B;CAC5B,0BAA0B;CAC1B,gBAAgB;CAChB,iBAAiB;CACjB,iBAAiB;CACjB,qBAAqB;CACrB,6BAA6B;CAC7B,gBAAgB;CAChB,sBAAsB;CACtB,qBAAqB;CACrB,kBAAkB;AACpB,EACF;AAEA,SAAgB,YAAY,SAAqB,CAAC,GAAG;CACnD,OAAO,mBAAmB,MAAM;AAClC"}
1
+ {"version":3,"file":"i18n.js","names":[],"sources":["../src/i18n.ts"],"sourcesContent":["import { createI18nProvider, type I18nConfig } from \"@rpgjs/common\";\n\nexport const RpgClientBuiltinI18n = {\n en: {\n \"rpg.menu.title\": \"Menu\",\n \"rpg.menu.status\": \"Status\",\n \"rpg.menu.level\": \"Level\",\n \"rpg.menu.gold\": \"Gold\",\n \"rpg.menu.parameters\": \"Parameters\",\n \"rpg.menu.items\": \"Items\",\n \"rpg.menu.skills\": \"Skills\",\n \"rpg.menu.equip\": \"Equip\",\n \"rpg.menu.options\": \"Options\",\n \"rpg.menu.save\": \"Save\",\n \"rpg.menu.exit\": \"Exit\",\n \"rpg.menu.weapons\": \"Weapons\",\n \"rpg.menu.armor\": \"Armor\",\n \"rpg.menu.use\": \"Use\",\n \"rpg.menu.cancel\": \"Cancel\",\n \"rpg.menu.unequip\": \"Unequip\",\n \"rpg.menu.remove-equipment\": \"Remove the current equipment\",\n \"rpg.menu.empty\": \"Empty\",\n \"rpg.menu.leave-game\": \"Leave the game?\",\n \"rpg.menu.exit-help\": \"Press Action to confirm or Escape to go back.\",\n \"rpg.menu.options-help\": \"Configure your preferences here.\",\n \"rpg.save.title\": \"Save Game\",\n \"rpg.save.subtitle\": \"Choose a slot to overwrite or create.\",\n \"rpg.load.title\": \"Load Game\",\n \"rpg.load.subtitle\": \"Select a slot to load your progress.\",\n \"rpg.save.auto\": \"Auto Save\",\n \"rpg.save.slot\": \"Slot {index}\",\n \"rpg.save.empty-slot\": \"Empty Slot\",\n \"rpg.save.level\": \"Level\",\n \"rpg.save.exp\": \"Exp\",\n \"rpg.save.map\": \"Map\",\n \"rpg.save.date\": \"Date\",\n \"rpg.title.default\": \"RPG\",\n \"rpg.title.start\": \"Start\",\n \"rpg.title.load\": \"Load\",\n \"rpg.gameover.title\": \"Game Over\",\n \"rpg.gameover.title-screen\": \"Title Screen\",\n \"rpg.gameover.load-game\": \"Load Game\",\n \"rpg.input.confirm\": \"Confirm\",\n \"rpg.input.cancel\": \"Cancel\",\n \"rpg.input.error.invalid\": \"Invalid value.\",\n \"rpg.input.error.required\": \"A value is required.\",\n \"rpg.input.error.number\": \"Enter a valid number.\",\n \"rpg.input.error.min\": \"The value must be at least {min}.\",\n \"rpg.input.error.max\": \"The value must be at most {max}.\",\n \"rpg.input.error.step\": \"The value must use increments of {step}.\",\n \"rpg.input.error.min-length\": \"Enter at least {minLength} characters.\",\n \"rpg.input.error.max-length\": \"Enter no more than {maxLength} characters.\",\n \"rpg.input.error.email\": \"Enter a valid email address.\",\n \"rpg.shop.default-message\": \"Welcome to my shop!\",\n \"rpg.shop.choose-action\": \"Choose an action\",\n \"rpg.shop.buy\": \"Buy\",\n \"rpg.shop.sell\": \"Sell\",\n \"rpg.shop.back\": \"Back\",\n \"rpg.shop.equipped\": \"Equipped\",\n \"rpg.shop.already-equipped\": \"Already equipped\",\n \"rpg.shop.qty\": \"Qty\",\n \"rpg.shop.available\": \"Available\",\n \"rpg.shop.quantity\": \"Quantity\",\n \"rpg.shop.total\": \"Total\",\n },\n};\n\nexport function provideI18n(config: I18nConfig = {}) {\n return createI18nProvider(config);\n}\n"],"mappings":";;AAEA,IAAa,uBAAuB,EAClC,IAAI;CACF,kBAAkB;CAClB,mBAAmB;CACnB,kBAAkB;CAClB,iBAAiB;CACjB,uBAAuB;CACvB,kBAAkB;CAClB,mBAAmB;CACnB,kBAAkB;CAClB,oBAAoB;CACpB,iBAAiB;CACjB,iBAAiB;CACjB,oBAAoB;CACpB,kBAAkB;CAClB,gBAAgB;CAChB,mBAAmB;CACnB,oBAAoB;CACpB,6BAA6B;CAC7B,kBAAkB;CAClB,uBAAuB;CACvB,sBAAsB;CACtB,yBAAyB;CACzB,kBAAkB;CAClB,qBAAqB;CACrB,kBAAkB;CAClB,qBAAqB;CACrB,iBAAiB;CACjB,iBAAiB;CACjB,uBAAuB;CACvB,kBAAkB;CAClB,gBAAgB;CAChB,gBAAgB;CAChB,iBAAiB;CACjB,qBAAqB;CACrB,mBAAmB;CACnB,kBAAkB;CAClB,sBAAsB;CACtB,6BAA6B;CAC7B,0BAA0B;CAC1B,qBAAqB;CACrB,oBAAoB;CACpB,2BAA2B;CAC3B,4BAA4B;CAC5B,0BAA0B;CAC1B,uBAAuB;CACvB,uBAAuB;CACvB,wBAAwB;CACxB,8BAA8B;CAC9B,8BAA8B;CAC9B,yBAAyB;CACzB,4BAA4B;CAC5B,0BAA0B;CAC1B,gBAAgB;CAChB,iBAAiB;CACjB,iBAAiB;CACjB,qBAAqB;CACrB,6BAA6B;CAC7B,gBAAgB;CAChB,sBAAsB;CACtB,qBAAqB;CACrB,kBAAkB;AACpB,EACF;AAEA,SAAgB,YAAY,SAAqB,CAAC,GAAG;CACnD,OAAO,mBAAmB,MAAM;AAClC"}
package/dist/index.js CHANGED
@@ -2,25 +2,27 @@ import { Context } from "./node_modules/.pnpm/@signe_di@3.1.0/node_modules/@sign
2
2
  import { clearInject, context, inject, setInject } from "./core/inject.js";
3
3
  import { AbstractWebsocket, WebSocketToken } from "./services/AbstractSocket.js";
4
4
  import { getKeyboardControlBind, isKeyboardActionConfig, keyboardEventMatchesBind, normalizeActionInput, resolveKeyboardActionInput, resolveKeyboardDirectionInput } from "./services/actionInput.js";
5
+ import __ce_component$10 from "./components/gui/input-field.ce.js";
5
6
  import __ce_component$2 from "./components/gui/dialogbox/index.ce.js";
6
7
  import __ce_component from "./components/gui/box.ce.js";
7
- import __ce_component$16 from "./components/gui/shop/shop.ce.js";
8
+ import __ce_component$18 from "./components/gui/shop/shop.ce.js";
8
9
  import { getEntityProp } from "./utils/getEntityProp.js";
9
10
  import __ce_component$8 from "./components/gui/hud/hud.ce.js";
10
11
  import { SaveClientService, SaveClientToken, provideSaveClient } from "./services/save.js";
11
- import __ce_component$14 from "./components/gui/save-load.ce.js";
12
- import __ce_component$9 from "./components/gui/menu/items-menu.ce.js";
13
- import __ce_component$17 from "./components/gui/menu/skills-menu.ce.js";
12
+ import __ce_component$16 from "./components/gui/save-load.ce.js";
13
+ import __ce_component$11 from "./components/gui/menu/items-menu.ce.js";
14
+ import __ce_component$19 from "./components/gui/menu/skills-menu.ce.js";
14
15
  import __ce_component$3 from "./components/gui/menu/equip-menu.ce.js";
15
- import __ce_component$13 from "./components/gui/menu/options-menu.ce.js";
16
+ import __ce_component$15 from "./components/gui/menu/options-menu.ce.js";
16
17
  import __ce_component$5 from "./components/gui/menu/exit-menu.ce.js";
17
- import __ce_component$11 from "./components/gui/menu/main-menu.ce.js";
18
- import __ce_component$12 from "./components/gui/notification/notification.ce.js";
19
- import __ce_component$18 from "./components/gui/title-screen.ce.js";
18
+ import __ce_component$13 from "./components/gui/menu/main-menu.ce.js";
19
+ import __ce_component$14 from "./components/gui/notification/notification.ce.js";
20
+ import __ce_component$20 from "./components/gui/title-screen.ce.js";
20
21
  import __ce_component$6 from "./components/gui/gameover.ce.js";
22
+ import __ce_component$9 from "./components/gui/input.ce.js";
21
23
  import "./components/gui/index.js";
22
24
  import { RpgGui } from "./Gui/Gui.js";
23
- import __ce_component$15 from "./components/scenes/draw-map.ce.js";
25
+ import __ce_component$17 from "./components/scenes/draw-map.ce.js";
24
26
  import { LoadMapService, LoadMapToken, provideLoadMap } from "./services/loadMap.js";
25
27
  import { RpgSound, Sound, getSoundMetadata } from "./Sound.js";
26
28
  import { RpgResource } from "./Resource.js";
@@ -40,7 +42,7 @@ import { provideRpg } from "./services/standalone.js";
40
42
  import { BridgeWebsocket, provideMmorpg } from "./services/mmorpg.js";
41
43
  import { startGame } from "./core/setup.js";
42
44
  import __ce_component$7 from "./components/prebuilt/hp-bar.ce.js";
43
- import __ce_component$10 from "./components/prebuilt/light-halo.ce.js";
45
+ import __ce_component$12 from "./components/prebuilt/light-halo.ce.js";
44
46
  import "./components/prebuilt/index.js";
45
47
  import { Presets } from "./presets/index.js";
46
48
  import __ce_component$1 from "./components/character.ce.js";
@@ -49,4 +51,4 @@ import "./components/index.js";
49
51
  import { Spritesheet } from "./decorators/spritesheet.js";
50
52
  import { withMobile } from "./components/gui/mobile/index.js";
51
53
  import { Input, KeyboardControls } from "canvasengine";
52
- export { AbstractWebsocket, __ce_component as BoxComponent, BridgeWebsocket, __ce_component$1 as CharacterComponent, ClientVisualRegistry, Context, Control, __ce_component$2 as DialogboxComponent, __ce_component$3 as EquipMenuComponent, __ce_component$4 as EventLayerComponent, __ce_component$5 as ExitMenuComponent, __ce_component$6 as GameoverComponent, GlobalConfigToken, __ce_component$7 as HpBar, __ce_component$8 as HudComponent, Input, __ce_component$9 as ItemsMenuComponent, KeyboardControls, __ce_component$10 as LightHalo, LoadMapService, LoadMapToken, __ce_component$11 as MainMenuComponent, __ce_component$12 as NotificationComponent, __ce_component$13 as OptionsMenuComponent, PrebuiltComponentAnimations, Presets, ProjectileManager, RpgClientBuiltinI18n, RpgClientEngine, RpgClientEvent, RpgClientInteractions, RpgClientObject, RpgClientPlayer, RpgGui, RpgResource, RpgSound, SaveClientService, SaveClientToken, __ce_component$14 as SaveLoadComponent, __ce_component$15 as SceneMap, __ce_component$16 as ShopComponent, __ce_component$17 as SkillsMenuComponent, Sound, Spritesheet, __ce_component$18 as TitleScreenComponent, WebSocketToken, clearInject, context, createClientPointerContext, dragToTile, draggable, getEntityProp, getKeyboardControlBind, getSoundMetadata, hoverPopover, inject, isKeyboardActionConfig, keyboardEventMatchesBind, normalizeActionInput, provideClientGlobalConfig, provideClientModules, provideGlobalConfig, provideI18n, provideLoadMap, provideMmorpg, provideRpg, provideSaveClient, resolveKeyboardActionInput, resolveKeyboardDirectionInput, selectable, setInject, startGame, withMobile };
54
+ export { AbstractWebsocket, __ce_component as BoxComponent, BridgeWebsocket, __ce_component$1 as CharacterComponent, ClientVisualRegistry, Context, Control, __ce_component$2 as DialogboxComponent, __ce_component$3 as EquipMenuComponent, __ce_component$4 as EventLayerComponent, __ce_component$5 as ExitMenuComponent, __ce_component$6 as GameoverComponent, GlobalConfigToken, __ce_component$7 as HpBar, __ce_component$8 as HudComponent, Input, __ce_component$9 as InputComponent, __ce_component$10 as InputFieldComponent, __ce_component$11 as ItemsMenuComponent, KeyboardControls, __ce_component$12 as LightHalo, LoadMapService, LoadMapToken, __ce_component$13 as MainMenuComponent, __ce_component$14 as NotificationComponent, __ce_component$15 as OptionsMenuComponent, PrebuiltComponentAnimations, Presets, ProjectileManager, RpgClientBuiltinI18n, RpgClientEngine, RpgClientEvent, RpgClientInteractions, RpgClientObject, RpgClientPlayer, RpgGui, RpgResource, RpgSound, SaveClientService, SaveClientToken, __ce_component$16 as SaveLoadComponent, __ce_component$17 as SceneMap, __ce_component$18 as ShopComponent, __ce_component$19 as SkillsMenuComponent, Sound, Spritesheet, __ce_component$20 as TitleScreenComponent, WebSocketToken, clearInject, context, createClientPointerContext, dragToTile, draggable, getEntityProp, getKeyboardControlBind, getSoundMetadata, hoverPopover, inject, isKeyboardActionConfig, keyboardEventMatchesBind, normalizeActionInput, provideClientGlobalConfig, provideClientModules, provideGlobalConfig, provideI18n, provideLoadMap, provideMmorpg, provideRpg, provideSaveClient, resolveKeyboardActionInput, resolveKeyboardDirectionInput, selectable, setInject, startGame, withMobile };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rpgjs/client",
3
- "version": "5.0.0-beta.23",
3
+ "version": "5.0.0-beta.25",
4
4
  "description": "RPGJS is a framework for creating RPG/MMORPG games",
5
5
  "main": "dist/index.js",
6
6
  "types": "./dist/index.d.ts",
@@ -22,9 +22,9 @@
22
22
  "pixi.js": "^8.9.2"
23
23
  },
24
24
  "dependencies": {
25
- "@rpgjs/common": "5.0.0-beta.22",
26
- "@rpgjs/server": "5.0.0-beta.23",
27
- "@rpgjs/ui-css": "5.0.0-beta.21",
25
+ "@rpgjs/common": "5.0.0-beta.24",
26
+ "@rpgjs/server": "5.0.0-beta.25",
27
+ "@rpgjs/ui-css": "5.0.0-beta.23",
28
28
  "@signe/di": "3.1.0",
29
29
  "@signe/room": "3.1.0",
30
30
  "@signe/sync": "3.1.0",
@@ -1,6 +1,12 @@
1
1
  import { beforeEach, describe, expect, test, vi } from "vitest";
2
2
  import { signal } from "canvasengine";
3
- import { appendFramePayload, RpgClientObject, withGraphicDisplayScale } from "./Object";
3
+ import {
4
+ appendFramePayload,
5
+ mergeFreshFramePayload,
6
+ multiplyGraphicDisplayScale,
7
+ RpgClientObject,
8
+ withGraphicDisplayScale,
9
+ } from "./Object";
4
10
  import { RpgClientEvent } from "./Event";
5
11
  import { RpgClientPlayer } from "./Player";
6
12
 
@@ -40,10 +46,28 @@ describe("RpgClientObject animations", () => {
40
46
  ).toEqual([{ x: 10, y: 20, ts: 1 }]);
41
47
  });
42
48
 
49
+ test("drops stale movement frames that arrive after a newer frame was applied", () => {
50
+ expect(
51
+ mergeFreshFramePayload(
52
+ [],
53
+ [{ x: 733, y: 551, ts: 200 }],
54
+ 300,
55
+ ),
56
+ ).toEqual([]);
57
+ });
58
+
43
59
  test("keeps instance scale outside the spritesheet transform scale", () => {
44
60
  expect(withGraphicDisplayScale({ id: "hero" }, 0.5)).toEqual({
45
61
  id: "hero",
46
- displayScale: 0.5,
62
+ displayScale: [0.5, 0.5],
63
+ });
64
+ });
65
+
66
+ test("combines media and instance display scales", () => {
67
+ expect(multiplyGraphicDisplayScale(0.5, 2)).toEqual([1, 1]);
68
+ expect(withGraphicDisplayScale({ id: "hero", displayScale: 0.5 }, { x: 2, y: 3 })).toEqual({
69
+ id: "hero",
70
+ displayScale: [1, 1.5],
47
71
  });
48
72
  });
49
73
 
@@ -29,12 +29,49 @@ type ConfigurableTrigger<T> = Omit<Trigger<T>, "start"> & {
29
29
  start(config?: T): Promise<void>;
30
30
  };
31
31
 
32
+ const toFiniteScale = (value: unknown, fallback: number): number => {
33
+ if (typeof value === "number" && Number.isFinite(value)) return value;
34
+ if (typeof value === "string" && value.trim()) {
35
+ const parsed = Number(value);
36
+ return Number.isFinite(parsed) ? parsed : fallback;
37
+ }
38
+ return fallback;
39
+ };
40
+
41
+ const normalizeGraphicScale = (value: unknown, fallback: [number, number] = [1, 1]): [number, number] => {
42
+ if (typeof value === "number" || typeof value === "string") {
43
+ const scale = toFiniteScale(value, fallback[0]);
44
+ return [scale, scale];
45
+ }
46
+ if (Array.isArray(value)) {
47
+ const x = toFiniteScale(value[0], fallback[0]);
48
+ const y = toFiniteScale(value[1] ?? value[0], x);
49
+ return [x, y];
50
+ }
51
+ if (value && typeof value === "object") {
52
+ const scale = value as { x?: unknown; y?: unknown };
53
+ const x = toFiniteScale(scale.x, fallback[0]);
54
+ const y = toFiniteScale(scale.y ?? scale.x, x);
55
+ return [x, y];
56
+ }
57
+ return fallback;
58
+ };
59
+
60
+ export const multiplyGraphicDisplayScale = (
61
+ baseScale: unknown,
62
+ instanceScale: unknown,
63
+ ): [number, number] => {
64
+ const base = normalizeGraphicScale(baseScale);
65
+ const instance = normalizeGraphicScale(instanceScale);
66
+ return [base[0] * instance[0], base[1] * instance[1]];
67
+ };
68
+
32
69
  export const withGraphicDisplayScale = (spritesheet: any, scale: unknown): any => {
33
70
  if (!spritesheet || typeof spritesheet !== "object") return spritesheet;
34
71
  if (scale === undefined || scale === null) return spritesheet;
35
72
  return {
36
73
  ...spritesheet,
37
- displayScale: scale,
74
+ displayScale: multiplyGraphicDisplayScale(spritesheet.displayScale, scale),
38
75
  };
39
76
  };
40
77
 
@@ -47,6 +84,24 @@ export const appendFramePayload = (current: unknown, items: unknown): Frame[] =>
47
84
  return currentFrames.concat(nextFrames);
48
85
  };
49
86
 
87
+ export const getFrameTimestamp = (frame: Frame): number => {
88
+ const timestamp = Number(frame.ts);
89
+ return Number.isFinite(timestamp) ? timestamp : 0;
90
+ };
91
+
92
+ export const mergeFreshFramePayload = (
93
+ current: unknown,
94
+ items: unknown,
95
+ lastAppliedFrameTs = 0,
96
+ ): Frame[] => {
97
+ return appendFramePayload(current, items)
98
+ .filter((frame) => {
99
+ const timestamp = getFrameTimestamp(frame);
100
+ return timestamp === 0 || timestamp > lastAppliedFrameTs;
101
+ })
102
+ .sort((a, b) => getFrameTimestamp(a) - getFrameTimestamp(b));
103
+ };
104
+
50
105
  export abstract class RpgClientObject extends RpgCommonPlayer {
51
106
  abstract _type: string;
52
107
  emitParticleTrigger = trigger();
@@ -57,6 +112,7 @@ export abstract class RpgClientObject extends RpgCommonPlayer {
57
112
  frames: Frame[] = [];
58
113
  graphicsSignals = signal<any[]>([]);
59
114
  flashTrigger: ConfigurableTrigger<FlashTriggerOptions> = trigger<FlashTriggerOptions>();
115
+ private lastAppliedFrameTs = 0;
60
116
  private animationRestoreState?: {
61
117
  animationName: string;
62
118
  graphics: any[];
@@ -70,7 +126,11 @@ export abstract class RpgClientObject extends RpgCommonPlayer {
70
126
  this._frames.observable.subscribe(({ items }) => {
71
127
  if (!this.id) return;
72
128
  //if (this.id == this.engine.playerIdSignal()!) return;
73
- this.frames = appendFramePayload(this.frames, items);
129
+ this.frames = mergeFreshFramePayload(
130
+ this.frames,
131
+ items,
132
+ this.lastAppliedFrameTs,
133
+ );
74
134
  });
75
135
 
76
136
  const graphics$ = this.graphics.observable.pipe(map(({ items }) => items));
@@ -102,16 +162,25 @@ export abstract class RpgClientObject extends RpgCommonPlayer {
102
162
  const frame = this.frames.shift();
103
163
  if (frame) {
104
164
  if (typeof frame.x !== "number" || typeof frame.y !== "number") return;
165
+ const frameTs = this.getFrameTimestamp(frame);
166
+ if (frameTs > 0 && frameTs <= this.lastAppliedFrameTs) return;
105
167
  engine.scene.setBodyPosition(
106
168
  this.id,
107
169
  frame.x,
108
170
  frame.y,
109
171
  "top-left"
110
172
  );
173
+ if (frameTs > this.lastAppliedFrameTs) {
174
+ this.lastAppliedFrameTs = frameTs;
175
+ }
111
176
  }
112
177
  });
113
178
  }
114
179
 
180
+ private getFrameTimestamp(frame: Frame): number {
181
+ return getFrameTimestamp(frame);
182
+ }
183
+
115
184
  /**
116
185
  * Access the shared client hook registry.
117
186
  *
@@ -14,6 +14,7 @@ vi.mock("../components/gui", () => {
14
14
  NotificationComponent: component,
15
15
  TitleScreenComponent: component,
16
16
  GameoverComponent: component,
17
+ InputComponent: component,
17
18
  };
18
19
  });
19
20
 
@@ -64,6 +65,10 @@ const VueTooltip = {
64
65
  };
65
66
 
66
67
  describe("RpgGui Vue integration", () => {
68
+ test("registers the prebuilt input GUI", async () => {
69
+ const { gui } = await createGui();
70
+ expect(gui.get(PrebuiltGui.Input)).toBeDefined();
71
+ });
67
72
  test("tracks GUI open ids and sends them back when closing", async () => {
68
73
  const { gui, socket } = await createGui();
69
74
  await gui._initialize();
package/src/Gui/Gui.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import { Context, inject } from "@signe/di";
2
2
  import { signal, Signal, WritableSignal } from "canvasengine";
3
3
  import { AbstractWebsocket, WebSocketToken } from "../services/AbstractSocket";
4
- import { DialogboxComponent, ShopComponent, SaveLoadComponent, MainMenuComponent, NotificationComponent, TitleScreenComponent, GameoverComponent } from "../components/gui";
4
+ import { DialogboxComponent, ShopComponent, SaveLoadComponent, MainMenuComponent, NotificationComponent, TitleScreenComponent, GameoverComponent, InputComponent } from "../components/gui";
5
5
  import { combineLatest, Subscription } from "rxjs";
6
6
  import { PrebuiltGui } from "@rpgjs/common";
7
7
 
@@ -176,6 +176,10 @@ export class RpgGui {
176
176
  name: PrebuiltGui.Gameover,
177
177
  component: GameoverComponent,
178
178
  });
179
+ this.add({
180
+ name: PrebuiltGui.Input,
181
+ component: InputComponent,
182
+ });
179
183
 
180
184
  this.registerOptimisticReducer(PrebuiltGui.MainMenu, mainMenuOptimisticReducer);
181
185
  }
@@ -521,6 +521,16 @@
521
521
  });
522
522
  };
523
523
 
524
+ let lastEscapeActionAt = 0;
525
+ const processEscapeInput = () => {
526
+ const now = Date.now();
527
+ if (now - lastEscapeActionAt < 50) return;
528
+ lastEscapeActionAt = now;
529
+ if (canControls()) {
530
+ client.processAction({ action: 'escape' })
531
+ }
532
+ };
533
+
524
534
  const actionBind = () => getKeyboardControlBind(keyboardControls.action);
525
535
  const keyboardEventId = (event) => `${event.keyCode}:${event.code}:${event.key}`;
526
536
 
@@ -616,17 +626,13 @@
616
626
  back: {
617
627
  bind: keyboardControls.escape,
618
628
  keyDown() {
619
- if (canControls()) {
620
- client.processAction({ action: 'escape' })
621
- }
629
+ processEscapeInput()
622
630
  },
623
631
  },
624
632
  escape: {
625
633
  bind: keyboardControls.escape,
626
634
  keyDown() {
627
- if (canControls()) {
628
- client.processAction({ action: 'escape' })
629
- }
635
+ processEscapeInput()
630
636
  },
631
637
  },
632
638
  joystick: {