@rpgjs/server 5.0.0-alpha.8 → 5.0.0-beta.1
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/Gui/DialogGui.d.ts +5 -0
- package/dist/Gui/GameoverGui.d.ts +23 -0
- package/dist/Gui/Gui.d.ts +6 -0
- package/dist/Gui/MenuGui.d.ts +22 -3
- package/dist/Gui/NotificationGui.d.ts +1 -2
- package/dist/Gui/SaveLoadGui.d.ts +13 -0
- package/dist/Gui/ShopGui.d.ts +28 -3
- package/dist/Gui/TitleGui.d.ts +23 -0
- package/dist/Gui/index.d.ts +10 -1
- package/dist/Player/BattleManager.d.ts +44 -32
- package/dist/Player/ClassManager.d.ts +24 -4
- package/dist/Player/ComponentManager.d.ts +100 -7
- package/dist/Player/Components.d.ts +345 -0
- package/dist/Player/EffectManager.d.ts +50 -4
- package/dist/Player/ElementManager.d.ts +77 -4
- package/dist/Player/GoldManager.d.ts +1 -1
- package/dist/Player/GuiManager.d.ts +233 -5
- package/dist/Player/ItemFixture.d.ts +1 -1
- package/dist/Player/ItemManager.d.ts +431 -4
- package/dist/Player/MoveManager.d.ts +301 -34
- package/dist/Player/ParameterManager.d.ts +364 -28
- package/dist/Player/Player.d.ts +558 -14
- package/dist/Player/SkillManager.d.ts +187 -13
- package/dist/Player/StateManager.d.ts +75 -4
- package/dist/Player/VariableManager.d.ts +62 -4
- package/dist/RpgServer.d.ts +278 -63
- package/dist/RpgServerEngine.d.ts +2 -1
- package/dist/decorators/event.d.ts +46 -0
- package/dist/decorators/map.d.ts +299 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +17920 -29866
- package/dist/index.js.map +1 -1
- package/dist/logs/log.d.ts +2 -3
- package/dist/module-CaCW1SDh.js +11018 -0
- package/dist/module-CaCW1SDh.js.map +1 -0
- package/dist/module.d.ts +43 -1
- package/dist/node/connection.d.ts +51 -0
- package/dist/node/index.d.ts +5 -0
- package/dist/node/index.js +551 -0
- package/dist/node/index.js.map +1 -0
- package/dist/node/map.d.ts +16 -0
- package/dist/node/room.d.ts +21 -0
- package/dist/node/transport.d.ts +28 -0
- package/dist/node/types.d.ts +47 -0
- package/dist/presets/index.d.ts +0 -9
- package/dist/rooms/BaseRoom.d.ts +132 -0
- package/dist/rooms/lobby.d.ts +10 -2
- package/dist/rooms/map.d.ts +1359 -32
- package/dist/services/save.d.ts +43 -0
- package/dist/storage/index.d.ts +1 -0
- package/dist/storage/localStorage.d.ts +23 -0
- package/package.json +25 -10
- package/src/Gui/DialogGui.ts +19 -4
- package/src/Gui/GameoverGui.ts +39 -0
- package/src/Gui/Gui.ts +23 -1
- package/src/Gui/MenuGui.ts +155 -6
- package/src/Gui/NotificationGui.ts +1 -2
- package/src/Gui/SaveLoadGui.ts +60 -0
- package/src/Gui/ShopGui.ts +146 -16
- package/src/Gui/TitleGui.ts +39 -0
- package/src/Gui/index.ts +15 -2
- package/src/Player/BattleManager.ts +39 -56
- package/src/Player/ClassManager.ts +82 -74
- package/src/Player/ComponentManager.ts +401 -37
- package/src/Player/Components.ts +380 -0
- package/src/Player/EffectManager.ts +50 -96
- package/src/Player/ElementManager.ts +74 -152
- package/src/Player/GuiManager.ts +284 -149
- package/src/Player/ItemManager.ts +747 -341
- package/src/Player/MoveManager.ts +1532 -750
- package/src/Player/ParameterManager.ts +636 -106
- package/src/Player/Player.ts +1273 -79
- package/src/Player/SkillManager.ts +558 -197
- package/src/Player/StateManager.ts +131 -258
- package/src/Player/VariableManager.ts +85 -157
- package/src/RpgServer.ts +293 -62
- package/src/decorators/event.ts +61 -0
- package/src/decorators/map.ts +343 -0
- package/src/index.ts +11 -1
- package/src/logs/log.ts +10 -3
- package/src/module.ts +126 -3
- package/src/node/connection.ts +254 -0
- package/src/node/index.ts +22 -0
- package/src/node/map.ts +328 -0
- package/src/node/room.ts +63 -0
- package/src/node/transport.ts +532 -0
- package/src/node/types.ts +61 -0
- package/src/presets/index.ts +1 -10
- package/src/rooms/BaseRoom.ts +232 -0
- package/src/rooms/lobby.ts +25 -7
- package/src/rooms/map.ts +2682 -206
- package/src/services/save.ts +147 -0
- package/src/storage/index.ts +1 -0
- package/src/storage/localStorage.ts +76 -0
- package/tests/battle.spec.ts +375 -0
- package/tests/change-map.spec.ts +72 -0
- package/tests/class.spec.ts +274 -0
- package/tests/custom-websocket.spec.ts +127 -0
- package/tests/effect.spec.ts +219 -0
- package/tests/element.spec.ts +221 -0
- package/tests/event.spec.ts +80 -0
- package/tests/gold.spec.ts +99 -0
- package/tests/item.spec.ts +609 -0
- package/tests/module.spec.ts +38 -0
- package/tests/move.spec.ts +601 -0
- package/tests/node-transport.spec.ts +223 -0
- package/tests/player-param.spec.ts +45 -0
- package/tests/prediction-reconciliation.spec.ts +182 -0
- package/tests/random-move.spec.ts +65 -0
- package/tests/skill.spec.ts +658 -0
- package/tests/state.spec.ts +467 -0
- package/tests/variable.spec.ts +185 -0
- package/tests/world-maps.spec.ts +896 -0
- package/vite.config.ts +36 -3
- package/dist/Player/Event.d.ts +0 -0
- package/src/Player/Event.ts +0 -0
package/src/Gui/ShopGui.ts
CHANGED
|
@@ -1,43 +1,173 @@
|
|
|
1
1
|
import { PrebuiltGui } from '@rpgjs/common'
|
|
2
2
|
import { Gui } from './Gui'
|
|
3
3
|
import { RpgPlayer } from '../Player/Player'
|
|
4
|
-
import { IGui } from '../Interfaces/Gui'
|
|
5
4
|
|
|
6
|
-
export
|
|
5
|
+
export type ShopSellList = Record<string, number> | Array<{ id: string; multiplier: number }>
|
|
6
|
+
export type ShopItemInput = string | { id?: string; [key: string]: any }
|
|
7
|
+
|
|
8
|
+
export interface ShopGuiOptions {
|
|
9
|
+
items: ShopItemInput[]
|
|
10
|
+
sell?: ShopSellList
|
|
11
|
+
sellMultiplier?: number
|
|
12
|
+
message?: string
|
|
13
|
+
face?: {
|
|
14
|
+
id: string
|
|
15
|
+
expression?: string
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export class ShopGui extends Gui {
|
|
20
|
+
private itemsInput: ShopItemInput[] = []
|
|
21
|
+
private sellMultipliers: Record<string, number> = {}
|
|
22
|
+
private baseSellMultiplier = 0.5
|
|
23
|
+
private messageInput?: string
|
|
24
|
+
private faceInput?: { id: string; expression?: string }
|
|
25
|
+
|
|
7
26
|
constructor(player: RpgPlayer) {
|
|
8
27
|
super(PrebuiltGui.Shop, player)
|
|
9
28
|
}
|
|
10
29
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
30
|
+
private normalizeSellMultipliers(sell?: ShopSellList) {
|
|
31
|
+
if (!sell) return {}
|
|
32
|
+
if (Array.isArray(sell)) {
|
|
33
|
+
return sell.reduce<Record<string, number>>((acc, entry) => {
|
|
34
|
+
if (entry && entry.id) acc[entry.id] = entry.multiplier ?? 0
|
|
35
|
+
return acc
|
|
36
|
+
}, {})
|
|
37
|
+
}
|
|
38
|
+
return { ...sell }
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
private buildShopData() {
|
|
42
|
+
const player = this.player as any
|
|
43
|
+
const databaseById = player.databaseById?.bind(player)
|
|
44
|
+
const equippedIds = new Set(
|
|
45
|
+
(player.equipments?.() || []).map((it) => it?.id?.() ?? it?.id ?? it?.name)
|
|
46
|
+
)
|
|
47
|
+
const playerParams = {
|
|
48
|
+
...(player.param || {}),
|
|
49
|
+
atk: player.atk ?? 0,
|
|
50
|
+
def: player.pdef ?? 0,
|
|
51
|
+
pdef: player.pdef ?? 0,
|
|
52
|
+
sdef: player.sdef ?? 0
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const getStatValue = (data, key, fallbackKeys: string[] = []) => {
|
|
56
|
+
if (data && typeof data[key] === 'number') return data[key]
|
|
57
|
+
for (const fallbackKey of fallbackKeys) {
|
|
58
|
+
if (data && typeof data[fallbackKey] === 'number') return data[fallbackKey]
|
|
59
|
+
}
|
|
60
|
+
const modifier = data?.paramsModifier?.[key]
|
|
61
|
+
if (modifier && typeof modifier.value === 'number') return modifier.value
|
|
62
|
+
for (const fallbackKey of fallbackKeys) {
|
|
63
|
+
const fallbackModifier = data?.paramsModifier?.[fallbackKey]
|
|
64
|
+
if (fallbackModifier && typeof fallbackModifier.value === 'number') return fallbackModifier.value
|
|
65
|
+
}
|
|
66
|
+
return undefined
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const buildItemData = (item: ShopItemInput, overrides: { price?: number; quantity?: number } = {}) => {
|
|
70
|
+
const rawId = typeof item === 'string'
|
|
71
|
+
? item
|
|
72
|
+
: (typeof item?.id === 'function' ? item.id() : (item?.id ?? item?.name))
|
|
73
|
+
const data = databaseById(rawId)
|
|
74
|
+
const itemName = typeof item?.name === 'function' ? item.name() : item?.name
|
|
75
|
+
const itemDescription = typeof item?.description === 'function' ? item.description() : item?.description
|
|
76
|
+
const itemPrice = typeof item?.price === 'function' ? item.price() : item?.price
|
|
77
|
+
const itemIcon = typeof item?.icon === 'function' ? item.icon() : item?.icon
|
|
78
|
+
const atk = getStatValue(data, 'atk')
|
|
79
|
+
const def = getStatValue(data, 'def', ['pdef', 'sdef'])
|
|
80
|
+
const intValue = getStatValue(data, 'int')
|
|
81
|
+
const agi = getStatValue(data, 'agi')
|
|
82
|
+
const stats = {
|
|
83
|
+
...(atk !== undefined ? { atk } : {}),
|
|
84
|
+
...(def !== undefined ? { def } : {}),
|
|
85
|
+
...(intValue !== undefined ? { int: intValue } : {}),
|
|
86
|
+
...(agi !== undefined ? { agi } : {})
|
|
87
|
+
}
|
|
14
88
|
return {
|
|
15
|
-
price:
|
|
16
|
-
name:
|
|
17
|
-
description:
|
|
18
|
-
|
|
19
|
-
|
|
89
|
+
price: overrides.price ?? data?.price ?? itemPrice ?? 0,
|
|
90
|
+
name: data?.name ?? itemName ?? rawId,
|
|
91
|
+
description: data?.description ?? itemDescription ?? '',
|
|
92
|
+
icon: data?.icon ?? itemIcon,
|
|
93
|
+
id: rawId,
|
|
94
|
+
type: data?._type ?? item.type ?? item?._type,
|
|
95
|
+
stats: Object.keys(stats).length ? stats : undefined,
|
|
96
|
+
equipped: rawId ? equippedIds.has(rawId) : false,
|
|
97
|
+
...(overrides.quantity !== undefined ? { quantity: overrides.quantity } : {})
|
|
20
98
|
}
|
|
21
|
-
}
|
|
22
|
-
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
const items = this.itemsInput.map(item => buildItemData(item))
|
|
102
|
+
|
|
103
|
+
const sellItems = (player.items?.() || [])
|
|
104
|
+
.map((item) => {
|
|
105
|
+
const id = item?.id?.()
|
|
106
|
+
if (!id) return null
|
|
107
|
+
const multiplier = Object.prototype.hasOwnProperty.call(this.sellMultipliers, id)
|
|
108
|
+
? this.sellMultipliers[id]
|
|
109
|
+
: this.baseSellMultiplier
|
|
110
|
+
const basePrice = databaseById(id)?.price ?? (typeof item?.price === 'function' ? item.price() : item?.price) ?? 0
|
|
111
|
+
const price = basePrice * multiplier
|
|
112
|
+
const quantity = item?.quantity?.()
|
|
113
|
+
return buildItemData(item, { price, quantity })
|
|
114
|
+
})
|
|
115
|
+
.filter(Boolean)
|
|
116
|
+
|
|
117
|
+
return { items, sellItems, playerParams, message: this.messageInput, face: this.faceInput }
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
private refreshShop(clientActionId?: string) {
|
|
121
|
+
this.update(this.buildShopData(), { clientActionId })
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
open(itemsOrOptions: any[] | ShopGuiOptions) {
|
|
125
|
+
const options: ShopGuiOptions = Array.isArray(itemsOrOptions)
|
|
126
|
+
? { items: itemsOrOptions }
|
|
127
|
+
: (itemsOrOptions || { items: [] })
|
|
128
|
+
this.itemsInput = options.items || []
|
|
129
|
+
this.baseSellMultiplier = typeof options.sellMultiplier === 'number' ? options.sellMultiplier : 0.5
|
|
130
|
+
this.sellMultipliers = this.normalizeSellMultipliers(options.sell)
|
|
131
|
+
this.messageInput = options.message
|
|
132
|
+
this.faceInput = options.face
|
|
133
|
+
this.on('buyItem', ({ id, nb, clientActionId }) => {
|
|
23
134
|
try {
|
|
24
135
|
this.player.buyItem(id, nb)
|
|
136
|
+
this.player.syncChanges()
|
|
25
137
|
}
|
|
26
138
|
catch (err) {
|
|
27
139
|
console.log(err)
|
|
28
140
|
}
|
|
141
|
+
finally {
|
|
142
|
+
this.refreshShop(clientActionId)
|
|
143
|
+
}
|
|
29
144
|
})
|
|
30
|
-
this.on('sellItem', ({ id, nb }) => {
|
|
145
|
+
this.on('sellItem', ({ id, nb, clientActionId }) => {
|
|
31
146
|
try {
|
|
32
|
-
|
|
147
|
+
const multiplier = Object.prototype.hasOwnProperty.call(this.sellMultipliers, id)
|
|
148
|
+
? this.sellMultipliers[id]
|
|
149
|
+
: this.baseSellMultiplier
|
|
150
|
+
const basePrice = (this.player as any).databaseById?.(id)?.price ?? (typeof (inventory as any)?.price === 'function' ? (inventory as any).price() : (inventory as any)?.price) ?? 0
|
|
151
|
+
const price = basePrice * multiplier
|
|
152
|
+
if (!basePrice || price <= 0) return
|
|
153
|
+
const inventory = (this.player as any).getItem?.(id)
|
|
154
|
+
if (!inventory) return
|
|
155
|
+
const quantity = inventory.quantity()
|
|
156
|
+
if (quantity - nb < 0) return
|
|
157
|
+
;(this.player as any)._gold.update((gold) => gold + price * nb)
|
|
158
|
+
;(this.player as any).removeItem(id, nb)
|
|
159
|
+
this.player.syncChanges()
|
|
33
160
|
}
|
|
34
161
|
catch (err) {
|
|
35
162
|
console.log(err)
|
|
36
163
|
}
|
|
164
|
+
finally {
|
|
165
|
+
this.refreshShop(clientActionId)
|
|
166
|
+
}
|
|
37
167
|
})
|
|
38
|
-
return super.open(
|
|
168
|
+
return super.open(this.buildShopData(), {
|
|
39
169
|
waitingAction: true,
|
|
40
170
|
blockPlayerInput: true
|
|
41
171
|
})
|
|
42
172
|
}
|
|
43
|
-
}
|
|
173
|
+
}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { PrebuiltGui } from '@rpgjs/common'
|
|
2
|
+
import { Gui } from './Gui'
|
|
3
|
+
import { RpgPlayer } from '../Player/Player'
|
|
4
|
+
|
|
5
|
+
export interface TitleEntry {
|
|
6
|
+
id: string
|
|
7
|
+
label: string
|
|
8
|
+
disabled?: boolean
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface TitleGuiOptions {
|
|
12
|
+
entries?: TitleEntry[]
|
|
13
|
+
title?: string
|
|
14
|
+
subtitle?: string
|
|
15
|
+
version?: string
|
|
16
|
+
showPressStart?: boolean
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface TitleGuiSelection {
|
|
20
|
+
id?: string
|
|
21
|
+
index?: number
|
|
22
|
+
entry?: TitleEntry
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export class TitleGui extends Gui {
|
|
26
|
+
constructor(player: RpgPlayer) {
|
|
27
|
+
super(PrebuiltGui.TitleScreen, player)
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
open(options: TitleGuiOptions = {}): Promise<TitleGuiSelection | null> {
|
|
31
|
+
this.on('select', (selection: TitleGuiSelection) => {
|
|
32
|
+
this.close(selection)
|
|
33
|
+
})
|
|
34
|
+
return super.open(options, {
|
|
35
|
+
waitingAction: true,
|
|
36
|
+
blockPlayerInput: true
|
|
37
|
+
})
|
|
38
|
+
}
|
|
39
|
+
}
|
package/src/Gui/index.ts
CHANGED
|
@@ -3,11 +3,24 @@ import { DialogGui } from './DialogGui'
|
|
|
3
3
|
import { MenuGui } from './MenuGui'
|
|
4
4
|
import { ShopGui } from './ShopGui'
|
|
5
5
|
import { NotificationGui } from './NotificationGui'
|
|
6
|
+
import { SaveLoadGui } from './SaveLoadGui'
|
|
7
|
+
import { TitleGui } from './TitleGui'
|
|
8
|
+
import { GameoverGui } from './GameoverGui'
|
|
6
9
|
|
|
7
10
|
export {
|
|
8
11
|
Gui,
|
|
9
12
|
DialogGui,
|
|
10
13
|
MenuGui,
|
|
11
14
|
ShopGui,
|
|
12
|
-
NotificationGui
|
|
13
|
-
|
|
15
|
+
NotificationGui,
|
|
16
|
+
SaveLoadGui,
|
|
17
|
+
TitleGui,
|
|
18
|
+
GameoverGui
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export { DialogPosition } from './DialogGui'
|
|
22
|
+
export type { SaveLoadMode, SaveLoadOptions, SaveSlot } from './SaveLoadGui'
|
|
23
|
+
export type { MenuEntryId, MenuEntry, MenuGuiOptions } from './MenuGui'
|
|
24
|
+
export type { ShopGuiOptions, ShopSellList } from './ShopGui'
|
|
25
|
+
export type { TitleEntry, TitleGuiOptions, TitleGuiSelection } from './TitleGui'
|
|
26
|
+
export type { GameoverEntry, GameoverGuiOptions, GameoverGuiSelection } from './GameoverGui'
|
|
@@ -1,45 +1,22 @@
|
|
|
1
1
|
import { Constructor, PlayerCtor, RpgCommonPlayer } from "@rpgjs/common";
|
|
2
2
|
import { RpgPlayer } from "./Player";
|
|
3
|
-
import { ATK, PDEF, SDEF } from "
|
|
3
|
+
import { ATK, PDEF, SDEF } from "@rpgjs/common";
|
|
4
4
|
import { Effect } from "./EffectManager";
|
|
5
|
+
import type { IElementManager } from "./ElementManager";
|
|
6
|
+
import type { IEffectManager } from "./EffectManager";
|
|
7
|
+
import type { IParameterManager } from "./ParameterManager";
|
|
5
8
|
|
|
6
|
-
|
|
7
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Interface combining methods from other managers needed by BattleManager
|
|
11
|
+
* Reuses existing interfaces instead of duplicating method signatures
|
|
12
|
+
*/
|
|
13
|
+
interface PlayerWithMixins extends IElementManager, IEffectManager, Pick<IParameterManager, 'parameters' | 'hp'> {
|
|
8
14
|
getFormulas(name: string): any;
|
|
9
|
-
|
|
10
|
-
coefficientElements(attackerPlayer: RpgPlayer): number;
|
|
11
|
-
hp: number;
|
|
12
|
-
getCurrentMap(): any;
|
|
15
|
+
getCurrentMap(): ReturnType<RpgPlayer['getCurrentMap']>;
|
|
13
16
|
}
|
|
14
17
|
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
*
|
|
18
|
-
* Provides battle management capabilities to any class. This mixin handles
|
|
19
|
-
* damage calculation, critical hits, elemental vulnerabilities, and guard effects.
|
|
20
|
-
* It implements a comprehensive battle system with customizable formulas and effects.
|
|
21
|
-
*
|
|
22
|
-
* @param Base - The base class to extend with battle management
|
|
23
|
-
* @returns Extended class with battle management methods
|
|
24
|
-
*
|
|
25
|
-
* @example
|
|
26
|
-
* ```ts
|
|
27
|
-
* class MyPlayer extends WithBattleManager(BasePlayer) {
|
|
28
|
-
* constructor() {
|
|
29
|
-
* super();
|
|
30
|
-
* // Battle system is automatically initialized
|
|
31
|
-
* }
|
|
32
|
-
* }
|
|
33
|
-
*
|
|
34
|
-
* const player = new MyPlayer();
|
|
35
|
-
* const attacker = new MyPlayer();
|
|
36
|
-
* const result = player.applyDamage(attacker);
|
|
37
|
-
* console.log(`Damage dealt: ${result.damage}`);
|
|
38
|
-
* ```
|
|
39
|
-
*/
|
|
40
|
-
export function WithBattleManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
41
|
-
return class extends Base {
|
|
42
|
-
/**
|
|
18
|
+
export interface IBattleManager {
|
|
19
|
+
/**
|
|
43
20
|
* Apply damage. Player will lose HP. the `attackerPlayer` parameter is the other player, the one who attacks.
|
|
44
21
|
*
|
|
45
22
|
* If you don't set the skill parameter, it will be a physical attack.
|
|
@@ -71,6 +48,17 @@ export function WithBattleManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
71
48
|
* }
|
|
72
49
|
* ```
|
|
73
50
|
*/
|
|
51
|
+
applyDamage(attackerPlayer: RpgPlayer, skill?: any): {
|
|
52
|
+
damage: number;
|
|
53
|
+
critical: boolean;
|
|
54
|
+
elementVulnerable: boolean;
|
|
55
|
+
guard: boolean;
|
|
56
|
+
superGuard: boolean;
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
export function WithBattleManager<TBase extends PlayerCtor>(Base: TBase): new (...args: ConstructorParameters<TBase>) => InstanceType<TBase> & IBattleManager {
|
|
61
|
+
return class extends Base {
|
|
74
62
|
applyDamage(
|
|
75
63
|
attackerPlayer: RpgPlayer,
|
|
76
64
|
skill?: any
|
|
@@ -81,9 +69,10 @@ export function WithBattleManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
81
69
|
guard: boolean;
|
|
82
70
|
superGuard: boolean;
|
|
83
71
|
} {
|
|
72
|
+
const self = this as unknown as PlayerWithMixins;
|
|
84
73
|
const getParam = (player: RpgPlayer) => {
|
|
85
74
|
const params = {};
|
|
86
|
-
(
|
|
75
|
+
Object.keys(self.parameters).forEach((key) => {
|
|
87
76
|
params[key] = (player as any).param[key];
|
|
88
77
|
});
|
|
89
78
|
return {
|
|
@@ -100,26 +89,25 @@ export function WithBattleManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
100
89
|
let superGuard = false;
|
|
101
90
|
let elementVulnerable = false;
|
|
102
91
|
const paramA = getParam(attackerPlayer);
|
|
103
|
-
const paramB = getParam(
|
|
104
|
-
console.log(paramA, paramB)
|
|
92
|
+
const paramB = getParam(self as any);
|
|
105
93
|
if (skill) {
|
|
106
|
-
fn =
|
|
94
|
+
fn = self.getFormulas("damageSkill");
|
|
107
95
|
if (!fn) {
|
|
108
96
|
throw new Error("Skill Formulas not exists");
|
|
109
97
|
}
|
|
110
98
|
damage = fn(paramA, paramB, skill);
|
|
111
99
|
} else {
|
|
112
|
-
fn =
|
|
100
|
+
fn = self.getFormulas("damagePhysic");
|
|
113
101
|
if (!fn) {
|
|
114
102
|
throw new Error("Physic Formulas not exists");
|
|
115
103
|
}
|
|
116
104
|
damage = fn(paramA, paramB);
|
|
117
|
-
const coef =
|
|
105
|
+
const coef = self.coefficientElements(attackerPlayer);
|
|
118
106
|
if (coef >= 2) {
|
|
119
107
|
elementVulnerable = true;
|
|
120
108
|
}
|
|
121
109
|
damage *= coef;
|
|
122
|
-
fn =
|
|
110
|
+
fn = self.getFormulas("damageCritical");
|
|
123
111
|
if (fn) {
|
|
124
112
|
let newDamage = fn(damage, paramA, paramB);
|
|
125
113
|
if (damage != newDamage) {
|
|
@@ -128,8 +116,8 @@ export function WithBattleManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
128
116
|
damage = newDamage;
|
|
129
117
|
}
|
|
130
118
|
}
|
|
131
|
-
if (
|
|
132
|
-
fn =
|
|
119
|
+
if (self.hasEffect(Effect.GUARD)) {
|
|
120
|
+
fn = self.getFormulas("damageGuard");
|
|
133
121
|
if (fn) {
|
|
134
122
|
let newDamage = fn(damage, paramA, paramB);
|
|
135
123
|
if (damage != newDamage) {
|
|
@@ -138,11 +126,11 @@ export function WithBattleManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
138
126
|
damage = newDamage;
|
|
139
127
|
}
|
|
140
128
|
}
|
|
141
|
-
if (
|
|
129
|
+
if (self.hasEffect(Effect.SUPER_GUARD)) {
|
|
142
130
|
damage /= 4;
|
|
143
131
|
superGuard = true;
|
|
144
132
|
}
|
|
145
|
-
|
|
133
|
+
self.hp -= damage;
|
|
146
134
|
return {
|
|
147
135
|
damage,
|
|
148
136
|
critical,
|
|
@@ -179,14 +167,9 @@ export function WithBattleManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
179
167
|
* ```
|
|
180
168
|
*/
|
|
181
169
|
getFormulas(name: string) {
|
|
182
|
-
const
|
|
183
|
-
|
|
170
|
+
const self = this as unknown as PlayerWithMixins;
|
|
171
|
+
const map = self.getCurrentMap();
|
|
172
|
+
return map?.damageFormulas[name];
|
|
184
173
|
}
|
|
185
|
-
} as unknown as
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
/**
|
|
189
|
-
* Type helper to extract the interface from the WithBattleManager mixin
|
|
190
|
-
* This provides the type without duplicating method signatures
|
|
191
|
-
*/
|
|
192
|
-
export type IBattleManager = InstanceType<ReturnType<typeof WithBattleManager>>;
|
|
174
|
+
} as unknown as any;
|
|
175
|
+
}
|
|
@@ -6,8 +6,8 @@ type ActorClass = any;
|
|
|
6
6
|
interface PlayerWithMixins extends RpgCommonPlayer {
|
|
7
7
|
databaseById(id: string): any;
|
|
8
8
|
addParameter(name: string, { start, end }: { start: number, end: number }): void;
|
|
9
|
-
addItem(item: any):
|
|
10
|
-
equip(
|
|
9
|
+
addItem(item: any): any;
|
|
10
|
+
equip(itemId: string, equip?: boolean | 'auto'): void;
|
|
11
11
|
}
|
|
12
12
|
|
|
13
13
|
/**
|
|
@@ -36,81 +36,68 @@ interface PlayerWithMixins extends RpgCommonPlayer {
|
|
|
36
36
|
*/
|
|
37
37
|
export function WithClassManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
38
38
|
return class extends Base {
|
|
39
|
+
private _resolveClassInput(classInput: ClassClass | string, databaseByIdOverride?: (id: string) => any) {
|
|
40
|
+
if (isString(classInput)) {
|
|
41
|
+
return databaseByIdOverride
|
|
42
|
+
? databaseByIdOverride(classInput)
|
|
43
|
+
: (this as any).databaseById(classInput);
|
|
44
|
+
}
|
|
45
|
+
return classInput;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
private _createClassInstance(classInput: ClassClass | string) {
|
|
49
|
+
const classClass = this._resolveClassInput(classInput);
|
|
50
|
+
const instance = new (classClass as ClassClass)();
|
|
51
|
+
return { classClass, instance };
|
|
52
|
+
}
|
|
39
53
|
|
|
40
54
|
/**
|
|
41
|
-
*
|
|
42
|
-
*
|
|
43
|
-
* Sets the player's class, which defines their combat abilities, stat growth,
|
|
44
|
-
* and available skills. The class system provides the foundation for character
|
|
45
|
-
* progression and specialization. When a class is set, it automatically triggers
|
|
46
|
-
* the class's onSet method for any additional initialization.
|
|
47
|
-
*
|
|
48
|
-
* @param _class - The class constructor or class ID to assign to the player
|
|
49
|
-
* @returns The instantiated class object
|
|
50
|
-
*
|
|
51
|
-
* @example
|
|
52
|
-
* ```ts
|
|
53
|
-
* import { Fighter } from 'my-database/classes/fighter'
|
|
54
|
-
*
|
|
55
|
-
* // Set class using constructor
|
|
56
|
-
* const fighterClass = player.setClass(Fighter);
|
|
57
|
-
* console.log('Class set:', fighterClass.name);
|
|
58
|
-
*
|
|
59
|
-
* // Set class using string ID
|
|
60
|
-
* player.setClass('fighter');
|
|
61
|
-
*
|
|
62
|
-
* // Class affects available skills and stats
|
|
63
|
-
* console.log('Available skills:', player.skills);
|
|
64
|
-
* console.log('Class bonuses applied to stats');
|
|
65
|
-
*
|
|
66
|
-
* // Class determines level progression
|
|
67
|
-
* player.level = 5;
|
|
68
|
-
* // Skills may be automatically learned based on class definition
|
|
69
|
-
* ```
|
|
55
|
+
* Create a class instance without side effects.
|
|
70
56
|
*/
|
|
57
|
+
createClassInstance(classInput: ClassClass | string) {
|
|
58
|
+
return this._createClassInstance(classInput);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Resolve class snapshot entry into a class instance without side effects.
|
|
63
|
+
*/
|
|
64
|
+
resolveClassSnapshot(snapshot: { _class?: any }, mapOverride?: any) {
|
|
65
|
+
if (!snapshot || snapshot._class == null) {
|
|
66
|
+
return snapshot;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const map = mapOverride ?? ((this as any).getCurrentMap?.() || (this as any).map);
|
|
70
|
+
if (!map || !map.database) {
|
|
71
|
+
return snapshot;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const databaseByIdOverride = (id: string) => {
|
|
75
|
+
const data = map.database()[id];
|
|
76
|
+
if (!data) {
|
|
77
|
+
throw new Error(
|
|
78
|
+
`The ID=${id} data is not found in the database. Add the data in the property "database"`
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
return data;
|
|
82
|
+
};
|
|
83
|
+
|
|
84
|
+
const classId = isString(snapshot._class) ? snapshot._class : snapshot._class?.id;
|
|
85
|
+
if (!classId) {
|
|
86
|
+
return snapshot;
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const classClass = this._resolveClassInput(classId, databaseByIdOverride);
|
|
90
|
+
const { instance } = this._createClassInstance(classClass as ClassClass);
|
|
91
|
+
return { ...snapshot, _class: instance };
|
|
92
|
+
}
|
|
93
|
+
|
|
71
94
|
setClass(_class: ClassClass | string) {
|
|
72
|
-
|
|
73
|
-
const classInstance =
|
|
95
|
+
const { instance } = this._createClassInstance(_class);
|
|
96
|
+
const classInstance = instance;
|
|
74
97
|
(this as any)["execMethod"]("onSet", [this], classInstance);
|
|
75
98
|
return classInstance;
|
|
76
99
|
}
|
|
77
100
|
|
|
78
|
-
/**
|
|
79
|
-
* Allows to give a set of already defined properties to the player (default equipment, or a list of skills to learn according to the level)
|
|
80
|
-
*
|
|
81
|
-
* Sets up the player as a specific actor archetype, which includes predefined
|
|
82
|
-
* characteristics like starting equipment, parameters, level ranges, and associated class.
|
|
83
|
-
* This is typically used for creating pre-configured character templates or NPCs
|
|
84
|
-
* with specific roles and equipment loadouts.
|
|
85
|
-
*
|
|
86
|
-
* @param actorClass - The actor constructor or actor ID to assign to the player
|
|
87
|
-
* @returns The instantiated actor object
|
|
88
|
-
*
|
|
89
|
-
* @example
|
|
90
|
-
* ```ts
|
|
91
|
-
* import { Hero } from 'my-database/classes/hero'
|
|
92
|
-
*
|
|
93
|
-
* // Set up player as Hero actor
|
|
94
|
-
* const heroActor = player.setActor(Hero);
|
|
95
|
-
* console.log('Actor configured:', heroActor.name);
|
|
96
|
-
*
|
|
97
|
-
* // Actor automatically sets up:
|
|
98
|
-
* // - Starting equipment (sword, armor, etc.)
|
|
99
|
-
* console.log('Starting equipment:', player.equipments());
|
|
100
|
-
*
|
|
101
|
-
* // - Parameter ranges and growth
|
|
102
|
-
* console.log('Level range:', player.initialLevel, '-', player.finalLevel);
|
|
103
|
-
*
|
|
104
|
-
* // - Associated class
|
|
105
|
-
* console.log('Assigned class:', player.class);
|
|
106
|
-
*
|
|
107
|
-
* // - Experience curve
|
|
108
|
-
* console.log('EXP curve:', player.expCurve);
|
|
109
|
-
*
|
|
110
|
-
* // Actor setup is comprehensive
|
|
111
|
-
* player.setActor('hero'); // Can also use string ID
|
|
112
|
-
* ```
|
|
113
|
-
*/
|
|
114
101
|
setActor(actorClass: ActorClass | string) {
|
|
115
102
|
if (isString(actorClass)) actorClass = (this as any).databaseById(actorClass);
|
|
116
103
|
const actor = new (actorClass as ActorClass)();
|
|
@@ -121,8 +108,11 @@ export function WithClassManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
121
108
|
(this as any).addParameter(param, actor.parameters[param]);
|
|
122
109
|
}
|
|
123
110
|
for (let item of actor.startingEquipment) {
|
|
124
|
-
(this as any).addItem(item);
|
|
125
|
-
|
|
111
|
+
const inventory = (this as any).addItem(item);
|
|
112
|
+
const itemId = inventory?.id?.();
|
|
113
|
+
if (itemId) {
|
|
114
|
+
(this as any).equip(itemId, true);
|
|
115
|
+
}
|
|
126
116
|
}
|
|
127
117
|
if (actor.class) this.setClass(actor.class);
|
|
128
118
|
(this as any)["execMethod"]("onSet", [this], actor);
|
|
@@ -132,7 +122,25 @@ export function WithClassManager<TBase extends PlayerCtor>(Base: TBase) {
|
|
|
132
122
|
}
|
|
133
123
|
|
|
134
124
|
/**
|
|
135
|
-
*
|
|
136
|
-
*
|
|
125
|
+
* Interface for Class Manager functionality
|
|
126
|
+
*
|
|
127
|
+
* Provides class and actor management capabilities including character class assignment
|
|
128
|
+
* and actor setup. This interface defines the public API of the ClassManager mixin.
|
|
137
129
|
*/
|
|
138
|
-
export
|
|
130
|
+
export interface IClassManager {
|
|
131
|
+
/**
|
|
132
|
+
* Assign a class to the player
|
|
133
|
+
*
|
|
134
|
+
* @param _class - The class constructor or class ID to assign to the player
|
|
135
|
+
* @returns The instantiated class object
|
|
136
|
+
*/
|
|
137
|
+
setClass(_class: ClassClass | string): any;
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Set up the player as a specific actor archetype
|
|
141
|
+
*
|
|
142
|
+
* @param actorClass - The actor constructor or actor ID to assign to the player
|
|
143
|
+
* @returns The instantiated actor object
|
|
144
|
+
*/
|
|
145
|
+
setActor(actorClass: ActorClass | string): any;
|
|
146
|
+
}
|