coh-content-db 2.0.0-rc.5 → 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.
Files changed (82) hide show
  1. package/README.md +7 -7
  2. package/dist/coh-content-db.d.ts +493 -243
  3. package/dist/coh-content-db.js +675 -353
  4. package/dist/coh-content-db.js.map +1 -1
  5. package/dist/coh-content-db.mjs +655 -345
  6. package/dist/coh-content-db.mjs.map +1 -1
  7. package/eslint.config.mjs +1 -0
  8. package/package.json +1 -1
  9. package/src/main/api/alignment.ts +18 -2
  10. package/src/main/api/badge-data.ts +15 -38
  11. package/src/main/api/badge-requirement-data.ts +64 -0
  12. package/src/main/api/badge-requirement-type.ts +32 -0
  13. package/src/main/api/badge-type.ts +15 -15
  14. package/src/main/api/contact-data.ts +48 -0
  15. package/src/main/api/content-bundle.ts +16 -4
  16. package/src/main/api/enhancement-category.ts +26 -26
  17. package/src/main/api/location-data.ts +28 -0
  18. package/src/main/api/mission-data.ts +83 -0
  19. package/src/main/api/mission-type.ts +2 -0
  20. package/src/main/api/morality.ts +31 -0
  21. package/src/main/api/sex.ts +8 -1
  22. package/src/main/api/zone-data.ts +20 -0
  23. package/src/main/changelog.ts +8 -3
  24. package/src/main/db/alignment-list.ts +54 -0
  25. package/src/main/db/alternates.ts +15 -32
  26. package/src/main/db/badge-index.ts +17 -36
  27. package/src/main/db/badge-requirement.ts +81 -0
  28. package/src/main/db/badge-search-options.ts +5 -5
  29. package/src/main/db/badge.ts +73 -62
  30. package/src/main/db/bundle-metadata.ts +3 -3
  31. package/src/main/db/coh-content-database.ts +60 -23
  32. package/src/main/db/contact.ts +62 -0
  33. package/src/main/db/location.ts +30 -0
  34. package/src/main/db/mission.ts +107 -0
  35. package/src/main/db/morality-list.ts +99 -0
  36. package/src/main/db/zone.ts +28 -0
  37. package/src/main/index.ts +16 -13
  38. package/src/main/util.ts +85 -14
  39. package/src/test/api/alignment.test.ts +38 -4
  40. package/src/test/api/badge-data.fixture.ts +1 -15
  41. package/src/test/api/badge-data.test.ts +3 -3
  42. package/src/test/api/badge-requirement-data.fixture.ts +7 -0
  43. package/src/test/api/badge-requirement-type.test.ts +31 -0
  44. package/src/test/api/badge-type.test.ts +5 -5
  45. package/src/test/api/contact-data.fixture.ts +7 -0
  46. package/src/test/api/content-bundle.fixture.ts +1 -17
  47. package/src/test/api/content-bundle.test.ts +1 -1
  48. package/src/test/api/enhancement-category.test.ts +5 -5
  49. package/src/test/api/mission-data.fixture.ts +12 -0
  50. package/src/test/api/sex.test.ts +33 -1
  51. package/src/test/api/zone-data.fixture.ts +8 -0
  52. package/src/test/db/alignment-list.test.ts +200 -0
  53. package/src/test/db/alternates.test.ts +60 -56
  54. package/src/test/db/badge-index.test.ts +108 -66
  55. package/src/test/db/badge-requirement.test.ts +145 -0
  56. package/src/test/db/badge.test.ts +310 -14
  57. package/src/test/db/coh-content-database.test.ts +110 -29
  58. package/src/test/db/contact.test.ts +97 -0
  59. package/src/test/db/location.test.ts +51 -0
  60. package/src/test/db/mission.test.ts +171 -0
  61. package/src/test/db/morality-list.test.ts +457 -0
  62. package/src/test/db/zone.test.ts +36 -0
  63. package/src/test/index.test.ts +4 -2
  64. package/src/test/util.test.ts +112 -22
  65. package/src/main/api/badge-partial-data.ts +0 -66
  66. package/src/main/api/badge-partial-type.ts +0 -8
  67. package/src/main/api/game-map-data.ts +0 -26
  68. package/src/main/api/plaque-type.ts +0 -6
  69. package/src/main/api/vidiot-map-data.ts +0 -18
  70. package/src/main/api/vidiot-map-point-of-interest-data.ts +0 -30
  71. package/src/main/db/alignments.ts +0 -17
  72. package/src/main/db/badge-partial.ts +0 -83
  73. package/src/main/db/game-map.ts +0 -33
  74. package/src/main/db/vidiot-map-point-of-interest.ts +0 -39
  75. package/src/main/db/vidiot-map.ts +0 -25
  76. package/src/test/api/alignments.test.ts +0 -40
  77. package/src/test/api/badge-partial-data.fixture.ts +0 -17
  78. package/src/test/api/badge-partial-type.test.ts +0 -31
  79. package/src/test/api/game-map-data.fixture.ts +0 -10
  80. package/src/test/api/plaque-type.test.ts +0 -31
  81. package/src/test/api/vidiot-map-point-of-interest.fixture.ts +0 -10
  82. package/src/test/api/vidiot-map.fixture.ts +0 -9
@@ -1,14 +1,16 @@
1
1
  import { BadgeType } from '../api/badge-type'
2
2
  import { Link } from '../api/link'
3
3
  import { BadgeData } from '../api/badge-data'
4
- import { BadgePartial } from './badge-partial'
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
- readonly #partialsIndex: Record<string, BadgePartial> = {}
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,17 +30,17 @@ export class Badge {
28
30
  readonly name: Alternates<string>
29
31
 
30
32
  /**
31
- * The character alignments that this badge is available to.
33
+ * The character moralities that this badge is available to.
32
34
  */
33
- readonly alignment: Alignments
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<string>
40
+ readonly badgeText: Alternates<MarkdownString>
39
41
 
40
42
  /**
41
- * Description of how to acquire the badge.
43
+ * Short description of how to acquire the badge. Detailed instructions will be in the notes field.
42
44
  */
43
45
  readonly acquisition?: MarkdownString
44
46
 
@@ -55,38 +57,15 @@ export class Badge {
55
57
  readonly notes?: MarkdownString
56
58
 
57
59
  /**
58
- * List of external links for this Badge. Wiki, forums, etc.
60
+ * List of external links. Wiki, forums, etc.
59
61
  */
60
- readonly links?: Link[]
62
+ readonly links: Link[]
61
63
 
62
64
  /**
63
- * For exploration badges, the key of the {@link GameMap} that this badge is found on.
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 mapKey?: string
66
-
67
- /**
68
- * For exploration badges, the `/loc` coordinates of the badge on the in-game map.
69
- */
70
- readonly loc?: [number, number, number]
71
-
72
- /**
73
- * For badges 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,43 +73,75 @@ export class Badge {
94
73
  readonly effect?: MarkdownString
95
74
 
96
75
  /**
97
- * A list of requirements for badges that have partial fulfilment steps, such as visiting plaques for history badges, or collecting other badges for meta-badges like accolades.
76
+ * Represents the requirements for badges with multiple fulfillment steps, such as visiting monuments for history badges, completing missions, or collecting other badges.
98
77
  */
99
- readonly partials?: BadgePartial[]
78
+ readonly requirements?: BadgeRequirement[]
100
79
 
101
80
  /**
102
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.
103
82
  */
104
83
  readonly ignoreInTotals: boolean
105
84
 
106
- constructor(data: BadgeData) {
107
- this.key = new Key(data.key).value
108
- this.type = data.type
109
- this.name = new Alternates(data.name)
110
- this.alignment = new Alignments(data.alignment)
111
- this.badgeText = new Alternates(data.badgeText ?? [])
112
- this.acquisition = data.acquisition
113
- this.icon = new Alternates(data.icon ?? [])
114
- this.notes = data.notes
115
- this.links = data.links
116
- this.mapKey = data.mapKey
117
- this.loc = data.loc
118
- this.effect = data.effect
119
- this.vidiotMapKey = data.vidiotMapKey
120
- this.setTitle = data.setTitle
121
- this.ignoreInTotals = data.ignoreInTotals ?? false
122
-
123
- this.partials = data.partials?.map((data) => {
124
- if (this.#partialsIndex[data.key] !== undefined) throw new Error(`Duplicate badge partial key [${data.key}]`)
125
- const badge = new BadgePartial(data)
126
- this.#partialsIndex[badge.key] = badge
127
- return badge
85
+ constructor(badgeData: BadgeData) {
86
+ this.key = new Key(badgeData.key).value
87
+ this.type = badgeData.type
88
+ this.name = new Alternates(badgeData.name)
89
+ this.morality = new MoralityList(coalesceToArray(badgeData.morality))
90
+ this.badgeText = new Alternates(badgeData.badgeText ?? [])
91
+ this.acquisition = badgeData.acquisition
92
+ this.icon = new Alternates(badgeData.icon ?? [])
93
+ this.notes = badgeData.notes
94
+ this.links = badgeData.links ?? []
95
+ this.effect = badgeData.effect
96
+ this.setTitleId = badgeData.setTitleId
97
+ this.ignoreInTotals = badgeData.ignoreInTotals ?? false
98
+
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
128
107
  })
129
108
  }
130
109
 
131
- getPartial(key: string): BadgePartial {
132
- const result = this.#partialsIndex[key]
133
- if (result === undefined) throw new Error(`Unknown badge partial key [${key}]`)
110
+ getRequirement(key: string): BadgeRequirement {
111
+ const result = this.#requirementsIndex[key]
112
+ if (result === undefined) throw new Error(`Unknown badge requirement key [${key}]`)
134
113
  return result
135
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)
136
147
  }
@@ -5,12 +5,12 @@ import { MarkdownString } from '../api/markdown-string'
5
5
 
6
6
  export class BundleMetadata {
7
7
  /**
8
- * Name of the server group.
8
+ * Name of the content bundle.
9
9
  */
10
10
  readonly name: string
11
11
 
12
12
  /**
13
- * Description of the server group.
13
+ * Description of the fork.
14
14
  */
15
15
  readonly description?: MarkdownString
16
16
 
@@ -20,7 +20,7 @@ export class BundleMetadata {
20
20
  readonly repository?: string
21
21
 
22
22
  /**
23
- * List of external links for this Server Group. Wiki, forums, etc.
23
+ * List of external links. Wiki, forums, etc.
24
24
  */
25
25
  readonly links: Link[]
26
26
 
@@ -1,15 +1,19 @@
1
1
  import { ContentBundle } from '../api/content-bundle'
2
2
  import { Archetype } from './archetype'
3
- import { GameMap } from './game-map'
3
+ import { Zone } from './zone'
4
4
  import { Badge } from './badge'
5
5
  import { BundleMetadata } from './bundle-metadata'
6
6
  import { BadgeIndex } from './badge-index'
7
7
  import { BadgeSearchOptions } from './badge-search-options'
8
8
  import { Paged } from './paged'
9
+ import { Contact } from './contact'
10
+ import { Mission } from './mission'
9
11
 
10
12
  export class CohContentDatabase {
11
13
  readonly #archetypeIndex: Record<string, Archetype> = {}
12
- readonly #mapIndex: Record<string, GameMap> = {}
14
+ readonly #zoneIndex: Record<string, Zone> = {}
15
+ readonly #contactIndex: Record<string, Contact> = {}
16
+ readonly #missionIndex: Record<string, Mission> = {}
13
17
  readonly #badgeIndex: BadgeIndex
14
18
 
15
19
  /**
@@ -18,23 +22,34 @@ export class CohContentDatabase {
18
22
  readonly metadata: BundleMetadata
19
23
 
20
24
  /**
21
- * List of the game server names in this server group.
25
+ * List of the game server names.
26
+ *
22
27
  * Torchbearer, Excelsior, etc.
23
28
  */
24
29
  readonly servers: string[]
25
30
 
26
31
  /**
27
- * List of archetypes available in this server group.
32
+ * List of archetypes.
28
33
  */
29
34
  readonly archetypes: Archetype[]
30
35
 
31
36
  /**
32
- * List of game maps supported by this server group.
37
+ * List of game zones.
33
38
  */
34
- readonly maps: GameMap[]
39
+ readonly zones: Zone[]
35
40
 
36
41
  /**
37
- * List of badges available on this server group.
42
+ * List of contacts.
43
+ */
44
+ readonly contacts: Contact[]
45
+
46
+ /**
47
+ * List of missions.
48
+ */
49
+ readonly missions: Contact[]
50
+
51
+ /**
52
+ * List of badges.
38
53
  */
39
54
  readonly badges: Badge[]
40
55
 
@@ -47,36 +62,58 @@ export class CohContentDatabase {
47
62
  this.servers = bundle.servers ?? []
48
63
 
49
64
  this.archetypes = bundle.archetypes?.map((data) => {
50
- if (this.#archetypeIndex[data.key] !== undefined) throw new Error(`Duplicate archetype key [${data.key}]`)
65
+ if (this.#archetypeIndex[data.key] !== undefined) throw new Error(`Duplicate archetype key '${data.key}'`)
51
66
  const archetype = new Archetype(data)
52
67
  this.#archetypeIndex[archetype.key] = archetype
53
68
  return archetype
54
69
  }) ?? []
55
70
 
56
- this.maps = bundle.maps?.map((data) => {
57
- if (this.#mapIndex[data.key] !== undefined) throw new Error(`Duplicate map key [${data.key}]`)
58
- const map = new GameMap(data)
59
- this.#mapIndex[map.key] = map
60
- return map
71
+ this.zones = bundle.zones?.map((data) => {
72
+ if (this.#zoneIndex[data.key] !== undefined) throw new Error(`Duplicate zone key '${data.key}'`)
73
+ const zone = new Zone(data)
74
+ this.#zoneIndex[zone.key] = zone
75
+ return zone
76
+ }) ?? []
77
+
78
+ this.contacts = bundle.contacts?.map((data) => {
79
+ if (this.#contactIndex[data.key] !== undefined) throw new Error(`Duplicate contact key '${data.key}'`)
80
+ const contact = new Contact(data)
81
+ this.#contactIndex[contact.key] = contact
82
+ return contact
83
+ }) ?? []
84
+
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
61
90
  }) ?? []
62
91
 
63
92
  this.badges = bundle.badges?.map(data => new Badge(data)) ?? []
64
- this.#badgeIndex = new BadgeIndex(this.badges, this.maps)
93
+ this.#badgeIndex = new BadgeIndex(this.badges)
94
+ }
95
+
96
+ getArchetype(key?: string): Archetype | undefined {
97
+ if (!key) return undefined
98
+ return this.#archetypeIndex[key]
99
+ }
100
+
101
+ getZone(key?: string): Zone | undefined {
102
+ if (!key) return undefined
103
+ return this.#zoneIndex[key]
65
104
  }
66
105
 
67
- getArchetype(key: string): Archetype {
68
- const result = this.#archetypeIndex[key]
69
- if (result === undefined) throw new Error(`Unknown archetype key [${key}]`)
70
- return result
106
+ getContact(key?: string): Contact | undefined {
107
+ if (!key) return undefined
108
+ return this.#contactIndex[key]
71
109
  }
72
110
 
73
- getMap(key: string): GameMap {
74
- const result = this.#mapIndex[key]
75
- if (result === undefined) throw new Error(`Unknown map key [${key}]`)
76
- return result
111
+ getMission(key?: string): Mission | undefined {
112
+ if (!key) return undefined
113
+ return this.#missionIndex[key]
77
114
  }
78
115
 
79
- getBadge(key: string): Badge {
116
+ getBadge(key?: string): Badge | undefined {
80
117
  return this.#badgeIndex.getBadge(key)
81
118
  }
82
119
 
@@ -0,0 +1,62 @@
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'
8
+
9
+ export class Contact {
10
+ /**
11
+ * Unique key used to reference this contact.
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 this contact.
19
+ */
20
+ readonly name: string
21
+
22
+ /**
23
+ * The contact's title.
24
+ */
25
+ readonly title?: string
26
+
27
+ /**
28
+ * The character moralities that this contact will interact with.
29
+ */
30
+ readonly morality?: MoralityList
31
+
32
+ /**
33
+ * The location of this contact.
34
+ */
35
+ readonly location?: Location
36
+
37
+ /**
38
+ * The level range this contact will offer missions for.
39
+ */
40
+ readonly levelRange?: [number, number?]
41
+
42
+ /**
43
+ * Freeform notes or tips about the contact.
44
+ */
45
+ readonly notes?: MarkdownString
46
+
47
+ /**
48
+ * List of external links. Wiki, forums, etc.
49
+ */
50
+ readonly links: Link[]
51
+
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 ?? []
61
+ }
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
+ }
@@ -0,0 +1,28 @@
1
+ import { Link } from '../api/link'
2
+ import { ZoneData } from '../api/zone-data'
3
+ import { Key } from './key'
4
+
5
+ export class Zone {
6
+ /**
7
+ * Unique key used to reference this zone.
8
+ *
9
+ * Keys must be unique and can only contain lowercase letters, numbers and hyphens (`-`).
10
+ */
11
+ readonly key: string
12
+
13
+ /**
14
+ * The name of the zone as it appears in-game.
15
+ */
16
+ readonly name: string
17
+
18
+ /**
19
+ * List of external links. Wiki, forums, etc.
20
+ */
21
+ readonly links: Link[]
22
+
23
+ constructor(data: ZoneData) {
24
+ this.key = new Key(data.key).value
25
+ this.name = data.name
26
+ this.links = data.links ?? []
27
+ }
28
+ }