@rpgjs/action-battle 5.0.0-beta.4 → 5.0.0-beta.6

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