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
package/game-logic/v1.5/index.js
CHANGED
|
@@ -128,7 +128,7 @@ const getNextToAct = (team1, team2, rng) => {
|
|
|
128
128
|
|
|
129
129
|
const getTarget = (defendingTeam, rng) => {
|
|
130
130
|
// Check for taunt gotchis
|
|
131
|
-
const taunt = [...getAlive(defendingTeam, 'front'), ...getAlive(defendingTeam, 'back')].filter(gotchi => gotchi.statuses && gotchi.statuses.includes(
|
|
131
|
+
const taunt = [...getAlive(defendingTeam, 'front'), ...getAlive(defendingTeam, 'back')].filter(gotchi => gotchi.statuses && gotchi.statuses.includes('taunt'))
|
|
132
132
|
|
|
133
133
|
if (taunt.length) {
|
|
134
134
|
if (taunt.length === 1) return taunt[0]
|
|
@@ -513,7 +513,7 @@ const scrambleGotchiIds = (allAliveGotchis, team1, team2) => {
|
|
|
513
513
|
**/
|
|
514
514
|
const prepareTeams = (allAliveGotchis, team1, team2) => {
|
|
515
515
|
// check there's no duplicate gotchis
|
|
516
|
-
scrambleGotchiIds(allAliveGotchis, team1, team2)
|
|
516
|
+
scrambleGotchiIds(allAliveGotchis, team1, team2)
|
|
517
517
|
|
|
518
518
|
allAliveGotchis.forEach(x => {
|
|
519
519
|
// Add statuses property to all gotchis
|
|
@@ -535,7 +535,7 @@ const prepareTeams = (allAliveGotchis, team1, team2) => {
|
|
|
535
535
|
|
|
536
536
|
// Add leader passive to team
|
|
537
537
|
addLeaderToTeam(team1)
|
|
538
|
-
addLeaderToTeam(team2)
|
|
538
|
+
addLeaderToTeam(team2)
|
|
539
539
|
}
|
|
540
540
|
|
|
541
541
|
/**
|
|
@@ -581,20 +581,20 @@ const getLogGotchis = (allAliveGotchis) => {
|
|
|
581
581
|
* @returns {Object} logs The battle logs
|
|
582
582
|
*/
|
|
583
583
|
const gameLoop = (team1, team2, seed, debug) => {
|
|
584
|
-
if (!team1) throw new Error(
|
|
585
|
-
if (!team2) throw new Error(
|
|
586
|
-
if (!seed) throw new Error(
|
|
584
|
+
if (!team1) throw new Error('Team 1 not found')
|
|
585
|
+
if (!team2) throw new Error('Team 2 not found')
|
|
586
|
+
if (!seed) throw new Error('Seed not found')
|
|
587
587
|
|
|
588
588
|
// Validate team objects
|
|
589
589
|
const team1Validation = validator.validate(team1, teamSchema)
|
|
590
590
|
if (!team1Validation) {
|
|
591
591
|
console.error('Team 1 validation failed: ', JSON.stringify(validator.getLastErrors(), null, 2))
|
|
592
|
-
throw new Error(
|
|
592
|
+
throw new Error('Team 1 validation failed')
|
|
593
593
|
}
|
|
594
594
|
const team2Validation = validator.validate(team2, teamSchema)
|
|
595
595
|
if (!team2Validation) {
|
|
596
596
|
console.error('Team 2 validation failed: ', JSON.stringify(validator.getLastErrors(), null, 2))
|
|
597
|
-
throw new Error(
|
|
597
|
+
throw new Error('Team 2 validation failed')
|
|
598
598
|
}
|
|
599
599
|
|
|
600
600
|
const rng = seedrandom(seed)
|
|
@@ -612,7 +612,7 @@ const gameLoop = (team1, team2, seed, debug) => {
|
|
|
612
612
|
]
|
|
613
613
|
},
|
|
614
614
|
turns: []
|
|
615
|
-
}
|
|
615
|
+
}
|
|
616
616
|
|
|
617
617
|
// Used for turn by turn health and status summaries
|
|
618
618
|
// Deleted if not in development or no errors
|
|
@@ -640,7 +640,7 @@ const gameLoop = (team1, team2, seed, debug) => {
|
|
|
640
640
|
turnLogs.statusesExpired = [...turnLogs.statusesExpired, ...getExpiredStatuses(team1, team2)]
|
|
641
641
|
}
|
|
642
642
|
|
|
643
|
-
logs.turns.push({index: turnCounter, ...turnLogs})
|
|
643
|
+
logs.turns.push({ index: turnCounter, ...turnLogs })
|
|
644
644
|
|
|
645
645
|
if (debug) {
|
|
646
646
|
logs.debug.push({
|
|
@@ -850,12 +850,10 @@ const attack = (attackingGotchi, attackingTeam, defendingTeam, defendingTargets,
|
|
|
850
850
|
}
|
|
851
851
|
|
|
852
852
|
// Deal with start of turn status effects
|
|
853
|
-
const handleStatusEffects = (attackingGotchi, attackingTeam, defendingTeam
|
|
853
|
+
const handleStatusEffects = (attackingGotchi, attackingTeam, defendingTeam) => {
|
|
854
854
|
const statusEffects = []
|
|
855
855
|
const passiveEffects = []
|
|
856
856
|
|
|
857
|
-
const modifiedAttackingGotchi = getModifiedStats(attackingGotchi)
|
|
858
|
-
|
|
859
857
|
// Check for cleansing_aura
|
|
860
858
|
// if (attackingGotchi.statuses.includes('cleansing_aura')) {
|
|
861
859
|
// // Remove all debuffs from all allies
|
|
@@ -1098,7 +1096,7 @@ const specialAttack = (attackingGotchi, attackingTeam, defendingTeam, rng) => {
|
|
|
1098
1096
|
const modifiedAttackingGotchi = getModifiedStats(attackingGotchi)
|
|
1099
1097
|
|
|
1100
1098
|
switch (specialId) {
|
|
1101
|
-
case 1:
|
|
1099
|
+
case 1: {
|
|
1102
1100
|
// Spectral Strike - ignore armor and appply bleed status
|
|
1103
1101
|
// get single target
|
|
1104
1102
|
const ssTarget = getTarget(defendingTeam, rng)
|
|
@@ -1113,7 +1111,8 @@ const specialAttack = (attackingGotchi, attackingTeam, defendingTeam, rng) => {
|
|
|
1113
1111
|
noResistSpeedPenalty: true
|
|
1114
1112
|
})
|
|
1115
1113
|
break
|
|
1116
|
-
|
|
1114
|
+
}
|
|
1115
|
+
case 2: {
|
|
1117
1116
|
// Meditate - Boost own speed, magic, physical by 30%
|
|
1118
1117
|
// If gotchi already has 2 power_up statuses, do nothing
|
|
1119
1118
|
if (!addStatusToGotchi(attackingGotchi, 'power_up_2')) {
|
|
@@ -1147,7 +1146,8 @@ const specialAttack = (attackingGotchi, attackingTeam, defendingTeam, rng) => {
|
|
|
1147
1146
|
}
|
|
1148
1147
|
|
|
1149
1148
|
break
|
|
1150
|
-
|
|
1149
|
+
}
|
|
1150
|
+
case 3: {
|
|
1151
1151
|
// Cleave - attack all enemies in a row (that have the most gotchis) for 75% damage
|
|
1152
1152
|
// Find row with most gotchis
|
|
1153
1153
|
const cleaveRow = getAlive(defendingTeam, 'front').length > getAlive(defendingTeam, 'back').length ? 'front' : 'back'
|
|
@@ -1159,7 +1159,8 @@ const specialAttack = (attackingGotchi, attackingTeam, defendingTeam, rng) => {
|
|
|
1159
1159
|
noPassiveStatuses: true
|
|
1160
1160
|
})
|
|
1161
1161
|
break
|
|
1162
|
-
|
|
1162
|
+
}
|
|
1163
|
+
case 4: {
|
|
1163
1164
|
// Taunt - add taunt status to self
|
|
1164
1165
|
|
|
1165
1166
|
// Check if gotchi already has taunt status
|
|
@@ -1181,7 +1182,8 @@ const specialAttack = (attackingGotchi, attackingTeam, defendingTeam, rng) => {
|
|
|
1181
1182
|
}
|
|
1182
1183
|
]
|
|
1183
1184
|
break
|
|
1184
|
-
|
|
1185
|
+
}
|
|
1186
|
+
case 5: {
|
|
1185
1187
|
// Curse - attack random enemy for 50% damage, apply fear status and remove all buffs
|
|
1186
1188
|
|
|
1187
1189
|
const curseTarget = getTarget(defendingTeam, rng)
|
|
@@ -1228,7 +1230,8 @@ const specialAttack = (attackingGotchi, attackingTeam, defendingTeam, rng) => {
|
|
|
1228
1230
|
}
|
|
1229
1231
|
|
|
1230
1232
|
break
|
|
1231
|
-
|
|
1233
|
+
}
|
|
1234
|
+
case 6: {
|
|
1232
1235
|
// Blessing - Heal all non-healer allies and remove all debuffs
|
|
1233
1236
|
|
|
1234
1237
|
// Get all alive non-healer allies on the attacking team
|
|
@@ -1292,7 +1295,8 @@ const specialAttack = (attackingGotchi, attackingTeam, defendingTeam, rng) => {
|
|
|
1292
1295
|
}
|
|
1293
1296
|
|
|
1294
1297
|
break
|
|
1295
|
-
|
|
1298
|
+
}
|
|
1299
|
+
case 7: {
|
|
1296
1300
|
// Thunder - Attack all enemies for 50% damage and apply stun status
|
|
1297
1301
|
|
|
1298
1302
|
const thunderTargets = getAlive(defendingTeam)
|
|
@@ -1318,7 +1322,8 @@ const specialAttack = (attackingGotchi, attackingTeam, defendingTeam, rng) => {
|
|
|
1318
1322
|
}
|
|
1319
1323
|
|
|
1320
1324
|
break
|
|
1321
|
-
|
|
1325
|
+
}
|
|
1326
|
+
case 8: {
|
|
1322
1327
|
// Devestating Smash - Attack random enemy for 200% damage
|
|
1323
1328
|
|
|
1324
1329
|
const smashTarget = getTarget(defendingTeam, rng)
|
|
@@ -1362,6 +1367,7 @@ const specialAttack = (attackingGotchi, attackingTeam, defendingTeam, rng) => {
|
|
|
1362
1367
|
}
|
|
1363
1368
|
|
|
1364
1369
|
break
|
|
1370
|
+
}
|
|
1365
1371
|
}
|
|
1366
1372
|
|
|
1367
1373
|
return {
|
|
@@ -86,29 +86,6 @@ const MULTS = {
|
|
|
86
86
|
CLAN_MOMENTUM_DAMAGE: 3.1
|
|
87
87
|
}
|
|
88
88
|
|
|
89
|
-
const passiveIcons = {
|
|
90
|
-
'sharp_blades': 'https://game-icons.net/1x1/lorc/plain-dagger.html',
|
|
91
|
-
'cloud_of_zen': 'https://game-icons.net/1x1/lorc/meditation.html',
|
|
92
|
-
'frenzy': 'https://game-icons.net/1x1/lorc/totem-head.html',
|
|
93
|
-
'fortify': 'https://game-icons.net/1x1/lorc/crenulated-shield.html',
|
|
94
|
-
'spread_the_fear': 'https://game-icons.net/1x1/lorc/evil-book.html',
|
|
95
|
-
'cleansing_aura': 'https://game-icons.net/1x1/lorc/aura.html',
|
|
96
|
-
'channel_the_coven': 'https://game-icons.net/1x1/lorc/witch-flight.html',
|
|
97
|
-
'clan_momentum': 'https://game-icons.net/1x1/delapouite/bully-minion.html'
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
const debuffIcons = {
|
|
101
|
-
'bleed': 'https://game-icons.net/1x1/lorc/broken-heart.html',
|
|
102
|
-
'stun': 'https://game-icons.net/1x1/sbed/electric.html',
|
|
103
|
-
'fear': 'https://game-icons.net/1x1/lorc/screaming.html'
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
const buffIcons = {
|
|
107
|
-
'taunt': 'https://game-icons.net/1x1/lorc/archery-target.html',
|
|
108
|
-
'power_up_1': 'https://game-icons.net/1x1/lorc/strong.html',
|
|
109
|
-
'power_up_2': 'https://game-icons.net/1x1/delapouite/mighty-force.html'
|
|
110
|
-
}
|
|
111
|
-
|
|
112
89
|
module.exports = {
|
|
113
90
|
PASSIVES,
|
|
114
91
|
DEBUFF_STATUSES,
|
package/game-logic/v1.6/index.js
CHANGED
|
@@ -128,7 +128,7 @@ const getNextToAct = (team1, team2, rng) => {
|
|
|
128
128
|
|
|
129
129
|
const getTarget = (defendingTeam, rng) => {
|
|
130
130
|
// Check for taunt gotchis
|
|
131
|
-
const taunt = [...getAlive(defendingTeam, 'front'), ...getAlive(defendingTeam, 'back')].filter(gotchi => gotchi.statuses && gotchi.statuses.includes(
|
|
131
|
+
const taunt = [...getAlive(defendingTeam, 'front'), ...getAlive(defendingTeam, 'back')].filter(gotchi => gotchi.statuses && gotchi.statuses.includes('taunt'))
|
|
132
132
|
|
|
133
133
|
if (taunt.length) {
|
|
134
134
|
if (taunt.length === 1) return taunt[0]
|
|
@@ -513,7 +513,7 @@ const scrambleGotchiIds = (allAliveGotchis, team1, team2) => {
|
|
|
513
513
|
**/
|
|
514
514
|
const prepareTeams = (allAliveGotchis, team1, team2) => {
|
|
515
515
|
// check there's no duplicate gotchis
|
|
516
|
-
scrambleGotchiIds(allAliveGotchis, team1, team2)
|
|
516
|
+
scrambleGotchiIds(allAliveGotchis, team1, team2)
|
|
517
517
|
|
|
518
518
|
allAliveGotchis.forEach(x => {
|
|
519
519
|
// Add statuses property to all gotchis
|
|
@@ -535,7 +535,7 @@ const prepareTeams = (allAliveGotchis, team1, team2) => {
|
|
|
535
535
|
|
|
536
536
|
// Add leader passive to team
|
|
537
537
|
addLeaderToTeam(team1)
|
|
538
|
-
addLeaderToTeam(team2)
|
|
538
|
+
addLeaderToTeam(team2)
|
|
539
539
|
}
|
|
540
540
|
|
|
541
541
|
/**
|
|
@@ -581,20 +581,20 @@ const getLogGotchis = (allAliveGotchis) => {
|
|
|
581
581
|
* @returns {Object} logs The battle logs
|
|
582
582
|
*/
|
|
583
583
|
const gameLoop = (team1, team2, seed, debug) => {
|
|
584
|
-
if (!team1) throw new Error(
|
|
585
|
-
if (!team2) throw new Error(
|
|
586
|
-
if (!seed) throw new Error(
|
|
584
|
+
if (!team1) throw new Error('Team 1 not found')
|
|
585
|
+
if (!team2) throw new Error('Team 2 not found')
|
|
586
|
+
if (!seed) throw new Error('Seed not found')
|
|
587
587
|
|
|
588
588
|
// Validate team objects
|
|
589
589
|
const team1Validation = validator.validate(team1, teamSchema)
|
|
590
590
|
if (!team1Validation) {
|
|
591
591
|
console.error('Team 1 validation failed: ', JSON.stringify(validator.getLastErrors(), null, 2))
|
|
592
|
-
throw new Error(
|
|
592
|
+
throw new Error('Team 1 validation failed')
|
|
593
593
|
}
|
|
594
594
|
const team2Validation = validator.validate(team2, teamSchema)
|
|
595
595
|
if (!team2Validation) {
|
|
596
596
|
console.error('Team 2 validation failed: ', JSON.stringify(validator.getLastErrors(), null, 2))
|
|
597
|
-
throw new Error(
|
|
597
|
+
throw new Error('Team 2 validation failed')
|
|
598
598
|
}
|
|
599
599
|
|
|
600
600
|
// Make deep copy of team objects to avoid modifying the original objects
|
|
@@ -616,7 +616,7 @@ const gameLoop = (team1, team2, seed, debug) => {
|
|
|
616
616
|
]
|
|
617
617
|
},
|
|
618
618
|
turns: []
|
|
619
|
-
}
|
|
619
|
+
}
|
|
620
620
|
|
|
621
621
|
// Used for turn by turn health and status summaries
|
|
622
622
|
// Deleted if not in development or no errors
|
|
@@ -644,7 +644,7 @@ const gameLoop = (team1, team2, seed, debug) => {
|
|
|
644
644
|
turnLogs.statusesExpired = [...turnLogs.statusesExpired, ...getExpiredStatuses(team1, team2)]
|
|
645
645
|
}
|
|
646
646
|
|
|
647
|
-
logs.turns.push({index: turnCounter, ...turnLogs})
|
|
647
|
+
logs.turns.push({ index: turnCounter, ...turnLogs })
|
|
648
648
|
|
|
649
649
|
if (debug) {
|
|
650
650
|
logs.debug.push({
|
|
@@ -854,12 +854,10 @@ const attack = (attackingGotchi, attackingTeam, defendingTeam, defendingTargets,
|
|
|
854
854
|
}
|
|
855
855
|
|
|
856
856
|
// Deal with start of turn status effects
|
|
857
|
-
const handleStatusEffects = (attackingGotchi, attackingTeam, defendingTeam
|
|
857
|
+
const handleStatusEffects = (attackingGotchi, attackingTeam, defendingTeam) => {
|
|
858
858
|
const statusEffects = []
|
|
859
859
|
const passiveEffects = []
|
|
860
860
|
|
|
861
|
-
const modifiedAttackingGotchi = getModifiedStats(attackingGotchi)
|
|
862
|
-
|
|
863
861
|
// Check for cleansing_aura
|
|
864
862
|
// if (attackingGotchi.statuses.includes('cleansing_aura')) {
|
|
865
863
|
// // Remove all debuffs from all allies
|
|
@@ -1103,7 +1101,7 @@ const specialAttack = (attackingGotchi, attackingTeam, defendingTeam, rng) => {
|
|
|
1103
1101
|
const modifiedAttackingGotchi = getModifiedStats(attackingGotchi)
|
|
1104
1102
|
|
|
1105
1103
|
switch (specialId) {
|
|
1106
|
-
case 1:
|
|
1104
|
+
case 1: {
|
|
1107
1105
|
// Spectral Strike - ignore armor and appply bleed status
|
|
1108
1106
|
// get single target
|
|
1109
1107
|
const ssTarget = getTarget(defendingTeam, rng)
|
|
@@ -1118,7 +1116,8 @@ const specialAttack = (attackingGotchi, attackingTeam, defendingTeam, rng) => {
|
|
|
1118
1116
|
noResistSpeedPenalty: true
|
|
1119
1117
|
})
|
|
1120
1118
|
break
|
|
1121
|
-
|
|
1119
|
+
}
|
|
1120
|
+
case 2: {
|
|
1122
1121
|
// Meditate - Boost own speed, magic, physical by 30%
|
|
1123
1122
|
// If gotchi already has 2 power_up statuses, do nothing
|
|
1124
1123
|
if (!addStatusToGotchi(attackingGotchi, 'power_up_2')) {
|
|
@@ -1152,7 +1151,8 @@ const specialAttack = (attackingGotchi, attackingTeam, defendingTeam, rng) => {
|
|
|
1152
1151
|
}
|
|
1153
1152
|
|
|
1154
1153
|
break
|
|
1155
|
-
|
|
1154
|
+
}
|
|
1155
|
+
case 3: {
|
|
1156
1156
|
// Cleave - attack all enemies in a row (that have the most gotchis) for 75% damage
|
|
1157
1157
|
// Find row with most gotchis
|
|
1158
1158
|
const cleaveRow = getAlive(defendingTeam, 'front').length > getAlive(defendingTeam, 'back').length ? 'front' : 'back'
|
|
@@ -1164,7 +1164,8 @@ const specialAttack = (attackingGotchi, attackingTeam, defendingTeam, rng) => {
|
|
|
1164
1164
|
noPassiveStatuses: true
|
|
1165
1165
|
})
|
|
1166
1166
|
break
|
|
1167
|
-
|
|
1167
|
+
}
|
|
1168
|
+
case 4: {
|
|
1168
1169
|
// Taunt - add taunt status to self
|
|
1169
1170
|
|
|
1170
1171
|
// Check if gotchi already has taunt status
|
|
@@ -1186,7 +1187,8 @@ const specialAttack = (attackingGotchi, attackingTeam, defendingTeam, rng) => {
|
|
|
1186
1187
|
}
|
|
1187
1188
|
]
|
|
1188
1189
|
break
|
|
1189
|
-
|
|
1190
|
+
}
|
|
1191
|
+
case 5: {
|
|
1190
1192
|
// Curse - attack random enemy for 50% damage, apply fear status and remove all buffs
|
|
1191
1193
|
|
|
1192
1194
|
const curseTarget = getTarget(defendingTeam, rng)
|
|
@@ -1251,7 +1253,8 @@ const specialAttack = (attackingGotchi, attackingTeam, defendingTeam, rng) => {
|
|
|
1251
1253
|
}
|
|
1252
1254
|
|
|
1253
1255
|
break
|
|
1254
|
-
|
|
1256
|
+
}
|
|
1257
|
+
case 6: {
|
|
1255
1258
|
// Blessing - Heal all non-healer allies and remove all debuffs
|
|
1256
1259
|
|
|
1257
1260
|
// Get all alive non-healer allies on the attacking team
|
|
@@ -1320,7 +1323,8 @@ const specialAttack = (attackingGotchi, attackingTeam, defendingTeam, rng) => {
|
|
|
1320
1323
|
}
|
|
1321
1324
|
|
|
1322
1325
|
break
|
|
1323
|
-
|
|
1326
|
+
}
|
|
1327
|
+
case 7: {
|
|
1324
1328
|
// Thunder - Attack all enemies for 50% damage and apply stun status
|
|
1325
1329
|
|
|
1326
1330
|
const thunderTargets = getAlive(defendingTeam)
|
|
@@ -1347,7 +1351,8 @@ const specialAttack = (attackingGotchi, attackingTeam, defendingTeam, rng) => {
|
|
|
1347
1351
|
}
|
|
1348
1352
|
|
|
1349
1353
|
break
|
|
1350
|
-
|
|
1354
|
+
}
|
|
1355
|
+
case 8: {
|
|
1351
1356
|
// Devestating Smash - Attack random enemy for 200% damage
|
|
1352
1357
|
|
|
1353
1358
|
const smashTarget = getTarget(defendingTeam, rng)
|
|
@@ -1391,6 +1396,7 @@ const specialAttack = (attackingGotchi, attackingTeam, defendingTeam, rng) => {
|
|
|
1391
1396
|
}
|
|
1392
1397
|
|
|
1393
1398
|
break
|
|
1399
|
+
}
|
|
1394
1400
|
}
|
|
1395
1401
|
|
|
1396
1402
|
return {
|
|
@@ -105,29 +105,6 @@ const MULTS = {
|
|
|
105
105
|
DEVESTATING_SMASH_X2_DAMAGE: 1.5,
|
|
106
106
|
}
|
|
107
107
|
|
|
108
|
-
const passiveIcons = {
|
|
109
|
-
'sharp_blades': 'https://game-icons.net/1x1/lorc/plain-dagger.html',
|
|
110
|
-
'cloud_of_zen': 'https://game-icons.net/1x1/lorc/meditation.html',
|
|
111
|
-
'frenzy': 'https://game-icons.net/1x1/lorc/totem-head.html',
|
|
112
|
-
'fortify': 'https://game-icons.net/1x1/lorc/crenulated-shield.html',
|
|
113
|
-
'spread_the_fear': 'https://game-icons.net/1x1/lorc/evil-book.html',
|
|
114
|
-
'cleansing_aura': 'https://game-icons.net/1x1/lorc/aura.html',
|
|
115
|
-
'channel_the_coven': 'https://game-icons.net/1x1/lorc/witch-flight.html',
|
|
116
|
-
'clan_momentum': 'https://game-icons.net/1x1/delapouite/bully-minion.html'
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
const debuffIcons = {
|
|
120
|
-
'bleed': 'https://game-icons.net/1x1/lorc/broken-heart.html',
|
|
121
|
-
'stun': 'https://game-icons.net/1x1/sbed/electric.html',
|
|
122
|
-
'fear': 'https://game-icons.net/1x1/lorc/screaming.html'
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
const buffIcons = {
|
|
126
|
-
'taunt': 'https://game-icons.net/1x1/lorc/archery-target.html',
|
|
127
|
-
'power_up_1': 'https://game-icons.net/1x1/lorc/strong.html',
|
|
128
|
-
'power_up_2': 'https://game-icons.net/1x1/delapouite/mighty-force.html'
|
|
129
|
-
}
|
|
130
|
-
|
|
131
108
|
module.exports = {
|
|
132
109
|
PASSIVES,
|
|
133
110
|
DEBUFF_STATUSES,
|
|
@@ -119,7 +119,7 @@ const getNextToAct = (team1, team2, rng) => {
|
|
|
119
119
|
|
|
120
120
|
const getTarget = (defendingTeam, rng) => {
|
|
121
121
|
// Check for taunt gotchis
|
|
122
|
-
const taunt = [...getAlive(defendingTeam, 'front'), ...getAlive(defendingTeam, 'back')].filter(gotchi => gotchi.statuses && gotchi.statuses.includes(
|
|
122
|
+
const taunt = [...getAlive(defendingTeam, 'front'), ...getAlive(defendingTeam, 'back')].filter(gotchi => gotchi.statuses && gotchi.statuses.includes('taunt'))
|
|
123
123
|
|
|
124
124
|
if (taunt.length) {
|
|
125
125
|
if (taunt.length === 1) return taunt[0]
|
|
@@ -509,7 +509,7 @@ const scrambleGotchiIds = (allAliveGotchis, team1, team2) => {
|
|
|
509
509
|
**/
|
|
510
510
|
const prepareTeams = (allAliveGotchis, team1, team2) => {
|
|
511
511
|
// check there's no duplicate gotchis
|
|
512
|
-
scrambleGotchiIds(allAliveGotchis, team1, team2)
|
|
512
|
+
scrambleGotchiIds(allAliveGotchis, team1, team2)
|
|
513
513
|
|
|
514
514
|
// Apply stat items
|
|
515
515
|
applyStatItems(allAliveGotchis)
|
package/game-logic/v1.7/index.js
CHANGED
|
@@ -35,20 +35,20 @@ const {
|
|
|
35
35
|
* @returns {Object} logs The battle logs
|
|
36
36
|
*/
|
|
37
37
|
const gameLoop = (team1, team2, seed, debug) => {
|
|
38
|
-
if (!team1) throw new Error(
|
|
39
|
-
if (!team2) throw new Error(
|
|
40
|
-
if (!seed) throw new Error(
|
|
38
|
+
if (!team1) throw new Error('Team 1 not found')
|
|
39
|
+
if (!team2) throw new Error('Team 2 not found')
|
|
40
|
+
if (!seed) throw new Error('Seed not found')
|
|
41
41
|
|
|
42
42
|
// Validate team objects
|
|
43
43
|
const team1Validation = validator.validate(team1, teamSchema)
|
|
44
44
|
if (!team1Validation) {
|
|
45
45
|
console.error('Team 1 validation failed: ', JSON.stringify(validator.getLastErrors(), null, 2))
|
|
46
|
-
throw new Error(
|
|
46
|
+
throw new Error('Team 1 validation failed')
|
|
47
47
|
}
|
|
48
48
|
const team2Validation = validator.validate(team2, teamSchema)
|
|
49
49
|
if (!team2Validation) {
|
|
50
50
|
console.error('Team 2 validation failed: ', JSON.stringify(validator.getLastErrors(), null, 2))
|
|
51
|
-
throw new Error(
|
|
51
|
+
throw new Error('Team 2 validation failed')
|
|
52
52
|
}
|
|
53
53
|
|
|
54
54
|
// Make deep copy of team objects to avoid modifying the original objects
|
|
@@ -70,7 +70,7 @@ const gameLoop = (team1, team2, seed, debug) => {
|
|
|
70
70
|
]
|
|
71
71
|
},
|
|
72
72
|
turns: []
|
|
73
|
-
}
|
|
73
|
+
}
|
|
74
74
|
|
|
75
75
|
// Used for turn by turn health and status summaries
|
|
76
76
|
// Deleted if not in development or no errors
|
|
@@ -98,7 +98,7 @@ const gameLoop = (team1, team2, seed, debug) => {
|
|
|
98
98
|
turnLogs.statusesExpired = [...turnLogs.statusesExpired, ...getExpiredStatuses(team1, team2)]
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
logs.turns.push({index: turnCounter, ...turnLogs})
|
|
101
|
+
logs.turns.push({ index: turnCounter, ...turnLogs })
|
|
102
102
|
|
|
103
103
|
if (debug) {
|
|
104
104
|
logs.debug.push({
|
|
@@ -306,12 +306,10 @@ const attack = (attackingGotchi, attackingTeam, defendingTeam, defendingTargets,
|
|
|
306
306
|
}
|
|
307
307
|
|
|
308
308
|
// Deal with start of turn status effects
|
|
309
|
-
const handleStatusEffects = (attackingGotchi, attackingTeam, defendingTeam
|
|
309
|
+
const handleStatusEffects = (attackingGotchi, attackingTeam, defendingTeam) => {
|
|
310
310
|
const statusEffects = []
|
|
311
311
|
const passiveEffects = []
|
|
312
312
|
|
|
313
|
-
const modifiedAttackingGotchi = getModifiedStats(attackingGotchi)
|
|
314
|
-
|
|
315
313
|
// Check for global status effects
|
|
316
314
|
const allAliveGotchis = [...getAlive(attackingTeam), ...getAlive(defendingTeam)]
|
|
317
315
|
|
|
@@ -513,7 +511,7 @@ const specialAttack = (attackingGotchi, attackingTeam, defendingTeam, rng) => {
|
|
|
513
511
|
const modifiedAttackingGotchi = getModifiedStats(attackingGotchi)
|
|
514
512
|
|
|
515
513
|
switch (specialId) {
|
|
516
|
-
case 1:
|
|
514
|
+
case 1: {
|
|
517
515
|
// Spectral Strike - ignore armor and appply bleed status
|
|
518
516
|
// get single target
|
|
519
517
|
const ssTarget = getTarget(defendingTeam, rng)
|
|
@@ -528,7 +526,8 @@ const specialAttack = (attackingGotchi, attackingTeam, defendingTeam, rng) => {
|
|
|
528
526
|
noResistSpeedPenalty: true
|
|
529
527
|
})
|
|
530
528
|
break
|
|
531
|
-
|
|
529
|
+
}
|
|
530
|
+
case 2: {
|
|
532
531
|
// Meditate - Boost own speed, magic, physical by 30%
|
|
533
532
|
// If gotchi already has 2 power_up statuses, do nothing
|
|
534
533
|
if (!addStatusToGotchi(attackingGotchi, 'power_up_2')) {
|
|
@@ -562,7 +561,8 @@ const specialAttack = (attackingGotchi, attackingTeam, defendingTeam, rng) => {
|
|
|
562
561
|
}
|
|
563
562
|
|
|
564
563
|
break
|
|
565
|
-
|
|
564
|
+
}
|
|
565
|
+
case 3: {
|
|
566
566
|
// Cleave - attack all enemies in a row (that have the most gotchis) for 75% damage
|
|
567
567
|
// Find row with most gotchis
|
|
568
568
|
const cleaveRow = getAlive(defendingTeam, 'front').length > getAlive(defendingTeam, 'back').length ? 'front' : 'back'
|
|
@@ -574,7 +574,8 @@ const specialAttack = (attackingGotchi, attackingTeam, defendingTeam, rng) => {
|
|
|
574
574
|
noPassiveStatuses: true
|
|
575
575
|
})
|
|
576
576
|
break
|
|
577
|
-
|
|
577
|
+
}
|
|
578
|
+
case 4: {
|
|
578
579
|
// Taunt - add taunt status to self
|
|
579
580
|
|
|
580
581
|
// Check if gotchi already has taunt status
|
|
@@ -596,7 +597,8 @@ const specialAttack = (attackingGotchi, attackingTeam, defendingTeam, rng) => {
|
|
|
596
597
|
}
|
|
597
598
|
]
|
|
598
599
|
break
|
|
599
|
-
|
|
600
|
+
}
|
|
601
|
+
case 5: {
|
|
600
602
|
// Curse - attack random enemy for 50% damage, apply fear status and remove all buffs
|
|
601
603
|
|
|
602
604
|
const curseTarget = getTarget(defendingTeam, rng)
|
|
@@ -661,7 +663,8 @@ const specialAttack = (attackingGotchi, attackingTeam, defendingTeam, rng) => {
|
|
|
661
663
|
}
|
|
662
664
|
|
|
663
665
|
break
|
|
664
|
-
|
|
666
|
+
}
|
|
667
|
+
case 6: {
|
|
665
668
|
// Blessing - Heal all non-healer allies and remove all debuffs
|
|
666
669
|
|
|
667
670
|
// Get all alive non-healer allies on the attacking team
|
|
@@ -731,7 +734,8 @@ const specialAttack = (attackingGotchi, attackingTeam, defendingTeam, rng) => {
|
|
|
731
734
|
}
|
|
732
735
|
|
|
733
736
|
break
|
|
734
|
-
|
|
737
|
+
}
|
|
738
|
+
case 7: {
|
|
735
739
|
// Thunder - Attack all enemies for 50% damage and apply stun status
|
|
736
740
|
|
|
737
741
|
const thunderTargets = getAlive(defendingTeam)
|
|
@@ -752,7 +756,8 @@ const specialAttack = (attackingGotchi, attackingTeam, defendingTeam, rng) => {
|
|
|
752
756
|
})
|
|
753
757
|
|
|
754
758
|
break
|
|
755
|
-
|
|
759
|
+
}
|
|
760
|
+
case 8: {
|
|
756
761
|
// Devestating Smash - Attack random enemy for 200% damage
|
|
757
762
|
|
|
758
763
|
const smashTarget = getTarget(defendingTeam, rng)
|
|
@@ -787,6 +792,7 @@ const specialAttack = (attackingGotchi, attackingTeam, defendingTeam, rng) => {
|
|
|
787
792
|
}
|
|
788
793
|
|
|
789
794
|
break
|
|
795
|
+
}
|
|
790
796
|
}
|
|
791
797
|
|
|
792
798
|
return {
|
|
@@ -98,29 +98,6 @@ const MULTS = {
|
|
|
98
98
|
DEVESTATING_SMASH_X2_DAMAGE: 1.9,
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
const passiveIcons = {
|
|
102
|
-
'sharp_blades': 'https://game-icons.net/1x1/lorc/plain-dagger.html',
|
|
103
|
-
'cloud_of_zen': 'https://game-icons.net/1x1/lorc/meditation.html',
|
|
104
|
-
'frenzy': 'https://game-icons.net/1x1/lorc/totem-head.html',
|
|
105
|
-
'fortify': 'https://game-icons.net/1x1/lorc/crenulated-shield.html',
|
|
106
|
-
'spread_the_fear': 'https://game-icons.net/1x1/lorc/evil-book.html',
|
|
107
|
-
'cleansing_aura': 'https://game-icons.net/1x1/lorc/aura.html',
|
|
108
|
-
'channel_the_coven': 'https://game-icons.net/1x1/lorc/witch-flight.html',
|
|
109
|
-
'clan_momentum': 'https://game-icons.net/1x1/delapouite/bully-minion.html'
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
const debuffIcons = {
|
|
113
|
-
'bleed': 'https://game-icons.net/1x1/lorc/broken-heart.html',
|
|
114
|
-
'stun': 'https://game-icons.net/1x1/sbed/electric.html',
|
|
115
|
-
'fear': 'https://game-icons.net/1x1/lorc/screaming.html'
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const buffIcons = {
|
|
119
|
-
'taunt': 'https://game-icons.net/1x1/lorc/archery-target.html',
|
|
120
|
-
'cloud_of_zen': 'https://game-icons.net/1x1/lorc/strong.html',
|
|
121
|
-
'power_up_2': 'https://game-icons.net/1x1/delapouite/mighty-force.html'
|
|
122
|
-
}
|
|
123
|
-
|
|
124
101
|
module.exports = {
|
|
125
102
|
PASSIVES,
|
|
126
103
|
DEBUFF_STATUSES,
|
|
@@ -119,7 +119,7 @@ const getNextToAct = (team1, team2, rng) => {
|
|
|
119
119
|
|
|
120
120
|
const getTarget = (defendingTeam, rng) => {
|
|
121
121
|
// Check for taunt gotchis
|
|
122
|
-
const taunt = [...getAlive(defendingTeam, 'front'), ...getAlive(defendingTeam, 'back')].filter(gotchi => gotchi.statuses && gotchi.statuses.includes(
|
|
122
|
+
const taunt = [...getAlive(defendingTeam, 'front'), ...getAlive(defendingTeam, 'back')].filter(gotchi => gotchi.statuses && gotchi.statuses.includes('taunt'))
|
|
123
123
|
|
|
124
124
|
if (taunt.length) {
|
|
125
125
|
if (taunt.length === 1) return taunt[0]
|
|
@@ -526,7 +526,7 @@ const scrambleGotchiIds = (allAliveGotchis, team1, team2) => {
|
|
|
526
526
|
**/
|
|
527
527
|
const prepareTeams = (allAliveGotchis, team1, team2) => {
|
|
528
528
|
// check there's no duplicate gotchis
|
|
529
|
-
scrambleGotchiIds(allAliveGotchis, team1, team2)
|
|
529
|
+
scrambleGotchiIds(allAliveGotchis, team1, team2)
|
|
530
530
|
|
|
531
531
|
// Apply stat items
|
|
532
532
|
applyStatItems(allAliveGotchis)
|