gotchi-battler-game-logic 3.0.0 → 4.0.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/.cursor/rules/cursor-rules.mdc +67 -0
- package/.cursor/rules/directory-structure.mdc +63 -0
- package/.cursor/rules/self-improvement.mdc +64 -0
- package/.cursor/rules/tech-stack.mdc +99 -0
- package/README.md +4 -0
- package/eslint.config.js +31 -0
- package/game-logic/index.js +2 -6
- package/game-logic/v1.4/constants.js +0 -23
- package/game-logic/v1.4/index.js +64 -56
- package/game-logic/v1.5/constants.js +0 -23
- package/game-logic/v1.5/index.js +27 -21
- package/game-logic/v1.6/constants.js +0 -23
- package/game-logic/v1.6/index.js +27 -21
- package/game-logic/v1.7/constants.js +0 -23
- package/game-logic/v1.7/helpers.js +2 -2
- package/game-logic/v1.7/index.js +24 -18
- package/game-logic/v1.8/constants.js +0 -23
- package/game-logic/v1.8/helpers.js +2 -2
- package/game-logic/v1.8/index.js +25 -19
- package/game-logic/v2.0/constants.js +112 -0
- package/game-logic/v2.0/helpers.js +713 -0
- package/game-logic/v2.0/index.js +782 -0
- package/game-logic/v2.0/statuses.json +439 -0
- package/package.json +11 -4
- package/schemas/crystal.js +14 -0
- package/schemas/effect.js +25 -0
- package/schemas/gotchi.js +53 -0
- package/schemas/ingameteam.js +14 -0
- package/schemas/item.js +13 -0
- package/schemas/leaderskill.js +15 -0
- package/schemas/leaderskillstatus.js +12 -0
- package/schemas/special.js +22 -0
- package/schemas/team.js +24 -0
- package/schemas/team.json +252 -114
- package/scripts/balancing/createTrainingGotchis.js +44 -44
- package/scripts/balancing/extractOnchainTraits.js +3 -3
- package/scripts/balancing/fixTrainingGotchis.js +41 -41
- package/scripts/balancing/processSims.js +5 -5
- package/scripts/balancing/sims.js +8 -15
- package/scripts/balancing/v1.7/setTeamPositions.js +2 -2
- package/scripts/balancing/v1.7.1/setTeamPositions.js +2 -2
- package/scripts/balancing/v1.7.2/setTeamPositions.js +2 -2
- package/scripts/balancing/v1.7.3/setTeamPositions.js +2 -2
- package/scripts/data/dungeon_mob_1.json +87 -0
- package/scripts/data/dungeon_mob_2.json +87 -0
- package/scripts/data/immaterialTeam1.json +374 -0
- package/scripts/data/immaterialTeam2.json +365 -0
- package/scripts/generateAllSpecialsLogs.js +93 -0
- package/scripts/generateSpecialLogs.js +94 -0
- package/scripts/runCampaignBattles.js +41 -0
- package/scripts/runLocalBattle.js +6 -3
- package/scripts/runLocalDungeon.js +52 -0
- package/scripts/runPvPBattle.js +15 -0
- package/scripts/runRealBattle.js +8 -8
- package/scripts/simRealBattle.js +8 -8
- package/scripts/validateBattle.js +12 -14
- package/scripts/validateTournament.js +9 -9
- package/tests/getModifiedStats.test.js +78 -0
- package/utils/errors.js +13 -13
- package/utils/transforms.js +2 -8
- package/scripts/output/.gitkeep +0 -0
|
@@ -0,0 +1,365 @@
|
|
|
1
|
+
{
|
|
2
|
+
"formation": {
|
|
3
|
+
"front": [
|
|
4
|
+
null,
|
|
5
|
+
{
|
|
6
|
+
"id": 47,
|
|
7
|
+
"onchainId": 23921,
|
|
8
|
+
"name": "Godly",
|
|
9
|
+
"type": "gotchi",
|
|
10
|
+
"visualCode": "wEth-Common1-Rare_Low-160-416-202-124-70-70-417",
|
|
11
|
+
"level": 72,
|
|
12
|
+
"speed": 191,
|
|
13
|
+
"health": 862,
|
|
14
|
+
"attack": 210,
|
|
15
|
+
"defense": 133,
|
|
16
|
+
"criticalRate": 50.1,
|
|
17
|
+
"criticalDamage": 168,
|
|
18
|
+
"resist": 152,
|
|
19
|
+
"focus": 152,
|
|
20
|
+
"createdAt": "2025-12-01T15:01:53.416Z",
|
|
21
|
+
"updatedAt": "2025-12-01T15:01:53.416Z",
|
|
22
|
+
"gotchiClass": "troll",
|
|
23
|
+
"special": "furious_bash",
|
|
24
|
+
"leaderSkill": "smash",
|
|
25
|
+
"item": null,
|
|
26
|
+
"crystalSlot1": null,
|
|
27
|
+
"crystalSlot2": null,
|
|
28
|
+
"crystalSlot3": null,
|
|
29
|
+
"crystalSlot4": null,
|
|
30
|
+
"crystalSlot5": null,
|
|
31
|
+
"crystalSlot6": null,
|
|
32
|
+
"specialExpanded": {
|
|
33
|
+
"code": "furious_bash",
|
|
34
|
+
"name": "Furious Bash",
|
|
35
|
+
"initialCooldown": 1,
|
|
36
|
+
"cooldown": 3,
|
|
37
|
+
"actionType": "attack",
|
|
38
|
+
"actionMultiplier": 1,
|
|
39
|
+
"monstersOnly": false,
|
|
40
|
+
"notes": "50 % chance for Troll to gain +SPD.",
|
|
41
|
+
"gotchiClass": "troll",
|
|
42
|
+
"target": "enemy_random",
|
|
43
|
+
"effects": [
|
|
44
|
+
{
|
|
45
|
+
"id": 99,
|
|
46
|
+
"effectType": "status",
|
|
47
|
+
"value": null,
|
|
48
|
+
"chance": 0.5,
|
|
49
|
+
"special": "furious_bash",
|
|
50
|
+
"target": "self",
|
|
51
|
+
"status": "spd_up"
|
|
52
|
+
}
|
|
53
|
+
]
|
|
54
|
+
},
|
|
55
|
+
"leaderSkillExpanded": {
|
|
56
|
+
"code": "smash",
|
|
57
|
+
"name": "Smash!",
|
|
58
|
+
"description": "Your team gets +CRD.",
|
|
59
|
+
"monstersOnly": false,
|
|
60
|
+
"gotchiClass": "troll",
|
|
61
|
+
"statuses": [
|
|
62
|
+
{
|
|
63
|
+
"leaderSkill": "smash",
|
|
64
|
+
"status": "crd_up",
|
|
65
|
+
"stackCount": 1
|
|
66
|
+
}
|
|
67
|
+
]
|
|
68
|
+
}
|
|
69
|
+
},
|
|
70
|
+
null,
|
|
71
|
+
{
|
|
72
|
+
"id": 49,
|
|
73
|
+
"onchainId": 8062,
|
|
74
|
+
"name": "Magestic",
|
|
75
|
+
"type": "gotchi",
|
|
76
|
+
"visualCode": "Uni-UNI-Rare_High-15-13-14-48-217-217-237",
|
|
77
|
+
"level": 71,
|
|
78
|
+
"speed": 185,
|
|
79
|
+
"health": 854,
|
|
80
|
+
"attack": 175,
|
|
81
|
+
"defense": 132,
|
|
82
|
+
"criticalRate": 50.2,
|
|
83
|
+
"criticalDamage": 158,
|
|
84
|
+
"resist": 181,
|
|
85
|
+
"focus": 173,
|
|
86
|
+
"createdAt": "2025-12-01T15:01:53.417Z",
|
|
87
|
+
"updatedAt": "2025-12-01T15:01:53.417Z",
|
|
88
|
+
"gotchiClass": "cleaver",
|
|
89
|
+
"special": "sword_breaker",
|
|
90
|
+
"leaderSkill": "sharp_wits",
|
|
91
|
+
"item": null,
|
|
92
|
+
"crystalSlot1": null,
|
|
93
|
+
"crystalSlot2": null,
|
|
94
|
+
"crystalSlot3": null,
|
|
95
|
+
"crystalSlot4": null,
|
|
96
|
+
"crystalSlot5": null,
|
|
97
|
+
"crystalSlot6": null,
|
|
98
|
+
"specialExpanded": {
|
|
99
|
+
"code": "sword_breaker",
|
|
100
|
+
"name": "Sword Breaker",
|
|
101
|
+
"initialCooldown": 3,
|
|
102
|
+
"cooldown": 1,
|
|
103
|
+
"actionType": "attack",
|
|
104
|
+
"actionMultiplier": 1.2,
|
|
105
|
+
"monstersOnly": false,
|
|
106
|
+
"notes": "May apply −2 CRT (50% chance per stack).",
|
|
107
|
+
"gotchiClass": "cleaver",
|
|
108
|
+
"target": "enemy_random",
|
|
109
|
+
"effects": [
|
|
110
|
+
{
|
|
111
|
+
"id": 29,
|
|
112
|
+
"effectType": "status",
|
|
113
|
+
"value": null,
|
|
114
|
+
"chance": 0.5,
|
|
115
|
+
"special": "sword_breaker",
|
|
116
|
+
"target": "same_as_attack",
|
|
117
|
+
"status": "crt_down"
|
|
118
|
+
},
|
|
119
|
+
{
|
|
120
|
+
"id": 30,
|
|
121
|
+
"effectType": "status",
|
|
122
|
+
"value": null,
|
|
123
|
+
"chance": 0.5,
|
|
124
|
+
"special": "sword_breaker",
|
|
125
|
+
"target": "same_as_attack",
|
|
126
|
+
"status": "crt_down"
|
|
127
|
+
}
|
|
128
|
+
]
|
|
129
|
+
},
|
|
130
|
+
"leaderSkillExpanded": {
|
|
131
|
+
"code": "sharp_wits",
|
|
132
|
+
"name": "Sharp Wits",
|
|
133
|
+
"description": "Your team gets +FOC.",
|
|
134
|
+
"monstersOnly": false,
|
|
135
|
+
"gotchiClass": "cleaver",
|
|
136
|
+
"statuses": [
|
|
137
|
+
{
|
|
138
|
+
"leaderSkill": "sharp_wits",
|
|
139
|
+
"status": "foc_up",
|
|
140
|
+
"stackCount": 1
|
|
141
|
+
}
|
|
142
|
+
]
|
|
143
|
+
}
|
|
144
|
+
},
|
|
145
|
+
null
|
|
146
|
+
],
|
|
147
|
+
"back": [
|
|
148
|
+
{
|
|
149
|
+
"id": 46,
|
|
150
|
+
"onchainId": 13681,
|
|
151
|
+
"name": "Artemis",
|
|
152
|
+
"type": "gotchi",
|
|
153
|
+
"visualCode": "USDC-MythicalLow2_H2-Common-258-368-259-63-315-386-261",
|
|
154
|
+
"level": 98,
|
|
155
|
+
"speed": 178,
|
|
156
|
+
"health": 1328,
|
|
157
|
+
"attack": 193,
|
|
158
|
+
"defense": 166,
|
|
159
|
+
"criticalRate": 45.1,
|
|
160
|
+
"criticalDamage": 175,
|
|
161
|
+
"resist": 186,
|
|
162
|
+
"focus": 217,
|
|
163
|
+
"createdAt": "2025-12-01T15:01:53.416Z",
|
|
164
|
+
"updatedAt": "2025-12-01T15:01:53.416Z",
|
|
165
|
+
"gotchiClass": "mage",
|
|
166
|
+
"special": "tornado",
|
|
167
|
+
"leaderSkill": "obsessive_studies",
|
|
168
|
+
"item": null,
|
|
169
|
+
"crystalSlot1": null,
|
|
170
|
+
"crystalSlot2": null,
|
|
171
|
+
"crystalSlot3": null,
|
|
172
|
+
"crystalSlot4": null,
|
|
173
|
+
"crystalSlot5": null,
|
|
174
|
+
"crystalSlot6": null,
|
|
175
|
+
"specialExpanded": {
|
|
176
|
+
"code": "tornado",
|
|
177
|
+
"name": "Tornado",
|
|
178
|
+
"initialCooldown": 2,
|
|
179
|
+
"cooldown": 3,
|
|
180
|
+
"actionType": "none",
|
|
181
|
+
"actionMultiplier": null,
|
|
182
|
+
"monstersOnly": false,
|
|
183
|
+
"notes": "Each enemy has a 25 % chance to be Feared.",
|
|
184
|
+
"gotchiClass": "mage",
|
|
185
|
+
"target": "all_enemies",
|
|
186
|
+
"effects": [
|
|
187
|
+
{
|
|
188
|
+
"id": 87,
|
|
189
|
+
"effectType": "status",
|
|
190
|
+
"value": null,
|
|
191
|
+
"chance": 0.25,
|
|
192
|
+
"special": "tornado",
|
|
193
|
+
"target": "same_as_attack",
|
|
194
|
+
"status": "fear"
|
|
195
|
+
}
|
|
196
|
+
]
|
|
197
|
+
},
|
|
198
|
+
"leaderSkillExpanded": {
|
|
199
|
+
"code": "obsessive_studies",
|
|
200
|
+
"name": "Obsessive Studies",
|
|
201
|
+
"description": "Your team gets -DEF and +2 FOC.",
|
|
202
|
+
"monstersOnly": false,
|
|
203
|
+
"gotchiClass": "mage",
|
|
204
|
+
"statuses": [
|
|
205
|
+
{
|
|
206
|
+
"leaderSkill": "obsessive_studies",
|
|
207
|
+
"status": "def_down",
|
|
208
|
+
"stackCount": 1
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
"leaderSkill": "obsessive_studies",
|
|
212
|
+
"status": "foc_up",
|
|
213
|
+
"stackCount": 2
|
|
214
|
+
}
|
|
215
|
+
]
|
|
216
|
+
}
|
|
217
|
+
},
|
|
218
|
+
null,
|
|
219
|
+
{
|
|
220
|
+
"id": 48,
|
|
221
|
+
"onchainId": 19172,
|
|
222
|
+
"name": "Gatekeeper",
|
|
223
|
+
"type": "gotchi",
|
|
224
|
+
"visualCode": "Dai-UncommonLow2-Mythical_High-160-263-262-62-51-312-261",
|
|
225
|
+
"level": 72,
|
|
226
|
+
"speed": 194,
|
|
227
|
+
"health": 863,
|
|
228
|
+
"attack": 176,
|
|
229
|
+
"defense": 156,
|
|
230
|
+
"criticalRate": 38.5,
|
|
231
|
+
"criticalDamage": 169,
|
|
232
|
+
"resist": 153,
|
|
233
|
+
"focus": 186,
|
|
234
|
+
"createdAt": "2025-12-01T15:01:53.416Z",
|
|
235
|
+
"updatedAt": "2025-12-01T15:01:53.416Z",
|
|
236
|
+
"gotchiClass": "mage",
|
|
237
|
+
"special": "tornado",
|
|
238
|
+
"leaderSkill": "obsessive_studies",
|
|
239
|
+
"item": null,
|
|
240
|
+
"crystalSlot1": null,
|
|
241
|
+
"crystalSlot2": null,
|
|
242
|
+
"crystalSlot3": null,
|
|
243
|
+
"crystalSlot4": null,
|
|
244
|
+
"crystalSlot5": null,
|
|
245
|
+
"crystalSlot6": null,
|
|
246
|
+
"specialExpanded": {
|
|
247
|
+
"code": "tornado",
|
|
248
|
+
"name": "Tornado",
|
|
249
|
+
"initialCooldown": 2,
|
|
250
|
+
"cooldown": 3,
|
|
251
|
+
"actionType": "none",
|
|
252
|
+
"actionMultiplier": null,
|
|
253
|
+
"monstersOnly": false,
|
|
254
|
+
"notes": "Each enemy has a 25 % chance to be Feared.",
|
|
255
|
+
"gotchiClass": "mage",
|
|
256
|
+
"target": "all_enemies",
|
|
257
|
+
"effects": [
|
|
258
|
+
{
|
|
259
|
+
"id": 87,
|
|
260
|
+
"effectType": "status",
|
|
261
|
+
"value": null,
|
|
262
|
+
"chance": 0.25,
|
|
263
|
+
"special": "tornado",
|
|
264
|
+
"target": "same_as_attack",
|
|
265
|
+
"status": "fear"
|
|
266
|
+
}
|
|
267
|
+
]
|
|
268
|
+
},
|
|
269
|
+
"leaderSkillExpanded": {
|
|
270
|
+
"code": "obsessive_studies",
|
|
271
|
+
"name": "Obsessive Studies",
|
|
272
|
+
"description": "Your team gets -DEF and +2 FOC.",
|
|
273
|
+
"monstersOnly": false,
|
|
274
|
+
"gotchiClass": "mage",
|
|
275
|
+
"statuses": [
|
|
276
|
+
{
|
|
277
|
+
"leaderSkill": "obsessive_studies",
|
|
278
|
+
"status": "def_down",
|
|
279
|
+
"stackCount": 1
|
|
280
|
+
},
|
|
281
|
+
{
|
|
282
|
+
"leaderSkill": "obsessive_studies",
|
|
283
|
+
"status": "foc_up",
|
|
284
|
+
"stackCount": 2
|
|
285
|
+
}
|
|
286
|
+
]
|
|
287
|
+
}
|
|
288
|
+
},
|
|
289
|
+
null,
|
|
290
|
+
{
|
|
291
|
+
"id": 50,
|
|
292
|
+
"onchainId": 4751,
|
|
293
|
+
"name": "best ever",
|
|
294
|
+
"type": "gotchi",
|
|
295
|
+
"visualCode": "Yfi-Common1-Mythical_High-0-0-0-0-0-0-0",
|
|
296
|
+
"level": 41,
|
|
297
|
+
"speed": 158,
|
|
298
|
+
"health": 662,
|
|
299
|
+
"attack": 156,
|
|
300
|
+
"defense": 117,
|
|
301
|
+
"criticalRate": 37.5,
|
|
302
|
+
"criticalDamage": 148,
|
|
303
|
+
"resist": 135,
|
|
304
|
+
"focus": 136,
|
|
305
|
+
"createdAt": "2025-12-01T15:01:53.417Z",
|
|
306
|
+
"updatedAt": "2025-12-01T15:01:53.417Z",
|
|
307
|
+
"gotchiClass": "mage",
|
|
308
|
+
"special": "tornado",
|
|
309
|
+
"leaderSkill": "obsessive_studies",
|
|
310
|
+
"item": null,
|
|
311
|
+
"crystalSlot1": null,
|
|
312
|
+
"crystalSlot2": null,
|
|
313
|
+
"crystalSlot3": null,
|
|
314
|
+
"crystalSlot4": null,
|
|
315
|
+
"crystalSlot5": null,
|
|
316
|
+
"crystalSlot6": null,
|
|
317
|
+
"specialExpanded": {
|
|
318
|
+
"code": "tornado",
|
|
319
|
+
"name": "Tornado",
|
|
320
|
+
"initialCooldown": 2,
|
|
321
|
+
"cooldown": 3,
|
|
322
|
+
"actionType": "none",
|
|
323
|
+
"actionMultiplier": null,
|
|
324
|
+
"monstersOnly": false,
|
|
325
|
+
"notes": "Each enemy has a 25 % chance to be Feared.",
|
|
326
|
+
"gotchiClass": "mage",
|
|
327
|
+
"target": "all_enemies",
|
|
328
|
+
"effects": [
|
|
329
|
+
{
|
|
330
|
+
"id": 87,
|
|
331
|
+
"effectType": "status",
|
|
332
|
+
"value": null,
|
|
333
|
+
"chance": 0.25,
|
|
334
|
+
"special": "tornado",
|
|
335
|
+
"target": "same_as_attack",
|
|
336
|
+
"status": "fear"
|
|
337
|
+
}
|
|
338
|
+
]
|
|
339
|
+
},
|
|
340
|
+
"leaderSkillExpanded": {
|
|
341
|
+
"code": "obsessive_studies",
|
|
342
|
+
"name": "Obsessive Studies",
|
|
343
|
+
"description": "Your team gets -DEF and +2 FOC.",
|
|
344
|
+
"monstersOnly": false,
|
|
345
|
+
"gotchiClass": "mage",
|
|
346
|
+
"statuses": [
|
|
347
|
+
{
|
|
348
|
+
"leaderSkill": "obsessive_studies",
|
|
349
|
+
"status": "def_down",
|
|
350
|
+
"stackCount": 1
|
|
351
|
+
},
|
|
352
|
+
{
|
|
353
|
+
"leaderSkill": "obsessive_studies",
|
|
354
|
+
"status": "foc_up",
|
|
355
|
+
"stackCount": 2
|
|
356
|
+
}
|
|
357
|
+
]
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
]
|
|
361
|
+
},
|
|
362
|
+
"leader": 46,
|
|
363
|
+
"name": "Immaterial Team 2",
|
|
364
|
+
"owner": "0xfe698c212526f15cc25af671beba14aac309457f"
|
|
365
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
// Generate logs which test the sequences for all special attacks
|
|
2
|
+
const fs = require('fs')
|
|
3
|
+
const path = require('path')
|
|
4
|
+
const seedrandom = require('seedrandom')
|
|
5
|
+
const { attack } = require('../game-logic/v2.0')
|
|
6
|
+
const { getAlive, prepareTeams, getLogGotchis, simplifyTeam } = require('../game-logic/v2.0/helpers')
|
|
7
|
+
const ninjaSpecials = require('../../gotchi-battler-backend/data/specials_ninja.json')
|
|
8
|
+
const enlightenedSpecials = require('../../gotchi-battler-backend/data/specials_enlightened.json')
|
|
9
|
+
const cleaverSpecials = require('../../gotchi-battler-backend/data/specials_cleaver.json')
|
|
10
|
+
const tankSpecials = require('../../gotchi-battler-backend/data/specials_tank.json')
|
|
11
|
+
const cursedSpecials = require('../../gotchi-battler-backend/data/specials_cursed.json')
|
|
12
|
+
const healerSpecials = require('../../gotchi-battler-backend/data/specials_healer.json')
|
|
13
|
+
const mageSpecials = require('../../gotchi-battler-backend/data/specials_mage.json')
|
|
14
|
+
const trollSpecials = require('../../gotchi-battler-backend/data/specials_troll.json')
|
|
15
|
+
|
|
16
|
+
const specials = [
|
|
17
|
+
...ninjaSpecials, ...enlightenedSpecials,
|
|
18
|
+
...cleaverSpecials, ...tankSpecials,
|
|
19
|
+
...cursedSpecials, ...healerSpecials,
|
|
20
|
+
...mageSpecials, ...trollSpecials
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
const main = async () => {
|
|
24
|
+
// Mock the game loop
|
|
25
|
+
let team1 = require('./data/immaterialTeam1.json')
|
|
26
|
+
let team2 = require('./data/immaterialTeam2.json')
|
|
27
|
+
|
|
28
|
+
team1 = JSON.parse(JSON.stringify(team1))
|
|
29
|
+
team2 = JSON.parse(JSON.stringify(team2))
|
|
30
|
+
|
|
31
|
+
const seed = 'randomseed'
|
|
32
|
+
const rng = seedrandom(seed)
|
|
33
|
+
|
|
34
|
+
// Give all gotchis 100000 health for testing
|
|
35
|
+
const allAliveGotchis = [...getAlive(team1), ...getAlive(team2)]
|
|
36
|
+
allAliveGotchis.forEach(x => {
|
|
37
|
+
x.health = 1000000
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
prepareTeams(allAliveGotchis, team1, team2)
|
|
41
|
+
|
|
42
|
+
const logs = {
|
|
43
|
+
meta: {
|
|
44
|
+
seed,
|
|
45
|
+
timestamp: new Date(),
|
|
46
|
+
type: 'pvp',
|
|
47
|
+
campaign: {},
|
|
48
|
+
isBoss: false
|
|
49
|
+
},
|
|
50
|
+
gotchis: getLogGotchis(allAliveGotchis),
|
|
51
|
+
layout: {
|
|
52
|
+
teams: [
|
|
53
|
+
simplifyTeam(team1),
|
|
54
|
+
simplifyTeam(team2)
|
|
55
|
+
]
|
|
56
|
+
},
|
|
57
|
+
turns: [],
|
|
58
|
+
result: {},
|
|
59
|
+
debug: []
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
let turnCounter = 0
|
|
63
|
+
for (const special of specials) {
|
|
64
|
+
|
|
65
|
+
// Make each gotchi do each special
|
|
66
|
+
|
|
67
|
+
for (const gotchi of allAliveGotchis) {
|
|
68
|
+
const specialResults = attack({
|
|
69
|
+
...gotchi,
|
|
70
|
+
special: special.code,
|
|
71
|
+
specialExpanded: special
|
|
72
|
+
}, team1, team2, rng, true)
|
|
73
|
+
logs.turns.push({
|
|
74
|
+
index: turnCounter,
|
|
75
|
+
skipTurn: null,
|
|
76
|
+
action: {
|
|
77
|
+
user: gotchi.id,
|
|
78
|
+
name: special.code,
|
|
79
|
+
effects: specialResults.effects
|
|
80
|
+
},
|
|
81
|
+
statusEffects: [],
|
|
82
|
+
statusesExpired: specialResults.statusesExpired
|
|
83
|
+
})
|
|
84
|
+
turnCounter++
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Save logs to file
|
|
89
|
+
fs.writeFileSync(path.join(__dirname, 'output', 'gotchi-sequences.json'), JSON.stringify(logs, null, 2))
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// node scripts/generateAllSpecialsLogs.js
|
|
93
|
+
main()
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
// Generate logs which test the sequences for all special attacks
|
|
2
|
+
const fs = require('fs')
|
|
3
|
+
const path = require('path')
|
|
4
|
+
const seedrandom = require('seedrandom')
|
|
5
|
+
const { attack } = require('../game-logic/v2.0')
|
|
6
|
+
const { getAlive, prepareTeams, getLogGotchis, simplifyTeam } = require('../game-logic/v2.0/helpers')
|
|
7
|
+
const ninjaSpecials = require('../../gotchi-battler-backend/data/specials_ninja.json')
|
|
8
|
+
const enlightenedSpecials = require('../../gotchi-battler-backend/data/specials_enlightened.json')
|
|
9
|
+
const cleaverSpecials = require('../../gotchi-battler-backend/data/specials_cleaver.json')
|
|
10
|
+
const tankSpecials = require('../../gotchi-battler-backend/data/specials_tank.json')
|
|
11
|
+
const cursedSpecials = require('../../gotchi-battler-backend/data/specials_cursed.json')
|
|
12
|
+
const healerSpecials = require('../../gotchi-battler-backend/data/specials_healer.json')
|
|
13
|
+
const mageSpecials = require('../../gotchi-battler-backend/data/specials_mage.json')
|
|
14
|
+
const trollSpecials = require('../../gotchi-battler-backend/data/specials_troll.json')
|
|
15
|
+
|
|
16
|
+
const specials = [
|
|
17
|
+
...ninjaSpecials, ...enlightenedSpecials,
|
|
18
|
+
...cleaverSpecials, ...tankSpecials,
|
|
19
|
+
...cursedSpecials, ...healerSpecials,
|
|
20
|
+
...mageSpecials, ...trollSpecials
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
const main = async () => {
|
|
24
|
+
for (const special of specials) {
|
|
25
|
+
// Mock the game loop
|
|
26
|
+
let team1 = require('./data/immaterialTeam1.json')
|
|
27
|
+
let team2 = require('./data/immaterialTeam2.json')
|
|
28
|
+
|
|
29
|
+
team1 = JSON.parse(JSON.stringify(team1))
|
|
30
|
+
team2 = JSON.parse(JSON.stringify(team2))
|
|
31
|
+
|
|
32
|
+
const seed = 'randomseed'
|
|
33
|
+
const rng = seedrandom(seed)
|
|
34
|
+
|
|
35
|
+
// Give all gotchis 100000 health for testing
|
|
36
|
+
const allAliveGotchis = [...getAlive(team1), ...getAlive(team2)]
|
|
37
|
+
allAliveGotchis.forEach(x => {
|
|
38
|
+
x.health = 1000000
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
prepareTeams(allAliveGotchis, team1, team2)
|
|
42
|
+
|
|
43
|
+
const logs = {
|
|
44
|
+
meta: {
|
|
45
|
+
seed,
|
|
46
|
+
timestamp: new Date(),
|
|
47
|
+
type: 'pvp',
|
|
48
|
+
campaign: {},
|
|
49
|
+
isBoss: false
|
|
50
|
+
},
|
|
51
|
+
gotchis: getLogGotchis(allAliveGotchis),
|
|
52
|
+
layout: {
|
|
53
|
+
teams: [
|
|
54
|
+
simplifyTeam(team1),
|
|
55
|
+
simplifyTeam(team2)
|
|
56
|
+
]
|
|
57
|
+
},
|
|
58
|
+
turns: [],
|
|
59
|
+
result: {},
|
|
60
|
+
debug: []
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
let turnCounter = 0
|
|
64
|
+
|
|
65
|
+
// Do the special attack 10 times in a row
|
|
66
|
+
for (let i = 0; i < 10; i++) {
|
|
67
|
+
const specialResults = attack({
|
|
68
|
+
...allAliveGotchis[0],
|
|
69
|
+
special: special.code,
|
|
70
|
+
specialExpanded: special
|
|
71
|
+
}, team1, team2, rng, true)
|
|
72
|
+
|
|
73
|
+
logs.turns.push({
|
|
74
|
+
index: turnCounter,
|
|
75
|
+
skipTurn: null,
|
|
76
|
+
action: {
|
|
77
|
+
user: allAliveGotchis[0].id,
|
|
78
|
+
name: special.code,
|
|
79
|
+
actionEffects: specialResults.actionEffects,
|
|
80
|
+
additionalEffects: specialResults.additionalEffects
|
|
81
|
+
},
|
|
82
|
+
statusEffects: [],
|
|
83
|
+
statusesExpired: specialResults.statusesExpired
|
|
84
|
+
})
|
|
85
|
+
turnCounter++
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Save logs to file
|
|
89
|
+
fs.writeFileSync(path.join(__dirname, 'output', 'specials', `${special.code}.json`), JSON.stringify(logs, null, 2))
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// node scripts/generateSpecialLogs.js
|
|
94
|
+
main()
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
const { battle } = require('..')
|
|
4
|
+
|
|
5
|
+
const campaigns = [
|
|
6
|
+
'yield_fields',
|
|
7
|
+
'daark_forest',
|
|
8
|
+
'caaverns',
|
|
9
|
+
'defi_desert',
|
|
10
|
+
'laughing_peaks',
|
|
11
|
+
'mount_oomf'
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
const team1 = require('./data/immaterialTeam1.json')
|
|
15
|
+
|
|
16
|
+
for (const campaign of campaigns) {
|
|
17
|
+
const campaignDir = path.join(__dirname, 'data', 'campaign', campaign)
|
|
18
|
+
const files = fs.readdirSync(campaignDir).filter(file => file.endsWith('.json'))
|
|
19
|
+
|
|
20
|
+
for (const file of files) {
|
|
21
|
+
const team2 = require(path.join(campaignDir, file))
|
|
22
|
+
|
|
23
|
+
const results = battle(team1, team2, 'randomseed', {
|
|
24
|
+
debug: false,
|
|
25
|
+
type: 'pve',
|
|
26
|
+
campaign: {
|
|
27
|
+
biome: file.split('_s')[0],
|
|
28
|
+
stage: file.split('_b')[0],
|
|
29
|
+
battle: file.replace('.json', '')
|
|
30
|
+
},
|
|
31
|
+
isBoss: file.includes('b6')
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
const resultsDir = path.join(__dirname, 'output', 'campaign', campaign)
|
|
35
|
+
|
|
36
|
+
// Write output to /scripts/output/campaign/<campaign>/<file>-results.json
|
|
37
|
+
fs.writeFileSync(path.join(resultsDir, file.replace('.json', '-results.json')), JSON.stringify(results, null, '\t'))
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// node scripts/runCampaignBattles.js
|
|
@@ -4,10 +4,13 @@ const { battle } = require('..')
|
|
|
4
4
|
|
|
5
5
|
// Edit these json files to test different battles
|
|
6
6
|
// NOTE: Only the in-game stats (speed, health, crit etc..) are used in the game logic
|
|
7
|
-
const team1 = require('./data/
|
|
8
|
-
const team2 = require('./data/
|
|
7
|
+
const team1 = require('./data/oggyMaxi.json')
|
|
8
|
+
const team2 = require('./data/wagdiddly.json')
|
|
9
9
|
|
|
10
|
-
const results = battle(team1, team2,
|
|
10
|
+
const results = battle(team1, team2, '82807311112923564712218359337695919195403960526804010606215202651499586140469', {
|
|
11
|
+
debug: true,
|
|
12
|
+
type: 'pvp'
|
|
13
|
+
})
|
|
11
14
|
|
|
12
15
|
const timestamp = new Date().getTime()
|
|
13
16
|
const resultsFilename = `results-${timestamp}.json`
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
const { battle } = require('..')
|
|
4
|
+
|
|
5
|
+
// Edit these json files to test different battles
|
|
6
|
+
// NOTE: Only the in-game stats (speed, health, crit etc..) are used in the game logic
|
|
7
|
+
const team1 = require('./data/team1.json')
|
|
8
|
+
const mob1 = require('./data/dungeon_mob_1.json')
|
|
9
|
+
const mob2 = require('./data/dungeon_mob_2.json')
|
|
10
|
+
|
|
11
|
+
const mobs = {
|
|
12
|
+
'DUNGEON_1_MOB_1': mob1,
|
|
13
|
+
'DUNGEON_1_MOB_2': mob2,
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const scenario = {
|
|
17
|
+
'id': 'scenario123',
|
|
18
|
+
'name': 'Dungeon 1',
|
|
19
|
+
'battles': [
|
|
20
|
+
{
|
|
21
|
+
'id': 'battle1',
|
|
22
|
+
'environment': 'DUNGEON_1_1',
|
|
23
|
+
'mob': 'DUNGEON_1_MOB_1',
|
|
24
|
+
},
|
|
25
|
+
{
|
|
26
|
+
'id': 'battle2',
|
|
27
|
+
'environment': 'DUNGEON_1_2',
|
|
28
|
+
'mob': 'DUNGEON_1_MOB_2',
|
|
29
|
+
}
|
|
30
|
+
]
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
for (let i = 0; i < scenario.battles.length; i++) {
|
|
34
|
+
const scenarioBattle = scenario.battles[i]
|
|
35
|
+
const logs = battle(team1, mobs[scenarioBattle.mob], 'arandomseed', true)
|
|
36
|
+
|
|
37
|
+
scenarioBattle.logs = logs
|
|
38
|
+
|
|
39
|
+
if (logs.result.winner === 1) {
|
|
40
|
+
team1.startingState = logs.result.winningTeam
|
|
41
|
+
} else {
|
|
42
|
+
break
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
const timestamp = new Date().getTime()
|
|
47
|
+
const resultsFilename = `results-${timestamp}.json`
|
|
48
|
+
fs.writeFileSync(path.join(__dirname, 'output', resultsFilename), JSON.stringify(scenario, null, '\t'))
|
|
49
|
+
|
|
50
|
+
console.log(`Results written to ${path.join(__dirname, 'output', resultsFilename)}`)
|
|
51
|
+
|
|
52
|
+
// node scripts/runLocalDungeon.js
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
const fs = require('fs')
|
|
2
|
+
const path = require('path')
|
|
3
|
+
const { battle } = require('..')
|
|
4
|
+
|
|
5
|
+
// Edit these json files to test different battles
|
|
6
|
+
const team1 = require('./data/immaterialTeam1.json')
|
|
7
|
+
const team2 = require('./data/immaterialTeam2.json')
|
|
8
|
+
|
|
9
|
+
const results = battle(team1, team2, 'randomseed', {
|
|
10
|
+
debug: false
|
|
11
|
+
})
|
|
12
|
+
|
|
13
|
+
fs.writeFileSync(path.join(__dirname, 'output', 'pvp-results.json'), JSON.stringify(results, null, '\t'))
|
|
14
|
+
|
|
15
|
+
// node scripts/runPvPBattle.js
|