coh-content-db 2.0.0-rc.2 → 2.0.0-rc.21
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/.editorconfig +10 -11
- package/.github/workflows/build.yml +4 -2
- package/.github/workflows/pull-request.yml +1 -1
- package/.github/workflows/release.yml +1 -1
- package/CHANGELOG.md +47 -0
- package/README.md +77 -32
- package/dist/coh-content-db.d.ts +755 -290
- package/dist/coh-content-db.js +1028 -358
- package/dist/coh-content-db.js.map +1 -1
- package/dist/coh-content-db.mjs +998 -349
- package/dist/coh-content-db.mjs.map +1 -1
- package/eslint.config.mjs +1 -0
- package/jest.config.mjs +1 -0
- package/package.json +14 -14
- package/src/main/api/alignment.ts +18 -2
- package/src/main/api/badge-data.ts +29 -51
- package/src/main/api/badge-requirement-data.ts +64 -0
- package/src/main/api/badge-requirement-type.ts +32 -0
- package/src/main/api/badge-type.ts +15 -15
- package/src/main/api/bundle-data.ts +47 -0
- package/src/main/api/bundle-header-data.ts +44 -0
- package/src/main/api/contact-data.ts +49 -0
- package/src/main/api/enhancement-category.ts +26 -26
- package/src/main/api/level-range-data.ts +4 -0
- package/src/main/api/location-data.ts +28 -0
- package/src/main/api/markdown-string.ts +4 -0
- package/src/main/api/mission-data.ts +57 -0
- package/src/main/api/mission-flashback-data.ts +31 -0
- package/src/main/api/mission-type.ts +2 -0
- package/src/main/api/morality.ts +49 -0
- package/src/main/api/set-title-data.ts +4 -0
- package/src/main/api/sex.ts +8 -1
- package/src/main/api/variant-context.ts +11 -0
- package/src/main/api/variant-data.ts +22 -0
- package/src/main/api/zone-data.ts +44 -0
- package/src/main/api/zone-type.ts +59 -0
- package/src/main/db/abstract-index.ts +37 -0
- package/src/main/db/alignment-list.ts +54 -0
- package/src/main/db/badge-index.ts +83 -0
- package/src/main/db/badge-requirement.ts +81 -0
- package/src/main/db/badge-search-options.ts +52 -0
- package/src/main/db/badge.ts +97 -74
- package/src/main/db/bundle-header.ts +52 -0
- package/src/main/db/coh-content-database.ts +123 -14
- package/src/main/db/contact.ts +63 -0
- package/src/main/db/level-range.ts +15 -0
- package/src/main/db/location.ts +30 -0
- package/src/main/db/mission.ts +108 -0
- package/src/main/db/morality-list.ts +99 -0
- package/src/main/db/paged.ts +11 -0
- package/src/main/db/set-title-ids.ts +10 -0
- package/src/main/db/variants.ts +84 -0
- package/src/main/db/zone.ts +57 -0
- package/src/main/index.ts +33 -18
- package/src/main/util/coalesce-to-array.ts +13 -0
- package/src/main/util/links.ts +104 -0
- package/src/main/util/to-date.ts +9 -0
- package/src/test/api/alignment.test.ts +38 -4
- package/src/test/api/badge-data.fixture.ts +2 -15
- package/src/test/api/badge-data.test.ts +5 -4
- package/src/test/api/badge-requirement-data.fixture.ts +7 -0
- package/src/test/api/badge-requirement-type.test.ts +31 -0
- package/src/test/api/badge-type.test.ts +5 -5
- package/src/test/api/bundle-data.fixture.ts +7 -0
- package/src/test/api/bundle-header-data.fixture.ts +8 -0
- package/src/test/api/contact-data.fixture.ts +7 -0
- package/src/test/api/enhancement-category.test.ts +5 -5
- package/src/test/api/mission-data.fixture.ts +12 -0
- package/src/test/api/morality.test.ts +31 -0
- package/src/test/api/sex.test.ts +33 -1
- package/src/test/api/zone-data.fixture.ts +9 -0
- package/src/test/db/abstract-index.test.ts +55 -0
- package/src/test/db/alignment-list.test.ts +200 -0
- package/src/test/db/badge-index.test.ts +653 -0
- package/src/test/db/badge-requirement.test.ts +145 -0
- package/src/test/db/badge.test.ts +416 -14
- package/src/test/db/bundle-header.test.ts +89 -0
- package/src/test/db/coh-content-database.test.ts +265 -24
- package/src/test/db/contact.test.ts +98 -0
- package/src/test/db/level-range.test.ts +47 -0
- package/src/test/db/location.test.ts +51 -0
- package/src/test/db/mission.test.ts +173 -0
- package/src/test/db/morality-list.test.ts +457 -0
- package/src/test/db/set-title-ids.test.ts +19 -0
- package/src/test/db/variants.test.ts +188 -0
- package/src/test/db/zone.test.ts +81 -0
- package/src/test/integration.test.ts +16 -0
- package/src/test/util/coalese-to-array.test.ts +17 -0
- package/src/test/util/links.test.ts +149 -0
- package/src/test/util/to-date.test.ts +15 -0
- package/src/main/api/alternate-data.ts +0 -22
- package/src/main/api/badge-partial-data.ts +0 -65
- package/src/main/api/badge-partial-type.ts +0 -8
- package/src/main/api/change.ts +0 -14
- package/src/main/api/game-map-data.ts +0 -26
- package/src/main/api/plaque-type.ts +0 -6
- package/src/main/api/server-group-data.ts +0 -65
- package/src/main/api/vidiot-map-data.ts +0 -18
- package/src/main/api/vidiot-map-point-of-interest-data.ts +0 -30
- package/src/main/changelog.ts +0 -20
- package/src/main/db/alternates.ts +0 -81
- package/src/main/db/badge-partial.ts +0 -35
- package/src/main/db/game-map.ts +0 -33
- package/src/main/db/server-group.ts +0 -112
- package/src/main/db/vidiot-map-point-of-interest.ts +0 -40
- package/src/main/db/vidiot-map.ts +0 -25
- package/src/main/util.ts +0 -17
- package/src/test/api/badge-partial-data.fixture.ts +0 -17
- package/src/test/api/badge-partial-type.test.ts +0 -31
- package/src/test/api/game-map-data.fixture.ts +0 -10
- package/src/test/api/plaque-type.test.ts +0 -31
- package/src/test/api/server-group-data.fixture.ts +0 -23
- package/src/test/api/server-group-data.test.ts +0 -15
- package/src/test/api/vidiot-map-point-of-interest.fixture.ts +0 -10
- package/src/test/api/vidiot-map.fixture.ts +0 -9
- package/src/test/changelog.test.ts +0 -36
- package/src/test/db/alternates.test.ts +0 -223
- package/src/test/db/server-group.test.ts +0 -124
- package/src/test/index.test.ts +0 -10
- package/src/test/util.test.ts +0 -39
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Link } from '../api/link'
|
|
2
|
+
import { Key } from './key'
|
|
3
|
+
import { MarkdownString } from '../api/markdown-string'
|
|
4
|
+
import { ContactData } from '../api/contact-data'
|
|
5
|
+
import { Location } from './location'
|
|
6
|
+
import { MoralityList } from './morality-list'
|
|
7
|
+
import { coalesceToArray } from '../util/coalesce-to-array'
|
|
8
|
+
import { LevelRange } from './level-range'
|
|
9
|
+
|
|
10
|
+
export class Contact {
|
|
11
|
+
/**
|
|
12
|
+
* Unique key used to reference this contact.
|
|
13
|
+
*
|
|
14
|
+
* Keys must be unique and can only contain lowercase letters, numbers and hyphens (`-`).
|
|
15
|
+
*/
|
|
16
|
+
readonly key: string
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The name of this contact.
|
|
20
|
+
*/
|
|
21
|
+
readonly name: string
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The contact's title.
|
|
25
|
+
*/
|
|
26
|
+
readonly title?: string
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The character moralities that this contact will interact with.
|
|
30
|
+
*/
|
|
31
|
+
readonly morality: MoralityList
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The location of this contact.
|
|
35
|
+
*/
|
|
36
|
+
readonly location?: Location
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* The level range this contact will offer missions for.
|
|
40
|
+
*/
|
|
41
|
+
readonly levelRange?: LevelRange
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Freeform notes or tips about the contact.
|
|
45
|
+
*/
|
|
46
|
+
readonly notes?: MarkdownString
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* List of external links. Wiki, forums, etc.
|
|
50
|
+
*/
|
|
51
|
+
readonly links: Link[]
|
|
52
|
+
|
|
53
|
+
constructor(data: ContactData) {
|
|
54
|
+
this.key = new Key(data.key).value
|
|
55
|
+
this.name = data.name
|
|
56
|
+
this.title = data.title
|
|
57
|
+
this.morality = new MoralityList(coalesceToArray(data.morality))
|
|
58
|
+
this.location = data.location
|
|
59
|
+
this.levelRange = data.levelRange ? new LevelRange(data.levelRange) : undefined
|
|
60
|
+
this.notes = data.notes
|
|
61
|
+
this.links = data.links ?? []
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { LevelRangeData } from '../api/level-range-data'
|
|
2
|
+
|
|
3
|
+
export class LevelRange {
|
|
4
|
+
readonly min: number
|
|
5
|
+
readonly max?: number
|
|
6
|
+
|
|
7
|
+
constructor(value: LevelRangeData) {
|
|
8
|
+
if (Array.isArray(value)) {
|
|
9
|
+
this.min = value[0]
|
|
10
|
+
this.max = value[1] === undefined ? undefined : value[1]
|
|
11
|
+
} else {
|
|
12
|
+
this.min = value
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { Coords, LocationData, LocationIcon } from '../api/location-data'
|
|
2
|
+
|
|
3
|
+
export class Location {
|
|
4
|
+
/**
|
|
5
|
+
* Key of the {@link Zone} that the location references.
|
|
6
|
+
*/
|
|
7
|
+
readonly zoneKey?: string
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* In-game `/loc` coordinates of the location.
|
|
11
|
+
*/
|
|
12
|
+
readonly coords?: Coords
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* The type of icon to use if the location appears on a map. (Typically the Vidiot map icon).
|
|
16
|
+
*/
|
|
17
|
+
readonly icon?: LocationIcon
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* The text that should appear in the location icon. (Typically a number or symbol from the Vidiot map).
|
|
21
|
+
*/
|
|
22
|
+
readonly iconText?: string
|
|
23
|
+
|
|
24
|
+
constructor(data: LocationData) {
|
|
25
|
+
this.zoneKey = data.zoneKey
|
|
26
|
+
this.coords = data.coords
|
|
27
|
+
this.icon = data.icon
|
|
28
|
+
this.iconText = data.iconText
|
|
29
|
+
}
|
|
30
|
+
}
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
import { MissionType } from '../api/mission-type'
|
|
2
|
+
import { MarkdownString } from '../api/markdown-string'
|
|
3
|
+
import { Link } from '../api/link'
|
|
4
|
+
import { MissionData } from '../api/mission-data'
|
|
5
|
+
import { Key } from './key'
|
|
6
|
+
import { MoralityList } from './morality-list'
|
|
7
|
+
import { coalesceToArray } from '../util/coalesce-to-array'
|
|
8
|
+
import { LevelRange } from './level-range'
|
|
9
|
+
|
|
10
|
+
export class Mission {
|
|
11
|
+
/**
|
|
12
|
+
* Unique key used to reference this mission.
|
|
13
|
+
*
|
|
14
|
+
* Keys must be unique and can only contain lowercase letters, numbers and hyphens (`-`).
|
|
15
|
+
*/
|
|
16
|
+
readonly key: string
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The name of the mission as it appears from the contact.
|
|
20
|
+
*
|
|
21
|
+
* The name may be different when viewed in Ouroboros as a Flashback.
|
|
22
|
+
*/
|
|
23
|
+
readonly name: string
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* The type of mission... Story arc, task force, trial, etc.
|
|
27
|
+
*/
|
|
28
|
+
readonly type: MissionType
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* The character moralities that may accept the mission.
|
|
32
|
+
*/
|
|
33
|
+
readonly morality: MoralityList
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* The keys of any contacts that provide this mission.
|
|
37
|
+
*/
|
|
38
|
+
readonly contactKeys?: string[]
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* The level range this mission is available for.
|
|
42
|
+
*/
|
|
43
|
+
readonly levelRange?: LevelRange
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Freeform notes or tips about the mission.
|
|
47
|
+
*/
|
|
48
|
+
readonly notes?: MarkdownString
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* List of external links. Wiki, forums, etc.
|
|
52
|
+
*/
|
|
53
|
+
readonly links: Link[]
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* If the mission is available in Ouroboros as a Flashback.
|
|
57
|
+
*/
|
|
58
|
+
readonly flashback?: {
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* The id of the mission as seen in the Flashback menu, i.e. '14.01'.
|
|
62
|
+
*/
|
|
63
|
+
readonly id: string
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* The level range this mission appears under as a Flashback.
|
|
67
|
+
*/
|
|
68
|
+
readonly levelRange?: LevelRange
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* The name as it appears in the Flashback list.
|
|
72
|
+
*/
|
|
73
|
+
readonly name: string
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* The character moralities that the mission will appear for in the Flashback list.
|
|
77
|
+
*/
|
|
78
|
+
readonly morality: MoralityList
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Freeform notes or tips about the Flashback version of the mission.
|
|
82
|
+
*/
|
|
83
|
+
readonly notes?: MarkdownString
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
constructor(data: MissionData) {
|
|
87
|
+
this.key = new Key(data.key).value
|
|
88
|
+
this.name = data.name
|
|
89
|
+
this.type = data.type
|
|
90
|
+
this.morality = new MoralityList(coalesceToArray(data.morality))
|
|
91
|
+
this.contactKeys = coalesceToArray(data.contactKeys)
|
|
92
|
+
this.levelRange = data.levelRange ? new LevelRange(data.levelRange) : undefined
|
|
93
|
+
this.notes = data.notes
|
|
94
|
+
this.links = data.links ?? []
|
|
95
|
+
this.flashback = createFlashback(data)
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function createFlashback(data: MissionData): Mission['flashback'] {
|
|
100
|
+
if (!data.flashback) return undefined
|
|
101
|
+
return {
|
|
102
|
+
id: data.flashback.id,
|
|
103
|
+
levelRange: data.flashback.levelRange ? new LevelRange(data.flashback.levelRange) : undefined,
|
|
104
|
+
name: data.flashback.name ?? data.name,
|
|
105
|
+
morality: new MoralityList(coalesceToArray(data.flashback.morality ?? data.morality)),
|
|
106
|
+
notes: data.flashback.notes,
|
|
107
|
+
}
|
|
108
|
+
}
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { MORALITY, Morality, MoralityExtended } from '../api/morality'
|
|
2
|
+
|
|
3
|
+
export class MoralityList {
|
|
4
|
+
readonly #items: Set<Morality>
|
|
5
|
+
|
|
6
|
+
readonly hero: boolean
|
|
7
|
+
readonly vigilante: boolean
|
|
8
|
+
readonly villain: boolean
|
|
9
|
+
readonly rogue: boolean
|
|
10
|
+
readonly resistance: boolean
|
|
11
|
+
readonly loyalist: boolean
|
|
12
|
+
|
|
13
|
+
readonly primal: boolean
|
|
14
|
+
readonly praetorian: boolean
|
|
15
|
+
readonly heroic: boolean
|
|
16
|
+
readonly villainous: boolean
|
|
17
|
+
readonly paragonCityAccess: boolean
|
|
18
|
+
readonly rogueIslesAccess: boolean
|
|
19
|
+
|
|
20
|
+
readonly all: boolean
|
|
21
|
+
|
|
22
|
+
constructor(items?: MoralityExtended[]) {
|
|
23
|
+
const set = new Set(items ?? [...MORALITY])
|
|
24
|
+
this.hero = set.has('hero') || set.has('primal') || set.has('heroic') || set.has('paragon-city-access') || set.has('all')
|
|
25
|
+
this.vigilante = set.has('vigilante') || set.has('primal') || set.has('heroic') || set.has('paragon-city-access') || set.has('rogue-isles-access') || set.has('all')
|
|
26
|
+
this.villain = set.has('villain') || set.has('primal') || set.has('villainous') || set.has('rogue-isles-access') || set.has('all')
|
|
27
|
+
this.rogue = set.has('rogue') || set.has('primal') || set.has('villainous') || set.has('paragon-city-access') || set.has('rogue-isles-access') || set.has('all')
|
|
28
|
+
this.resistance = set.has('resistance') || set.has('praetorian') || set.has('all')
|
|
29
|
+
this.loyalist = set.has('loyalist') || set.has('praetorian') || set.has('all')
|
|
30
|
+
|
|
31
|
+
this.primal = this.hero && this.vigilante && this.villain && this.rogue
|
|
32
|
+
this.praetorian = this.loyalist && this.resistance
|
|
33
|
+
this.heroic = this.hero && this.vigilante
|
|
34
|
+
this.villainous = this.villain && this.rogue
|
|
35
|
+
this.paragonCityAccess = this.heroic && this.rogue
|
|
36
|
+
this.rogueIslesAccess = this.villainous && this.vigilante
|
|
37
|
+
|
|
38
|
+
this.all = this.primal && this.praetorian
|
|
39
|
+
|
|
40
|
+
this.#items = new Set()
|
|
41
|
+
if (this.hero) this.#items.add('hero')
|
|
42
|
+
if (this.vigilante) this.#items.add('vigilante')
|
|
43
|
+
if (this.villain) this.#items.add('villain')
|
|
44
|
+
if (this.rogue) this.#items.add('rogue')
|
|
45
|
+
if (this.resistance) this.#items.add('resistance')
|
|
46
|
+
if (this.loyalist) this.#items.add('loyalist')
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
get items(): Morality[] {
|
|
50
|
+
return [...this.#items]
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
has(morality?: MoralityExtended): boolean {
|
|
54
|
+
switch (morality) {
|
|
55
|
+
case 'hero': {
|
|
56
|
+
return this.hero
|
|
57
|
+
}
|
|
58
|
+
case 'vigilante': {
|
|
59
|
+
return this.vigilante
|
|
60
|
+
}
|
|
61
|
+
case 'villain': {
|
|
62
|
+
return this.villain
|
|
63
|
+
}
|
|
64
|
+
case 'rogue': {
|
|
65
|
+
return this.rogue
|
|
66
|
+
}
|
|
67
|
+
case 'resistance': {
|
|
68
|
+
return this.resistance
|
|
69
|
+
}
|
|
70
|
+
case 'loyalist': {
|
|
71
|
+
return this.loyalist
|
|
72
|
+
}
|
|
73
|
+
case 'primal' : {
|
|
74
|
+
return this.primal
|
|
75
|
+
}
|
|
76
|
+
case 'praetorian': {
|
|
77
|
+
return this.praetorian
|
|
78
|
+
}
|
|
79
|
+
case 'heroic': {
|
|
80
|
+
return this.hero
|
|
81
|
+
}
|
|
82
|
+
case 'paragon-city-access': {
|
|
83
|
+
return this.paragonCityAccess
|
|
84
|
+
}
|
|
85
|
+
case 'rogue-isles-access': {
|
|
86
|
+
return this.rogueIslesAccess
|
|
87
|
+
}
|
|
88
|
+
case 'villainous': {
|
|
89
|
+
return this.villainous
|
|
90
|
+
}
|
|
91
|
+
case 'all': {
|
|
92
|
+
return this.all
|
|
93
|
+
}
|
|
94
|
+
default: {
|
|
95
|
+
return false
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { VariantData } from '../api/variant-data'
|
|
2
|
+
import { compareSex } from '../api/sex'
|
|
3
|
+
import { compareAlignment } from '../api/alignment'
|
|
4
|
+
import { VariantContext } from '../api/variant-context'
|
|
5
|
+
import { MoralityMap } from '../api/morality'
|
|
6
|
+
|
|
7
|
+
export class Variants<T> {
|
|
8
|
+
readonly #sortedValues: VariantData<T>[] = []
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Create a variant set from either a list of categorized values, or a single value when there are no variants.
|
|
12
|
+
* @param value List of variants, or a single value.
|
|
13
|
+
*/
|
|
14
|
+
constructor(value: VariantData<T>[] | T) {
|
|
15
|
+
if (Array.isArray(value)) {
|
|
16
|
+
this.#sortedValues = value.toSorted()
|
|
17
|
+
this.#sortedValues.sort((a, b) => this.#compareVariants(a, b))
|
|
18
|
+
} else {
|
|
19
|
+
this.#sortedValues = [{ value }]
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Get a variant by context
|
|
25
|
+
* @param context The context
|
|
26
|
+
*/
|
|
27
|
+
getVariant(context?: VariantContext): VariantData<T> | undefined {
|
|
28
|
+
const alignment = context?.morality ? MoralityMap[context.morality] : undefined
|
|
29
|
+
const sex = context?.sex
|
|
30
|
+
|
|
31
|
+
for (let index = this.#sortedValues.length; index--;) {
|
|
32
|
+
const entry = this.#sortedValues[index]
|
|
33
|
+
if ((entry.alignment === undefined || entry.alignment === alignment)
|
|
34
|
+
&& (entry.sex === undefined || entry.sex === sex)
|
|
35
|
+
) return entry
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
return this.default
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/**
|
|
42
|
+
* Get a value by variant context
|
|
43
|
+
* @param context The context
|
|
44
|
+
*/
|
|
45
|
+
getValue(context?: VariantContext): T | undefined {
|
|
46
|
+
return this.getVariant(context)?.value
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Get the default value for this list of variants, the value with the highest priority and lowest specificity.
|
|
51
|
+
*/
|
|
52
|
+
get default(): VariantData<T> | undefined {
|
|
53
|
+
return this.#sortedValues[0]
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Get the list of variants sorted in canonical order (alignment then sex, low to high specificity).
|
|
58
|
+
*/
|
|
59
|
+
get canonical(): VariantData<T>[] {
|
|
60
|
+
return this.#sortedValues
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Create a joined string from the variant values in canonical order.
|
|
65
|
+
* @param separator Separator to use. Default is ' / '
|
|
66
|
+
*/
|
|
67
|
+
toString(separator: string): string {
|
|
68
|
+
return this.canonical.map(x => x.value).join(separator)
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
#compareVariants(a: VariantData<T>, b: VariantData<T>): number {
|
|
72
|
+
const aSpecificity = (a.alignment ? 2 : 0) + (a.sex ? 1 : 0)
|
|
73
|
+
const bSpecificity = (b.alignment ? 2 : 0) + (b.sex ? 1 : 0)
|
|
74
|
+
if (aSpecificity !== bSpecificity) return aSpecificity - bSpecificity // Order first by least-specific
|
|
75
|
+
|
|
76
|
+
const alignmentComparison = compareAlignment(a.alignment, b.alignment) // Next by alignment
|
|
77
|
+
if (alignmentComparison !== 0) return alignmentComparison
|
|
78
|
+
|
|
79
|
+
const sexComparison = compareSex(a.sex, b.sex) // Last by sex
|
|
80
|
+
if (sexComparison !== 0) return sexComparison
|
|
81
|
+
|
|
82
|
+
return String(a.value).localeCompare(String(b.value))
|
|
83
|
+
}
|
|
84
|
+
}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
import { Link } from '../api/link'
|
|
2
|
+
import { ZoneData } from '../api/zone-data'
|
|
3
|
+
import { Key } from './key'
|
|
4
|
+
import { MoralityList } from './morality-list'
|
|
5
|
+
import { coalesceToArray } from '../util/coalesce-to-array'
|
|
6
|
+
import { ZoneType } from '../api/zone-type'
|
|
7
|
+
import { MarkdownString } from '../api/markdown-string'
|
|
8
|
+
import { LevelRange } from './level-range'
|
|
9
|
+
|
|
10
|
+
export class Zone {
|
|
11
|
+
/**
|
|
12
|
+
* Unique key used to reference this zone.
|
|
13
|
+
*
|
|
14
|
+
* Keys must be unique and can only contain lowercase letters, numbers and hyphens (`-`).
|
|
15
|
+
*/
|
|
16
|
+
readonly key: string
|
|
17
|
+
|
|
18
|
+
/**
|
|
19
|
+
* The name of the zone as it appears in-game.
|
|
20
|
+
*/
|
|
21
|
+
readonly name: string
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* The type of zone.
|
|
25
|
+
*/
|
|
26
|
+
readonly type: ZoneType
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The character moralities that this zone is accessible by.
|
|
30
|
+
*/
|
|
31
|
+
readonly morality: MoralityList
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* The level range this zone is recommended for.
|
|
35
|
+
*/
|
|
36
|
+
readonly levelRange?: LevelRange
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Freeform notes or tips about the zone.
|
|
40
|
+
*/
|
|
41
|
+
readonly notes?: MarkdownString
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* List of external links. Wiki, forums, etc.
|
|
45
|
+
*/
|
|
46
|
+
readonly links: Link[]
|
|
47
|
+
|
|
48
|
+
constructor(data: ZoneData) {
|
|
49
|
+
this.key = new Key(data.key).value
|
|
50
|
+
this.name = data.name
|
|
51
|
+
this.type = data.type
|
|
52
|
+
this.morality = new MoralityList(coalesceToArray(data.morality))
|
|
53
|
+
this.levelRange = data.levelRange ? new LevelRange(data.levelRange) : undefined
|
|
54
|
+
this.notes = data.notes
|
|
55
|
+
this.links = data.links ?? []
|
|
56
|
+
}
|
|
57
|
+
}
|
package/src/main/index.ts
CHANGED
|
@@ -1,33 +1,48 @@
|
|
|
1
1
|
// API
|
|
2
2
|
export * from './api/alignment'
|
|
3
|
-
export * from './api/alternate-data'
|
|
4
3
|
export * from './api/archetype-data'
|
|
5
4
|
export * from './api/badge-data'
|
|
6
|
-
export * from './api/badge-
|
|
7
|
-
export * from './api/badge-
|
|
5
|
+
export * from './api/badge-requirement-data'
|
|
6
|
+
export * from './api/badge-requirement-type'
|
|
8
7
|
export * from './api/badge-type'
|
|
9
|
-
export * from './api/
|
|
8
|
+
export * from './api/bundle-data'
|
|
9
|
+
export * from './api/bundle-header-data'
|
|
10
|
+
export * from './api/contact-data'
|
|
10
11
|
export * from './api/enhancement-category'
|
|
11
|
-
export * from './api/game-map-data'
|
|
12
12
|
export * from './api/link'
|
|
13
|
-
export * from './api/
|
|
14
|
-
export * from './api/
|
|
13
|
+
export * from './api/location-data'
|
|
14
|
+
export * from './api/level-range-data'
|
|
15
|
+
export * from './api/markdown-string'
|
|
16
|
+
export * from './api/mission-data'
|
|
17
|
+
export * from './api/mission-flashback-data'
|
|
18
|
+
export * from './api/mission-type'
|
|
19
|
+
export * from './api/morality'
|
|
20
|
+
export * from './api/set-title-data'
|
|
15
21
|
export * from './api/sex'
|
|
16
|
-
export * from './api/
|
|
17
|
-
export * from './api/
|
|
22
|
+
export * from './api/variant-context'
|
|
23
|
+
export * from './api/variant-data'
|
|
24
|
+
export * from './api/zone-data'
|
|
25
|
+
export * from './api/zone-type'
|
|
18
26
|
|
|
19
27
|
// DB
|
|
28
|
+
export * from './db/alignment-list'
|
|
20
29
|
export * from './db/archetype'
|
|
21
30
|
export * from './db/badge'
|
|
22
|
-
export * from './db/badge-
|
|
31
|
+
export * from './db/badge-index'
|
|
32
|
+
export * from './db/badge-requirement'
|
|
33
|
+
export * from './db/badge-search-options'
|
|
34
|
+
export * from './db/bundle-header'
|
|
23
35
|
export * from './db/coh-content-database'
|
|
24
|
-
export * from './db/
|
|
36
|
+
export * from './db/contact'
|
|
25
37
|
export * from './db/key'
|
|
26
|
-
export * from './db/
|
|
27
|
-
export * from './db/
|
|
28
|
-
export * from './db/
|
|
38
|
+
export * from './db/location'
|
|
39
|
+
export * from './db/level-range'
|
|
40
|
+
export * from './db/mission'
|
|
41
|
+
export * from './db/morality-list'
|
|
42
|
+
export * from './db/paged'
|
|
43
|
+
export * from './db/set-title-ids'
|
|
44
|
+
export * from './db/variants'
|
|
45
|
+
export * from './db/zone'
|
|
29
46
|
|
|
30
|
-
//
|
|
31
|
-
export
|
|
32
|
-
|
|
33
|
-
export * from './util'
|
|
47
|
+
// UTILS
|
|
48
|
+
export * from './util/links'
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* For fields that accept either an array of values or a single value, coalesces the value to an array.
|
|
3
|
+
*
|
|
4
|
+
* Arrays are returned as-is.
|
|
5
|
+
* Single values are returned as a single-value array.
|
|
6
|
+
* Undefined values are returned as undefined.
|
|
7
|
+
*
|
|
8
|
+
* @param value The value to coalesce.
|
|
9
|
+
*/
|
|
10
|
+
export function coalesceToArray<T>(value?: T | T[]): T[] | undefined {
|
|
11
|
+
if (!value) return undefined
|
|
12
|
+
return Array.isArray(value) ? value as T[] : [value]
|
|
13
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { BadgeData } from '../api/badge-data'
|
|
2
|
+
import { Badge } from '../db/badge'
|
|
3
|
+
import { ZoneData } from '../api/zone-data'
|
|
4
|
+
import { Zone } from '../db/zone'
|
|
5
|
+
import { Contact } from '../db/contact'
|
|
6
|
+
import { ContactData } from '../api/contact-data'
|
|
7
|
+
import { Mission } from '../db/mission'
|
|
8
|
+
import { MissionData } from '../api/mission-data'
|
|
9
|
+
|
|
10
|
+
/**
|
|
11
|
+
* Returns the URI of the given badge that can be used in {@link MarkdownString} fields.
|
|
12
|
+
*
|
|
13
|
+
* URI format: `badge://<key>`
|
|
14
|
+
*
|
|
15
|
+
* @param target The badge or badge key to target.
|
|
16
|
+
*/
|
|
17
|
+
export function badgeUri(target: string | Badge | BadgeData): string {
|
|
18
|
+
const key = typeof target === 'string' ? target : target.key
|
|
19
|
+
return `badge://${key}`
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Returns a {@link MarkdownString} link to the given badge.
|
|
24
|
+
*
|
|
25
|
+
* Link format: `[<key>](badge://<key>)`
|
|
26
|
+
*
|
|
27
|
+
* @param target The {@link Badge} or badge key to target.
|
|
28
|
+
*/
|
|
29
|
+
export function badgeLink(target: string | Badge | BadgeData): string {
|
|
30
|
+
const key = typeof target === 'string' ? target : target.key
|
|
31
|
+
return `[${key}](${badgeUri(target)})`
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Returns the URI of the given contact that can be used in {@link MarkdownString} fields.
|
|
36
|
+
*
|
|
37
|
+
* URI format: `contact://<key>`
|
|
38
|
+
*
|
|
39
|
+
* @param target The {@link Contact} or contact key to target.
|
|
40
|
+
*/
|
|
41
|
+
export function contactUri(target: string | Contact | ContactData): string {
|
|
42
|
+
const key = typeof target === 'string' ? target : target.key
|
|
43
|
+
return `contact://${key}`
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Returns a {@link MarkdownString} link to the given contact.
|
|
48
|
+
*
|
|
49
|
+
* Link format: `[<key>](contact://<key>)`
|
|
50
|
+
*
|
|
51
|
+
* @param target The {@link Contact} or contact key to target.
|
|
52
|
+
*/
|
|
53
|
+
export function contactLink(target: string | Contact | ContactData): string {
|
|
54
|
+
const key = typeof target === 'string' ? target : target.key
|
|
55
|
+
return `[${key}](${contactUri(target)})`
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Returns the URI of the given mission that can be used in {@link MarkdownString} fields.
|
|
60
|
+
*
|
|
61
|
+
* URI format: `mission://<key>`
|
|
62
|
+
*
|
|
63
|
+
* @param target The {@link Mission} or mission key to target.
|
|
64
|
+
*/
|
|
65
|
+
export function missionUri(target: string | Mission | MissionData): string {
|
|
66
|
+
const key = typeof target === 'string' ? target : target.key
|
|
67
|
+
return `mission://${key}`
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Returns a {@link MarkdownString} link to the given mission.
|
|
72
|
+
*
|
|
73
|
+
* Link format: `[<key>](mission://<key>)`
|
|
74
|
+
*
|
|
75
|
+
* @param target The {@link Mission} or mission key to target.
|
|
76
|
+
*/
|
|
77
|
+
export function missionLink(target: string | Mission | MissionData): string {
|
|
78
|
+
const key = typeof target === 'string' ? target : target.key
|
|
79
|
+
return `[${key}](${missionUri(target)})`
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
/**
|
|
83
|
+
* Returns the URI of the given zone that can be used in {@link MarkdownString} fields.
|
|
84
|
+
*
|
|
85
|
+
* URI format: `zone://<key>`
|
|
86
|
+
*
|
|
87
|
+
* @param target The {@link Zone} or zone key to target.
|
|
88
|
+
*/
|
|
89
|
+
export function zoneUri(target: string | Zone | ZoneData): string {
|
|
90
|
+
const key = typeof target === 'string' ? target : target.key
|
|
91
|
+
return `zone://${key}`
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Returns a {@link MarkdownString} link to the given zone.
|
|
96
|
+
*
|
|
97
|
+
* Link format: `[<key>](zone://<key>)`
|
|
98
|
+
*
|
|
99
|
+
* @param target The {@link Zone} or zone key to target.
|
|
100
|
+
*/
|
|
101
|
+
export function zoneLink(target: string | Zone | ZoneData): string {
|
|
102
|
+
const key = typeof target === 'string' ? target : target.key
|
|
103
|
+
return `[${key}](${zoneUri(target)})`
|
|
104
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Converts an iso string to a Date object, throwing an error if the iso string is invalid.
|
|
3
|
+
* @param iso ISO-8601 Date string
|
|
4
|
+
*/
|
|
5
|
+
export function toDate(iso: string): Date {
|
|
6
|
+
const date = new Date(iso)
|
|
7
|
+
if (!date || Number.isNaN(date.getTime())) throw new Error(`Invalid date format: [${iso}]`)
|
|
8
|
+
return date
|
|
9
|
+
}
|