@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.
- package/CHANGELOG.md +29 -0
- package/dist/Game/Object.d.ts +5 -0
- package/dist/Game/Object.js +48 -2
- package/dist/Game/Object.js.map +1 -1
- package/dist/Gui/Gui.js +5 -0
- package/dist/Gui/Gui.js.map +1 -1
- package/dist/components/character.ce.js +9 -2
- package/dist/components/character.ce.js.map +1 -1
- package/dist/components/gui/dialogbox/index.ce.js +54 -4
- package/dist/components/gui/dialogbox/index.ce.js.map +1 -1
- package/dist/components/gui/hud/hud.ce.js +2 -2
- package/dist/components/gui/hud/hud.ce.js.map +1 -1
- package/dist/components/gui/index.d.ts +3 -1
- package/dist/components/gui/index.js +2 -0
- package/dist/components/gui/input-field.ce.js +123 -0
- package/dist/components/gui/input-field.ce.js.map +1 -0
- package/dist/components/gui/input.ce.js +40 -0
- package/dist/components/gui/input.ce.js.map +1 -0
- package/dist/components/gui/menu/items-menu.ce.js +27 -4
- package/dist/components/gui/menu/items-menu.ce.js.map +1 -1
- package/dist/components/gui/menu/main-menu.ce.js +210 -155
- package/dist/components/gui/menu/main-menu.ce.js.map +1 -1
- package/dist/components/gui/save-load.ce.js +33 -4
- package/dist/components/gui/save-load.ce.js.map +1 -1
- package/dist/components/gui/shop/shop.ce.js +47 -4
- package/dist/components/gui/shop/shop.ce.js.map +1 -1
- package/dist/i18n.d.ts +11 -0
- package/dist/i18n.js +11 -0
- package/dist/i18n.js.map +1 -1
- package/dist/index.js +13 -11
- package/package.json +4 -4
- package/src/Game/Object.spec.ts +26 -2
- package/src/Game/Object.ts +71 -2
- package/src/Gui/Gui.spec.ts +5 -0
- package/src/Gui/Gui.ts +5 -1
- package/src/components/character.ce +12 -6
- package/src/components/gui/dialogbox/index.ce +82 -41
- package/src/components/gui/hud/hud.ce +2 -2
- package/src/components/gui/index.ts +5 -1
- package/src/components/gui/input-field.ce +106 -0
- package/src/components/gui/input.ce +26 -0
- package/src/components/gui/menu/items-menu.ce +12 -2
- package/src/components/gui/menu/main-menu.ce +37 -6
- package/src/components/gui/save-load.ce +48 -32
- package/src/components/gui/shop/shop.ce +28 -3
- package/src/i18n.ts +11 -0
- 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: {
|
|
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: {
|
|
567
|
+
attrs: {
|
|
568
|
+
class: "rpg-shop-modal",
|
|
569
|
+
click: closeQuantityDialogFromPointer
|
|
570
|
+
}
|
|
531
571
|
}, h(DOMElement, {
|
|
532
572
|
element: "div",
|
|
533
|
-
attrs: {
|
|
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$
|
|
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$
|
|
12
|
-
import __ce_component$
|
|
13
|
-
import __ce_component$
|
|
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$
|
|
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$
|
|
18
|
-
import __ce_component$
|
|
19
|
-
import __ce_component$
|
|
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$
|
|
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$
|
|
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$
|
|
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.
|
|
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.
|
|
26
|
-
"@rpgjs/server": "5.0.0-beta.
|
|
27
|
-
"@rpgjs/ui-css": "5.0.0-beta.
|
|
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",
|
package/src/Game/Object.spec.ts
CHANGED
|
@@ -1,6 +1,12 @@
|
|
|
1
1
|
import { beforeEach, describe, expect, test, vi } from "vitest";
|
|
2
2
|
import { signal } from "canvasengine";
|
|
3
|
-
import {
|
|
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
|
|
package/src/Game/Object.ts
CHANGED
|
@@ -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 =
|
|
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
|
*
|
package/src/Gui/Gui.spec.ts
CHANGED
|
@@ -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
|
-
|
|
620
|
-
client.processAction({ action: 'escape' })
|
|
621
|
-
}
|
|
629
|
+
processEscapeInput()
|
|
622
630
|
},
|
|
623
631
|
},
|
|
624
632
|
escape: {
|
|
625
633
|
bind: keyboardControls.escape,
|
|
626
634
|
keyDown() {
|
|
627
|
-
|
|
628
|
-
client.processAction({ action: 'escape' })
|
|
629
|
-
}
|
|
635
|
+
processEscapeInput()
|
|
630
636
|
},
|
|
631
637
|
},
|
|
632
638
|
joystick: {
|