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,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:
|
package/eslint.config.js
ADDED
|
@@ -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
|
+
]
|
package/game-logic/index.js
CHANGED
|
@@ -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,
|
package/game-logic/v1.4/index.js
CHANGED
|
@@ -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(
|
|
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
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
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
|
-
|
|
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(
|
|
583
|
-
if (!team2) throw new Error(
|
|
584
|
-
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')
|
|
585
587
|
|
|
586
|
-
|
|
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(
|
|
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(
|
|
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
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|