coh-content-db 2.0.0-rc.6 → 2.0.0-rc.7
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 +3 -3
- package/dist/coh-content-db.d.ts +384 -181
- package/dist/coh-content-db.js +604 -348
- package/dist/coh-content-db.js.map +1 -1
- package/dist/coh-content-db.mjs +592 -347
- package/dist/coh-content-db.mjs.map +1 -1
- package/eslint.config.mjs +1 -0
- package/package.json +1 -1
- package/src/main/api/alignment.ts +18 -2
- package/src/main/api/badge-data.ts +12 -42
- package/src/main/api/badge-requirement-data.ts +17 -35
- package/src/main/api/badge-requirement-type.ts +28 -7
- package/src/main/api/badge-type.ts +15 -15
- package/src/main/api/contact-data.ts +7 -5
- package/src/main/api/content-bundle.ts +6 -0
- package/src/main/api/enhancement-category.ts +26 -26
- package/src/main/api/location-data.ts +28 -0
- package/src/main/api/mission-data.ts +83 -0
- package/src/main/api/mission-type.ts +2 -0
- package/src/main/api/morality.ts +31 -0
- package/src/main/api/sex.ts +8 -1
- package/src/main/api/zone-data.ts +1 -1
- package/src/main/changelog.ts +5 -4
- package/src/main/db/alignment-list.ts +54 -0
- package/src/main/db/alternates.ts +15 -32
- package/src/main/db/badge-index.ts +11 -36
- package/src/main/db/badge-requirement.ts +22 -43
- package/src/main/db/badge-search-options.ts +4 -4
- package/src/main/db/badge.ts +53 -54
- package/src/main/db/coh-content-database.ts +28 -24
- package/src/main/db/contact.ts +17 -14
- package/src/main/db/location.ts +30 -0
- package/src/main/db/mission.ts +107 -0
- package/src/main/db/morality-list.ts +99 -0
- package/src/main/db/zone.ts +1 -1
- package/src/main/index.ts +8 -3
- package/src/main/util.ts +43 -3
- package/src/test/api/alignment.test.ts +38 -4
- package/src/test/api/badge-data.fixture.ts +1 -17
- package/src/test/api/badge-data.test.ts +3 -3
- package/src/test/api/badge-requirement-data.fixture.ts +1 -11
- package/src/test/api/badge-requirement-type.test.ts +3 -3
- package/src/test/api/badge-type.test.ts +5 -5
- package/src/test/api/contact-data.fixture.ts +0 -6
- package/src/test/api/content-bundle.fixture.ts +1 -17
- package/src/test/api/enhancement-category.test.ts +5 -5
- package/src/test/api/mission-data.fixture.ts +12 -0
- package/src/test/api/sex.test.ts +33 -1
- package/src/test/api/zone-data.fixture.ts +1 -1
- package/src/test/db/alignment-list.test.ts +200 -0
- package/src/test/db/alternates.test.ts +60 -56
- package/src/test/db/badge-index.test.ts +82 -72
- package/src/test/db/badge-requirement.test.ts +35 -70
- package/src/test/db/badge.test.ts +185 -64
- package/src/test/db/coh-content-database.test.ts +58 -69
- package/src/test/db/contact.test.ts +25 -24
- package/src/test/db/location.test.ts +51 -0
- package/src/test/db/mission.test.ts +171 -0
- package/src/test/db/morality-list.test.ts +457 -0
- package/src/test/db/zone.test.ts +4 -4
- package/src/test/util.test.ts +54 -1
- package/src/main/api/plaque-type.ts +0 -6
- package/src/main/db/alignments.ts +0 -17
- package/src/test/api/alignments.test.ts +0 -40
- package/src/test/api/plaque-type.test.ts +0 -31
package/src/main/db/badge.ts
CHANGED
|
@@ -4,11 +4,13 @@ import { BadgeData } from '../api/badge-data'
|
|
|
4
4
|
import { BadgeRequirement } from './badge-requirement'
|
|
5
5
|
import { Key } from './key'
|
|
6
6
|
import { Alternates } from './alternates'
|
|
7
|
-
import { Alignments } from './alignments'
|
|
8
7
|
import { MarkdownString } from '../api/markdown-string'
|
|
8
|
+
import { coalesceToArray } from '../util'
|
|
9
|
+
import { MoralityList } from './morality-list'
|
|
9
10
|
|
|
10
11
|
export class Badge {
|
|
11
12
|
readonly #requirementsIndex: Record<string, BadgeRequirement> = {}
|
|
13
|
+
readonly #zoneKeys = new Set<string>()
|
|
12
14
|
|
|
13
15
|
/**
|
|
14
16
|
* The database key for this badge.
|
|
@@ -28,14 +30,14 @@ export class Badge {
|
|
|
28
30
|
readonly name: Alternates<string>
|
|
29
31
|
|
|
30
32
|
/**
|
|
31
|
-
* The character
|
|
33
|
+
* The character moralities that this badge is available to.
|
|
32
34
|
*/
|
|
33
|
-
readonly
|
|
35
|
+
readonly morality: MoralityList
|
|
34
36
|
|
|
35
37
|
/**
|
|
36
38
|
* The badge text as it appears in-game. May vary by character sex or alignment.
|
|
37
39
|
*/
|
|
38
|
-
readonly badgeText: Alternates<
|
|
40
|
+
readonly badgeText: Alternates<MarkdownString>
|
|
39
41
|
|
|
40
42
|
/**
|
|
41
43
|
* Short description of how to acquire the badge. Detailed instructions will be in the notes field.
|
|
@@ -60,33 +62,10 @@ export class Badge {
|
|
|
60
62
|
readonly links: Link[]
|
|
61
63
|
|
|
62
64
|
/**
|
|
63
|
-
*
|
|
65
|
+
* The id used with the in-game `/settitle` command to apply the badge.
|
|
66
|
+
* The first value is the id for primal characters and the (optional) second number is the id for praetorian characters.
|
|
64
67
|
*/
|
|
65
|
-
readonly
|
|
66
|
-
|
|
67
|
-
/**
|
|
68
|
-
* For exploration badges, the `/loc` coordinates of the badge.
|
|
69
|
-
*/
|
|
70
|
-
readonly loc?: [number, number, number]
|
|
71
|
-
|
|
72
|
-
/**
|
|
73
|
-
* For plaques that appear on a Vidiot Map, the number or letter the badge appears as.
|
|
74
|
-
*/
|
|
75
|
-
readonly vidiotMapKey?: string
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* ID used with the in-game `/settitle` command to apply the badge.
|
|
79
|
-
*/
|
|
80
|
-
readonly setTitle?: {
|
|
81
|
-
/**
|
|
82
|
-
* `/settitle` id.
|
|
83
|
-
*/
|
|
84
|
-
id?: number
|
|
85
|
-
/**
|
|
86
|
-
* `/settitle` id if different for praetorian characters.
|
|
87
|
-
*/
|
|
88
|
-
praetorianId?: number
|
|
89
|
-
}
|
|
68
|
+
readonly setTitleId?: [number, number?]
|
|
90
69
|
|
|
91
70
|
/**
|
|
92
71
|
* A description of the effect the badge will have, such as a buff or granting a temporary power.
|
|
@@ -94,16 +73,9 @@ export class Badge {
|
|
|
94
73
|
readonly effect?: MarkdownString
|
|
95
74
|
|
|
96
75
|
/**
|
|
97
|
-
* Represents the
|
|
98
|
-
* such as visiting plaques for history badges or collecting other badges.
|
|
99
|
-
*
|
|
100
|
-
* The outer array represents groups of requirements evaluated with OR logic —
|
|
101
|
-
* fulfilling any group satisfies the badge.
|
|
102
|
-
*
|
|
103
|
-
* Each inner array represents individual requirements evaluated with AND logic —
|
|
104
|
-
* all conditions in the group must be met.
|
|
76
|
+
* Represents the requirements for badges with multiple fulfillment steps, such as visiting monuments for history badges, completing missions, or collecting other badges.
|
|
105
77
|
*/
|
|
106
|
-
readonly requirements?: BadgeRequirement[]
|
|
78
|
+
readonly requirements?: BadgeRequirement[]
|
|
107
79
|
|
|
108
80
|
/**
|
|
109
81
|
* Some badges are not included in the badge total count... such as Flames of Prometheus, which can be removed by redeeming it for a Notice of the Well.
|
|
@@ -114,29 +86,24 @@ export class Badge {
|
|
|
114
86
|
this.key = new Key(badgeData.key).value
|
|
115
87
|
this.type = badgeData.type
|
|
116
88
|
this.name = new Alternates(badgeData.name)
|
|
117
|
-
this.
|
|
89
|
+
this.morality = new MoralityList(coalesceToArray(badgeData.morality))
|
|
118
90
|
this.badgeText = new Alternates(badgeData.badgeText ?? [])
|
|
119
91
|
this.acquisition = badgeData.acquisition
|
|
120
92
|
this.icon = new Alternates(badgeData.icon ?? [])
|
|
121
93
|
this.notes = badgeData.notes
|
|
122
94
|
this.links = badgeData.links ?? []
|
|
123
|
-
this.zoneKey = badgeData.zoneKey
|
|
124
|
-
this.loc = badgeData.loc
|
|
125
95
|
this.effect = badgeData.effect
|
|
126
|
-
this.
|
|
127
|
-
this.setTitle = badgeData.setTitle
|
|
96
|
+
this.setTitleId = badgeData.setTitleId
|
|
128
97
|
this.ignoreInTotals = badgeData.ignoreInTotals ?? false
|
|
129
98
|
|
|
130
|
-
this.requirements = badgeData.requirements?.map((
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
return badge
|
|
139
|
-
})
|
|
99
|
+
this.requirements = badgeData.requirements?.map((requirementData) => {
|
|
100
|
+
if (this.#requirementsIndex[requirementData.key]) throw new Error(`Duplicate badge requirement key [${badgeData.key}:${requirementData.key}]`)
|
|
101
|
+
const requirement = new BadgeRequirement(requirementData)
|
|
102
|
+
this.#requirementsIndex[requirement.key] = requirement
|
|
103
|
+
if (requirement.location) for (const location of requirement.location) {
|
|
104
|
+
if (location.zoneKey) this.#zoneKeys.add(location.zoneKey)
|
|
105
|
+
}
|
|
106
|
+
return requirement
|
|
140
107
|
})
|
|
141
108
|
}
|
|
142
109
|
|
|
@@ -145,4 +112,36 @@ export class Badge {
|
|
|
145
112
|
if (result === undefined) throw new Error(`Unknown badge requirement key [${key}]`)
|
|
146
113
|
return result
|
|
147
114
|
}
|
|
115
|
+
|
|
116
|
+
/**
|
|
117
|
+
* Return a list of all the zone keys referenced by this badge.
|
|
118
|
+
*/
|
|
119
|
+
get zoneKeys(): string[] {
|
|
120
|
+
return [...this.#zoneKeys]
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* The zone key if this badge relates to a single zone.
|
|
125
|
+
*/
|
|
126
|
+
get zoneKey(): string | undefined {
|
|
127
|
+
return this.#zoneKeys.size === 1 ? this.#zoneKeys.values().next().value : undefined
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
export function compareByDefaultName(a?: Badge, b?: Badge): number {
|
|
132
|
+
const aName = a?.name.default?.value
|
|
133
|
+
const bName = b?.name.default?.value
|
|
134
|
+
if (!aName && !bName) return 0
|
|
135
|
+
if (!aName) return 1
|
|
136
|
+
if (!bName) return -1
|
|
137
|
+
return aName.localeCompare(bName)
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
export function compareByZoneKey(a?: Badge, b?: Badge): number {
|
|
141
|
+
const aZone = a?.zoneKey
|
|
142
|
+
const bZone = b?.zoneKey
|
|
143
|
+
if (!aZone && !bZone) return 0
|
|
144
|
+
if (!aZone) return 1
|
|
145
|
+
if (!bZone) return -1
|
|
146
|
+
return aZone.localeCompare(bZone)
|
|
148
147
|
}
|
|
@@ -7,11 +7,13 @@ import { BadgeIndex } from './badge-index'
|
|
|
7
7
|
import { BadgeSearchOptions } from './badge-search-options'
|
|
8
8
|
import { Paged } from './paged'
|
|
9
9
|
import { Contact } from './contact'
|
|
10
|
+
import { Mission } from './mission'
|
|
10
11
|
|
|
11
12
|
export class CohContentDatabase {
|
|
12
13
|
readonly #archetypeIndex: Record<string, Archetype> = {}
|
|
13
14
|
readonly #zoneIndex: Record<string, Zone> = {}
|
|
14
15
|
readonly #contactIndex: Record<string, Contact> = {}
|
|
16
|
+
readonly #missionIndex: Record<string, Mission> = {}
|
|
15
17
|
readonly #badgeIndex: BadgeIndex
|
|
16
18
|
|
|
17
19
|
/**
|
|
@@ -41,6 +43,11 @@ export class CohContentDatabase {
|
|
|
41
43
|
*/
|
|
42
44
|
readonly contacts: Contact[]
|
|
43
45
|
|
|
46
|
+
/**
|
|
47
|
+
* List of missions.
|
|
48
|
+
*/
|
|
49
|
+
readonly missions: Contact[]
|
|
50
|
+
|
|
44
51
|
/**
|
|
45
52
|
* List of badges.
|
|
46
53
|
*/
|
|
@@ -75,44 +82,41 @@ export class CohContentDatabase {
|
|
|
75
82
|
return contact
|
|
76
83
|
}) ?? []
|
|
77
84
|
|
|
78
|
-
this.
|
|
79
|
-
|
|
80
|
-
|
|
85
|
+
this.missions = bundle.missions?.map((data) => {
|
|
86
|
+
if (this.#missionIndex[data.key] !== undefined) throw new Error(`Duplicate mission key '${data.key}'`)
|
|
87
|
+
const mission = new Mission(data)
|
|
88
|
+
this.#missionIndex[mission.key] = mission
|
|
89
|
+
return mission
|
|
90
|
+
}) ?? []
|
|
81
91
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
if (result === undefined) throw new Error(`Unknown archetype key '${key}'`)
|
|
85
|
-
return result
|
|
92
|
+
this.badges = bundle.badges?.map(data => new Badge(data)) ?? []
|
|
93
|
+
this.#badgeIndex = new BadgeIndex(this.badges)
|
|
86
94
|
}
|
|
87
95
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
return result
|
|
96
|
+
getArchetype(key?: string): Archetype | undefined {
|
|
97
|
+
if (!key) return undefined
|
|
98
|
+
return this.#archetypeIndex[key]
|
|
92
99
|
}
|
|
93
100
|
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
return result
|
|
101
|
+
getZone(key?: string): Zone | undefined {
|
|
102
|
+
if (!key) return undefined
|
|
103
|
+
return this.#zoneIndex[key]
|
|
98
104
|
}
|
|
99
105
|
|
|
100
|
-
|
|
101
|
-
return
|
|
106
|
+
getContact(key?: string): Contact | undefined {
|
|
107
|
+
if (!key) return undefined
|
|
108
|
+
return this.#contactIndex[key]
|
|
102
109
|
}
|
|
103
110
|
|
|
104
|
-
|
|
105
|
-
return
|
|
111
|
+
getMission(key?: string): Mission | undefined {
|
|
112
|
+
if (!key) return undefined
|
|
113
|
+
return this.#missionIndex[key]
|
|
106
114
|
}
|
|
107
115
|
|
|
108
|
-
getBadge(key
|
|
116
|
+
getBadge(key?: string): Badge | undefined {
|
|
109
117
|
return this.#badgeIndex.getBadge(key)
|
|
110
118
|
}
|
|
111
119
|
|
|
112
|
-
badgeExists(key: string): boolean {
|
|
113
|
-
return this.#badgeIndex.badgeExists(key)
|
|
114
|
-
}
|
|
115
|
-
|
|
116
120
|
/**
|
|
117
121
|
* Search, sort and filter the badge list.
|
|
118
122
|
* This is a fairly brute-forced approach and will not be as performant as loading the badge data into a traditional
|
package/src/main/db/contact.ts
CHANGED
|
@@ -2,12 +2,15 @@ import { Link } from '../api/link'
|
|
|
2
2
|
import { Key } from './key'
|
|
3
3
|
import { MarkdownString } from '../api/markdown-string'
|
|
4
4
|
import { ContactData } from '../api/contact-data'
|
|
5
|
+
import { Location } from './location'
|
|
6
|
+
import { MoralityList } from './morality-list'
|
|
7
|
+
import { coalesceToArray } from '../util'
|
|
5
8
|
|
|
6
9
|
export class Contact {
|
|
7
10
|
/**
|
|
8
11
|
* Unique key used to reference this contact.
|
|
9
12
|
*
|
|
10
|
-
* Keys can only contain lowercase letters, numbers and hyphens (`-`).
|
|
13
|
+
* Keys must be unique and can only contain lowercase letters, numbers and hyphens (`-`).
|
|
11
14
|
*/
|
|
12
15
|
readonly key: string
|
|
13
16
|
|
|
@@ -22,14 +25,14 @@ export class Contact {
|
|
|
22
25
|
readonly title?: string
|
|
23
26
|
|
|
24
27
|
/**
|
|
25
|
-
* The
|
|
28
|
+
* The character moralities that this contact will interact with.
|
|
26
29
|
*/
|
|
27
|
-
readonly
|
|
30
|
+
readonly morality?: MoralityList
|
|
28
31
|
|
|
29
32
|
/**
|
|
30
|
-
* The
|
|
33
|
+
* The location of this contact.
|
|
31
34
|
*/
|
|
32
|
-
readonly
|
|
35
|
+
readonly location?: Location
|
|
33
36
|
|
|
34
37
|
/**
|
|
35
38
|
* The level range this contact will offer missions for.
|
|
@@ -46,14 +49,14 @@ export class Contact {
|
|
|
46
49
|
*/
|
|
47
50
|
readonly links: Link[]
|
|
48
51
|
|
|
49
|
-
constructor(
|
|
50
|
-
this.key = new Key(
|
|
51
|
-
this.name =
|
|
52
|
-
this.title =
|
|
53
|
-
this.
|
|
54
|
-
this.
|
|
55
|
-
this.levelRange =
|
|
56
|
-
this.notes =
|
|
57
|
-
this.links =
|
|
52
|
+
constructor(data: ContactData) {
|
|
53
|
+
this.key = new Key(data.key).value
|
|
54
|
+
this.name = data.name
|
|
55
|
+
this.title = data.title
|
|
56
|
+
this.morality = new MoralityList(coalesceToArray(data.morality))
|
|
57
|
+
this.location = data.location
|
|
58
|
+
this.levelRange = data.levelRange
|
|
59
|
+
this.notes = data.notes
|
|
60
|
+
this.links = data.links ?? []
|
|
58
61
|
}
|
|
59
62
|
}
|
|
@@ -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,107 @@
|
|
|
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 { coalesceToArray } from '../util'
|
|
7
|
+
import { MoralityList } from './morality-list'
|
|
8
|
+
|
|
9
|
+
export class Mission {
|
|
10
|
+
/**
|
|
11
|
+
* Unique key used to reference this mission.
|
|
12
|
+
*
|
|
13
|
+
* Keys must be unique and can only contain lowercase letters, numbers and hyphens (`-`).
|
|
14
|
+
*/
|
|
15
|
+
readonly key: string
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* The name of the mission as it appears from the contact.
|
|
19
|
+
*
|
|
20
|
+
* The name may be different when viewed in Ouroboros as a Flashback.
|
|
21
|
+
*/
|
|
22
|
+
readonly name: string
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* The type of mission... Story arc, task force, trial, etc.
|
|
26
|
+
*/
|
|
27
|
+
readonly type: MissionType
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* The character moralities that may accept the mission.
|
|
31
|
+
*/
|
|
32
|
+
readonly morality: MoralityList
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* The keys of any contacts that provide this mission.
|
|
36
|
+
*/
|
|
37
|
+
readonly contactKeys?: string[]
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* The level range this mission is available for.
|
|
41
|
+
*/
|
|
42
|
+
readonly levelRange?: [number, number?]
|
|
43
|
+
|
|
44
|
+
/**
|
|
45
|
+
* Freeform notes or tips about the mission.
|
|
46
|
+
*/
|
|
47
|
+
readonly notes?: MarkdownString
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* List of external links. Wiki, forums, etc.
|
|
51
|
+
*/
|
|
52
|
+
readonly links: Link[]
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* If the mission is available in Ouroboros as a Flashback.
|
|
56
|
+
*/
|
|
57
|
+
readonly flashback?: {
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* The id of the mission as seen in the Flashback menu, i.e. '14.01'.
|
|
61
|
+
*/
|
|
62
|
+
readonly id: string
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* The level range this mission appears under as a Flashback. Leave undefined if the same as the base mission.
|
|
66
|
+
*/
|
|
67
|
+
readonly levelRange?: [number, number?]
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* The name as it appears in the Flashback list. Leave undefined if the same as the base mission.
|
|
71
|
+
*/
|
|
72
|
+
readonly name?: string
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* The character moralities that the mission will appear for in the Flashback list. Leave undefined if the same as the base mission.
|
|
76
|
+
*/
|
|
77
|
+
readonly morality?: MoralityList
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Freeform notes or tips about the Flashback version of the mission.
|
|
81
|
+
*/
|
|
82
|
+
readonly notes?: MarkdownString
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
constructor(data: MissionData) {
|
|
86
|
+
this.key = new Key(data.key).value
|
|
87
|
+
this.name = data.name
|
|
88
|
+
this.type = data.type
|
|
89
|
+
this.morality = new MoralityList(coalesceToArray(data.morality))
|
|
90
|
+
this.contactKeys = coalesceToArray(data.contactKeys)
|
|
91
|
+
this.levelRange = data.levelRange
|
|
92
|
+
this.notes = data.notes
|
|
93
|
+
this.links = data.links ?? []
|
|
94
|
+
this.flashback = createFlashback(data)
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function createFlashback(data: MissionData): Mission['flashback'] {
|
|
99
|
+
if (!data.flashback) return undefined
|
|
100
|
+
return {
|
|
101
|
+
id: data.flashback.id,
|
|
102
|
+
levelRange: data.flashback.levelRange ?? data.levelRange,
|
|
103
|
+
name: data.flashback.name ?? data.name,
|
|
104
|
+
morality: new MoralityList(coalesceToArray(data.flashback.morality ?? data.morality)),
|
|
105
|
+
notes: data.flashback.notes,
|
|
106
|
+
}
|
|
107
|
+
}
|
|
@@ -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
|
+
}
|
package/src/main/db/zone.ts
CHANGED
|
@@ -6,7 +6,7 @@ export class Zone {
|
|
|
6
6
|
/**
|
|
7
7
|
* Unique key used to reference this zone.
|
|
8
8
|
*
|
|
9
|
-
* Keys can only contain lowercase letters, numbers and hyphens (`-`).
|
|
9
|
+
* Keys must be unique and can only contain lowercase letters, numbers and hyphens (`-`).
|
|
10
10
|
*/
|
|
11
11
|
readonly key: string
|
|
12
12
|
|
package/src/main/index.ts
CHANGED
|
@@ -11,13 +11,16 @@ export * from './api/contact-data'
|
|
|
11
11
|
export * from './api/content-bundle'
|
|
12
12
|
export * from './api/enhancement-category'
|
|
13
13
|
export * from './api/link'
|
|
14
|
+
export * from './api/location-data'
|
|
14
15
|
export * from './api/markdown-string'
|
|
15
|
-
export * from './api/
|
|
16
|
+
export * from './api/mission-data'
|
|
17
|
+
export * from './api/mission-type'
|
|
18
|
+
export * from './api/morality'
|
|
16
19
|
export * from './api/sex'
|
|
17
20
|
export * from './api/zone-data'
|
|
18
21
|
|
|
19
22
|
// DB
|
|
20
|
-
export * from './db/
|
|
23
|
+
export * from './db/alignment-list'
|
|
21
24
|
export * from './db/alternates'
|
|
22
25
|
export * from './db/archetype'
|
|
23
26
|
export * from './db/badge'
|
|
@@ -28,10 +31,12 @@ export * from './db/bundle-metadata'
|
|
|
28
31
|
export * from './db/coh-content-database'
|
|
29
32
|
export * from './db/contact'
|
|
30
33
|
export * from './db/key'
|
|
34
|
+
export * from './db/location'
|
|
35
|
+
export * from './db/mission'
|
|
36
|
+
export * from './db/morality-list'
|
|
31
37
|
export * from './db/paged'
|
|
32
38
|
export * from './db/zone'
|
|
33
39
|
|
|
34
40
|
// ROOT
|
|
35
41
|
export { CHANGELOG } from './changelog'
|
|
36
|
-
|
|
37
42
|
export * from './util'
|
package/src/main/util.ts
CHANGED
|
@@ -4,9 +4,11 @@ import { ZoneData } from './api/zone-data'
|
|
|
4
4
|
import { Zone } from './db/zone'
|
|
5
5
|
import { Contact } from './db/contact'
|
|
6
6
|
import { ContactData } from './api/contact-data'
|
|
7
|
+
import { Mission } from './db/mission'
|
|
8
|
+
import { MissionData } from './api/mission-data'
|
|
7
9
|
|
|
8
10
|
/**
|
|
9
|
-
* Returns the URI of the given badge that can be used in {@link MarkdownString}
|
|
11
|
+
* Returns the URI of the given badge that can be used in {@link MarkdownString} fields.
|
|
10
12
|
*
|
|
11
13
|
* URI format: `badge://<key>`
|
|
12
14
|
*
|
|
@@ -30,7 +32,7 @@ export function badgeLink(target: string | Badge | BadgeData): string {
|
|
|
30
32
|
}
|
|
31
33
|
|
|
32
34
|
/**
|
|
33
|
-
* Returns the URI of the given contact that can be used in {@link MarkdownString}
|
|
35
|
+
* Returns the URI of the given contact that can be used in {@link MarkdownString} fields.
|
|
34
36
|
*
|
|
35
37
|
* URI format: `contact://<key>`
|
|
36
38
|
*
|
|
@@ -54,7 +56,31 @@ export function contactLink(target: string | Contact | ContactData): string {
|
|
|
54
56
|
}
|
|
55
57
|
|
|
56
58
|
/**
|
|
57
|
-
* Returns the URI of the given
|
|
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.
|
|
58
84
|
*
|
|
59
85
|
* URI format: `zone://<key>`
|
|
60
86
|
*
|
|
@@ -76,3 +102,17 @@ export function zoneLink(target: string | Zone | ZoneData): string {
|
|
|
76
102
|
const key = typeof target === 'string' ? target : target.key
|
|
77
103
|
return `[${key}](${zoneUri(target)})`
|
|
78
104
|
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* For fields that accept either an array of values or a single value, coalesces the value to an array.
|
|
108
|
+
*
|
|
109
|
+
* Arrays are returned as-is.
|
|
110
|
+
* Single values are returned as a single-value array.
|
|
111
|
+
* Undefined values are returned as undefined.
|
|
112
|
+
*
|
|
113
|
+
* @param value The value to coalesce.
|
|
114
|
+
*/
|
|
115
|
+
export function coalesceToArray<T>(value?: T | T[]): T[] | undefined {
|
|
116
|
+
if (!value) return undefined
|
|
117
|
+
return Array.isArray(value) ? value as T[] : [value]
|
|
118
|
+
}
|