@rpgjs/action-battle 5.0.0-beta.2 → 5.0.0-beta.3
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/dist/client/index.js +15 -31
- package/dist/client/index10.js +375 -29
- package/dist/client/index2.js +63 -46
- package/dist/client/index3.js +50 -1329
- package/dist/client/index4.js +301 -344
- package/dist/client/index5.js +36 -291
- package/dist/client/index6.js +99 -95
- package/dist/client/index7.js +33 -60
- package/dist/client/index8.js +37 -65
- package/dist/client/index9.js +1168 -62
- package/dist/client.d.ts +3 -2
- package/dist/index.d.ts +3 -3
- package/dist/server/index.js +14 -31
- package/dist/server/index2.js +39 -345
- package/dist/server/index3.js +62 -1329
- package/dist/server/index4.js +1167 -67
- package/dist/server/index5.js +35 -28
- package/dist/server/index6.js +372 -60
- package/dist/server.d.ts +3 -3
- package/dist/ui/state.d.ts +3 -3
- package/package.json +5 -5
package/dist/client/index4.js
CHANGED
|
@@ -1,346 +1,303 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
down: { offsetX: -16, offsetY: 16, width: 32, height: 32 },
|
|
11
|
-
left: { offsetX: -48, offsetY: -16, width: 32, height: 32 },
|
|
12
|
-
right: { offsetX: 16, offsetY: -16, width: 32, height: 32 },
|
|
13
|
-
default: { offsetX: 0, offsetY: -32, width: 32, height: 32 }
|
|
14
|
-
};
|
|
15
|
-
function getPlayerWeaponKnockbackForce(player) {
|
|
16
|
-
try {
|
|
17
|
-
const equipments = player.equipments?.() || [];
|
|
18
|
-
for (const item of equipments) {
|
|
19
|
-
const itemData = player.databaseById?.(item.id());
|
|
20
|
-
if (itemData?._type === "weapon" && itemData.knockbackForce !== void 0) {
|
|
21
|
-
return itemData.knockbackForce;
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
} catch {
|
|
25
|
-
}
|
|
26
|
-
return DEFAULT_KNOCKBACK.force;
|
|
1
|
+
import { actionBattleTargetingState, actionBattleUiOptions, moveTargetingOffset, startTargeting, stopTargeting } from "./index3.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" && !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);
|
|
27
10
|
}
|
|
28
|
-
function
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
11
|
+
function component($$props) {
|
|
12
|
+
useProps($$props);
|
|
13
|
+
const defineProps = useDefineProps($$props);
|
|
14
|
+
const engine = inject(RpgClientEngine);
|
|
15
|
+
const keyboardControls = engine.globalConfig.keyboardControls;
|
|
16
|
+
const { data, onInteraction, onBack } = defineProps();
|
|
17
|
+
const currentPlayer = engine.getCurrentPlayer();
|
|
18
|
+
const ACTION_BAR_SIZE = 10;
|
|
19
|
+
const SLOT_LABELS = [
|
|
20
|
+
"1",
|
|
21
|
+
"2",
|
|
22
|
+
"3",
|
|
23
|
+
"4",
|
|
24
|
+
"5",
|
|
25
|
+
"6",
|
|
26
|
+
"7",
|
|
27
|
+
"8",
|
|
28
|
+
"9",
|
|
29
|
+
"0"
|
|
30
|
+
];
|
|
31
|
+
const SLOT_CONFIG_KEYS = [
|
|
32
|
+
"hotbar1",
|
|
33
|
+
"hotbar2",
|
|
34
|
+
"hotbar3",
|
|
35
|
+
"hotbar4",
|
|
36
|
+
"hotbar5",
|
|
37
|
+
"hotbar6",
|
|
38
|
+
"hotbar7",
|
|
39
|
+
"hotbar8",
|
|
40
|
+
"hotbar9",
|
|
41
|
+
"hotbar0"
|
|
42
|
+
];
|
|
43
|
+
const selectedSlotIndex = signal(-1);
|
|
44
|
+
const uiOptions = computed(() => actionBattleUiOptions());
|
|
45
|
+
const showItems = computed(() => {
|
|
46
|
+
const mode = uiOptions().actionBar?.mode || "both";
|
|
47
|
+
return mode === "items" || mode === "both";
|
|
48
|
+
});
|
|
49
|
+
const showSkills = computed(() => {
|
|
50
|
+
const mode = uiOptions().actionBar?.mode || "both";
|
|
51
|
+
return mode === "skills" || mode === "both";
|
|
52
|
+
});
|
|
53
|
+
const isTargeting = computed(() => actionBattleTargetingState().active);
|
|
54
|
+
const iconSheet = (iconId) => ({
|
|
55
|
+
definition: engine.getSpriteSheet(iconId),
|
|
56
|
+
playing: "default"
|
|
57
|
+
});
|
|
58
|
+
const actionBarSlots = computed(() => {
|
|
59
|
+
const entries = [];
|
|
60
|
+
if (showSkills()) currentPlayer.skills().forEach((skill, index) => {
|
|
61
|
+
entries.push({
|
|
62
|
+
type: "skill",
|
|
63
|
+
skill,
|
|
64
|
+
item: null,
|
|
65
|
+
sourceIndex: index
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
if (showItems()) currentPlayer.items().forEach((item, index) => {
|
|
69
|
+
entries.push({
|
|
70
|
+
type: "item",
|
|
71
|
+
skill: null,
|
|
72
|
+
item,
|
|
73
|
+
sourceIndex: index
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
const slots = entries.slice(0, ACTION_BAR_SIZE).map((entry, index) => ({
|
|
77
|
+
...entry,
|
|
78
|
+
label: SLOT_LABELS[index]
|
|
79
|
+
}));
|
|
80
|
+
while (slots.length < ACTION_BAR_SIZE) slots.push({
|
|
81
|
+
type: "empty",
|
|
82
|
+
skill: null,
|
|
83
|
+
item: null,
|
|
84
|
+
sourceIndex: -1,
|
|
85
|
+
label: SLOT_LABELS[slots.length]
|
|
86
|
+
});
|
|
87
|
+
return slots;
|
|
88
|
+
});
|
|
89
|
+
const hasSlotEntry = (slot) => slot.type === "skill" || slot.type === "item";
|
|
90
|
+
const isSlotDisabled = (slot) => {
|
|
91
|
+
if (!hasSlotEntry(slot)) return true;
|
|
92
|
+
const entry = slot.type === "skill" ? slot.skill : slot.item;
|
|
93
|
+
return !entry || !entry.usable;
|
|
94
|
+
};
|
|
95
|
+
const getItemQuantity = (item) => {
|
|
96
|
+
if (!item) return 0;
|
|
97
|
+
if (typeof item.quantity === "function") return item.quantity();
|
|
98
|
+
return item.quantity || 0;
|
|
99
|
+
};
|
|
100
|
+
const selectItem = (item) => {
|
|
101
|
+
if (!item || !item.usable) return;
|
|
102
|
+
if (onInteraction) onInteraction("useItem", { id: item.id });
|
|
103
|
+
};
|
|
104
|
+
const selectSkill = (skill) => {
|
|
105
|
+
if (!skill || !skill.usable) return;
|
|
106
|
+
if ((skill.range ?? 0) > 0 || skill.aoeMask) {
|
|
107
|
+
startTargeting(skill);
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
if (onInteraction) onInteraction("useSkill", { id: skill.id });
|
|
111
|
+
};
|
|
112
|
+
const selectSlot = (index) => {
|
|
113
|
+
const slot = actionBarSlots()[index];
|
|
114
|
+
if (!slot || !hasSlotEntry(slot)) return;
|
|
115
|
+
if (slot.type === "skill") {
|
|
116
|
+
selectSkill(slot.skill);
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
selectItem(slot.item);
|
|
120
|
+
};
|
|
121
|
+
const onSelectSlot = (index) => () => {
|
|
122
|
+
selectedSlotIndex.set(index);
|
|
123
|
+
selectSlot(index);
|
|
124
|
+
};
|
|
125
|
+
const getPlayerTile = () => {
|
|
126
|
+
const player = engine.scene.getCurrentPlayer();
|
|
127
|
+
if (!player) return null;
|
|
128
|
+
const hitbox = player.hitbox?.() || {
|
|
129
|
+
w: 32,
|
|
130
|
+
h: 32
|
|
131
|
+
};
|
|
132
|
+
const tileWidth = hitbox.w || 32;
|
|
133
|
+
const tileHeight = hitbox.h || 32;
|
|
134
|
+
return {
|
|
135
|
+
x: Math.floor((player.x() + hitbox.w / 2) / tileWidth),
|
|
136
|
+
y: Math.floor((player.y() + hitbox.h / 2) / tileHeight)
|
|
137
|
+
};
|
|
138
|
+
};
|
|
139
|
+
const confirmTargeting = () => {
|
|
140
|
+
const state = actionBattleTargetingState();
|
|
141
|
+
if (!state.skill) return;
|
|
142
|
+
const origin = getPlayerTile();
|
|
143
|
+
if (!origin) return;
|
|
144
|
+
const target = {
|
|
145
|
+
x: origin.x + state.offset.x,
|
|
146
|
+
y: origin.y + state.offset.y
|
|
147
|
+
};
|
|
148
|
+
if (onInteraction) onInteraction("useSkill", {
|
|
149
|
+
id: state.skill.id,
|
|
150
|
+
target
|
|
151
|
+
});
|
|
152
|
+
stopTargeting();
|
|
153
|
+
};
|
|
154
|
+
const resolveKeyBind = (key) => {
|
|
155
|
+
if (!key) return null;
|
|
156
|
+
if (typeof key === "string" && keyboardControls?.[key]) return keyboardControls[key];
|
|
157
|
+
return key;
|
|
158
|
+
};
|
|
159
|
+
const slotBind = (index) => keyboardControls?.[SLOT_CONFIG_KEYS[index]] || SLOT_LABELS[index];
|
|
160
|
+
const buildControls = () => {
|
|
161
|
+
const hotbarControls = {};
|
|
162
|
+
actionBarSlots().forEach((slot, index) => {
|
|
163
|
+
if (!hasSlotEntry(slot)) return;
|
|
164
|
+
const bind = slotBind(index);
|
|
165
|
+
hotbarControls[`slot-${index}`] = {
|
|
166
|
+
bind,
|
|
167
|
+
keyDown() {
|
|
168
|
+
if (isTargeting()) return;
|
|
169
|
+
selectedSlotIndex.set(index);
|
|
170
|
+
selectSlot(index);
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
if (slot.type === "skill") {
|
|
174
|
+
const skillBind = resolveKeyBind(slot.skill?.key);
|
|
175
|
+
if (!skillBind || skillBind === bind) return;
|
|
176
|
+
hotbarControls[`skill-${index}`] = {
|
|
177
|
+
bind: skillBind,
|
|
178
|
+
keyDown() {
|
|
179
|
+
if (isTargeting()) return;
|
|
180
|
+
selectedSlotIndex.set(index);
|
|
181
|
+
selectSlot(index);
|
|
182
|
+
}
|
|
183
|
+
};
|
|
184
|
+
}
|
|
185
|
+
});
|
|
186
|
+
return {
|
|
187
|
+
left: {
|
|
188
|
+
repeat: true,
|
|
189
|
+
bind: keyboardControls.left,
|
|
190
|
+
throttle: 150,
|
|
191
|
+
keyDown() {
|
|
192
|
+
if (isTargeting()) moveTargetingOffset(-1, 0);
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
right: {
|
|
196
|
+
repeat: true,
|
|
197
|
+
bind: keyboardControls.right,
|
|
198
|
+
throttle: 150,
|
|
199
|
+
keyDown() {
|
|
200
|
+
if (isTargeting()) moveTargetingOffset(1, 0);
|
|
201
|
+
}
|
|
202
|
+
},
|
|
203
|
+
up: {
|
|
204
|
+
repeat: true,
|
|
205
|
+
bind: keyboardControls.up,
|
|
206
|
+
throttle: 150,
|
|
207
|
+
keyDown() {
|
|
208
|
+
if (isTargeting()) moveTargetingOffset(0, -1);
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
down: {
|
|
212
|
+
repeat: true,
|
|
213
|
+
bind: keyboardControls.down,
|
|
214
|
+
throttle: 150,
|
|
215
|
+
keyDown() {
|
|
216
|
+
if (isTargeting()) moveTargetingOffset(0, 1);
|
|
217
|
+
}
|
|
218
|
+
},
|
|
219
|
+
action: {
|
|
220
|
+
bind: keyboardControls.action,
|
|
221
|
+
keyDown() {
|
|
222
|
+
if (isTargeting()) confirmTargeting();
|
|
223
|
+
}
|
|
224
|
+
},
|
|
225
|
+
escape: {
|
|
226
|
+
bind: keyboardControls.escape,
|
|
227
|
+
keyDown() {
|
|
228
|
+
if (isTargeting()) {
|
|
229
|
+
stopTargeting();
|
|
230
|
+
return;
|
|
231
|
+
}
|
|
232
|
+
if (onBack) onBack();
|
|
233
|
+
}
|
|
234
|
+
},
|
|
235
|
+
...hotbarControls,
|
|
236
|
+
gamepad: { enabled: true }
|
|
237
|
+
};
|
|
238
|
+
};
|
|
239
|
+
const controls = signal(buildControls());
|
|
240
|
+
effect(() => {
|
|
241
|
+
controls.set(buildControls());
|
|
242
|
+
});
|
|
243
|
+
return h(DOMContainer, {
|
|
244
|
+
width: "100%",
|
|
245
|
+
height: "100%",
|
|
246
|
+
attrs: { class: "action-battle-actionbar-root" },
|
|
247
|
+
controls
|
|
248
|
+
}, h(DOMElement, {
|
|
249
|
+
element: "div",
|
|
250
|
+
attrs: { class: "action-battle-actionbar" }
|
|
251
|
+
}, h(DOMElement, {
|
|
252
|
+
element: "div",
|
|
253
|
+
attrs: { class: "rpg-ui-hotbar action-battle-actionbar-plate" }
|
|
254
|
+
}, h(DOMElement, {
|
|
255
|
+
element: "div",
|
|
256
|
+
attrs: { class: "rpg-ui-hotbar-track action-battle-actionbar-track" }
|
|
257
|
+
}, loop(actionBarSlots, (slot, index) => h(DOMElement, {
|
|
258
|
+
element: "div",
|
|
259
|
+
attrs: {
|
|
260
|
+
class: "rpg-ui-hotbar-slot action-battle-actionbar-slot",
|
|
261
|
+
"data-selected": computed(() => selectedSlotIndex() === index ? "true" : "false"),
|
|
262
|
+
"data-disabled": computed(() => isSlotDisabled(slot) ? "true" : "false"),
|
|
263
|
+
"data-empty": computed(() => hasSlotEntry(slot) ? "false" : "true"),
|
|
264
|
+
"data-type": slot.type,
|
|
265
|
+
tabindex: computed(() => hasSlotEntry(slot) ? index : -1),
|
|
266
|
+
click: hasSlotEntry(slot) ? onSelectSlot(index) : void 0
|
|
267
|
+
}
|
|
268
|
+
}, [
|
|
269
|
+
h(DOMElement, {
|
|
270
|
+
element: "span",
|
|
271
|
+
attrs: { class: "rpg-ui-hotbar-key action-battle-actionbar-key" },
|
|
272
|
+
textContent: slot.label
|
|
273
|
+
}),
|
|
274
|
+
cond(computed(() => slot.type === "skill" && slot.skill?.icon), () => h(DOMSprite, {
|
|
275
|
+
sheet: computed(() => iconSheet(slot.skill.icon)),
|
|
276
|
+
width: "60px",
|
|
277
|
+
height: "60px",
|
|
278
|
+
scale: 2,
|
|
279
|
+
objectFit: "contain"
|
|
280
|
+
}), [computed(() => slot.type === "item" && slot.item?.icon), () => h(DOMSprite, {
|
|
281
|
+
sheet: computed(() => iconSheet(slot.item.icon)),
|
|
282
|
+
width: "60px",
|
|
283
|
+
height: "60px",
|
|
284
|
+
scale: 2,
|
|
285
|
+
objectFit: "contain"
|
|
286
|
+
})], [computed(() => slot.type === "skill" && slot.skill), () => h(DOMElement, {
|
|
287
|
+
element: "span",
|
|
288
|
+
attrs: { class: "rpg-ui-hotbar-text action-battle-actionbar-text" },
|
|
289
|
+
textContent: slot.skill.name
|
|
290
|
+
})], [computed(() => slot.type === "item" && slot.item), () => h(DOMElement, {
|
|
291
|
+
element: "span",
|
|
292
|
+
attrs: { class: "rpg-ui-hotbar-text action-battle-actionbar-text" },
|
|
293
|
+
textContent: slot.item.name
|
|
294
|
+
})]),
|
|
295
|
+
cond(computed(() => slot.type === "item" && slot.item), () => h(DOMElement, {
|
|
296
|
+
element: "span",
|
|
297
|
+
attrs: { class: "rpg-ui-hotbar-count action-battle-actionbar-count" },
|
|
298
|
+
textContent: "x" + getItemQuantity(slot.item)
|
|
299
|
+
}))
|
|
300
|
+
]))))));
|
|
62
301
|
}
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
try {
|
|
66
|
-
return player.databaseById?.(itemId);
|
|
67
|
-
} catch {
|
|
68
|
-
return null;
|
|
69
|
-
}
|
|
70
|
-
};
|
|
71
|
-
const resolveSkillData = (player, skillId) => {
|
|
72
|
-
try {
|
|
73
|
-
return player.databaseById?.(skillId);
|
|
74
|
-
} catch {
|
|
75
|
-
return null;
|
|
76
|
-
}
|
|
77
|
-
};
|
|
78
|
-
const resolveSkillTargeting = (player, skillId, options) => {
|
|
79
|
-
const skillsOptions = options.skills;
|
|
80
|
-
const skillData = resolveSkillData(player, skillId);
|
|
81
|
-
if (skillsOptions?.getTargeting) {
|
|
82
|
-
return skillsOptions.getTargeting(skillData);
|
|
83
|
-
}
|
|
84
|
-
const range = skillData?.range ?? skillData?.targeting?.range ?? skillData?.targeting?.distance;
|
|
85
|
-
const aoeMask = skillData?.aoeMask ?? skillData?.targeting?.aoeMask ?? skillData?.targeting?.mask;
|
|
86
|
-
if (range === void 0 && aoeMask === void 0) {
|
|
87
|
-
return null;
|
|
88
|
-
}
|
|
89
|
-
return {
|
|
90
|
-
range: range ?? 0,
|
|
91
|
-
aoeMask
|
|
92
|
-
};
|
|
93
|
-
};
|
|
94
|
-
const normalizeMaskRows = (mask) => {
|
|
95
|
-
if (!mask) return [];
|
|
96
|
-
if (Array.isArray(mask)) return mask;
|
|
97
|
-
return mask.trim().split("\n").map((row) => row.replace(/\r/g, ""));
|
|
98
|
-
};
|
|
99
|
-
const buildActionBarData = (player, options) => {
|
|
100
|
-
const items = (player.items?.() || []).map((item) => {
|
|
101
|
-
const id = item.id?.() ?? item.id;
|
|
102
|
-
const data = resolveItemData(player, id);
|
|
103
|
-
const name = resolveSignal(data?.name) ?? resolveSignal(item.name) ?? id;
|
|
104
|
-
const description = resolveSignal(data?.description) ?? resolveSignal(item.description) ?? "";
|
|
105
|
-
const icon = resolveSignal(data?.icon) ?? resolveSignal(item.icon);
|
|
106
|
-
const quantity = resolveSignal(item.quantity) ?? 1;
|
|
107
|
-
const consumable = resolveSignal(data?.consumable);
|
|
108
|
-
const itemType = resolveSignal(data?._type);
|
|
109
|
-
const usable = quantity > 0 && consumable !== false && (itemType ? itemType === "item" : true);
|
|
110
|
-
return {
|
|
111
|
-
id,
|
|
112
|
-
name,
|
|
113
|
-
description,
|
|
114
|
-
icon,
|
|
115
|
-
quantity,
|
|
116
|
-
usable
|
|
117
|
-
};
|
|
118
|
-
});
|
|
119
|
-
const skills = (player.skills?.() || []).map((skill) => {
|
|
120
|
-
const id = skill.id?.() ?? skill.id;
|
|
121
|
-
const data = resolveSkillData(player, id) || skill;
|
|
122
|
-
const name = resolveSignal(data?.name) ?? resolveSignal(skill.name) ?? id;
|
|
123
|
-
const description = resolveSignal(data?.description) ?? resolveSignal(skill.description) ?? "";
|
|
124
|
-
const icon = resolveSignal(data?.icon) ?? resolveSignal(skill.icon);
|
|
125
|
-
const spCost = resolveSignal(data?.spCost) ?? resolveSignal(skill.spCost) ?? 0;
|
|
126
|
-
const usable = spCost <= player.sp;
|
|
127
|
-
const targeting = resolveSkillTargeting(player, id, options);
|
|
128
|
-
const skillEntry = {
|
|
129
|
-
id,
|
|
130
|
-
name,
|
|
131
|
-
description,
|
|
132
|
-
icon,
|
|
133
|
-
spCost,
|
|
134
|
-
usable,
|
|
135
|
-
range: targeting?.range ?? 0
|
|
136
|
-
};
|
|
137
|
-
if (targeting) {
|
|
138
|
-
const mask = targeting.aoeMask ?? options.skills?.defaultAoeMask;
|
|
139
|
-
if (mask) {
|
|
140
|
-
skillEntry.aoeMask = normalizeMaskRows(mask);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
return skillEntry;
|
|
144
|
-
});
|
|
145
|
-
return { items, skills };
|
|
146
|
-
};
|
|
147
|
-
const ensureActionBarGui = (player, options) => {
|
|
148
|
-
const existing = player.getGui?.(ACTION_BATTLE_ACTION_BAR_GUI_ID);
|
|
149
|
-
const gui = existing || player.gui(ACTION_BATTLE_ACTION_BAR_GUI_ID);
|
|
150
|
-
if (!gui.__actionBattleReady) {
|
|
151
|
-
gui.__actionBattleReady = true;
|
|
152
|
-
gui.on("useItem", ({ id }) => {
|
|
153
|
-
try {
|
|
154
|
-
player.useItem(id);
|
|
155
|
-
} catch {
|
|
156
|
-
}
|
|
157
|
-
gui.update(buildActionBarData(player, options));
|
|
158
|
-
});
|
|
159
|
-
gui.on("useSkill", ({ id, target }) => {
|
|
160
|
-
handleActionBattleSkillUse(player, id, target, options);
|
|
161
|
-
gui.update(buildActionBarData(player, options));
|
|
162
|
-
});
|
|
163
|
-
gui.on("refresh", () => {
|
|
164
|
-
gui.update(buildActionBarData(player, options));
|
|
165
|
-
});
|
|
166
|
-
}
|
|
167
|
-
return gui;
|
|
168
|
-
};
|
|
169
|
-
const openActionBattleActionBar = (player, rawOptions = {}) => {
|
|
170
|
-
const options = normalizeActionBattleOptions(rawOptions);
|
|
171
|
-
const gui = ensureActionBarGui(player, options);
|
|
172
|
-
gui.open(buildActionBarData(player, options));
|
|
173
|
-
};
|
|
174
|
-
const updateActionBattleActionBar = (player, rawOptions = {}) => {
|
|
175
|
-
const options = normalizeActionBattleOptions(rawOptions);
|
|
176
|
-
const gui = player.getGui?.(ACTION_BATTLE_ACTION_BAR_GUI_ID);
|
|
177
|
-
if (gui) {
|
|
178
|
-
gui.update(buildActionBarData(player, options));
|
|
179
|
-
}
|
|
180
|
-
};
|
|
181
|
-
const getTileSize = (map) => ({
|
|
182
|
-
width: map?.tileWidth ?? 32,
|
|
183
|
-
height: map?.tileHeight ?? 32
|
|
184
|
-
});
|
|
185
|
-
const getEntityTile = (entity, tileSize) => {
|
|
186
|
-
const hitbox = entity.hitbox?.() || { w: tileSize.width, h: tileSize.height };
|
|
187
|
-
const x = Math.floor((entity.x() + hitbox.w / 2) / tileSize.width);
|
|
188
|
-
const y = Math.floor((entity.y() + hitbox.h / 2) / tileSize.height);
|
|
189
|
-
return { x, y };
|
|
190
|
-
};
|
|
191
|
-
const handleActionBattleSkillUse = (player, skillId, target, options) => {
|
|
192
|
-
const skillData = resolveSkillData(player, skillId);
|
|
193
|
-
const map = player.getCurrentMap();
|
|
194
|
-
if (!map) {
|
|
195
|
-
playActionBattleAnimation("castSkill", player, options.animations, {
|
|
196
|
-
skill: skillData
|
|
197
|
-
});
|
|
198
|
-
player.useSkill(skillId);
|
|
199
|
-
return;
|
|
200
|
-
}
|
|
201
|
-
const targeting = resolveSkillTargeting(player, skillId, options);
|
|
202
|
-
if (!targeting || !target) {
|
|
203
|
-
playActionBattleAnimation("castSkill", player, options.animations, {
|
|
204
|
-
skill: skillData
|
|
205
|
-
});
|
|
206
|
-
player.useSkill(skillId);
|
|
207
|
-
return;
|
|
208
|
-
}
|
|
209
|
-
const tileSize = getTileSize(map);
|
|
210
|
-
const origin = getEntityTile(player, tileSize);
|
|
211
|
-
const targetTile = { x: target.x, y: target.y };
|
|
212
|
-
if (manhattanDistance(origin, targetTile) > targeting.range) {
|
|
213
|
-
return;
|
|
214
|
-
}
|
|
215
|
-
const mask = parseAoeMask(
|
|
216
|
-
targeting.aoeMask || options.skills?.defaultAoeMask
|
|
217
|
-
);
|
|
218
|
-
const affected = /* @__PURE__ */ new Set();
|
|
219
|
-
mask.cells.forEach((cell) => {
|
|
220
|
-
const x = targetTile.x + cell.dx;
|
|
221
|
-
const y = targetTile.y + cell.dy;
|
|
222
|
-
affected.add(`${x},${y}`);
|
|
223
|
-
});
|
|
224
|
-
const targets = [];
|
|
225
|
-
const affects = options.targeting?.affects || "events";
|
|
226
|
-
if (affects === "events" || affects === "both") {
|
|
227
|
-
map.getEvents().forEach((event) => {
|
|
228
|
-
const tile = getEntityTile(event, tileSize);
|
|
229
|
-
if (affected.has(`${tile.x},${tile.y}`)) {
|
|
230
|
-
targets.push(event);
|
|
231
|
-
}
|
|
232
|
-
});
|
|
233
|
-
}
|
|
234
|
-
if (affects === "players" || affects === "both") {
|
|
235
|
-
map.getPlayers().forEach((other) => {
|
|
236
|
-
if (other.id === player.id) return;
|
|
237
|
-
const tile = getEntityTile(other, tileSize);
|
|
238
|
-
if (affected.has(`${tile.x},${tile.y}`)) {
|
|
239
|
-
targets.push(other);
|
|
240
|
-
}
|
|
241
|
-
});
|
|
242
|
-
}
|
|
243
|
-
if (!options.targeting?.allowEmptyTarget && targets.length === 0) {
|
|
244
|
-
return;
|
|
245
|
-
}
|
|
246
|
-
playActionBattleAnimation("castSkill", player, options.animations, {
|
|
247
|
-
skill: skillData,
|
|
248
|
-
target: targets[0]
|
|
249
|
-
});
|
|
250
|
-
player.useSkill(skillId, targets);
|
|
251
|
-
};
|
|
252
|
-
const createActionBattleServer = (rawOptions = {}) => {
|
|
253
|
-
const options = normalizeActionBattleOptions(rawOptions);
|
|
254
|
-
setActionBattleOptions(options);
|
|
255
|
-
return defineModule({
|
|
256
|
-
player: {
|
|
257
|
-
/**
|
|
258
|
-
* Handle player input for combat actions
|
|
259
|
-
*
|
|
260
|
-
* When a player presses the action key, create an attack hitbox
|
|
261
|
-
* that can damage AI enemies within range and knockback the event.
|
|
262
|
-
* Knockback force is based on the player's equipped weapon.
|
|
263
|
-
* Triggers attack animation and visual effects.
|
|
264
|
-
*
|
|
265
|
-
* @param player - The player performing the action
|
|
266
|
-
* @param input - Input data containing pressed keys
|
|
267
|
-
*/
|
|
268
|
-
onInput(player, input) {
|
|
269
|
-
if (input.action == Control.Action) {
|
|
270
|
-
playActionBattleAnimation("attack", player, options.animations);
|
|
271
|
-
const playerX = player.x();
|
|
272
|
-
const playerY = player.y();
|
|
273
|
-
const direction = player.getDirection();
|
|
274
|
-
const directionKey = direction;
|
|
275
|
-
const hitboxConfig = DEFAULT_PLAYER_ATTACK_HITBOXES[directionKey] || DEFAULT_PLAYER_ATTACK_HITBOXES.default;
|
|
276
|
-
const hitboxes = [
|
|
277
|
-
{
|
|
278
|
-
x: playerX + hitboxConfig.offsetX,
|
|
279
|
-
y: playerY + hitboxConfig.offsetY,
|
|
280
|
-
width: hitboxConfig.width,
|
|
281
|
-
height: hitboxConfig.height
|
|
282
|
-
}
|
|
283
|
-
];
|
|
284
|
-
const map = player.getCurrentMap();
|
|
285
|
-
map?.createMovingHitbox(hitboxes, { speed: 3 }).subscribe({
|
|
286
|
-
next(hits) {
|
|
287
|
-
hits.forEach((hit) => {
|
|
288
|
-
if (hit instanceof RpgEvent) {
|
|
289
|
-
const result = applyPlayerHitToEvent(player, hit);
|
|
290
|
-
if (result?.defeated) {
|
|
291
|
-
console.log(`Player ${player.id} defeated AI ${hit.id}`);
|
|
292
|
-
}
|
|
293
|
-
}
|
|
294
|
-
});
|
|
295
|
-
}
|
|
296
|
-
});
|
|
297
|
-
}
|
|
298
|
-
},
|
|
299
|
-
onConnected(player) {
|
|
300
|
-
if (options.ui?.actionBar?.enabled && options.ui?.actionBar?.autoOpen) {
|
|
301
|
-
openActionBattleActionBar(player, options);
|
|
302
|
-
}
|
|
303
|
-
}
|
|
304
|
-
},
|
|
305
|
-
event: {
|
|
306
|
-
/**
|
|
307
|
-
* Handle player detection when entering AI vision
|
|
308
|
-
*
|
|
309
|
-
* Called when a player enters an AI event's vision range.
|
|
310
|
-
* The AI will start pursuing and attacking the player.
|
|
311
|
-
*
|
|
312
|
-
* @param event - The AI event
|
|
313
|
-
* @param player - The player entering vision
|
|
314
|
-
* @param shape - The vision shape
|
|
315
|
-
*/
|
|
316
|
-
onDetectInShape(event, player, shape) {
|
|
317
|
-
const ai = event.battleAi;
|
|
318
|
-
ai?.onDetectInShape(player, shape);
|
|
319
|
-
},
|
|
320
|
-
/**
|
|
321
|
-
* Handle player leaving AI vision
|
|
322
|
-
*
|
|
323
|
-
* Called when a player leaves an AI event's vision range.
|
|
324
|
-
* The AI will stop pursuing the player.
|
|
325
|
-
*
|
|
326
|
-
* @param event - The AI event
|
|
327
|
-
* @param player - The player leaving vision
|
|
328
|
-
* @param shape - The vision shape
|
|
329
|
-
*/
|
|
330
|
-
onDetectOutShape(event, player, shape) {
|
|
331
|
-
const ai = event.battleAi;
|
|
332
|
-
ai?.onDetectOutShape(player, shape);
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
});
|
|
336
|
-
};
|
|
337
|
-
createActionBattleServer();
|
|
338
|
-
export {
|
|
339
|
-
ACTION_BATTLE_ACTION_BAR_GUI_ID,
|
|
340
|
-
DEFAULT_PLAYER_ATTACK_HITBOXES,
|
|
341
|
-
applyPlayerHitToEvent,
|
|
342
|
-
createActionBattleServer,
|
|
343
|
-
getPlayerWeaponKnockbackForce,
|
|
344
|
-
openActionBattleActionBar,
|
|
345
|
-
updateActionBattleActionBar
|
|
346
|
-
};
|
|
302
|
+
//#endregion
|
|
303
|
+
export { component as default };
|