Compare commits
111
Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6fc36c131e | ||
|
|
5cd59ca6ce | ||
|
|
075182370c | ||
|
|
d2b8a4450d | ||
|
|
e42a15c483 | ||
|
|
08a7d8d2c8 | ||
|
|
d1c9795e83 | ||
|
|
591a36e914 | ||
|
|
9392990109 | ||
|
|
1961c57a97 | ||
|
|
d4b8ad3bd8 | ||
|
|
514653be04 | ||
|
|
bd6fc3c144 | ||
|
|
772da37386 | ||
|
|
a7988fc426 | ||
|
|
6958dd7f7b | ||
|
|
d2418f4036 | ||
|
|
6a14e811fe | ||
|
|
5611ba5440 | ||
|
|
fcb30ba06b | ||
|
|
90d3adc6f4 | ||
|
|
62818120f9 | ||
|
|
90ca20f52f | ||
|
|
0da2984acf | ||
|
|
b651562db1 | ||
|
|
0f1082cdf5 | ||
|
|
ffe04f0859 | ||
|
|
80b726f945 | ||
|
|
dfd83bd28b | ||
|
|
a3e35594cc | ||
|
|
5d5db60ce3 | ||
|
|
782ae60ca8 | ||
|
|
f8e53e7dc3 | ||
|
|
699fab08d3 | ||
|
|
43b86dbf6b | ||
|
|
92d9269a92 | ||
|
|
3858cec872 | ||
|
|
6d3e560394 | ||
|
|
a2fdcdb06e | ||
|
|
9d2dc029c2 | ||
|
|
fbde6692ed | ||
|
|
bb4a906cdf | ||
|
|
c4cb19947c | ||
|
|
31ea4894e8 | ||
|
|
da69a793a6 | ||
|
|
64e9770a7b | ||
|
|
bfbf0c017f | ||
|
|
0a86d7396b | ||
|
|
fb34438161 | ||
|
|
6d0ad1e9db | ||
|
|
8d14f891da | ||
|
|
68b857afbe | ||
|
|
146723c3e7 | ||
|
|
06b5443531 | ||
|
|
490a4ebeeb | ||
|
|
618d60d538 | ||
|
|
4cde8a5482 | ||
|
|
797825b6da | ||
|
|
d7065f2e9e | ||
|
|
15b7d13c1c | ||
|
|
92555ec91e | ||
|
|
ace11a4eea | ||
|
|
9d9c421846 | ||
|
|
f76c622545 | ||
|
|
dec9a69d7e | ||
|
|
278f214498 | ||
|
|
58bf76e352 | ||
|
|
23d429466a | ||
|
|
2348130ce2 | ||
|
|
a2fb493d25 | ||
|
|
7ba241cce5 | ||
|
|
6503ac161b | ||
|
|
f2243b2f68 | ||
|
|
5848215674 | ||
|
|
79c4daa3d5 | ||
|
|
b89bda8e67 | ||
|
|
e3f91381fe | ||
|
|
47b308b4b9 | ||
|
|
b3a88fa9ea | ||
|
|
85f83c3fad | ||
|
|
5952ef214d | ||
|
|
73166944a1 | ||
|
|
f11c1e1e07 | ||
|
|
6dd5814b9e | ||
|
|
7898b2da78 | ||
|
|
70621f4755 | ||
|
|
4e60943174 | ||
|
|
6f42387571 | ||
|
|
373538121f | ||
|
|
10c0b46389 | ||
|
|
bd4507d250 | ||
|
|
c575ec11f7 | ||
|
|
48e5d3f8c6 | ||
|
|
f8f4f99a43 | ||
|
|
fabb70af03 | ||
|
|
034a2ed684 | ||
|
|
1ddb0ea0cc | ||
|
|
19cf1a8582 | ||
|
|
5c8740d21f | ||
|
|
1b42e26383 | ||
|
|
99ac5738a2 | ||
|
|
a7b1bed0fa | ||
|
|
74f127037d | ||
|
|
01852d16d2 | ||
|
|
8f89936281 | ||
|
|
0bd47d12b5 | ||
|
|
922b05834f | ||
|
|
a236346ba2 | ||
|
|
e609e61ec3 | ||
|
|
dc0f80ee94 | ||
|
|
b3f734484b |
@@ -12,13 +12,22 @@ jobs:
|
||||
build:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Use Node.js 24
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24
|
||||
- run: npm ci
|
||||
- run: npm run build
|
||||
- run: npm test
|
||||
registry-url: https://registry.npmjs.org/
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
- name: Build TypeScript (1st pass)
|
||||
run: npm run build
|
||||
continue-on-error: true
|
||||
- name: Generate database typings
|
||||
run: npm run generate
|
||||
- name: Build TypeScript (2nd pass)
|
||||
run: npm run build
|
||||
|
||||
publish-npm:
|
||||
needs: build
|
||||
@@ -27,14 +36,21 @@ jobs:
|
||||
contents: read
|
||||
id-token: write
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- uses: actions/setup-node@v4
|
||||
- name: Checkout repository
|
||||
uses: actions/checkout@v4
|
||||
- name: Use Node.js 24
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: 24
|
||||
registry-url: https://registry.npmjs.org/
|
||||
- run: npm ci
|
||||
- run: npm run build
|
||||
# - run: npm run generate
|
||||
- run: npm publish --provenance --access public
|
||||
env:
|
||||
NODE_AUTH_TOKEN: ${{secrets.NPM_TOKEN}}
|
||||
- name: Install dependencies
|
||||
run: npm ci
|
||||
- name: Build TypeScript (1st pass)
|
||||
run: npm run build
|
||||
continue-on-error: true
|
||||
- name: Generate database typings
|
||||
run: npm run generate
|
||||
- name: Build TypeScript (2nd pass)
|
||||
run: npm run build
|
||||
- name: Publish package to npm
|
||||
run: npm publish --provenance --access public
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
# This workflow will do a clean installation of node dependencies, cache/restore them, build the source code and run tests across different versions of node
|
||||
# For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-nodejs
|
||||
|
||||
name: Node.js CI
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ "main" ]
|
||||
pull_request:
|
||||
branches: [ "main" ]
|
||||
|
||||
jobs:
|
||||
build:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
matrix:
|
||||
node-version: [23, 24]
|
||||
# See supported Node.js release schedule at https://nodejs.org/en/about/releases/
|
||||
|
||||
steps:
|
||||
- uses: actions/checkout@v4
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'npm'
|
||||
- run: npm ci
|
||||
- run: npm run build --if-present
|
||||
- run: npm run lint
|
||||
- run: npm run format:check
|
||||
- run: npm test
|
||||
@@ -1,3 +1,4 @@
|
||||
gen
|
||||
node_modules
|
||||
lib
|
||||
|
||||
|
||||
+273
@@ -2,6 +2,279 @@
|
||||
|
||||
All notable changes to this project will be documented in this file. See [commit-and-tag-version](https://github.com/absolute-version/commit-and-tag-version) for commit guidelines.
|
||||
|
||||
## [0.24.0](https://github.com/elyukai/optolith-database-schema/compare/v0.23.2...v0.24.0) (2025-12-05)
|
||||
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
* The prerequisites field is moved from profession
|
||||
versions to profession packages.
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* arcane traditions and elements can be requirable select options ([d1c9795](https://github.com/elyukai/optolith-database-schema/commit/d1c9795e8378b8f7da69e47491c48df6514661a8))
|
||||
* profession prerequisites can differ per profession version ([08a7d8d](https://github.com/elyukai/optolith-database-schema/commit/08a7d8d2c88beff69ea7b4d68995b1eb6d2528a6))
|
||||
|
||||
## [0.23.2](https://github.com/elyukai/optolith-database-schema/compare/v0.23.1...v0.23.2) (2025-12-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* use correct identifiers in restrictions ([9392990](https://github.com/elyukai/optolith-database-schema/commit/9392990109296bc4cfbc5116c8fa5360efd4a02d))
|
||||
|
||||
## [0.23.1](https://github.com/elyukai/optolith-database-schema/compare/v0.23.0...v0.23.1) (2025-12-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* restore removed magical artifact translation properties ([d4b8ad3](https://github.com/elyukai/optolith-database-schema/commit/d4b8ad3bd854b32d2a6b05d1e79837c210910d4b))
|
||||
|
||||
## [0.23.0](https://github.com/elyukai/optolith-database-schema/compare/v0.22.0...v0.23.0) (2025-12-04)
|
||||
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
* **gen:** duplicate translation postfix
|
||||
* This removes previous note and rules fields.
|
||||
* add map and permanent cost options to magical signs
|
||||
* add item restrictions to equipment of blessed ones
|
||||
* unify item restrictions and add profession restrictions
|
||||
* translation keys follow Unicode MessageFormat 2.0
|
||||
|
||||
### Features
|
||||
|
||||
* add expression-based poison duration option ([fcb30ba](https://github.com/elyukai/optolith-database-schema/commit/fcb30ba06b93924ce9399ea5481b98bd2689b229)), closes [#70](https://github.com/elyukai/optolith-database-schema/issues/70)
|
||||
* add item restrictions to armor ([6a14e81](https://github.com/elyukai/optolith-database-schema/commit/6a14e811fe71e2f6ade006572d4ba4740cc7b4cd)), closes [#68](https://github.com/elyukai/optolith-database-schema/issues/68)
|
||||
* add item restrictions to equipment of blessed ones ([d2418f4](https://github.com/elyukai/optolith-database-schema/commit/d2418f4036c52d5cf972208c67ecf1229e883c85)), closes [#72](https://github.com/elyukai/optolith-database-schema/issues/72)
|
||||
* add map and permanent cost options to magical signs ([6958dd7](https://github.com/elyukai/optolith-database-schema/commit/6958dd7f7bda88027d38ba2a3de75a0c645ff7f7)), closes [#75](https://github.com/elyukai/optolith-database-schema/issues/75)
|
||||
* add structure points option to some item categories ([6281812](https://github.com/elyukai/optolith-database-schema/commit/62818120f9e849ba7e95f3a68a3f00758fb8a514)), closes [#76](https://github.com/elyukai/optolith-database-schema/issues/76)
|
||||
* add translatable intoxicant addiction chance ([bd6fc3c](https://github.com/elyukai/optolith-database-schema/commit/bd6fc3c1448c4b6a221603f33197d8b44a743509)), closes [#71](https://github.com/elyukai/optolith-database-schema/issues/71)
|
||||
* allow penalty for combat style special abilities ([90ca20f](https://github.com/elyukai/optolith-database-schema/commit/90ca20f52f806824325a916b8a89f46070db941a)), closes [#77](https://github.com/elyukai/optolith-database-schema/issues/77)
|
||||
* optional intoxicant overdose ([90d3adc](https://github.com/elyukai/optolith-database-schema/commit/90d3adc6f4a214f3a4178fb402becc446984005d)), closes [#69](https://github.com/elyukai/optolith-database-schema/issues/69)
|
||||
* translation keys follow Unicode MessageFormat 2.0 ([b651562](https://github.com/elyukai/optolith-database-schema/commit/b651562db13d1531d160f8d3288485a04f9bfa3e))
|
||||
* unify item restrictions and add profession restrictions ([5611ba5](https://github.com/elyukai/optolith-database-schema/commit/5611ba54402ce6ac401d40160513b3db70327d8d)), closes [#51](https://github.com/elyukai/optolith-database-schema/issues/51)
|
||||
* use description, effect and cost for magical artifact ([a7988fc](https://github.com/elyukai/optolith-database-schema/commit/a7988fc426ad76f4b876071b4cde05b11d523212)), closes [#74](https://github.com/elyukai/optolith-database-schema/issues/74)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* **gen:** duplicate translation postfix ([772da37](https://github.com/elyukai/optolith-database-schema/commit/772da37386622cff0a1ab4954a392eeaca1ac72a))
|
||||
|
||||
## [0.22.0](https://github.com/elyukai/optolith-database-schema/compare/v0.21.2...v0.22.0) (2025-12-01)
|
||||
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
* Previous lists of values by level need to be wrapped in
|
||||
an object.
|
||||
|
||||
### Features
|
||||
|
||||
* add two-handed option to combat technique restriction ([92d9269](https://github.com/elyukai/optolith-database-schema/commit/92d9269a923ccaa9a99617c34de612947ddbcb88)), closes [#67](https://github.com/elyukai/optolith-database-schema/issues/67)
|
||||
* allow magical signs having AE cost ([f8e53e7](https://github.com/elyukai/optolith-database-schema/commit/f8e53e7dc3645e19a24cc60f1257ccd33f8579d6)), closes [#53](https://github.com/elyukai/optolith-database-schema/issues/53)
|
||||
* ap value may depend on (any) active instance ([80b726f](https://github.com/elyukai/optolith-database-schema/commit/80b726f945cd6fe4609dfc0b11195ee3801d6433)), closes [#49](https://github.com/elyukai/optolith-database-schema/issues/49)
|
||||
* ap value per level based on size category ([43b86db](https://github.com/elyukai/optolith-database-schema/commit/43b86dbf6b6940b6352a69d2a5e9c2f2430e2516)), closes [#43](https://github.com/elyukai/optolith-database-schema/issues/43)
|
||||
* enable automatic entries for advantages ([a3e3559](https://github.com/elyukai/optolith-database-schema/commit/a3e35594ccaf85a7d10db4425234bdf8adc7bca0))
|
||||
* make magical sign property optional ([5d5db60](https://github.com/elyukai/optolith-database-schema/commit/5d5db60ce3dba9330ed1b6f63b8ceb9e8b4c393b)), closes [#52](https://github.com/elyukai/optolith-database-schema/issues/52)
|
||||
* support levels for automatic entries ([dfd83bd](https://github.com/elyukai/optolith-database-schema/commit/dfd83bd28b68bf73df6936d566765f526b1879c4))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* identifier access error ([782ae60](https://github.com/elyukai/optolith-database-schema/commit/782ae60ca8e20f9118448922946735313852fed8))
|
||||
|
||||
## [0.21.2](https://github.com/elyukai/optolith-database-schema/compare/v0.21.1...v0.21.2) (2025-11-30)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* allow three fraction digits for item weight ([9d2dc02](https://github.com/elyukai/optolith-database-schema/commit/9d2dc029c2a07ec3180b4d340ff5ea8afe67fcba))
|
||||
|
||||
## [0.21.1](https://github.com/elyukai/optolith-database-schema/compare/v0.21.0...v0.21.1) (2025-11-27)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add trade secret option for diseases ([da69a79](https://github.com/elyukai/optolith-database-schema/commit/da69a793a65e7737505c4ff140f6be2132119d6c))
|
||||
* add weight and complexity options to vehicles ([bfbf0c0](https://github.com/elyukai/optolith-database-schema/commit/bfbf0c017f0e7bd08bf0ebebe189a862796831ce)), closes [#59](https://github.com/elyukai/optolith-database-schema/issues/59)
|
||||
* magical artifacts may define complexity and combat uses ([0a86d73](https://github.com/elyukai/optolith-database-schema/commit/0a86d7396b2fddd977e214ab8be283dd31353292)), closes [#62](https://github.com/elyukai/optolith-database-schema/issues/62)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* ranged weapons may have a range of 0 ([fb34438](https://github.com/elyukai/optolith-database-schema/commit/fb3443816195b3079362d07c9bb3a261f98609c8)), closes [#63](https://github.com/elyukai/optolith-database-schema/issues/63)
|
||||
|
||||
## [0.21.0](https://github.com/elyukai/optolith-database-schema/compare/v0.20.6...v0.21.0) (2025-11-21)
|
||||
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
* patrons also work with tribes instead of plain cultures to align with animist powers
|
||||
|
||||
### Features
|
||||
|
||||
* allow race prerequisite for personality traits ([146723c](https://github.com/elyukai/optolith-database-schema/commit/146723c3e7f91e8c4b4e63b61df95c5a497ea117)), closes [#45](https://github.com/elyukai/optolith-database-schema/issues/45)
|
||||
* allow trade secret for animal poisons ([8d14f89](https://github.com/elyukai/optolith-database-schema/commit/8d14f891dabad75b9ad1d37c98f1bf8b10c74d69))
|
||||
* patrons also work with tribes instead of plain cultures to align with animist powers ([68b857a](https://github.com/elyukai/optolith-database-schema/commit/68b857afbefb0655a842cd86f5321f155054db7a)), closes [#47](https://github.com/elyukai/optolith-database-schema/issues/47)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* always display child instances of activatable entries ([06b5443](https://github.com/elyukai/optolith-database-schema/commit/06b5443531c5b00fbf4b422aac9e2cdc6ea6ff14)), closes [#51](https://github.com/elyukai/optolith-database-schema/issues/51)
|
||||
* cantrip note translations should be optional ([490a4eb](https://github.com/elyukai/optolith-database-schema/commit/490a4ebeeb728ec61e83193a6f84cd774625b777))
|
||||
|
||||
## [0.20.6](https://github.com/elyukai/optolith-database-schema/compare/v0.20.5...v0.20.6) (2025-11-16)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* focus rules and optional rules can require other focus rules or optional rules to be active ([797825b](https://github.com/elyukai/optolith-database-schema/commit/797825b6da46517b1f7a9e2f00f34b21c8bae15d))
|
||||
|
||||
## [0.20.5](https://github.com/elyukai/optolith-database-schema/compare/v0.20.4...v0.20.5) (2025-11-15)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* include translations in locales ([15b7d13](https://github.com/elyukai/optolith-database-schema/commit/15b7d13c1c64a62b5c3f6406a980cf6477b211db))
|
||||
|
||||
## [0.20.4](https://github.com/elyukai/optolith-database-schema/compare/v0.20.3...v0.20.4) (2025-11-15)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* generated typescript typings ([ace11a4](https://github.com/elyukai/optolith-database-schema/commit/ace11a4eea93665decc0070267e9a873aa856734))
|
||||
|
||||
## [0.20.3](https://github.com/elyukai/optolith-database-schema/compare/v0.20.2...v0.20.3) (2025-11-14)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* enhancement display name ([f76c622](https://github.com/elyukai/optolith-database-schema/commit/f76c6225456d09817bb960ce041dd2bb121047e3))
|
||||
|
||||
## [0.20.2](https://github.com/elyukai/optolith-database-schema/compare/v0.20.1...v0.20.2) (2025-11-13)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* wrong newspaper translation name ([278f214](https://github.com/elyukai/optolith-database-schema/commit/278f21449836143440d485ba350a8e60748fc3f1))
|
||||
|
||||
## [0.20.1](https://github.com/elyukai/optolith-database-schema/compare/v0.20.0...v0.20.1) (2025-11-13)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add "various" case to armor complexity ([23d4294](https://github.com/elyukai/optolith-database-schema/commit/23d429466ac6eb34c8086c63abde93bfec5d354d)), closes [#21](https://github.com/elyukai/optolith-database-schema/issues/21)
|
||||
* add newspaper entity ([6503ac1](https://github.com/elyukai/optolith-database-schema/commit/6503ac161b00c980695ea94a87d27ebbfcef7674)), closes [#40](https://github.com/elyukai/optolith-database-schema/issues/40)
|
||||
* add supplemental content option to magical traditions to enable trailing tables ([2348130](https://github.com/elyukai/optolith-database-schema/commit/2348130ce28e6ddde64ff6304ae04d01a00573c8)), closes [elyukai/optolith-data#791](https://github.com/elyukai/optolith-data/issues/791)
|
||||
* animal poison level may depend on the subtype of animal ([a2fb493](https://github.com/elyukai/optolith-database-schema/commit/a2fb493d251d733c13b1d22034c9b8e917e67d68)), closes [#38](https://github.com/elyukai/optolith-database-schema/issues/38)
|
||||
|
||||
## [0.20.0](https://github.com/elyukai/optolith-database-schema/compare/v0.19.1...v0.20.0) (2025-11-02)
|
||||
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
* align book type/category with official sources
|
||||
* use book cost variant with single single costs as well to enable label usage
|
||||
|
||||
### Features
|
||||
|
||||
* add book category ([b3a88fa](https://github.com/elyukai/optolith-database-schema/commit/b3a88fa9ea417930c4c5249869f4153404570393))
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* align book type/category with official sources ([b89bda8](https://github.com/elyukai/optolith-database-schema/commit/b89bda8e670d1a46d6a5ea2f094401e458ce62f7))
|
||||
* general select option display name customizer ([47b308b](https://github.com/elyukai/optolith-database-schema/commit/47b308b4b93deabb83aaec11b4036b9a70350a83))
|
||||
* make definite book cost variant translations optional if label is not used ([79c4daa](https://github.com/elyukai/optolith-database-schema/commit/79c4daa3d50aa3468f0c72ef6a5736779c421039))
|
||||
* use book cost variant with single single costs as well to enable label usage ([e3f9138](https://github.com/elyukai/optolith-database-schema/commit/e3f91381fe6d93525366e15c3d4073cda90821b8))
|
||||
|
||||
## [0.19.1](https://github.com/elyukai/optolith-database-schema/compare/v0.19.0...v0.19.1) (2025-10-27)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* general special ability options and activatable instances in general should use child entity types instead of reference types ([5952ef2](https://github.com/elyukai/optolith-database-schema/commit/5952ef214d388e328b6a94364384eed712cefb13))
|
||||
|
||||
## [0.19.0](https://github.com/elyukai/optolith-database-schema/compare/v0.18.13...v0.19.0) (2025-10-27)
|
||||
|
||||
|
||||
### ⚠ BREAKING CHANGES
|
||||
|
||||
* combineable binary math operators to specific adventure points value by active instances instead of fixed functions
|
||||
|
||||
### Features
|
||||
|
||||
* combineable binary math operators to specific adventure points value by active instances instead of fixed functions ([f11c1e1](https://github.com/elyukai/optolith-database-schema/commit/f11c1e1e07318ed5836d9964f3658335bf07d072))
|
||||
|
||||
## [0.18.13](https://github.com/elyukai/optolith-database-schema/compare/v0.18.12...v0.18.13) (2025-10-17)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add options for adventure point multiplier expression ([7898b2d](https://github.com/elyukai/optolith-database-schema/commit/7898b2da788e386e74f75b9883638026900b3e95))
|
||||
|
||||
## [0.18.12](https://github.com/elyukai/optolith-database-schema/compare/v0.18.11...v0.18.12) (2025-10-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* allow single range as a possible book cost value ([4e60943](https://github.com/elyukai/optolith-database-schema/commit/4e60943174213defe3ad006f64b18c359cc86469))
|
||||
|
||||
## [0.18.11](https://github.com/elyukai/optolith-database-schema/compare/v0.18.10...v0.18.11) (2025-10-17)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* duplicate DefiniteBookCostVariant identifier ([3735381](https://github.com/elyukai/optolith-database-schema/commit/373538121fecbae58dfb02d782d23bbdc9a17029))
|
||||
|
||||
## [0.18.10](https://github.com/elyukai/optolith-database-schema/compare/v0.18.9...v0.18.10) (2025-10-17)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* books may have multiple cost values dependending on the edition or other factors ([bd4507d](https://github.com/elyukai/optolith-database-schema/commit/bd4507d2507e01e363dad831a50f87e691ed6087))
|
||||
* trade secrets and activatable entries can have an adventure points value that is based on the number of active instances ([c575ec1](https://github.com/elyukai/optolith-database-schema/commit/c575ec11f7326902348956971bea73cf7e9b2cb9))
|
||||
* trade secrets can have explicit select options ([48e5d3f](https://github.com/elyukai/optolith-database-schema/commit/48e5d3f8c688733227a8de5cc629b3878f949fcf))
|
||||
|
||||
## [0.18.9](https://github.com/elyukai/optolith-database-schema/compare/v0.18.8...v0.18.9) (2025-10-15)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add alternative rule and player type entities ([034a2ed](https://github.com/elyukai/optolith-database-schema/commit/034a2ed6849a5581951fe1757c5c7179509f76bd))
|
||||
|
||||
## [0.18.8](https://github.com/elyukai/optolith-database-schema/compare/v0.18.7...v0.18.8) (2025-10-14)
|
||||
|
||||
## [0.18.7](https://github.com/elyukai/optolith-database-schema/compare/v0.18.6...v0.18.7) (2025-10-14)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* additionalRules have to be optional ([1b42e26](https://github.com/elyukai/optolith-database-schema/commit/1b42e26383567195fc328fecedb760990b3ca6aa))
|
||||
|
||||
## [0.18.6](https://github.com/elyukai/optolith-database-schema/compare/v0.18.5...v0.18.6) (2025-10-14)
|
||||
|
||||
|
||||
### Features
|
||||
|
||||
* add additionalRules to optional rules to separate box text from associated main text ([a7b1bed](https://github.com/elyukai/optolith-database-schema/commit/a7b1bed0fadc3d346b4962e1ba808eb2ac05e670))
|
||||
|
||||
## [0.18.5](https://github.com/elyukai/optolith-database-schema/compare/v0.18.4...v0.18.5) (2025-10-09)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* duplicate identifier violations and non-existent display name ([0bd47d1](https://github.com/elyukai/optolith-database-schema/commit/0bd47d12b58cf47f804387443927c0d97acc7ce7))
|
||||
|
||||
## [0.18.4](https://github.com/elyukai/optolith-database-schema/compare/v0.18.3...v0.18.4) (2025-10-05)
|
||||
|
||||
## [0.18.3](https://github.com/elyukai/optolith-database-schema/compare/v0.18.2...v0.18.3) (2025-10-04)
|
||||
|
||||
|
||||
### Bug Fixes
|
||||
|
||||
* include new equipment entities top-level in schema ([b3f7344](https://github.com/elyukai/optolith-database-schema/commit/b3f734484bbc52bea1f0a3542f3fccb1edfe66cf))
|
||||
|
||||
## [0.18.2](https://github.com/elyukai/optolith-database-schema/compare/v0.18.1...v0.18.2) (2025-10-04)
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
import js from "@eslint/js"
|
||||
import { defineConfig, globalIgnores } from "eslint/config"
|
||||
import globals from "globals"
|
||||
import tseslint from "typescript-eslint"
|
||||
|
||||
export default defineConfig([
|
||||
globalIgnores([
|
||||
"**/gen/",
|
||||
"**/lib/",
|
||||
"src/{cache,config,helpers,rendering}/**/*",
|
||||
"tsondb.config.{js,mjs,cjs,ts,mts,cts}",
|
||||
]),
|
||||
{
|
||||
files: [
|
||||
"src/**/*.{js,mjs,cjs,ts,mts,cts}"
|
||||
],
|
||||
plugins: {
|
||||
js,
|
||||
},
|
||||
extends: ["js/recommended"],
|
||||
},
|
||||
{
|
||||
files: [
|
||||
"src/**/*.{js,mjs,cjs,ts,mts,cts}"
|
||||
],
|
||||
languageOptions: {
|
||||
globals: {
|
||||
...globals.node,
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
ignores: [
|
||||
"eslint.config.js",
|
||||
],
|
||||
extends: tseslint.configs.strictTypeChecked,
|
||||
languageOptions: {
|
||||
parserOptions: {
|
||||
projectService: true,
|
||||
tsconfigRootDir: import.meta.dirname,
|
||||
},
|
||||
},
|
||||
rules: {
|
||||
"@typescript-eslint/no-unused-vars": [
|
||||
"error",
|
||||
{
|
||||
args: "all",
|
||||
argsIgnorePattern: "^_",
|
||||
caughtErrors: "all",
|
||||
caughtErrorsIgnorePattern: "^_",
|
||||
destructuredArrayIgnorePattern: "^_",
|
||||
varsIgnorePattern: "^_",
|
||||
ignoreRestSiblings: true,
|
||||
},
|
||||
],
|
||||
"@typescript-eslint/consistent-type-exports": "error",
|
||||
"@typescript-eslint/consistent-type-imports": "error",
|
||||
"@typescript-eslint/no-empty-object-type": [
|
||||
"error",
|
||||
{
|
||||
allowInterfaces: "with-single-extends",
|
||||
},
|
||||
],
|
||||
"@typescript-eslint/no-namespace": [
|
||||
"error",
|
||||
{
|
||||
allowDeclarations: true,
|
||||
},
|
||||
],
|
||||
"@typescript-eslint/prefer-promise-reject-errors": [
|
||||
"error",
|
||||
{
|
||||
allowThrowingUnknown: true,
|
||||
}
|
||||
],
|
||||
"@typescript-eslint/no-base-to-string": "off",
|
||||
"@typescript-eslint/no-array-constructor": "off",
|
||||
},
|
||||
},
|
||||
// {
|
||||
// files: ["test/**/*.{js,mjs,cjs,ts,mts,cts}"],
|
||||
// rules: {
|
||||
// "@typescript-eslint/no-floating-promises": "off",
|
||||
// }
|
||||
// },
|
||||
])
|
||||
Generated
+1428
-89
File diff suppressed because it is too large
Load Diff
+16
-6
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "optolith-database-schema",
|
||||
"version": "0.18.2",
|
||||
"version": "0.24.0",
|
||||
"description": "Definitions and utilities for the flat-file database of Optolith, a character creation tool for the Pen and Paper RPG “The Dark Eye 5”, and its external integrations into other software.",
|
||||
"keywords": [
|
||||
"tde",
|
||||
@@ -9,6 +9,7 @@
|
||||
"dasschwarzeauge"
|
||||
],
|
||||
"files": [
|
||||
"gen",
|
||||
"lib",
|
||||
"schema",
|
||||
"AUTHORS",
|
||||
@@ -17,17 +18,21 @@
|
||||
"main": "./lib/main.js",
|
||||
"exports": {
|
||||
".": "./lib/main.js",
|
||||
"./errors": "./lib/errors.js",
|
||||
"./gen": "./gen/types.d.ts",
|
||||
"./utils": "./lib/utils.js",
|
||||
"./cache/*": "./lib/cache/*.js",
|
||||
"./config/*": "./lib/config/*.js",
|
||||
"./rendering/*": "./lib/rendering/*.js",
|
||||
"./types": "./lib/types/index.js",
|
||||
"./types/*": "./lib/types/*.js"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"watch": "tsc -w",
|
||||
"generate": "tsondb generate",
|
||||
"test": "node ./lib/test.js",
|
||||
"lint": "eslint",
|
||||
"format": "prettier \"src/{types/**/*.{ts,tsx},*.{ts,tsx}}\" --write",
|
||||
"format:check": "prettier \"src/{types/**/*.{ts,tsx},*.{ts,tsx}}\" --check",
|
||||
"release": "commit-and-tag-version",
|
||||
"release:sign": "commit-and-tag-version --sign --signoff"
|
||||
},
|
||||
@@ -35,13 +40,18 @@
|
||||
"license": "MPL-2.0",
|
||||
"dependencies": {
|
||||
"@optolith/helpers": "^0.2.2",
|
||||
"tsondb": "^0.7.4",
|
||||
"tsondb": "^0.12.9",
|
||||
"yaml": "^2.8.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/node": "^24.6.2",
|
||||
"@eslint/js": "^9.39.1",
|
||||
"@types/node": "^24.10.1",
|
||||
"commit-and-tag-version": "^12.6.0",
|
||||
"typescript": "^5.9.3"
|
||||
"eslint": "^9.39.1",
|
||||
"globals": "^16.5.0",
|
||||
"prettier": "^3.7.3",
|
||||
"typescript": "^5.9.3",
|
||||
"typescript-eslint": "^8.48.0"
|
||||
},
|
||||
"repository": "github:elyukai/optolith-database-schema",
|
||||
"bugs": {
|
||||
|
||||
+61
-107
@@ -1,60 +1,14 @@
|
||||
import { isNotNullish } from "@optolith/helpers/nullable"
|
||||
import { mapObject } from "@optolith/helpers/object"
|
||||
import { assertExhaustive } from "@optolith/helpers/typeSafety"
|
||||
import type { CacheConfig } from "../cacheConfig.js"
|
||||
import { TypeMap } from "../config/types.js"
|
||||
import { ValidResults } from "../main.js"
|
||||
import { Aspect } from "../types/Aspect.js"
|
||||
import { Element } from "../types/Element.js"
|
||||
import { Property } from "../types/Property.js"
|
||||
import { TargetCategory } from "../types/TargetCategory.js"
|
||||
import {
|
||||
ExplicitSelectOption,
|
||||
SelectOptions,
|
||||
SkillApplication,
|
||||
SkillApplications,
|
||||
SkillUse,
|
||||
SkillUses,
|
||||
} from "../types/_Activatable.js"
|
||||
import {
|
||||
SelectOptionCategory,
|
||||
SelectOptionsAdventurePointsValue,
|
||||
SkillApplicationOrUse,
|
||||
SkillSelectOptionCategoryPrerequisite,
|
||||
SpecificFromSkillSelectOptionCategoryCategory,
|
||||
SpecificTargetCategory,
|
||||
} from "../types/_ActivatableSelectOptionCategory.js"
|
||||
import {
|
||||
CeremonyIdentifier,
|
||||
CloseCombatTechniqueIdentifier,
|
||||
LiturgicalChantIdentifier,
|
||||
RangedCombatTechniqueIdentifier,
|
||||
RitualIdentifier,
|
||||
SkillIdentifier,
|
||||
SpellIdentifier,
|
||||
} from "../types/_Identifier.js"
|
||||
import {
|
||||
ActivatableIdentifier,
|
||||
CombatTechniqueIdentifier,
|
||||
SelectOptionIdentifier,
|
||||
SkillishIdentifier as SkillIdentifierGroup,
|
||||
} from "../types/_IdentifierGroup.js"
|
||||
import { ImprovementCost } from "../types/_ImprovementCost.js"
|
||||
import { LocaleMap } from "../types/_LocaleMap.js"
|
||||
import { GeneralPrerequisites, PrerequisiteForLevel } from "../types/_Prerequisite.js"
|
||||
import { Poison } from "../types/equipment/item/Poison.js"
|
||||
import { GeneralPrerequisiteGroup } from "../types/prerequisites/PrerequisiteGroups.js"
|
||||
import { Errata } from "../types/source/_Erratum.js"
|
||||
import { PublicationRefs } from "../types/source/_PublicationRef.js"
|
||||
import { BlessedTradition } from "../types/specialAbility/BlessedTradition.js"
|
||||
import { Language } from "../types/specialAbility/sub/Language.js"
|
||||
import * as Database from "../../gen/types.js"
|
||||
|
||||
const PRINCIPLES_ID = 31
|
||||
const PROPERTY_KNOWLEDGE_ID = 3
|
||||
const ASPECT_KNOWLEDGE_ID = 1
|
||||
|
||||
export type ResolvedSelectOption = {
|
||||
id: SelectOptionIdentifier
|
||||
id: Database.RequirableSelectOptionIdentifier
|
||||
|
||||
/**
|
||||
* Sometimes, professions use specific text selections that are not
|
||||
@@ -99,7 +53,7 @@ export type ResolvedSelectOption = {
|
||||
*/
|
||||
ap_value?: number
|
||||
|
||||
src?: PublicationRefs
|
||||
src?: Database.PublicationRefs
|
||||
|
||||
/**
|
||||
* All translations for the entry, identified by IETF language tag (BCP47).
|
||||
@@ -129,10 +83,10 @@ export type ResolvedSelectOptionTranslation = {
|
||||
}
|
||||
const matchesSpecificSkillishIdList = <T>(
|
||||
id: T,
|
||||
config: SpecificFromSkillSelectOptionCategoryCategory<{ id: T }>,
|
||||
equalsId: (a: T, b: T) => boolean
|
||||
config: Database.SpecificFromSkillSelectOptionCategoryCategory<{ id: T }>,
|
||||
equalsId: (a: T, b: T) => boolean,
|
||||
): boolean => {
|
||||
switch (config.operation) {
|
||||
switch (config.operation.kind) {
|
||||
case "Intersection":
|
||||
return config.list.some(ref => equalsId(ref.id, id))
|
||||
case "Difference":
|
||||
@@ -143,8 +97,8 @@ const matchesSpecificSkillishIdList = <T>(
|
||||
}
|
||||
|
||||
const getSkillishPrerequisites = (
|
||||
ps: SkillSelectOptionCategoryPrerequisite[] | undefined,
|
||||
id: SkillIdentifierGroup | CombatTechniqueIdentifier
|
||||
ps: Database.SkillSelectOptionCategoryPrerequisite[] | undefined,
|
||||
id: SkillishIdentifier | Database.CombatTechniqueIdentifier,
|
||||
): GeneralPrerequisites | undefined => {
|
||||
if (ps === undefined) {
|
||||
return undefined
|
||||
@@ -190,7 +144,7 @@ const getSkillishPrerequisites = (
|
||||
|
||||
const equalsSkillishIdGroup = (
|
||||
a: SkillIdentifierGroup | CombatTechniqueIdentifier,
|
||||
b: SkillIdentifierGroup | CombatTechniqueIdentifier
|
||||
b: SkillIdentifierGroup | CombatTechniqueIdentifier,
|
||||
): boolean => {
|
||||
switch (a.tag) {
|
||||
case "Skill":
|
||||
@@ -221,7 +175,7 @@ const getApValueForSkillish = (
|
||||
| SelectOptionsAdventurePointsValue<SkillIdentifierGroup | CombatTechniqueIdentifier>
|
||||
| undefined,
|
||||
id: SkillIdentifierGroup | CombatTechniqueIdentifier,
|
||||
ic: ImprovementCost
|
||||
ic: ImprovementCost,
|
||||
): number | undefined => {
|
||||
if (config === undefined) {
|
||||
return undefined
|
||||
@@ -259,7 +213,7 @@ const getApValueForSkillish = (
|
||||
|
||||
const convertSkillApplicationOrUse = (
|
||||
id: SkillIdentifier,
|
||||
applicationOrUse: SkillApplicationOrUse
|
||||
applicationOrUse: SkillApplicationOrUse,
|
||||
): SkillApplication | SkillUse => ({
|
||||
id: applicationOrUse.id,
|
||||
skill: { tag: "Single", single: { id } },
|
||||
@@ -269,7 +223,7 @@ const convertSkillApplicationOrUse = (
|
||||
const getDerivedSelectOptions = (
|
||||
selectOptionCategory: SelectOptionCategory,
|
||||
entryId: ActivatableIdentifier,
|
||||
database: ValidResults
|
||||
database: ValidResults,
|
||||
): ResolvedSelectOption[] => {
|
||||
switch (selectOptionCategory.tag) {
|
||||
case "Blessings":
|
||||
@@ -317,14 +271,14 @@ const getDerivedSelectOptions = (
|
||||
.toSorted(([_1, a], [_2, b]) => a.size.id - b.size.id)
|
||||
.map(([id]) => id),
|
||||
}),
|
||||
{}
|
||||
{},
|
||||
)
|
||||
|
||||
return database.animalShapes.map(([_, animalShape]) => {
|
||||
const path = database.animalShapePaths.find(([id]) => id === animalShape.path.id)?.[1]
|
||||
const size = database.animalShapeSizes.find(([id]) => id === animalShape.size.id)?.[1]
|
||||
const pathIndex =
|
||||
path !== undefined ? pathsWithOrderedIds[path.id]?.indexOf(animalShape.id) ?? -1 : -1
|
||||
path !== undefined ? (pathsWithOrderedIds[path.id]?.indexOf(animalShape.id) ?? -1) : -1
|
||||
return {
|
||||
id: { tag: "AnimalShape", animal_shape: animalShape.id },
|
||||
prerequisites:
|
||||
@@ -333,7 +287,7 @@ const getDerivedSelectOptions = (
|
||||
? database.animalShapePaths
|
||||
.filter(
|
||||
([id]) =>
|
||||
id !== animalShape.path.id && pathsWithOrderedIds[id]?.[0] !== undefined
|
||||
id !== animalShape.path.id && pathsWithOrderedIds[id]?.[0] !== undefined,
|
||||
)
|
||||
.map(([id]) => ({
|
||||
level: 1,
|
||||
@@ -445,7 +399,7 @@ const getDerivedSelectOptions = (
|
||||
translations: mapObject(race.translations, t10n => ({
|
||||
name: t10n.name,
|
||||
})),
|
||||
})
|
||||
}),
|
||||
),
|
||||
...database.cultures.map(
|
||||
([_, culture]): ResolvedSelectOption => ({
|
||||
@@ -454,13 +408,13 @@ const getDerivedSelectOptions = (
|
||||
translations: mapObject(culture.translations, t10n => ({
|
||||
name: t10n.name,
|
||||
})),
|
||||
})
|
||||
}),
|
||||
),
|
||||
]
|
||||
|
||||
case "BlessedTraditions": {
|
||||
const getPrerequisites = (
|
||||
blessedTradition: BlessedTradition
|
||||
blessedTradition: BlessedTradition,
|
||||
): GeneralPrerequisites | undefined => {
|
||||
if (
|
||||
selectOptionCategory.blessed_traditions.require_principles &&
|
||||
@@ -500,7 +454,7 @@ const getDerivedSelectOptions = (
|
||||
case "Elements": {
|
||||
const mapToResolvedSelectOption = ([_, element]: [
|
||||
number,
|
||||
Element
|
||||
Element,
|
||||
]): ResolvedSelectOption => ({
|
||||
id: { tag: "Element", element: element.id },
|
||||
translations: mapObject(element.translations, t10n => ({
|
||||
@@ -511,7 +465,7 @@ const getDerivedSelectOptions = (
|
||||
if (selectOptionCategory.elements.specific) {
|
||||
return database.elements
|
||||
.filter(([id]) =>
|
||||
selectOptionCategory.elements.specific!.some(ref => ref.id.element === id)
|
||||
selectOptionCategory.elements.specific!.some(ref => ref.id.element === id),
|
||||
)
|
||||
.map(mapToResolvedSelectOption)
|
||||
}
|
||||
@@ -650,9 +604,9 @@ const getDerivedSelectOptions = (
|
||||
? undefined
|
||||
: {
|
||||
name: t10n.master_of_aspect_suffix,
|
||||
}
|
||||
},
|
||||
),
|
||||
})
|
||||
}),
|
||||
)
|
||||
.filter(value => Object.keys(value.translations).length > 0)
|
||||
}
|
||||
@@ -759,7 +713,7 @@ const getDerivedSelectOptions = (
|
||||
const matchesGroupRequirement =
|
||||
category.skills.groups === undefined ||
|
||||
category.skills.groups.some(
|
||||
ref => ref.id.skill_group === skill.group.id.skill_group
|
||||
ref => ref.id.skill_group === skill.group.id.skill_group,
|
||||
)
|
||||
|
||||
const matchesIdRequirement =
|
||||
@@ -767,7 +721,7 @@ const getDerivedSelectOptions = (
|
||||
matchesSpecificSkillishIdList<SkillIdentifier>(
|
||||
{ tag: "Skill", skill: skill.id },
|
||||
category.skills.specific,
|
||||
equalsSkillishIdGroup
|
||||
equalsSkillishIdGroup,
|
||||
)
|
||||
|
||||
return matchesGroupRequirement && matchesIdRequirement
|
||||
@@ -777,16 +731,16 @@ const getDerivedSelectOptions = (
|
||||
return {
|
||||
id,
|
||||
skill_uses: category.skills.skill_uses?.map(use =>
|
||||
convertSkillApplicationOrUse(id, use)
|
||||
convertSkillApplicationOrUse(id, use),
|
||||
),
|
||||
skill_applications: category.skills.skill_applications?.map(use =>
|
||||
convertSkillApplicationOrUse(id, use)
|
||||
convertSkillApplicationOrUse(id, use),
|
||||
),
|
||||
prerequisites: getSkillishPrerequisites(category.skills.prerequisites, id),
|
||||
ap_value: getApValueForSkillish(
|
||||
category.skills.ap_value ?? apValueGen,
|
||||
id,
|
||||
skill.improvement_cost
|
||||
skill.improvement_cost,
|
||||
),
|
||||
src: skill.src,
|
||||
translations: mapObject(skill.translations, t10n => ({
|
||||
@@ -802,8 +756,8 @@ const getDerivedSelectOptions = (
|
||||
matchesSpecificSkillishIdList<SpellIdentifier>(
|
||||
{ tag: "Spell", spell: spell.id },
|
||||
category.spells.specific,
|
||||
equalsSkillishIdGroup
|
||||
)
|
||||
equalsSkillishIdGroup,
|
||||
),
|
||||
)
|
||||
.map(([_, spell]): ResolvedSelectOption => {
|
||||
const id: SpellIdentifier = { tag: "Spell", spell: spell.id }
|
||||
@@ -825,8 +779,8 @@ const getDerivedSelectOptions = (
|
||||
matchesSpecificSkillishIdList<RitualIdentifier>(
|
||||
{ tag: "Ritual", ritual: ritual.id },
|
||||
category.rituals.specific,
|
||||
equalsSkillishIdGroup
|
||||
)
|
||||
equalsSkillishIdGroup,
|
||||
),
|
||||
)
|
||||
.map(([_, ritual]): ResolvedSelectOption => {
|
||||
const id: RitualIdentifier = { tag: "Ritual", ritual: ritual.id }
|
||||
@@ -848,8 +802,8 @@ const getDerivedSelectOptions = (
|
||||
matchesSpecificSkillishIdList<LiturgicalChantIdentifier>(
|
||||
{ tag: "LiturgicalChant", liturgical_chant: liturgicalChant.id },
|
||||
category.liturgical_chants.specific,
|
||||
equalsSkillishIdGroup
|
||||
)
|
||||
equalsSkillishIdGroup,
|
||||
),
|
||||
)
|
||||
.map(([_, liturgicalChant]): ResolvedSelectOption => {
|
||||
const id: LiturgicalChantIdentifier = {
|
||||
@@ -860,7 +814,7 @@ const getDerivedSelectOptions = (
|
||||
id,
|
||||
prerequisites: getSkillishPrerequisites(
|
||||
category.liturgical_chants.prerequisites,
|
||||
id
|
||||
id,
|
||||
),
|
||||
ap_value: getApValueForSkillish(apValueGen, id, liturgicalChant.improvement_cost),
|
||||
src: liturgicalChant.src,
|
||||
@@ -877,8 +831,8 @@ const getDerivedSelectOptions = (
|
||||
matchesSpecificSkillishIdList<CeremonyIdentifier>(
|
||||
{ tag: "Ceremony", ceremony: ceremony.id },
|
||||
category.ceremonies.specific,
|
||||
equalsSkillishIdGroup
|
||||
)
|
||||
equalsSkillishIdGroup,
|
||||
),
|
||||
)
|
||||
.map(([_, ceremony]): ResolvedSelectOption => {
|
||||
const id: CeremonyIdentifier = { tag: "Ceremony", ceremony: ceremony.id }
|
||||
@@ -914,8 +868,8 @@ const getDerivedSelectOptions = (
|
||||
close_combat_technique: closeCombatTechnique.id,
|
||||
},
|
||||
category.close_combat_techniques.specific,
|
||||
equalsSkillishIdGroup
|
||||
)
|
||||
equalsSkillishIdGroup,
|
||||
),
|
||||
)
|
||||
.map(([_, closeCombatTechnique]): ResolvedSelectOption => {
|
||||
const id: CloseCombatTechniqueIdentifier = {
|
||||
@@ -926,12 +880,12 @@ const getDerivedSelectOptions = (
|
||||
id,
|
||||
prerequisites: getSkillishPrerequisites(
|
||||
category.close_combat_techniques.prerequisites,
|
||||
id
|
||||
id,
|
||||
),
|
||||
ap_value: getApValueForSkillish(
|
||||
apValueGen,
|
||||
id,
|
||||
closeCombatTechnique.improvement_cost
|
||||
closeCombatTechnique.improvement_cost,
|
||||
),
|
||||
src: closeCombatTechnique.src,
|
||||
translations: mapObject(closeCombatTechnique.translations, t10n => ({
|
||||
@@ -950,8 +904,8 @@ const getDerivedSelectOptions = (
|
||||
ranged_combat_technique: rangedCombatTechnique.id,
|
||||
},
|
||||
category.ranged_combat_techniques.specific,
|
||||
equalsSkillishIdGroup
|
||||
)
|
||||
equalsSkillishIdGroup,
|
||||
),
|
||||
)
|
||||
.map(([_, rangedCombatTechnique]): ResolvedSelectOption => {
|
||||
const id: RangedCombatTechniqueIdentifier = {
|
||||
@@ -962,12 +916,12 @@ const getDerivedSelectOptions = (
|
||||
id,
|
||||
prerequisites: getSkillishPrerequisites(
|
||||
category.ranged_combat_techniques.prerequisites,
|
||||
id
|
||||
id,
|
||||
),
|
||||
ap_value: getApValueForSkillish(
|
||||
apValueGen,
|
||||
id,
|
||||
rangedCombatTechnique.improvement_cost
|
||||
rangedCombatTechnique.improvement_cost,
|
||||
),
|
||||
src: rangedCombatTechnique.src,
|
||||
translations: mapObject(rangedCombatTechnique.translations, t10n => ({
|
||||
@@ -984,7 +938,7 @@ const getDerivedSelectOptions = (
|
||||
case "TargetCategories": {
|
||||
const mapToResolvedSelectOption = (
|
||||
targetCategory: TargetCategory,
|
||||
specificTargetCategory?: SpecificTargetCategory
|
||||
specificTargetCategory?: SpecificTargetCategory,
|
||||
): ResolvedSelectOption => ({
|
||||
id: { tag: "TargetCategory", target_category: targetCategory.id },
|
||||
volume: specificTargetCategory?.volume,
|
||||
@@ -996,15 +950,15 @@ const getDerivedSelectOptions = (
|
||||
if (selectOptionCategory.target_categories.list) {
|
||||
return database.targetCategories
|
||||
.filter(([id]) =>
|
||||
selectOptionCategory.target_categories.list!.some(ref => ref.id.target_category === id)
|
||||
selectOptionCategory.target_categories.list!.some(ref => ref.id.target_category === id),
|
||||
)
|
||||
.map(([id, targetCategory]) =>
|
||||
mapToResolvedSelectOption(
|
||||
targetCategory,
|
||||
selectOptionCategory.target_categories.list!.find(
|
||||
ref => ref.id.target_category === id
|
||||
)
|
||||
)
|
||||
ref => ref.id.target_category === id,
|
||||
),
|
||||
),
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1019,7 +973,7 @@ const getDerivedSelectOptions = (
|
||||
const joinLocaleMaps = <T, U, V>(
|
||||
a: LocaleMap<T>,
|
||||
b: LocaleMap<U>,
|
||||
join: (a?: T, b?: U) => V
|
||||
join: (a?: T, b?: U) => V,
|
||||
): LocaleMap<NonNullable<V>> => {
|
||||
const combinedLocaleMap: LocaleMap<NonNullable<V>> = {}
|
||||
for (const key of new Set([...Object.keys(a), ...Object.keys(b)])) {
|
||||
@@ -1033,7 +987,7 @@ const joinLocaleMaps = <T, U, V>(
|
||||
|
||||
const getExplicitSelectOptions = (
|
||||
explicitSelectOptions: ExplicitSelectOption[],
|
||||
database: ValidResults
|
||||
database: ValidResults,
|
||||
): ResolvedSelectOption[] =>
|
||||
explicitSelectOptions
|
||||
.map((explicitSelectOption): ResolvedSelectOption | undefined => {
|
||||
@@ -1053,16 +1007,16 @@ const getExplicitSelectOptions = (
|
||||
...explicitSelectOption.skill,
|
||||
id,
|
||||
skill_applications: explicitSelectOption.skill.skill_applications?.map(use =>
|
||||
convertSkillApplicationOrUse(id, use)
|
||||
convertSkillApplicationOrUse(id, use),
|
||||
),
|
||||
skill_uses: explicitSelectOption.skill.skill_uses?.map(use =>
|
||||
convertSkillApplicationOrUse(id, use)
|
||||
convertSkillApplicationOrUse(id, use),
|
||||
),
|
||||
translations: joinLocaleMaps(
|
||||
explicitSelectOption.skill.translations ?? {},
|
||||
skill.translations,
|
||||
(explicit, base) =>
|
||||
base === undefined ? undefined : { ...explicit, name: base.name }
|
||||
base === undefined ? undefined : { ...explicit, name: base.name },
|
||||
),
|
||||
}
|
||||
}
|
||||
@@ -1071,7 +1025,7 @@ const getExplicitSelectOptions = (
|
||||
case "CloseCombatTechnique": {
|
||||
const id = explicitSelectOption.combat_technique.id.close_combat_technique
|
||||
const closeCombatTechnique = database.closeCombatTechniques.find(
|
||||
p => p[0] === id
|
||||
p => p[0] === id,
|
||||
)?.[1]
|
||||
if (closeCombatTechnique === undefined) {
|
||||
return undefined
|
||||
@@ -1083,14 +1037,14 @@ const getExplicitSelectOptions = (
|
||||
explicitSelectOption.combat_technique.translations ?? {},
|
||||
closeCombatTechnique.translations,
|
||||
(explicit, base) =>
|
||||
base === undefined ? undefined : { ...explicit, name: base.name }
|
||||
base === undefined ? undefined : { ...explicit, name: base.name },
|
||||
),
|
||||
}
|
||||
}
|
||||
case "RangedCombatTechnique": {
|
||||
const id = explicitSelectOption.combat_technique.id.ranged_combat_technique
|
||||
const rangedCombatTechnique = database.rangedCombatTechniques.find(
|
||||
p => p[0] === id
|
||||
p => p[0] === id,
|
||||
)?.[1]
|
||||
if (rangedCombatTechnique === undefined) {
|
||||
return undefined
|
||||
@@ -1102,7 +1056,7 @@ const getExplicitSelectOptions = (
|
||||
explicitSelectOption.combat_technique.translations ?? {},
|
||||
rangedCombatTechnique.translations,
|
||||
(explicit, base) =>
|
||||
base === undefined ? undefined : { ...explicit, name: base.name }
|
||||
base === undefined ? undefined : { ...explicit, name: base.name },
|
||||
),
|
||||
}
|
||||
}
|
||||
@@ -1118,7 +1072,7 @@ const getExplicitSelectOptions = (
|
||||
const getSelectOptions = (
|
||||
selectOptions: SelectOptions,
|
||||
id: ActivatableIdentifier,
|
||||
database: ValidResults
|
||||
database: ValidResults,
|
||||
): ResolvedSelectOption[] => [
|
||||
...(selectOptions.derived === undefined
|
||||
? []
|
||||
@@ -1131,7 +1085,7 @@ const getSelectOptions = (
|
||||
const getSelectOptionsForResults = (
|
||||
database: ValidResults,
|
||||
idTag: ActivatableIdentifier["tag"],
|
||||
results: [id: number, data: { select_options?: SelectOptions }][]
|
||||
results: [id: number, data: { select_options?: SelectOptions }][],
|
||||
) =>
|
||||
results.reduce<{
|
||||
[id: number]: ResolvedSelectOption[]
|
||||
@@ -1247,7 +1201,7 @@ const getSelectOptionsForResults = (
|
||||
return assertExhaustive(idTag)
|
||||
}
|
||||
})(),
|
||||
database
|
||||
database,
|
||||
)
|
||||
if (options.length > 0) {
|
||||
acc[id] = options
|
||||
|
||||
Vendored
+20
-26
@@ -1,30 +1,24 @@
|
||||
import type { CacheConfig } from "../cacheConfig.js"
|
||||
import type { CacheBuilder } from "./cacheConfig.js"
|
||||
|
||||
export type AncestorBloodAdvantagesCache = {
|
||||
ids: number[]
|
||||
ids: string[]
|
||||
}
|
||||
|
||||
export const config: CacheConfig<AncestorBloodAdvantagesCache> = {
|
||||
builder(database) {
|
||||
return {
|
||||
ids: database.advantages
|
||||
.filter(([id, advantage]) =>
|
||||
advantage.prerequisites?.some(p => {
|
||||
switch (p.prerequisite.tag) {
|
||||
case "Single":
|
||||
return p.prerequisite.single.tag === "NoOtherAncestorBloodAdvantage"
|
||||
case "Disjunction":
|
||||
return p.prerequisite.disjunction.list.some(
|
||||
d => d.tag === "NoOtherAncestorBloodAdvantage"
|
||||
)
|
||||
case "Group":
|
||||
return p.prerequisite.group.list.some(
|
||||
g => g.tag === "NoOtherAncestorBloodAdvantage"
|
||||
)
|
||||
}
|
||||
})
|
||||
)
|
||||
.map(([id]) => id),
|
||||
}
|
||||
},
|
||||
}
|
||||
export const build: CacheBuilder<AncestorBloodAdvantagesCache> = ({ getAllInstances }) => ({
|
||||
ids: getAllInstances("Advantage")
|
||||
.filter(([id, advantage]) =>
|
||||
advantage.prerequisites?.some(p => {
|
||||
switch (p.prerequisite.kind) {
|
||||
case "Single":
|
||||
return p.prerequisite.Single.kind === "NoOtherAncestorBloodAdvantage"
|
||||
case "Disjunction":
|
||||
return p.prerequisite.Disjunction.list.some(
|
||||
d => d.kind === "NoOtherAncestorBloodAdvantage",
|
||||
)
|
||||
case "Group":
|
||||
return p.prerequisite.Group.list.some(g => g.kind === "NoOtherAncestorBloodAdvantage")
|
||||
}
|
||||
}),
|
||||
)
|
||||
.map(([id]) => id),
|
||||
})
|
||||
|
||||
Vendored
+8
@@ -0,0 +1,8 @@
|
||||
import type { ChildEntityMap, EntityMap } from "../../gen/types.js"
|
||||
import type { GetAllChildInstancesForParent, GetAllInstances, GetInstanceById } from "../utils.js"
|
||||
|
||||
export type CacheBuilder<T> = (database: {
|
||||
getInstanceById: GetInstanceById<keyof EntityMap>
|
||||
getAllInstances: GetAllInstances<keyof EntityMap>
|
||||
getAllChildInstancesForParent: GetAllChildInstancesForParent<keyof ChildEntityMap>
|
||||
}) => T
|
||||
+67
-114
@@ -1,8 +1,12 @@
|
||||
import { assertExhaustive } from "@optolith/helpers/typeSafety"
|
||||
import type { CacheConfig } from "../cacheConfig.js"
|
||||
import { ActivatableIdentifier, RatedIdentifier } from "../types/_IdentifierGroup.js"
|
||||
import { AdvantageDisadvantagePrerequisites } from "../types/_Prerequisite.js"
|
||||
import { AdvantageDisadvantagePrerequisiteGroup } from "../types/prerequisites/PrerequisiteGroups.js"
|
||||
import type {
|
||||
AdvantageDisadvantagePrerequisiteGroup,
|
||||
AdvantageDisadvantagePrerequisites,
|
||||
EntityMap,
|
||||
RatedIdentifier,
|
||||
} from "../../gen/types.js"
|
||||
import { stringFromEnumIdentifier, type GetInstanceById } from "../utils.js"
|
||||
import type { CacheBuilder } from "./cacheConfig.js"
|
||||
|
||||
const BLESSED_ID = 12
|
||||
const SPELLCASTER_ID = 47
|
||||
@@ -10,18 +14,18 @@ const SPELLCASTER_ID = 47
|
||||
export type MagicalAndBlessedAdvantagesAndDisadvantagesCache = {
|
||||
advantages: {
|
||||
magical: {
|
||||
ids: number[]
|
||||
ids: string[]
|
||||
}
|
||||
blessed: {
|
||||
ids: number[]
|
||||
ids: string[]
|
||||
}
|
||||
}
|
||||
disadvantages: {
|
||||
magical: {
|
||||
ids: number[]
|
||||
ids: string[]
|
||||
}
|
||||
blessed: {
|
||||
ids: number[]
|
||||
ids: string[]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -38,7 +42,7 @@ const getAdvantageId = (type: "Magical" | "Blessed") => {
|
||||
}
|
||||
|
||||
const isRatedFor = (type: "Magical" | "Blessed", ratedId: RatedIdentifier) => {
|
||||
switch (ratedId.tag) {
|
||||
switch (ratedId.kind) {
|
||||
case "Spell":
|
||||
case "Ritual":
|
||||
return type === "Magical"
|
||||
@@ -58,26 +62,33 @@ const isRatedFor = (type: "Magical" | "Blessed", ratedId: RatedIdentifier) => {
|
||||
const isPrerequisiteFor = (
|
||||
type: "Magical" | "Blessed",
|
||||
prerequisite: AdvantageDisadvantagePrerequisiteGroup,
|
||||
getById: (
|
||||
id: ActivatableIdentifier
|
||||
) => { id: number; prerequisites?: AdvantageDisadvantagePrerequisites } | undefined,
|
||||
traversedIds: number[]
|
||||
getInstanceById: GetInstanceById<keyof EntityMap>,
|
||||
traversedIds: string[],
|
||||
): boolean => {
|
||||
switch (prerequisite.tag) {
|
||||
switch (prerequisite.kind) {
|
||||
case "Activatable": {
|
||||
if (
|
||||
prerequisite.activatable.id.tag === "Advantage" &&
|
||||
prerequisite.activatable.id.advantage === getAdvantageId(type) &&
|
||||
prerequisite.activatable.active
|
||||
prerequisite.Activatable.id.kind === "Advantage" &&
|
||||
prerequisite.Activatable.id.Advantage === getAdvantageId(type) &&
|
||||
prerequisite.Activatable.active
|
||||
) {
|
||||
return true
|
||||
}
|
||||
|
||||
const entry = getById(prerequisite.activatable.id)
|
||||
return entry !== undefined && is(type, entry, getById, traversedIds)
|
||||
const entry = getInstanceById(prerequisite.Activatable.id)
|
||||
return (
|
||||
entry !== undefined &&
|
||||
is(
|
||||
type,
|
||||
stringFromEnumIdentifier(prerequisite.Activatable.id),
|
||||
entry,
|
||||
getInstanceById,
|
||||
traversedIds,
|
||||
)
|
||||
)
|
||||
}
|
||||
case "Rated":
|
||||
return isRatedFor(type, prerequisite.rated.id)
|
||||
return isRatedFor(type, prerequisite.Rated.id)
|
||||
case "CommonSuggestedByRCP":
|
||||
case "Sex":
|
||||
case "Race":
|
||||
@@ -91,7 +102,7 @@ const isPrerequisiteFor = (
|
||||
case "MagicalTradition":
|
||||
case "RatedMinimumNumber":
|
||||
case "RatedSum":
|
||||
case "ExternalEnhancement":
|
||||
case "Enhancement":
|
||||
case "Text":
|
||||
case "NoOtherAncestorBloodAdvantage":
|
||||
case "SexualCharacteristic":
|
||||
@@ -103,31 +114,35 @@ const isPrerequisiteFor = (
|
||||
|
||||
const is = (
|
||||
type: "Magical" | "Blessed",
|
||||
entry: { id: number; prerequisites?: AdvantageDisadvantagePrerequisites },
|
||||
getById: (
|
||||
id: ActivatableIdentifier
|
||||
) => { id: number; prerequisites?: AdvantageDisadvantagePrerequisites } | undefined,
|
||||
traversedIds: number[]
|
||||
entryId: string,
|
||||
entry: { prerequisites?: AdvantageDisadvantagePrerequisites },
|
||||
getInstanceById: GetInstanceById<keyof EntityMap>,
|
||||
traversedIds: string[],
|
||||
): boolean => {
|
||||
if (!entry.prerequisites || traversedIds.includes(entry.id)) {
|
||||
if (!entry.prerequisites || traversedIds.includes(entryId)) {
|
||||
return false
|
||||
}
|
||||
|
||||
const newTraversedIds = [...traversedIds, entry.id]
|
||||
const newTraversedIds = [...traversedIds, entryId]
|
||||
|
||||
return (
|
||||
entry.prerequisites !== undefined &&
|
||||
entry.prerequisites.some(prerequisite => {
|
||||
switch (prerequisite.prerequisite.tag) {
|
||||
switch (prerequisite.prerequisite.kind) {
|
||||
case "Single":
|
||||
return isPrerequisiteFor(type, prerequisite.prerequisite.single, getById, newTraversedIds)
|
||||
return isPrerequisiteFor(
|
||||
type,
|
||||
prerequisite.prerequisite.Single,
|
||||
getInstanceById,
|
||||
newTraversedIds,
|
||||
)
|
||||
case "Disjunction":
|
||||
return prerequisite.prerequisite.disjunction.list.some(p =>
|
||||
isPrerequisiteFor(type, p, getById, newTraversedIds)
|
||||
return prerequisite.prerequisite.Disjunction.list.some(p =>
|
||||
isPrerequisiteFor(type, p, getInstanceById, newTraversedIds),
|
||||
)
|
||||
case "Group":
|
||||
return prerequisite.prerequisite.group.list.some(p =>
|
||||
isPrerequisiteFor(type, p, getById, newTraversedIds)
|
||||
return prerequisite.prerequisite.Group.list.some(p =>
|
||||
isPrerequisiteFor(type, p, getInstanceById, newTraversedIds),
|
||||
)
|
||||
default:
|
||||
return assertExhaustive(prerequisite.prerequisite)
|
||||
@@ -136,84 +151,22 @@ const is = (
|
||||
)
|
||||
}
|
||||
|
||||
export const config: CacheConfig<MagicalAndBlessedAdvantagesAndDisadvantagesCache> = {
|
||||
builder(database) {
|
||||
const getActivatableById = (id: ActivatableIdentifier) => {
|
||||
// prettier-ignore
|
||||
switch (id.tag) {
|
||||
case "AdvancedCombatSpecialAbility": return database.advancedCombatSpecialAbilities.find(([entryId]) => entryId === id.advanced_combat_special_ability)?.[1]
|
||||
case "AdvancedKarmaSpecialAbility": return database.advancedKarmaSpecialAbilities.find(([entryId]) => entryId === id.advanced_karma_special_ability)?.[1]
|
||||
case "AdvancedMagicalSpecialAbility": return database.advancedMagicalSpecialAbilities.find(([entryId]) => entryId === id.advanced_magical_special_ability)?.[1]
|
||||
case "AdvancedSkillSpecialAbility": return database.advancedSkillSpecialAbilities.find(([entryId]) => entryId === id.advanced_skill_special_ability)?.[1]
|
||||
case "Advantage": return database.advantages.find(([entryId]) => entryId === id.advantage)?.[1]
|
||||
case "AncestorGlyph": return database.ancestorGlyphs.find(([entryId]) => entryId === id.ancestor_glyph)?.[1]
|
||||
case "ArcaneOrbEnchantment": return database.arcaneOrbEnchantments.find(([entryId]) => entryId === id.arcane_orb_enchantment)?.[1]
|
||||
case "AttireEnchantment": return database.attireEnchantments.find(([entryId]) => entryId === id.attire_enchantment)?.[1]
|
||||
case "BlessedTradition": return database.blessedTraditions.find(([entryId]) => entryId === id.blessed_tradition)?.[1]
|
||||
case "BowlEnchantment": return database.bowlEnchantments.find(([entryId]) => entryId === id.bowl_enchantment)?.[1]
|
||||
case "BrawlingSpecialAbility": return database.brawlingSpecialAbilities.find(([entryId]) => entryId === id.brawling_special_ability)?.[1]
|
||||
case "CauldronEnchantment": return database.cauldronEnchantments.find(([entryId]) => entryId === id.cauldron_enchantment)?.[1]
|
||||
case "CeremonialItemSpecialAbility": return database.ceremonialItemSpecialAbilities.find(([entryId]) => entryId === id.ceremonial_item_special_ability)?.[1]
|
||||
case "ChronicleEnchantment": return database.chronicleEnchantments.find(([entryId]) => entryId === id.chronicle_enchantment)?.[1]
|
||||
case "CombatSpecialAbility": return database.combatSpecialAbilities.find(([entryId]) => entryId === id.combat_special_ability)?.[1]
|
||||
case "CombatStyleSpecialAbility": return database.combatStyleSpecialAbilities.find(([entryId]) => entryId === id.combat_style_special_ability)?.[1]
|
||||
case "CommandSpecialAbility": return database.commandSpecialAbilities.find(([entryId]) => entryId === id.command_special_ability)?.[1]
|
||||
case "DaggerRitual": return database.daggerRituals.find(([entryId]) => entryId === id.dagger_ritual)?.[1]
|
||||
case "Disadvantage": return database.disadvantages.find(([entryId]) => entryId === id.disadvantage)?.[1]
|
||||
case "FamiliarSpecialAbility": return database.familiarSpecialAbilities.find(([entryId]) => entryId === id.familiar_special_ability)?.[1]
|
||||
case "FatePointSexSpecialAbility": return database.fatePointSexSpecialAbilities.find(([entryId]) => entryId === id.fate_point_sex_special_ability)?.[1]
|
||||
case "FatePointSpecialAbility": return database.fatePointSpecialAbilities.find(([entryId]) => entryId === id.fate_point_special_ability)?.[1]
|
||||
case "FoolsHatEnchantment": return database.foolsHatEnchantments.find(([entryId]) => entryId === id.fools_hat_enchantment)?.[1]
|
||||
case "GeneralSpecialAbility": return database.generalSpecialAbilities.find(([entryId]) => entryId === id.general_special_ability)?.[1]
|
||||
case "InstrumentEnchantment": return database.instrumentEnchantments.find(([entryId]) => entryId === id.instrument_enchantment)?.[1]
|
||||
case "KarmaSpecialAbility": return database.karmaSpecialAbilities.find(([entryId]) => entryId === id.karma_special_ability)?.[1]
|
||||
case "Krallenkettenzauber": return database.krallenkettenzauber.find(([entryId]) => entryId === id.krallenkettenzauber)?.[1]
|
||||
case "LiturgicalStyleSpecialAbility": return database.liturgicalStyleSpecialAbilities.find(([entryId]) => entryId === id.liturgical_style_special_ability)?.[1]
|
||||
case "LycantropicGift": return database.lycantropicGifts.find(([entryId]) => entryId === id.lycantropic_gift)?.[1]
|
||||
case "MagicalSign": return database.magicalSigns.find(([entryId]) => entryId === id.magical_sign)?.[1]
|
||||
case "MagicalSpecialAbility": return database.magicalSpecialAbilities.find(([entryId]) => entryId === id.magical_special_ability)?.[1]
|
||||
case "MagicalTradition": return database.magicalTraditions.find(([entryId]) => entryId === id.magical_tradition)?.[1]
|
||||
case "MagicStyleSpecialAbility": return database.magicStyleSpecialAbilities.find(([entryId]) => entryId === id.magic_style_special_ability)?.[1]
|
||||
case "OrbEnchantment": return database.orbEnchantments.find(([entryId]) => entryId === id.orb_enchantment)?.[1]
|
||||
case "PactGift": return database.pactGifts.find(([entryId]) => entryId === id.pact_gift)?.[1]
|
||||
case "ProtectiveWardingCircleSpecialAbility": return database.protectiveWardingCircleSpecialAbilities.find(([entryId]) => entryId === id.protective_warding_circle_special_ability)?.[1]
|
||||
case "RingEnchantment": return database.ringEnchantments.find(([entryId]) => entryId === id.ring_enchantment)?.[1]
|
||||
case "Sermon": return database.sermons.find(([entryId]) => entryId === id.sermon)?.[1]
|
||||
case "SexSpecialAbility": return database.sexSpecialAbilities.find(([entryId]) => entryId === id.sex_special_ability)?.[1]
|
||||
case "SickleRitual": return database.sickleRituals.find(([entryId]) => entryId === id.sickle_ritual)?.[1]
|
||||
case "SikaryanDrainSpecialAbility": return database.sikaryanDrainSpecialAbilities.find(([entryId]) => entryId === id.sikaryan_drain_special_ability)?.[1]
|
||||
case "SkillStyleSpecialAbility": return database.skillStyleSpecialAbilities.find(([entryId]) => entryId === id.skill_style_special_ability)?.[1]
|
||||
case "SpellSwordEnchantment": return database.spellSwordEnchantments.find(([entryId]) => entryId === id.spell_sword_enchantment)?.[1]
|
||||
case "StaffEnchantment": return database.staffEnchantments.find(([entryId]) => entryId === id.staff_enchantment)?.[1]
|
||||
case "ToyEnchantment": return database.toyEnchantments.find(([entryId]) => entryId === id.toy_enchantment)?.[1]
|
||||
case "Trinkhornzauber": return database.trinkhornzauber.find(([entryId]) => entryId === id.trinkhornzauber)?.[1]
|
||||
case "VampiricGift": return database.vampiricGifts.find(([entryId]) => entryId === id.vampiric_gift)?.[1]
|
||||
case "Vision": return database.visions.find(([entryId]) => entryId === id.vision)?.[1]
|
||||
case "WandEnchantment": return database.wandEnchantments.find(([entryId]) => entryId === id.wand_enchantment)?.[1]
|
||||
case "WeaponEnchantment": return database.weaponEnchantments.find(([entryId]) => entryId === id.weapon_enchantment)?.[1]
|
||||
default:
|
||||
return assertExhaustive(id)
|
||||
}
|
||||
}
|
||||
|
||||
// prettier-ignore
|
||||
return {
|
||||
advantages: {
|
||||
magical: {
|
||||
ids: database.advantages.filter(([_, entry]) => is("Magical", entry, getActivatableById, [])).map(([id]) => id),
|
||||
},
|
||||
blessed: {
|
||||
ids: database.advantages.filter(([_, entry]) => is("Blessed", entry, getActivatableById, [])).map(([id]) => id),
|
||||
},
|
||||
},
|
||||
disadvantages: {
|
||||
magical: {
|
||||
ids: database.disadvantages.filter(([_, entry]) => is("Magical", entry, getActivatableById, [])).map(([id]) => id),
|
||||
},
|
||||
blessed: {
|
||||
ids: database.disadvantages.filter(([_, entry]) => is("Blessed", entry, getActivatableById, [])).map(([id]) => id),
|
||||
},
|
||||
},
|
||||
}
|
||||
// prettier-ignore
|
||||
export const config: CacheBuilder<MagicalAndBlessedAdvantagesAndDisadvantagesCache> = ({ getAllInstances, getInstanceById }) => ({
|
||||
advantages: {
|
||||
magical: {
|
||||
ids: getAllInstances("Advantage").filter(([id, entry]) => is("Magical", id, entry, getInstanceById, [])).map(([id]) => id),
|
||||
},
|
||||
blessed: {
|
||||
ids: getAllInstances("Advantage").filter(([id, entry]) => is("Blessed", id, entry, getInstanceById, [])).map(([id]) => id),
|
||||
},
|
||||
},
|
||||
}
|
||||
disadvantages: {
|
||||
magical: {
|
||||
ids: getAllInstances("Disadvantage").filter(([id, entry]) => is("Magical", id, entry, getInstanceById, [])).map(([id]) => id),
|
||||
},
|
||||
blessed: {
|
||||
ids: getAllInstances("Disadvantage").filter(([id, entry]) => is("Blessed", id, entry, getInstanceById, [])).map(([id]) => id),
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
@@ -1,5 +0,0 @@
|
||||
import { ValidResults } from "./main.js"
|
||||
|
||||
export type CacheConfig<T, D extends unknown[] = []> = {
|
||||
builder: (data: ValidResults, ...deps: D) => T
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
import { dirname, join } from "node:path"
|
||||
import { fileURLToPath } from "node:url"
|
||||
import { JsonSchemaSpec } from "optolith-tsjsonschemamd/renderers/jsonSchema"
|
||||
|
||||
const root = join(dirname(fileURLToPath(import.meta.url)), "..", "..")
|
||||
|
||||
export const sourceDir = join(root, "src", "types")
|
||||
export const libDir = join(root, "lib", "types")
|
||||
export const jsonSchemaDir = join(root, "schema")
|
||||
export const markdownDir = join(root, "docs", "reference")
|
||||
export const swiftDir = join(root, "Sources", "OptolithDatabaseSchema", "GeneratedTypes")
|
||||
|
||||
export const jsonSchemaSpec: JsonSchemaSpec = "Draft_2019_09"
|
||||
@@ -1,63 +0,0 @@
|
||||
import { filterNonNullable } from "@optolith/helpers/array"
|
||||
import { collator } from "./helpers/i18n.js"
|
||||
import { TypeValidationError } from "./main.js"
|
||||
import { IntegrityError } from "./validation/builders/integrity.js"
|
||||
import { FileNameError } from "./validation/builders/naming.js"
|
||||
|
||||
/**
|
||||
* Options for pretty-printing errors.
|
||||
*/
|
||||
export type PrintOptions = {
|
||||
/**
|
||||
* Whether to print each error message in a file or just print that a file has
|
||||
* errors.
|
||||
* @default false
|
||||
*/
|
||||
verbose?: boolean
|
||||
}
|
||||
|
||||
/**
|
||||
* Pretty-prints an error map.
|
||||
* @param errorsByFile A map from file paths to the errors that occurred in
|
||||
* them.
|
||||
* @param printOptions Configuration options for the printer.
|
||||
* @returns The pretty-printed error map.
|
||||
*/
|
||||
export const printErrors = (
|
||||
errorsByFile: Record<string, TypeValidationError[]>,
|
||||
printOptions: PrintOptions = {}
|
||||
): string => {
|
||||
const { verbose = false } = printOptions
|
||||
|
||||
return Object.entries(errorsByFile)
|
||||
.sort(([filePathA], [filePathB]) => collator.compare(filePathA, filePathB))
|
||||
.flatMap(
|
||||
verbose
|
||||
? ([filePath, errors]) => filterNonNullable(errors.map(printVerboseError(filePath)))
|
||||
: ([filePath, errors]) => [
|
||||
...errors
|
||||
.filter(
|
||||
(error): error is IntegrityError | FileNameError =>
|
||||
error.keyword === "integrity" || error.keyword === "filename"
|
||||
)
|
||||
.map(printVerboseError(filePath)),
|
||||
...(errors.some(error => error.keyword !== "integrity" && error.keyword !== "filename")
|
||||
? [errorMessageBlock([filePath], "has schema errors")]
|
||||
: []),
|
||||
]
|
||||
)
|
||||
.join("\n\n")
|
||||
}
|
||||
|
||||
const printVerboseError =
|
||||
(filePath: string) =>
|
||||
(error: TypeValidationError): string => {
|
||||
const pathSegments = [filePath, ...error.instancePath.split("/").slice(1)]
|
||||
return errorMessageBlock(pathSegments, error.message ?? "")
|
||||
}
|
||||
|
||||
const errorMessageBlock = (path: string[], message: string): string =>
|
||||
[
|
||||
...path.map((segment, i) => `${" ".repeat(i * 2)}in "${segment}":`),
|
||||
`${" ".repeat(path.length * 2)}${message}`,
|
||||
].join("\n")
|
||||
@@ -1,30 +0,0 @@
|
||||
declare global {
|
||||
interface Array<T> {
|
||||
/**
|
||||
* Returns a `Promise` that resolves when all `Promise`s in the array
|
||||
* resolve and rejects when any of the `Promise`s in the array rejects.
|
||||
*/
|
||||
promiseAll<T>(this: Promise<T>[]): Promise<T[]>
|
||||
|
||||
/**
|
||||
* Returns an object from an array of key-value pairs.
|
||||
*/
|
||||
objectFromEntries<K extends string, T>(this: [key: K, value: T][]): Record<K, T>
|
||||
}
|
||||
}
|
||||
|
||||
Object.defineProperty(Array.prototype, "promiseAll", {
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: function promiseAll<T>(this: Promise<T>[]): Promise<T[]> {
|
||||
return Promise.all(this)
|
||||
},
|
||||
})
|
||||
|
||||
Object.defineProperty(Array.prototype, "objectFromEntries", {
|
||||
configurable: true,
|
||||
writable: true,
|
||||
value: function objectFromEntries<T>(this: [string, T][]): { [k: string]: T } {
|
||||
return Object.fromEntries(this)
|
||||
},
|
||||
})
|
||||
@@ -1 +0,0 @@
|
||||
export const collator = Intl.Collator(undefined, { numeric: true })
|
||||
@@ -1,60 +0,0 @@
|
||||
import { readFile, readdir } from "node:fs/promises"
|
||||
import { basename, dirname, extname, join } from "node:path"
|
||||
import YAML from "yaml"
|
||||
|
||||
/**
|
||||
* Recursively reads all files in a directory and its subdirectories.
|
||||
* @param dirPath The path to the directory to read.
|
||||
*/
|
||||
export async function* readDirectoryRec(
|
||||
dirPath: string,
|
||||
filterName: (entryName: string) => boolean = () => true
|
||||
): AsyncGenerator<string> {
|
||||
const dirEntries = await readdir(dirPath, { withFileTypes: true })
|
||||
|
||||
for (const dirEntry of dirEntries) {
|
||||
if (filterName(dirEntry.name)) {
|
||||
const dirEntryPath = join(dirPath, dirEntry.name)
|
||||
if (dirEntry.isDirectory()) {
|
||||
yield* readDirectoryRec(dirEntryPath)
|
||||
} else {
|
||||
yield dirEntryPath
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a JSON file and parses its content.
|
||||
* @param filePath The path to the JSON file.
|
||||
* @returns The parsed JSON content.
|
||||
*/
|
||||
export const readJsonFile = async (filePath: string): Promise<unknown> => {
|
||||
const fileContent = await readFile(filePath, "utf-8")
|
||||
return JSON.parse(fileContent)
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads a YAML file and parses its content.
|
||||
* @param filePath The path to the YAML file.
|
||||
* @returns The parsed YAML content.
|
||||
*/
|
||||
export const readYamlFile = async (filePath: string): Promise<unknown> => {
|
||||
const fileContent = await readFile(filePath, "utf-8")
|
||||
return YAML.parse(fileContent)
|
||||
}
|
||||
|
||||
/**
|
||||
* Changes the extension of a file path.
|
||||
* @param path The path to the file.
|
||||
* @param ext The new extension to use.
|
||||
* @returns
|
||||
*/
|
||||
export const changeFileExtension = (path: string, ext: string) =>
|
||||
join(dirname(path), basename(path, extname(path)) + ext)
|
||||
|
||||
/**
|
||||
* Checks if a file name represents a hidden file.
|
||||
* @param name The file name to check.
|
||||
*/
|
||||
export const isHiddenFileName = (name: string) => !name.startsWith(".")
|
||||
@@ -1,14 +0,0 @@
|
||||
/**
|
||||
* A path segment descriptor is either a key or an index.
|
||||
*/
|
||||
export type PathSegmentDescriptor = { k: string } | { i: number }
|
||||
|
||||
/**
|
||||
* A path descriptor is a list of path segment descriptors.
|
||||
*/
|
||||
export type PathDescriptor = PathSegmentDescriptor[]
|
||||
|
||||
export const printPathDescriptor = (pathDescriptor: PathDescriptor): string =>
|
||||
pathDescriptor
|
||||
.map(segment => "k" in segment ? segment.k : segment.i)
|
||||
.join("/")
|
||||
@@ -1,27 +0,0 @@
|
||||
/**
|
||||
* Map the second element of a pair using a function that receives both
|
||||
* elements.
|
||||
*/
|
||||
export const mapSecond = <A, B, C>(f: (second: B, first: A) => C) =>
|
||||
(pair: [A, B]): [A, C] => [pair[0], f(pair[1], pair[0])]
|
||||
|
||||
/**
|
||||
* Map the second element of a pair using a function that receives both
|
||||
* elements and returns a `Promise`.
|
||||
*/
|
||||
export const mapSecondAsync = <A, B, C>(f: (second: B, first: A) => Promise<C>) =>
|
||||
async (pair: [A, B]): Promise<[A, C]> => [pair[0], await f(pair[1], pair[0])]
|
||||
|
||||
/**
|
||||
* Map a value using a function into the second value of a pair with the
|
||||
* original value.
|
||||
*/
|
||||
export const mapToSecond = <A, B>(f: (value: A) => B) =>
|
||||
(value: A): [A, B] => [value, f(value)]
|
||||
|
||||
/**
|
||||
* Map a value using a function that returns a `Promise` into the second value
|
||||
* of a pair with the original value.
|
||||
*/
|
||||
export const mapToSecondAsync = <A, B>(f: (value: A) => Promise<B>) =>
|
||||
async (value: A): Promise<[A, B]> => [value, await f(value)]
|
||||
+156
-298
@@ -1,306 +1,164 @@
|
||||
import { Schema } from "tsondb/schema"
|
||||
import { Advantage } from "./types/Advantage.js"
|
||||
import { AnimalDisease } from "./types/AnimalDisease.js"
|
||||
import { AnimalType } from "./types/AnimalType.js"
|
||||
import { ArcaneBardTradition } from "./types/ArcaneBardTradition.js"
|
||||
import { ArcaneDancerTradition } from "./types/ArcaneDancerTradition.js"
|
||||
import { Aspect } from "./types/Aspect.js"
|
||||
import { Attribute } from "./types/Attribute.js"
|
||||
import { Blessing } from "./types/Blessing.js"
|
||||
import { Cantrip } from "./types/Cantrip.js"
|
||||
import { Ceremony } from "./types/Ceremony.js"
|
||||
import { CloseCombatTechnique, RangedCombatTechnique } from "./types/CombatTechnique.js"
|
||||
import { Condition } from "./types/Condition.js"
|
||||
import { Continent } from "./types/Continent.js"
|
||||
import { Culture } from "./types/Culture.js"
|
||||
import { DerivedCharacteristic } from "./types/DerivedCharacteristic.js"
|
||||
import { Disadvantage } from "./types/Disadvantage.js"
|
||||
import { Disease } from "./types/Disease.js"
|
||||
import { Element } from "./types/Element.js"
|
||||
import { ExperienceLevel } from "./types/ExperienceLevel.js"
|
||||
import { EyeColor } from "./types/EyeColor.js"
|
||||
import { FamiliarsTrick } from "./types/FamiliarsTrick.js"
|
||||
import { HairColor } from "./types/HairColor.js"
|
||||
import { Curriculum, Guideline } from "./types/Lessons.js"
|
||||
import { LiturgicalChant } from "./types/LiturgicalChant.js"
|
||||
import { Locale } from "./types/Locale.js"
|
||||
import { MetaCondition } from "./types/MetaCondition.js"
|
||||
import { PactCategory } from "./types/PactCategory.js"
|
||||
import { Patron } from "./types/Patron.js"
|
||||
import { PatronCategory } from "./types/PatronCategory.js"
|
||||
import { PersonalityTrait } from "./types/PersonalityTrait.js"
|
||||
import { Profession } from "./types/Profession.js"
|
||||
import { Property } from "./types/Property.js"
|
||||
import { Race } from "./types/Race.js"
|
||||
import { Region } from "./types/Region.js"
|
||||
import { Ritual } from "./types/Ritual.js"
|
||||
import { Service } from "./types/Service.js"
|
||||
import { SexPractice } from "./types/SexPractice.js"
|
||||
import { Skill } from "./types/Skill.js"
|
||||
import { SkillGroup } from "./types/SkillGroup.js"
|
||||
import { SkillModificationLevel } from "./types/SkillModificationLevel.js"
|
||||
import { SocialStatus } from "./types/SocialStatus.js"
|
||||
import { Spell } from "./types/Spell.js"
|
||||
import { State } from "./types/State.js"
|
||||
import { Talisman } from "./types/Talisman.js"
|
||||
import { TargetCategory } from "./types/TargetCategory.js"
|
||||
import { EquipmentPackage } from "./types/equipment/EquipmentPackage.js"
|
||||
import { Ammunition } from "./types/equipment/item/Ammunition.js"
|
||||
import { Animal } from "./types/equipment/item/Animal.js"
|
||||
import { AnimalCare } from "./types/equipment/item/AnimalCare.js"
|
||||
import { Armor } from "./types/equipment/item/Armor.js"
|
||||
import { BandageOrRemedy } from "./types/equipment/item/BandageOrRemedy.js"
|
||||
import { Book } from "./types/equipment/item/Book.js"
|
||||
import { CeremonialItem } from "./types/equipment/item/CeremonialItem.js"
|
||||
import { Clothes } from "./types/equipment/item/Clothes.js"
|
||||
import { Container } from "./types/equipment/item/Container.js"
|
||||
import { Elixir } from "./types/equipment/item/Elixir.js"
|
||||
import { EquipmentOfBlessedOnes } from "./types/equipment/item/EquipmentOfBlessedOnes.js"
|
||||
import { GemOrPreciousStone } from "./types/equipment/item/GemOrPreciousStone.js"
|
||||
import { IlluminationLightSource } from "./types/equipment/item/IlluminationLightSource.js"
|
||||
import { IlluminationRefillOrSupply } from "./types/equipment/item/IlluminationRefillOrSupply.js"
|
||||
import { Jewelry } from "./types/equipment/item/Jewelry.js"
|
||||
import { Liebesspielzeug } from "./types/equipment/item/Liebesspielzeug.js"
|
||||
import { LuxuryGood } from "./types/equipment/item/LuxuryGood.js"
|
||||
import { MagicalArtifact } from "./types/equipment/item/MagicalArtifact.js"
|
||||
import { MusicalInstrument } from "./types/equipment/item/MusicalInstrument.js"
|
||||
import { OrienteeringAid } from "./types/equipment/item/OrienteeringAid.js"
|
||||
import { Poison } from "./types/equipment/item/Poison.js"
|
||||
import { RopeOrChain } from "./types/equipment/item/RopeOrChain.js"
|
||||
import { Stationery } from "./types/equipment/item/Stationery.js"
|
||||
import { ThievesTool } from "./types/equipment/item/ThievesTool.js"
|
||||
import { ToolOfTheTrade } from "./types/equipment/item/ToolOfTheTrade.js"
|
||||
import { TravelGearOrTool } from "./types/equipment/item/TravelGearOrTool.js"
|
||||
import { Vehicle } from "./types/equipment/item/Vehicle.js"
|
||||
import { Weapon } from "./types/equipment/item/Weapon.js"
|
||||
import { WeaponAccessory } from "./types/equipment/item/WeaponAccessory.js"
|
||||
import { ArmorType } from "./types/equipment/item/sub/ArmorType.js"
|
||||
import { Reach } from "./types/equipment/item/sub/Reach.js"
|
||||
import { AnimistPower } from "./types/magicalActions/AnimistPower.js"
|
||||
import { Tribe } from "./types/magicalActions/AnimistPower_Tribe.js"
|
||||
import { Curse } from "./types/magicalActions/Curse.js"
|
||||
import { DominationRitual } from "./types/magicalActions/DominationRitual.js"
|
||||
import { ElvenMagicalSong } from "./types/magicalActions/ElvenMagicalSong.js"
|
||||
import { GeodeRitual } from "./types/magicalActions/GeodeRitual.js"
|
||||
import { JesterTrick } from "./types/magicalActions/JesterTrick.js"
|
||||
import { MagicalDance } from "./types/magicalActions/MagicalDance.js"
|
||||
import { MagicalMelody } from "./types/magicalActions/MagicalMelody.js"
|
||||
import { MagicalRune } from "./types/magicalActions/MagicalRune.js"
|
||||
import { ZibiljaRitual } from "./types/magicalActions/ZibiljaRitual.js"
|
||||
import { CoreRule } from "./types/rule/CoreRule.js"
|
||||
import { FocusRule } from "./types/rule/FocusRule.js"
|
||||
import { Subject } from "./types/rule/FocusRule_Subject.js"
|
||||
import { OptionalRule } from "./types/rule/OptionalRule.js"
|
||||
import { Publication } from "./types/source/Publication.js"
|
||||
import { AdvancedCombatSpecialAbility } from "./types/specialAbility/AdvancedCombatSpecialAbility.js"
|
||||
import { AdvancedKarmaSpecialAbility } from "./types/specialAbility/AdvancedKarmaSpecialAbility.js"
|
||||
import { AdvancedMagicalSpecialAbility } from "./types/specialAbility/AdvancedMagicalSpecialAbility.js"
|
||||
import { AdvancedSkillSpecialAbility } from "./types/specialAbility/AdvancedSkillSpecialAbility.js"
|
||||
import { AncestorGlyph } from "./types/specialAbility/AncestorGlyph.js"
|
||||
import { BlessedTradition } from "./types/specialAbility/BlessedTradition.js"
|
||||
import { BrawlingSpecialAbility } from "./types/specialAbility/BrawlingSpecialAbility.js"
|
||||
import { CeremonialItemSpecialAbility } from "./types/specialAbility/CeremonialItemSpecialAbility.js"
|
||||
import { CombatSpecialAbility } from "./types/specialAbility/CombatSpecialAbility.js"
|
||||
import { CombatStyleSpecialAbility } from "./types/specialAbility/CombatStyleSpecialAbility.js"
|
||||
import { CommandSpecialAbility } from "./types/specialAbility/CommandSpecialAbility.js"
|
||||
import { FamiliarSpecialAbility } from "./types/specialAbility/FamiliarSpecialAbility.js"
|
||||
import { FatePointSexSpecialAbility } from "./types/specialAbility/FatePointSexSpecialAbility.js"
|
||||
import { FatePointSpecialAbility } from "./types/specialAbility/FatePointSpecialAbility.js"
|
||||
import { GeneralSpecialAbility } from "./types/specialAbility/GeneralSpecialAbility.js"
|
||||
import { KarmaSpecialAbility } from "./types/specialAbility/KarmaSpecialAbility.js"
|
||||
import { LiturgicalStyleSpecialAbility } from "./types/specialAbility/LiturgicalStyleSpecialAbility.js"
|
||||
import { LycantropicGift } from "./types/specialAbility/LycantropicGift.js"
|
||||
import { MagicStyleSpecialAbility } from "./types/specialAbility/MagicStyleSpecialAbility.js"
|
||||
import { MagicalSign } from "./types/specialAbility/MagicalSign.js"
|
||||
import { MagicalSpecialAbility } from "./types/specialAbility/MagicalSpecialAbility.js"
|
||||
import { MagicalTradition } from "./types/specialAbility/MagicalTradition.js"
|
||||
import { PactGift } from "./types/specialAbility/PactGift.js"
|
||||
import { ProtectiveWardingCircleSpecialAbility } from "./types/specialAbility/ProtectiveWardingCircleSpecialAbility.js"
|
||||
import { Sermon } from "./types/specialAbility/Sermon.js"
|
||||
import { SexSpecialAbility } from "./types/specialAbility/SexSpecialAbility.js"
|
||||
import { SikaryanDrainSpecialAbility } from "./types/specialAbility/SikaryanDrainSpecialAbility.js"
|
||||
import { SkillStyleSpecialAbility } from "./types/specialAbility/SkillStyleSpecialAbility.js"
|
||||
import { VampiricGift } from "./types/specialAbility/VampiricGift.js"
|
||||
import { Vision } from "./types/specialAbility/Vision.js"
|
||||
import { Language } from "./types/specialAbility/sub/Language.js"
|
||||
import { Script } from "./types/specialAbility/sub/Script.js"
|
||||
import { TradeSecret } from "./types/specialAbility/sub/TradeSecret.js"
|
||||
import { ArcaneOrbEnchantment } from "./types/traditionArtifacts/ArcaneOrbEnchantment.js"
|
||||
import { AttireEnchantment } from "./types/traditionArtifacts/AttireEnchantment.js"
|
||||
import { BowlEnchantment } from "./types/traditionArtifacts/BowlEnchantment.js"
|
||||
import { CauldronEnchantment } from "./types/traditionArtifacts/CauldronEnchantment.js"
|
||||
import { ChronicleEnchantment } from "./types/traditionArtifacts/ChronicleEnchantment.js"
|
||||
import { DaggerRitual } from "./types/traditionArtifacts/DaggerRitual.js"
|
||||
import { FoolsHatEnchantment } from "./types/traditionArtifacts/FoolsHatEnchantment.js"
|
||||
import { InstrumentEnchantment } from "./types/traditionArtifacts/InstrumentEnchantment.js"
|
||||
import { Krallenkettenzauber } from "./types/traditionArtifacts/Krallenkettenzauber.js"
|
||||
import { OrbEnchantment } from "./types/traditionArtifacts/OrbEnchantment.js"
|
||||
import { RingEnchantment } from "./types/traditionArtifacts/RingEnchantment.js"
|
||||
import { SickleRitual } from "./types/traditionArtifacts/SickleRitual.js"
|
||||
import { SpellSwordEnchantment } from "./types/traditionArtifacts/SpellSwordEnchantment.js"
|
||||
import { StaffEnchantment } from "./types/traditionArtifacts/StaffEnchantment.js"
|
||||
import { ToyEnchantment } from "./types/traditionArtifacts/ToyEnchantment.js"
|
||||
import { Trinkhornzauber } from "./types/traditionArtifacts/Trinkhornzauber.js"
|
||||
import { WandEnchantment } from "./types/traditionArtifacts/WandEnchantment.js"
|
||||
import { WeaponEnchantment } from "./types/traditionArtifacts/WeaponEnchantment.js"
|
||||
import { AnimalShape } from "./types/traditionArtifacts/sub/AnimalShape.js"
|
||||
import { AnimalShapePath } from "./types/traditionArtifacts/sub/AnimalShapePath.js"
|
||||
import { AnimalShapeSize } from "./types/traditionArtifacts/sub/AnimalShapeSize.js"
|
||||
import { Brew } from "./types/traditionArtifacts/sub/Brew.js"
|
||||
import * as Types from "./types/index.js"
|
||||
|
||||
export const schema = Schema(
|
||||
[
|
||||
AdvancedCombatSpecialAbility,
|
||||
AdvancedKarmaSpecialAbility,
|
||||
AdvancedMagicalSpecialAbility,
|
||||
AdvancedSkillSpecialAbility,
|
||||
Advantage,
|
||||
Ammunition,
|
||||
AncestorGlyph,
|
||||
AnimalCare,
|
||||
AnimalDisease,
|
||||
Animal,
|
||||
AnimalShapePath,
|
||||
AnimalShape,
|
||||
AnimalShapeSize,
|
||||
AnimalType,
|
||||
AnimistPower,
|
||||
Tribe,
|
||||
ArcaneBardTradition,
|
||||
ArcaneDancerTradition,
|
||||
ArcaneOrbEnchantment,
|
||||
Armor,
|
||||
ArmorType,
|
||||
Aspect,
|
||||
AttireEnchantment,
|
||||
Attribute,
|
||||
BandageOrRemedy,
|
||||
BlessedTradition,
|
||||
Blessing,
|
||||
Book,
|
||||
BowlEnchantment,
|
||||
BrawlingSpecialAbility,
|
||||
Brew,
|
||||
Cantrip,
|
||||
CauldronEnchantment,
|
||||
CeremonialItem,
|
||||
CeremonialItemSpecialAbility,
|
||||
Ceremony,
|
||||
ChronicleEnchantment,
|
||||
CloseCombatTechnique,
|
||||
Clothes,
|
||||
CombatSpecialAbility,
|
||||
CombatStyleSpecialAbility,
|
||||
CommandSpecialAbility,
|
||||
Condition,
|
||||
Container,
|
||||
Continent,
|
||||
CoreRule,
|
||||
Culture,
|
||||
Curse,
|
||||
DaggerRitual,
|
||||
DerivedCharacteristic,
|
||||
Disadvantage,
|
||||
Disease,
|
||||
DominationRitual,
|
||||
Element,
|
||||
Elixir,
|
||||
ElvenMagicalSong,
|
||||
EquipmentOfBlessedOnes,
|
||||
EquipmentPackage,
|
||||
ExperienceLevel,
|
||||
EyeColor,
|
||||
FamiliarSpecialAbility,
|
||||
FamiliarsTrick,
|
||||
FatePointSexSpecialAbility,
|
||||
FatePointSpecialAbility,
|
||||
FocusRule,
|
||||
Subject,
|
||||
FoolsHatEnchantment,
|
||||
GemOrPreciousStone,
|
||||
GeneralSpecialAbility,
|
||||
GeodeRitual,
|
||||
HairColor,
|
||||
IlluminationLightSource,
|
||||
IlluminationRefillOrSupply,
|
||||
InstrumentEnchantment,
|
||||
JesterTrick,
|
||||
Jewelry,
|
||||
KarmaSpecialAbility,
|
||||
Krallenkettenzauber,
|
||||
Language,
|
||||
Curriculum,
|
||||
Guideline,
|
||||
Liebesspielzeug,
|
||||
LiturgicalChant,
|
||||
LiturgicalStyleSpecialAbility,
|
||||
LuxuryGood,
|
||||
LycantropicGift,
|
||||
MagicalArtifact,
|
||||
MagicalDance,
|
||||
MagicalMelody,
|
||||
MagicalRune,
|
||||
MagicalSign,
|
||||
MagicalSpecialAbility,
|
||||
MagicalTradition,
|
||||
MagicStyleSpecialAbility,
|
||||
MetaCondition,
|
||||
MusicalInstrument,
|
||||
OptionalRule,
|
||||
OrbEnchantment,
|
||||
OrienteeringAid,
|
||||
PactCategory,
|
||||
PactGift,
|
||||
PatronCategory,
|
||||
Patron,
|
||||
PersonalityTrait,
|
||||
Poison,
|
||||
Profession,
|
||||
Property,
|
||||
ProtectiveWardingCircleSpecialAbility,
|
||||
Publication,
|
||||
Race,
|
||||
RangedCombatTechnique,
|
||||
Reach,
|
||||
Region,
|
||||
RingEnchantment,
|
||||
Ritual,
|
||||
RopeOrChain,
|
||||
Script,
|
||||
Sermon,
|
||||
Service,
|
||||
SexPractice,
|
||||
SexSpecialAbility,
|
||||
SickleRitual,
|
||||
SikaryanDrainSpecialAbility,
|
||||
SkillGroup,
|
||||
SkillModificationLevel,
|
||||
Skill,
|
||||
SkillStyleSpecialAbility,
|
||||
SocialStatus,
|
||||
Spell,
|
||||
SpellSwordEnchantment,
|
||||
StaffEnchantment,
|
||||
State,
|
||||
Stationery,
|
||||
Talisman,
|
||||
TargetCategory,
|
||||
ThievesTool,
|
||||
ToolOfTheTrade,
|
||||
ToyEnchantment,
|
||||
TradeSecret,
|
||||
TravelGearOrTool,
|
||||
Trinkhornzauber,
|
||||
VampiricGift,
|
||||
Vehicle,
|
||||
Vision,
|
||||
WandEnchantment,
|
||||
WeaponAccessory,
|
||||
WeaponEnchantment,
|
||||
Weapon,
|
||||
ZibiljaRitual,
|
||||
Types.AdvancedCombatSpecialAbility,
|
||||
Types.AdvancedKarmaSpecialAbility,
|
||||
Types.AdvancedMagicalSpecialAbility,
|
||||
Types.AdvancedSkillSpecialAbility,
|
||||
Types.Advantage,
|
||||
Types.AlternativeRule,
|
||||
Types.Ammunition,
|
||||
Types.AncestorGlyph,
|
||||
Types.AnimalCare,
|
||||
Types.AnimalDisease,
|
||||
Types.Animal,
|
||||
Types.AnimalShapePath,
|
||||
Types.AnimalShape,
|
||||
Types.AnimalShapeSize,
|
||||
Types.AnimalType,
|
||||
Types.AnimistPower,
|
||||
Types.Tribe,
|
||||
Types.ArcaneBardTradition,
|
||||
Types.ArcaneDancerTradition,
|
||||
Types.ArcaneOrbEnchantment,
|
||||
Types.Armor,
|
||||
Types.ArmorType,
|
||||
Types.Aspect,
|
||||
Types.AttireEnchantment,
|
||||
Types.Attribute,
|
||||
Types.BandageOrRemedy,
|
||||
Types.BlessedTradition,
|
||||
Types.Blessing,
|
||||
Types.Book,
|
||||
Types.BowlEnchantment,
|
||||
Types.BrawlingSpecialAbility,
|
||||
Types.Brew,
|
||||
Types.Cantrip,
|
||||
Types.CauldronEnchantment,
|
||||
Types.CeremonialItem,
|
||||
Types.CeremonialItemSpecialAbility,
|
||||
Types.Ceremony,
|
||||
Types.ChronicleEnchantment,
|
||||
Types.CloseCombatTechnique,
|
||||
Types.Clothes,
|
||||
Types.ClothingPackage,
|
||||
Types.CombatSpecialAbility,
|
||||
Types.CombatStyleSpecialAbility,
|
||||
Types.CommandSpecialAbility,
|
||||
Types.Condition,
|
||||
Types.Container,
|
||||
Types.Continent,
|
||||
Types.CoreRule,
|
||||
Types.Culture,
|
||||
Types.Curse,
|
||||
Types.DaggerRitual,
|
||||
Types.DerivedCharacteristic,
|
||||
Types.Disadvantage,
|
||||
Types.Disease,
|
||||
Types.DominationRitual,
|
||||
Types.Element,
|
||||
Types.Elixir,
|
||||
Types.ElvenMagicalSong,
|
||||
Types.EquipmentOfBlessedOnes,
|
||||
Types.EquipmentPackage,
|
||||
Types.ExperienceLevel,
|
||||
Types.EyeColor,
|
||||
Types.FamiliarSpecialAbility,
|
||||
Types.FamiliarsTrick,
|
||||
Types.FatePointSexSpecialAbility,
|
||||
Types.FatePointSpecialAbility,
|
||||
Types.FocusRule,
|
||||
Types.Subject,
|
||||
Types.FoolsHatEnchantment,
|
||||
Types.GemOrPreciousStone,
|
||||
Types.GeneralSpecialAbility,
|
||||
Types.GeodeRitual,
|
||||
Types.HairColor,
|
||||
Types.IlluminationLightSource,
|
||||
Types.IlluminationRefillOrSupply,
|
||||
Types.InstrumentEnchantment,
|
||||
Types.JesterTrick,
|
||||
Types.Jewelry,
|
||||
Types.KarmaSpecialAbility,
|
||||
Types.Krallenkettenzauber,
|
||||
Types.Language,
|
||||
Types.Curriculum,
|
||||
Types.Guideline,
|
||||
Types.Laboratory,
|
||||
Types.Liebesspielzeug,
|
||||
Types.LiturgicalChant,
|
||||
Types.LiturgicalStyleSpecialAbility,
|
||||
Types.LuxuryGood,
|
||||
Types.LycantropicGift,
|
||||
Types.MagicalArtifact,
|
||||
Types.MagicalDance,
|
||||
Types.MagicalMelody,
|
||||
Types.MagicalRune,
|
||||
Types.MagicalSign,
|
||||
Types.MagicalSpecialAbility,
|
||||
Types.MagicalTradition,
|
||||
Types.MagicStyleSpecialAbility,
|
||||
Types.MetaCondition,
|
||||
Types.MusicalInstrument,
|
||||
Types.Newspaper,
|
||||
Types.OptionalRule,
|
||||
Types.OrbEnchantment,
|
||||
Types.OrienteeringAid,
|
||||
Types.PactCategory,
|
||||
Types.PactGift,
|
||||
Types.PatronCategory,
|
||||
Types.Patron,
|
||||
Types.PersonalityTrait,
|
||||
Types.PlayerType,
|
||||
Types.Poison,
|
||||
Types.Profession,
|
||||
Types.Property,
|
||||
Types.ProtectiveWardingCircleSpecialAbility,
|
||||
Types.Publication,
|
||||
Types.Race,
|
||||
Types.RangedCombatTechnique,
|
||||
Types.Reach,
|
||||
Types.Region,
|
||||
Types.RingEnchantment,
|
||||
Types.Ritual,
|
||||
Types.RopeOrChain,
|
||||
Types.Script,
|
||||
Types.Sermon,
|
||||
Types.Service,
|
||||
Types.SexPractice,
|
||||
Types.SexSpecialAbility,
|
||||
Types.SickleRitual,
|
||||
Types.SikaryanDrainSpecialAbility,
|
||||
Types.SkillGroup,
|
||||
Types.SkillModificationLevel,
|
||||
Types.Skill,
|
||||
Types.SkillStyleSpecialAbility,
|
||||
Types.SocialStatus,
|
||||
Types.Spell,
|
||||
Types.SpellSwordEnchantment,
|
||||
Types.StaffEnchantment,
|
||||
Types.State,
|
||||
Types.Stationery,
|
||||
Types.Talisman,
|
||||
Types.TargetCategory,
|
||||
Types.ThievesTool,
|
||||
Types.ToolOfTheTrade,
|
||||
Types.ToyEnchantment,
|
||||
Types.TradeSecret,
|
||||
Types.TravelGearOrTool,
|
||||
Types.Trinkhornzauber,
|
||||
Types.VampiricGift,
|
||||
Types.Vehicle,
|
||||
Types.Vision,
|
||||
Types.WandEnchantment,
|
||||
Types.WeaponAccessory,
|
||||
Types.WeaponEnchantment,
|
||||
Types.Weapon,
|
||||
Types.ZibiljaRitual,
|
||||
],
|
||||
Locale
|
||||
Types.Locale,
|
||||
)
|
||||
|
||||
// /**
|
||||
|
||||
@@ -1,41 +0,0 @@
|
||||
import { PluralizationCategories } from "../types/_I18n.js"
|
||||
import { UI } from "../types/UI.js"
|
||||
|
||||
const pluralType: {
|
||||
[K in keyof UI as UI[K] extends PluralizationCategories ? K : never]: Intl.PluralRuleType
|
||||
} = {
|
||||
"{0} Adventure Points": "cardinal",
|
||||
"You are missing {0} Adventure Points to do this.": "cardinal",
|
||||
"since the {0}. printing": "ordinal",
|
||||
"removed in {0}. printing": "ordinal",
|
||||
"{0} actions": "cardinal",
|
||||
"{0} hours": "cardinal",
|
||||
"{0} minutes": "cardinal",
|
||||
"{0} rounds": "cardinal",
|
||||
"{0} seduction actions": "cardinal",
|
||||
", {0} of which are permanent": "cardinal",
|
||||
"{0} centuries": "cardinal",
|
||||
"{0} combat rounds": "cardinal",
|
||||
"{0} days": "cardinal",
|
||||
"{0} months": "cardinal",
|
||||
"{0} mos.": "cardinal",
|
||||
"{0} seconds": "cardinal",
|
||||
"{0} weeks": "cardinal",
|
||||
"{0} wks.": "cardinal",
|
||||
"{0} years": "cardinal",
|
||||
"{0} yrs.": "cardinal",
|
||||
"{0} miles": "cardinal",
|
||||
"{0} yards": "cardinal",
|
||||
}
|
||||
|
||||
/**
|
||||
* The keys of the UI strings that support pluralization categories.
|
||||
*/
|
||||
export type KeySupportingPluralizationCategories = keyof typeof pluralType
|
||||
|
||||
/**
|
||||
* Returns the plural rule type for the given key.
|
||||
*/
|
||||
export const getPluralRuleTypeForKey = (
|
||||
key: KeySupportingPluralizationCategories
|
||||
): Intl.PluralRuleType => pluralType[key]
|
||||
+11
-19
@@ -1,21 +1,11 @@
|
||||
import { Boolean, Entity, IncludeIdentifier, Object, Optional, Required } from "tsondb/schema/def"
|
||||
import {
|
||||
ap_value,
|
||||
ap_value_append,
|
||||
ap_value_l10n,
|
||||
input,
|
||||
levels,
|
||||
maximum,
|
||||
name,
|
||||
name_in_library,
|
||||
range_l10n,
|
||||
rules,
|
||||
select_options,
|
||||
skill_applications,
|
||||
skill_uses,
|
||||
} from "./_Activatable.js"
|
||||
import { input, levels, maximum, name, name_in_library, range_l10n, rules } from "./_Activatable.js"
|
||||
import { ap_value, ap_value_append, ap_value_l10n } from "./_ActivatableAdventurePointsValue.js"
|
||||
import { automatic_entries } from "./_ActivatableAutomatic.js"
|
||||
import { explicit_select_options, select_options } from "./_ActivatableSelectOptions.js"
|
||||
import { skill_applications, skill_uses } from "./_ActivatableSkillApplicationsAndUses.js"
|
||||
import { AdvantageDisadvantagePrerequisites } from "./_Prerequisite.js"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
import { Errata } from "./source/_Erratum.js"
|
||||
import { src } from "./source/_PublicationRef.js"
|
||||
|
||||
@@ -26,9 +16,11 @@ export const Advantage = Entity(import.meta.url, {
|
||||
Object({
|
||||
levels,
|
||||
select_options,
|
||||
explicit_select_options,
|
||||
skill_applications,
|
||||
skill_uses,
|
||||
maximum,
|
||||
automatic_entries,
|
||||
prerequisites: Optional({
|
||||
type: IncludeIdentifier(AdvantageDisadvantagePrerequisites),
|
||||
}),
|
||||
@@ -43,9 +35,9 @@ export const Advantage = Entity(import.meta.url, {
|
||||
type: Boolean(),
|
||||
}),
|
||||
src,
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"AdvantageTranslation",
|
||||
"Advantage",
|
||||
Object({
|
||||
name,
|
||||
name_in_library,
|
||||
@@ -57,7 +49,7 @@ export const Advantage = Entity(import.meta.url, {
|
||||
errata: Optional({
|
||||
type: IncludeIdentifier(Errata),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Array, Entity, IncludeIdentifier, Object, Required } from "tsondb/schema/def"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
import { cause, Cause, DiseaseTranslation, level, resistance } from "./_DiseasePoison.js"
|
||||
import { AnimalTypeIdentifier } from "./_Identifier.js"
|
||||
import { src } from "./source/_PublicationRef.js"
|
||||
@@ -25,7 +25,7 @@ If no causes are given, the disease is not communicable to intelligent creatures
|
||||
type: Array(IncludeIdentifier(Cause)),
|
||||
}),
|
||||
src,
|
||||
translations: NestedLocaleMap(Required, "AnimalDiseaseTranslation", DiseaseTranslation),
|
||||
translations: NestedTranslationMap(Required, "AnimalDisease", DiseaseTranslation),
|
||||
}),
|
||||
displayName: {},
|
||||
})
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import { Entity, Object, Required, String } from "tsondb/schema/def"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
|
||||
export const AnimalType = Entity(import.meta.url, {
|
||||
name: "AnimalType",
|
||||
namePlural: "AnimalTypes",
|
||||
type: () =>
|
||||
Object({
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"AnimalTypeTranslation",
|
||||
"AnimalType",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The animal type’s name.",
|
||||
type: String({ minLength: 1 }),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
|
||||
+5
-5
@@ -1,14 +1,14 @@
|
||||
import { Entity, Object, Optional, Required, String } from "tsondb/schema/def"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
|
||||
export const Aspect = Entity(import.meta.url, {
|
||||
name: "Aspect",
|
||||
namePlural: "Aspects",
|
||||
type: () =>
|
||||
Object({
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"AspectTranslation",
|
||||
"Aspect",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The aspect’s name.",
|
||||
@@ -16,12 +16,12 @@ export const Aspect = Entity(import.meta.url, {
|
||||
}),
|
||||
master_of_aspect_suffix: Optional({
|
||||
comment:
|
||||
"The aspect’s name appended to the simple name (not `name_in_library`) of the special ability *Master of (Aspect)*.",
|
||||
"In some languages, the aspect’s grammatical gender influences the full name of a *Master of (Aspect)* instance. To make this possible, the *name* (**not** the *name in library*) is joined with what is contained in this field.",
|
||||
type: String({
|
||||
minLength: 1,
|
||||
}),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Entity, Object, Required, String } from "tsondb/schema/def"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
|
||||
export const Attribute = Entity(import.meta.url, {
|
||||
name: "Attribute",
|
||||
@@ -7,9 +7,9 @@ export const Attribute = Entity(import.meta.url, {
|
||||
comment: "A base attribute of a character.",
|
||||
type: () =>
|
||||
Object({
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"AttributeTranslation",
|
||||
"Attribute",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The attribute’s name.",
|
||||
@@ -23,7 +23,7 @@ export const Attribute = Entity(import.meta.url, {
|
||||
comment: "The description of the attribute.",
|
||||
type: String({ minLength: 1 }),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
|
||||
@@ -11,8 +11,9 @@ import {
|
||||
TypeAlias,
|
||||
} from "tsondb/schema/def"
|
||||
import { DurationUnit } from "./_ActivatableSkillDuration.js"
|
||||
import { FixedRange } from "./_ActivatableSkillRange.js"
|
||||
import { AffectedTargetCategories } from "./_ActivatableSkillTargetCategory.js"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
import { Errata } from "./source/_Erratum.js"
|
||||
import { src } from "./source/_PublicationRef.js"
|
||||
|
||||
@@ -30,9 +31,9 @@ export const Blessing = Entity(import.meta.url, {
|
||||
type: IncludeIdentifier(AffectedTargetCategories),
|
||||
}),
|
||||
src,
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"BlessingTranslation",
|
||||
"Blessing",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The blessing’s name.",
|
||||
@@ -57,7 +58,7 @@ export const Blessing = Entity(import.meta.url, {
|
||||
errata: Optional({
|
||||
type: IncludeIdentifier(Errata),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
@@ -78,7 +79,7 @@ const BlessingRange = Enum(import.meta.url, {
|
||||
values: () => ({
|
||||
Self: EnumCase({ type: null }),
|
||||
Touch: EnumCase({ type: null }),
|
||||
Fixed: EnumCase({ type: null }),
|
||||
Fixed: EnumCase({ type: IncludeIdentifier(FixedRange) }),
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -110,15 +111,15 @@ const IndefiniteBlessingDuration = TypeAlias(import.meta.url, {
|
||||
name: "IndefiniteBlessingDuration",
|
||||
type: () =>
|
||||
Object({
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"IndefiniteBlessingDurationTranslation",
|
||||
"IndefiniteBlessingDuration",
|
||||
Object({
|
||||
description: Required({
|
||||
comment: "A description of the duration.",
|
||||
type: String({ minLength: 1 }),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
})
|
||||
|
||||
+11
-11
@@ -22,7 +22,7 @@ import {
|
||||
PropertyIdentifier,
|
||||
} from "./_Identifier.js"
|
||||
import { ResponsiveText } from "./_ResponsiveText.js"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
import { Errata } from "./source/_Erratum.js"
|
||||
import { src } from "./source/_PublicationRef.js"
|
||||
|
||||
@@ -49,9 +49,9 @@ export const Cantrip = Entity(import.meta.url, {
|
||||
type: IncludeIdentifier(CantripNote),
|
||||
}),
|
||||
src,
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"CantripTranslation",
|
||||
"Cantrip",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The cantrip’s name.",
|
||||
@@ -76,7 +76,7 @@ export const Cantrip = Entity(import.meta.url, {
|
||||
errata: Optional({
|
||||
type: IncludeIdentifier(Errata),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
@@ -130,15 +130,15 @@ const CommonCantripTraditionNote = TypeAlias(import.meta.url, {
|
||||
comment: "The magical tradition’s identifier.",
|
||||
type: MagicalTraditionIdentifier(),
|
||||
}),
|
||||
translations: NestedLocaleMap(
|
||||
Required,
|
||||
"CommonCantripTraditionNoteTranslation",
|
||||
translations: NestedTranslationMap(
|
||||
Optional,
|
||||
"CommonCantripTraditionNote",
|
||||
Object({
|
||||
note: Required({
|
||||
comment: "A note, appended to the generated string in parenthesis.",
|
||||
type: String({ minLength: 1 }),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
})
|
||||
@@ -195,15 +195,15 @@ const IndefiniteCantripDuration = TypeAlias(import.meta.url, {
|
||||
name: "IndefiniteCantripDuration",
|
||||
type: () =>
|
||||
Object({
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"IndefiniteCantripDurationTranslation",
|
||||
"IndefiniteCantripDuration",
|
||||
Object({
|
||||
description: Required({
|
||||
comment: "A description of the duration.",
|
||||
type: IncludeIdentifier(ResponsiveText),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -16,7 +16,7 @@ import { Enhancement } from "./_Enhancements.js"
|
||||
import { ImprovementCost } from "./_ImprovementCost.js"
|
||||
import { LiturgyPrerequisites } from "./_Prerequisite.js"
|
||||
import { SkillCheck, SkillCheckPenalty } from "./_SkillCheck.js"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
import { Errata } from "./source/_Erratum.js"
|
||||
import { src } from "./source/_PublicationRef.js"
|
||||
|
||||
@@ -55,9 +55,9 @@ export const Ceremony = Entity(import.meta.url, {
|
||||
type: IncludeIdentifier(LiturgyPrerequisites),
|
||||
}),
|
||||
src,
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"LiturgicalChantTranslation",
|
||||
"Ceremony",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The ceremony’s name.",
|
||||
@@ -96,7 +96,7 @@ export const Ceremony = Entity(import.meta.url, {
|
||||
errata: Optional({
|
||||
type: IncludeIdentifier(Errata),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
enhancements: Required({
|
||||
type: ChildEntities(Enhancement),
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
} from "tsondb/schema/def"
|
||||
import { AttributeIdentifier } from "./_Identifier.js"
|
||||
import { ImprovementCost } from "./_ImprovementCost.js"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
import { Errata } from "./source/_Erratum.js"
|
||||
import { src } from "./source/_PublicationRef.js"
|
||||
|
||||
@@ -39,9 +39,9 @@ export const CloseCombatTechnique = Entity(import.meta.url, {
|
||||
type: IncludeIdentifier(ImprovementCost),
|
||||
}),
|
||||
src,
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"CloseCombatTechniqueTranslation",
|
||||
"CloseCombatTechnique",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The combat technique’s name.",
|
||||
@@ -54,7 +54,7 @@ export const CloseCombatTechnique = Entity(import.meta.url, {
|
||||
errata: Optional({
|
||||
type: IncludeIdentifier(Errata),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
@@ -99,9 +99,9 @@ export const RangedCombatTechnique = Entity(import.meta.url, {
|
||||
type: IncludeIdentifier(ImprovementCost),
|
||||
}),
|
||||
src,
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"RangedCombatTechniqueTranslation",
|
||||
"RangedCombatTechnique",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The combat technique’s name.",
|
||||
@@ -114,7 +114,7 @@ export const RangedCombatTechnique = Entity(import.meta.url, {
|
||||
errata: Optional({
|
||||
type: IncludeIdentifier(Errata),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
Required,
|
||||
String,
|
||||
} from "tsondb/schema/def"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
import { Errata } from "./source/_Erratum.js"
|
||||
import { src } from "./source/_PublicationRef.js"
|
||||
|
||||
@@ -17,9 +17,9 @@ export const Condition = Entity(import.meta.url, {
|
||||
type: () =>
|
||||
Object({
|
||||
src,
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"ConditionTranslation",
|
||||
"Condition",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The condition’s name.",
|
||||
@@ -39,7 +39,7 @@ export const Condition = Entity(import.meta.url, {
|
||||
errata: Optional({
|
||||
type: IncludeIdentifier(Errata),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Entity, Object, Required, String } from "tsondb/schema/def"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
|
||||
export const Continent = Entity(import.meta.url, {
|
||||
name: "Continent",
|
||||
@@ -8,15 +8,15 @@ export const Continent = Entity(import.meta.url, {
|
||||
"Continents are mostly referenced to in languages and scripts that occur on a specific continent.",
|
||||
type: () =>
|
||||
Object({
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"ContinentTranslation",
|
||||
"Continent",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The continent name.",
|
||||
type: String({ minLength: 1 }),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
|
||||
+12
-12
@@ -30,7 +30,7 @@ import {
|
||||
SocialStatusIdentifier,
|
||||
} from "./_Identifier.js"
|
||||
import { BinarySex } from "./_Sex.js"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
import { MundaneProfessionGroup } from "./Profession.js"
|
||||
import { Errata } from "./source/_Erratum.js"
|
||||
import { src } from "./source/_PublicationRef.js"
|
||||
@@ -66,28 +66,28 @@ export const Culture = Entity(import.meta.url, {
|
||||
comment: "A list of common advantages.",
|
||||
type: Array(
|
||||
GenIncludeIdentifier(CommonnessRatedAdvantageDisadvantage, [AdvantageIdentifier()]),
|
||||
{ minItems: 1 }
|
||||
{ minItems: 1 },
|
||||
),
|
||||
}),
|
||||
common_disadvantages: Optional({
|
||||
comment: "A list of common disadvantages.",
|
||||
type: Array(
|
||||
GenIncludeIdentifier(CommonnessRatedAdvantageDisadvantage, [DisadvantageIdentifier()]),
|
||||
{ minItems: 1 }
|
||||
{ minItems: 1 },
|
||||
),
|
||||
}),
|
||||
uncommon_advantages: Optional({
|
||||
comment: "A list of uncommon advantages.",
|
||||
type: Array(
|
||||
GenIncludeIdentifier(CommonnessRatedAdvantageDisadvantage, [AdvantageIdentifier()]),
|
||||
{ minItems: 1 }
|
||||
{ minItems: 1 },
|
||||
),
|
||||
}),
|
||||
uncommon_disadvantages: Optional({
|
||||
comment: "A list of uncommon disadvantages.",
|
||||
type: Array(
|
||||
GenIncludeIdentifier(CommonnessRatedAdvantageDisadvantage, [DisadvantageIdentifier()]),
|
||||
{ minItems: 1 }
|
||||
{ minItems: 1 },
|
||||
),
|
||||
}),
|
||||
common_skills: Required({
|
||||
@@ -103,9 +103,9 @@ export const Culture = Entity(import.meta.url, {
|
||||
type: Array(IncludeIdentifier(CulturalPackageItem), { minItems: 1 }),
|
||||
}),
|
||||
src,
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"CultureTranslation",
|
||||
"Culture",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The race’s name.",
|
||||
@@ -138,7 +138,7 @@ export const Culture = Entity(import.meta.url, {
|
||||
errata: Optional({
|
||||
type: IncludeIdentifier(Errata),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
@@ -333,7 +333,7 @@ const GroupedCommonProfessions = TypeAlias(import.meta.url, {
|
||||
]),
|
||||
}),
|
||||
},
|
||||
{ minProperties: 1 }
|
||||
{ minProperties: 1 },
|
||||
),
|
||||
})
|
||||
|
||||
@@ -420,7 +420,7 @@ const CommonNames = TypeAlias(import.meta.url, {
|
||||
type: String({ minLength: 1 }),
|
||||
}),
|
||||
},
|
||||
{ minProperties: 1 }
|
||||
{ minProperties: 1 },
|
||||
),
|
||||
})
|
||||
|
||||
@@ -442,7 +442,7 @@ const CommonNameGroup = TypeAlias(import.meta.url, {
|
||||
type: Array(IncludeIdentifier(CommonName), { minItems: 1 }),
|
||||
}),
|
||||
},
|
||||
{ minProperties: 1 }
|
||||
{ minProperties: 1 },
|
||||
),
|
||||
})
|
||||
|
||||
@@ -459,6 +459,6 @@ const CommonName = TypeAlias(import.meta.url, {
|
||||
type: String({ minLength: 1 }),
|
||||
}),
|
||||
},
|
||||
{ minProperties: 1 }
|
||||
{ minProperties: 1 },
|
||||
),
|
||||
})
|
||||
|
||||
@@ -7,7 +7,7 @@ import {
|
||||
String,
|
||||
TypeAlias,
|
||||
} from "tsondb/schema/def"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
import { DerivedCharacteristicPrerequisites } from "./_Prerequisite.js"
|
||||
import { src } from "./source/_PublicationRef.js"
|
||||
|
||||
@@ -20,9 +20,9 @@ export const DerivedCharacteristic = Entity(import.meta.url, {
|
||||
type: IncludeIdentifier(DerivedCharacteristicPrerequisites),
|
||||
}),
|
||||
src,
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"DerivedCharacteristicTranslation",
|
||||
"DerivedCharacteristic",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The derived characteristic’s name.",
|
||||
@@ -36,7 +36,7 @@ export const DerivedCharacteristic = Entity(import.meta.url, {
|
||||
comment: "Possible calculation strings for the final value.",
|
||||
type: IncludeIdentifier(CalculationTranslation),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
|
||||
@@ -7,22 +7,12 @@ import {
|
||||
Required,
|
||||
String,
|
||||
} from "tsondb/schema/def"
|
||||
import {
|
||||
ap_value,
|
||||
ap_value_append,
|
||||
ap_value_l10n,
|
||||
input,
|
||||
levels,
|
||||
maximum,
|
||||
name,
|
||||
name_in_library,
|
||||
rules,
|
||||
select_options,
|
||||
skill_applications,
|
||||
skill_uses,
|
||||
} from "./_Activatable.js"
|
||||
import { input, levels, maximum, name, name_in_library, rules } from "./_Activatable.js"
|
||||
import { ap_value, ap_value_append, ap_value_l10n } from "./_ActivatableAdventurePointsValue.js"
|
||||
import { explicit_select_options, select_options } from "./_ActivatableSelectOptions.js"
|
||||
import { skill_applications, skill_uses } from "./_ActivatableSkillApplicationsAndUses.js"
|
||||
import { AdvantageDisadvantagePrerequisites } from "./_Prerequisite.js"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
import { Errata } from "./source/_Erratum.js"
|
||||
import { src } from "./source/_PublicationRef.js"
|
||||
|
||||
@@ -33,6 +23,7 @@ export const Disadvantage = Entity(import.meta.url, {
|
||||
Object({
|
||||
levels,
|
||||
select_options,
|
||||
explicit_select_options,
|
||||
skill_applications,
|
||||
skill_uses,
|
||||
maximum,
|
||||
@@ -50,9 +41,9 @@ export const Disadvantage = Entity(import.meta.url, {
|
||||
type: Boolean(),
|
||||
}),
|
||||
src,
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"DisadvantageTranslation",
|
||||
"Disadvantage",
|
||||
Object({
|
||||
name,
|
||||
name_in_library,
|
||||
@@ -67,7 +58,7 @@ export const Disadvantage = Entity(import.meta.url, {
|
||||
errata: Optional({
|
||||
type: IncludeIdentifier(Errata),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { Entity, Object, Required } from "tsondb/schema/def"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { Entity, IncludeIdentifier, Object, Optional, Required } from "tsondb/schema/def"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
import { cause, DiseaseTranslation, level, resistance } from "./_DiseasePoison.js"
|
||||
import { RecipeTradeSecret } from "./equipment/item/_Herbary.js"
|
||||
import { src } from "./source/_PublicationRef.js"
|
||||
|
||||
export const Disease = Entity(import.meta.url, {
|
||||
@@ -11,8 +12,12 @@ export const Disease = Entity(import.meta.url, {
|
||||
level,
|
||||
resistance,
|
||||
cause,
|
||||
trade_secret: Optional({
|
||||
comment: "AP value and prerequisites of the disease’s trade secret.",
|
||||
type: IncludeIdentifier(RecipeTradeSecret),
|
||||
}),
|
||||
src,
|
||||
translations: NestedLocaleMap(Required, "DiseaseTranslation", DiseaseTranslation),
|
||||
translations: NestedTranslationMap(Required, "Disease", DiseaseTranslation),
|
||||
}),
|
||||
displayName: {},
|
||||
})
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import { Entity, Object, Required, String } from "tsondb/schema/def"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
|
||||
export const Element = Entity(import.meta.url, {
|
||||
name: "Element",
|
||||
namePlural: "Elements",
|
||||
type: () =>
|
||||
Object({
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"ElementTranslation",
|
||||
"Element",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The element’s name.",
|
||||
type: String({ minLength: 1 }),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Entity, Integer, Object, Required, String } from "tsondb/schema/def"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
|
||||
export const ExperienceLevel = Entity(import.meta.url, {
|
||||
name: "ExperienceLevel",
|
||||
@@ -35,15 +35,15 @@ export const ExperienceLevel = Entity(import.meta.url, {
|
||||
comment: "The maximum of spells of an unfamiliar tradition you can activate.",
|
||||
type: Integer(),
|
||||
}),
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"ExperienceLevelTranslation",
|
||||
"ExperienceLevel",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The experience level’s name.",
|
||||
type: String({ minLength: 1 }),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import { Entity, Object, Required, String } from "tsondb/schema/def"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
|
||||
export const EyeColor = Entity(import.meta.url, {
|
||||
name: "EyeColor",
|
||||
namePlural: "EyeColors",
|
||||
type: () =>
|
||||
Object({
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"EyeColorTranslation",
|
||||
"EyeColor",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The eye color’s name.",
|
||||
type: String({ minLength: 1 }),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
|
||||
+11
-11
@@ -20,7 +20,7 @@ import {
|
||||
} from "./_ActivatableSkillDuration.js"
|
||||
import { AnimalTypeIdentifier, PropertyIdentifier } from "./_Identifier.js"
|
||||
import { ResponsiveText, ResponsiveTextOptional } from "./_ResponsiveText.js"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
import { Errata } from "./source/_Erratum.js"
|
||||
import { src } from "./source/_PublicationRef.js"
|
||||
|
||||
@@ -49,9 +49,9 @@ If no animal types are given, the animal disease applies to all animal types.`,
|
||||
type: Integer({ minimum: 1 }),
|
||||
}),
|
||||
src,
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"FamiliarsTrickTranslation",
|
||||
"FamiliarsTrick",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The familiar’s trick’s name.",
|
||||
@@ -72,7 +72,7 @@ If no animal types are given, the animal disease applies to all animal types.`,
|
||||
errata: Optional({
|
||||
type: IncludeIdentifier(Errata),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
@@ -90,15 +90,15 @@ const IndefiniteFamiliarsTrickProperty = TypeAlias(import.meta.url, {
|
||||
name: "IndefiniteFamiliarsTrickProperty",
|
||||
type: () =>
|
||||
Object({
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Optional,
|
||||
"IndefiniteFamiliarsTrickPropertyTranslation",
|
||||
"IndefiniteFamiliarsTrickProperty",
|
||||
Object({
|
||||
description: Required({
|
||||
comment: "A description of the property.",
|
||||
type: IncludeIdentifier(ResponsiveText),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
})
|
||||
@@ -153,9 +153,9 @@ const FamiliarsTrickFixedOneTimeCost = TypeAlias(import.meta.url, {
|
||||
comment: "The interval in which you have to pay the AE cost again.",
|
||||
type: IncludeIdentifier(DurationUnitValue),
|
||||
}),
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Optional,
|
||||
"FamiliarsTrickFixedOneTimeCostTranslation",
|
||||
"FamiliarsTrickFixedOneTimeCost",
|
||||
Object(
|
||||
{
|
||||
per: Optional({
|
||||
@@ -164,8 +164,8 @@ const FamiliarsTrickFixedOneTimeCost = TypeAlias(import.meta.url, {
|
||||
type: IncludeIdentifier(ResponsiveTextOptional),
|
||||
}),
|
||||
},
|
||||
{ minProperties: 1 }
|
||||
)
|
||||
{ minProperties: 1 },
|
||||
),
|
||||
),
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -1,20 +1,20 @@
|
||||
import { Entity, Object, Required, String } from "tsondb/schema/def"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
|
||||
export const HairColor = Entity(import.meta.url, {
|
||||
name: "HairColor",
|
||||
namePlural: "HairColors",
|
||||
type: () =>
|
||||
Object({
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"HairColorTranslation",
|
||||
"HairColor",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The hair color’s name.",
|
||||
type: String({ minLength: 1 }),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
|
||||
+11
-11
@@ -12,7 +12,7 @@ import {
|
||||
String,
|
||||
TypeAlias,
|
||||
} from "tsondb/schema/def"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
import {
|
||||
CurriculumIdentifier,
|
||||
ElementIdentifier,
|
||||
@@ -34,15 +34,15 @@ export const Guideline = Entity(import.meta.url, {
|
||||
comment: "Maximum number of spells that can be exchanged.",
|
||||
type: Integer({ minimum: 0 }),
|
||||
}),
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"GuidelineTranslation",
|
||||
"Guideline",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The guideline’s name.",
|
||||
type: String({ minLength: 1 }),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
@@ -72,9 +72,9 @@ export const Curriculum = Entity(import.meta.url, {
|
||||
type: ChildEntities(LessonPackage),
|
||||
}),
|
||||
src,
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"CurriculumTranslation",
|
||||
"Curriculum",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The institution’s name.",
|
||||
@@ -83,7 +83,7 @@ export const Curriculum = Entity(import.meta.url, {
|
||||
errata: Optional({
|
||||
type: IncludeIdentifier(Errata),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
@@ -187,9 +187,9 @@ export const LessonPackage = Entity(import.meta.url, {
|
||||
skills: Optional({
|
||||
type: Array(IncludeIdentifier(AbilityAdjustment), { minItems: 1, uniqueItems: true }),
|
||||
}),
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"LessonPackageTranslation",
|
||||
"LessonPackage",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The lesson package’s name.",
|
||||
@@ -200,10 +200,10 @@ export const LessonPackage = Entity(import.meta.url, {
|
||||
"The spell values difference of the lesson package. Use this field to specify a text that is displayed instead of the generated `spellwork_changes` list. The field is displayed even if no list is present.",
|
||||
type: String({ minLength: 1 }),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
},
|
||||
{ minProperties: 3 }
|
||||
{ minProperties: 3 },
|
||||
),
|
||||
parentReferenceKey: "curriculum",
|
||||
displayName: {},
|
||||
|
||||
@@ -16,7 +16,7 @@ import { Enhancement } from "./_Enhancements.js"
|
||||
import { ImprovementCost } from "./_ImprovementCost.js"
|
||||
import { LiturgyPrerequisites } from "./_Prerequisite.js"
|
||||
import { SkillCheck, SkillCheckPenalty } from "./_SkillCheck.js"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
import { Errata } from "./source/_Erratum.js"
|
||||
import { src } from "./source/_PublicationRef.js"
|
||||
|
||||
@@ -55,9 +55,9 @@ export const LiturgicalChant = Entity(import.meta.url, {
|
||||
type: IncludeIdentifier(LiturgyPrerequisites),
|
||||
}),
|
||||
src,
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"LiturgicalChantTranslation",
|
||||
"LiturgicalChant",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The liturgical chant’s name.",
|
||||
@@ -96,7 +96,7 @@ export const LiturgicalChant = Entity(import.meta.url, {
|
||||
errata: Optional({
|
||||
type: IncludeIdentifier(Errata),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
enhancements: Required({
|
||||
type: ChildEntities(Enhancement),
|
||||
|
||||
+1340
-1298
File diff suppressed because it is too large
Load Diff
@@ -7,7 +7,7 @@ import {
|
||||
Required,
|
||||
String,
|
||||
} from "tsondb/schema/def"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
import { Errata } from "./source/_Erratum.js"
|
||||
import { src } from "./source/_PublicationRef.js"
|
||||
|
||||
@@ -19,9 +19,9 @@ export const MetaCondition = Entity(import.meta.url, {
|
||||
type: () =>
|
||||
Object({
|
||||
src,
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"MetaConditionTranslation",
|
||||
"MetaCondition",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The meta condition’s name.",
|
||||
@@ -41,7 +41,7 @@ export const MetaCondition = Entity(import.meta.url, {
|
||||
errata: Optional({
|
||||
type: IncludeIdentifier(Errata),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
|
||||
+10
-10
@@ -8,7 +8,7 @@ import {
|
||||
String,
|
||||
} from "tsondb/schema/def"
|
||||
import { PactCategoryIdentifier } from "./_Identifier.js"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
import { Errata } from "./source/_Erratum.js"
|
||||
import { src } from "./source/_PublicationRef.js"
|
||||
|
||||
@@ -26,9 +26,9 @@ export const PactCategory = Entity(import.meta.url, {
|
||||
type: ChildEntities(PactDomain),
|
||||
}),
|
||||
src,
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"PactCategoryTranslation",
|
||||
"PactCategory",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The pact category’s name.",
|
||||
@@ -37,7 +37,7 @@ export const PactCategory = Entity(import.meta.url, {
|
||||
errata: Optional({
|
||||
type: IncludeIdentifier(Errata),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
@@ -52,15 +52,15 @@ export const PactType = Entity(import.meta.url, {
|
||||
comment: "The pact category this type belongs to.",
|
||||
type: PactCategoryIdentifier(),
|
||||
}),
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"PactTypeTranslation",
|
||||
"PactType",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The type’s name.",
|
||||
type: String({ minLength: 1 }),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
parentReferenceKey: "parent",
|
||||
@@ -76,15 +76,15 @@ export const PactDomain = Entity(import.meta.url, {
|
||||
comment: "The pact category this domain belongs to.",
|
||||
type: PactCategoryIdentifier(),
|
||||
}),
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"PactDomainTranslation",
|
||||
"PactDomain",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The domain’s name.",
|
||||
type: String({ minLength: 1 }),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
parentReferenceKey: "parent",
|
||||
|
||||
+6
-8
@@ -14,14 +14,14 @@ import {
|
||||
import {
|
||||
AdvantageIdentifier,
|
||||
AttributeIdentifier,
|
||||
CultureIdentifier,
|
||||
DisadvantageIdentifier,
|
||||
PatronCategoryIdentifier,
|
||||
SkillIdentifier,
|
||||
TribeIdentifier,
|
||||
} from "./_Identifier.js"
|
||||
import { SpellworkIdentifier } from "./_IdentifierGroup.js"
|
||||
import { ImprovementCost } from "./_ImprovementCost.js"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
import { src } from "./source/_PublicationRef.js"
|
||||
|
||||
export const Patron = Entity(import.meta.url, {
|
||||
@@ -75,15 +75,15 @@ export const Patron = Entity(import.meta.url, {
|
||||
type: Array(IncludeIdentifier(SpellworkIdentifier), { minItems: 1, uniqueItems: true }),
|
||||
}),
|
||||
src,
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"PatronTranslation",
|
||||
"Patron",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The patron’s name.",
|
||||
type: String({ minLength: 1 }),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
@@ -96,11 +96,9 @@ const PatronCulture = TypeAlias(import.meta.url, {
|
||||
type: () =>
|
||||
Object({
|
||||
set: Required({
|
||||
comment: "The AE cost value.",
|
||||
type: Array(CultureIdentifier(), { uniqueItems: true }),
|
||||
type: Array(TribeIdentifier(), { uniqueItems: true }),
|
||||
}),
|
||||
operation: Optional({
|
||||
comment: "The interval in which you have to pay the AE cost again.",
|
||||
type: IncludeIdentifier(PatronCultureOperation),
|
||||
}),
|
||||
}),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Array, Entity, Object, Required, String } from "tsondb/schema/def"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
import { CultureIdentifier } from "./_Identifier.js"
|
||||
|
||||
export const PatronCategory = Entity(import.meta.url, {
|
||||
@@ -12,15 +12,15 @@ export const PatronCategory = Entity(import.meta.url, {
|
||||
"The list of cultures where patrons from this category can be the primary patron of.",
|
||||
type: Array(CultureIdentifier(), { minItems: 1, uniqueItems: true }),
|
||||
}),
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"PatronCategoryTranslation",
|
||||
"PatronCategory",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The patron category’s name.",
|
||||
type: String({ minLength: 1 }),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
String,
|
||||
TypeAlias,
|
||||
} from "tsondb/schema/def"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
import { PersonalityTraitIdentifier } from "./_Identifier.js"
|
||||
import { PersonalityTraitPrerequisites } from "./_Prerequisite.js"
|
||||
import { Errata } from "./source/_Erratum.js"
|
||||
@@ -34,9 +34,9 @@ export const PersonalityTrait = Entity(import.meta.url, {
|
||||
type: Array(PersonalityTraitIdentifier(), { minItems: 1, uniqueItems: true }),
|
||||
}),
|
||||
src,
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"PersonalityTraitTranslation",
|
||||
"PersonalityTrait",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The personality trait’s name.",
|
||||
@@ -50,7 +50,7 @@ export const PersonalityTrait = Entity(import.meta.url, {
|
||||
errata: Optional({
|
||||
type: IncludeIdentifier(Errata),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
|
||||
+29
-31
@@ -41,7 +41,7 @@ import {
|
||||
SpellworkIdentifier,
|
||||
} from "./_IdentifierGroup.js"
|
||||
import { ProfessionPrerequisites } from "./_Prerequisite.js"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
import { Errata } from "./source/_Erratum.js"
|
||||
import { src } from "./source/_PublicationRef.js"
|
||||
|
||||
@@ -70,7 +70,7 @@ export const Profession = Entity(import.meta.url, {
|
||||
const [firstProfessionVersion, ...otherProfessionVersions] = getChildInstancesForInstanceId(
|
||||
Profession.name,
|
||||
instanceId,
|
||||
ProfessionVersion.name
|
||||
ProfessionVersion.name,
|
||||
)
|
||||
|
||||
if (!firstProfessionVersion) {
|
||||
@@ -87,7 +87,7 @@ export const Profession = Entity(import.meta.url, {
|
||||
|
||||
return {
|
||||
...displayName,
|
||||
name: `${displayName.name} (+${otherProfessionVersions.length} version${
|
||||
name: `${displayName.name} (+${otherProfessionVersions.length.toString()} version${
|
||||
otherProfessionVersions.length > 1 ? "s" : ""
|
||||
})`,
|
||||
}
|
||||
@@ -132,11 +132,6 @@ export const ProfessionVersion = Entity(import.meta.url, {
|
||||
comment: "The associated profession.",
|
||||
type: ProfessionIdentifier(),
|
||||
}),
|
||||
prerequisites: Optional({
|
||||
comment:
|
||||
"Which prerequisites must be met to buy the stat block? For example, a character might need the advantage Spellcaster or Blessed. Note: the AP cost for a profession package does not include these prerequisites.",
|
||||
type: IncludeIdentifier(ProfessionPrerequisites),
|
||||
}),
|
||||
package: Required({
|
||||
comment:
|
||||
"A list of available race variants where one has to be selected. If no variants are to be selected, a single variant with no name has to be provided which will be used as the missing values for the base race.",
|
||||
@@ -146,14 +141,14 @@ export const ProfessionVersion = Entity(import.meta.url, {
|
||||
comment: "A list of typical advantages.",
|
||||
type: Array(
|
||||
GenIncludeIdentifier(CommonnessRatedAdvantageDisadvantage, [AdvantageIdentifier()]),
|
||||
{ minItems: 1 }
|
||||
{ minItems: 1 },
|
||||
),
|
||||
}),
|
||||
suggested_disadvantages: Optional({
|
||||
comment: "A list of typical disadvantages.",
|
||||
type: Array(
|
||||
GenIncludeIdentifier(CommonnessRatedAdvantageDisadvantage, [DisadvantageIdentifier()]),
|
||||
{ minItems: 1 }
|
||||
{ minItems: 1 },
|
||||
),
|
||||
}),
|
||||
unsuitable_advantages: Optional({
|
||||
@@ -161,7 +156,7 @@ export const ProfessionVersion = Entity(import.meta.url, {
|
||||
"A list of advantages that do not fit well with this profession; to be checked with the GM before taking any of them.",
|
||||
type: Array(
|
||||
GenIncludeIdentifier(CommonnessRatedAdvantageDisadvantage, [AdvantageIdentifier()]),
|
||||
{ minItems: 1 }
|
||||
{ minItems: 1 },
|
||||
),
|
||||
}),
|
||||
unsuitable_disadvantages: Optional({
|
||||
@@ -169,13 +164,13 @@ export const ProfessionVersion = Entity(import.meta.url, {
|
||||
"A list of disadvantages that do not fit well with this profession; to be checked with the GM before taking any of them.",
|
||||
type: Array(
|
||||
GenIncludeIdentifier(CommonnessRatedAdvantageDisadvantage, [DisadvantageIdentifier()]),
|
||||
{ minItems: 1 }
|
||||
{ minItems: 1 },
|
||||
),
|
||||
}),
|
||||
src,
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"ProfessionVersionTranslation",
|
||||
"ProfessionVersion",
|
||||
ObjectType({
|
||||
name: Required({
|
||||
comment: "The basic profession’s name.",
|
||||
@@ -207,7 +202,7 @@ export const ProfessionVersion = Entity(import.meta.url, {
|
||||
errata: Optional({
|
||||
type: IncludeIdentifier(Errata),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
parentReferenceKey: "profession",
|
||||
@@ -216,17 +211,15 @@ export const ProfessionVersion = Entity(import.meta.url, {
|
||||
pathInLocaleMap: "name.default",
|
||||
},
|
||||
displayNameCustomizer: ({ instance, instanceId, locales }) => {
|
||||
if (typeof instance.translations === "object" && instance.translations !== null) {
|
||||
const translations = Object.entries(instance.translations)
|
||||
for (const locale of locales) {
|
||||
const translation = translations.find(([key]) => key === locale)
|
||||
if (translation) {
|
||||
const [, value] = translation
|
||||
return {
|
||||
name:
|
||||
value.name.default + (value.specification ? ` (${value.specification.default})` : ""),
|
||||
localeId: locale,
|
||||
}
|
||||
const translations = Object.entries(instance.translations)
|
||||
for (const locale of locales) {
|
||||
const translation = translations.find(([key]) => key === locale)
|
||||
if (translation) {
|
||||
const [, value] = translation
|
||||
return {
|
||||
name:
|
||||
value.name.default + (value.specification ? ` (${value.specification.default})` : ""),
|
||||
localeId: locale,
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -255,6 +248,11 @@ export const ProfessionPackage = Entity(import.meta.url, {
|
||||
comment: "What does the professional package cost in adventure points?",
|
||||
type: Integer({ minimum: 0 }),
|
||||
}),
|
||||
prerequisites: Optional({
|
||||
comment:
|
||||
"Which prerequisites must be met to buy the stat block? For example, a character might need the advantage Spellcaster or Blessed. Note: the AP cost for a profession package does not include these prerequisites.",
|
||||
type: IncludeIdentifier(ProfessionPrerequisites),
|
||||
}),
|
||||
options: Optional({
|
||||
comment:
|
||||
"In some areas, the profession package grants a loose set of stats where the player must choose between different options for the profession package.",
|
||||
@@ -361,9 +359,9 @@ export const ProfessionVariant = Entity(import.meta.url, {
|
||||
"Clerical profession variants receive these liturgical chants at the listed skill ratings. These liturgical chants are considered activated.",
|
||||
type: Array(IncludeIdentifier(LiturgicalChantRating), { minItems: 1 }),
|
||||
}),
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"ProfessionVariantTranslation",
|
||||
"ProfessionVariant",
|
||||
ObjectType({
|
||||
name: Required({
|
||||
comment: "The profession variant’s name.",
|
||||
@@ -377,7 +375,7 @@ export const ProfessionVariant = Entity(import.meta.url, {
|
||||
comment: `A text that is appended to the generated text for the profession variant.`,
|
||||
type: String({ minLength: 1 }),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
parentReferenceKey: "profession_package",
|
||||
@@ -581,7 +579,7 @@ const ProfessionPackageOptions = TypeAlias(import.meta.url, {
|
||||
type: IncludeIdentifier(SkillsOptions),
|
||||
}),
|
||||
},
|
||||
{ minProperties: 1 }
|
||||
{ minProperties: 1 },
|
||||
),
|
||||
})
|
||||
|
||||
@@ -622,7 +620,7 @@ const ProfessionVariantPackageOptions = TypeAlias(import.meta.url, {
|
||||
type: GenIncludeIdentifier(VariantOptionAction, [IncludeIdentifier(SkillsOptions)]),
|
||||
}),
|
||||
},
|
||||
{ minProperties: 1 }
|
||||
{ minProperties: 1 },
|
||||
),
|
||||
})
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Entity, IncludeIdentifier, Object, Optional, Required, String } from "tsondb/schema/def"
|
||||
import { SkillCheck } from "./_SkillCheck.js"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
|
||||
export const Property = Entity(import.meta.url, {
|
||||
name: "Property",
|
||||
@@ -12,15 +12,15 @@ export const Property = Entity(import.meta.url, {
|
||||
"The property check’s attributes. Only the properties from the Core Rules have defined property checks.",
|
||||
type: IncludeIdentifier(SkillCheck),
|
||||
}),
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"PropertyTranslation",
|
||||
"Property",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The property’s name.",
|
||||
type: String({ minLength: 1 }),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
|
||||
+16
-14
@@ -26,7 +26,7 @@ import {
|
||||
RaceIdentifier,
|
||||
} from "./_Identifier.js"
|
||||
import { ExperienceLevel } from "./ExperienceLevel.js"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
import { Errata } from "./source/_Erratum.js"
|
||||
import { src } from "./source/_PublicationRef.js"
|
||||
|
||||
@@ -57,14 +57,14 @@ export const Race = Entity(import.meta.url, {
|
||||
comment: "A list of strongly recommended advantages.",
|
||||
type: Array(
|
||||
GenIncludeIdentifier(CommonnessRatedAdvantageDisadvantage, [AdvantageIdentifier()]),
|
||||
{ minItems: 1 }
|
||||
{ minItems: 1 },
|
||||
),
|
||||
}),
|
||||
strongly_recommended_disadvantages: Optional({
|
||||
comment: "A list of strongly recommended disadvantages.",
|
||||
type: Array(
|
||||
GenIncludeIdentifier(CommonnessRatedAdvantageDisadvantage, [DisadvantageIdentifier()]),
|
||||
{ minItems: 1 }
|
||||
{ minItems: 1 },
|
||||
),
|
||||
}),
|
||||
weight: Required({
|
||||
@@ -76,6 +76,7 @@ export const Race = Entity(import.meta.url, {
|
||||
"Defines the starting ages for the race. It depends on the selected experience level.",
|
||||
type: NestedEntityMap({
|
||||
name: "StartingAgeConfigForExperienceLevel",
|
||||
namePlural: "StartingAgeConfigsForExperienceLevel",
|
||||
secondaryEntity: ExperienceLevel,
|
||||
type: Object({
|
||||
base: Required({
|
||||
@@ -88,6 +89,7 @@ export const Race = Entity(import.meta.url, {
|
||||
type: IncludeIdentifier(Dice),
|
||||
}),
|
||||
}),
|
||||
displayName: null,
|
||||
}),
|
||||
}),
|
||||
variants: Required({
|
||||
@@ -96,9 +98,9 @@ export const Race = Entity(import.meta.url, {
|
||||
type: ChildEntities(RaceVariant),
|
||||
}),
|
||||
src,
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"RaceTranslation",
|
||||
"Race",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The race’s name.",
|
||||
@@ -123,7 +125,7 @@ export const Race = Entity(import.meta.url, {
|
||||
errata: Optional({
|
||||
type: IncludeIdentifier(Errata),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
@@ -172,7 +174,7 @@ The array only permits a single entry because no race specified more than one se
|
||||
}),
|
||||
}),
|
||||
},
|
||||
{ minProperties: 1 }
|
||||
{ minProperties: 1 },
|
||||
),
|
||||
})
|
||||
|
||||
@@ -289,28 +291,28 @@ export const RaceVariant = Entity(import.meta.url, {
|
||||
comment: "A list of common advantages.",
|
||||
type: Array(
|
||||
GenIncludeIdentifier(CommonnessRatedAdvantageDisadvantage, [AdvantageIdentifier()]),
|
||||
{ minItems: 1 }
|
||||
{ minItems: 1 },
|
||||
),
|
||||
}),
|
||||
common_disadvantages: Optional({
|
||||
comment: "A list of common disadvantages.",
|
||||
type: Array(
|
||||
GenIncludeIdentifier(CommonnessRatedAdvantageDisadvantage, [DisadvantageIdentifier()]),
|
||||
{ minItems: 1 }
|
||||
{ minItems: 1 },
|
||||
),
|
||||
}),
|
||||
uncommon_advantages: Optional({
|
||||
comment: "A list of uncommon advantages.",
|
||||
type: Array(
|
||||
GenIncludeIdentifier(CommonnessRatedAdvantageDisadvantage, [AdvantageIdentifier()]),
|
||||
{ minItems: 1 }
|
||||
{ minItems: 1 },
|
||||
),
|
||||
}),
|
||||
uncommon_disadvantages: Optional({
|
||||
comment: "A list of uncommon disadvantages.",
|
||||
type: Array(
|
||||
GenIncludeIdentifier(CommonnessRatedAdvantageDisadvantage, [DisadvantageIdentifier()]),
|
||||
{ minItems: 1 }
|
||||
{ minItems: 1 },
|
||||
),
|
||||
}),
|
||||
hair_color: Optional({
|
||||
@@ -327,9 +329,9 @@ export const RaceVariant = Entity(import.meta.url, {
|
||||
comment: "Configuration for random height generation.",
|
||||
type: IncludeIdentifier(RandomHeightGeneration),
|
||||
}),
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"RaceVariantTranslation",
|
||||
"RaceVariant",
|
||||
Object({
|
||||
name: Required({
|
||||
comment:
|
||||
@@ -352,7 +354,7 @@ export const RaceVariant = Entity(import.meta.url, {
|
||||
comment: "The respective uncommon disadvantages text from the source book.",
|
||||
type: String({ minLength: 1 }),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
parentReferenceKey: "race",
|
||||
|
||||
+4
-4
@@ -1,20 +1,20 @@
|
||||
import { Entity, Object, Required, String } from "tsondb/schema/def"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
|
||||
export const Region = Entity(import.meta.url, {
|
||||
name: "Region",
|
||||
namePlural: "Regions",
|
||||
type: () =>
|
||||
Object({
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"RegionTranslation",
|
||||
"Region",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The region’s name.",
|
||||
type: String({ minLength: 1 }),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
|
||||
+4
-4
@@ -16,7 +16,7 @@ import { ImprovementCost } from "./_ImprovementCost.js"
|
||||
import { SpellworkPrerequisites } from "./_Prerequisite.js"
|
||||
import { SkillCheck, SkillCheckPenalty } from "./_SkillCheck.js"
|
||||
import { SpellworkTraditions } from "./_Spellwork.js"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
import { Errata } from "./source/_Erratum.js"
|
||||
import { src } from "./source/_PublicationRef.js"
|
||||
|
||||
@@ -59,9 +59,9 @@ export const Ritual = Entity(import.meta.url, {
|
||||
type: IncludeIdentifier(SpellworkPrerequisites),
|
||||
}),
|
||||
src,
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"RitualTranslation",
|
||||
"Ritual",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The ritual’s name.",
|
||||
@@ -95,7 +95,7 @@ export const Ritual = Entity(import.meta.url, {
|
||||
errata: Optional({
|
||||
type: IncludeIdentifier(Errata),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
enhancements: Required({
|
||||
type: ChildEntities(Enhancement),
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
Required,
|
||||
String,
|
||||
} from "tsondb/schema/def"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
import { Errata } from "./source/_Erratum.js"
|
||||
import { src } from "./source/_PublicationRef.js"
|
||||
|
||||
@@ -26,9 +26,9 @@ export const Service = Entity(import.meta.url, {
|
||||
}),
|
||||
}),
|
||||
src,
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"ServiceTranslation",
|
||||
"Service",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The service’s name.",
|
||||
@@ -41,7 +41,7 @@ export const Service = Entity(import.meta.url, {
|
||||
errata: Optional({
|
||||
type: IncludeIdentifier(Errata),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Entity, Object, Optional, Required, String } from "tsondb/schema/def"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
import { src } from "./source/_PublicationRef.js"
|
||||
|
||||
export const SexPractice = Entity(import.meta.url, {
|
||||
@@ -8,9 +8,9 @@ export const SexPractice = Entity(import.meta.url, {
|
||||
type: () =>
|
||||
Object({
|
||||
src,
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"StateTranslation",
|
||||
"SexPractice",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The sex practice’s name.",
|
||||
@@ -33,7 +33,7 @@ export const SexPractice = Entity(import.meta.url, {
|
||||
comment: "Effects of a failed *Seduction* check.",
|
||||
type: String({ minLength: 1 }),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
|
||||
+7
-7
@@ -17,7 +17,7 @@ import {
|
||||
import { SkillGroupIdentifier, SkillIdentifier } from "./_Identifier.js"
|
||||
import { ImprovementCost } from "./_ImprovementCost.js"
|
||||
import { SkillCheck } from "./_SkillCheck.js"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
import { Errata } from "./source/_Erratum.js"
|
||||
import { src } from "./source/_PublicationRef.js"
|
||||
|
||||
@@ -48,9 +48,9 @@ export const Skill = Entity(import.meta.url, {
|
||||
type: SkillGroupIdentifier(),
|
||||
}),
|
||||
src,
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"SkillTranslation",
|
||||
"Skill",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The skill’s name.",
|
||||
@@ -89,7 +89,7 @@ export const Skill = Entity(import.meta.url, {
|
||||
errata: Optional({
|
||||
type: IncludeIdentifier(Errata),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
@@ -130,15 +130,15 @@ export const SkillApplication = Entity(import.meta.url, {
|
||||
comment: "The skill this application belongs to.",
|
||||
type: SkillIdentifier(),
|
||||
}),
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"SkillApplicationTranslation",
|
||||
"SkillApplication",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The skill application’s name.",
|
||||
type: String({ minLength: 1 }),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
parentReferenceKey: "parent",
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Entity, IncludeIdentifier, Object, Required, String } from "tsondb/schema/def"
|
||||
import { SkillCheck } from "./_SkillCheck.js"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
|
||||
export const SkillGroup = Entity(import.meta.url, {
|
||||
name: "SkillGroup",
|
||||
@@ -11,9 +11,9 @@ export const SkillGroup = Entity(import.meta.url, {
|
||||
comment: "The skill group’s skill check attributes",
|
||||
type: IncludeIdentifier(SkillCheck),
|
||||
}),
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"SkillGroupTranslation",
|
||||
"SkillGroup",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The skill group’s name.",
|
||||
@@ -23,7 +23,7 @@ export const SkillGroup = Entity(import.meta.url, {
|
||||
comment: "The skill group’s long name.",
|
||||
type: String({ minLength: 1 }),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
|
||||
@@ -14,7 +14,7 @@ import {
|
||||
String,
|
||||
TypeAlias,
|
||||
} from "tsondb/schema/def"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
|
||||
export const SkillModificationLevel = Entity(import.meta.url, {
|
||||
name: "SkillModificationLevel",
|
||||
@@ -29,9 +29,9 @@ export const SkillModificationLevel = Entity(import.meta.url, {
|
||||
comment: "Configuration for this level for slow skills (rituals, ceremonies).",
|
||||
type: IncludeIdentifier(SlowSkillModificationLevelConfig),
|
||||
}),
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Optional,
|
||||
"SkillModificationLevelTranslation",
|
||||
"SkillModificationLevel",
|
||||
Object(
|
||||
{
|
||||
fast: Optional({
|
||||
@@ -45,19 +45,17 @@ export const SkillModificationLevel = Entity(import.meta.url, {
|
||||
type: IncludeIdentifier(LevelTypeConfigTranslation),
|
||||
}),
|
||||
},
|
||||
{ minProperties: 1 }
|
||||
)
|
||||
{ minProperties: 1 },
|
||||
),
|
||||
),
|
||||
}),
|
||||
displayName: null,
|
||||
displayNameCustomizer: ({ instance, locales }) => ({
|
||||
name: `${(instance.fast as any).casting_time} Act. / ${
|
||||
(instance.slow as any).casting_time.value
|
||||
} ${(instance.slow as any).casting_time.unit.kind} (fast/slow) — ${
|
||||
(instance.fast as any).range === (instance.slow as any).range
|
||||
? (instance.fast as any).range
|
||||
: (instance.fast as any).range + "/" + (instance.slow as any).range + " (fast/slow)"
|
||||
} m — ${(instance.fast as any).cost}/${(instance.slow as any).cost} (fast/slow) AE or KP`,
|
||||
name: `${instance.fast.casting_time.toString()} Act. / ${instance.slow.casting_time.value.toString()} ${instance.slow.casting_time.unit.kind} (fast/slow) — ${
|
||||
instance.fast.range === instance.slow.range
|
||||
? instance.fast.range.toString()
|
||||
: `${instance.fast.range.toString()}/${instance.slow.range.toString()} (fast/slow)`
|
||||
} m — ${instance.fast.cost.toString()}/${instance.slow.cost.toString()} (fast/slow) AE or KP`,
|
||||
localeId: locales[0],
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Entity, Integer, Object, Required, String } from "tsondb/schema/def"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
|
||||
export const SocialStatus = Entity(import.meta.url, {
|
||||
name: "SocialStatus",
|
||||
@@ -11,15 +11,15 @@ export const SocialStatus = Entity(import.meta.url, {
|
||||
"The social status’ order. The higher the order, the more powerful the social status. This has to be a unique value.",
|
||||
type: Integer({ minimum: 1 }),
|
||||
}),
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"SocialStatusTranslation",
|
||||
"SocialStatus",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The social status’ name.",
|
||||
type: String({ minLength: 1 }),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
|
||||
+4
-4
@@ -16,7 +16,7 @@ import { ImprovementCost } from "./_ImprovementCost.js"
|
||||
import { SpellworkPrerequisites } from "./_Prerequisite.js"
|
||||
import { SkillCheck, SkillCheckPenalty } from "./_SkillCheck.js"
|
||||
import { SpellworkTraditions } from "./_Spellwork.js"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
import { Errata } from "./source/_Erratum.js"
|
||||
import { src } from "./source/_PublicationRef.js"
|
||||
|
||||
@@ -59,9 +59,9 @@ export const Spell = Entity(import.meta.url, {
|
||||
type: IncludeIdentifier(SpellworkPrerequisites),
|
||||
}),
|
||||
src,
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"SpellTranslation",
|
||||
"Spell",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The spell’s name.",
|
||||
@@ -95,7 +95,7 @@ export const Spell = Entity(import.meta.url, {
|
||||
errata: Optional({
|
||||
type: IncludeIdentifier(Errata),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
enhancements: Required({
|
||||
type: ChildEntities(Enhancement),
|
||||
|
||||
+4
-4
@@ -1,5 +1,5 @@
|
||||
import { Entity, IncludeIdentifier, Object, Optional, Required, String } from "tsondb/schema/def"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
import { Errata } from "./source/_Erratum.js"
|
||||
import { src } from "./source/_PublicationRef.js"
|
||||
|
||||
@@ -9,9 +9,9 @@ export const State = Entity(import.meta.url, {
|
||||
type: () =>
|
||||
Object({
|
||||
src,
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"StateTranslation",
|
||||
"State",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The state’s name.",
|
||||
@@ -24,7 +24,7 @@ export const State = Entity(import.meta.url, {
|
||||
errata: Optional({
|
||||
type: IncludeIdentifier(Errata),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
TypeAlias,
|
||||
} from "tsondb/schema/def"
|
||||
import { BlessedTraditionIdentifier } from "./_Identifier.js"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
import { Errata } from "./source/_Erratum.js"
|
||||
import { src } from "./source/_PublicationRef.js"
|
||||
|
||||
@@ -34,9 +34,9 @@ export const Talisman = Entity(import.meta.url, {
|
||||
type: Integer({ minimum: 5, multipleOf: 5 }),
|
||||
}),
|
||||
src,
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"TalismanTranslation",
|
||||
"Talisman",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The talisman’s name.",
|
||||
@@ -53,7 +53,7 @@ export const Talisman = Entity(import.meta.url, {
|
||||
errata: Optional({
|
||||
type: IncludeIdentifier(Errata),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Entity, ObjectType, Optional, Required, String } from "tsondb/schema/def"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
import { TargetCategoryIdentifier } from "./_Identifier.js"
|
||||
|
||||
export const TargetCategory = Entity(import.meta.url, {
|
||||
@@ -11,15 +11,15 @@ export const TargetCategory = Entity(import.meta.url, {
|
||||
comment: "A superordinate target category, if present.",
|
||||
type: TargetCategoryIdentifier(),
|
||||
}),
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"TargetCategoryTranslation",
|
||||
"TargetCategory",
|
||||
ObjectType({
|
||||
name: Required({
|
||||
comment: "The target category’s name.",
|
||||
type: String({ minLength: 1 }),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
|
||||
+1
-1709
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,128 @@
|
||||
import {
|
||||
Array,
|
||||
Boolean,
|
||||
EnumCase,
|
||||
GenEnum,
|
||||
GenIncludeIdentifier,
|
||||
GenTypeAlias,
|
||||
IncludeIdentifier,
|
||||
Object,
|
||||
Optional,
|
||||
Param,
|
||||
Required,
|
||||
type Type,
|
||||
TypeArgument,
|
||||
} from "tsondb/schema/def"
|
||||
import { MagicalTraditionIdentifier, PatronIdentifier } from "./_Identifier.js"
|
||||
import { AdvancedSpecialAbilityRestrictedOptionIdentifier } from "./_IdentifierGroup.js"
|
||||
import { DisplayOption } from "./prerequisites/DisplayOption.js"
|
||||
|
||||
const AdvancedSpecialAbility = GenEnum(import.meta.url, {
|
||||
name: "AdvancedSpecialAbility",
|
||||
comment: "A reference to an advanced special ability.",
|
||||
parameters: [Param("Identifier")],
|
||||
values: Identifier => ({
|
||||
General: EnumCase({ type: TypeArgument(Identifier) }),
|
||||
RestrictOptions: EnumCase({
|
||||
type: GenIncludeIdentifier(RestrictAdvancedSpecialAbilityOptions, [TypeArgument(Identifier)]),
|
||||
}),
|
||||
OneOf: EnumCase({
|
||||
type: GenIncludeIdentifier(OneOfAdvancedSpecialAbilityOptions, [TypeArgument(Identifier)]),
|
||||
}),
|
||||
DeriveFromExternalOption: EnumCase({
|
||||
type: GenIncludeIdentifier(AdvancedSpecialAbilityDerivedFromExternalOption, [
|
||||
TypeArgument(Identifier),
|
||||
]),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const RestrictAdvancedSpecialAbilityOptions = GenTypeAlias(import.meta.url, {
|
||||
name: "RestrictAdvancedSpecialAbilityOptions",
|
||||
parameters: [Param("Identifier")],
|
||||
type: Identifier =>
|
||||
Object({
|
||||
id: Required({
|
||||
comment: "The advanced special ability’s identifier.",
|
||||
type: TypeArgument(Identifier),
|
||||
}),
|
||||
option: Required({
|
||||
comment:
|
||||
"Specify the select option(s) that only are allowed for the referenced advanced special ability; others are disallowed.",
|
||||
type: Array(IncludeIdentifier(AdvancedSpecialAbilityRestrictedOptionIdentifier), {
|
||||
minItems: 1,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const OneOfAdvancedSpecialAbilityOptions = GenTypeAlias(import.meta.url, {
|
||||
name: "OneOfAdvancedSpecialAbilityOptions",
|
||||
parameters: [Param("Identifier")],
|
||||
type: Identifier =>
|
||||
Object({
|
||||
options: Required({
|
||||
comment: "The possible advanced special abilities.",
|
||||
type: Array(TypeArgument(Identifier), { minItems: 2 }),
|
||||
}),
|
||||
is_selection_required_on_purchase: Required({
|
||||
comment:
|
||||
"Do have to choose the advanced special ability when buying the style special ability? Otherwise the decision can be made later.",
|
||||
type: Boolean(),
|
||||
}),
|
||||
display_option: Optional({
|
||||
type: IncludeIdentifier(DisplayOption),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const AdvancedSpecialAbilityDerivedFromExternalOption = GenTypeAlias(import.meta.url, {
|
||||
name: "AdvancedSpecialAbilityDerivedFromExternalOption",
|
||||
parameters: [Param("Identifier")],
|
||||
type: Identifier =>
|
||||
Object({
|
||||
external_entry: Required({
|
||||
comment: "The possible advanced special abilities.",
|
||||
type: MagicalTraditionIdentifier(),
|
||||
}),
|
||||
map: Required({
|
||||
comment: "Map options from the external entry to allowed advanced special abilities.",
|
||||
type: Array(
|
||||
GenIncludeIdentifier(AdvancedSpecialAbilityDerivedFromExternalOptionMapping, [
|
||||
TypeArgument(Identifier),
|
||||
]),
|
||||
{ minItems: 1 },
|
||||
),
|
||||
}),
|
||||
display_option: Optional({
|
||||
type: IncludeIdentifier(DisplayOption),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const AdvancedSpecialAbilityDerivedFromExternalOptionMapping = GenTypeAlias(import.meta.url, {
|
||||
name: "AdvancedSpecialAbilityDerivedFromExternalOptionMapping",
|
||||
parameters: [Param("Identifier")],
|
||||
comment: "Mapping from an option of the external entry to an allowed advanced special ability.",
|
||||
type: Identifier =>
|
||||
Object({
|
||||
from_option: Required({
|
||||
comment: "The select option’s identifier.",
|
||||
type: PatronIdentifier(),
|
||||
}),
|
||||
to_advanced: Required({
|
||||
comment: "The advanced special ability’s identifier.",
|
||||
type: TypeArgument(Identifier),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
export const advanced = (identifier: Type) =>
|
||||
Required({
|
||||
comment:
|
||||
"The Advanced Special Abilities for the respective Style Special Ability. Sometimes, only a specific select option or a set of select options of an entry is allowed, which can be modelled by the option property. It can also be that you can choose from a set of special abilities, but then you can’t specify an option.",
|
||||
type: Array(GenIncludeIdentifier(AdvancedSpecialAbility, [identifier]), {
|
||||
minItems: 3,
|
||||
maxItems: 3,
|
||||
}),
|
||||
})
|
||||
@@ -0,0 +1,186 @@
|
||||
import {
|
||||
ArrayType as Array,
|
||||
EnumDecl as Enum,
|
||||
EnumCaseDecl as EnumCase,
|
||||
GenIncludeIdentifierType as GenIncludeIdentifier,
|
||||
IncludeIdentifier,
|
||||
IntegerType as Integer,
|
||||
ObjectType as Object,
|
||||
Optional,
|
||||
Required,
|
||||
String,
|
||||
TypeAlias,
|
||||
} from "tsondb/schema/def"
|
||||
import { ActivatableIdentifier } from "./_IdentifierGroup.js"
|
||||
import { MathOperation } from "./_MathExpression.js"
|
||||
import { BySizeCategory } from "./_SizeCategory.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
|
||||
const AdventurePointsValue = Enum(import.meta.url, {
|
||||
name: "AdventurePointsValue",
|
||||
values: () => ({
|
||||
Fixed: EnumCase({ type: IncludeIdentifier(FixedAdventurePointsValue) }),
|
||||
ByLevel: EnumCase({ type: IncludeIdentifier(AdventurePointsValueByLevel) }),
|
||||
DerivedFromSelection: EnumCase({
|
||||
type: IncludeIdentifier(AdventurePointsDerivedFromSelection),
|
||||
}),
|
||||
DependingOnActive: EnumCase({
|
||||
type: IncludeIdentifier(AdventurePointsDependingOnActive),
|
||||
}),
|
||||
DependingOnActiveInstances: EnumCase({
|
||||
type: IncludeIdentifier(AdventurePointsDependingOnActiveInstances),
|
||||
}),
|
||||
Indefinite: EnumCase({ type: null }),
|
||||
}),
|
||||
})
|
||||
|
||||
export const ap_value = Required({
|
||||
comment: "The adventure points value.",
|
||||
type: IncludeIdentifier(AdventurePointsValue),
|
||||
})
|
||||
|
||||
export const ap_value_l10n = Optional({
|
||||
comment: "The adventure points value.",
|
||||
isDeprecated: true,
|
||||
type: String({ minLength: 1 }),
|
||||
})
|
||||
|
||||
export const ap_value_append = Optional({
|
||||
comment: "The string to append to the generated AP value text.",
|
||||
isDeprecated: true,
|
||||
type: String({ minLength: 1 }),
|
||||
})
|
||||
|
||||
export const FixedAdventurePointsValue = TypeAlias(import.meta.url, {
|
||||
name: "FixedAdventurePointsValue",
|
||||
comment:
|
||||
"A fixed adventure points value. If the entry has levels, this is the cost per level as well.",
|
||||
type: () => IncludeIdentifier(AdventurePointsSingleValue),
|
||||
})
|
||||
|
||||
const AdventurePointsValueByLevel = TypeAlias(import.meta.url, {
|
||||
name: "AdventurePointsValueByLevel",
|
||||
comment: "An entry with levels may have different costs for each level.",
|
||||
type: () =>
|
||||
Object({
|
||||
list: Required({
|
||||
comment:
|
||||
"The list of adventure point values, where the position in the list corresponds to the level (i.e. the first value is for level I, the second value is for level II, and so on). The length of the list must match the amount of levels the special ability has.",
|
||||
type: Array(IncludeIdentifier(AdventurePointsSingleValue), { minItems: 2 }),
|
||||
}),
|
||||
additionalBySizeCategory: Optional({
|
||||
comment:
|
||||
"Values may be added to each level based on the size category of the character. Negative values mean that the AP value is lowered.",
|
||||
type: GenIncludeIdentifier(BySizeCategory, [Integer()]),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const AdventurePointsDerivedFromSelection = TypeAlias(import.meta.url, {
|
||||
name: "AdventurePointsDerivedFromSelection",
|
||||
comment:
|
||||
"The adventure points value is derived from the selection of the special ability. Its display value may be able to be derived from the given information for the select options. If that is not the case or the generated text would not match the original one, a replacement text can be provided.",
|
||||
type: () =>
|
||||
Object({
|
||||
translations: NestedTranslationMap(
|
||||
Optional,
|
||||
"AdventurePointsDerivedFromSelection",
|
||||
Object(
|
||||
{
|
||||
replacement: Optional({
|
||||
comment:
|
||||
"A replacement for the generated text if it would not match the original one.",
|
||||
type: String({ minLength: 1, isMarkdown: true }),
|
||||
}),
|
||||
},
|
||||
{ minProperties: 1 },
|
||||
),
|
||||
),
|
||||
}),
|
||||
})
|
||||
|
||||
const AdventurePointsSingleValue = TypeAlias(import.meta.url, {
|
||||
name: "AdventurePointsSingleValue",
|
||||
comment: "A single adventure points value.",
|
||||
type: () => Integer({ minimum: 0 }),
|
||||
})
|
||||
|
||||
export const AdventurePointsDependingOnActive = TypeAlias(import.meta.url, {
|
||||
name: "AdventurePointsDependingOnActive",
|
||||
comment: "The adventure points cost depends on if an instance of an entry is active.",
|
||||
type: () =>
|
||||
Object({
|
||||
id: Required({
|
||||
comment: "The identifier of the entry to that decides which AP value to pick.",
|
||||
type: IncludeIdentifier(ActivatableIdentifier),
|
||||
}),
|
||||
active: Required({
|
||||
comment: "The adventure points value if an instance of the referenced entry is active.",
|
||||
type: IncludeIdentifier(AdventurePointsSingleValue),
|
||||
}),
|
||||
inactive: Required({
|
||||
comment: "The adventure points value if no instance of the referenced entry is active.",
|
||||
type: IncludeIdentifier(AdventurePointsSingleValue),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
export const AdventurePointsDependingOnActiveInstances = Enum(import.meta.url, {
|
||||
name: "AdventurePointsDependingOnActiveInstances",
|
||||
comment:
|
||||
"The adventure points cost depends on how many instances of the entry are already active.",
|
||||
values: () => ({
|
||||
Threshold: EnumCase({
|
||||
type: IncludeIdentifier(AdventurePointsDependingOnActiveInstancesThreshold),
|
||||
}),
|
||||
Expression: EnumCase({
|
||||
type: IncludeIdentifier(AdventurePointsDependingOnActiveInstancesExpression),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const AdventurePointsDependingOnActiveInstancesThreshold = TypeAlias(import.meta.url, {
|
||||
name: "AdventurePointsDependingOnActiveInstancesThreshold",
|
||||
comment:
|
||||
"The adventure points cost depends on how many instances of the entry are already active.",
|
||||
type: () =>
|
||||
Object({
|
||||
normal: Required({
|
||||
comment: "The normal adventure points value.",
|
||||
type: Integer({ minimum: 0 }),
|
||||
}),
|
||||
threshold: Required({
|
||||
comment: "The number of active instances **after** which the alternative cost applies.",
|
||||
type: Integer({ minimum: 1 }),
|
||||
}),
|
||||
alternative: Required({
|
||||
comment: "The alternative adventure points value.",
|
||||
type: Integer({ minimum: 0 }),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const AdventurePointsDependingOnActiveInstancesExpression = TypeAlias(import.meta.url, {
|
||||
name: "AdventurePointsDependingOnActiveInstancesExpression",
|
||||
comment:
|
||||
"The adventure points cost depends on how many instances of the entry are already active.",
|
||||
type: () =>
|
||||
GenIncludeIdentifier(MathOperation, [
|
||||
IncludeIdentifier(AdventurePointsDependingOnActiveInstancesExpressionValue),
|
||||
]),
|
||||
})
|
||||
|
||||
const AdventurePointsDependingOnActiveInstancesExpressionValue = Enum(import.meta.url, {
|
||||
name: "AdventurePointsDependingOnActiveInstancesExpressionValue",
|
||||
comment: `The type of value allowed in the expression to calculate the AP value.`,
|
||||
values: () => ({
|
||||
Constant: EnumCase({
|
||||
comment: "A constant integer value.",
|
||||
type: Integer(),
|
||||
}),
|
||||
Active: EnumCase({
|
||||
comment: "The number of active instances.",
|
||||
type: null,
|
||||
}),
|
||||
}),
|
||||
})
|
||||
@@ -0,0 +1,89 @@
|
||||
import {
|
||||
Array,
|
||||
Boolean,
|
||||
Enum,
|
||||
EnumCase,
|
||||
IncludeIdentifier,
|
||||
Integer,
|
||||
Object,
|
||||
Optional,
|
||||
Required,
|
||||
TypeAlias,
|
||||
} from "tsondb/schema/def"
|
||||
import { ActivatableIdentifier } from "./_IdentifierGroup.js"
|
||||
|
||||
const AutomaticEntry = TypeAlias(import.meta.url, {
|
||||
name: "AutomaticEntry",
|
||||
type: () =>
|
||||
Object({
|
||||
action: Required({
|
||||
comment: "What type of action is applied to the target entry?",
|
||||
type: IncludeIdentifier(AutomaticEntryAction),
|
||||
}),
|
||||
apply_ap_value: Required({
|
||||
comment:
|
||||
"If an entry is added or removed, does is cost or grant adventure points or is it free of charge?",
|
||||
type: Boolean(),
|
||||
}),
|
||||
target: Required({
|
||||
comment:
|
||||
"The entry that is to be added or removed. It can be a fixed entry or a selection where the player must choose one entry.",
|
||||
type: IncludeIdentifier(AutomaticEntryTarget),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
export const automatic_entries = Optional({
|
||||
comment:
|
||||
"This entry has a direct influence on the existence of other entries. It may add or remove entries.",
|
||||
type: Array(IncludeIdentifier(AutomaticEntry), { minItems: 1 }),
|
||||
})
|
||||
|
||||
const AutomaticEntryAction = Enum(import.meta.url, {
|
||||
name: "AutomaticEntryAction",
|
||||
values: () => ({
|
||||
Add: EnumCase({ type: null }),
|
||||
Remove: EnumCase({ type: null }),
|
||||
}),
|
||||
})
|
||||
|
||||
const AutomaticEntryTarget = Enum(import.meta.url, {
|
||||
name: "AutomaticEntryTarget",
|
||||
values: () => ({
|
||||
Selection: EnumCase({ type: IncludeIdentifier(AutomaticEntryTargetSelection) }),
|
||||
Fixed: EnumCase({ type: IncludeIdentifier(FixedAutomaticEntryTarget) }),
|
||||
}),
|
||||
})
|
||||
|
||||
const AutomaticEntryTargetSelection = TypeAlias(import.meta.url, {
|
||||
name: "AutomaticEntryTargetSelection",
|
||||
type: () =>
|
||||
Object({
|
||||
list: Required({
|
||||
type: IncludeIdentifier(AutomaticEntryTargetSelectionList),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const AutomaticEntryTargetSelectionList = Enum(import.meta.url, {
|
||||
name: "AutomaticEntryTargetSelectionList",
|
||||
values: () => ({
|
||||
MagicalTraditions: EnumCase({ type: null }),
|
||||
MagicalDilettanteTraditions: EnumCase({ type: null }),
|
||||
}),
|
||||
})
|
||||
|
||||
const FixedAutomaticEntryTarget = TypeAlias(import.meta.url, {
|
||||
name: "FixedAutomaticEntryTarget",
|
||||
type: () =>
|
||||
Object({
|
||||
id: Required({
|
||||
type: IncludeIdentifier(ActivatableIdentifier),
|
||||
}),
|
||||
level: Optional({
|
||||
comment:
|
||||
"The level of the target entry to be added or removed. If this is omitted for a multi-level entry, level 1 is assumed.",
|
||||
type: Integer({ minimum: 1 }),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
@@ -0,0 +1,450 @@
|
||||
import {
|
||||
Array,
|
||||
Boolean,
|
||||
Enum,
|
||||
EnumCase,
|
||||
GenIncludeIdentifier,
|
||||
GenTypeAlias,
|
||||
IncludeIdentifier,
|
||||
Integer,
|
||||
Object,
|
||||
Optional,
|
||||
Param,
|
||||
Required,
|
||||
String,
|
||||
TypeAlias,
|
||||
TypeArgument,
|
||||
} from "tsondb/schema/def"
|
||||
import {
|
||||
CloseCombatTechniqueIdentifier,
|
||||
RaceIdentifier,
|
||||
RangedCombatTechniqueIdentifier,
|
||||
WeaponIdentifier,
|
||||
} from "./_Identifier.js"
|
||||
import {
|
||||
CombatRelatedSpecialAbilityIdentifier,
|
||||
CombatTechniqueIdentifier,
|
||||
} from "./_IdentifierGroup.js"
|
||||
|
||||
const CombatSpecialAbilityUsageType = Enum(import.meta.url, {
|
||||
name: "CombatSpecialAbilityUsageType",
|
||||
comment: "The definition of how the combat special ability can be used in combat.",
|
||||
values: () => ({
|
||||
Passive: EnumCase({ type: null }),
|
||||
BasicManeuver: EnumCase({ type: null }),
|
||||
SpecialManeuver: EnumCase({ type: null }),
|
||||
}),
|
||||
})
|
||||
|
||||
export const usage_type = Required({
|
||||
comment: "The definition of how the combat special ability can be used in combat.",
|
||||
type: IncludeIdentifier(CombatSpecialAbilityUsageType),
|
||||
})
|
||||
|
||||
const CombatSpecialAbilityType = Enum(import.meta.url, {
|
||||
name: "CombatSpecialAbilityType",
|
||||
comment:
|
||||
"The definition of if the combat special ability can be used when armed or when unarmed.",
|
||||
values: () => ({
|
||||
Armed: EnumCase({ type: null }),
|
||||
Unarmed: EnumCase({ type: null }),
|
||||
}),
|
||||
})
|
||||
|
||||
export const type = Required({
|
||||
comment:
|
||||
"The definition of if the combat special ability can be used when armed or when unarmed.",
|
||||
type: IncludeIdentifier(CombatSpecialAbilityType),
|
||||
})
|
||||
|
||||
const Penalty = Enum(import.meta.url, {
|
||||
name: "Penalty",
|
||||
comment: "The penalty the special ability gives when used.",
|
||||
values: () => ({
|
||||
Single: EnumCase({ type: IncludeIdentifier(SinglePenalty) }),
|
||||
ByHandedness: EnumCase({ type: IncludeIdentifier(PenaltyByHandedness) }),
|
||||
ByActivation: EnumCase({ type: IncludeIdentifier(PenaltyByActivation) }),
|
||||
Selection: EnumCase({ type: IncludeIdentifier(PenaltySelection) }),
|
||||
ByLevel: EnumCase({ type: IncludeIdentifier(PenaltyByLevel) }),
|
||||
ByAttack: EnumCase({ type: IncludeIdentifier(PenaltyByAttack) }),
|
||||
DependsOnHitZone: EnumCase({ type: null }),
|
||||
}),
|
||||
})
|
||||
|
||||
export const penalty = Optional({
|
||||
comment: "The penalty the special ability gives when used.",
|
||||
type: IncludeIdentifier(Penalty),
|
||||
})
|
||||
|
||||
export const penalty_l10n = Optional({
|
||||
comment: "The penalty the special ability gives when used.",
|
||||
isDeprecated: true,
|
||||
type: String({ minLength: 1 }),
|
||||
})
|
||||
|
||||
const SinglePenalty = TypeAlias(import.meta.url, {
|
||||
name: "SinglePenalty",
|
||||
type: () =>
|
||||
Object({
|
||||
value: Required({
|
||||
comment: "The penalty value.",
|
||||
type: Integer(),
|
||||
}),
|
||||
applies_to_parry: Optional({
|
||||
comment: "Set to `true` if the penalty applies to the parry instead of the attack.",
|
||||
type: Boolean(),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const PenaltyByHandedness = TypeAlias(import.meta.url, {
|
||||
name: "PenaltyByHandedness",
|
||||
type: () =>
|
||||
Object({
|
||||
one_handed: Required({
|
||||
comment: "The penalty value for one-handed weapons.",
|
||||
type: Integer(),
|
||||
}),
|
||||
two_handed: Required({
|
||||
comment: "The penalty value for two-handed weapons.",
|
||||
type: Integer(),
|
||||
}),
|
||||
applies_to_parry: Optional({
|
||||
comment: "Set to `true` if the penalty applies to the parry instead of the attack.",
|
||||
type: Boolean(),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const PenaltyByActivation = TypeAlias(import.meta.url, {
|
||||
name: "PenaltyByActivation",
|
||||
type: () =>
|
||||
Object({
|
||||
active: Required({
|
||||
comment: "The penalty value if the entry has been bought by the character.",
|
||||
type: Integer(),
|
||||
}),
|
||||
inactive: Required({
|
||||
comment: "The penalty value if the entry has not been bought by the character.",
|
||||
type: Integer(),
|
||||
}),
|
||||
applies_to_parry: Optional({
|
||||
comment: "Set to `true` if the penalty applies to the parry instead of the attack.",
|
||||
type: Boolean(),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const PenaltySelection = TypeAlias(import.meta.url, {
|
||||
name: "PenaltySelection",
|
||||
type: () =>
|
||||
Object({
|
||||
options: Required({
|
||||
type: IncludeIdentifier(PenaltySelectionOptions),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const PenaltySelectionOptions = Enum(import.meta.url, {
|
||||
name: "PenaltySelectionOptions",
|
||||
values: () => ({
|
||||
Specific: EnumCase({ type: IncludeIdentifier(SpecificPenaltySelectionOptions) }),
|
||||
Range: EnumCase({ type: IncludeIdentifier(PenaltySelectionOptionsRange) }),
|
||||
}),
|
||||
})
|
||||
|
||||
const SpecificPenaltySelectionOptions = TypeAlias(import.meta.url, {
|
||||
name: "SpecificPenaltySelectionOptions",
|
||||
type: () =>
|
||||
Object({
|
||||
list: Required({
|
||||
comment: "The list of specific penalty options.",
|
||||
type: Array(IncludeIdentifier(SpecificPenaltySelectionOption), { minItems: 2 }),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const SpecificPenaltySelectionOption = TypeAlias(import.meta.url, {
|
||||
name: "SpecificPenaltySelectionOption",
|
||||
type: () =>
|
||||
Object({
|
||||
value: Required({
|
||||
comment: "The penalty value.",
|
||||
type: Integer(),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const PenaltySelectionOptionsRange = TypeAlias(import.meta.url, {
|
||||
name: "PenaltySelectionOptionsRange",
|
||||
type: () =>
|
||||
Object({
|
||||
minimum: Required({
|
||||
comment: "The minimum penalty value.",
|
||||
type: Integer(),
|
||||
}),
|
||||
maximum: Required({
|
||||
comment: "The maximum penalty value.",
|
||||
type: Integer(),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const PenaltyByLevel = TypeAlias(import.meta.url, {
|
||||
name: "PenaltyByLevel",
|
||||
type: () =>
|
||||
Object({
|
||||
levels: Required({
|
||||
comment:
|
||||
"A continuous range of penalties for each level. The first element is the penalty for the first level, the second element is the penalty for the second level, and so on.",
|
||||
type: Array(IncludeIdentifier(PenaltyByLevelLevel), { minItems: 2 }),
|
||||
}),
|
||||
external: Optional({
|
||||
comment:
|
||||
"The combat-related special ability of which the level defines the penalty instead.",
|
||||
type: IncludeIdentifier(PenaltyByExternalLevel),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const PenaltyByLevelLevel = TypeAlias(import.meta.url, {
|
||||
name: "PenaltyByLevelLevel",
|
||||
type: () =>
|
||||
Object({
|
||||
value: Required({
|
||||
comment: "The penalty value for this level.",
|
||||
type: Integer(),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const PenaltyByExternalLevel = TypeAlias(import.meta.url, {
|
||||
name: "PenaltyByExternalLevel",
|
||||
comment: "The combat-related special ability of which the level defines the penalty instead.",
|
||||
type: () =>
|
||||
Object({
|
||||
id: Required({
|
||||
comment:
|
||||
"The identifier of the combat-related special ability of which the level defines the penalty instead.",
|
||||
type: IncludeIdentifier(CombatRelatedSpecialAbilityIdentifier),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const PenaltyByAttack = TypeAlias(import.meta.url, {
|
||||
name: "PenaltyByAttack",
|
||||
type: () =>
|
||||
Object({
|
||||
list: Required({
|
||||
comment:
|
||||
"A list of penalties for subsequent attacks. The first element is the penalty for the first attack, the second element is the penalty for the second attack, and so on. The order of the first element may be changed using `initial_order`, so that e.g. if set to `2`, the first element is the penalty for the second attack, the second element is the penalty for the third attack, and so on.",
|
||||
type: Array(IncludeIdentifier(PenaltyByAttackOrderItem), { minItems: 1 }),
|
||||
}),
|
||||
initial_order: Optional({
|
||||
comment: "The order of the first element in the `list` of penalties.",
|
||||
type: Integer(),
|
||||
}),
|
||||
attack_replacement: Optional({
|
||||
comment:
|
||||
"Set if a predefined different word should be used instead of the word `attack` for display purposes.",
|
||||
type: IncludeIdentifier(PenaltyByAttackReplacement),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const PenaltyByAttackOrderItem = TypeAlias(import.meta.url, {
|
||||
name: "PenaltyByAttackOrderItem",
|
||||
type: () =>
|
||||
Object({
|
||||
value: Required({
|
||||
comment: "The penalty value for this order.",
|
||||
type: Integer(),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const PenaltyByAttackReplacement = Enum(import.meta.url, {
|
||||
name: "PenaltyByAttackReplacement",
|
||||
comment:
|
||||
"Set if a predefined different word should be used instead of the word `attack` for display purposes.",
|
||||
values: () => ({
|
||||
Throw: EnumCase({ type: null }),
|
||||
}),
|
||||
})
|
||||
|
||||
const ApplicableCombatTechniques = Enum(import.meta.url, {
|
||||
name: "ApplicableCombatTechniques",
|
||||
values: () => ({
|
||||
None: EnumCase({ type: null }),
|
||||
DependingOnCombatStyle: EnumCase({ type: null }),
|
||||
All: EnumCase({ type: IncludeIdentifier(AllApplicableCombatTechniques) }),
|
||||
AllClose: EnumCase({ type: IncludeIdentifier(AllApplicableCloseCombatTechniques) }),
|
||||
AllRanged: EnumCase({ type: IncludeIdentifier(AllApplicableRangedCombatTechniques) }),
|
||||
Specific: EnumCase({ type: IncludeIdentifier(SpecificApplicableCombatTechniques) }),
|
||||
}),
|
||||
})
|
||||
|
||||
export const combat_techniques = Required({
|
||||
comment: "The combat techniques the special ability is applicable to.",
|
||||
type: IncludeIdentifier(ApplicableCombatTechniques),
|
||||
})
|
||||
|
||||
const AllApplicableCombatTechniques = TypeAlias(import.meta.url, {
|
||||
name: "AllApplicableCombatTechniques",
|
||||
type: () =>
|
||||
Object({
|
||||
restrictions: Optional({
|
||||
type: Array(IncludeIdentifier(ApplicableAllCombatTechniquesRestriction), { minItems: 1 }),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const AllApplicableCloseCombatTechniques = TypeAlias(import.meta.url, {
|
||||
name: "AllApplicableCloseCombatTechniques",
|
||||
type: () =>
|
||||
Object({
|
||||
restrictions: Optional({
|
||||
type: Array(IncludeIdentifier(ApplicableCloseCombatTechniquesRestriction), { minItems: 1 }),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const AllApplicableRangedCombatTechniques = TypeAlias(import.meta.url, {
|
||||
name: "AllApplicableRangedCombatTechniques",
|
||||
type: () =>
|
||||
Object({
|
||||
restrictions: Optional({
|
||||
type: Array(IncludeIdentifier(ApplicableRangedCombatTechniquesRestriction), {
|
||||
minItems: 1,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const SpecificApplicableCombatTechniques = TypeAlias(import.meta.url, {
|
||||
name: "SpecificApplicableCombatTechniques",
|
||||
type: () =>
|
||||
Object({
|
||||
list: Required({
|
||||
type: Array(IncludeIdentifier(SpecificApplicableCombatTechnique), { minItems: 1 }),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const SpecificApplicableCombatTechnique = TypeAlias(import.meta.url, {
|
||||
name: "SpecificApplicableCombatTechnique",
|
||||
type: () =>
|
||||
Object({
|
||||
id: Required({
|
||||
type: IncludeIdentifier(CombatTechniqueIdentifier),
|
||||
}),
|
||||
restrictions: Optional({
|
||||
type: Array(IncludeIdentifier(ApplicableSpecificCombatTechniquesRestriction), {
|
||||
minItems: 1,
|
||||
}),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const ApplicableAllCombatTechniquesRestriction = Enum(import.meta.url, {
|
||||
name: "ApplicableAllCombatTechniquesRestriction",
|
||||
values: () => ({
|
||||
Improvised: EnumCase({ type: null }),
|
||||
PointedBlade: EnumCase({ type: null }),
|
||||
Mount: EnumCase({ type: null }),
|
||||
Race: EnumCase({ type: IncludeIdentifier(ApplicableCombatTechniquesRaceRestriction) }),
|
||||
ExcludeCombatTechniques: EnumCase({
|
||||
type: GenIncludeIdentifier(ApplicableCombatTechniquesNegativeCombatTechniquesRestriction, [
|
||||
IncludeIdentifier(CombatTechniqueIdentifier),
|
||||
]),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const ApplicableCloseCombatTechniquesRestriction = Enum(import.meta.url, {
|
||||
name: "ApplicableCloseCombatTechniquesRestriction",
|
||||
values: () => ({
|
||||
Improvised: EnumCase({ type: null }),
|
||||
PointedBlade: EnumCase({ type: null }),
|
||||
Mount: EnumCase({ type: null }),
|
||||
HasParry: EnumCase({ type: null }),
|
||||
OneHanded: EnumCase({ type: null }),
|
||||
TwoHanded: EnumCase({ type: null }),
|
||||
ParryingWeapon: EnumCase({ type: null }),
|
||||
Race: EnumCase({ type: IncludeIdentifier(ApplicableCombatTechniquesRaceRestriction) }),
|
||||
ExcludeCombatTechniques: EnumCase({
|
||||
type: GenIncludeIdentifier(ApplicableCombatTechniquesNegativeCombatTechniquesRestriction, [
|
||||
CloseCombatTechniqueIdentifier(),
|
||||
]),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const ApplicableRangedCombatTechniquesRestriction = Enum(import.meta.url, {
|
||||
name: "ApplicableRangedCombatTechniquesRestriction",
|
||||
values: () => ({
|
||||
Improvised: EnumCase({ type: null }),
|
||||
PointedBlade: EnumCase({ type: null }),
|
||||
Mount: EnumCase({ type: null }),
|
||||
Race: EnumCase({ type: IncludeIdentifier(ApplicableCombatTechniquesRaceRestriction) }),
|
||||
ExcludeCombatTechniques: EnumCase({
|
||||
type: GenIncludeIdentifier(ApplicableCombatTechniquesNegativeCombatTechniquesRestriction, [
|
||||
RangedCombatTechniqueIdentifier(),
|
||||
]),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const ApplicableSpecificCombatTechniquesRestriction = Enum(import.meta.url, {
|
||||
name: "ApplicableSpecificCombatTechniquesRestriction",
|
||||
values: () => ({
|
||||
Improvised: EnumCase({ type: null }),
|
||||
PointedBlade: EnumCase({ type: null }),
|
||||
Mount: EnumCase({ type: null }),
|
||||
Race: EnumCase({ type: IncludeIdentifier(ApplicableCombatTechniquesRaceRestriction) }),
|
||||
Level: EnumCase({ type: IncludeIdentifier(ApplicableCombatTechniquesLevelRestriction) }),
|
||||
Weapons: EnumCase({ type: IncludeIdentifier(ApplicableCombatTechniquesWeaponRestriction) }),
|
||||
}),
|
||||
})
|
||||
|
||||
const ApplicableCombatTechniquesNegativeCombatTechniquesRestriction = GenTypeAlias(
|
||||
import.meta.url,
|
||||
{
|
||||
name: "ApplicableCombatTechniquesNegativeCombatTechniquesRestriction",
|
||||
parameters: [Param("Ref")],
|
||||
type: Ref =>
|
||||
Object({
|
||||
list: Required({
|
||||
comment: "The combat techniques this combat special ability is **not** applicable to.",
|
||||
type: Array(TypeArgument(Ref), { minItems: 1 }),
|
||||
}),
|
||||
}),
|
||||
},
|
||||
)
|
||||
|
||||
const ApplicableCombatTechniquesRaceRestriction = TypeAlias(import.meta.url, {
|
||||
name: "ApplicableCombatTechniquesRaceRestriction",
|
||||
type: () => RaceIdentifier(),
|
||||
})
|
||||
|
||||
const ApplicableCombatTechniquesLevelRestriction = TypeAlias(import.meta.url, {
|
||||
name: "ApplicableCombatTechniquesLevelRestriction",
|
||||
type: () =>
|
||||
Object({
|
||||
level: Required({
|
||||
comment: "The combat special ability is only applicable on a certain level.",
|
||||
type: Integer({ minimum: 1 }),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const ApplicableCombatTechniquesWeaponRestriction = TypeAlias(import.meta.url, {
|
||||
name: "ApplicableCombatTechniquesWeaponRestriction",
|
||||
type: () =>
|
||||
Object({
|
||||
list: Required({
|
||||
comment: "The specific weapons this combat special ability is only applicable to.",
|
||||
type: Array(WeaponIdentifier(), { minItems: 1 }),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
@@ -0,0 +1,628 @@
|
||||
import {
|
||||
Array,
|
||||
Boolean,
|
||||
Enum,
|
||||
EnumCase,
|
||||
IncludeIdentifier,
|
||||
Integer,
|
||||
Object,
|
||||
Optional,
|
||||
Required,
|
||||
String,
|
||||
TypeAlias,
|
||||
} from "tsondb/schema/def"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
import { DurationUnitValue } from "./_ActivatableSkillDuration.js"
|
||||
import { AspectIdentifier, PropertyIdentifier } from "./_Identifier.js"
|
||||
import { VolumePointsOptionReferenceIdentifier } from "./_IdentifierGroup.js"
|
||||
import { ResponsiveText, ResponsiveTextOptional } from "./_ResponsiveText.js"
|
||||
|
||||
const EnchantmentCost = Enum(import.meta.url, {
|
||||
name: "EnchantmentCost",
|
||||
values: () => ({
|
||||
ArcaneEnergyCost: EnumCase({ type: IncludeIdentifier(ArcaneEnergyCost) }),
|
||||
BindingCost: EnumCase({ type: IncludeIdentifier(BindingCost) }),
|
||||
}),
|
||||
})
|
||||
|
||||
export const cost = Optional({
|
||||
comment: "The cost when casting the entry.",
|
||||
type: IncludeIdentifier(EnchantmentCost),
|
||||
})
|
||||
|
||||
export const ArcaneEnergyCost = Enum(import.meta.url, {
|
||||
name: "ArcaneEnergyCost",
|
||||
comment: "The AE cost.",
|
||||
values: () => ({
|
||||
Fixed: EnumCase({ type: IncludeIdentifier(FixedArcaneEnergyCost) }),
|
||||
PerCountable: EnumCase({ type: IncludeIdentifier(ArcaneEnergyCostPerCountable) }),
|
||||
Interval: EnumCase({ type: IncludeIdentifier(IntervalArcaneEnergyCost) }),
|
||||
ActivationAndHalfInterval: EnumCase({
|
||||
type: IncludeIdentifier(ActivationAndHalfIntervalArcaneEnergyCost),
|
||||
}),
|
||||
Indefinite: EnumCase({ type: IncludeIdentifier(IndefiniteArcaneEnergyCost) }),
|
||||
Disjunction: EnumCase({ type: IncludeIdentifier(ArcaneEnergyCostDisjunction) }),
|
||||
Variable: EnumCase({ type: null }),
|
||||
}),
|
||||
})
|
||||
|
||||
export const aeCost = Optional({
|
||||
comment: "The AE cost.",
|
||||
isDeprecated: true,
|
||||
type: String({ minLength: 1 }),
|
||||
})
|
||||
|
||||
const FixedArcaneEnergyCost = TypeAlias(import.meta.url, {
|
||||
name: "FixedArcaneEnergyCost",
|
||||
type: () =>
|
||||
Object({
|
||||
value: Required({
|
||||
comment: "The AE cost value.",
|
||||
type: Integer({ minimum: 1 }),
|
||||
}),
|
||||
is_permanent: Optional({
|
||||
comment: "Set to `true` if the AE costs are permanent.",
|
||||
type: Boolean(),
|
||||
}),
|
||||
interval: Optional({
|
||||
comment: "Specified if the AE cost `value` has to be paid for each time interval.",
|
||||
type: IncludeIdentifier(DurationUnitValue),
|
||||
}),
|
||||
per_level: Optional({
|
||||
comment:
|
||||
"The AE cost are per level of the enchantment. It may either be displayed in a compressed way (e.g. `1 AE per level`) or in a verbose way (e.g. `1 AE for level I; 2 AE for level II`).",
|
||||
type: IncludeIdentifier(FixedArcaneEnergyCostPerLevel),
|
||||
}),
|
||||
translations: NestedTranslationMap(
|
||||
Optional,
|
||||
"FixedArcaneEnergyCost",
|
||||
Object(
|
||||
{
|
||||
note: Optional({
|
||||
comment: "A note, appended to the generated option string in parenthesis.",
|
||||
type: IncludeIdentifier(ResponsiveTextOptional),
|
||||
}),
|
||||
},
|
||||
{ minProperties: 1 },
|
||||
),
|
||||
),
|
||||
}),
|
||||
})
|
||||
|
||||
const FixedArcaneEnergyCostPerLevel = Enum(import.meta.url, {
|
||||
name: "FixedArcaneEnergyCostPerLevel",
|
||||
comment:
|
||||
"The AE cost are per level of the enchantment. It may either be displayed in a compressed way (e.g. `1 AE per level`) or in a verbose way (e.g. `1 AE for level I; 2 AE for level II`).",
|
||||
values: () => ({
|
||||
Compressed: EnumCase({ type: null }),
|
||||
Verbose: EnumCase({ type: null }),
|
||||
}),
|
||||
})
|
||||
|
||||
const ArcaneEnergyCostPerCountable = TypeAlias(import.meta.url, {
|
||||
name: "ArcaneEnergyCostPerCountable",
|
||||
type: () =>
|
||||
Object({
|
||||
value: Required({
|
||||
comment: "The AE cost value that has to be per a specific countable entity.",
|
||||
type: Integer({ minimum: 1 }),
|
||||
}),
|
||||
base_value: Optional({
|
||||
comment:
|
||||
"If defined, in addition to the cost per entity you have to pay a flat amount, regardless of the entity count.",
|
||||
type: Integer({ minimum: 1 }),
|
||||
}),
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"ArcaneEnergyCostPerCountable",
|
||||
Object({
|
||||
per: Required({
|
||||
comment: "The cost have to be per a specific countable entity, e.g. `8 AE per person`.",
|
||||
type: IncludeIdentifier(ResponsiveText),
|
||||
}),
|
||||
note: Optional({
|
||||
comment: "A note, appended to the generated string in parenthesis.",
|
||||
type: IncludeIdentifier(ResponsiveTextOptional),
|
||||
}),
|
||||
}),
|
||||
),
|
||||
}),
|
||||
})
|
||||
|
||||
const IntervalArcaneEnergyCost = TypeAlias(import.meta.url, {
|
||||
name: "IntervalArcaneEnergyCost",
|
||||
type: () =>
|
||||
Object({
|
||||
value: Required({
|
||||
comment: "The AE cost value that has to be payed each interval.",
|
||||
type: Integer({ minimum: 1 }),
|
||||
}),
|
||||
interval: Required({
|
||||
comment: "The time interval for which the AE cost `value` has to be paid.",
|
||||
type: IncludeIdentifier(DurationUnitValue),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const ActivationAndHalfIntervalArcaneEnergyCost = TypeAlias(import.meta.url, {
|
||||
name: "ActivationAndHalfIntervalArcaneEnergyCost",
|
||||
type: () =>
|
||||
Object({
|
||||
value: Required({
|
||||
comment:
|
||||
"The AE cost value that has to be payed for activation. Half of this value has to be payed each interval.",
|
||||
type: Integer({ minimum: 2, multipleOf: 2 }),
|
||||
}),
|
||||
interval: Required({
|
||||
comment: "The time interval for which the AE cost `value` has to be paid.",
|
||||
type: IncludeIdentifier(DurationUnitValue),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const IndefiniteArcaneEnergyCost = TypeAlias(import.meta.url, {
|
||||
name: "IndefiniteArcaneEnergyCost",
|
||||
type: () =>
|
||||
Object({
|
||||
modifier: Optional({
|
||||
comment:
|
||||
"Specified if the indefinite AP cost description needs to be modified by a certain value.",
|
||||
type: IncludeIdentifier(IndefiniteArcaneEnergyCostModifier),
|
||||
}),
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"IndefiniteArcaneEnergyCost",
|
||||
Object({
|
||||
description: Required({
|
||||
comment: "A description of where the cost come from.",
|
||||
type: IncludeIdentifier(ResponsiveText),
|
||||
}),
|
||||
}),
|
||||
),
|
||||
}),
|
||||
})
|
||||
|
||||
const IndefiniteArcaneEnergyCostModifier = TypeAlias(import.meta.url, {
|
||||
name: "IndefiniteArcaneEnergyCostModifier",
|
||||
type: () =>
|
||||
Object({
|
||||
arithmetic: Required({
|
||||
comment: "The arithmetic how to apply the `value` to the `base`.",
|
||||
type: IncludeIdentifier(IndefiniteArcaneEnergyCostModifierArithmetic),
|
||||
}),
|
||||
value: Required({
|
||||
comment: "The value that is applied to the `base` using the defined `arithmetic`.",
|
||||
type: Integer({ minimum: 1 }),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const IndefiniteArcaneEnergyCostModifierArithmetic = Enum(import.meta.url, {
|
||||
name: "IndefiniteArcaneEnergyCostModifierArithmetic",
|
||||
values: () => ({
|
||||
Add: EnumCase({ type: null }),
|
||||
}),
|
||||
})
|
||||
|
||||
const ArcaneEnergyCostDisjunction = TypeAlias(import.meta.url, {
|
||||
name: "ArcaneEnergyCostDisjunction",
|
||||
type: () =>
|
||||
Object({
|
||||
interval: Optional({
|
||||
comment: "Specified if the selected AE cost option has to be paid for each time interval.",
|
||||
type: IncludeIdentifier(ArcaneEnergyCostDisjunctionInterval),
|
||||
}),
|
||||
options: Required({
|
||||
comment: "The possible AE cost values.",
|
||||
type: Array(IncludeIdentifier(ArcaneEnergyCostDisjunctionOption), { minItems: 1 }),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const ArcaneEnergyCostDisjunctionInterval = TypeAlias(import.meta.url, {
|
||||
name: "ArcaneEnergyCostDisjunctionInterval",
|
||||
type: () =>
|
||||
Object({
|
||||
value: Required({
|
||||
comment: "The interval itself.",
|
||||
type: IncludeIdentifier(DurationUnitValue),
|
||||
}),
|
||||
activation_value: Required({
|
||||
comment: "The AE cost value for activation.",
|
||||
type: Integer({ minimum: 1 }),
|
||||
}),
|
||||
after_activation: Required({
|
||||
comment: `Set to \`true\` if the action where the enchantment is casted does not as a part of the first interval that has to be payed, so that the first interval payment needs to be done after the activation.
|
||||
|
||||
This works different than other sustained spells, since for them the end of the cast usually already counts as part of the first interval.`,
|
||||
type: Boolean(),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const ArcaneEnergyCostDisjunctionOption = TypeAlias(import.meta.url, {
|
||||
name: "ArcaneEnergyCostDisjunctionOption",
|
||||
type: () =>
|
||||
Object({
|
||||
value: Required({
|
||||
comment: "A possible AE cost value.",
|
||||
type: Integer({ minimum: 1 }),
|
||||
}),
|
||||
translations: NestedTranslationMap(
|
||||
Optional,
|
||||
"ArcaneEnergyCostDisjunctionOption",
|
||||
Object(
|
||||
{
|
||||
note: Optional({
|
||||
comment: "A note, appended to the generated option string in parenthesis.",
|
||||
type: IncludeIdentifier(ResponsiveTextOptional),
|
||||
}),
|
||||
},
|
||||
{ minProperties: 1 },
|
||||
),
|
||||
),
|
||||
}),
|
||||
})
|
||||
|
||||
const Volume = Enum(import.meta.url, {
|
||||
name: "Volume",
|
||||
comment: "The volume points the enchantment needs.",
|
||||
values: () => ({
|
||||
Fixed: EnumCase({ type: IncludeIdentifier(FixedVolume) }),
|
||||
PerLevel: EnumCase({ type: IncludeIdentifier(VolumePerLevel) }),
|
||||
ByLevel: EnumCase({ type: IncludeIdentifier(VolumeByLevel) }),
|
||||
Map: EnumCase({ type: IncludeIdentifier(VolumeMap) }),
|
||||
DerivedFromSelection: EnumCase({ type: IncludeIdentifier(VolumeDerivedFromSelection) }),
|
||||
}),
|
||||
})
|
||||
|
||||
export const volume = Required({
|
||||
comment: "The volume points the enchantment needs.",
|
||||
type: IncludeIdentifier(Volume),
|
||||
})
|
||||
|
||||
export const volume_l10n = Optional({
|
||||
comment: "The volume points the enchantment needs.",
|
||||
isDeprecated: true,
|
||||
type: String({ minLength: 1 }),
|
||||
})
|
||||
|
||||
const FixedVolume = TypeAlias(import.meta.url, {
|
||||
name: "FixedVolume",
|
||||
type: () =>
|
||||
Object({
|
||||
points: Required({
|
||||
comment: "The volume points.",
|
||||
type: Integer({ minimum: 0 }),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const VolumePerLevel = TypeAlias(import.meta.url, {
|
||||
name: "VolumePerLevel",
|
||||
type: () =>
|
||||
Object({
|
||||
points: Required({
|
||||
comment: "The volume points per level.",
|
||||
type: Integer({ minimum: 1 }),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const VolumeByLevel = TypeAlias(import.meta.url, {
|
||||
name: "VolumeByLevel",
|
||||
type: () =>
|
||||
Object({
|
||||
list: Required({
|
||||
comment:
|
||||
"The volume points for each level. The first element is the volume points for the first level, the second element is the volume points for the second level, and so on.",
|
||||
type: Array(IncludeIdentifier(VolumeByLevelItem), { minItems: 2 }),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const VolumeByLevelItem = TypeAlias(import.meta.url, {
|
||||
name: "VolumeByLevelItem",
|
||||
type: () =>
|
||||
Object({
|
||||
points: Required({
|
||||
comment: "The volume points for this level.",
|
||||
type: Integer({ minimum: 0 }),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const VolumeMap = TypeAlias(import.meta.url, {
|
||||
name: "VolumeMap",
|
||||
comment: `A content that is \`3/4/5 Points for Chimera, Daimonid, Golems, Undead / Fairies, Ghosts / Demons, Elementals\` may be respresented as the following map:
|
||||
|
||||
\`\`\`yaml
|
||||
options:
|
||||
- points: 3
|
||||
associated_options:
|
||||
- id:
|
||||
tag: General
|
||||
value: # ...
|
||||
# ...
|
||||
translations:
|
||||
en-US:
|
||||
label: "Chimera, Daimonid, Golems, Undead"
|
||||
label_standalone: "Chimera/Daimonid/Golems/Undead"
|
||||
- points: 4
|
||||
associated_options:
|
||||
- id:
|
||||
tag: General
|
||||
value: # ...
|
||||
# ...
|
||||
translations:
|
||||
en-US:
|
||||
label: "Fairies, Ghosts"
|
||||
label_standalone: "Fairies/Ghosts"
|
||||
- points: 5
|
||||
associated_options:
|
||||
- id:
|
||||
tag: General
|
||||
value: # ...
|
||||
# ...
|
||||
translations:
|
||||
en-US:
|
||||
label: "Demons, Elementals"
|
||||
label_standalone: "Demons/Elementals"
|
||||
\`\`\`
|
||||
|
||||
This will generate the exact same string as seen above. The associated options are not present in the example, but they link to the options the volume specification is meant for.`,
|
||||
type: () =>
|
||||
Object({
|
||||
options: Required({
|
||||
comment: "The possible costs and associated labels.",
|
||||
type: Array(IncludeIdentifier(VolumeMapOption), { minItems: 2 }),
|
||||
}),
|
||||
translations: NestedTranslationMap(
|
||||
Optional,
|
||||
"VolumeMap",
|
||||
Object(
|
||||
{
|
||||
list_prepend: Optional({
|
||||
comment: "Place a string between the `for` and the grouped map option labels.",
|
||||
type: IncludeIdentifier(ResponsiveTextOptional),
|
||||
}),
|
||||
list_append: Optional({
|
||||
comment: "Place a string after the grouped map option labels.",
|
||||
type: IncludeIdentifier(ResponsiveTextOptional),
|
||||
}),
|
||||
replacement: Optional({
|
||||
comment:
|
||||
"If the string from the book cannot be generated using the default generation technique, use this string. All options still need to be inserted propertly, since it may be used by in-game tools to provide a selection to players.",
|
||||
type: IncludeIdentifier(ResponsiveTextOptional),
|
||||
}),
|
||||
},
|
||||
{ minProperties: 1 },
|
||||
),
|
||||
),
|
||||
}),
|
||||
})
|
||||
|
||||
const VolumeMapOption = TypeAlias(import.meta.url, {
|
||||
name: "VolumeMapOption",
|
||||
type: () =>
|
||||
Object({
|
||||
points: Required({
|
||||
comment: "The full permanent AE cost value for this option.",
|
||||
type: Integer({ minimum: 1 }),
|
||||
}),
|
||||
associated_options: Required({
|
||||
comment: "Links to the options this volume specification is meant for.",
|
||||
type: Array(IncludeIdentifier(VolumeMapOptionAssociatedOption), { minItems: 1 }),
|
||||
}),
|
||||
translations: NestedTranslationMap(
|
||||
Optional,
|
||||
"VolumeMapOption",
|
||||
Object(
|
||||
{
|
||||
label: Required({
|
||||
comment: "The description of the option for cost string generation.",
|
||||
type: IncludeIdentifier(ResponsiveTextOptional),
|
||||
}),
|
||||
label_standalone: Optional({
|
||||
comment:
|
||||
"The description of the option if used standalone. Only used if different from `label`.",
|
||||
type: IncludeIdentifier(ResponsiveTextOptional),
|
||||
}),
|
||||
},
|
||||
{ minProperties: 1 },
|
||||
),
|
||||
),
|
||||
}),
|
||||
})
|
||||
|
||||
const VolumeMapOptionAssociatedOption = TypeAlias(import.meta.url, {
|
||||
name: "VolumeMapOptionAssociatedOption",
|
||||
type: () =>
|
||||
Object({
|
||||
id: Required({
|
||||
comment: "The option’s identifier.",
|
||||
type: IncludeIdentifier(VolumePointsOptionReferenceIdentifier),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const VolumeDerivedFromSelection = TypeAlias(import.meta.url, {
|
||||
name: "VolumeDerivedFromSelection",
|
||||
type: () =>
|
||||
Object({
|
||||
fallback: Required({
|
||||
comment: "The volume for the selection if it does not define a specific one.",
|
||||
type: Integer({ minimum: 0 }),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
export const BindingCost = Enum(import.meta.url, {
|
||||
name: "BindingCost",
|
||||
comment: "The binding cost for an enchantment.",
|
||||
values: () => ({
|
||||
Fixed: EnumCase({ type: IncludeIdentifier(FixedBindingCost) }),
|
||||
PerLevel: EnumCase({ type: IncludeIdentifier(BindingCostPerLevel) }),
|
||||
Map: EnumCase({ type: IncludeIdentifier(BindingCostMap) }),
|
||||
DerivedFromSelection: EnumCase({ type: IncludeIdentifier(BindingCostDerivedFromSelection) }),
|
||||
}),
|
||||
})
|
||||
|
||||
export const bindingCost = Optional({
|
||||
comment: "The binding cost for an enchantment.",
|
||||
isDeprecated: true,
|
||||
type: String({ minLength: 1 }),
|
||||
})
|
||||
|
||||
const FixedBindingCost = TypeAlias(import.meta.url, {
|
||||
name: "FixedBindingCost",
|
||||
type: () =>
|
||||
Object({
|
||||
permanent_value: Required({
|
||||
comment: `The permanent AE cost.
|
||||
|
||||
If the enchantment has multiple levels, it is only applied for the first level.`,
|
||||
type: Integer({ minimum: 1 }),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const BindingCostPerLevel = TypeAlias(import.meta.url, {
|
||||
name: "BindingCostPerLevel",
|
||||
type: () =>
|
||||
Object({
|
||||
permanent_value: Required({
|
||||
comment: "The permanent AE cost per level.",
|
||||
type: Integer({ minimum: 1 }),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const BindingCostMap = TypeAlias(import.meta.url, {
|
||||
name: "BindingCostMap",
|
||||
comment: `A content that is \`2/4/8 permanent AE for spell-swords with the combat technique Daggers, Swords, or Two-Handed Swords\` may be respresented as the following map:
|
||||
|
||||
\`\`\`yaml
|
||||
options:
|
||||
- permanent_value: 2
|
||||
translations:
|
||||
en-US:
|
||||
label: "Daggers"
|
||||
label_standalone: "Dagger"
|
||||
- permanent_value: 4
|
||||
translations:
|
||||
en-US:
|
||||
label: "Swords"
|
||||
label_standalone: "Sword"
|
||||
- permanent_value: 8
|
||||
translations:
|
||||
en-US:
|
||||
label: "Two-Handed Swords"
|
||||
label_standalone: "Two-Handed Sword"
|
||||
list_prepend: "spell-swords with the combat technique"
|
||||
\`\`\`
|
||||
|
||||
This will generate the exact same string as seen above.`,
|
||||
type: () =>
|
||||
Object({
|
||||
options: Required({
|
||||
comment: "The possible costs and associated labels.",
|
||||
type: Array(IncludeIdentifier(BindingCostMapOption), { minItems: 2 }),
|
||||
}),
|
||||
translations: NestedTranslationMap(
|
||||
Optional,
|
||||
"BindingCostMap",
|
||||
Object(
|
||||
{
|
||||
list_prepend: Optional({
|
||||
comment: "Place a string between the `for` and the grouped map option labels.",
|
||||
type: IncludeIdentifier(ResponsiveTextOptional),
|
||||
}),
|
||||
list_append: Optional({
|
||||
comment: "Place a string after the grouped map option labels.",
|
||||
type: IncludeIdentifier(ResponsiveTextOptional),
|
||||
}),
|
||||
replacement: Optional({
|
||||
comment:
|
||||
"If the string from the book cannot be generated using the default generation technique, use this string. All options still need to be inserted propertly, since it may be used by in-game tools to provide a selection to players.",
|
||||
type: IncludeIdentifier(ResponsiveTextOptional),
|
||||
}),
|
||||
},
|
||||
{ minProperties: 1 },
|
||||
),
|
||||
),
|
||||
}),
|
||||
})
|
||||
|
||||
const BindingCostMapOption = TypeAlias(import.meta.url, {
|
||||
name: "BindingCostMapOption",
|
||||
type: () =>
|
||||
Object({
|
||||
permanent_value: Required({
|
||||
comment: "The full permanent AE cost value for this option.",
|
||||
type: Integer({ minimum: 1 }),
|
||||
}),
|
||||
translations: NestedTranslationMap(
|
||||
Optional,
|
||||
"BindingCostMapOption",
|
||||
Object({
|
||||
label: Required({
|
||||
comment: "The description of the option for cost string generation.",
|
||||
type: IncludeIdentifier(ResponsiveTextOptional),
|
||||
}),
|
||||
label_standalone: Optional({
|
||||
comment:
|
||||
"The description of the option if used standalone. Only used if different from `label`.",
|
||||
type: IncludeIdentifier(ResponsiveTextOptional),
|
||||
}),
|
||||
}),
|
||||
),
|
||||
}),
|
||||
})
|
||||
|
||||
const BindingCostDerivedFromSelection = TypeAlias(import.meta.url, {
|
||||
name: "BindingCostDerivedFromSelection",
|
||||
type: () =>
|
||||
Object({
|
||||
fallback: Required({
|
||||
comment: "The binding cost for the selection if it does not define a specific one.",
|
||||
type: Integer({ minimum: 0 }),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const PropertyDeclaration = Enum(import.meta.url, {
|
||||
name: "PropertyDeclaration",
|
||||
comment:
|
||||
"The magic property’s identifier. `DependingOnProperty` can only be used if the special ability has an option to select a property.",
|
||||
values: () => ({
|
||||
DependingOnSelection: EnumCase({ type: null }),
|
||||
Fixed: EnumCase({ type: PropertyIdentifier() }),
|
||||
}),
|
||||
})
|
||||
|
||||
export const property = () =>
|
||||
Required({
|
||||
comment:
|
||||
"The magic property’s identifier. `DependingOnProperty` can only be used if the special ability has an option to select a property.",
|
||||
type: IncludeIdentifier(PropertyDeclaration),
|
||||
})
|
||||
|
||||
export const propertyOptional = () =>
|
||||
Optional({
|
||||
comment:
|
||||
"The magic property’s identifier. `DependingOnProperty` can only be used if the special ability has an option to select a property.",
|
||||
type: IncludeIdentifier(PropertyDeclaration),
|
||||
})
|
||||
|
||||
export const aspect = () =>
|
||||
Required({
|
||||
comment: "The blessed aspect.",
|
||||
type: AspectIdentifier(),
|
||||
})
|
||||
|
||||
export const aspectOptional = () =>
|
||||
Optional({
|
||||
comment: "The blessed aspect.",
|
||||
type: AspectIdentifier(),
|
||||
})
|
||||
@@ -16,7 +16,7 @@ import {
|
||||
TypeAlias,
|
||||
TypeArgument,
|
||||
} from "tsondb/schema/def"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
import {
|
||||
CeremonyIdentifier,
|
||||
CloseCombatTechniqueIdentifier,
|
||||
@@ -285,16 +285,16 @@ const SkillApplicationOrUse = TypeAlias(import.meta.url, {
|
||||
comment: "The application’s or use’s identifier. An entry-unique, increasing integer.",
|
||||
type: Integer({ minimum: 1 }),
|
||||
}),
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Optional,
|
||||
"SkillApplicationOrUseTranslation",
|
||||
"SkillApplicationOrUse",
|
||||
Object({
|
||||
name: Required({
|
||||
comment:
|
||||
"The name of the application or use if different from the activatable entry’s name.",
|
||||
type: String({ minLength: 1 }),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
})
|
||||
@@ -424,7 +424,7 @@ const SelectOptionsFixedAdventurePointsValue = GenTypeAlias(import.meta.url, {
|
||||
type: Array(
|
||||
GenIncludeIdentifier(SelectOptionsFixedAdventurePointsValueMapping, [
|
||||
TypeArgument(Identifier),
|
||||
])
|
||||
]),
|
||||
),
|
||||
}),
|
||||
default: Required({
|
||||
|
||||
@@ -0,0 +1,333 @@
|
||||
import {
|
||||
Boolean,
|
||||
ChildEntities,
|
||||
Entity,
|
||||
Enum,
|
||||
EnumCase,
|
||||
getAnyEnumCaseValue,
|
||||
IncludeIdentifier,
|
||||
Integer,
|
||||
Object,
|
||||
Optional,
|
||||
Required,
|
||||
String,
|
||||
TypeAlias,
|
||||
} from "tsondb/schema/def"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
import { SelectOptionCategory } from "./_ActivatableSelectOptionCategory.js"
|
||||
import { NewSkillApplication, SkillUse } from "./_ActivatableSkillApplicationsAndUses.js"
|
||||
import { GeneralIdentifier, SkillIdentifier } from "./_Identifier.js"
|
||||
import { CombatTechniqueIdentifier, SelectOptionParentIdentifier } from "./_IdentifierGroup.js"
|
||||
import { GeneralPrerequisites } from "./_Prerequisite.js"
|
||||
import { Errata } from "./source/_Erratum.js"
|
||||
import { optionalSrc } from "./source/_PublicationRef.js"
|
||||
|
||||
const SelectOptions = TypeAlias(import.meta.url, {
|
||||
name: "SelectOptions",
|
||||
comment: `Definitions for possible options for the activatable entry. They can either be derived from entry categories or be defined explicitly. Both can happen as well, but if there is an explicitly defined select option and a derived select option has the same identifier (which may only happen if skill or combat technique identifiers are used for explicit select options), the explicit definition overwrites the derived option.
|
||||
|
||||
Note that this is only a full definition of options for simple logic that can be made explicit using the more detailed configuration for both derived categories and explicit options. There are quite a few entries whose option logic cannot be fully represented here, so that it needs to be implemented manually.`,
|
||||
type: () =>
|
||||
Object(
|
||||
{
|
||||
derived: Optional({
|
||||
comment: `An entry category with optional further configuration. All available entries from the specified categories will be included as separate select options. You can also specify a set of groups that should only be included. Groups not mentioned will be excluded then.`,
|
||||
type: IncludeIdentifier(SelectOptionCategory),
|
||||
}),
|
||||
},
|
||||
{ minProperties: 1 },
|
||||
),
|
||||
})
|
||||
|
||||
export const select_options = Optional({
|
||||
comment: `Definitions for possible options for the activatable entry, derived from entry categories.
|
||||
|
||||
Note that this is only a full definition of options for simple logic that can be made explicit using the more detailed configuration for both derived categories and explicit options. There are quite a few entries whose option logic cannot be fully represented here, so that it needs to be implemented manually.`,
|
||||
type: IncludeIdentifier(SelectOptions),
|
||||
})
|
||||
|
||||
export const ExplicitSelectOption = Enum(import.meta.url, {
|
||||
name: "ExplicitSelectOption",
|
||||
values: () => ({
|
||||
General: EnumCase({ type: GeneralIdentifier() }),
|
||||
Skill: EnumCase({ type: IncludeIdentifier(ExplicitSkillSelectOption) }),
|
||||
CombatTechnique: EnumCase({ type: IncludeIdentifier(ExplicitCombatTechniqueSelectOption) }),
|
||||
}),
|
||||
})
|
||||
|
||||
export const GeneralSelectOption = Entity(import.meta.url, {
|
||||
name: "GeneralSelectOption",
|
||||
namePlural: "GeneralSelectOptions",
|
||||
type: () =>
|
||||
Object({
|
||||
parent: Required({
|
||||
comment: "The entry that contains select option.",
|
||||
type: IncludeIdentifier(SelectOptionParentIdentifier),
|
||||
}),
|
||||
profession_only: Optional({
|
||||
comment:
|
||||
"Sometimes, professions use specific text selections that are not contained in described lists. This ensures you can use them for professions only. They are not going to be displayed as options to the user.",
|
||||
type: Boolean(),
|
||||
}),
|
||||
skill_applications: Required({
|
||||
comment:
|
||||
"Registers new applications, which get enabled once this entry is activated with its respective select option. It specifies an entry-unique identifier and the skill it belongs to. A translation can be left out if its name equals the name of the origin select option.",
|
||||
type: ChildEntities(NewSkillApplication),
|
||||
}),
|
||||
skill_uses: Required({
|
||||
comment:
|
||||
"Registers uses, which get enabled once this entry is activated with its respective select option. It specifies an entry-unique identifier and the skill it belongs to. A translation can be left out if its name equals the name of the origin select option.",
|
||||
type: ChildEntities(SkillUse),
|
||||
}),
|
||||
prerequisites: Optional({
|
||||
comment: "Prerequisites for the select option.",
|
||||
type: IncludeIdentifier(GeneralPrerequisites),
|
||||
}),
|
||||
binding_cost: Optional({
|
||||
comment:
|
||||
"Specific binding cost for the select option. Only has an effect if the associated entry supports binding costs.",
|
||||
type: Integer({ minimum: 0 }),
|
||||
}),
|
||||
ap_value: Optional({
|
||||
comment: "Specific AP cost for the select option.",
|
||||
type: Integer({ minimum: 1 }),
|
||||
}),
|
||||
src: optionalSrc,
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"GeneralSelectOption",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The name of the select option.",
|
||||
type: String({ minLength: 1 }),
|
||||
}),
|
||||
name_in_profession: Optional({
|
||||
comment: "The name of the select option when displayed in a generated profession text.",
|
||||
type: String({ minLength: 1 }),
|
||||
}),
|
||||
description: Optional({
|
||||
comment:
|
||||
"The description of the select option. Useful for Bad Habits, Trade Secrets and other entries where a description is available.",
|
||||
type: String({ minLength: 1, isMarkdown: true }),
|
||||
}),
|
||||
errata: Optional({
|
||||
type: IncludeIdentifier(Errata),
|
||||
}),
|
||||
}),
|
||||
),
|
||||
}),
|
||||
parentReferenceKey: "parent",
|
||||
displayName: {},
|
||||
displayNameCustomizer: ({
|
||||
instance,
|
||||
instanceDisplayName,
|
||||
instanceDisplayNameLocaleId,
|
||||
getDisplayNameForInstanceId,
|
||||
}) => ({
|
||||
name: `${
|
||||
instanceDisplayName.length > 0
|
||||
? instanceDisplayName
|
||||
: (getDisplayNameForInstanceId(getAnyEnumCaseValue(instance.parent))?.name ?? "")
|
||||
} — ${instanceDisplayName}`,
|
||||
localeId: instanceDisplayNameLocaleId,
|
||||
}),
|
||||
})
|
||||
|
||||
export const explicit_select_options = Required({
|
||||
comment: `Explicit definitions for possible options for the activatable entry.
|
||||
|
||||
Note that this is only a full definition of options for simple logic that can be made explicit using the more detailed configuration for both derived categories and explicit options. There are quite a few entries whose option logic cannot be fully represented here, so that it needs to be implemented manually.`,
|
||||
type: ChildEntities(GeneralSelectOption),
|
||||
})
|
||||
|
||||
const ExplicitSkillSelectOption = TypeAlias(import.meta.url, {
|
||||
name: "ExplicitSkillSelectOption",
|
||||
type: () =>
|
||||
Object({
|
||||
id: Required({
|
||||
comment: "The skill’s identifier.",
|
||||
type: SkillIdentifier(),
|
||||
}),
|
||||
prerequisites: Optional({
|
||||
comment: "Prerequisites for the select option.",
|
||||
type: IncludeIdentifier(GeneralPrerequisites),
|
||||
}),
|
||||
binding_cost: Optional({
|
||||
comment:
|
||||
"Specific binding cost for the select option. Only has an effect if the associated entry supports binding costs.",
|
||||
type: Integer({ minimum: 0 }),
|
||||
}),
|
||||
ap_value: Optional({
|
||||
comment: "Specific AP cost for the select option.",
|
||||
type: Integer({ minimum: 1 }),
|
||||
}),
|
||||
src: optionalSrc,
|
||||
translations: NestedTranslationMap(
|
||||
Optional,
|
||||
"ExplicitSkillSelectOption",
|
||||
Object(
|
||||
{
|
||||
errata: Optional({
|
||||
type: IncludeIdentifier(Errata),
|
||||
}),
|
||||
},
|
||||
{ minProperties: 1 },
|
||||
),
|
||||
),
|
||||
}),
|
||||
})
|
||||
|
||||
const ExplicitCombatTechniqueSelectOption = TypeAlias(import.meta.url, {
|
||||
name: "ExplicitCombatTechniqueSelectOption",
|
||||
type: () =>
|
||||
Object({
|
||||
id: Required({
|
||||
comment: "The combat technique’s identifier.",
|
||||
type: IncludeIdentifier(CombatTechniqueIdentifier),
|
||||
}),
|
||||
prerequisites: Optional({
|
||||
comment: "Prerequisites for the select option.",
|
||||
type: IncludeIdentifier(GeneralPrerequisites),
|
||||
}),
|
||||
binding_cost: Optional({
|
||||
comment:
|
||||
"Specific binding cost for the select option. Only has an effect if the associated entry supports binding costs.",
|
||||
type: Integer({ minimum: 0 }),
|
||||
}),
|
||||
ap_value: Optional({
|
||||
comment: "Specific AP cost for the select option.",
|
||||
type: Integer({ minimum: 1 }),
|
||||
}),
|
||||
src: optionalSrc,
|
||||
translations: NestedTranslationMap(
|
||||
Optional,
|
||||
"ExplicitCombatTechniqueSelectOption",
|
||||
Object(
|
||||
{
|
||||
errata: Optional({
|
||||
type: IncludeIdentifier(Errata),
|
||||
}),
|
||||
},
|
||||
{ minProperties: 1 },
|
||||
),
|
||||
),
|
||||
}),
|
||||
})
|
||||
|
||||
// "Options": {
|
||||
// "$comment": "Unfinished generalized options handling.",
|
||||
// "type": "array",
|
||||
// "minItems": 1,
|
||||
// "items": {
|
||||
// "oneOf": [
|
||||
// {
|
||||
// "type": "object",
|
||||
// "properties": {
|
||||
// "type": { "const": "List" },
|
||||
// "value": {
|
||||
// "type": "object",
|
||||
// "properties": {
|
||||
// "list": {
|
||||
// "oneOf": [
|
||||
// {
|
||||
// "type": "object",
|
||||
// "properties": {
|
||||
// "type": { "const": "Generate" },
|
||||
// "value": { "$ref": "#/definitions/SelectOptionCategories" }
|
||||
// },
|
||||
// "required": ["type", "value"],
|
||||
// "additionalProperties": false
|
||||
// },
|
||||
// {
|
||||
// "type": "object",
|
||||
// "properties": {
|
||||
// "type": { "const": "Explicit" },
|
||||
// "value": { "$ref": "#/definitions/SelectOptions" }
|
||||
// },
|
||||
// "required": ["type", "value"],
|
||||
// "additionalProperties": false
|
||||
// }
|
||||
// ]
|
||||
// },
|
||||
// "multiple": {
|
||||
// "description": "Sometimes, multiple options from a single list have to or can be chosen. Its possible to define a number of options that have to be selected or a range of numbers of options that can be selected.",
|
||||
// "oneOf": [
|
||||
// {
|
||||
// "type": "object",
|
||||
// "properties": {
|
||||
// "type": { "const": "Fixed" },
|
||||
// "value": {
|
||||
// "description": "The number of options that have to be selected.",
|
||||
// "type": "integer",
|
||||
// "minimum": 2
|
||||
// }
|
||||
// },
|
||||
// "required": ["type", "value"],
|
||||
// "additionalProperties": false
|
||||
// },
|
||||
// {
|
||||
// "type": "object",
|
||||
// "properties": {
|
||||
// "type": { "const": "Range" },
|
||||
// "value": {
|
||||
// "type": "object",
|
||||
// "properties": {
|
||||
// "min": {
|
||||
// "description": "The minimum number of options that need to be selected. If left empty it defaults to 1.",
|
||||
// "type": "integer",
|
||||
// "minimum": 2
|
||||
// },
|
||||
// "max": {
|
||||
// "description": "The maximum number of options that need to be selected.",
|
||||
// "type": "integer",
|
||||
// "minimum": 2
|
||||
// }
|
||||
// },
|
||||
// "required": ["max"],
|
||||
// "additionalProperties": false
|
||||
// }
|
||||
// },
|
||||
// "required": ["type", "value"],
|
||||
// "additionalProperties": false
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// },
|
||||
// "required": ["list"],
|
||||
// "additionalProperties": false
|
||||
// }
|
||||
// },
|
||||
// "required": ["type", "value"],
|
||||
// "additionalProperties": false
|
||||
// },
|
||||
// {
|
||||
// "type": "object",
|
||||
// "properties": {
|
||||
// "type": { "const": "TextInput" },
|
||||
// "value": {
|
||||
// "type": "object",
|
||||
// "properties": {
|
||||
// "label": {
|
||||
// "type": "object",
|
||||
// "description": "All translations for the entry, identified by IETF language tag (BCP47).",
|
||||
// "patternProperties": {
|
||||
// "^[a-z]{2}-[A-Z]{2}$": {
|
||||
// "description": "The text input label.",
|
||||
// "type": "string",
|
||||
// "minLength": 1
|
||||
// }
|
||||
// },
|
||||
// "minProperties": 1,
|
||||
// "additionalProperties": false
|
||||
// }
|
||||
// },
|
||||
// "required": ["label"],
|
||||
// "additionalProperties": false
|
||||
// }
|
||||
// },
|
||||
// "required": ["type", "value"],
|
||||
// "additionalProperties": false
|
||||
// }
|
||||
// ]
|
||||
// }
|
||||
// },
|
||||
@@ -0,0 +1,121 @@
|
||||
import {
|
||||
Array,
|
||||
ChildEntities,
|
||||
Entity,
|
||||
getAnyEnumCaseValue,
|
||||
IncludeIdentifier,
|
||||
Integer,
|
||||
Object,
|
||||
Optional,
|
||||
Required,
|
||||
String,
|
||||
} from "tsondb/schema/def"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
import { SkillIdentifier } from "./_Identifier.js"
|
||||
import { NewSkillApplicationOrUseParentIdentifier } from "./_IdentifierGroup.js"
|
||||
|
||||
export const NewSkillApplication = Entity(import.meta.url, {
|
||||
name: "NewSkillApplication",
|
||||
namePlural: "NewSkillApplications",
|
||||
comment:
|
||||
"Registers new skill applications, which get enabled once the referenced entry is activated. It specifies the skill(s) it belongs to. A translation can be left out if its name equals the name of the origin activatable entry.",
|
||||
type: () =>
|
||||
Object({
|
||||
parent: Required({
|
||||
comment: "The entry that enables the new skill application.",
|
||||
type: IncludeIdentifier(NewSkillApplicationOrUseParentIdentifier),
|
||||
}),
|
||||
skills: Required({
|
||||
comment: "The skill(s) this application belongs to.",
|
||||
type: Array(SkillIdentifier(), { minItems: 1 }),
|
||||
}),
|
||||
required_skill_rating: Optional({
|
||||
comment:
|
||||
"If an application applies to multiple skills, it may need to ensure the respective skill is on a certain skill rating if the activatable entry cannot ensure this prerequisite.",
|
||||
type: Integer({ minimum: 1 }),
|
||||
}),
|
||||
translations: NestedTranslationMap(
|
||||
Optional,
|
||||
"NewSkillApplication",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The name of the application if different from the activatable entry’s name.",
|
||||
type: String({ minLength: 1 }),
|
||||
}),
|
||||
}),
|
||||
),
|
||||
}),
|
||||
parentReferenceKey: "parent",
|
||||
displayName: {},
|
||||
displayNameCustomizer: ({
|
||||
instance,
|
||||
instanceDisplayName,
|
||||
instanceDisplayNameLocaleId,
|
||||
getDisplayNameForInstanceId,
|
||||
}) => ({
|
||||
name:
|
||||
instanceDisplayName.length > 0
|
||||
? instanceDisplayName
|
||||
: (getDisplayNameForInstanceId(getAnyEnumCaseValue(instance.parent))?.name ?? ""),
|
||||
localeId: instanceDisplayNameLocaleId,
|
||||
}),
|
||||
})
|
||||
|
||||
export const skill_applications = Required({
|
||||
comment:
|
||||
"Registers new skill applications, which get enabled once this entry is activated. It specifies an entry-unique identifier and the skill it belongs to. A translation can be left out if its name equals the name of the origin activatable entry.",
|
||||
type: ChildEntities(NewSkillApplication),
|
||||
})
|
||||
|
||||
export const SkillUse = Entity(import.meta.url, {
|
||||
name: "SkillUse",
|
||||
namePlural: "SkillUses",
|
||||
comment:
|
||||
"Registers uses, which get enabled once this entry is activated. It specifies the skill(s) it belongs to. A translation can be left out if its name equals the name of the origin activatable entry.",
|
||||
type: () =>
|
||||
Object({
|
||||
parent: Required({
|
||||
comment: "The entry that enables the use.",
|
||||
type: IncludeIdentifier(NewSkillApplicationOrUseParentIdentifier),
|
||||
}),
|
||||
skills: Required({
|
||||
comment: "The skill(s) this use belongs to.",
|
||||
type: Array(SkillIdentifier(), { minItems: 1 }),
|
||||
}),
|
||||
required_skill_rating: Optional({
|
||||
comment:
|
||||
"If a use applies to multiple skills, it may need to ensure the respective skill is on a certain skill rating if the activatable entry cannot ensure this prerequisite.",
|
||||
type: Integer({ minimum: 1 }),
|
||||
}),
|
||||
translations: NestedTranslationMap(
|
||||
Optional,
|
||||
"SkillUse",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The name of the use if different from the activatable entry’s name.",
|
||||
type: String({ minLength: 1 }),
|
||||
}),
|
||||
}),
|
||||
),
|
||||
}),
|
||||
parentReferenceKey: "parent",
|
||||
displayName: {},
|
||||
displayNameCustomizer: ({
|
||||
instance,
|
||||
instanceDisplayName,
|
||||
instanceDisplayNameLocaleId,
|
||||
getDisplayNameForInstanceId,
|
||||
}) => ({
|
||||
name:
|
||||
instanceDisplayName.length > 0
|
||||
? instanceDisplayName
|
||||
: (getDisplayNameForInstanceId(getAnyEnumCaseValue(instance.parent))?.name ?? ""),
|
||||
localeId: instanceDisplayNameLocaleId,
|
||||
}),
|
||||
})
|
||||
|
||||
export const skill_uses = Required({
|
||||
comment:
|
||||
"Registers uses, which get enabled once this entry is activated. It specifies an entry-unique identifier and the skill it belongs to. A translation can be left out if its name equals the name of the origin activatable entry.",
|
||||
type: ChildEntities(SkillUse),
|
||||
})
|
||||
@@ -55,7 +55,7 @@ There must always be at least one casting time value.`,
|
||||
type: IncludeIdentifier(CastingTimeDuringLovemaking),
|
||||
}),
|
||||
},
|
||||
{ minProperties: 1 }
|
||||
{ minProperties: 1 },
|
||||
),
|
||||
})
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
import { DurationUnitValue } from "./_ActivatableSkillDuration.js"
|
||||
import { SkillModificationLevelIdentifier } from "./_Identifier.js"
|
||||
import { ResponsiveText, ResponsiveTextOptional, ResponsiveTextReplace } from "./_ResponsiveText.js"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
|
||||
export const OneTimeCost = Enum(import.meta.url, {
|
||||
name: "OneTimeCost",
|
||||
@@ -54,15 +54,15 @@ const ModifiableOneTimeCost = TypeAlias(import.meta.url, {
|
||||
comment: "The part of the cost value that has to be spent permanently.",
|
||||
type: Integer({ minimum: 1 }),
|
||||
}),
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Optional,
|
||||
"ModifiableOneTimeCostTranslation",
|
||||
"ModifiableOneTimeCost",
|
||||
Object({
|
||||
replacement: Required({
|
||||
comment: "A replacement string.",
|
||||
type: IncludeIdentifier(ResponsiveTextReplace),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
})
|
||||
@@ -87,15 +87,15 @@ const NonModifiableOneTimeCost = TypeAlias(import.meta.url, {
|
||||
comment: "The cost have to be per a specific countable entity, e.g. `8 KP per person`.",
|
||||
type: IncludeIdentifier(NonModifiableOneTimeCostPerCountable),
|
||||
}),
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Optional,
|
||||
"NonModifiableOneTimeCostTranslation",
|
||||
"NonModifiableOneTimeCost",
|
||||
Object({
|
||||
note: Required({
|
||||
comment: "A note, appended to the generated string in parenthesis.",
|
||||
type: IncludeIdentifier(ResponsiveTextOptional),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
})
|
||||
@@ -108,15 +108,15 @@ const NonModifiableOneTimeCostPerCountable = TypeAlias(import.meta.url, {
|
||||
comment: "If defined, the minimum total AE that have to be spent casting the skill.",
|
||||
type: Integer({ minimum: 1 }),
|
||||
}),
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Optional,
|
||||
"NonModifiableOneTimeCostPerCountableTranslation",
|
||||
"NonModifiableOneTimeCostPerCountable",
|
||||
Object({
|
||||
countable: Required({
|
||||
comment: "The countable entity name.",
|
||||
type: IncludeIdentifier(ResponsiveText),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
})
|
||||
@@ -125,15 +125,15 @@ export const IndefiniteOneTimeCost = TypeAlias(import.meta.url, {
|
||||
name: "IndefiniteOneTimeCost",
|
||||
type: () =>
|
||||
Object({
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"IndefiniteOneTimeCostTranslation",
|
||||
"IndefiniteOneTimeCost",
|
||||
Object({
|
||||
description: Required({
|
||||
comment: "A description of where the cost come from.",
|
||||
type: IncludeIdentifier(ResponsiveText),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
})
|
||||
@@ -164,9 +164,9 @@ This will generate the exact same string as seen above – given it is set for a
|
||||
minItems: 2,
|
||||
}),
|
||||
}),
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Optional,
|
||||
"CostMapTranslation",
|
||||
"CostMap",
|
||||
Object(
|
||||
{
|
||||
list_prepend: Optional({
|
||||
@@ -183,8 +183,8 @@ This will generate the exact same string as seen above – given it is set for a
|
||||
type: IncludeIdentifier(ResponsiveTextOptional),
|
||||
}),
|
||||
},
|
||||
{ minProperties: 1 }
|
||||
)
|
||||
{ minProperties: 1 },
|
||||
),
|
||||
),
|
||||
}),
|
||||
})
|
||||
@@ -201,9 +201,9 @@ const CostMapOption = TypeAlias(import.meta.url, {
|
||||
comment: "The part of the `value` that has to be paid permanently.",
|
||||
type: Integer({ minimum: 0 }),
|
||||
}),
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Optional,
|
||||
"CostMapOptionTranslation",
|
||||
"CostMapOption",
|
||||
Object({
|
||||
label: Required({
|
||||
comment: "The description of the option for cost string generation.",
|
||||
@@ -214,7 +214,7 @@ const CostMapOption = TypeAlias(import.meta.url, {
|
||||
"The description of the option if used standalone. Only used if different from `label`.",
|
||||
type: IncludeIdentifier(ResponsiveTextOptional),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
})
|
||||
@@ -277,15 +277,15 @@ const NonModifiableSustainedCostPerCountable = TypeAlias(import.meta.url, {
|
||||
comment: "If defined, the minimum total AE that have to be spent casting the skill.",
|
||||
type: Integer({ minimum: 1 }),
|
||||
}),
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"NonModifiableSustainedCostPerCountableTranslation",
|
||||
"NonModifiableSustainedCostPerCountable",
|
||||
Object({
|
||||
countable: Required({
|
||||
comment: "The countable entity name.",
|
||||
type: IncludeIdentifier(ResponsiveText),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -11,7 +11,7 @@ import {
|
||||
} from "tsondb/schema/def"
|
||||
import { CheckResultBasedModifier, CheckResultValue } from "./_ActivatableSkillCheckResultBased.js"
|
||||
import { ResponsiveText, ResponsiveTextReplace } from "./_ResponsiveText.js"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
|
||||
export const DurationForOneTime = Enum(import.meta.url, {
|
||||
name: "DurationForOneTime",
|
||||
@@ -32,15 +32,15 @@ const Immediate = TypeAlias(import.meta.url, {
|
||||
comment: "Specified if the duration has a maximum time span.",
|
||||
type: IncludeIdentifier(DurationUnitValue),
|
||||
}),
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Optional,
|
||||
"ImmediateTranslation",
|
||||
"Immediate",
|
||||
Object({
|
||||
replacement: Optional({
|
||||
comment: "A replacement string.",
|
||||
type: IncludeIdentifier(ResponsiveTextReplace),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
})
|
||||
@@ -49,15 +49,15 @@ const PermanentDuration = TypeAlias(import.meta.url, {
|
||||
name: "PermanentDuration",
|
||||
type: () =>
|
||||
Object({
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Optional,
|
||||
"PermanentDurationTranslation",
|
||||
"PermanentDuration",
|
||||
Object({
|
||||
replacement: Optional({
|
||||
comment: "A replacement string.",
|
||||
type: IncludeIdentifier(ResponsiveTextReplace),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
})
|
||||
@@ -78,9 +78,9 @@ export const FixedDuration = TypeAlias(import.meta.url, {
|
||||
comment: "The duration unit.",
|
||||
type: IncludeIdentifier(DurationUnit),
|
||||
}),
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Optional,
|
||||
"FixedDurationTranslation",
|
||||
"FixedDuration",
|
||||
Object(
|
||||
{
|
||||
replacement: Optional({
|
||||
@@ -88,8 +88,8 @@ export const FixedDuration = TypeAlias(import.meta.url, {
|
||||
type: IncludeIdentifier(ResponsiveTextReplace),
|
||||
}),
|
||||
},
|
||||
{ minProperties: 1 }
|
||||
)
|
||||
{ minProperties: 1 },
|
||||
),
|
||||
),
|
||||
}),
|
||||
})
|
||||
@@ -114,15 +114,15 @@ export const CheckResultBasedDuration = TypeAlias(import.meta.url, {
|
||||
comment: "The duration unit.",
|
||||
type: IncludeIdentifier(DurationUnit),
|
||||
}),
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Optional,
|
||||
"CheckResultBasedDurationTranslation",
|
||||
"CheckResultBasedDuration",
|
||||
Object({
|
||||
replacement: Optional({
|
||||
comment: "A replacement string.",
|
||||
type: IncludeIdentifier(ResponsiveTextReplace),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
})
|
||||
@@ -131,15 +131,15 @@ export const IndefiniteDuration = TypeAlias(import.meta.url, {
|
||||
name: "IndefiniteDuration",
|
||||
type: () =>
|
||||
Object({
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"IndefiniteDurationTranslation",
|
||||
"IndefiniteDuration",
|
||||
Object({
|
||||
description: Required({
|
||||
comment: "A description of the duration.",
|
||||
type: IncludeIdentifier(ResponsiveText),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -12,7 +12,7 @@ import {
|
||||
import { CheckResultBasedModifier, CheckResultValue } from "./_ActivatableSkillCheckResultBased.js"
|
||||
import { SkillModificationLevelIdentifier } from "./_Identifier.js"
|
||||
import { ResponsiveTextOptional, ResponsiveTextReplace } from "./_ResponsiveText.js"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
|
||||
export const Range = TypeAlias(import.meta.url, {
|
||||
name: "Range",
|
||||
@@ -22,9 +22,9 @@ export const Range = TypeAlias(import.meta.url, {
|
||||
comment: "The range value.",
|
||||
type: IncludeIdentifier(RangeValue),
|
||||
}),
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Optional,
|
||||
"RangeTranslation",
|
||||
"Range",
|
||||
Object(
|
||||
{
|
||||
note: Optional({
|
||||
@@ -38,8 +38,8 @@ export const Range = TypeAlias(import.meta.url, {
|
||||
type: IncludeIdentifier(ResponsiveTextReplace),
|
||||
}),
|
||||
},
|
||||
{ minProperties: 1 }
|
||||
)
|
||||
{ minProperties: 1 },
|
||||
),
|
||||
),
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
String,
|
||||
TypeAlias,
|
||||
} from "tsondb/schema/def"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
import { TargetCategoryIdentifier } from "./_Identifier.js"
|
||||
|
||||
export const AffectedTargetCategories = TypeAlias(import.meta.url, {
|
||||
@@ -25,12 +25,12 @@ const SpecificAffectedTargetCategory = TypeAlias(import.meta.url, {
|
||||
type: () =>
|
||||
Object({
|
||||
id: Required({ type: IncludeIdentifier(SpecificAffectedTargetCategoryIdentifier) }),
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Optional,
|
||||
"SpecificAffectedTargetCategoryTranslation",
|
||||
"SpecificAffectedTargetCategory",
|
||||
Object({
|
||||
note: Required({ type: String({ minLength: 1 }) }),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { IncludeIdentifier, Object, Required, String } from "tsondb/schema/def"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
import { ArcaneTraditionPrerequisites } from "./_Prerequisite.js"
|
||||
|
||||
export const ArcaneTraditionType =
|
||||
@@ -9,7 +9,7 @@ export const ArcaneTraditionType =
|
||||
prerequisites: Required({
|
||||
type: IncludeIdentifier(ArcaneTraditionPrerequisites),
|
||||
}),
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
translationName,
|
||||
Object({
|
||||
@@ -17,6 +17,6 @@ export const ArcaneTraditionType =
|
||||
comment: "The arcane tradition’s name.",
|
||||
type: String({ minLength: 1 }),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
})
|
||||
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
TypeAlias,
|
||||
TypeArgument,
|
||||
} from "tsondb/schema/def"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
import { AlternativeName } from "./_AlternativeNames.js"
|
||||
import { Errata } from "./source/_Erratum.js"
|
||||
|
||||
@@ -55,9 +55,9 @@ export const Cause = TypeAlias(import.meta.url, {
|
||||
"The chance to get infected by this cause, in percent. Some causes do not have a specific chance.",
|
||||
type: Integer({ minimum: 5, multipleOf: 5, maximum: 100 }),
|
||||
}),
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"CauseTranslation",
|
||||
"Cause",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The cause’s name.",
|
||||
@@ -72,7 +72,7 @@ export const Cause = TypeAlias(import.meta.url, {
|
||||
comment: "An additional note about this cause.",
|
||||
type: String({ minLength: 1 }),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
import {
|
||||
ChildEntities,
|
||||
Entity,
|
||||
getAnyEnumCaseValue,
|
||||
IncludeIdentifier,
|
||||
Integer,
|
||||
Object,
|
||||
@@ -15,7 +16,7 @@ import {
|
||||
} from "tsondb/schema/def"
|
||||
import { SkillWithEnhancementsIdentifier } from "./_IdentifierGroup.js"
|
||||
import { EnhancementPrerequisites } from "./_Prerequisite.js"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
import { Errata } from "./source/_Erratum.js"
|
||||
import { PublicationRefs } from "./source/_PublicationRef.js"
|
||||
|
||||
@@ -56,9 +57,9 @@ export const Enhancement = Entity(import.meta.url, {
|
||||
comment: "Only defined if different than the associated skill.",
|
||||
type: IncludeIdentifier(PublicationRefs),
|
||||
}),
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"EnhancementTranslation",
|
||||
"Enhancement",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The name of the enhancement.",
|
||||
@@ -76,7 +77,7 @@ export const Enhancement = Entity(import.meta.url, {
|
||||
errata: Optional({
|
||||
type: IncludeIdentifier(Errata),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
parentReferenceKey: "parent",
|
||||
@@ -87,9 +88,9 @@ export const Enhancement = Entity(import.meta.url, {
|
||||
instanceDisplayNameLocaleId,
|
||||
getDisplayNameForInstanceId,
|
||||
}) => ({
|
||||
name: `${getDisplayNameForInstanceId(
|
||||
(instance as any).parent[(instance as any).parent.kind]
|
||||
)} — ${instanceDisplayName}`,
|
||||
name: `${
|
||||
getDisplayNameForInstanceId(getAnyEnumCaseValue(instance.parent))?.name ?? ""
|
||||
} — ${instanceDisplayName}`,
|
||||
localeId: instanceDisplayNameLocaleId,
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { ReferenceIdentifier as R } from "tsondb/schema/def"
|
||||
import { GeneralSelectOption, NewSkillApplication, SkillUse } from "./_Activatable.js"
|
||||
import { GeneralSelectOption } from "./_ActivatableSelectOptions.js"
|
||||
import { NewSkillApplication, SkillUse } from "./_ActivatableSkillApplicationsAndUses.js"
|
||||
import { Enhancement } from "./_Enhancements.js"
|
||||
import { Influence } from "./_Influence.js"
|
||||
import { Advantage } from "./Advantage.js"
|
||||
@@ -39,6 +40,7 @@ import { Liebesspielzeug } from "./equipment/item/Liebesspielzeug.js"
|
||||
import { LuxuryGood } from "./equipment/item/LuxuryGood.js"
|
||||
import { MagicalArtifact } from "./equipment/item/MagicalArtifact.js"
|
||||
import { MusicalInstrument } from "./equipment/item/MusicalInstrument.js"
|
||||
import { Newspaper } from "./equipment/item/Newspaper.js"
|
||||
import { OrienteeringAid } from "./equipment/item/OrienteeringAid.js"
|
||||
import { Poison } from "./equipment/item/Poison.js"
|
||||
import { RopeOrChain } from "./equipment/item/RopeOrChain.js"
|
||||
@@ -80,6 +82,8 @@ import {
|
||||
import { Property } from "./Property.js"
|
||||
import { Race, RaceVariant } from "./Race.js"
|
||||
import { Ritual } from "./Ritual.js"
|
||||
import { AlternativeRule } from "./rule/AlternativeRule.js"
|
||||
import { PlayerType } from "./rule/AlternativeRule_PlayerType.js"
|
||||
import { CoreRule } from "./rule/CoreRule.js"
|
||||
import { FocusRule } from "./rule/FocusRule.js"
|
||||
import { Subject } from "./rule/FocusRule_Subject.js"
|
||||
@@ -155,6 +159,7 @@ export const AdvancedMagicalSpecialAbilityIdentifier: () => R = () =>
|
||||
R(AdvancedMagicalSpecialAbility)
|
||||
export const AdvancedSkillSpecialAbilityIdentifier: () => R = () => R(AdvancedSkillSpecialAbility)
|
||||
export const AdvantageIdentifier: () => R = () => R(Advantage)
|
||||
export const AlternativeRuleIdentifier: () => R = () => R(AlternativeRule)
|
||||
export const AmmunitionIdentifier: () => R = () => R(Ammunition)
|
||||
export const AncestorGlyphIdentifier: () => R = () => R(AncestorGlyph)
|
||||
export const AnimalCareIdentifier: () => R = () => R(AnimalCare)
|
||||
@@ -249,6 +254,7 @@ export const MagicalSpecialAbilityIdentifier: () => R = () => R(MagicalSpecialAb
|
||||
export const MagicalTraditionIdentifier: () => R = () => R(MagicalTradition)
|
||||
export const MagicStyleSpecialAbilityIdentifier: () => R = () => R(MagicStyleSpecialAbility)
|
||||
export const MusicalInstrumentIdentifier: () => R = () => R(MusicalInstrument)
|
||||
export const NewspaperIdentifier: () => R = () => R(Newspaper)
|
||||
export const OptionalRuleIdentifier: () => R = () => R(OptionalRule)
|
||||
export const OrbEnchantmentIdentifier: () => R = () => R(OrbEnchantment)
|
||||
export const OrienteeringAidIdentifier: () => R = () => R(OrienteeringAid)
|
||||
@@ -259,6 +265,7 @@ export const PactTypeIdentifier: () => R = () => R(PactType)
|
||||
export const PatronCategoryIdentifier: () => R = () => R(PatronCategory)
|
||||
export const PatronIdentifier: () => R = () => R(Patron)
|
||||
export const PersonalityTraitIdentifier: () => R = () => R(PersonalityTrait)
|
||||
export const PlayerTypeIdentifier: () => R = () => R(PlayerType)
|
||||
export const PoisonIdentifier: () => R = () => R(Poison)
|
||||
export const ProfessionIdentifier: () => R = () => R(Profession)
|
||||
export const ProfessionPackageIdentifier: () => R = () => R(ProfessionPackage)
|
||||
|
||||
@@ -80,6 +80,7 @@ import {
|
||||
MagicalTraditionIdentifier,
|
||||
MagicStyleSpecialAbilityIdentifier,
|
||||
MusicalInstrumentIdentifier,
|
||||
NewspaperIdentifier,
|
||||
OptionalRuleIdentifier,
|
||||
OrbEnchantmentIdentifier,
|
||||
OrienteeringAidIdentifier,
|
||||
@@ -416,6 +417,65 @@ export const SelectOptionIdentifier = Enum(import.meta.url, {
|
||||
}),
|
||||
})
|
||||
|
||||
export const SelectOptionParentIdentifier = Enum(import.meta.url, {
|
||||
name: "SelectOptionParentIdentifier",
|
||||
values: () => ({
|
||||
Advantage: EnumCase({ type: AdvantageIdentifier() }),
|
||||
Disadvantage: EnumCase({ type: DisadvantageIdentifier() }),
|
||||
AdvancedCombatSpecialAbility: EnumCase({ type: AdvancedCombatSpecialAbilityIdentifier() }),
|
||||
AdvancedKarmaSpecialAbility: EnumCase({ type: AdvancedKarmaSpecialAbilityIdentifier() }),
|
||||
AdvancedMagicalSpecialAbility: EnumCase({ type: AdvancedMagicalSpecialAbilityIdentifier() }),
|
||||
AdvancedSkillSpecialAbility: EnumCase({ type: AdvancedSkillSpecialAbilityIdentifier() }),
|
||||
AncestorGlyph: EnumCase({ type: AncestorGlyphIdentifier() }),
|
||||
ArcaneOrbEnchantment: EnumCase({ type: ArcaneOrbEnchantmentIdentifier() }),
|
||||
AttireEnchantment: EnumCase({ type: AttireEnchantmentIdentifier() }),
|
||||
BlessedTradition: EnumCase({ type: BlessedTraditionIdentifier() }),
|
||||
BowlEnchantment: EnumCase({ type: BowlEnchantmentIdentifier() }),
|
||||
BrawlingSpecialAbility: EnumCase({ type: BrawlingSpecialAbilityIdentifier() }),
|
||||
CauldronEnchantment: EnumCase({ type: CauldronEnchantmentIdentifier() }),
|
||||
CeremonialItemSpecialAbility: EnumCase({ type: CeremonialItemSpecialAbilityIdentifier() }),
|
||||
ChronicleEnchantment: EnumCase({ type: ChronicleEnchantmentIdentifier() }),
|
||||
CombatSpecialAbility: EnumCase({ type: CombatSpecialAbilityIdentifier() }),
|
||||
CombatStyleSpecialAbility: EnumCase({ type: CombatStyleSpecialAbilityIdentifier() }),
|
||||
CommandSpecialAbility: EnumCase({ type: CommandSpecialAbilityIdentifier() }),
|
||||
DaggerRitual: EnumCase({ type: DaggerRitualIdentifier() }),
|
||||
FamiliarSpecialAbility: EnumCase({ type: FamiliarSpecialAbilityIdentifier() }),
|
||||
FatePointSexSpecialAbility: EnumCase({ type: FatePointSexSpecialAbilityIdentifier() }),
|
||||
FatePointSpecialAbility: EnumCase({ type: FatePointSpecialAbilityIdentifier() }),
|
||||
FoolsHatEnchantment: EnumCase({ type: FoolsHatEnchantmentIdentifier() }),
|
||||
GeneralSpecialAbility: EnumCase({ type: GeneralSpecialAbilityIdentifier() }),
|
||||
InstrumentEnchantment: EnumCase({ type: InstrumentEnchantmentIdentifier() }),
|
||||
KarmaSpecialAbility: EnumCase({ type: KarmaSpecialAbilityIdentifier() }),
|
||||
Krallenkettenzauber: EnumCase({ type: KrallenkettenzauberIdentifier() }),
|
||||
LiturgicalStyleSpecialAbility: EnumCase({ type: LiturgicalStyleSpecialAbilityIdentifier() }),
|
||||
LycantropicGift: EnumCase({ type: LycantropicGiftIdentifier() }),
|
||||
MagicalSign: EnumCase({ type: MagicalSignIdentifier() }),
|
||||
MagicalSpecialAbility: EnumCase({ type: MagicalSpecialAbilityIdentifier() }),
|
||||
MagicalTradition: EnumCase({ type: MagicalTraditionIdentifier() }),
|
||||
MagicStyleSpecialAbility: EnumCase({ type: MagicStyleSpecialAbilityIdentifier() }),
|
||||
OrbEnchantment: EnumCase({ type: OrbEnchantmentIdentifier() }),
|
||||
PactGift: EnumCase({ type: PactGiftIdentifier() }),
|
||||
ProtectiveWardingCircleSpecialAbility: EnumCase({
|
||||
type: ProtectiveWardingCircleSpecialAbilityIdentifier(),
|
||||
}),
|
||||
RingEnchantment: EnumCase({ type: RingEnchantmentIdentifier() }),
|
||||
Sermon: EnumCase({ type: SermonIdentifier() }),
|
||||
SexSpecialAbility: EnumCase({ type: SexSpecialAbilityIdentifier() }),
|
||||
SickleRitual: EnumCase({ type: SickleRitualIdentifier() }),
|
||||
SikaryanDrainSpecialAbility: EnumCase({ type: SikaryanDrainSpecialAbilityIdentifier() }),
|
||||
SkillStyleSpecialAbility: EnumCase({ type: SkillStyleSpecialAbilityIdentifier() }),
|
||||
SpellSwordEnchantment: EnumCase({ type: SpellSwordEnchantmentIdentifier() }),
|
||||
StaffEnchantment: EnumCase({ type: StaffEnchantmentIdentifier() }),
|
||||
ToyEnchantment: EnumCase({ type: ToyEnchantmentIdentifier() }),
|
||||
Trinkhornzauber: EnumCase({ type: TrinkhornzauberIdentifier() }),
|
||||
VampiricGift: EnumCase({ type: VampiricGiftIdentifier() }),
|
||||
Vision: EnumCase({ type: VisionIdentifier() }),
|
||||
WandEnchantment: EnumCase({ type: WandEnchantmentIdentifier() }),
|
||||
WeaponEnchantment: EnumCase({ type: WeaponEnchantmentIdentifier() }),
|
||||
TradeSecret: EnumCase({ type: TradeSecretIdentifier() }),
|
||||
}),
|
||||
})
|
||||
|
||||
export const RequirableSelectOptionIdentifier = Enum(import.meta.url, {
|
||||
name: "RequirableSelectOptionIdentifier",
|
||||
values: () => ({
|
||||
@@ -433,6 +493,9 @@ export const RequirableSelectOptionIdentifier = Enum(import.meta.url, {
|
||||
Spell: EnumCase({ type: SpellIdentifier() }),
|
||||
Ritual: EnumCase({ type: RitualIdentifier() }),
|
||||
TradeSecret: EnumCase({ type: TradeSecretIdentifier() }),
|
||||
ArcaneBardTradition: EnumCase({ type: ArcaneBardTraditionIdentifier() }),
|
||||
ArcaneDancerTradition: EnumCase({ type: ArcaneDancerTraditionIdentifier() }),
|
||||
Element: EnumCase({ type: ElementIdentifier() }),
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -470,6 +533,7 @@ export const EquipmentIdentifier = Enum(import.meta.url, {
|
||||
LuxuryGood: EnumCase({ type: LuxuryGoodIdentifier() }),
|
||||
MagicalArtifact: EnumCase({ type: MagicalArtifactIdentifier() }),
|
||||
MusicalInstrument: EnumCase({ type: MusicalInstrumentIdentifier() }),
|
||||
Newspaper: EnumCase({ type: NewspaperIdentifier() }),
|
||||
OrienteeringAid: EnumCase({ type: OrienteeringAidIdentifier() }),
|
||||
Poison: EnumCase({ type: PoisonIdentifier() }),
|
||||
RopeOrChain: EnumCase({ type: RopeOrChainIdentifier() }),
|
||||
|
||||
@@ -9,7 +9,7 @@ import {
|
||||
TypeAlias,
|
||||
} from "tsondb/schema/def"
|
||||
import { InfluencePrerequisites } from "./_Prerequisite.js"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
import { Errata } from "./source/_Erratum.js"
|
||||
import { src } from "./source/_PublicationRef.js"
|
||||
|
||||
@@ -22,9 +22,9 @@ export const Influence = Entity(import.meta.url, {
|
||||
type: IncludeIdentifier(InfluencePrerequisites),
|
||||
}),
|
||||
src,
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"InfluenceTranslation",
|
||||
"Influence",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The influence’s name.",
|
||||
@@ -38,7 +38,7 @@ export const Influence = Entity(import.meta.url, {
|
||||
errata: Optional({
|
||||
type: IncludeIdentifier(Errata),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
import {
|
||||
Array,
|
||||
EnumCase,
|
||||
GenEnum,
|
||||
GenIncludeIdentifier,
|
||||
Param,
|
||||
TypeArgument,
|
||||
type Enum,
|
||||
type IncludeIdentifier,
|
||||
type TypeParameter,
|
||||
} from "tsondb/schema/def"
|
||||
|
||||
type BinaryMathOperationName =
|
||||
| "Addition"
|
||||
| "Subtraction"
|
||||
| "Multiplication"
|
||||
| "Division"
|
||||
| "Exponentiation"
|
||||
|
||||
type BinaryMathOperation = {
|
||||
[K in BinaryMathOperationName]: EnumCase<
|
||||
Array<IncludeIdentifier<[Value: TypeParameter], MathOperation>>
|
||||
>
|
||||
}
|
||||
|
||||
export type MathOperation = Enum<
|
||||
"MathOperation",
|
||||
{
|
||||
Value: EnumCase<TypeArgument>
|
||||
} & BinaryMathOperation,
|
||||
[Value: TypeParameter]
|
||||
>
|
||||
|
||||
export const MathOperation: MathOperation = GenEnum(import.meta.url, {
|
||||
name: "MathOperation",
|
||||
parameters: [Param("Value")],
|
||||
values: Value => {
|
||||
const binaryOperationType = Array(
|
||||
GenIncludeIdentifier<MathOperation, [Value: TypeParameter]>(MathOperation, [
|
||||
TypeArgument(Value),
|
||||
]),
|
||||
{ minItems: 2, maxItems: 2 },
|
||||
)
|
||||
|
||||
return {
|
||||
Value: EnumCase({
|
||||
comment: "A direct value.",
|
||||
type: TypeArgument(Value),
|
||||
}),
|
||||
Addition: EnumCase({
|
||||
comment: "Adds two values.",
|
||||
type: binaryOperationType,
|
||||
}),
|
||||
Subtraction: EnumCase({
|
||||
comment: "Subtracts the right value from the left value.",
|
||||
type: binaryOperationType,
|
||||
}),
|
||||
Multiplication: EnumCase({
|
||||
comment: "Multiplies two values.",
|
||||
type: binaryOperationType,
|
||||
}),
|
||||
Division: EnumCase({
|
||||
comment: "Divides the left value by the right value.",
|
||||
type: binaryOperationType,
|
||||
}),
|
||||
Exponentiation: EnumCase({
|
||||
comment: "Raises the left value to the power of the right value.",
|
||||
type: binaryOperationType,
|
||||
}),
|
||||
}
|
||||
},
|
||||
})
|
||||
@@ -18,7 +18,7 @@ import {
|
||||
TypeAlias,
|
||||
TypeArgument,
|
||||
} from "tsondb/schema/def"
|
||||
import { NestedLocaleMap } from "./Locale.js"
|
||||
import { NestedTranslationMap } from "./Locale.js"
|
||||
import { DisplayOption } from "./prerequisites/DisplayOption.js"
|
||||
import {
|
||||
AdvantageDisadvantagePrerequisiteGroup,
|
||||
@@ -34,7 +34,9 @@ import {
|
||||
PersonalityTraitPrerequisiteGroup,
|
||||
ProfessionPrerequisiteGroup,
|
||||
PublicationPrerequisiteGroup,
|
||||
RulePrerequisiteGroup,
|
||||
SpellworkPrerequisiteGroup,
|
||||
TribePrerequisiteGroup,
|
||||
} from "./prerequisites/PrerequisiteGroups.js"
|
||||
|
||||
const PrerequisiteGroup = GenTypeAlias(import.meta.url, {
|
||||
@@ -47,14 +49,14 @@ const PrerequisiteGroup = GenTypeAlias(import.meta.url, {
|
||||
list: Required({
|
||||
type: Array(TypeArgument(T), { minItems: 2 }),
|
||||
}),
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"PrerequisiteGroupTranslation",
|
||||
"PrerequisiteGroup",
|
||||
Object({
|
||||
text: Required({
|
||||
type: String({ minLength: 1 }),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
})
|
||||
@@ -118,6 +120,11 @@ const PrerequisitesForLevels = GenTypeAlias(import.meta.url, {
|
||||
}),
|
||||
})
|
||||
|
||||
export const RulePrerequisites = TypeAlias(import.meta.url, {
|
||||
name: "RulePrerequisites",
|
||||
type: () => GenIncludeIdentifier(PlainPrerequisites, [IncludeIdentifier(RulePrerequisiteGroup)]),
|
||||
})
|
||||
|
||||
export const DerivedCharacteristicPrerequisites = TypeAlias(import.meta.url, {
|
||||
name: "DerivedCharacteristicPrerequisites",
|
||||
type: () =>
|
||||
@@ -202,6 +209,11 @@ export const AnimistPowerPrerequisites = TypeAlias(import.meta.url, {
|
||||
GenIncludeIdentifier(PlainPrerequisites, [IncludeIdentifier(AnimistPowerPrerequisiteGroup)]),
|
||||
})
|
||||
|
||||
export const TribePrerequisites = TypeAlias(import.meta.url, {
|
||||
name: "TribePrerequisites",
|
||||
type: () => GenIncludeIdentifier(PlainPrerequisites, [IncludeIdentifier(TribePrerequisiteGroup)]),
|
||||
})
|
||||
|
||||
export const GeodeRitualPrerequisites = TypeAlias(import.meta.url, {
|
||||
name: "GeodeRitualPrerequisites",
|
||||
type: () =>
|
||||
|
||||
@@ -0,0 +1,35 @@
|
||||
import {
|
||||
Enum,
|
||||
EnumCase,
|
||||
GenTypeAlias,
|
||||
Object,
|
||||
Param,
|
||||
Required,
|
||||
TypeArgument,
|
||||
} from "tsondb/schema/def"
|
||||
|
||||
export const SizeCategory = Enum(import.meta.url, {
|
||||
name: "SizeCategory",
|
||||
comment: "Each creature fits into one of these size categories.",
|
||||
values: () => ({
|
||||
Tiny: EnumCase({ type: null }),
|
||||
Small: EnumCase({ type: null }),
|
||||
Medium: EnumCase({ type: null }),
|
||||
Large: EnumCase({ type: null }),
|
||||
Huge: EnumCase({ type: null }),
|
||||
}),
|
||||
})
|
||||
|
||||
export const BySizeCategory = GenTypeAlias(import.meta.url, {
|
||||
name: "BySizeCategory",
|
||||
comment: "An object that holds values for the different size categories",
|
||||
parameters: [Param("T")],
|
||||
type: T =>
|
||||
Object({
|
||||
tiny: Required({ type: TypeArgument(T) }),
|
||||
small: Required({ type: TypeArgument(T) }),
|
||||
medium: Required({ type: TypeArgument(T) }),
|
||||
large: Required({ type: TypeArgument(T) }),
|
||||
huge: Required({ type: TypeArgument(T) }),
|
||||
}),
|
||||
})
|
||||
@@ -10,7 +10,7 @@ import {
|
||||
TypeAlias,
|
||||
} from "tsondb/schema/def"
|
||||
import { EquipmentIdentifier } from "../_IdentifierGroup.js"
|
||||
import { NestedLocaleMap } from "../Locale.js"
|
||||
import { NestedTranslationMap } from "../Locale.js"
|
||||
import { src } from "../source/_PublicationRef.js"
|
||||
|
||||
export const EquipmentPackage = Entity(import.meta.url, {
|
||||
@@ -24,15 +24,15 @@ export const EquipmentPackage = Entity(import.meta.url, {
|
||||
type: Array(IncludeIdentifier(EquipmentPackageItem), { minItems: 2 }),
|
||||
}),
|
||||
src,
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"EquipmentPackageTranslation",
|
||||
"EquipmentPackage",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The equipment package’s name.",
|
||||
type: String({ minLength: 1 }),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Entity, IncludeIdentifier, Object, Optional, Required, String } from "tsondb/schema/def"
|
||||
import { NestedLocaleMap } from "../../Locale.js"
|
||||
import { NestedTranslationMap } from "../../Locale.js"
|
||||
import { Errata } from "../../source/_Erratum.js"
|
||||
import { src } from "../../source/_PublicationRef.js"
|
||||
import { Cost } from "./_Item.js"
|
||||
@@ -14,9 +14,9 @@ export const Ammunition = Entity(import.meta.url, {
|
||||
type: IncludeIdentifier(Cost),
|
||||
}),
|
||||
src,
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"AmmunitionTranslation",
|
||||
"Ammunition",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The item’s name.",
|
||||
@@ -37,7 +37,7 @@ export const Ammunition = Entity(import.meta.url, {
|
||||
errata: Optional({
|
||||
type: IncludeIdentifier(Errata),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
|
||||
@@ -13,10 +13,10 @@ import {
|
||||
TypeAlias,
|
||||
} from "tsondb/schema/def"
|
||||
import { ArmorIdentifier, ArmorTypeIdentifier } from "../../_Identifier.js"
|
||||
import { NestedLocaleMap } from "../../Locale.js"
|
||||
import { NestedTranslationMap } from "../../Locale.js"
|
||||
import { Errata } from "../../source/_Erratum.js"
|
||||
import { src } from "../../source/_PublicationRef.js"
|
||||
import { Complexity, Cost, Weight } from "./_Item.js"
|
||||
import { ComplexComplexity, Cost, RestrictedTo, Weight } from "./_Item.js"
|
||||
|
||||
export const Armor = Entity(import.meta.url, {
|
||||
name: "Armor",
|
||||
@@ -33,7 +33,7 @@ export const Armor = Entity(import.meta.url, {
|
||||
}),
|
||||
complexity: Optional({
|
||||
comment: "The complexity of crafting the item.",
|
||||
type: IncludeIdentifier(Complexity),
|
||||
type: IncludeIdentifier(ArmorComplexity),
|
||||
}),
|
||||
protection: Required({
|
||||
comment: "The PRO value.",
|
||||
@@ -55,10 +55,15 @@ export const Armor = Entity(import.meta.url, {
|
||||
comment: "Specify if armor is only available for a specific hit zone.",
|
||||
type: IncludeIdentifier(HitZone),
|
||||
}),
|
||||
restrictedTo: Optional({
|
||||
comment:
|
||||
"Define if during character creation this weapon can only be bought by a specific subset of characters.",
|
||||
type: IncludeIdentifier(RestrictedTo),
|
||||
}),
|
||||
src,
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"ArmorTranslation",
|
||||
"Armor",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The item’s name.",
|
||||
@@ -87,7 +92,7 @@ export const Armor = Entity(import.meta.url, {
|
||||
errata: Optional({
|
||||
type: IncludeIdentifier(Errata),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
@@ -117,9 +122,14 @@ export const SecondaryArmor = TypeAlias(import.meta.url, {
|
||||
comment: "Specify if armor is only available for a specific hit zone.",
|
||||
type: IncludeIdentifier(HitZone),
|
||||
}),
|
||||
translations: NestedLocaleMap(
|
||||
restrictedTo: Optional({
|
||||
comment:
|
||||
"Define if during character creation this armor can only be bought by a specific subset of characters.",
|
||||
type: IncludeIdentifier(RestrictedTo),
|
||||
}),
|
||||
translations: NestedTranslationMap(
|
||||
Optional,
|
||||
"SecondaryArmorTranslation",
|
||||
"SecondaryArmor",
|
||||
Object(
|
||||
{
|
||||
advantage: Optional({
|
||||
@@ -131,12 +141,23 @@ export const SecondaryArmor = TypeAlias(import.meta.url, {
|
||||
type: String({ minLength: 1, isMarkdown: true }),
|
||||
}),
|
||||
},
|
||||
{ minProperties: 1 }
|
||||
)
|
||||
{ minProperties: 1 },
|
||||
),
|
||||
),
|
||||
}),
|
||||
})
|
||||
|
||||
const ArmorComplexity = Enum(import.meta.url, {
|
||||
name: "ArmorComplexity",
|
||||
comment: "The complexity of crafting the armor.",
|
||||
values: () => ({
|
||||
Primitive: EnumCase({ type: null }),
|
||||
Simple: EnumCase({ type: null }),
|
||||
Complex: EnumCase({ type: IncludeIdentifier(ComplexComplexity) }),
|
||||
Various: EnumCase({ type: null }),
|
||||
}),
|
||||
})
|
||||
|
||||
export const Protection = TypeAlias(import.meta.url, {
|
||||
name: "Protection",
|
||||
comment: "The PRO value.",
|
||||
|
||||
@@ -1,5 +1,17 @@
|
||||
import { Entity, IncludeIdentifier, Object, Optional, Required, String } from "tsondb/schema/def"
|
||||
import { NestedLocaleMap } from "../../Locale.js"
|
||||
import {
|
||||
Array,
|
||||
Entity,
|
||||
Enum,
|
||||
EnumCase,
|
||||
IncludeIdentifier,
|
||||
Object,
|
||||
Optional,
|
||||
Required,
|
||||
String,
|
||||
TypeAlias,
|
||||
} from "tsondb/schema/def"
|
||||
import { SkillIdentifier } from "../../_Identifier.js"
|
||||
import { NestedTranslationMap } from "../../Locale.js"
|
||||
import { Errata } from "../../source/_Erratum.js"
|
||||
import { src } from "../../source/_PublicationRef.js"
|
||||
import { Complexity, Cost, StructurePoints, Weight } from "./_Item.js"
|
||||
@@ -9,9 +21,13 @@ export const Book = Entity(import.meta.url, {
|
||||
namePlural: "Books",
|
||||
type: () =>
|
||||
Object({
|
||||
types: Required({
|
||||
comment: "The type of book.",
|
||||
type: Array(IncludeIdentifier(BookType), { minItems: 1, uniqueItems: true }),
|
||||
}),
|
||||
cost: Required({
|
||||
comment: "The cost in silverthalers.",
|
||||
type: IncludeIdentifier(Cost),
|
||||
type: IncludeIdentifier(BookCost),
|
||||
}),
|
||||
weight: Optional({
|
||||
comment: "The weight in kg.",
|
||||
@@ -27,9 +43,9 @@ export const Book = Entity(import.meta.url, {
|
||||
type: IncludeIdentifier(StructurePoints),
|
||||
}),
|
||||
src,
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"BookTranslation",
|
||||
"Book",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The item’s name.",
|
||||
@@ -58,8 +74,142 @@ export const Book = Entity(import.meta.url, {
|
||||
errata: Optional({
|
||||
type: IncludeIdentifier(Errata),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
})
|
||||
|
||||
const BookType = Enum(import.meta.url, {
|
||||
name: "BookType",
|
||||
comment: "The type of book.",
|
||||
values: () => ({
|
||||
Mundane: EnumCase({
|
||||
comment: "A mundane book without special rules.",
|
||||
type: IncludeIdentifier(MundaneBookType),
|
||||
}),
|
||||
Magical: EnumCase({
|
||||
comment: "A magical book.",
|
||||
type: null,
|
||||
}),
|
||||
Religious: EnumCase({
|
||||
comment: "A religious book.",
|
||||
type: null,
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const MundaneBookType = Enum(import.meta.url, {
|
||||
name: "MundaneBookType",
|
||||
comment: "The type of mundane book, i.e. the type of writing it contains.",
|
||||
values: () => ({
|
||||
RomanceNovel: EnumCase({
|
||||
comment: "A romance novel.",
|
||||
type: null,
|
||||
}),
|
||||
Poetry: EnumCase({
|
||||
comment: "A piece of poetry.",
|
||||
type: null,
|
||||
}),
|
||||
PoliticalPamphlet: EnumCase({
|
||||
comment: "A political pamphlet.",
|
||||
type: null,
|
||||
}),
|
||||
CrimeStory: EnumCase({
|
||||
comment: "A crime story.",
|
||||
type: null,
|
||||
}),
|
||||
FairyTale: EnumCase({
|
||||
comment: "A fairy tale.",
|
||||
type: null,
|
||||
}),
|
||||
Novel: EnumCase({
|
||||
comment: "A novel.",
|
||||
type: null,
|
||||
}),
|
||||
ProfessionalPublication: EnumCase({
|
||||
comment:
|
||||
"A professional publication about a specific topic, represented by a (knowledge) skill.",
|
||||
type: SkillIdentifier(),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const BookCost = Enum(import.meta.url, {
|
||||
name: "BookCost",
|
||||
comment:
|
||||
"The cost of the book, which may be a single value or multiple values that are defined by specific editions or other defining factors of the book.",
|
||||
values: () => ({
|
||||
Single: EnumCase({
|
||||
comment: "The book only has a single cost description.",
|
||||
type: IncludeIdentifier(BookCostVariant),
|
||||
}),
|
||||
Multiple: EnumCase({
|
||||
comment: "The book’s cost varies by edition or other factors.",
|
||||
type: Array(IncludeIdentifier(BookCostVariant), { minItems: 2 }),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const BookCostVariant = Enum(import.meta.url, {
|
||||
name: "BookCostVariant",
|
||||
comment: "A variant for the book’s cost.",
|
||||
values: () => ({
|
||||
Definite: EnumCase({
|
||||
comment: "A definite cost value.",
|
||||
type: IncludeIdentifier(DefiniteBookCostVariant),
|
||||
}),
|
||||
Indefinite: EnumCase({
|
||||
comment: "An indefinite cost value.",
|
||||
type: IncludeIdentifier(IndefiniteBookCostVariant),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
const DefiniteBookCostVariant = TypeAlias(import.meta.url, {
|
||||
name: "DefiniteBookCostVariant",
|
||||
comment: "A definite cost value for the book, such as a specific cost or cost range.",
|
||||
type: () =>
|
||||
Object({
|
||||
cost: Required({
|
||||
comment: "The cost in silverthalers.",
|
||||
type: IncludeIdentifier(Cost),
|
||||
}),
|
||||
translations: NestedTranslationMap(
|
||||
Optional,
|
||||
"DefiniteBookCostVariant",
|
||||
Object(
|
||||
{
|
||||
label: Optional({
|
||||
comment: "The label for the cost variant, e.g. “Original script”.",
|
||||
type: String({ minLength: 1, isMarkdown: true }),
|
||||
}),
|
||||
},
|
||||
{ minProperties: 1 },
|
||||
),
|
||||
),
|
||||
}),
|
||||
})
|
||||
|
||||
const IndefiniteBookCostVariant = TypeAlias(import.meta.url, {
|
||||
name: "IndefiniteBookCostVariant",
|
||||
comment:
|
||||
"An indefinite cost value for the book, which means it is impossible to narrow the range of the cost value by any numbers.",
|
||||
type: () =>
|
||||
Object({
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"IndefiniteBookCostVariant",
|
||||
Object({
|
||||
label: Optional({
|
||||
comment: "The label for the cost variant, e.g. “Original script”.",
|
||||
type: String({ minLength: 1, isMarkdown: true }),
|
||||
}),
|
||||
description: Required({
|
||||
comment: "The description of the cost variant, e.g. “less”.",
|
||||
type: String({ minLength: 1, isMarkdown: true }),
|
||||
}),
|
||||
}),
|
||||
),
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -8,7 +8,7 @@ import {
|
||||
Required,
|
||||
String,
|
||||
} from "tsondb/schema/def"
|
||||
import { NestedLocaleMap } from "../../Locale.js"
|
||||
import { NestedTranslationMap } from "../../Locale.js"
|
||||
import { AlternativeName } from "../../_AlternativeNames.js"
|
||||
import { Errata } from "../../source/_Erratum.js"
|
||||
import { src } from "../../source/_PublicationRef.js"
|
||||
@@ -40,9 +40,9 @@ export const Elixir = Entity(import.meta.url, {
|
||||
type: IncludeIdentifier(RecipeTradeSecret),
|
||||
}),
|
||||
src,
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"ElixirTranslationTranslation",
|
||||
"ElixirTranslation",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The item’s name.",
|
||||
@@ -76,7 +76,7 @@ export const Elixir = Entity(import.meta.url, {
|
||||
errata: Optional({
|
||||
type: IncludeIdentifier(Errata),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
import { Array, Entity, IncludeIdentifier, Object, Optional, Required } from "tsondb/schema/def"
|
||||
import { BlessedTraditionIdentifier } from "../../_Identifier.js"
|
||||
import { Entity, IncludeIdentifier, Object, Optional, Required } from "tsondb/schema/def"
|
||||
import { src } from "../../source/_PublicationRef.js"
|
||||
import { CombatUse, Cost, DefaultItemTranslations, StructurePoints } from "./_Item.js"
|
||||
import { CombatUse, Cost, DefaultItemTranslations, RestrictedTo, StructurePoints } from "./_Item.js"
|
||||
|
||||
export const EquipmentOfBlessedOnes = Entity(import.meta.url, {
|
||||
name: "EquipmentOfBlessedOnes",
|
||||
@@ -17,9 +16,10 @@ export const EquipmentOfBlessedOnes = Entity(import.meta.url, {
|
||||
"The structure points of the item. Use an array if the item consists of multiple components that have individual structure points.",
|
||||
type: IncludeIdentifier(StructurePoints),
|
||||
}),
|
||||
associated_tradition: Required({
|
||||
comment: "The deity/deities associated with the equipment item.",
|
||||
type: Array(BlessedTraditionIdentifier(), { minItems: 1, uniqueItems: true }),
|
||||
restrictedTo: Optional({
|
||||
comment:
|
||||
"Define if during character creation this item can only be bought by a specific subset of characters.",
|
||||
type: IncludeIdentifier(RestrictedTo),
|
||||
}),
|
||||
combat_use: Optional({
|
||||
comment:
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
import { Entity, IncludeIdentifier, Object, Optional, Required, String } from "tsondb/schema/def"
|
||||
import { NestedLocaleMap } from "../../Locale.js"
|
||||
import { NestedTranslationMap } from "../../Locale.js"
|
||||
import { Errata } from "../../source/_Erratum.js"
|
||||
import { src } from "../../source/_PublicationRef.js"
|
||||
import { Cost } from "./_Item.js"
|
||||
@@ -14,9 +14,9 @@ export const GemOrPreciousStone = Entity(import.meta.url, {
|
||||
type: IncludeIdentifier(Cost),
|
||||
}),
|
||||
src,
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"GemOrPreciousStoneTranslation",
|
||||
"GemOrPreciousStone",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The item’s name.",
|
||||
@@ -41,7 +41,7 @@ export const GemOrPreciousStone = Entity(import.meta.url, {
|
||||
errata: Optional({
|
||||
type: IncludeIdentifier(Errata),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
import { Entity, IncludeIdentifier, Object, Optional, Required } from "tsondb/schema/def"
|
||||
import { Entity, IncludeIdentifier, Object, Optional, Required, String } from "tsondb/schema/def"
|
||||
import { NestedTranslationMap } from "../../Locale.js"
|
||||
import { Errata } from "../../source/_Erratum.js"
|
||||
import { src } from "../../source/_PublicationRef.js"
|
||||
import { Cost, DefaultItemTranslations, Weight } from "./_Item.js"
|
||||
import { CombatUse, Complexity, Cost, StructurePoints, Weight } from "./_Item.js"
|
||||
|
||||
export const MagicalArtifact = Entity(import.meta.url, {
|
||||
name: "MagicalArtifact",
|
||||
@@ -15,8 +17,58 @@ export const MagicalArtifact = Entity(import.meta.url, {
|
||||
comment: "The weight in kg.",
|
||||
type: IncludeIdentifier(Weight),
|
||||
}),
|
||||
complexity: Optional({
|
||||
comment: "The complexity of crafting the item.",
|
||||
type: IncludeIdentifier(Complexity),
|
||||
}),
|
||||
structure_points: Optional({
|
||||
comment:
|
||||
"The structure points of the item. Use an array if the item consists of multiple components that have individual structure points.",
|
||||
type: IncludeIdentifier(StructurePoints),
|
||||
}),
|
||||
combat_use: Optional({
|
||||
comment:
|
||||
"The item can also be used either as an improvised weapon or as an armor, although this is not the primary use case of the item.",
|
||||
type: IncludeIdentifier(CombatUse),
|
||||
}),
|
||||
src,
|
||||
translations: DefaultItemTranslations("MagicalArtifact"),
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
`MagicalArtifact`,
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The item’s name.",
|
||||
type: String({ minLength: 1 }),
|
||||
}),
|
||||
secondary_name: Optional({
|
||||
comment: "An auxiliary name or label of the item, if available.",
|
||||
type: String({ minLength: 1 }),
|
||||
}),
|
||||
description: Optional({
|
||||
comment: "Description text.",
|
||||
type: String({ minLength: 1, isMarkdown: true }),
|
||||
}),
|
||||
note: Optional({
|
||||
comment: "A note.",
|
||||
type: String({ minLength: 1, isMarkdown: true }),
|
||||
}),
|
||||
rules: Optional({
|
||||
comment: "Rules text.",
|
||||
type: String({ minLength: 1, isMarkdown: true }),
|
||||
}),
|
||||
effect: Optional({
|
||||
comment: "Effect text.",
|
||||
type: String({ minLength: 1, isMarkdown: true }),
|
||||
}),
|
||||
cost: Optional({
|
||||
comment: "Cost text.",
|
||||
type: String({ minLength: 1, isMarkdown: true }),
|
||||
}),
|
||||
errata: Optional({
|
||||
type: IncludeIdentifier(Errata),
|
||||
}),
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
})
|
||||
|
||||
@@ -1,6 +1,13 @@
|
||||
import { Entity, IncludeIdentifier, Object, Optional, Required } from "tsondb/schema/def"
|
||||
import { src } from "../../source/_PublicationRef.js"
|
||||
import { CombatUse, Complexity, Cost, DefaultItemTranslations, Weight } from "./_Item.js"
|
||||
import {
|
||||
CombatUse,
|
||||
Complexity,
|
||||
Cost,
|
||||
DefaultItemTranslations,
|
||||
StructurePoints,
|
||||
Weight,
|
||||
} from "./_Item.js"
|
||||
|
||||
export const MusicalInstrument = Entity(import.meta.url, {
|
||||
name: "MusicalInstrument",
|
||||
@@ -19,6 +26,11 @@ export const MusicalInstrument = Entity(import.meta.url, {
|
||||
comment: "The complexity of crafting the item.",
|
||||
type: IncludeIdentifier(Complexity),
|
||||
}),
|
||||
structure_points: Optional({
|
||||
comment:
|
||||
"The structure points of the item. Use an array if the item consists of multiple components that have individual structure points.",
|
||||
type: IncludeIdentifier(StructurePoints),
|
||||
}),
|
||||
combat_use: Optional({
|
||||
comment:
|
||||
"The item can also be used either as an improvised weapon or as an armor, although this is not the primary use case of the item.",
|
||||
|
||||
@@ -0,0 +1,36 @@
|
||||
import { Array, Entity, IncludeIdentifier, Object, Required, String } from "tsondb/schema/def"
|
||||
import { NestedTranslationMap } from "../../Locale.js"
|
||||
import { src } from "../../source/_PublicationRef.js"
|
||||
import { Cost } from "./_Item.js"
|
||||
|
||||
export const Newspaper = Entity(import.meta.url, {
|
||||
name: "Newspaper",
|
||||
namePlural: "Newspapers",
|
||||
type: () =>
|
||||
Object({
|
||||
cost: Required({
|
||||
comment: "The cost in silverthalers.",
|
||||
type: IncludeIdentifier(Cost),
|
||||
}),
|
||||
src,
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"Newspaper",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The newspaper’s name.",
|
||||
type: String({ minLength: 1 }),
|
||||
}),
|
||||
topics: Required({
|
||||
comment: "The topics covered by the newspaper.",
|
||||
type: Array(String({ minLength: 1 }), { minItems: 1, uniqueItems: true }),
|
||||
}),
|
||||
placeOfPublication: Required({
|
||||
comment: "The place the newspaper is published.",
|
||||
type: String({ minLength: 1 }),
|
||||
}),
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
})
|
||||
@@ -15,10 +15,11 @@ import {
|
||||
String,
|
||||
TypeAlias,
|
||||
} from "tsondb/schema/def"
|
||||
import { NestedLocaleMap } from "../../Locale.js"
|
||||
import { NestedTranslationMap } from "../../Locale.js"
|
||||
import { AlternativeName } from "../../_AlternativeNames.js"
|
||||
import { Dice } from "../../_Dice.js"
|
||||
import { Reduceable, Resistance } from "../../_DiseasePoison.js"
|
||||
import { MathOperation } from "../../_MathExpression.js"
|
||||
import { Errata } from "../../source/_Erratum.js"
|
||||
import { src } from "../../source/_PublicationRef.js"
|
||||
import { EffectType, LaboratoryLevel, RecipeTradeSecret } from "./_Herbary.js"
|
||||
@@ -57,9 +58,9 @@ export const Poison = Entity(import.meta.url, {
|
||||
type: IncludeIdentifier(PoisonCost),
|
||||
}),
|
||||
src,
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"PoisonTranslation",
|
||||
"Poison",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The name of the poison.",
|
||||
@@ -80,7 +81,7 @@ export const Poison = Entity(import.meta.url, {
|
||||
errata: Optional({
|
||||
type: IncludeIdentifier(Errata),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
@@ -112,6 +113,7 @@ const PoisonDuration = Enum(import.meta.url, {
|
||||
Instant: EnumCase({ type: null }),
|
||||
Constant: EnumCase({ type: IncludeIdentifier(ConstantPoisonTime) }),
|
||||
DiceBased: EnumCase({ type: IncludeIdentifier(DiceBasedPoisonTime) }),
|
||||
ExpressionBased: EnumCase({ type: IncludeIdentifier(ExpressionBasedPoisonTime) }),
|
||||
Indefinite: EnumCase({ type: IncludeIdentifier(IndefinitePoisonTime) }),
|
||||
}),
|
||||
})
|
||||
@@ -130,15 +132,15 @@ export const NoPoisonCost = TypeAlias(import.meta.url, {
|
||||
name: "NoPoisonCost",
|
||||
type: () =>
|
||||
Object({
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Optional,
|
||||
"NoPoisonCostTranslation",
|
||||
"NoPoisonCost",
|
||||
Object({
|
||||
description: Required({
|
||||
comment: "A description of the cost.",
|
||||
type: String({ minLength: 1, isMarkdown: true }),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
})
|
||||
@@ -147,15 +149,15 @@ export const IndefinitePoisonCost = TypeAlias(import.meta.url, {
|
||||
name: "IndefinitePoisonCost",
|
||||
type: () =>
|
||||
Object({
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"IndefinitePoisonCostTranslation",
|
||||
"IndefinitePoisonCost",
|
||||
Object({
|
||||
description: Required({
|
||||
comment: "A description of the cost.",
|
||||
type: String({ minLength: 1, isMarkdown: true }),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
})
|
||||
@@ -190,6 +192,35 @@ export const DiceBasedPoisonTime = TypeAlias(import.meta.url, {
|
||||
}),
|
||||
})
|
||||
|
||||
export const ExpressionBasedPoisonTime = TypeAlias(import.meta.url, {
|
||||
name: "ExpressionBasedPoisonTime",
|
||||
type: () =>
|
||||
Object({
|
||||
value: Required({
|
||||
type: IncludeIdentifier(ExpressionBasedPoisonTimeValue),
|
||||
}),
|
||||
unit: Required({
|
||||
type: IncludeIdentifier(PoisonTimeUnit),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
export const ExpressionBasedPoisonTimeValue = TypeAlias(import.meta.url, {
|
||||
name: "ExpressionBasedPoisonTimeValue",
|
||||
type: () =>
|
||||
GenIncludeIdentifier(MathOperation, [
|
||||
IncludeIdentifier(ExpressionBasedPoisonTimeExpressionValue),
|
||||
]),
|
||||
})
|
||||
|
||||
export const ExpressionBasedPoisonTimeExpressionValue = Enum(import.meta.url, {
|
||||
name: "ExpressionBasedPoisonTimeExpressionValue",
|
||||
values: () => ({
|
||||
Constant: EnumCase({ type: Integer({ minimum: 1 }) }),
|
||||
Dice: EnumCase({ type: IncludeIdentifier(Dice) }),
|
||||
}),
|
||||
})
|
||||
|
||||
const PoisonTimeUnit = Enum(import.meta.url, {
|
||||
name: "PoisonTimeUnit",
|
||||
values: () => ({
|
||||
@@ -204,9 +235,9 @@ export const IndefinitePoisonTime = TypeAlias(import.meta.url, {
|
||||
name: "IndefinitePoisonTime",
|
||||
type: () =>
|
||||
Object({
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Optional,
|
||||
"IndefinitePoisonTimeTranslation",
|
||||
"IndefinitePoisonTime",
|
||||
Object(
|
||||
{
|
||||
description: Optional({
|
||||
@@ -214,8 +245,8 @@ export const IndefinitePoisonTime = TypeAlias(import.meta.url, {
|
||||
type: String({ minLength: 1, isMarkdown: true }),
|
||||
}),
|
||||
},
|
||||
{ minProperties: 1 }
|
||||
)
|
||||
{ minProperties: 1 },
|
||||
),
|
||||
),
|
||||
}),
|
||||
})
|
||||
@@ -243,6 +274,10 @@ export const AnimalVenom = TypeAlias(import.meta.url, {
|
||||
comment: "If `false`, the poison cannot be extracted.",
|
||||
type: Boolean(),
|
||||
}),
|
||||
trade_secret: Optional({
|
||||
comment: "AP value and prerequisites of the poison’s trade secret.",
|
||||
type: IncludeIdentifier(RecipeTradeSecret),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
@@ -251,9 +286,35 @@ const AnimalVenomLevel = Enum(import.meta.url, {
|
||||
values: () => ({
|
||||
QualityLevel: EnumCase({ type: null }),
|
||||
Constant: EnumCase({ type: Integer({ minimum: 1, maximum: 6 }) }),
|
||||
BySubtype: EnumCase({ type: IncludeIdentifier(AnimalVenomLevelBySubType) }),
|
||||
}),
|
||||
})
|
||||
|
||||
const AnimalVenomLevelBySubType = TypeAlias(import.meta.url, {
|
||||
name: "AnimalVenomLevelBySubType",
|
||||
type: () =>
|
||||
Array(
|
||||
Object({
|
||||
value: Required({
|
||||
comment: "The poison’s level.",
|
||||
type: Integer({ minimum: 1, maximum: 6 }),
|
||||
}),
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"AnimalVenomLevelBySubType",
|
||||
Object({
|
||||
name: Required({
|
||||
comment:
|
||||
"The subtype name. If there are multiple subtypes with the same level, specify them separately.",
|
||||
type: String({ minLength: 1 }),
|
||||
}),
|
||||
}),
|
||||
),
|
||||
}),
|
||||
{ minItems: 1 },
|
||||
),
|
||||
})
|
||||
|
||||
export const AlchemicalPoison = TypeAlias(import.meta.url, {
|
||||
name: "AlchemicalPoison",
|
||||
type: () =>
|
||||
@@ -282,9 +343,9 @@ export const AlchemicalPoison = TypeAlias(import.meta.url, {
|
||||
comment: "Additional information if the poison is an intoxicant.",
|
||||
type: IncludeIdentifier(Intoxicant),
|
||||
}),
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"AlchemicalPoisonTranslation",
|
||||
"AlchemicalPoison",
|
||||
Object({
|
||||
typical_ingredients: Required({
|
||||
comment: "A list of typical ingredients.",
|
||||
@@ -294,7 +355,7 @@ export const AlchemicalPoison = TypeAlias(import.meta.url, {
|
||||
comment: "Prerequsites for the brewing process, if any.",
|
||||
type: String({ minLength: 1, isMarkdown: true }),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
})
|
||||
@@ -337,9 +398,9 @@ export const DemonicPoison = TypeAlias(import.meta.url, {
|
||||
comment: "The poison’s level.",
|
||||
type: IncludeIdentifier(DemonicPoisonLevel),
|
||||
}),
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Optional,
|
||||
"DemonicPoisonTranslation",
|
||||
"DemonicPoison",
|
||||
Object(
|
||||
{
|
||||
note: Optional({
|
||||
@@ -347,8 +408,8 @@ export const DemonicPoison = TypeAlias(import.meta.url, {
|
||||
type: String({ minLength: 1, isMarkdown: true }),
|
||||
}),
|
||||
},
|
||||
{ minProperties: 1 }
|
||||
)
|
||||
{ minProperties: 1 },
|
||||
),
|
||||
),
|
||||
}),
|
||||
})
|
||||
@@ -403,9 +464,9 @@ export const Intoxicant = TypeAlias(import.meta.url, {
|
||||
"The chance of getting addicted after an ingestion in addition to the maximum interval at which it, while addicted, must be ingested to not suffer from withdrawal symptoms.",
|
||||
type: IncludeIdentifier(IntoxicantAddiction),
|
||||
}),
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"IntoxicantTranslation",
|
||||
"Intoxicant",
|
||||
Object({
|
||||
ingestion: Required({
|
||||
comment: "How to ingest the intoxicant.",
|
||||
@@ -416,7 +477,7 @@ export const Intoxicant = TypeAlias(import.meta.url, {
|
||||
"The intoxicants side effects that always happen, no matter whether the intoxicant has the default or the reduced effect.",
|
||||
type: String({ minLength: 1, isMarkdown: true }),
|
||||
}),
|
||||
overdose: Required({
|
||||
overdose: Optional({
|
||||
comment:
|
||||
"What happens if the intoxicant has been overdosed, that is, it has been ingested another time within the duration.",
|
||||
type: String({ minLength: 1, isMarkdown: true }),
|
||||
@@ -425,7 +486,7 @@ export const Intoxicant = TypeAlias(import.meta.url, {
|
||||
comment: "Special information about the intoxicant.",
|
||||
type: String({ minLength: 1, isMarkdown: true }),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
})
|
||||
@@ -458,6 +519,17 @@ export const IntoxicantAddiction = TypeAlias(import.meta.url, {
|
||||
"The maximum interval at which it, while addicted, must be ingested to not suffer from withdrawal symptoms.",
|
||||
type: IncludeIdentifier(IntoxicantAddictionInterval),
|
||||
}),
|
||||
translations: NestedTranslationMap(
|
||||
Optional,
|
||||
"IntoxicantAddiction",
|
||||
Object({
|
||||
chance: Required({
|
||||
comment:
|
||||
"The chance of getting addicted after an ingestion. Use if and only if the plain constant percent is not sufficient.",
|
||||
type: String({ minLength: 1, isMarkdown: true }),
|
||||
}),
|
||||
}),
|
||||
),
|
||||
}),
|
||||
})
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
import { Entity, IncludeIdentifier, Object, Required } from "tsondb/schema/def"
|
||||
import { Entity, IncludeIdentifier, Object, Optional, Required } from "tsondb/schema/def"
|
||||
import { src } from "../../source/_PublicationRef.js"
|
||||
import { Cost, DefaultItemTranslations } from "./_Item.js"
|
||||
import { Complexity, Cost, DefaultItemTranslations, Weight } from "./_Item.js"
|
||||
|
||||
export const Vehicle = Entity(import.meta.url, {
|
||||
name: "Vehicle",
|
||||
@@ -11,6 +11,14 @@ export const Vehicle = Entity(import.meta.url, {
|
||||
comment: "The cost in silverthalers.",
|
||||
type: IncludeIdentifier(Cost),
|
||||
}),
|
||||
weight: Optional({
|
||||
comment: "The weight in kg.",
|
||||
type: IncludeIdentifier(Weight),
|
||||
}),
|
||||
complexity: Optional({
|
||||
comment: "The complexity of crafting the item.",
|
||||
type: IncludeIdentifier(Complexity),
|
||||
}),
|
||||
src,
|
||||
translations: DefaultItemTranslations("Vehicle"),
|
||||
}),
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
import {
|
||||
Array,
|
||||
Entity,
|
||||
Enum,
|
||||
EnumCase,
|
||||
IncludeIdentifier,
|
||||
NestedEntityMap,
|
||||
Object,
|
||||
@@ -11,17 +8,11 @@ import {
|
||||
String,
|
||||
TypeAlias,
|
||||
} from "tsondb/schema/def"
|
||||
import {
|
||||
BlessedTraditionIdentifier,
|
||||
CultureIdentifier,
|
||||
MagicalTraditionIdentifier,
|
||||
RaceIdentifier,
|
||||
} from "../../_Identifier.js"
|
||||
import { CloseCombatTechnique, RangedCombatTechnique } from "../../CombatTechnique.js"
|
||||
import { NestedLocaleMap } from "../../Locale.js"
|
||||
import { NestedTranslationMap } from "../../Locale.js"
|
||||
import { Errata } from "../../source/_Erratum.js"
|
||||
import { src } from "../../source/_PublicationRef.js"
|
||||
import { Complexity, Cost, StructurePoints, Weight } from "./_Item.js"
|
||||
import { Complexity, Cost, RestrictedTo, StructurePoints, Weight } from "./_Item.js"
|
||||
import { MeleeWeapon } from "./_MeleeWeapon.js"
|
||||
import { RangedWeapon } from "./_RangedWeapon.js"
|
||||
|
||||
@@ -52,6 +43,7 @@ export const Weapon = Entity(import.meta.url, {
|
||||
"A list of stat blocks for each close combat technique this weapon can be used with.",
|
||||
type: NestedEntityMap({
|
||||
name: "MeleeWeaponUse",
|
||||
namePlural: "MeleeWeaponUses",
|
||||
secondaryEntity: CloseCombatTechnique,
|
||||
type: IncludeIdentifier(MeleeWeapon),
|
||||
}),
|
||||
@@ -61,28 +53,20 @@ export const Weapon = Entity(import.meta.url, {
|
||||
"A list of stat blocks for each ranged combat technique this weapon can be used with.",
|
||||
type: NestedEntityMap({
|
||||
name: "RangedWeaponUse",
|
||||
namePlural: "RangedWeaponUses",
|
||||
secondaryEntity: RangedCombatTechnique,
|
||||
type: IncludeIdentifier(RangedWeapon),
|
||||
}),
|
||||
}),
|
||||
sanctified_by: Optional({
|
||||
comment: "If the weapon is sanctified by a god and thus restricted to it's Blessed Ones.",
|
||||
type: IncludeIdentifier(SanctifiedBy),
|
||||
}),
|
||||
restricted_to_cultures: Optional({
|
||||
restrictedTo: Optional({
|
||||
comment:
|
||||
"Define if during character creation this weapon can only be bought by characters of a specific race or culture.",
|
||||
type: IncludeIdentifier(RestrictedToCultures),
|
||||
}),
|
||||
restricted_to_traditions: Optional({
|
||||
comment:
|
||||
"Define if during character creation this weapon can only be bought by characters of specific magical or blessed traditions.",
|
||||
type: Array(MagicalTraditionIdentifier(), { minItems: 1 }),
|
||||
"Define if during character creation this weapon can only be bought by a specific subset of characters.",
|
||||
type: IncludeIdentifier(RestrictedTo),
|
||||
}),
|
||||
src,
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Required,
|
||||
"WeaponTranslation",
|
||||
"Weapon",
|
||||
Object({
|
||||
name: Required({
|
||||
comment: "The item’s name.",
|
||||
@@ -111,7 +95,7 @@ export const Weapon = Entity(import.meta.url, {
|
||||
errata: Optional({
|
||||
type: IncludeIdentifier(Errata),
|
||||
}),
|
||||
})
|
||||
}),
|
||||
),
|
||||
}),
|
||||
displayName: {},
|
||||
@@ -126,6 +110,7 @@ export const ImprovisedWeapon = TypeAlias(import.meta.url, {
|
||||
"A list of stat blocks for each close combat technique this weapon can be used with.",
|
||||
type: NestedEntityMap({
|
||||
name: "ImprovisedMeleeWeaponUse",
|
||||
namePlural: "ImprovisedMeleeWeaponUses",
|
||||
secondaryEntity: CloseCombatTechnique,
|
||||
type: IncludeIdentifier(MeleeWeapon),
|
||||
}),
|
||||
@@ -135,27 +120,19 @@ export const ImprovisedWeapon = TypeAlias(import.meta.url, {
|
||||
"A list of stat blocks for each ranged combat technique this weapon can be used with.",
|
||||
type: NestedEntityMap({
|
||||
name: "ImprovisedRangedWeaponUse",
|
||||
namePlural: "ImprovisedRangedWeaponUses",
|
||||
secondaryEntity: RangedCombatTechnique,
|
||||
type: IncludeIdentifier(RangedWeapon),
|
||||
}),
|
||||
}),
|
||||
sanctified_by: Optional({
|
||||
comment: "If the weapon is sanctified by a god and thus restricted to it's Blessed Ones.",
|
||||
type: IncludeIdentifier(SanctifiedBy),
|
||||
}),
|
||||
restricted_to_cultures: Optional({
|
||||
restrictedTo: Optional({
|
||||
comment:
|
||||
"Define if during character creation this weapon can only be bought by characters of a specific race or culture.",
|
||||
type: IncludeIdentifier(RestrictedToCultures),
|
||||
"Define if during character creation this weapon can only be bought by a specific subset of characters.",
|
||||
type: IncludeIdentifier(RestrictedTo),
|
||||
}),
|
||||
restricted_to_traditions: Optional({
|
||||
comment:
|
||||
"Define if during character creation this weapon can only be bought by characters of specific magical or blessed traditions.",
|
||||
type: Array(MagicalTraditionIdentifier(), { minItems: 1 }),
|
||||
}),
|
||||
translations: NestedLocaleMap(
|
||||
translations: NestedTranslationMap(
|
||||
Optional,
|
||||
"ImprovisedWeaponTranslation",
|
||||
"ImprovisedWeapon",
|
||||
Object(
|
||||
{
|
||||
advantage: Optional({
|
||||
@@ -167,34 +144,8 @@ export const ImprovisedWeapon = TypeAlias(import.meta.url, {
|
||||
type: String({ minLength: 1, isMarkdown: true }),
|
||||
}),
|
||||
},
|
||||
{ minProperties: 1 }
|
||||
)
|
||||
{ minProperties: 1 },
|
||||
),
|
||||
),
|
||||
}),
|
||||
})
|
||||
|
||||
export const SanctifiedBy = TypeAlias(import.meta.url, {
|
||||
name: "SanctifiedBy",
|
||||
comment: "If the weapon is sanctified by a god and thus restricted to it's Blessed Ones.",
|
||||
type: () => Array(BlessedTraditionIdentifier(), { minItems: 1 }),
|
||||
})
|
||||
|
||||
const RestrictedToCultures = Enum(import.meta.url, {
|
||||
name: "RestrictedToCultures",
|
||||
comment:
|
||||
"Define if during character creation this weapon can only be bought by characters of a specific race or culture.",
|
||||
values: () => ({
|
||||
CulturesOfRace: EnumCase({ type: RaceIdentifier() }),
|
||||
Cultures: EnumCase({ type: IncludeIdentifier(RestrictedToSpecificCultures) }),
|
||||
}),
|
||||
})
|
||||
|
||||
const RestrictedToSpecificCultures = TypeAlias(import.meta.url, {
|
||||
name: "RestrictedToSpecificCultures",
|
||||
type: () =>
|
||||
Object({
|
||||
list: Required({
|
||||
type: Array(CultureIdentifier(), { minItems: 1 }),
|
||||
}),
|
||||
}),
|
||||
})
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user