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
@@ -0,0 +1,67 @@
1
+ ---
2
+ description: How to add or edit Cursor rules in our project
3
+ globs:
4
+ alwaysApply: false
5
+ ---
6
+ # Cursor Rules Location
7
+
8
+ How to add new cursor rules to the project
9
+
10
+ 1. Always place rule files in PROJECT_ROOT/.cursor/rules/:
11
+ ```
12
+ .cursor/rules/
13
+ ├── your-rule-name.mdc
14
+ ├── another-rule.mdc
15
+ └── ...
16
+ ```
17
+
18
+ 2. Follow the naming convention:
19
+ - Use kebab-case for filenames
20
+ - Always use .mdc extension
21
+ - Make names descriptive of the rule's purpose
22
+
23
+ 3. Directory structure:
24
+ ```
25
+ PROJECT_ROOT/
26
+ ├── .cursor/
27
+ │ └── rules/
28
+ │ ├── your-rule-name.mdc
29
+ │ └── ...
30
+ └── ...
31
+ ```
32
+
33
+ 4. Never place rule files:
34
+ - In the project root
35
+ - In subdirectories outside .cursor/rules
36
+ - In any other location
37
+
38
+ 5. Cursor rules have the following structure:
39
+
40
+ ```
41
+ ---
42
+ description: Short description of the rule's purpose
43
+ globs: optional/path/pattern/**/*
44
+ alwaysApply: false
45
+ ---
46
+ # Rule Title
47
+
48
+ Main content explaining the rule with markdown formatting.
49
+
50
+ 1. Step-by-step instructions
51
+ 2. Code examples
52
+ 3. Guidelines
53
+
54
+ Example:
55
+
56
+ ```typescript
57
+ // Good example
58
+ function goodExample() {
59
+ // Implementation following guidelines
60
+ }
61
+
62
+ // Bad example
63
+ function badExample() {
64
+ // Implementation not following guidelines
65
+ }
66
+ ```
67
+
@@ -0,0 +1,63 @@
1
+ ---
2
+ description: This rule documents the main directory structure of the project and highlights the purpose of important files and folders. Use this as a reference for navigation and for understanding where to place or find code and resources.
3
+ globs: **/*
4
+ alwaysApply: true
5
+ ---
6
+ # Project Directory Structure
7
+
8
+ This rule documents the main directory structure of the project and highlights the purpose of important files and folders. Use this as a reference for navigation and for understanding where to place or find code and resources.
9
+
10
+ ## Root Level
11
+
12
+ - `README.md`: Project overview and documentation.
13
+ - `package.json`, `package-lock.json`: Node.js dependencies and scripts.
14
+ - `Dockerfile`, `cloudbuild.yaml`: Deployment and CI/CD configuration.
15
+ - `eslint.config.js`: Linting configuration.
16
+ - `index.js`: Entry point or root logic.
17
+ - `.gitignore`, `.DS_Store`: Git and system files.
18
+
19
+ ## Main Folders
20
+
21
+ - `game-logic/`: Core game logic, organized by version (e.g., `v1.4`, `v1.5`, ..., `v2.0`). Each version contains:
22
+ - `index.js`: Main logic for that version.
23
+ - `constants.js`: Constants for that version.
24
+ - `helpers.js`: Helper functions (in later versions).
25
+ - `scripts/`: Utility scripts for running, validating, and simulating battles, as well as data and balancing tools.
26
+ - `balancing/`: Scripts and data for balancing, with subfolders for different balancing versions (e.g., `v1.7`, `v1.7.1`, ...).
27
+ - `data/`: JSON data for dungeons, teams, tournaments, etc.
28
+ - `output/`: Output files from scripts.
29
+ - Various JS scripts for running and validating battles and tournaments.
30
+ - `utils/`: Shared utility modules (e.g., `errors.js`, `transforms.js`, `mapGotchi.js`, `contracts.js`, `validations.js`).
31
+ - `schemas/`: JSON schemas (e.g., `team.json`).
32
+ - `constants/`: Shared constants (e.g., `tournamentManagerAbi.json`).
33
+ - `.cursor/`: Cursor rules and configuration.
34
+ - `rules/`: All cursor rules (e.g., `cursor-rules.mdc`, `directory-structure.mdc`).
35
+
36
+ ## Notes
37
+ - All source and configuration files should be placed in their appropriate folders as outlined above.
38
+ - Cursor rules must always be placed in `.cursor/rules/` following the naming and structure guidelines in `cursor-rules.mdc`.
39
+
40
+ ## Example Structure
41
+
42
+ ```plaintext
43
+ PROJECT_ROOT/
44
+ ├── game-logic/
45
+ │ ├── v1.4/
46
+ │ ├── v1.5/
47
+ │ ├── ...
48
+ │ └── v2.0/
49
+ ├── scripts/
50
+ │ ├── balancing/
51
+ │ │ ├── v1.7/
52
+ │ │ ├── v1.7.1/
53
+ │ │ └── ...
54
+ │ ├── data/
55
+ │ └── ...
56
+ ├── utils/
57
+ ├── schemas/
58
+ ├── constants/
59
+ ├── .cursor/
60
+ │ └── rules/
61
+ ├── ... (root files)
62
+ ```
63
+
@@ -0,0 +1,64 @@
1
+ ---
2
+ description: Guidelines for continuously improving Cursor rules based on emerging code patterns and best practices.
3
+ globs: **/*
4
+ alwaysApply: true
5
+ ---
6
+ ## Rule Improvement Triggers
7
+
8
+ - New code patterns not covered by existing rules
9
+ - Repeated similar implementations across files
10
+ - Common error patterns that could be prevented
11
+ - New libraries or tools being used consistently
12
+ - Emerging best practices in the codebase
13
+
14
+ # Analysis Process:
15
+ - Compare new code with existing rules
16
+ - Identify patterns that should be standardized
17
+ - Look for references to external documentation
18
+ - Check for consistent error handling patterns
19
+ - Monitor test patterns and coverage
20
+
21
+ # Rule Updates:
22
+
23
+ - **Add New Rules When:**
24
+ - A new technology/pattern is used in 3+ files
25
+ - Common bugs could be prevented by a rule
26
+ - Code reviews repeatedly mention the same feedback
27
+ - New security or performance patterns emerge
28
+
29
+ - **Modify Existing Rules When:**
30
+ - Better examples exist in the codebase
31
+ - Additional edge cases are discovered
32
+ - Related rules have been updated
33
+ - Implementation details have changed
34
+
35
+ - **Rule Quality Checks:**
36
+ - Rules should be actionable and specific
37
+ - Examples should come from actual code
38
+ - References should be up to date
39
+ - Patterns should be consistently enforced
40
+
41
+ ## Continuous Improvement:
42
+
43
+ - Monitor code review comments
44
+ - Track common development questions
45
+ - Update rules after major refactors
46
+ - Add links to relevant documentation
47
+ - Cross-reference related rules
48
+
49
+ ## Rule Deprecation
50
+
51
+ - Mark outdated patterns as deprecated
52
+ - Remove rules that no longer apply
53
+ - Update references to deprecated rules
54
+ - Document migration paths for old patterns
55
+
56
+ ## Documentation Updates:
57
+
58
+ - Keep examples synchronized with code
59
+ - Update references to external docs
60
+ - Maintain links between related rules
61
+ - Document breaking changes
62
+
63
+ Follow [cursor-rules.mdc](mdc:.cursor/rules/curs or-rules.mdc) for proper rule formatting and structure.
64
+
@@ -0,0 +1,99 @@
1
+ ---
2
+ description: This rule documents the tech stack used in the project. Use this as a reference for understanding the technologies and tools used in the project.
3
+ globs: **/*
4
+ alwaysApply: true
5
+ ---
6
+
7
+ # Tech Stack and Dependency Best Practices (2024)
8
+
9
+ This rule documents the major dependencies used in this project, their specific versions, and the latest best practices for their use in Node.js applications as of 2024. Use this as a reference for maintaining, upgrading, or extending the project.
10
+
11
+ ## Major Dependencies
12
+
13
+ | Package | Version | Purpose/Notes |
14
+ |------------------------|------------|----------------------------------------------------|
15
+ | seedrandom | ^3.0.5 | Deterministic random number generation |
16
+ | z-schema | ^6.0.2 | JSON schema validation |
17
+ | @google-cloud/storage | ^7.13.0 | Google Cloud Storage client (dev only) |
18
+ | axios | ^1.7.4 | HTTP client for API requests (dev only) |
19
+ | axios-retry | ^4.5.0 | Retry logic for axios (dev only) |
20
+ | dotenv | ^16.4.5 | Environment variable management (dev only) |
21
+ | eslint | ^9.30.1 | Linting and code quality (dev only) |
22
+ | ethers | ^6.13.4 | Ethereum blockchain interaction (dev only) |
23
+ | json-csv | ^4.0.18 | Convert JSON to CSV (dev only) |
24
+
25
+ ## Best Practices for Each Dependency
26
+
27
+ ### 1. seedrandom (^3.0.5)
28
+ - Use for deterministic random number generation, especially for reproducible simulations or tests.
29
+ - Always seed with a unique value for each session if reproducibility is required.
30
+ - Avoid using for cryptographic purposes; use Node's `crypto` module for secure randomness.
31
+
32
+ ### 2. z-schema (^6.0.2)
33
+ - Use for validating JSON data against schemas, especially for input validation.
34
+ - Define schemas in a dedicated directory (e.g., `schemas/`).
35
+ - Validate all external input and fail fast on invalid data.
36
+
37
+ ### 3. @google-cloud/storage (^7.13.0)
38
+ - Use environment variables (with dotenv) for credentials and configuration.
39
+ - Avoid hardcoding sensitive information in code or config files.
40
+ - Handle errors and retries gracefully; use axios-retry or built-in retry logic.
41
+
42
+ ### 4. axios (^1.7.4) & axios-retry (^4.5.0)
43
+ - Use axios for all HTTP requests for consistency and easier error handling.
44
+ - Use axios-retry to automatically retry failed requests (e.g., network errors, 5xx responses).
45
+ - Always set timeouts and handle errors with try/catch or `.catch()`.
46
+ - Prefer async/await for readability.
47
+
48
+ ### 5. dotenv (^16.4.5)
49
+ - Store all secrets and environment-specific config in a `.env` file (never commit to VCS).
50
+ - Provide a `.env.example` for onboarding and CI.
51
+ - Load dotenv at the very top of your entry file (e.g., `require('dotenv').config()`).
52
+
53
+ ### 6. eslint (^9.30.1)
54
+ - Use ESLint to enforce code style and catch bugs early.
55
+ - Integrate with your editor for real-time feedback.
56
+ - Use `npx eslint .` and `npx eslint . --fix` in CI and pre-commit hooks.
57
+ - Extend with Prettier for formatting if desired.
58
+
59
+ ### 7. ethers (^6.13.4)
60
+ - Use for all Ethereum blockchain interactions (reading/writing contracts, signing, etc.).
61
+ - Always validate addresses and handle errors from blockchain calls.
62
+ - Store private keys and sensitive data in environment variables, never in code.
63
+ - Use async/await for all blockchain calls.
64
+
65
+ ### 8. json-csv (^4.0.18)
66
+ - Use for converting JSON data to CSV for exports or reporting.
67
+ - Validate data before conversion to avoid malformed CSVs.
68
+
69
+ ## General Node.js Best Practices (2024)
70
+
71
+ - **Use Node Version Manager (nvm):** Specify a `.nvmrc` file to lock Node.js version for all contributors.
72
+ - **Environment Variables:** Never commit secrets; always use environment variables and provide `.env.example`.
73
+ - **Project Structure:** Organize code by domain (e.g., `game-logic/`, `utils/`, `scripts/`).
74
+ - **Async/Await:** Prefer async/await over callbacks or raw promises for readability and error handling.
75
+ - **Error Handling:** Always handle errors from async operations and external services.
76
+ - **Testing:** Use a test framework (e.g., Mocha, Jest) and aim for high coverage.
77
+ - **CI/CD:** Automate linting, testing, and deployment with CI/CD pipelines.
78
+ - **Documentation:** Keep `README.md` and code comments up to date.
79
+ - **Security:** Regularly audit dependencies (`npm audit`), and keep packages up to date.
80
+
81
+ ## Example: Loading dotenv and Validating Input
82
+
83
+ ```js
84
+ require('dotenv').config();
85
+ const ZSchema = require('z-schema');
86
+ const schema = require('./schemas/team.json');
87
+
88
+ function validateTeam(team) {
89
+ const validator = new ZSchema();
90
+ if (!validator.validate(team, schema)) {
91
+ throw new Error('Invalid team data');
92
+ }
93
+ }
94
+ ```
95
+
96
+ ## References
97
+ - [Node.js Best Practices](https://github.com/goldbergyoni/nodebestpractices)
98
+ - [Modern Node.js Practices (2025)](https://habtesoft.medium.com/7-modern-node-js-practices-to-adopt-in-2025-fbdec1078c6b)
99
+ - [Web3 Node.js Best Practices](https://docs.chainstack.com/docs/web3-nodejs-from-zero-to-a-full-fledged-project)
package/README.md CHANGED
@@ -2,6 +2,10 @@
2
2
 
3
3
  This npm module contains the game logic for the Gotchi Battler game developed by Galaxy Brain Studios.
4
4
 
5
+ It has been split out into an npm module for two reasons:
6
+ 1. So that it can be used in both the frontend and backend of the Gotchi Battler game. This allows heavy simulations and training to be done on the frontend (users browser), to reduce server load, and in the backend when provable randomness is needed (e.g. for tournaments)
7
+ 2. So that users can download this repository and verify the results of the game logic run in the backend to prove that the game is fair and that the results are not manipulated. See the [Validate Tournament](#validate-tournament) section for more details.
8
+
5
9
  ## Installation
6
10
 
7
11
  To install the module, run the following command:
@@ -0,0 +1,31 @@
1
+ const js = require('@eslint/js')
2
+ const globals = require('globals')
3
+
4
+ module.exports = [
5
+ js.configs.recommended,
6
+ {
7
+ files: ['**/*.js'],
8
+ languageOptions: {
9
+ ecmaVersion: 2021,
10
+ sourceType: 'commonjs',
11
+ globals: {
12
+ ...globals.node
13
+ }
14
+ },
15
+ rules: {
16
+ quotes: ['error', 'single'],
17
+ semi: ['error', 'never'],
18
+ indent: ['error', 4, { SwitchCase: 1 }],
19
+ 'object-curly-spacing': ['error', 'always'],
20
+ 'array-bracket-spacing': ['error', 'never']
21
+ }
22
+ },
23
+ {
24
+ files: ['tests/**/*.test.js'],
25
+ languageOptions: {
26
+ globals: {
27
+ ...globals.mocha
28
+ }
29
+ }
30
+ }
31
+ ]
@@ -1,8 +1,4 @@
1
1
  module.exports = {
2
- 'v1.4': require('./v1.4'),
3
- 'v1.5': require('./v1.5'),
4
- 'v1.6': require('./v1.6'),
5
- 'v1.7': require('./v1.7'),
6
- 'v1.8': require('./v1.8'),
7
- current: 'v1.8'
2
+ 'v2.0': require('./v2.0'),
3
+ current: 'v2.0'
8
4
  }
@@ -77,29 +77,6 @@ const MULTS = {
77
77
  CLAN_MOMENTUM_DAMAGE: 2.5
78
78
  }
79
79
 
80
- const passiveIcons = {
81
- 'sharp_blades': 'https://game-icons.net/1x1/lorc/plain-dagger.html',
82
- 'cloud_of_zen': 'https://game-icons.net/1x1/lorc/meditation.html',
83
- 'frenzy': 'https://game-icons.net/1x1/lorc/totem-head.html',
84
- 'fortify': 'https://game-icons.net/1x1/lorc/crenulated-shield.html',
85
- 'spread_the_fear': 'https://game-icons.net/1x1/lorc/evil-book.html',
86
- 'cleansing_aura': 'https://game-icons.net/1x1/lorc/aura.html',
87
- 'channel_the_coven': 'https://game-icons.net/1x1/lorc/witch-flight.html',
88
- 'clan_momentum': 'https://game-icons.net/1x1/delapouite/bully-minion.html'
89
- }
90
-
91
- const debuffIcons = {
92
- 'bleed': 'https://game-icons.net/1x1/lorc/broken-heart.html',
93
- 'stun': 'https://game-icons.net/1x1/sbed/electric.html',
94
- 'fear': 'https://game-icons.net/1x1/lorc/screaming.html'
95
- }
96
-
97
- const buffIcons = {
98
- 'taunt': 'https://game-icons.net/1x1/lorc/archery-target.html',
99
- 'power_up_1': 'https://game-icons.net/1x1/lorc/strong.html',
100
- 'power_up_2': 'https://game-icons.net/1x1/delapouite/mighty-force.html'
101
- }
102
-
103
80
  module.exports = {
104
81
  PASSIVES,
105
82
  DEBUFF_STATUSES,
@@ -1,6 +1,8 @@
1
1
  const seedrandom = require('seedrandom')
2
2
  const ZSchema = require('z-schema')
3
3
  const validator = new ZSchema()
4
+ const teamSchema = require('../../schemas/team.json')
5
+
4
6
  const { GameError } = require('../../utils/errors')
5
7
 
6
8
  let {
@@ -126,7 +128,7 @@ const getNextToAct = (team1, team2, rng) => {
126
128
 
127
129
  const getTarget = (defendingTeam, rng) => {
128
130
  // Check for taunt gotchis
129
- const taunt = [...getAlive(defendingTeam, 'front'), ...getAlive(defendingTeam, 'back')].filter(gotchi => gotchi.statuses && gotchi.statuses.includes("taunt"))
131
+ const taunt = [...getAlive(defendingTeam, 'front'), ...getAlive(defendingTeam, 'back')].filter(gotchi => gotchi.statuses && gotchi.statuses.includes('taunt'))
130
132
 
131
133
  if (taunt.length) {
132
134
  if (taunt.length === 1) return taunt[0]
@@ -473,33 +475,33 @@ const scrambleGotchiIds = (allAliveGotchis, team1, team2) => {
473
475
  const gotchiIds = allAliveGotchis.map(x => x.id)
474
476
 
475
477
  if (gotchiIds.length !== new Set(gotchiIds).size) {
476
- // scramble gotchi ids
477
- allAliveGotchis.forEach(x => {
478
- const newId = Math.floor(Math.random() * 10000000)
479
-
480
- // find gotchi in team1 or team2
481
- const position = getFormationPosition(team1, team2, x.id)
482
-
483
- // change gotchi id
484
- if (position) {
485
- if (position.team === 1) {
486
- if (x.id === team1.leader) team1.leader = newId
487
- team1.formation[position.row][position.position].id = newId
488
- } else {
489
- if (x.id === team2.leader) team2.leader = newId
490
- team2.formation[position.row][position.position].id = newId
491
- }
478
+ // scramble gotchi ids
479
+ allAliveGotchis.forEach(x => {
480
+ const newId = Math.floor(Math.random() * 10000000)
481
+
482
+ // find gotchi in team1 or team2
483
+ const position = getFormationPosition(team1, team2, x.id)
484
+
485
+ // change gotchi id
486
+ if (position) {
487
+ if (position.team === 1) {
488
+ if (x.id === team1.leader) team1.leader = newId
489
+ team1.formation[position.row][position.position].id = newId
492
490
  } else {
493
- throw new Error('Gotchi not found in team1 or team2')
491
+ if (x.id === team2.leader) team2.leader = newId
492
+ team2.formation[position.row][position.position].id = newId
494
493
  }
495
- })
496
-
497
- // check again
498
- const newGotchiIds = allAliveGotchis.map(x => x.id)
499
- if (newGotchiIds.length !== new Set(newGotchiIds).size) {
500
- // Scramble again
501
- scrambleGotchiIds(allAliveGotchis, team1, team2)
494
+ } else {
495
+ throw new Error('Gotchi not found in team1 or team2')
502
496
  }
497
+ })
498
+
499
+ // check again
500
+ const newGotchiIds = allAliveGotchis.map(x => x.id)
501
+ if (newGotchiIds.length !== new Set(newGotchiIds).size) {
502
+ // Scramble again
503
+ scrambleGotchiIds(allAliveGotchis, team1, team2)
504
+ }
503
505
  }
504
506
  }
505
507
 
@@ -511,7 +513,7 @@ const scrambleGotchiIds = (allAliveGotchis, team1, team2) => {
511
513
  **/
512
514
  const prepareTeams = (allAliveGotchis, team1, team2) => {
513
515
  // check there's no duplicate gotchis
514
- scrambleGotchiIds(allAliveGotchis, team1, team2);
516
+ scrambleGotchiIds(allAliveGotchis, team1, team2)
515
517
 
516
518
  allAliveGotchis.forEach(x => {
517
519
  // Add statuses property to all gotchis
@@ -533,7 +535,7 @@ const prepareTeams = (allAliveGotchis, team1, team2) => {
533
535
 
534
536
  // Add leader passive to team
535
537
  addLeaderToTeam(team1)
536
- addLeaderToTeam(team2);
538
+ addLeaderToTeam(team2)
537
539
  }
538
540
 
539
541
  /**
@@ -579,20 +581,20 @@ const getLogGotchis = (allAliveGotchis) => {
579
581
  * @returns {Object} logs The battle logs
580
582
  */
581
583
  const gameLoop = (team1, team2, seed, debug) => {
582
- if (!team1) throw new Error("Team 1 not found")
583
- if (!team2) throw new Error("Team 2 not found")
584
- if (!seed) throw new Error("Seed not found")
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')
585
587
 
586
- // Validate team objects
588
+ // Validate team objects
587
589
  const team1Validation = validator.validate(team1, teamSchema)
588
590
  if (!team1Validation) {
589
591
  console.error('Team 1 validation failed: ', JSON.stringify(validator.getLastErrors(), null, 2))
590
- throw new Error(`Team 1 validation failed`)
592
+ throw new Error('Team 1 validation failed')
591
593
  }
592
594
  const team2Validation = validator.validate(team2, teamSchema)
593
595
  if (!team2Validation) {
594
596
  console.error('Team 2 validation failed: ', JSON.stringify(validator.getLastErrors(), null, 2))
595
- throw new Error(`Team 2 validation failed`)
597
+ throw new Error('Team 2 validation failed')
596
598
  }
597
599
 
598
600
  const rng = seedrandom(seed)
@@ -610,7 +612,7 @@ const gameLoop = (team1, team2, seed, debug) => {
610
612
  ]
611
613
  },
612
614
  turns: []
613
- };
615
+ }
614
616
 
615
617
  // Used for turn by turn health and status summaries
616
618
  // Deleted if not in development or no errors
@@ -638,20 +640,20 @@ const gameLoop = (team1, team2, seed, debug) => {
638
640
  turnLogs.statusesExpired = [...turnLogs.statusesExpired, ...getExpiredStatuses(team1, team2)]
639
641
  }
640
642
 
641
- logs.turns.push({index: turnCounter, ...turnLogs})
643
+ logs.turns.push({ index: turnCounter, ...turnLogs })
642
644
 
643
645
  if (debug) {
644
- logs.debug.push({
645
- turn: turnCounter,
646
- user: logs.turns[logs.turns.length - 1].action.user,
647
- move: logs.turns[logs.turns.length - 1].action.name,
648
- team1: getAlive(team1).map((x) => {
649
- return `Id: ${x.id}, Name: ${x.name}, Health: ${x.health}, Statuses: ${x.statuses}`
650
- }),
651
- team2: getAlive(team2).map((x) => {
652
- return `Id: ${x.id}, Name: ${x.name}, Health: ${x.health}, Statuses: ${x.statuses}`
646
+ logs.debug.push({
647
+ turn: turnCounter,
648
+ user: logs.turns[logs.turns.length - 1].action.user,
649
+ move: logs.turns[logs.turns.length - 1].action.name,
650
+ team1: getAlive(team1).map((x) => {
651
+ return `Id: ${x.id}, Name: ${x.name}, Health: ${x.health}, Statuses: ${x.statuses}`
652
+ }),
653
+ team2: getAlive(team2).map((x) => {
654
+ return `Id: ${x.id}, Name: ${x.name}, Health: ${x.health}, Statuses: ${x.statuses}`
655
+ })
653
656
  })
654
- })
655
657
  }
656
658
 
657
659
  turnCounter++
@@ -841,12 +843,10 @@ const attack = (attackingGotchi, attackingTeam, defendingTeam, defendingTargets,
841
843
  }
842
844
 
843
845
  // Deal with start of turn status effects
844
- const handleStatusEffects = (attackingGotchi, attackingTeam, defendingTeam, rng) => {
846
+ const handleStatusEffects = (attackingGotchi, attackingTeam, defendingTeam) => {
845
847
  const statusEffects = []
846
848
  const passiveEffects = []
847
849
 
848
- const modifiedAttackingGotchi = getModifiedStats(attackingGotchi)
849
-
850
850
  // Check for cleansing_aura
851
851
  // if (attackingGotchi.statuses.includes('cleansing_aura')) {
852
852
  // // Remove all debuffs from all allies
@@ -1089,7 +1089,7 @@ const specialAttack = (attackingGotchi, attackingTeam, defendingTeam, rng) => {
1089
1089
  const modifiedAttackingGotchi = getModifiedStats(attackingGotchi)
1090
1090
 
1091
1091
  switch (specialId) {
1092
- case 1:
1092
+ case 1: {
1093
1093
  // Spectral Strike - ignore armor and appply bleed status
1094
1094
  // get single target
1095
1095
  const ssTarget = getTarget(defendingTeam, rng)
@@ -1104,7 +1104,8 @@ const specialAttack = (attackingGotchi, attackingTeam, defendingTeam, rng) => {
1104
1104
  noResistSpeedPenalty: true
1105
1105
  })
1106
1106
  break
1107
- case 2:
1107
+ }
1108
+ case 2: {
1108
1109
  // Meditate - Boost own speed, magic, physical by 30%
1109
1110
  // If gotchi already has 2 power_up statuses, do nothing
1110
1111
  if (!addStatusToGotchi(attackingGotchi, 'power_up_2')) {
@@ -1138,7 +1139,8 @@ const specialAttack = (attackingGotchi, attackingTeam, defendingTeam, rng) => {
1138
1139
  }
1139
1140
 
1140
1141
  break
1141
- case 3:
1142
+ }
1143
+ case 3: {
1142
1144
  // Cleave - attack all enemies in a row (that have the most gotchis) for 75% damage
1143
1145
  // Find row with most gotchis
1144
1146
  const cleaveRow = getAlive(defendingTeam, 'front').length > getAlive(defendingTeam, 'back').length ? 'front' : 'back'
@@ -1150,7 +1152,8 @@ const specialAttack = (attackingGotchi, attackingTeam, defendingTeam, rng) => {
1150
1152
  inflictPassiveStatuses: false
1151
1153
  })
1152
1154
  break
1153
- case 4:
1155
+ }
1156
+ case 4: {
1154
1157
  // Taunt - add taunt status to self
1155
1158
 
1156
1159
  // Check if gotchi already has taunt status
@@ -1172,7 +1175,8 @@ const specialAttack = (attackingGotchi, attackingTeam, defendingTeam, rng) => {
1172
1175
  }
1173
1176
  ]
1174
1177
  break
1175
- case 5:
1178
+ }
1179
+ case 5: {
1176
1180
  // Curse - attack random enemy for 50% damage, apply fear status and remove all buffs
1177
1181
 
1178
1182
  const curseTarget = getTarget(defendingTeam, rng)
@@ -1219,7 +1223,8 @@ const specialAttack = (attackingGotchi, attackingTeam, defendingTeam, rng) => {
1219
1223
  }
1220
1224
 
1221
1225
  break
1222
- case 6:
1226
+ }
1227
+ case 6: {
1223
1228
  // Blessing - Heal all non-healer allies and remove all debuffs
1224
1229
 
1225
1230
  // Get all alive non-healer allies on the attacking team
@@ -1283,7 +1288,8 @@ const specialAttack = (attackingGotchi, attackingTeam, defendingTeam, rng) => {
1283
1288
  }
1284
1289
 
1285
1290
  break
1286
- case 7:
1291
+ }
1292
+ case 7: {
1287
1293
  // Thunder - Attack all enemies for 50% damage and apply stun status
1288
1294
 
1289
1295
  const thunderTargets = getAlive(defendingTeam)
@@ -1307,7 +1313,8 @@ const specialAttack = (attackingGotchi, attackingTeam, defendingTeam, rng) => {
1307
1313
  }
1308
1314
 
1309
1315
  break
1310
- case 8:
1316
+ }
1317
+ case 8: {
1311
1318
  // Devestating Smash - Attack random enemy for 200% damage
1312
1319
 
1313
1320
  const smashTarget = getTarget(defendingTeam, rng)
@@ -1351,6 +1358,7 @@ const specialAttack = (attackingGotchi, attackingTeam, defendingTeam, rng) => {
1351
1358
  }
1352
1359
 
1353
1360
  break
1361
+ }
1354
1362
  }
1355
1363
 
1356
1364
  return {
@@ -80,29 +80,6 @@ const MULTS = {
80
80
  CLAN_MOMENTUM_DAMAGE: 3.2
81
81
  }
82
82
 
83
- const passiveIcons = {
84
- 'sharp_blades': 'https://game-icons.net/1x1/lorc/plain-dagger.html',
85
- 'cloud_of_zen': 'https://game-icons.net/1x1/lorc/meditation.html',
86
- 'frenzy': 'https://game-icons.net/1x1/lorc/totem-head.html',
87
- 'fortify': 'https://game-icons.net/1x1/lorc/crenulated-shield.html',
88
- 'spread_the_fear': 'https://game-icons.net/1x1/lorc/evil-book.html',
89
- 'cleansing_aura': 'https://game-icons.net/1x1/lorc/aura.html',
90
- 'channel_the_coven': 'https://game-icons.net/1x1/lorc/witch-flight.html',
91
- 'clan_momentum': 'https://game-icons.net/1x1/delapouite/bully-minion.html'
92
- }
93
-
94
- const debuffIcons = {
95
- 'bleed': 'https://game-icons.net/1x1/lorc/broken-heart.html',
96
- 'stun': 'https://game-icons.net/1x1/sbed/electric.html',
97
- 'fear': 'https://game-icons.net/1x1/lorc/screaming.html'
98
- }
99
-
100
- const buffIcons = {
101
- 'taunt': 'https://game-icons.net/1x1/lorc/archery-target.html',
102
- 'power_up_1': 'https://game-icons.net/1x1/lorc/strong.html',
103
- 'power_up_2': 'https://game-icons.net/1x1/delapouite/mighty-force.html'
104
- }
105
-
106
83
  module.exports = {
107
84
  PASSIVES,
108
85
  DEBUFF_STATUSES,