@datasworn-community/build-tools 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +17 -0
- package/dist/cli/args.d.ts +3 -0
- package/dist/cli/args.d.ts.map +1 -0
- package/dist/cli/args.js +10 -0
- package/dist/cli/args.js.map +1 -0
- package/dist/cli/datasworn-build.d.ts +3 -0
- package/dist/cli/datasworn-build.d.ts.map +1 -0
- package/dist/cli/datasworn-build.js +33 -0
- package/dist/cli/datasworn-build.js.map +1 -0
- package/dist/cli/datasworn-migrate.d.ts +3 -0
- package/dist/cli/datasworn-migrate.d.ts.map +1 -0
- package/dist/cli/datasworn-migrate.js +26 -0
- package/dist/cli/datasworn-migrate.js.map +1 -0
- package/dist/config.d.ts +3 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.js +10 -0
- package/dist/config.js.map +1 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -0
- package/dist/rules-package-builder.d.ts +22 -0
- package/dist/rules-package-builder.d.ts.map +1 -0
- package/dist/rules-package-builder.js +84 -0
- package/dist/rules-package-builder.js.map +1 -0
- package/dist/schema.d.ts +8 -0
- package/dist/schema.d.ts.map +1 -0
- package/dist/schema.js +18 -0
- package/dist/schema.js.map +1 -0
- package/dist/validators.d.ts +8 -0
- package/dist/validators.d.ts.map +1 -0
- package/dist/validators.js +35 -0
- package/dist/validators.js.map +1 -0
- package/package.json +60 -0
- package/schema-source/schema/Assets.ts +66 -0
- package/schema-source/schema/Atlas.ts +20 -0
- package/schema-source/schema/Defs.ts +126 -0
- package/schema-source/schema/DelveSites.ts +3 -0
- package/schema-source/schema/Entities.ts +1 -0
- package/schema-source/schema/Generic.ts +18 -0
- package/schema-source/schema/Moves.ts +1 -0
- package/schema-source/schema/Npcs.ts +80 -0
- package/schema-source/schema/Oracles.ts +20 -0
- package/schema-source/schema/Rarities.ts +36 -0
- package/schema-source/schema/Root.ts +94 -0
- package/schema-source/schema/Rules.ts +61 -0
- package/schema-source/schema/RulesPackages.ts +135 -0
- package/schema-source/schema/Symbols.ts +30 -0
- package/schema-source/schema/Truths.ts +53 -0
- package/schema-source/schema/Utils.ts +21 -0
- package/schema-source/schema/assets/Ability.ts +79 -0
- package/schema-source/schema/assets/Enhancement.ts +62 -0
- package/schema-source/schema/assets/Fields.ts +285 -0
- package/schema-source/schema/assets/common.ts +29 -0
- package/schema-source/schema/common/Fields.ts +327 -0
- package/schema-source/schema/common/Id.ts +64 -0
- package/schema-source/schema/common/Inputs.ts +374 -0
- package/schema-source/schema/common/Metadata.ts +142 -0
- package/schema-source/schema/common/Player.ts +17 -0
- package/schema-source/schema/common/Progress.ts +63 -0
- package/schema-source/schema/common/Range.ts +53 -0
- package/schema-source/schema/common/RollableValues.ts +152 -0
- package/schema-source/schema/common/Rolls.ts +108 -0
- package/schema-source/schema/common/Text.ts +95 -0
- package/schema-source/schema/common/index.ts +16 -0
- package/schema-source/schema/delve/DelveSite.ts +121 -0
- package/schema-source/schema/delve/DelveSiteDomain.ts +136 -0
- package/schema-source/schema/delve/DelveSiteTheme.ts +97 -0
- package/schema-source/schema/delve/common.ts +32 -0
- package/schema-source/schema/entities/EntityPrompt.ts +13 -0
- package/schema-source/schema/entities/index.ts +1 -0
- package/schema-source/schema/generic/CollectableNode.ts +75 -0
- package/schema-source/schema/generic/CollectionNode.ts +145 -0
- package/schema-source/schema/generic/Dictionary.ts +39 -0
- package/schema-source/schema/generic/EmbedOnlyNode.ts +19 -0
- package/schema-source/schema/generic/Enhance.ts +38 -0
- package/schema-source/schema/generic/IdNode.ts +40 -0
- package/schema-source/schema/generic/Mixin.ts +11 -0
- package/schema-source/schema/generic/NonCollectableNode.ts +38 -0
- package/schema-source/schema/generic/PrimaryTypeNode.ts +115 -0
- package/schema-source/schema/generic/SourcedNode.ts +51 -0
- package/schema-source/schema/index.ts +23 -0
- package/schema-source/schema/moves/ActionRoll.ts +152 -0
- package/schema-source/schema/moves/EmbeddedMove.ts +24 -0
- package/schema-source/schema/moves/Move.ts +26 -0
- package/schema-source/schema/moves/MoveCategory.ts +6 -0
- package/schema-source/schema/moves/MoveEnhancement.ts +29 -0
- package/schema-source/schema/moves/ProgressRoll.ts +184 -0
- package/schema-source/schema/moves/Trigger.ts +193 -0
- package/schema-source/schema/moves/common.ts +136 -0
- package/schema-source/schema/moves/index.ts +13 -0
- package/schema-source/schema/moves/utils.ts +132 -0
- package/schema-source/schema/object_type/ObjectType.ts +23 -0
- package/schema-source/schema/oracles/EmbeddedOracleRollable.ts +28 -0
- package/schema-source/schema/oracles/OracleCollection.ts +140 -0
- package/schema-source/schema/oracles/OracleRollable.ts +207 -0
- package/schema-source/schema/oracles/Table.ts +94 -0
- package/schema-source/schema/oracles/TableRow.ts +141 -0
- package/schema-source/schema/root/NiceSchema.ts +15 -0
- package/schema-source/schema/root/Root.ts +41 -0
- package/schema-source/schema/root/SchemaTransform.ts +89 -0
- package/schema-source/schema/root/SourceData.ts +151 -0
- package/schema-source/schema/rules/ConditionMeterRule.ts +25 -0
- package/schema-source/schema/rules/ImpactRule.ts +54 -0
- package/schema-source/schema/rules/SpecialTrackRule.ts +29 -0
- package/schema-source/schema/rules/StatRule.ts +23 -0
- package/schema-source/schema/rules/TagRule.ts +176 -0
- package/schema-source/schema/rules/TagSchema.ts +161 -0
- package/schema-source/schema/tags/canonicalTags.ts +29 -0
- package/schema-source/schema/tools/build/index.ts +48 -0
- package/schema-source/schema/tools/i18n/index.ts +9 -0
- package/schema-source/schema/utils/Computed.ts +82 -0
- package/schema-source/schema/utils/DiscriminatedUnion.ts +255 -0
- package/schema-source/schema/utils/EmbeddedNode.ts +172 -0
- package/schema-source/schema/utils/ExtractLiteralFromEnum.ts +22 -0
- package/schema-source/schema/utils/FlatIntersect.ts +107 -0
- package/schema-source/schema/utils/IfThen.ts +50 -0
- package/schema-source/schema/utils/MergeEnumSchemas.ts +16 -0
- package/schema-source/schema/utils/NonNullable.ts +6 -0
- package/schema-source/schema/utils/Nullable.ts +29 -0
- package/schema-source/schema/utils/ObjectLiteral.ts +16 -0
- package/schema-source/schema/utils/ObjectProperties.ts +4 -0
- package/schema-source/schema/utils/OmitMeta.ts +22 -0
- package/schema-source/schema/utils/OmitOptional.ts +27 -0
- package/schema-source/schema/utils/PartialExcept.ts +26 -0
- package/schema-source/schema/utils/SetNullable.ts +44 -0
- package/schema-source/schema/utils/SetOptional.ts +48 -0
- package/schema-source/schema/utils/UnionEnum.ts +82 -0
- package/schema-source/schema/utils/UnionEnumFromRecord.ts +36 -0
- package/schema-source/schema/utils/UnionOneOf.ts +80 -0
- package/schema-source/schema/utils/generateRulesetSchemas.ts +204 -0
- package/schema-source/schema/utils/string.ts +15 -0
- package/schema-source/schema/utils/typebox.ts +141 -0
- package/schema-source/scripts/const.ts +55 -0
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { type TSchema, Type } from '@sinclair/typebox'
|
|
2
|
+
import { pickBy } from 'lodash-es'
|
|
3
|
+
import { DefsKey } from '../scripts/const.js'
|
|
4
|
+
import Log from '../scripts/utils/Log.js'
|
|
5
|
+
import * as Assets from './Assets.js'
|
|
6
|
+
import * as Atlas from './Atlas.js'
|
|
7
|
+
import Id from './common/Id.js'
|
|
8
|
+
import * as Metadata from './common/Metadata.js'
|
|
9
|
+
import * as Player from './common/Player.js'
|
|
10
|
+
import * as Progress from './common/Progress.js'
|
|
11
|
+
import { DiceRange } from './common/Range.js'
|
|
12
|
+
import * as RollableValues from './common/RollableValues.js'
|
|
13
|
+
import * as Rolls from './common/Rolls.js'
|
|
14
|
+
import * as Text from './common/Text.js'
|
|
15
|
+
import * as DelveSites from './DelveSites.js'
|
|
16
|
+
import * as Entities from './Entities.js'
|
|
17
|
+
import * as Moves from './Moves.js'
|
|
18
|
+
import * as Npcs from './Npcs.js'
|
|
19
|
+
import * as Oracles from './Oracles.js'
|
|
20
|
+
import * as Rarities from './Rarities.js'
|
|
21
|
+
import * as Rules from './Rules.js'
|
|
22
|
+
import { Expansion, Ruleset, RulesPackage } from './RulesPackages.js'
|
|
23
|
+
import { isValidTagSchema } from './rules/TagSchema.js'
|
|
24
|
+
import * as Truths from './Truths.js'
|
|
25
|
+
import { UnionEnum } from './Utils.js'
|
|
26
|
+
|
|
27
|
+
function validateSchemaDefinitions(defs: Record<string, TSchema>) {
|
|
28
|
+
const usedRefs = new Set<string>()
|
|
29
|
+
const availableRefs = new Set<string>()
|
|
30
|
+
|
|
31
|
+
// lazy way to iterate over all values
|
|
32
|
+
void JSON.stringify(defs, (k, v) => {
|
|
33
|
+
if (k === '$ref' && typeof v === 'string') usedRefs.add(v)
|
|
34
|
+
if (k === '$id' && typeof v === 'string') availableRefs.add(v)
|
|
35
|
+
return v
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
const invalidPointers = new Set<string>()
|
|
39
|
+
const unusedDefinitions = new Set<string>()
|
|
40
|
+
|
|
41
|
+
const allPointers = new Set<string>([...usedRefs, ...availableRefs])
|
|
42
|
+
|
|
43
|
+
for (const pointer of allPointers) {
|
|
44
|
+
if (pointer.startsWith('http')) continue
|
|
45
|
+
if (!usedRefs.has(pointer)) unusedDefinitions.add(pointer)
|
|
46
|
+
if (!availableRefs.has(pointer)) invalidPointers.add(pointer)
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// console.log('unusedDefinitions', unusedDefinitions)
|
|
50
|
+
if (invalidPointers.size > 0)
|
|
51
|
+
throw new Error(
|
|
52
|
+
`Missing referenced definitions: ${Array.from(invalidPointers).join(', ')}`
|
|
53
|
+
)
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const defsBase = pickBy(
|
|
57
|
+
{
|
|
58
|
+
RulesPackage,
|
|
59
|
+
Ruleset,
|
|
60
|
+
Expansion,
|
|
61
|
+
...Id,
|
|
62
|
+
...Metadata,
|
|
63
|
+
...Text,
|
|
64
|
+
...Rules,
|
|
65
|
+
...Progress,
|
|
66
|
+
...RollableValues,
|
|
67
|
+
...Npcs,
|
|
68
|
+
...Rolls,
|
|
69
|
+
DiceRange,
|
|
70
|
+
...Entities,
|
|
71
|
+
...Oracles,
|
|
72
|
+
...Moves,
|
|
73
|
+
...Assets,
|
|
74
|
+
...Truths,
|
|
75
|
+
...Atlas,
|
|
76
|
+
...Player,
|
|
77
|
+
...Rarities,
|
|
78
|
+
...DelveSites
|
|
79
|
+
} as Record<string, TSchema>,
|
|
80
|
+
(schema, key) => {
|
|
81
|
+
if (typeof schema !== 'object') return false
|
|
82
|
+
|
|
83
|
+
if (typeof schema.$id !== 'string') {
|
|
84
|
+
Log.warn(`Schema in #/${DefsKey}, but doesn't have an ID?`)
|
|
85
|
+
Log.warn(JSON.stringify(schema))
|
|
86
|
+
return false
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
if (key !== schema.$id)
|
|
90
|
+
Log.warn(`Schema has $id ${schema.$id}, but its key is ${key}`)
|
|
91
|
+
|
|
92
|
+
return true
|
|
93
|
+
}
|
|
94
|
+
)
|
|
95
|
+
|
|
96
|
+
validateSchemaDefinitions(defsBase)
|
|
97
|
+
|
|
98
|
+
export type Defs = Record<string, TSchema>
|
|
99
|
+
const entries: [string, TSchema][] = Object.values<TSchema>(defsBase)
|
|
100
|
+
.filter((entry): entry is TSchema & { $id: string } => entry.$id != null)
|
|
101
|
+
.map((entry) => [entry.$id, entry])
|
|
102
|
+
|
|
103
|
+
const Defs: Defs = Object.fromEntries(
|
|
104
|
+
entries.sort(([a], [b]) => a.localeCompare(b))
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
// special: update the '$ref' subschema of SafeValue schema so that it's an enum of all Datasworn schemata references that are valid for use in tags.
|
|
108
|
+
|
|
109
|
+
const _schemaToUpdate = Rules.TagSchema
|
|
110
|
+
|
|
111
|
+
const enumValues: string[] = []
|
|
112
|
+
|
|
113
|
+
for (const k in Defs) {
|
|
114
|
+
const def = Defs[k]
|
|
115
|
+
if (def?.title?.includes('Tag') || def?.$id?.includes('Tag')) continue
|
|
116
|
+
if (isValidTagSchema(def)) enumValues.push(`#/${DefsKey}/${k}`)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
const union = Rules.TagSchema.allOf[0]
|
|
120
|
+
const _dataswornRef = union.anyOf[0]
|
|
121
|
+
union.anyOf[0] = Type.Object(
|
|
122
|
+
{ $ref: UnionEnum(enumValues) },
|
|
123
|
+
{ additionalProperties: true }
|
|
124
|
+
)
|
|
125
|
+
|
|
126
|
+
export default Defs
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './entities/index.js'
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
export * from './generic/CollectableNode.js'
|
|
2
|
+
export * from './generic/CollectionNode.js'
|
|
3
|
+
export * from './generic/Dictionary.js'
|
|
4
|
+
export * from './generic/Enhance.js'
|
|
5
|
+
export * from './generic/IdNode.js'
|
|
6
|
+
export * from './generic/Mixin.js'
|
|
7
|
+
export * from './generic/NonCollectableNode.js'
|
|
8
|
+
export * from './generic/SourcedNode.js'
|
|
9
|
+
|
|
10
|
+
// Utility type for runtime collection handling
|
|
11
|
+
import type { SourcedNode } from './generic/SourcedNode.js'
|
|
12
|
+
export type Collection<TContent = any> = {
|
|
13
|
+
contents: Record<string, TContent>
|
|
14
|
+
collections?: Record<string, Collection<any>>
|
|
15
|
+
} & SourcedNode
|
|
16
|
+
|
|
17
|
+
// Utility type for collectable items
|
|
18
|
+
export type Collectable<TContent = unknown> = TContent & SourcedNode
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './moves/index.js'
|
|
@@ -0,0 +1,80 @@
|
|
|
1
|
+
import { type Static, Type } from '@sinclair/typebox'
|
|
2
|
+
import * as Progress from './common/Progress.js'
|
|
3
|
+
import * as Text from './common/Text.js'
|
|
4
|
+
import * as Generic from './Generic.js'
|
|
5
|
+
import { EmbedOnlyNode } from './generic/EmbedOnlyNode.js'
|
|
6
|
+
import { Namespace } from './Symbols.js'
|
|
7
|
+
import { FlatIntersect } from './utils/FlatIntersect.js'
|
|
8
|
+
|
|
9
|
+
const ns = 'Npcs'
|
|
10
|
+
|
|
11
|
+
export const NpcNature = Type.Ref(Text.Label, {
|
|
12
|
+
description:
|
|
13
|
+
"A localized category label describing the nature of this NPC.\n\nIn Ironsworn classic, this is probably the singular form of the parent collection's name.\n\nFor Starforged, see the table on p. 258 for examples.",
|
|
14
|
+
examples: [
|
|
15
|
+
// classic natures
|
|
16
|
+
'Ironlander',
|
|
17
|
+
'Firstborn',
|
|
18
|
+
'Animal',
|
|
19
|
+
'Beast',
|
|
20
|
+
'Horror',
|
|
21
|
+
'Anomaly',
|
|
22
|
+
// Starforged natures
|
|
23
|
+
'Creature',
|
|
24
|
+
// 'Horror',
|
|
25
|
+
'Human',
|
|
26
|
+
'Machine',
|
|
27
|
+
'Monster',
|
|
28
|
+
'Vehicle'
|
|
29
|
+
],
|
|
30
|
+
$id: 'NpcNature',
|
|
31
|
+
[Namespace]: ns
|
|
32
|
+
})
|
|
33
|
+
export type NpcNature = Static<typeof NpcNature>
|
|
34
|
+
|
|
35
|
+
const NpcMixin = FlatIntersect([
|
|
36
|
+
Generic.Cyclopedia,
|
|
37
|
+
Type.Object({
|
|
38
|
+
rank: Type.Ref(Progress.ChallengeRank, {
|
|
39
|
+
description: 'The suggested challenge rank for this NPC.'
|
|
40
|
+
}),
|
|
41
|
+
nature: Type.Ref(NpcNature),
|
|
42
|
+
drives: Type.Array(Type.Ref(Text.MarkdownString)),
|
|
43
|
+
tactics: Type.Array(Type.Ref(Text.MarkdownString))
|
|
44
|
+
})
|
|
45
|
+
])
|
|
46
|
+
|
|
47
|
+
export const NpcVariant = EmbedOnlyNode(
|
|
48
|
+
Type.Pick(NpcMixin, ['name', 'rank', 'nature', 'summary', 'description']),
|
|
49
|
+
'variant',
|
|
50
|
+
'npc'
|
|
51
|
+
)
|
|
52
|
+
|
|
53
|
+
export type NpcVariant = Static<typeof NpcVariant>
|
|
54
|
+
|
|
55
|
+
export const Npc = Generic.CollectableNode(
|
|
56
|
+
FlatIntersect([
|
|
57
|
+
NpcMixin,
|
|
58
|
+
Type.Object({
|
|
59
|
+
variants: Generic.Dictionary(Type.Ref(NpcVariant))
|
|
60
|
+
})
|
|
61
|
+
]),
|
|
62
|
+
'npc',
|
|
63
|
+
{
|
|
64
|
+
[Namespace]: ns,
|
|
65
|
+
description:
|
|
66
|
+
'A non-player character entry, similar to those in Chapter 5 of the Ironsworn Rulebook, or Chapter 4 of Starforged.'
|
|
67
|
+
}
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
export type Npc = Static<typeof Npc>
|
|
71
|
+
|
|
72
|
+
export const NpcCollection = Generic.CollectionNode(
|
|
73
|
+
Type.Object({}),
|
|
74
|
+
'npc_collection',
|
|
75
|
+
{
|
|
76
|
+
[Namespace]: ns
|
|
77
|
+
}
|
|
78
|
+
)
|
|
79
|
+
export type NpcCollection = Static<typeof NpcCollection>
|
|
80
|
+
export type TNpcCollection = typeof NpcCollection
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// constituent types
|
|
2
|
+
|
|
3
|
+
// derived type
|
|
4
|
+
export * from './oracles/EmbeddedOracleRollable.js'
|
|
5
|
+
export {
|
|
6
|
+
OracleCollection,
|
|
7
|
+
OracleTableSharedRolls,
|
|
8
|
+
OracleTableSharedText,
|
|
9
|
+
OracleTableSharedText2,
|
|
10
|
+
OracleTableSharedText3,
|
|
11
|
+
OracleTablesCollection
|
|
12
|
+
} from './oracles/OracleCollection.js'
|
|
13
|
+
// primary type
|
|
14
|
+
export * from './oracles/OracleRollable.js'
|
|
15
|
+
export {
|
|
16
|
+
OracleRollableRow,
|
|
17
|
+
OracleRollableRowText,
|
|
18
|
+
OracleRollableRowText2,
|
|
19
|
+
OracleRollableRowText3
|
|
20
|
+
} from './oracles/TableRow.js'
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type Static, Type } from '@sinclair/typebox'
|
|
2
|
+
import Id from './common/Id.js'
|
|
3
|
+
import { MarkdownString } from './common/Text.js'
|
|
4
|
+
import { NonCollectableNode } from './Generic.js'
|
|
5
|
+
import { setSourceDataSchema } from './Utils.js'
|
|
6
|
+
|
|
7
|
+
export const Rarity = setSourceDataSchema(
|
|
8
|
+
NonCollectableNode(
|
|
9
|
+
Type.Object({
|
|
10
|
+
asset: Type.Ref(Id.AssetId, {
|
|
11
|
+
description: 'The asset augmented by this rarity.'
|
|
12
|
+
}),
|
|
13
|
+
description: Type.Ref(MarkdownString, {
|
|
14
|
+
description: 'A description of this rarity.'
|
|
15
|
+
}),
|
|
16
|
+
xp_cost: Type.Integer({
|
|
17
|
+
minimum: 3,
|
|
18
|
+
maximum: 5,
|
|
19
|
+
default: 3,
|
|
20
|
+
description: `From Ironsworn: Delve, p. 174:
|
|
21
|
+
|
|
22
|
+
Some assets will bring a rarity into play more often than others, so the experience point cost for a rarity will vary by the linked asset. These costs are shown in the tables on page 175.
|
|
23
|
+
|
|
24
|
+
If you are playing solo, and aren’t concerned with the relative balance of rarity abilities, you can ignore these variable costs. If so, spend 3 experience points to purchase a rarity.`
|
|
25
|
+
})
|
|
26
|
+
}),
|
|
27
|
+
'rarity',
|
|
28
|
+
{
|
|
29
|
+
$id: 'Rarity',
|
|
30
|
+
description: 'A rarity, as described in Ironsworn: Delve.'
|
|
31
|
+
}
|
|
32
|
+
),
|
|
33
|
+
(schema) => ({ ...schema, additionalProperties: true })
|
|
34
|
+
)
|
|
35
|
+
export type Rarity = Static<typeof Rarity>
|
|
36
|
+
export type TRarity = typeof Rarity
|
|
@@ -0,0 +1,94 @@
|
|
|
1
|
+
import { type Static, Type } from '@sinclair/typebox'
|
|
2
|
+
import * as CONST from '../scripts/const.js'
|
|
3
|
+
import { RulesetId } from './common/Id.js'
|
|
4
|
+
import Defs from './Defs.js'
|
|
5
|
+
import {
|
|
6
|
+
Asset,
|
|
7
|
+
DelveSite,
|
|
8
|
+
DelveSiteDomain,
|
|
9
|
+
DelveSiteTheme,
|
|
10
|
+
Move,
|
|
11
|
+
Npc,
|
|
12
|
+
OracleRollable,
|
|
13
|
+
Rarity
|
|
14
|
+
} from './index.js'
|
|
15
|
+
import { RulesPackage, Version } from './RulesPackages.js'
|
|
16
|
+
import { RootObject, SourceRootObject } from './root/Root.js'
|
|
17
|
+
import * as Utils from './Utils.js'
|
|
18
|
+
|
|
19
|
+
const RootObjectMixin = Type.Object(
|
|
20
|
+
{
|
|
21
|
+
datasworn_version: Version,
|
|
22
|
+
ruleset: Type.Ref(RulesetId)
|
|
23
|
+
},
|
|
24
|
+
{ additionalProperties: true }
|
|
25
|
+
)
|
|
26
|
+
|
|
27
|
+
const StandaloneNode = Type.Intersect([
|
|
28
|
+
Utils.DiscriminatedUnion(
|
|
29
|
+
{
|
|
30
|
+
asset: Asset,
|
|
31
|
+
npc: Npc,
|
|
32
|
+
move: Move,
|
|
33
|
+
oracle_rollable: OracleRollable,
|
|
34
|
+
// delve types
|
|
35
|
+
delve_site: DelveSite,
|
|
36
|
+
delve_site_theme: DelveSiteTheme,
|
|
37
|
+
delve_site_domain: DelveSiteDomain,
|
|
38
|
+
rarity: Rarity
|
|
39
|
+
},
|
|
40
|
+
'type'
|
|
41
|
+
),
|
|
42
|
+
RootObjectMixin
|
|
43
|
+
])
|
|
44
|
+
|
|
45
|
+
// this isn't friendly to JTD, but currently the source schema isn't processed for JTD anyways
|
|
46
|
+
const SourceRoot = Type.Union([Type.Ref(RulesPackage), StandaloneNode], {
|
|
47
|
+
$id: 'SourceRoot',
|
|
48
|
+
description:
|
|
49
|
+
'The root object for a Datasworn source file, whose schema is discriminated by the `type` property. Unlike the JSON schema for distribution, this may be a standalone object (Asset, Npc, Move, OracleRollable, DelveSite, DelveSiteTheme, DelveSiteDomain, or Rarity), but it still must specify its `ruleset` and `datasworn_version`.'
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
export const DataswornSchema = RootObject(Type.Ref(RulesPackage), {
|
|
53
|
+
$schema: CONST.$schema,
|
|
54
|
+
$id: CONST.SCHEMA_ID,
|
|
55
|
+
title: `Datasworn v${CONST.VERSION}`,
|
|
56
|
+
description:
|
|
57
|
+
'Describes game rules compatible with the Ironsworn tabletop role-playing game by Shawn Tomkin.',
|
|
58
|
+
[CONST.DefsKey]: Defs
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
export const DataswornSourceSchema = SourceRootObject(Type.Ref(SourceRoot), {
|
|
62
|
+
$schema: CONST.$schema,
|
|
63
|
+
$id: CONST.SOURCE_SCHEMA_ID,
|
|
64
|
+
title: `DataswornSource v${CONST.VERSION}`,
|
|
65
|
+
description:
|
|
66
|
+
'Source data schema for Datasworn, which describes game rules compatible with the Ironsworn tabletop roleplaying game by Shawn Tomkin.\n\nThe source data omits IDs, and makes properties that provide a default value optional; these values are inserted during validation/processing to produce the JSON for distribution.',
|
|
67
|
+
// additionalProperties: true,
|
|
68
|
+
[CONST.DefsKey]: { SourceRoot, ...Defs }
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
export type DataswornSchema = Static<typeof DataswornSchema>
|
|
72
|
+
export type DataswornSourceSchema = Static<typeof DataswornSourceSchema>
|
|
73
|
+
|
|
74
|
+
// for (const schema of [
|
|
75
|
+
// Defs.Asset,
|
|
76
|
+
// ...Object.values(Defs.Move[Mapping]),
|
|
77
|
+
// ...Object.values(Defs.OracleRollable[Mapping]),
|
|
78
|
+
// Defs.AtlasEntry,
|
|
79
|
+
// Defs.Npc,
|
|
80
|
+
// Defs.DelveSiteTheme,
|
|
81
|
+
// Defs.DelveSiteDomain,
|
|
82
|
+
// Defs.DelveSite
|
|
83
|
+
// ]) {
|
|
84
|
+
// const base = {
|
|
85
|
+
// type: schema.properties.type.const
|
|
86
|
+
// }
|
|
87
|
+
// for (const k in schema.properties) {
|
|
88
|
+
// if (k.endsWith('_type')) base[k] = schema.properties[k].const
|
|
89
|
+
// }
|
|
90
|
+
// console.log(
|
|
91
|
+
// schema.$id,
|
|
92
|
+
// Value.Default(schema, Object.values(DataswornSchema[CONST.DefsKey]), base)
|
|
93
|
+
// )
|
|
94
|
+
// }
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { type Static, Type } from '@sinclair/typebox'
|
|
2
|
+
import * as Generic from './Generic.js'
|
|
3
|
+
import { ConditionMeterRule } from './rules/ConditionMeterRule.js'
|
|
4
|
+
import { ImpactCategory } from './rules/ImpactRule.js'
|
|
5
|
+
import { SpecialTrackRule } from './rules/SpecialTrackRule.js'
|
|
6
|
+
import { StatRule } from './rules/StatRule.js'
|
|
7
|
+
import { TagRule } from './rules/TagRule.js'
|
|
8
|
+
|
|
9
|
+
export * from './rules/ConditionMeterRule.js'
|
|
10
|
+
export * from './rules/ImpactRule.js'
|
|
11
|
+
export * from './rules/SpecialTrackRule.js'
|
|
12
|
+
export * from './rules/StatRule.js'
|
|
13
|
+
export * from './rules/TagRule.js'
|
|
14
|
+
|
|
15
|
+
export const Rules = Type.Object(
|
|
16
|
+
{
|
|
17
|
+
stats: Generic.Dictionary(Type.Ref(StatRule, { default: undefined }), {
|
|
18
|
+
description:
|
|
19
|
+
'Describes the standard stats used by player characters in this ruleset.'
|
|
20
|
+
}),
|
|
21
|
+
condition_meters: Generic.Dictionary(
|
|
22
|
+
Type.Ref(ConditionMeterRule, { default: undefined }),
|
|
23
|
+
{
|
|
24
|
+
description:
|
|
25
|
+
'Describes the standard condition meters used by player characters in this ruleset.'
|
|
26
|
+
}
|
|
27
|
+
),
|
|
28
|
+
impacts: Generic.Dictionary(
|
|
29
|
+
Type.Ref(ImpactCategory, { default: undefined }),
|
|
30
|
+
{
|
|
31
|
+
description:
|
|
32
|
+
'Describes the standard impacts/debilities used by player characters in this ruleset.'
|
|
33
|
+
}
|
|
34
|
+
),
|
|
35
|
+
special_tracks: Generic.Dictionary(
|
|
36
|
+
Type.Ref(SpecialTrackRule, { default: undefined }),
|
|
37
|
+
{
|
|
38
|
+
description:
|
|
39
|
+
'Describes the special tracks used by player characters in this ruleset, like Bonds (classic Ironsworn), Failure (Delve), or Legacies (Starforged).'
|
|
40
|
+
}
|
|
41
|
+
),
|
|
42
|
+
tags: Generic.Dictionary(Type.Ref(TagRule), {
|
|
43
|
+
releaseStage: 'experimental'
|
|
44
|
+
})
|
|
45
|
+
},
|
|
46
|
+
{
|
|
47
|
+
$id: 'Rules',
|
|
48
|
+
description:
|
|
49
|
+
'Describes rules for player characters in this ruleset, such as stats and condition meters.',
|
|
50
|
+
releaseStage: 'experimental'
|
|
51
|
+
}
|
|
52
|
+
)
|
|
53
|
+
export type Rules = Static<typeof Rules>
|
|
54
|
+
export type TRules = typeof Rules
|
|
55
|
+
|
|
56
|
+
export const RulesExpansion = Type.Partial(Rules, {
|
|
57
|
+
$id: 'RulesExpansion'
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
export type RulesExpansion = Static<typeof RulesExpansion>
|
|
61
|
+
export type TRulesExpansion = typeof RulesExpansion
|
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
import { type Static, Type } from '@sinclair/typebox'
|
|
2
|
+
import { mapValues } from 'lodash-es'
|
|
3
|
+
import { rootSchemaName, VERSION } from '../scripts/const.js'
|
|
4
|
+
import { SourceInfo } from './common/Metadata.js'
|
|
5
|
+
import * as Text from './common/Text.js'
|
|
6
|
+
import * as Generic from './Generic.js'
|
|
7
|
+
import * as Rules from './Rules.js'
|
|
8
|
+
import * as Utils from './Utils.js'
|
|
9
|
+
import { Assign } from './utils/FlatIntersect.js'
|
|
10
|
+
|
|
11
|
+
export const Version = Type.Literal(VERSION, {
|
|
12
|
+
description: 'The version of the Datasworn format used by this data.',
|
|
13
|
+
pattern:
|
|
14
|
+
/^(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/
|
|
15
|
+
.source
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
const RulesPackageBase = Type.Object({
|
|
19
|
+
_id: Type.Ref('RulesPackageId'),
|
|
20
|
+
type: Type.String(),
|
|
21
|
+
// name: Utils.SourceOptional(Type.Ref(Text.Label)),
|
|
22
|
+
datasworn_version: Version,
|
|
23
|
+
description: Type.Optional(Type.Ref(Text.MarkdownString)),
|
|
24
|
+
...mapValues(Type.Required(Type.Omit(SourceInfo, ['page'])).properties, (v) =>
|
|
25
|
+
Utils.setSourceOptional(v)
|
|
26
|
+
),
|
|
27
|
+
oracles: Utils.setSourceOptional(
|
|
28
|
+
Generic.Dictionary(Type.Ref('OracleTablesCollection'), {
|
|
29
|
+
description:
|
|
30
|
+
'A dictionary object containing oracle collections, which may contain oracle tables and/or oracle collections.'
|
|
31
|
+
})
|
|
32
|
+
),
|
|
33
|
+
moves: Utils.setSourceOptional(
|
|
34
|
+
Generic.Dictionary(Type.Ref('MoveCategory'), {
|
|
35
|
+
description:
|
|
36
|
+
'A dictionary object containing move categories, which contain moves.'
|
|
37
|
+
})
|
|
38
|
+
),
|
|
39
|
+
assets: Utils.setSourceOptional(
|
|
40
|
+
Generic.Dictionary(Type.Ref('AssetCollection'), {
|
|
41
|
+
description:
|
|
42
|
+
'A dictionary object containing asset collections, which contain assets.'
|
|
43
|
+
})
|
|
44
|
+
),
|
|
45
|
+
atlas: Type.Optional(
|
|
46
|
+
Generic.Dictionary(Type.Ref('AtlasCollection'), {
|
|
47
|
+
description:
|
|
48
|
+
'A dictionary object containing atlas collections, which contain atlas entries.'
|
|
49
|
+
})
|
|
50
|
+
),
|
|
51
|
+
npcs: Type.Optional(
|
|
52
|
+
Generic.Dictionary(Type.Ref('NpcCollection'), {
|
|
53
|
+
description:
|
|
54
|
+
'A dictionary object containing NPC collections, which contain NPCs.'
|
|
55
|
+
})
|
|
56
|
+
),
|
|
57
|
+
truths: Type.Optional(
|
|
58
|
+
Generic.Dictionary(Type.Ref('Truth'), {
|
|
59
|
+
description: 'A dictionary object of truth categories.'
|
|
60
|
+
})
|
|
61
|
+
),
|
|
62
|
+
rarities: Type.Optional(
|
|
63
|
+
Generic.Dictionary(Type.Ref('Rarity'), {
|
|
64
|
+
description:
|
|
65
|
+
'A dictionary object containing rarities, like those presented in Ironsworn: Delve.'
|
|
66
|
+
})
|
|
67
|
+
),
|
|
68
|
+
delve_sites: Type.Optional(
|
|
69
|
+
Generic.Dictionary(Type.Ref('DelveSite'), {
|
|
70
|
+
description:
|
|
71
|
+
'A dictionary object of delve sites, like the premade delve sites presented in Ironsworn: Delve'
|
|
72
|
+
})
|
|
73
|
+
),
|
|
74
|
+
site_themes: Type.Optional(
|
|
75
|
+
Generic.Dictionary(Type.Ref('DelveSiteTheme'), {
|
|
76
|
+
description: 'A dictionary object containing delve site themes.'
|
|
77
|
+
})
|
|
78
|
+
),
|
|
79
|
+
site_domains: Type.Optional(
|
|
80
|
+
Generic.Dictionary(Type.Ref('DelveSiteDomain'), {
|
|
81
|
+
description: 'A dictionary object containing delve site domains.'
|
|
82
|
+
})
|
|
83
|
+
)
|
|
84
|
+
})
|
|
85
|
+
|
|
86
|
+
export const Ruleset = Assign(
|
|
87
|
+
RulesPackageBase,
|
|
88
|
+
Type.Object({
|
|
89
|
+
_id: Type.Ref('RulesetId'),
|
|
90
|
+
type: Type.Literal('ruleset'),
|
|
91
|
+
rules: Utils.setSourceOptional(Type.Ref(Rules.Rules))
|
|
92
|
+
}),
|
|
93
|
+
{
|
|
94
|
+
$id: 'Ruleset',
|
|
95
|
+
additionalProperties: true,
|
|
96
|
+
description:
|
|
97
|
+
'A standalone Datasworn package that describes its own ruleset.'
|
|
98
|
+
}
|
|
99
|
+
)
|
|
100
|
+
|
|
101
|
+
export type Ruleset = Static<typeof Ruleset>
|
|
102
|
+
|
|
103
|
+
export const Expansion = Assign(
|
|
104
|
+
Type.Omit(RulesPackageBase, ['rules']),
|
|
105
|
+
Type.Object({
|
|
106
|
+
_id: Type.Ref('ExpansionId'),
|
|
107
|
+
type: Type.Literal('expansion'),
|
|
108
|
+
ruleset: Type.Ref('RulesetId'),
|
|
109
|
+
rules: Type.Optional(Type.Ref(Rules.RulesExpansion))
|
|
110
|
+
}),
|
|
111
|
+
{
|
|
112
|
+
additionalProperties: true,
|
|
113
|
+
|
|
114
|
+
description:
|
|
115
|
+
'A Datasworn package that relies on an external package to provide its ruleset.',
|
|
116
|
+
$id: 'Expansion'
|
|
117
|
+
}
|
|
118
|
+
)
|
|
119
|
+
export type Expansion = Static<typeof Expansion>
|
|
120
|
+
|
|
121
|
+
export const RulesPackage = Utils.DiscriminatedUnion(
|
|
122
|
+
{
|
|
123
|
+
ruleset: Ruleset,
|
|
124
|
+
expansion: Expansion
|
|
125
|
+
},
|
|
126
|
+
'type',
|
|
127
|
+
{
|
|
128
|
+
description:
|
|
129
|
+
'Describes game rules compatible with the Ironsworn tabletop role-playing game by Shawn Tomkin.',
|
|
130
|
+
title: rootSchemaName,
|
|
131
|
+
$id: rootSchemaName
|
|
132
|
+
}
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
export type RulesPackage = Static<typeof RulesPackage>
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/** Override type for typescript code generation. */
|
|
2
|
+
export const Typescript = Symbol('Typescript')
|
|
3
|
+
|
|
4
|
+
/** Hints type relationships between schemata. */
|
|
5
|
+
export const Extends = Symbol('Extends')
|
|
6
|
+
|
|
7
|
+
/** Use with SchemaOptions to hint/override JTD generation. */
|
|
8
|
+
export const JsonTypeDef = Symbol('JsonTypeDef')
|
|
9
|
+
/** Hints discriminator key for DiscriminatedUnion schemata */
|
|
10
|
+
export const Discriminator = Symbol('Discriminator')
|
|
11
|
+
/** Stores references to the member schema in a discriminated union */
|
|
12
|
+
export const Members = Symbol('Members')
|
|
13
|
+
/** Stores references to the member schema in a discriminated union */
|
|
14
|
+
export const Mapping = Symbol('Mapping')
|
|
15
|
+
|
|
16
|
+
/** A less complex alternate version of the schema for use with code generation tools. */
|
|
17
|
+
export const Simplify = Symbol('Simplify')
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Details type inheritance for use in code generation.
|
|
21
|
+
*/
|
|
22
|
+
export const Inherits = Symbol('Inherits')
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Sets the namespace/module name for use in code generation.
|
|
26
|
+
*/
|
|
27
|
+
export const Namespace = Symbol('Namespace')
|
|
28
|
+
|
|
29
|
+
/** Marks a property as being inherited from another schema. */
|
|
30
|
+
export const InheritedFrom = Symbol('InheritedFrom')
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
import { type Static, Type } from '@sinclair/typebox'
|
|
2
|
+
import { DiceRange } from './common/Range.js'
|
|
3
|
+
import { DiceExpression } from './common/Rolls.js'
|
|
4
|
+
import * as Text from './common/Text.js'
|
|
5
|
+
import { EntityPrompt } from './entities/EntityPrompt.js'
|
|
6
|
+
import * as Generic from './Generic.js'
|
|
7
|
+
import { EmbeddedOracleRollable } from './oracles/EmbeddedOracleRollable.js'
|
|
8
|
+
|
|
9
|
+
export type TruthOption = Static<typeof TruthOption>
|
|
10
|
+
|
|
11
|
+
export const TruthOption = Generic.IdNode(
|
|
12
|
+
Type.Object({
|
|
13
|
+
roll: Type.Ref(DiceRange),
|
|
14
|
+
summary: Type.Optional(Type.Ref(Text.MarkdownString)),
|
|
15
|
+
description: Type.Ref(Text.MarkdownString),
|
|
16
|
+
quest_starter: Type.Ref(Text.MarkdownString),
|
|
17
|
+
oracles: Type.Optional(
|
|
18
|
+
Generic.Dictionary(
|
|
19
|
+
Type.Ref(EmbeddedOracleRollable, {
|
|
20
|
+
title: 'TruthOptionOracleRollable'
|
|
21
|
+
}),
|
|
22
|
+
{
|
|
23
|
+
title: 'TruthOptionOracles'
|
|
24
|
+
}
|
|
25
|
+
)
|
|
26
|
+
)
|
|
27
|
+
}),
|
|
28
|
+
Type.Ref('TruthOptionId'),
|
|
29
|
+
{ $id: 'TruthOption' }
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
export const Truth = Generic.NonCollectableNode(
|
|
33
|
+
Type.Object({
|
|
34
|
+
dice: Type.Ref(DiceExpression, { default: '1d100' }),
|
|
35
|
+
options: Type.Array(Type.Ref(TruthOption), { rollable: true }),
|
|
36
|
+
your_character: Type.Optional(Type.Ref(Text.MarkdownString)),
|
|
37
|
+
factions: Type.Optional(
|
|
38
|
+
Type.Array(Type.Ref(EntityPrompt), {
|
|
39
|
+
description:
|
|
40
|
+
'Prompts for factions related to this truth, like those presented in standard isles. This is presented as a single paragraph in the original text; Datasworn uses an array (one faction prompt per string) to represent them in order to make them more suitable for programmatic use.\n\nThis property is a placeholder and may see signficant changes in v0.2.0.',
|
|
41
|
+
releaseStage: 'experimental'
|
|
42
|
+
})
|
|
43
|
+
)
|
|
44
|
+
}),
|
|
45
|
+
'truth',
|
|
46
|
+
{
|
|
47
|
+
$id: 'Truth',
|
|
48
|
+
description: 'A setting truth category.'
|
|
49
|
+
}
|
|
50
|
+
)
|
|
51
|
+
|
|
52
|
+
export type Truth = Static<typeof Truth>
|
|
53
|
+
export type TTruth = typeof Truth
|