gotchi-battler-game-logic 3.0.0 → 4.0.0

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.
Files changed (61) hide show
  1. package/.cursor/rules/cursor-rules.mdc +67 -0
  2. package/.cursor/rules/directory-structure.mdc +63 -0
  3. package/.cursor/rules/self-improvement.mdc +64 -0
  4. package/.cursor/rules/tech-stack.mdc +99 -0
  5. package/README.md +4 -0
  6. package/eslint.config.js +31 -0
  7. package/game-logic/index.js +2 -6
  8. package/game-logic/v1.4/constants.js +0 -23
  9. package/game-logic/v1.4/index.js +64 -56
  10. package/game-logic/v1.5/constants.js +0 -23
  11. package/game-logic/v1.5/index.js +27 -21
  12. package/game-logic/v1.6/constants.js +0 -23
  13. package/game-logic/v1.6/index.js +27 -21
  14. package/game-logic/v1.7/constants.js +0 -23
  15. package/game-logic/v1.7/helpers.js +2 -2
  16. package/game-logic/v1.7/index.js +24 -18
  17. package/game-logic/v1.8/constants.js +0 -23
  18. package/game-logic/v1.8/helpers.js +2 -2
  19. package/game-logic/v1.8/index.js +25 -19
  20. package/game-logic/v2.0/constants.js +112 -0
  21. package/game-logic/v2.0/helpers.js +713 -0
  22. package/game-logic/v2.0/index.js +782 -0
  23. package/game-logic/v2.0/statuses.json +439 -0
  24. package/package.json +11 -4
  25. package/schemas/crystal.js +14 -0
  26. package/schemas/effect.js +25 -0
  27. package/schemas/gotchi.js +53 -0
  28. package/schemas/ingameteam.js +14 -0
  29. package/schemas/item.js +13 -0
  30. package/schemas/leaderskill.js +15 -0
  31. package/schemas/leaderskillstatus.js +12 -0
  32. package/schemas/special.js +22 -0
  33. package/schemas/team.js +24 -0
  34. package/schemas/team.json +252 -114
  35. package/scripts/balancing/createTrainingGotchis.js +44 -44
  36. package/scripts/balancing/extractOnchainTraits.js +3 -3
  37. package/scripts/balancing/fixTrainingGotchis.js +41 -41
  38. package/scripts/balancing/processSims.js +5 -5
  39. package/scripts/balancing/sims.js +8 -15
  40. package/scripts/balancing/v1.7/setTeamPositions.js +2 -2
  41. package/scripts/balancing/v1.7.1/setTeamPositions.js +2 -2
  42. package/scripts/balancing/v1.7.2/setTeamPositions.js +2 -2
  43. package/scripts/balancing/v1.7.3/setTeamPositions.js +2 -2
  44. package/scripts/data/dungeon_mob_1.json +87 -0
  45. package/scripts/data/dungeon_mob_2.json +87 -0
  46. package/scripts/data/immaterialTeam1.json +374 -0
  47. package/scripts/data/immaterialTeam2.json +365 -0
  48. package/scripts/generateAllSpecialsLogs.js +93 -0
  49. package/scripts/generateSpecialLogs.js +94 -0
  50. package/scripts/runCampaignBattles.js +41 -0
  51. package/scripts/runLocalBattle.js +6 -3
  52. package/scripts/runLocalDungeon.js +52 -0
  53. package/scripts/runPvPBattle.js +16 -0
  54. package/scripts/runRealBattle.js +8 -8
  55. package/scripts/simRealBattle.js +8 -8
  56. package/scripts/validateBattle.js +12 -14
  57. package/scripts/validateTournament.js +9 -9
  58. package/tests/getModifiedStats.test.js +78 -0
  59. package/utils/errors.js +13 -13
  60. package/utils/transforms.js +2 -8
  61. package/scripts/output/.gitkeep +0 -0
@@ -41,12 +41,12 @@ if (require.main === module) {
41
41
  const battleId = process.argv[2]
42
42
 
43
43
  main(battleId)
44
- .then(() => {
45
- console.log('Done')
46
- process.exit(0)
47
- })
48
- .catch((error) => {
49
- console.error('Error: ', error)
50
- process.exit(1)
51
- })
44
+ .then(() => {
45
+ console.log('Done')
46
+ process.exit(0)
47
+ })
48
+ .catch((error) => {
49
+ console.error('Error: ', error)
50
+ process.exit(1)
51
+ })
52
52
  }
@@ -110,12 +110,12 @@ if (require.main === module) {
110
110
  const battleId = process.argv[2]
111
111
 
112
112
  main(battleId)
113
- .then(() => {
114
- console.log('Done')
115
- process.exit(0)
116
- })
117
- .catch((error) => {
118
- console.error('Error: ', error)
119
- process.exit(1)
120
- })
113
+ .then(() => {
114
+ console.log('Done')
115
+ process.exit(0)
116
+ })
117
+ .catch((error) => {
118
+ console.error('Error: ', error)
119
+ process.exit(1)
120
+ })
121
121
  }
@@ -4,8 +4,6 @@ axiosRetry(axios, {
4
4
  retries: 3,
5
5
  retryDelay: axiosRetry.exponentialDelay
6
6
  })
7
- const fs = require('fs')
8
- const path = require('path')
9
7
 
10
8
  const { GameError, ValidationError } = require('../utils/errors')
11
9
  const { logToInGameTeams } = require('../utils/transforms')
@@ -66,18 +64,18 @@ if (require.main === module) {
66
64
  const gameLogicVersion = process.argv[4]
67
65
 
68
66
  main(battleId, seed, gameLogicVersion)
69
- .then(() => {
70
- console.log('Results from game logic match the logs ✅')
71
- console.log('Done')
72
- process.exit(0)
73
- })
74
- .catch((error) => {
75
- if (error instanceof ValidationError) {
76
- console.error('Results from game logic do not match the logs ❌')
77
- }
67
+ .then(() => {
68
+ console.log('Results from game logic match the logs ✅')
69
+ console.log('Done')
70
+ process.exit(0)
71
+ })
72
+ .catch((error) => {
73
+ if (error instanceof ValidationError) {
74
+ console.error('Results from game logic do not match the logs ❌')
75
+ }
78
76
 
79
- console.error('Error: ', error.message)
77
+ console.error('Error: ', error.message)
80
78
 
81
- process.exit(1)
82
- })
79
+ process.exit(1)
80
+ })
83
81
  }
@@ -89,13 +89,13 @@ if (require.main === module) {
89
89
  const tournamentId = process.argv[2]
90
90
 
91
91
  main(tournamentId)
92
- .then(() => {
93
- console.log('Done')
94
- process.exit(0)
95
- })
96
- .catch((error) => {
97
- console.error(error)
98
-
99
- process.exit(1)
100
- })
92
+ .then(() => {
93
+ console.log('Done')
94
+ process.exit(0)
95
+ })
96
+ .catch((error) => {
97
+ console.error(error)
98
+
99
+ process.exit(1)
100
+ })
101
101
  }
@@ -0,0 +1,78 @@
1
+ const { expect } = require('chai')
2
+ const path = require('path')
3
+
4
+ // Import from helpers
5
+ const { getModifiedStats } = require(path.join('..', 'game-logic', 'v2.0', 'helpers'))
6
+
7
+ const makeGotchi = (overrides = {}) => ({
8
+ id: 1,
9
+ name: 'Test',
10
+ speed: 10,
11
+ attack: 5,
12
+ defense: 3,
13
+ resist: 0,
14
+ criticalRate: 0,
15
+ criticalDamage: 15,
16
+ special: { initialCooldown: 0 },
17
+ statuses: [],
18
+ ...overrides,
19
+ })
20
+
21
+ describe('getModifiedStats', () => {
22
+ it('applies flat stat buffs from statuses', () => {
23
+ const g = makeGotchi({ statuses: ['spd_up', 'atk_up', 'def_up'] })
24
+ const m = getModifiedStats(g)
25
+ expect(m.speed).to.equal(11)
26
+ expect(m.attack).to.equal(6)
27
+ expect(m.defense).to.equal(4)
28
+ })
29
+
30
+ it('stacks multiple statuses and nets against opposing debuffs', () => {
31
+ const g = makeGotchi({ statuses: ['spd_up', 'spd_up', 'spd_down'] })
32
+ const m = getModifiedStats(g)
33
+ expect(m.speed).to.equal(11) // 10 + 1 net
34
+ })
35
+
36
+ it('keeps speed at minimum 1 to avoid zero/negative speed', () => {
37
+ const g = makeGotchi({ speed: 2, statuses: ['spd_down', 'spd_down', 'spd_down'] })
38
+ const m = getModifiedStats(g)
39
+ expect(m.speed).to.equal(1)
40
+ })
41
+
42
+ it('ignores non stat_modifier statuses', () => {
43
+ const g = makeGotchi({ statuses: ['taunt', 'bleed'] }) // custom, turn_effect
44
+ const m = getModifiedStats(g)
45
+ expect(m.speed).to.equal(10)
46
+ expect(m.attack).to.equal(5)
47
+ expect(m.defense).to.equal(3)
48
+ })
49
+
50
+ it('keeps defense at minimum 1 to avoid zero/negative defense', () => {
51
+ const g = makeGotchi({ defense: 1, statuses: ['def_down'] })
52
+ const m = getModifiedStats(g)
53
+ expect(m.defense).to.equal(1)
54
+ })
55
+
56
+ it('keeps defense at minimum 1 even if base defense is 0 and no statuses', () => {
57
+ const g = makeGotchi({ defense: 0, statuses: [] })
58
+ const m = getModifiedStats(g)
59
+ expect(m.defense).to.equal(1)
60
+ })
61
+
62
+ it('does not mutate the original gotchi object', () => {
63
+ const g = makeGotchi({ statuses: ['spd_up'] })
64
+ const before = { speed: g.speed, attack: g.attack, defense: g.defense }
65
+ const m = getModifiedStats(g)
66
+ expect(g.speed).to.equal(before.speed)
67
+ expect(g.attack).to.equal(before.attack)
68
+ expect(g.defense).to.equal(before.defense)
69
+ expect(m).to.not.equal(g)
70
+ })
71
+
72
+ it('throws if an unknown status code is provided', () => {
73
+ const g = makeGotchi({ statuses: ['this_is_not_real'] })
74
+ expect(() => getModifiedStats(g)).to.throw('Status with code this_is_not_real not found')
75
+ })
76
+ })
77
+
78
+
package/utils/errors.js CHANGED
@@ -1,27 +1,27 @@
1
1
  class GameError extends Error {
2
- constructor(msg, logs) {
2
+ constructor(msg, logs) {
3
3
  super(msg)
4
4
  this.name = 'GameError'
5
5
 
6
- if(logs) {
7
- this.logs = logs
8
- }
9
- }
6
+ if(logs) {
7
+ this.logs = logs
8
+ }
9
+ }
10
10
  }
11
11
 
12
12
  class ValidationError extends Error {
13
- constructor(msg, originalLogs, newLogs) {
13
+ constructor(msg, originalLogs, newLogs) {
14
14
  super(msg)
15
15
  this.name = 'ValidationError'
16
16
 
17
- if(originalLogs) {
18
- this.originalLogs = originalLogs
19
- }
17
+ if(originalLogs) {
18
+ this.originalLogs = originalLogs
19
+ }
20
20
 
21
- if(newLogs) {
22
- this.newLogs = newLogs
23
- }
24
- }
21
+ if(newLogs) {
22
+ this.newLogs = newLogs
23
+ }
24
+ }
25
25
  }
26
26
 
27
27
  module.exports = {
@@ -38,7 +38,7 @@ const logToInGameTeams = (originalLog) => {
38
38
  name: log.layout.teams[teamIndex].name,
39
39
  owner: log.layout.teams[teamIndex].owner
40
40
  })
41
- });
41
+ })
42
42
 
43
43
  return teams
44
44
  }
@@ -49,17 +49,11 @@ const webappTeamToInGameTeam = (webappTeam) => {
49
49
  front: [webappTeam.front1Gotchi, webappTeam.front2Gotchi, webappTeam.front3Gotchi, webappTeam.front4Gotchi, webappTeam.front5Gotchi],
50
50
  back: [webappTeam.back1Gotchi, webappTeam.back2Gotchi, webappTeam.back3Gotchi, webappTeam.back4Gotchi, webappTeam.back5Gotchi],
51
51
  },
52
- leader: webappTeam.leader,
52
+ leader: webappTeam[webappTeam.leader],
53
53
  name: webappTeam.name,
54
54
  owner: webappTeam.owner
55
55
  }
56
56
 
57
- inGameTeam.formation.front.forEach(gotchi => {
58
- if (!gotchi) return
59
- // remove availableSpecials
60
- delete gotchi.availableSpecials
61
- })
62
-
63
57
  return inGameTeam
64
58
  }
65
59
 
File without changes