@rpgjs/action-battle 5.0.0-beta.5 → 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.
- package/README.md +115 -0
- package/dist/ai.server.d.ts +17 -2
- package/dist/client/index.js +13 -8
- package/dist/client/index10.js +54 -136
- package/dist/client/index11.js +52 -23
- package/dist/client/index12.js +101 -1217
- package/dist/client/index13.js +139 -42
- package/dist/client/index14.js +23 -8
- package/dist/client/index15.js +68 -444
- package/dist/client/index16.js +1281 -0
- package/dist/client/index17.js +13 -0
- package/dist/client/index18.js +60 -0
- package/dist/client/index19.js +10 -0
- package/dist/client/index2.js +25 -87
- package/dist/client/index20.js +504 -0
- package/dist/client/index3.js +45 -83
- package/dist/client/index4.js +98 -297
- package/dist/client/index5.js +81 -33
- package/dist/client/index6.js +284 -78
- package/dist/client/index7.js +33 -74
- package/dist/client/index8.js +95 -55
- package/dist/client/index9.js +75 -96
- package/dist/core/attack-profile.d.ts +9 -0
- package/dist/core/attack-runtime.d.ts +20 -0
- package/dist/core/enemy-attack-profiles.d.ts +6 -0
- package/dist/core/equipment.d.ts +2 -0
- package/dist/core/hit-reaction.d.ts +5 -0
- package/dist/index.d.ts +6 -1
- package/dist/server/index.js +12 -7
- package/dist/server/index10.js +1278 -8
- package/dist/server/index11.js +37 -0
- package/dist/server/index12.js +60 -0
- package/dist/server/index13.js +13 -0
- package/dist/server/index14.js +503 -0
- package/dist/server/index15.js +10 -0
- package/dist/server/index3.js +25 -87
- package/dist/server/index4.js +45 -141
- package/dist/server/index5.js +104 -21
- package/dist/server/index6.js +137 -1215
- package/dist/server/index7.js +22 -34
- package/dist/server/index8.js +70 -44
- package/dist/server/index9.js +44 -437
- package/dist/server.d.ts +7 -1
- package/package.json +5 -5
- package/src/ai.server.ts +172 -43
- package/src/client.ts +21 -12
- package/src/config.ts +17 -2
- package/src/core/attack-profile.spec.ts +118 -0
- package/src/core/attack-profile.ts +100 -0
- package/src/core/attack-runtime.spec.ts +103 -0
- package/src/core/attack-runtime.ts +83 -0
- package/src/core/contracts.ts +3 -0
- package/src/core/enemy-attack-profiles.spec.ts +35 -0
- package/src/core/enemy-attack-profiles.ts +103 -0
- package/src/core/equipment.spec.ts +37 -0
- package/src/core/equipment.ts +17 -0
- package/src/core/hit-reaction.spec.ts +43 -0
- package/src/core/hit-reaction.ts +70 -0
- package/src/core/hit.spec.ts +54 -1
- package/src/core/hit.ts +26 -0
- package/src/index.ts +36 -0
- package/src/server.ts +180 -33
- package/src/types.ts +62 -6
package/dist/client/index6.js
CHANGED
|
@@ -1,101 +1,307 @@
|
|
|
1
|
-
import { actionBattleTargetingState, actionBattleUiOptions } from "./
|
|
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 {
|
|
5
|
-
//#region src/components/
|
|
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
|
|
13
|
+
const defineProps = useDefineProps($$props);
|
|
9
14
|
const engine = inject(RpgClientEngine);
|
|
10
|
-
const
|
|
11
|
-
|
|
12
|
-
|
|
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
|
|
16
|
-
|
|
17
|
-
|
|
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
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
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
|
|
34
|
-
const
|
|
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
|
|
136
|
+
const tileWidth = hitbox.w || 32;
|
|
137
|
+
const tileHeight = hitbox.h || 32;
|
|
39
138
|
return {
|
|
40
|
-
x: Math.floor((
|
|
41
|
-
y: Math.floor((
|
|
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
|
|
45
|
-
|
|
46
|
-
return
|
|
47
|
-
|
|
48
|
-
|
|
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
|
-
|
|
58
|
-
|
|
59
|
-
|
|
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
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
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
|
-
|
|
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 };
|
package/dist/client/index7.js
CHANGED
|
@@ -1,78 +1,37 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
const
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
const
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
23
|
+
if (cells.length === 0) cells.push({
|
|
24
|
+
dx: 0,
|
|
25
|
+
dy: 0
|
|
27
26
|
});
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
-
|
|
76
|
-
|
|
34
|
+
};
|
|
35
|
+
var manhattanDistance = (a, b) => Math.abs(a.x - b.x) + Math.abs(a.y - b.y);
|
|
77
36
|
//#endregion
|
|
78
|
-
export {
|
|
37
|
+
export { manhattanDistance, parseAoeMask };
|
package/dist/client/index8.js
CHANGED
|
@@ -1,61 +1,101 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
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
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
44
|
+
const toColor = (value, fallback) => {
|
|
45
|
+
if (typeof value === "number") return value;
|
|
46
|
+
return fallback;
|
|
21
47
|
};
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
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 {
|
|
101
|
+
export { component as default };
|