@rpgjs/action-battle 5.0.0-alpha.44 → 5.0.0-beta.10

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 (103) hide show
  1. package/CHANGELOG.md +38 -0
  2. package/LICENSE +19 -0
  3. package/README.md +392 -22
  4. package/dist/{ai.server.d.ts → client/ai.server.d.ts} +90 -28
  5. package/dist/client/animations.d.ts +16 -0
  6. package/dist/{client.d.ts → client/client.d.ts} +3 -2
  7. package/dist/{config.d.ts → client/config.d.ts} +2 -0
  8. package/dist/client/core/attack-profile.d.ts +9 -0
  9. package/dist/client/core/attack-runtime.d.ts +20 -0
  10. package/dist/client/core/context.d.ts +5 -0
  11. package/dist/client/core/defaults.d.ts +81 -0
  12. package/dist/client/core/enemy-attack-profiles.d.ts +6 -0
  13. package/dist/client/core/equipment.d.ts +2 -0
  14. package/dist/client/core/hit-reaction.d.ts +5 -0
  15. package/dist/client/core/hit.d.ts +2 -0
  16. package/dist/client/enemies/factory.d.ts +7 -0
  17. package/dist/client/index.d.ts +21 -0
  18. package/dist/client/index.js +24 -31
  19. package/dist/client/index10.js +61 -0
  20. package/dist/client/index11.js +55 -0
  21. package/dist/client/index12.js +106 -0
  22. package/dist/client/index13.js +143 -0
  23. package/dist/client/index14.js +25 -0
  24. package/dist/client/index15.js +72 -0
  25. package/dist/client/index16.js +1343 -0
  26. package/dist/client/index17.js +13 -0
  27. package/dist/client/index18.js +60 -0
  28. package/dist/client/index19.js +10 -0
  29. package/dist/client/index2.js +30 -45
  30. package/dist/client/index20.js +504 -0
  31. package/dist/client/index3.js +45 -1288
  32. package/dist/client/index4.js +105 -330
  33. package/dist/client/index5.js +84 -291
  34. package/dist/client/index6.js +309 -95
  35. package/dist/client/index7.js +35 -59
  36. package/dist/client/index8.js +101 -54
  37. package/dist/client/index9.js +79 -30
  38. package/dist/{server.d.ts → client/server.d.ts} +12 -4
  39. package/dist/client/ui/state.d.ts +35 -0
  40. package/dist/server/ai.server.d.ts +569 -0
  41. package/dist/server/animations.d.ts +16 -0
  42. package/dist/server/config.d.ts +5 -0
  43. package/dist/server/core/attack-profile.d.ts +9 -0
  44. package/dist/server/core/attack-runtime.d.ts +20 -0
  45. package/dist/server/core/context.d.ts +5 -0
  46. package/dist/server/core/defaults.d.ts +81 -0
  47. package/dist/server/core/enemy-attack-profiles.d.ts +6 -0
  48. package/dist/server/core/equipment.d.ts +2 -0
  49. package/dist/server/core/hit-reaction.d.ts +5 -0
  50. package/dist/server/core/hit.d.ts +2 -0
  51. package/dist/server/enemies/factory.d.ts +7 -0
  52. package/dist/server/index.d.ts +21 -0
  53. package/dist/server/index.js +23 -31
  54. package/dist/server/index10.js +1342 -0
  55. package/dist/server/index11.js +37 -0
  56. package/dist/server/index12.js +60 -0
  57. package/dist/server/index13.js +13 -0
  58. package/dist/server/index14.js +503 -0
  59. package/dist/server/index15.js +10 -0
  60. package/dist/server/index2.js +59 -332
  61. package/dist/server/index3.js +29 -1286
  62. package/dist/server/index4.js +45 -53
  63. package/dist/server/index5.js +107 -29
  64. package/dist/server/index6.js +143 -0
  65. package/dist/server/index7.js +25 -0
  66. package/dist/server/index8.js +72 -0
  67. package/dist/server/index9.js +55 -0
  68. package/dist/server/server.d.ts +106 -0
  69. package/dist/server/targeting.d.ts +19 -0
  70. package/package.json +12 -12
  71. package/src/ai.server.spec.ts +120 -0
  72. package/src/ai.server.ts +515 -91
  73. package/src/animations.ts +149 -0
  74. package/src/canvas-engine-shim.ts +4 -0
  75. package/src/client.ts +130 -2
  76. package/src/components/action-bar.ce +5 -3
  77. package/src/components/attack-preview.ce +90 -0
  78. package/src/config.ts +61 -0
  79. package/src/core/attack-profile.spec.ts +118 -0
  80. package/src/core/attack-profile.ts +100 -0
  81. package/src/core/attack-runtime.spec.ts +103 -0
  82. package/src/core/attack-runtime.ts +83 -0
  83. package/src/core/context.ts +35 -0
  84. package/src/core/contracts.ts +126 -0
  85. package/src/core/defaults.ts +162 -0
  86. package/src/core/enemy-attack-profiles.spec.ts +35 -0
  87. package/src/core/enemy-attack-profiles.ts +103 -0
  88. package/src/core/equipment.spec.ts +37 -0
  89. package/src/core/equipment.ts +17 -0
  90. package/src/core/hit-reaction.spec.ts +43 -0
  91. package/src/core/hit-reaction.ts +70 -0
  92. package/src/core/hit.spec.ts +111 -0
  93. package/src/core/hit.ts +92 -0
  94. package/src/enemies/factory.ts +25 -0
  95. package/src/index.ts +94 -1
  96. package/src/server.ts +427 -93
  97. package/src/targeting.spec.ts +24 -0
  98. package/src/types/canvas-engine.d.ts +4 -0
  99. package/src/types.ts +148 -0
  100. package/src/ui/state.ts +57 -0
  101. package/dist/index.d.ts +0 -11
  102. package/dist/ui/state.d.ts +0 -18
  103. /package/dist/{targeting.d.ts → client/targeting.d.ts} +0 -0
@@ -1,97 +1,311 @@
1
- import { useProps, useDefineProps, computed, h, Container, cond, Graphics } from "canvasengine";
2
- import { inject, RpgClientEngine } from "@rpgjs/client";
3
- import { actionBattleUiOptions, actionBattleTargetingState } from "./index7.js";
4
- import { parseAoeMask } from "./index9.js";
1
+ import { actionBattleTargetingState, actionBattleUiOptions, moveTargetingOffset, startTargeting, stopTargeting } from "./index5.js";
2
+ import { RpgClientEngine, inject } from "@rpgjs/client";
3
+ import { DOMContainer, DOMElement, DOMSprite, computed, cond, effect, h, loop, signal, useDefineProps, useProps } from "canvasengine";
4
+ //#region src/components/action-bar.ce
5
+ if (typeof document !== "undefined") {
6
+ let styleElement = document.getElementById("ce-style--home-runner-work-RPG-JS-RPG-JS-packages-action-battle-src-components-action-bar-ce");
7
+ if (!styleElement) {
8
+ styleElement = document.createElement("style");
9
+ styleElement.id = "ce-style--home-runner-work-RPG-JS-RPG-JS-packages-action-battle-src-components-action-bar-ce";
10
+ document.head.appendChild(styleElement);
11
+ }
12
+ styleElement.textContent = ".action-battle-actionbar-root {\n pointer-events: none;\n }\n\n .action-battle-actionbar {\n position: absolute;\n left: 12px;\n right: 12px;\n bottom: 12px;\n pointer-events: auto;\n display: flex;\n justify-content: center;\n }\n\n .action-battle-actionbar-plate {\n width: min(760px, 100%);\n --rpg-ui-hotbar-size: clamp(38px, 8vw, 52px);\n }\n\n .action-battle-actionbar-track {\n --rpg-ui-hotbar-slots: 10;\n }";
13
+ }
5
14
  function component($$props) {
6
- useProps($$props);
7
- const defineProps = useDefineProps($$props);
8
- var object = defineProps().object;
9
- var engine = inject(RpgClientEngine);
10
- var isCurrentPlayer = computed(function() {
11
- if (!(object === null || object === void 0 ? void 0 : object.id))
12
- return false;
13
- var idValue = typeof object.id === "function" ? object.id() : object.id;
14
- return idValue === engine.playerId;
15
- });
16
- var uiOptions = computed(function() {
17
- return actionBattleUiOptions();
18
- });
19
- var targetingState = computed(function() {
20
- return actionBattleTargetingState();
21
- });
22
- var shouldRender = computed(function() {
23
- var _a;
24
- if (!isCurrentPlayer())
25
- return false;
26
- if (!((_a = uiOptions().targeting) === null || _a === void 0 ? void 0 : _a.enabled))
27
- return false;
28
- return targetingState().active;
29
- });
30
- var getTileSize = function() {
31
- var _a, _b;
32
- var uiTile = (_a = uiOptions().targeting) === null || _a === void 0 ? void 0 : _a.tileSize;
33
- if ((uiTile === null || uiTile === void 0 ? void 0 : uiTile.width) && (uiTile === null || uiTile === void 0 ? void 0 : uiTile.height))
34
- return uiTile;
35
- var hitbox = ((_b = object.hitbox) === null || _b === void 0 ? void 0 : _b.call(object)) || { w: 32, h: 32 };
36
- return { width: hitbox.w || 32, height: hitbox.h || 32 };
37
- };
38
- var getOriginTile = function() {
39
- var _a;
40
- var hitbox = ((_a = object.hitbox) === null || _a === void 0 ? void 0 : _a.call(object)) || { w: 32, h: 32 };
41
- var tileSize = getTileSize();
42
- var x = Math.floor((object.x() + hitbox.w / 2) / tileSize.width);
43
- var y = Math.floor((object.y() + hitbox.h / 2) / tileSize.height);
44
- return { x, y };
45
- };
46
- var toColor = function(value, fallback) {
47
- if (typeof value === "number")
48
- return value;
49
- return fallback;
50
- };
51
- var drawGrid = function(g) {
52
- var _a, _b;
53
- var state = targetingState();
54
- if (!state.active)
55
- return;
56
- var tileSize = getTileSize();
57
- var origin = getOriginTile();
58
- var target = {
59
- x: origin.x + state.offset.x,
60
- y: origin.y + state.offset.y
61
- };
62
- var mask = parseAoeMask(state.aoeMask);
63
- var colors = ((_a = uiOptions().targeting) === null || _a === void 0 ? void 0 : _a.colors) || {};
64
- var areaColor = toColor(colors.area, 3120887);
65
- var edgeColor = toColor(colors.edge, 1796760);
66
- var cursorColor = toColor(colors.cursor, 16765286);
67
- var showGrid = ((_b = uiOptions().targeting) === null || _b === void 0 ? void 0 : _b.showGrid) !== false;
68
- var playerX = object.x();
69
- var playerY = object.y();
70
- g.clear();
71
- mask.cells.forEach(function(cell) {
72
- var tileX = target.x + cell.dx;
73
- var tileY = target.y + cell.dy;
74
- var worldX = tileX * tileSize.width;
75
- var worldY = tileY * tileSize.height;
76
- var relX = worldX - playerX;
77
- var relY = worldY - playerY;
78
- g.rect(relX, relY, tileSize.width, tileSize.height);
79
- g.fill({ color: areaColor, alpha: 0.35 });
80
- if (showGrid) {
81
- g.rect(relX, relY, tileSize.width, tileSize.height);
82
- g.stroke({ color: edgeColor, alpha: 0.9, width: 1 });
83
- }
84
- });
85
- var cursorWorldX = target.x * tileSize.width;
86
- var cursorWorldY = target.y * tileSize.height;
87
- var cursorRelX = cursorWorldX - playerX;
88
- var cursorRelY = cursorWorldY - playerY;
89
- g.rect(cursorRelX, cursorRelY, tileSize.width, tileSize.height);
90
- g.stroke({ color: cursorColor, alpha: 1, width: 2 });
91
- };
92
- let $this = h(Container, null, cond(shouldRender, () => h(Graphics, { draw: drawGrid })));
93
- return $this;
15
+ useProps($$props);
16
+ const defineProps = useDefineProps($$props);
17
+ const engine = inject(RpgClientEngine);
18
+ const keyboardControls = engine.globalConfig.keyboardControls;
19
+ const { data, onInteraction, onBack } = defineProps();
20
+ const ACTION_BAR_SIZE = 10;
21
+ const SLOT_LABELS = [
22
+ "1",
23
+ "2",
24
+ "3",
25
+ "4",
26
+ "5",
27
+ "6",
28
+ "7",
29
+ "8",
30
+ "9",
31
+ "0"
32
+ ];
33
+ const SLOT_CONFIG_KEYS = [
34
+ "hotbar1",
35
+ "hotbar2",
36
+ "hotbar3",
37
+ "hotbar4",
38
+ "hotbar5",
39
+ "hotbar6",
40
+ "hotbar7",
41
+ "hotbar8",
42
+ "hotbar9",
43
+ "hotbar0"
44
+ ];
45
+ const selectedSlotIndex = signal(-1);
46
+ const uiOptions = computed(() => actionBattleUiOptions());
47
+ const showItems = computed(() => {
48
+ const mode = uiOptions().actionBar?.mode || "both";
49
+ return mode === "items" || mode === "both";
50
+ });
51
+ const showSkills = computed(() => {
52
+ const mode = uiOptions().actionBar?.mode || "both";
53
+ return mode === "skills" || mode === "both";
54
+ });
55
+ const isTargeting = computed(() => actionBattleTargetingState().active);
56
+ const iconSheet = (iconId) => ({
57
+ definition: engine.getSpriteSheet(iconId),
58
+ playing: "default"
59
+ });
60
+ const resolveProp = (value) => typeof value === "function" ? value() : value;
61
+ const actionBarData = computed(() => resolveProp(data) || {
62
+ items: [],
63
+ skills: []
64
+ });
65
+ const actionBarSlots = computed(() => {
66
+ const entries = [];
67
+ if (showSkills()) (actionBarData().skills || []).forEach((skill, index) => {
68
+ entries.push({
69
+ type: "skill",
70
+ skill,
71
+ item: null,
72
+ sourceIndex: index
73
+ });
74
+ });
75
+ if (showItems()) (actionBarData().items || []).forEach((item, index) => {
76
+ entries.push({
77
+ type: "item",
78
+ skill: null,
79
+ item,
80
+ sourceIndex: index
81
+ });
82
+ });
83
+ const slots = entries.slice(0, ACTION_BAR_SIZE).map((entry, index) => ({
84
+ ...entry,
85
+ label: SLOT_LABELS[index]
86
+ }));
87
+ while (slots.length < ACTION_BAR_SIZE) slots.push({
88
+ type: "empty",
89
+ skill: null,
90
+ item: null,
91
+ sourceIndex: -1,
92
+ label: SLOT_LABELS[slots.length]
93
+ });
94
+ return slots;
95
+ });
96
+ const hasSlotEntry = (slot) => slot.type === "skill" || slot.type === "item";
97
+ const isSlotDisabled = (slot) => {
98
+ if (!hasSlotEntry(slot)) return true;
99
+ const entry = slot.type === "skill" ? slot.skill : slot.item;
100
+ return !entry || !entry.usable;
101
+ };
102
+ const getItemQuantity = (item) => {
103
+ if (!item) return 0;
104
+ if (typeof item.quantity === "function") return item.quantity();
105
+ return item.quantity || 0;
106
+ };
107
+ const selectItem = (item) => {
108
+ if (!item || !item.usable) return;
109
+ if (onInteraction) onInteraction("useItem", { id: item.id });
110
+ };
111
+ const selectSkill = (skill) => {
112
+ if (!skill || !skill.usable) return;
113
+ if ((skill.range ?? 0) > 0 || skill.aoeMask) {
114
+ startTargeting(skill);
115
+ return;
116
+ }
117
+ if (onInteraction) onInteraction("useSkill", { id: skill.id });
118
+ };
119
+ const selectSlot = (index) => {
120
+ const slot = actionBarSlots()[index];
121
+ if (!slot || !hasSlotEntry(slot)) return;
122
+ if (slot.type === "skill") {
123
+ selectSkill(slot.skill);
124
+ return;
125
+ }
126
+ selectItem(slot.item);
127
+ };
128
+ const onSelectSlot = (index) => () => {
129
+ selectedSlotIndex.set(index);
130
+ selectSlot(index);
131
+ };
132
+ const getPlayerTile = () => {
133
+ const player = engine.scene.getCurrentPlayer();
134
+ if (!player) return null;
135
+ const hitbox = player.hitbox?.() || {
136
+ w: 32,
137
+ h: 32
138
+ };
139
+ const tileWidth = hitbox.w || 32;
140
+ const tileHeight = hitbox.h || 32;
141
+ return {
142
+ x: Math.floor((player.x() + hitbox.w / 2) / tileWidth),
143
+ y: Math.floor((player.y() + hitbox.h / 2) / tileHeight)
144
+ };
145
+ };
146
+ const confirmTargeting = () => {
147
+ const state = actionBattleTargetingState();
148
+ if (!state.skill) return;
149
+ const origin = getPlayerTile();
150
+ if (!origin) return;
151
+ const target = {
152
+ x: origin.x + state.offset.x,
153
+ y: origin.y + state.offset.y
154
+ };
155
+ if (onInteraction) onInteraction("useSkill", {
156
+ id: state.skill.id,
157
+ target
158
+ });
159
+ stopTargeting();
160
+ };
161
+ const resolveKeyBind = (key) => {
162
+ if (!key) return null;
163
+ if (typeof key === "string" && keyboardControls?.[key]) return keyboardControls[key];
164
+ return key;
165
+ };
166
+ const slotBind = (index) => keyboardControls?.[SLOT_CONFIG_KEYS[index]] || SLOT_LABELS[index];
167
+ const buildControls = () => {
168
+ const hotbarControls = {};
169
+ actionBarSlots().forEach((slot, index) => {
170
+ if (!hasSlotEntry(slot)) return;
171
+ const bind = slotBind(index);
172
+ hotbarControls[`slot-${index}`] = {
173
+ bind,
174
+ keyDown() {
175
+ if (isTargeting()) return;
176
+ selectedSlotIndex.set(index);
177
+ selectSlot(index);
178
+ }
179
+ };
180
+ if (slot.type === "skill") {
181
+ const skillBind = resolveKeyBind(slot.skill?.key);
182
+ if (!skillBind || skillBind === bind) return;
183
+ hotbarControls[`skill-${index}`] = {
184
+ bind: skillBind,
185
+ keyDown() {
186
+ if (isTargeting()) return;
187
+ selectedSlotIndex.set(index);
188
+ selectSlot(index);
189
+ }
190
+ };
191
+ }
192
+ });
193
+ return {
194
+ left: {
195
+ repeat: true,
196
+ bind: keyboardControls.left,
197
+ throttle: 150,
198
+ keyDown() {
199
+ if (isTargeting()) moveTargetingOffset(-1, 0);
200
+ }
201
+ },
202
+ right: {
203
+ repeat: true,
204
+ bind: keyboardControls.right,
205
+ throttle: 150,
206
+ keyDown() {
207
+ if (isTargeting()) moveTargetingOffset(1, 0);
208
+ }
209
+ },
210
+ up: {
211
+ repeat: true,
212
+ bind: keyboardControls.up,
213
+ throttle: 150,
214
+ keyDown() {
215
+ if (isTargeting()) moveTargetingOffset(0, -1);
216
+ }
217
+ },
218
+ down: {
219
+ repeat: true,
220
+ bind: keyboardControls.down,
221
+ throttle: 150,
222
+ keyDown() {
223
+ if (isTargeting()) moveTargetingOffset(0, 1);
224
+ }
225
+ },
226
+ action: {
227
+ bind: keyboardControls.action,
228
+ keyDown() {
229
+ if (isTargeting()) confirmTargeting();
230
+ }
231
+ },
232
+ escape: {
233
+ bind: keyboardControls.escape,
234
+ keyDown() {
235
+ if (isTargeting()) {
236
+ stopTargeting();
237
+ return;
238
+ }
239
+ if (onBack) onBack();
240
+ }
241
+ },
242
+ ...hotbarControls,
243
+ gamepad: { enabled: true }
244
+ };
245
+ };
246
+ const controls = signal(buildControls());
247
+ effect(() => {
248
+ controls.set(buildControls());
249
+ });
250
+ return h(DOMContainer, {
251
+ width: "100%",
252
+ height: "100%",
253
+ attrs: { class: "action-battle-actionbar-root" },
254
+ controls
255
+ }, h(DOMElement, {
256
+ element: "div",
257
+ attrs: { class: "action-battle-actionbar" }
258
+ }, h(DOMElement, {
259
+ element: "div",
260
+ attrs: { class: "rpg-ui-hotbar action-battle-actionbar-plate" }
261
+ }, h(DOMElement, {
262
+ element: "div",
263
+ attrs: { class: "rpg-ui-hotbar-track action-battle-actionbar-track" }
264
+ }, loop(actionBarSlots, (slot, index) => h(DOMElement, {
265
+ element: "div",
266
+ attrs: {
267
+ class: "rpg-ui-hotbar-slot action-battle-actionbar-slot",
268
+ "data-selected": computed(() => selectedSlotIndex() === index ? "true" : "false"),
269
+ "data-disabled": computed(() => isSlotDisabled(slot) ? "true" : "false"),
270
+ "data-empty": computed(() => hasSlotEntry(slot) ? "false" : "true"),
271
+ "data-type": slot.type,
272
+ tabindex: computed(() => hasSlotEntry(slot) ? index : -1),
273
+ click: hasSlotEntry(slot) ? onSelectSlot(index) : void 0
274
+ }
275
+ }, [
276
+ h(DOMElement, {
277
+ element: "span",
278
+ attrs: { class: "rpg-ui-hotbar-key action-battle-actionbar-key" },
279
+ textContent: slot.label
280
+ }),
281
+ cond(computed(() => slot.type === "skill" && slot.skill?.icon), () => h(DOMSprite, {
282
+ sheet: computed(() => iconSheet(slot.skill.icon)),
283
+ width: "60px",
284
+ height: "60px",
285
+ scale: 2,
286
+ objectFit: "contain"
287
+ }), [computed(() => slot.type === "item" && slot.item?.icon), () => h(DOMSprite, {
288
+ sheet: computed(() => iconSheet(slot.item.icon)),
289
+ width: "60px",
290
+ height: "60px",
291
+ scale: 2,
292
+ objectFit: "contain"
293
+ })], [computed(() => slot.type === "skill" && slot.skill), () => h(DOMElement, {
294
+ element: "span",
295
+ attrs: { class: "rpg-ui-hotbar-text action-battle-actionbar-text" },
296
+ textContent: slot.skill.name
297
+ })], [computed(() => slot.type === "item" && slot.item), () => h(DOMElement, {
298
+ element: "span",
299
+ attrs: { class: "rpg-ui-hotbar-text action-battle-actionbar-text" },
300
+ textContent: slot.item.name
301
+ })]),
302
+ cond(computed(() => slot.type === "item" && slot.item), () => h(DOMElement, {
303
+ element: "span",
304
+ attrs: { class: "rpg-ui-hotbar-count action-battle-actionbar-count" },
305
+ textContent: "x" + getItemQuantity(slot.item)
306
+ }))
307
+ ]))))));
94
308
  }
95
- export {
96
- component as default
97
- };
309
+ var __ce_component = component;
310
+ //#endregion
311
+ export { __ce_component as default };
@@ -1,61 +1,37 @@
1
- import { signal } from "canvasengine";
2
- import { normalizeActionBattleOptions, DEFAULT_ACTION_BATTLE_OPTIONS } from "./index8.js";
3
- const defaultTargetingState = {
4
- active: false,
5
- skill: null,
6
- range: 0,
7
- offset: { x: 0, y: 0 },
8
- aoeMask: DEFAULT_ACTION_BATTLE_OPTIONS.skills?.defaultAoeMask || ["#"]
1
+ //#region src/targeting.ts
2
+ var normalizeMaskRows = (mask) => {
3
+ if (!mask) return ["#"];
4
+ if (Array.isArray(mask)) return mask;
5
+ return mask.trim().split("\n").map((row) => row.replace(/\r/g, ""));
9
6
  };
10
- const actionBattleUiOptions = signal(
11
- normalizeActionBattleOptions({}).ui || {}
12
- );
13
- const actionBattleSkillOptions = signal(
14
- normalizeActionBattleOptions({}).skills || {}
15
- );
16
- const actionBattleTargetingState = signal({
17
- ...defaultTargetingState
18
- });
19
- const setActionBattleOptions = (options = {}) => {
20
- const normalized = normalizeActionBattleOptions(options);
21
- actionBattleUiOptions.set(normalized.ui || {});
22
- actionBattleSkillOptions.set(normalized.skills || {});
23
- };
24
- const startTargeting = (skill) => {
25
- const skillsOptions = actionBattleSkillOptions();
26
- const mask = skill.aoeMask || skillsOptions.defaultAoeMask || ["#"];
27
- actionBattleTargetingState.set({
28
- active: true,
29
- skill,
30
- range: skill.range ?? 0,
31
- offset: { x: 0, y: 0 },
32
- aoeMask: mask
33
- });
34
- };
35
- const stopTargeting = () => {
36
- actionBattleTargetingState.set({ ...defaultTargetingState });
37
- };
38
- const moveTargetingOffset = (dx, dy) => {
39
- const state = actionBattleTargetingState();
40
- if (!state.active) return;
41
- const next = {
42
- x: state.offset.x + dx,
43
- y: state.offset.y + dy
44
- };
45
- if (Math.abs(next.x) + Math.abs(next.y) > state.range) {
46
- return;
47
- }
48
- actionBattleTargetingState.set({
49
- ...state,
50
- offset: next
51
- });
52
- };
53
- export {
54
- actionBattleSkillOptions,
55
- actionBattleTargetingState,
56
- actionBattleUiOptions,
57
- moveTargetingOffset,
58
- setActionBattleOptions,
59
- startTargeting,
60
- stopTargeting
7
+ var parseAoeMask = (mask) => {
8
+ const rows = normalizeMaskRows(mask);
9
+ const height = rows.length;
10
+ const width = rows.reduce((max, row) => Math.max(max, row.length), 0);
11
+ const centerX = Math.floor(width / 2);
12
+ const centerY = Math.floor(height / 2);
13
+ const cells = [];
14
+ rows.forEach((row, y) => {
15
+ for (let x = 0; x < row.length; x++) {
16
+ const char = row[x];
17
+ if (char && char !== "." && char !== " ") cells.push({
18
+ dx: x - centerX,
19
+ dy: y - centerY
20
+ });
21
+ }
22
+ });
23
+ if (cells.length === 0) cells.push({
24
+ dx: 0,
25
+ dy: 0
26
+ });
27
+ return {
28
+ width,
29
+ height,
30
+ centerX,
31
+ centerY,
32
+ cells
33
+ };
61
34
  };
35
+ var manhattanDistance = (a, b) => Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
36
+ //#endregion
37
+ export { manhattanDistance, parseAoeMask };
@@ -1,55 +1,102 @@
1
- const DEFAULT_ACTION_BATTLE_OPTIONS = {
2
- ui: {
3
- actionBar: {
4
- enabled: false,
5
- autoOpen: false,
6
- mode: "both"
7
- },
8
- targeting: {
9
- enabled: true,
10
- showGrid: true,
11
- colors: {
12
- area: 3120887,
13
- edge: 1796760,
14
- cursor: 16765286
15
- }
16
- }
17
- },
18
- skills: {
19
- defaultAoeMask: ["#"]
20
- },
21
- targeting: {
22
- affects: "events",
23
- allowEmptyTarget: true
24
- }
25
- };
26
- function normalizeActionBattleOptions(options = {}) {
27
- return {
28
- ui: {
29
- actionBar: {
30
- ...DEFAULT_ACTION_BATTLE_OPTIONS.ui?.actionBar,
31
- ...options.ui?.actionBar
32
- },
33
- targeting: {
34
- ...DEFAULT_ACTION_BATTLE_OPTIONS.ui?.targeting,
35
- ...options.ui?.targeting,
36
- colors: {
37
- ...DEFAULT_ACTION_BATTLE_OPTIONS.ui?.targeting?.colors,
38
- ...options.ui?.targeting?.colors
39
- }
40
- }
41
- },
42
- skills: {
43
- ...DEFAULT_ACTION_BATTLE_OPTIONS.skills,
44
- ...options.skills
45
- },
46
- targeting: {
47
- ...DEFAULT_ACTION_BATTLE_OPTIONS.targeting,
48
- ...options.targeting
49
- }
50
- };
1
+ import { actionBattleTargetingState, actionBattleUiOptions } from "./index5.js";
2
+ import { parseAoeMask } from "./index7.js";
3
+ import { RpgClientEngine, inject } from "@rpgjs/client";
4
+ import { Container, Graphics, computed, cond, h, useDefineProps, useProps } from "canvasengine";
5
+ //#region src/components/targeting-overlay.ce
6
+ function component($$props) {
7
+ useProps($$props);
8
+ const { object } = useDefineProps($$props)();
9
+ const engine = inject(RpgClientEngine);
10
+ const isCurrentPlayer = computed(() => {
11
+ if (!object?.id) return false;
12
+ return (typeof object.id === "function" ? object.id() : object.id) === engine.playerId;
13
+ });
14
+ const uiOptions = computed(() => actionBattleUiOptions());
15
+ const targetingState = computed(() => actionBattleTargetingState());
16
+ const shouldRender = computed(() => {
17
+ if (!isCurrentPlayer()) return false;
18
+ if (!uiOptions().targeting?.enabled) return false;
19
+ return targetingState().active;
20
+ });
21
+ const getTileSize = () => {
22
+ const uiTile = uiOptions().targeting?.tileSize;
23
+ if (uiTile?.width && uiTile?.height) return uiTile;
24
+ const hitbox = object.hitbox?.() || {
25
+ w: 32,
26
+ h: 32
27
+ };
28
+ return {
29
+ width: hitbox.w || 32,
30
+ height: hitbox.h || 32
31
+ };
32
+ };
33
+ const getOriginTile = () => {
34
+ const hitbox = object.hitbox?.() || {
35
+ w: 32,
36
+ h: 32
37
+ };
38
+ const tileSize = getTileSize();
39
+ return {
40
+ x: Math.floor((object.x() + hitbox.w / 2) / tileSize.width),
41
+ y: Math.floor((object.y() + hitbox.h / 2) / tileSize.height)
42
+ };
43
+ };
44
+ const toColor = (value, fallback) => {
45
+ if (typeof value === "number") return value;
46
+ return fallback;
47
+ };
48
+ const drawGrid = (g) => {
49
+ const state = targetingState();
50
+ if (!state.active) return;
51
+ const tileSize = getTileSize();
52
+ const origin = getOriginTile();
53
+ const target = {
54
+ x: origin.x + state.offset.x,
55
+ y: origin.y + state.offset.y
56
+ };
57
+ const mask = parseAoeMask(state.aoeMask);
58
+ const colors = uiOptions().targeting?.colors || {};
59
+ const areaColor = toColor(colors.area, 3120887);
60
+ const edgeColor = toColor(colors.edge, 1796760);
61
+ const cursorColor = toColor(colors.cursor, 16765286);
62
+ const showGrid = uiOptions().targeting?.showGrid !== false;
63
+ const playerX = object.x();
64
+ const playerY = object.y();
65
+ g.clear();
66
+ mask.cells.forEach((cell) => {
67
+ const tileX = target.x + cell.dx;
68
+ const tileY = target.y + cell.dy;
69
+ const worldX = tileX * tileSize.width;
70
+ const worldY = tileY * tileSize.height;
71
+ const relX = worldX - playerX;
72
+ const relY = worldY - playerY;
73
+ g.rect(relX, relY, tileSize.width, tileSize.height);
74
+ g.fill({
75
+ color: areaColor,
76
+ alpha: .35
77
+ });
78
+ if (showGrid) {
79
+ g.rect(relX, relY, tileSize.width, tileSize.height);
80
+ g.stroke({
81
+ color: edgeColor,
82
+ alpha: .9,
83
+ width: 1
84
+ });
85
+ }
86
+ });
87
+ const cursorWorldX = target.x * tileSize.width;
88
+ const cursorWorldY = target.y * tileSize.height;
89
+ const cursorRelX = cursorWorldX - playerX;
90
+ const cursorRelY = cursorWorldY - playerY;
91
+ g.rect(cursorRelX, cursorRelY, tileSize.width, tileSize.height);
92
+ g.stroke({
93
+ color: cursorColor,
94
+ alpha: 1,
95
+ width: 2
96
+ });
97
+ };
98
+ return h(Container, null, cond(shouldRender, () => h(Graphics, { draw: drawGrid })));
51
99
  }
52
- export {
53
- DEFAULT_ACTION_BATTLE_OPTIONS,
54
- normalizeActionBattleOptions
55
- };
100
+ var __ce_component = component;
101
+ //#endregion
102
+ export { __ce_component as default };