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.
Files changed (120) hide show
  1. package/.editorconfig +10 -11
  2. package/.github/workflows/build.yml +4 -2
  3. package/.github/workflows/pull-request.yml +1 -1
  4. package/.github/workflows/release.yml +1 -1
  5. package/CHANGELOG.md +47 -0
  6. package/README.md +77 -32
  7. package/dist/coh-content-db.d.ts +755 -290
  8. package/dist/coh-content-db.js +1028 -358
  9. package/dist/coh-content-db.js.map +1 -1
  10. package/dist/coh-content-db.mjs +998 -349
  11. package/dist/coh-content-db.mjs.map +1 -1
  12. package/eslint.config.mjs +1 -0
  13. package/jest.config.mjs +1 -0
  14. package/package.json +14 -14
  15. package/src/main/api/alignment.ts +18 -2
  16. package/src/main/api/badge-data.ts +29 -51
  17. package/src/main/api/badge-requirement-data.ts +64 -0
  18. package/src/main/api/badge-requirement-type.ts +32 -0
  19. package/src/main/api/badge-type.ts +15 -15
  20. package/src/main/api/bundle-data.ts +47 -0
  21. package/src/main/api/bundle-header-data.ts +44 -0
  22. package/src/main/api/contact-data.ts +49 -0
  23. package/src/main/api/enhancement-category.ts +26 -26
  24. package/src/main/api/level-range-data.ts +4 -0
  25. package/src/main/api/location-data.ts +28 -0
  26. package/src/main/api/markdown-string.ts +4 -0
  27. package/src/main/api/mission-data.ts +57 -0
  28. package/src/main/api/mission-flashback-data.ts +31 -0
  29. package/src/main/api/mission-type.ts +2 -0
  30. package/src/main/api/morality.ts +49 -0
  31. package/src/main/api/set-title-data.ts +4 -0
  32. package/src/main/api/sex.ts +8 -1
  33. package/src/main/api/variant-context.ts +11 -0
  34. package/src/main/api/variant-data.ts +22 -0
  35. package/src/main/api/zone-data.ts +44 -0
  36. package/src/main/api/zone-type.ts +59 -0
  37. package/src/main/db/abstract-index.ts +37 -0
  38. package/src/main/db/alignment-list.ts +54 -0
  39. package/src/main/db/badge-index.ts +83 -0
  40. package/src/main/db/badge-requirement.ts +81 -0
  41. package/src/main/db/badge-search-options.ts +52 -0
  42. package/src/main/db/badge.ts +97 -74
  43. package/src/main/db/bundle-header.ts +52 -0
  44. package/src/main/db/coh-content-database.ts +123 -14
  45. package/src/main/db/contact.ts +63 -0
  46. package/src/main/db/level-range.ts +15 -0
  47. package/src/main/db/location.ts +30 -0
  48. package/src/main/db/mission.ts +108 -0
  49. package/src/main/db/morality-list.ts +99 -0
  50. package/src/main/db/paged.ts +11 -0
  51. package/src/main/db/set-title-ids.ts +10 -0
  52. package/src/main/db/variants.ts +84 -0
  53. package/src/main/db/zone.ts +57 -0
  54. package/src/main/index.ts +33 -18
  55. package/src/main/util/coalesce-to-array.ts +13 -0
  56. package/src/main/util/links.ts +104 -0
  57. package/src/main/util/to-date.ts +9 -0
  58. package/src/test/api/alignment.test.ts +38 -4
  59. package/src/test/api/badge-data.fixture.ts +2 -15
  60. package/src/test/api/badge-data.test.ts +5 -4
  61. package/src/test/api/badge-requirement-data.fixture.ts +7 -0
  62. package/src/test/api/badge-requirement-type.test.ts +31 -0
  63. package/src/test/api/badge-type.test.ts +5 -5
  64. package/src/test/api/bundle-data.fixture.ts +7 -0
  65. package/src/test/api/bundle-header-data.fixture.ts +8 -0
  66. package/src/test/api/contact-data.fixture.ts +7 -0
  67. package/src/test/api/enhancement-category.test.ts +5 -5
  68. package/src/test/api/mission-data.fixture.ts +12 -0
  69. package/src/test/api/morality.test.ts +31 -0
  70. package/src/test/api/sex.test.ts +33 -1
  71. package/src/test/api/zone-data.fixture.ts +9 -0
  72. package/src/test/db/abstract-index.test.ts +55 -0
  73. package/src/test/db/alignment-list.test.ts +200 -0
  74. package/src/test/db/badge-index.test.ts +653 -0
  75. package/src/test/db/badge-requirement.test.ts +145 -0
  76. package/src/test/db/badge.test.ts +416 -14
  77. package/src/test/db/bundle-header.test.ts +89 -0
  78. package/src/test/db/coh-content-database.test.ts +265 -24
  79. package/src/test/db/contact.test.ts +98 -0
  80. package/src/test/db/level-range.test.ts +47 -0
  81. package/src/test/db/location.test.ts +51 -0
  82. package/src/test/db/mission.test.ts +173 -0
  83. package/src/test/db/morality-list.test.ts +457 -0
  84. package/src/test/db/set-title-ids.test.ts +19 -0
  85. package/src/test/db/variants.test.ts +188 -0
  86. package/src/test/db/zone.test.ts +81 -0
  87. package/src/test/integration.test.ts +16 -0
  88. package/src/test/util/coalese-to-array.test.ts +17 -0
  89. package/src/test/util/links.test.ts +149 -0
  90. package/src/test/util/to-date.test.ts +15 -0
  91. package/src/main/api/alternate-data.ts +0 -22
  92. package/src/main/api/badge-partial-data.ts +0 -65
  93. package/src/main/api/badge-partial-type.ts +0 -8
  94. package/src/main/api/change.ts +0 -14
  95. package/src/main/api/game-map-data.ts +0 -26
  96. package/src/main/api/plaque-type.ts +0 -6
  97. package/src/main/api/server-group-data.ts +0 -65
  98. package/src/main/api/vidiot-map-data.ts +0 -18
  99. package/src/main/api/vidiot-map-point-of-interest-data.ts +0 -30
  100. package/src/main/changelog.ts +0 -20
  101. package/src/main/db/alternates.ts +0 -81
  102. package/src/main/db/badge-partial.ts +0 -35
  103. package/src/main/db/game-map.ts +0 -33
  104. package/src/main/db/server-group.ts +0 -112
  105. package/src/main/db/vidiot-map-point-of-interest.ts +0 -40
  106. package/src/main/db/vidiot-map.ts +0 -25
  107. package/src/main/util.ts +0 -17
  108. package/src/test/api/badge-partial-data.fixture.ts +0 -17
  109. package/src/test/api/badge-partial-type.test.ts +0 -31
  110. package/src/test/api/game-map-data.fixture.ts +0 -10
  111. package/src/test/api/plaque-type.test.ts +0 -31
  112. package/src/test/api/server-group-data.fixture.ts +0 -23
  113. package/src/test/api/server-group-data.test.ts +0 -15
  114. package/src/test/api/vidiot-map-point-of-interest.fixture.ts +0 -10
  115. package/src/test/api/vidiot-map.fixture.ts +0 -9
  116. package/src/test/changelog.test.ts +0 -36
  117. package/src/test/db/alternates.test.ts +0 -223
  118. package/src/test/db/server-group.test.ts +0 -124
  119. package/src/test/index.test.ts +0 -10
  120. package/src/test/util.test.ts +0 -39
@@ -0,0 +1,47 @@
1
+ import { ArchetypeData } from './archetype-data'
2
+ import { ZoneData } from './zone-data'
3
+ import { BadgeData } from './badge-data'
4
+ import { ContactData } from './contact-data'
5
+ import { MissionData } from './mission-data'
6
+ import { BundleHeaderData } from './bundle-header-data'
7
+
8
+ /**
9
+ * A bundle of game data from a forked instance of the game, such as Homecoming (https://forums.homecomingservers.com/).
10
+ */
11
+ export interface BundleData {
12
+ /**
13
+ * Bundle header.
14
+ */
15
+ readonly header: BundleHeaderData
16
+
17
+ /**
18
+ * List of the game server names in this fork.
19
+ * Torchbearer, Excelsior, etc.
20
+ */
21
+ readonly servers?: string[]
22
+
23
+ /**
24
+ * List of archetypes available in this fork.
25
+ */
26
+ readonly archetypes?: ArchetypeData[]
27
+
28
+ /**
29
+ * List of zones supported by this fork.
30
+ */
31
+ readonly zones?: ZoneData[]
32
+
33
+ /**
34
+ * List of contacts available in this fork.
35
+ */
36
+ readonly contacts?: ContactData[]
37
+
38
+ /**
39
+ * List of missions available in this fork.
40
+ */
41
+ readonly missions?: MissionData[]
42
+
43
+ /**
44
+ * List of badges available on this fork.
45
+ */
46
+ readonly badges?: BadgeData[]
47
+ }
@@ -0,0 +1,44 @@
1
+ import { Link } from './link'
2
+ import { MarkdownString } from './markdown-string'
3
+
4
+ /**
5
+ * Metadata about a content bundle.
6
+ */
7
+ export interface BundleHeaderData {
8
+ /**
9
+ * Name of the fork this bundle contains data for.
10
+ */
11
+ readonly name: string
12
+
13
+ /**
14
+ * Version number for this data package.
15
+ */
16
+ readonly version: string
17
+
18
+ /**
19
+ * The time this bundle was last updated.
20
+ *
21
+ * Must be an ISO-8601 string in UTC.
22
+ */
23
+ readonly lastUpdateTime: string
24
+
25
+ /**
26
+ * Description of the fork.
27
+ */
28
+ readonly description?: MarkdownString
29
+
30
+ /**
31
+ * Url for the repository where the bundle is maintained.
32
+ */
33
+ readonly repositoryUrl?: string
34
+
35
+ /**
36
+ * Url for the location of the changelog.
37
+ */
38
+ readonly changelogUrl?: string
39
+
40
+ /**
41
+ * List of external links. Wiki, forums, etc.
42
+ */
43
+ readonly links?: Link[]
44
+ }
@@ -0,0 +1,49 @@
1
+ import { Link } from './link'
2
+ import { MarkdownString } from './markdown-string'
3
+ import { LocationData } from './location-data'
4
+ import { MoralityExtended } from './morality'
5
+ import { LevelRangeData } from './level-range-data'
6
+
7
+ export interface ContactData {
8
+ /**
9
+ * Unique key used to reference this contact.
10
+ *
11
+ * Keys must be unique and can only contain lowercase letters, numbers and hyphens (`-`).
12
+ */
13
+ readonly key: string
14
+
15
+ /**
16
+ * The name of this contact.
17
+ */
18
+ readonly name: string
19
+
20
+ /**
21
+ * The contact's title.
22
+ */
23
+ readonly title?: string
24
+
25
+ /**
26
+ * The character moralities that this contact will interact with.
27
+ */
28
+ readonly morality?: MoralityExtended | MoralityExtended[]
29
+
30
+ /**
31
+ * The location of this contact.
32
+ */
33
+ readonly location?: LocationData
34
+
35
+ /**
36
+ * The level range this contact will offer missions for.
37
+ */
38
+ readonly levelRange?: LevelRangeData
39
+
40
+ /**
41
+ * Freeform notes or tips about the contact.
42
+ */
43
+ readonly notes?: MarkdownString
44
+
45
+ /**
46
+ * List of external links. Wiki, forums, etc.
47
+ */
48
+ readonly links?: Link[]
49
+ }
@@ -1,30 +1,30 @@
1
1
  export const ENHANCEMENT_CATEGORY = [
2
- 'DEFENSE_DEBUFF',
3
- 'TO_HIT_DEBUFF',
4
- 'TAUNT',
5
- 'CONFUSE',
6
- 'HEALING',
7
- 'DEFENSE_BUFF',
8
- 'RESIST_DAMAGE',
9
- 'INTANGIBILITY',
10
- 'SLEEP',
11
- 'SLOW',
12
- 'HOLD',
13
- 'STUN',
14
- 'IMMOBILIZE',
15
- 'FEAR',
16
- 'ENDURANCE_MODIFICATION',
17
- 'ENDURANCE_REDUCTION',
18
- 'RECHARGE_REDUCTION',
19
- 'INTERRUPT_DURATION',
20
- 'ACCURACY',
21
- 'TO_HIT_BUFF',
22
- 'DAMAGE',
23
- 'KNOCKBACK',
24
- 'RUN_SPEED',
25
- 'JUMP',
26
- 'FLY_SPEED',
27
- 'RANGE',
2
+ 'defense-debuff',
3
+ 'to-hit-debuff',
4
+ 'taunt',
5
+ 'confuse',
6
+ 'healing',
7
+ 'defense-buff',
8
+ 'resist-damage',
9
+ 'intangibility',
10
+ 'sleep',
11
+ 'slow',
12
+ 'hold',
13
+ 'stun',
14
+ 'immobilize',
15
+ 'fear',
16
+ 'endurance-modification',
17
+ 'endurance-reduction',
18
+ 'recharge-reduction',
19
+ 'interrupt-duration',
20
+ 'accuracy',
21
+ 'to-hit-buff',
22
+ 'damage',
23
+ 'knockback',
24
+ 'run-speed',
25
+ 'jump',
26
+ 'fly-speed',
27
+ 'range',
28
28
  ] as const
29
29
 
30
30
  export type EnhancementCategory = typeof ENHANCEMENT_CATEGORY[number]
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Can be an array of [min, max], just the [min], or a number representing the minimum value
3
+ */
4
+ export type LevelRangeData = [number, number?] | number
@@ -0,0 +1,28 @@
1
+ export interface LocationData {
2
+ /**
3
+ * Key of the {@link Zone} that the location references.
4
+ */
5
+ readonly zoneKey?: string
6
+
7
+ /**
8
+ * In-game `/loc` coordinates of the location.
9
+ */
10
+ readonly coords?: Coords
11
+
12
+ /**
13
+ * The type of icon to use if the location appears on a map. (Typically the Vidiot map icon).
14
+ */
15
+ readonly icon?: LocationIcon
16
+
17
+ /**
18
+ * The text that should appear in the location icon. (Typically a number or symbol from the Vidiot map).
19
+ */
20
+ readonly iconText?: string
21
+ }
22
+
23
+ /**
24
+ * Coordinates as they appear using the in-game `/loc` command.
25
+ */
26
+ export type Coords = [number, number, number]
27
+
28
+ export type LocationIcon = 'badge' | 'plaque' | 'pedestal' | 'object'
@@ -0,0 +1,4 @@
1
+ /**
2
+ * Tage type to indicate that a string will accept {@link https://www.markdownguide.org/|Markdown} format.
3
+ */
4
+ export type MarkdownString = string
@@ -0,0 +1,57 @@
1
+ import { Link } from './link'
2
+ import { MarkdownString } from './markdown-string'
3
+ import { MissionType } from './mission-type'
4
+ import { MoralityExtended } from './morality'
5
+ import { LevelRangeData } from './level-range-data'
6
+ import { MissionFlashbackData } from './mission-flashback-data'
7
+
8
+ export interface MissionData {
9
+ /**
10
+ * Unique key used to reference this mission.
11
+ *
12
+ * Keys must be unique and can only contain lowercase letters, numbers and hyphens (`-`).
13
+ */
14
+ readonly key: string
15
+
16
+ /**
17
+ * The name of the mission as it appears from the contact.
18
+ *
19
+ * The name may be different when viewed in Ouroboros as a Flashback.
20
+ */
21
+ readonly name: string
22
+
23
+ /**
24
+ * The type of mission... Story arc, task force, trial, etc.
25
+ */
26
+ readonly type: MissionType
27
+
28
+ /**
29
+ * The character moralities that may accept the mission.
30
+ */
31
+ readonly morality?: MoralityExtended | MoralityExtended[]
32
+
33
+ /**
34
+ * The keys of any contacts that provide this mission.
35
+ */
36
+ readonly contactKeys?: string | string[]
37
+
38
+ /**
39
+ * The level range this mission is available for.
40
+ */
41
+ readonly levelRange?: LevelRangeData
42
+
43
+ /**
44
+ * Freeform notes or tips about the mission.
45
+ */
46
+ readonly notes?: MarkdownString
47
+
48
+ /**
49
+ * List of external links. Wiki, forums, etc.
50
+ */
51
+ readonly links?: Link[]
52
+
53
+ /**
54
+ * If the mission is available in Ouroboros as a Flashback.
55
+ */
56
+ readonly flashback?: MissionFlashbackData
57
+ }
@@ -0,0 +1,31 @@
1
+ import { MarkdownString } from './markdown-string'
2
+ import { MoralityExtended } from './morality'
3
+ import { LevelRangeData } from './level-range-data'
4
+
5
+ export interface MissionFlashbackData {
6
+
7
+ /**
8
+ * The id of the mission as seen in the Flashback menu, i.e. '14.01'.
9
+ */
10
+ readonly id: string
11
+
12
+ /**
13
+ * The level range this mission appears under as a Flashback.
14
+ */
15
+ readonly levelRange?: LevelRangeData
16
+
17
+ /**
18
+ * The name as it appears in the Flashback list. Leave undefined if the same as the base mission.
19
+ */
20
+ readonly name?: string
21
+
22
+ /**
23
+ * The character moralities that the mission will appear for in the Flashback list. Leave undefined if the same as the base mission.
24
+ */
25
+ readonly morality?: MoralityExtended | MoralityExtended[]
26
+
27
+ /**
28
+ * Freeform notes or tips about the Flashback version of the mission.
29
+ */
30
+ readonly notes?: MarkdownString
31
+ }
@@ -0,0 +1,2 @@
1
+ export const MISSION_TYPE = ['story-arc', 'mission', 'task-force', 'strike-force', 'trial', 'personal-story'] as const
2
+ export type MissionType = typeof MISSION_TYPE[number]
@@ -0,0 +1,49 @@
1
+ import { Alignment } from './alignment'
2
+
3
+ export const MORALITY = ['hero', 'vigilante', 'villain', 'rogue', 'resistance', 'loyalist'] as const
4
+ export const MORALITY_EXTENDED = [
5
+ ...MORALITY,
6
+ /**
7
+ * Any of the Primal Earth moralities - Hero, Vigilante, Villain, Rogue.
8
+ */
9
+ 'primal',
10
+ /**
11
+ * Either of the Praetorian Earth moralities - Resistance or Loyalist.
12
+ */
13
+ 'praetorian',
14
+ /**
15
+ * The moralities that roll up to the Hero {@link Alignment} - Hero and Vigilante.
16
+ */
17
+ 'heroic',
18
+ /**
19
+ * The moralities that roll up to the Villain {@link Alignment} - Villain and Rogue.
20
+ */
21
+ 'villainous',
22
+ /**
23
+ * Moralities with access to Paragon City - Hero, Vigilante and Rogue.
24
+ */
25
+ 'paragon-city-access',
26
+ /**
27
+ * Moralities with access to the Rogue Isles - Villain, Rogue and Vigilante.
28
+ */
29
+ 'rogue-isles-access',
30
+ /**
31
+ * All the moralities.
32
+ */
33
+ 'all',
34
+ ] as const
35
+ export type Morality = typeof MORALITY[number]
36
+ export type MoralityExtended = typeof MORALITY_EXTENDED[number]
37
+
38
+ /**
39
+ * Maps a morality to the underlying alignment
40
+ */
41
+ export const MoralityMap: Record<Morality | Alignment, Alignment> = {
42
+ hero: 'hero',
43
+ vigilante: 'hero',
44
+ villain: 'villain',
45
+ rogue: 'villain',
46
+ loyalist: 'praetorian',
47
+ resistance: 'praetorian',
48
+ praetorian: 'praetorian',
49
+ }
@@ -0,0 +1,4 @@
1
+ /**
2
+ * The id, or a pair of ids [primal, praetorian] that are used with the /settitle command to set a badge
3
+ */
4
+ export type SetTitleData = [number, number?]
@@ -1,3 +1,10 @@
1
1
  export const SEX = ['M', 'F'] as const
2
-
3
2
  export type Sex = typeof SEX[number]
3
+
4
+ const SEX_ORDER = Object.fromEntries(SEX.map((x, index) => [x, index]))
5
+
6
+ export function compareSex(a?: Sex, b?: Sex): number {
7
+ const orderA = a ? SEX_ORDER[a] : -1
8
+ const orderB = b ? SEX_ORDER[b] : -1
9
+ return orderA - orderB
10
+ }
@@ -0,0 +1,11 @@
1
+ import { Sex } from './sex'
2
+ import { Morality } from './morality'
3
+ import { Alignment } from './alignment'
4
+
5
+ /**
6
+ * For badges, aspects like the name, icon, or badge text can vary depending on context, such as the alignment or sex of the character.
7
+ */
8
+ export interface VariantContext {
9
+ readonly morality?: Morality | Alignment
10
+ readonly sex?: Sex
11
+ }
@@ -0,0 +1,22 @@
1
+ import { Sex } from './sex'
2
+ import { Alignment } from './alignment'
3
+
4
+ /**
5
+ * Some badge values differ based on the alignment or sex of a character.
6
+ */
7
+ export interface VariantData<V> {
8
+ /**
9
+ * The character alignment this variant applies to.
10
+ */
11
+ readonly alignment?: Alignment
12
+
13
+ /**
14
+ * The character sex this variant applies to.
15
+ */
16
+ readonly sex?: Sex
17
+
18
+ /**
19
+ * The value for this combination.
20
+ */
21
+ readonly value: V
22
+ }
@@ -0,0 +1,44 @@
1
+ import { Link } from './link'
2
+ import { MoralityExtended } from './morality'
3
+ import { ZoneType } from './zone-type'
4
+ import { MarkdownString } from './markdown-string'
5
+ import { LevelRangeData } from './level-range-data'
6
+
7
+ export interface ZoneData {
8
+ /**
9
+ * Unique key used to reference this zone.
10
+ *
11
+ * Keys must be unique and can only contain lowercase letters, numbers and hyphens (`-`).
12
+ */
13
+ readonly key: string
14
+
15
+ /**
16
+ * The name of the zone as it appears in-game.
17
+ */
18
+ readonly name: string
19
+
20
+ /**
21
+ * The type of zone.
22
+ */
23
+ readonly type: ZoneType
24
+
25
+ /**
26
+ * The character moralities that this zone is accessible by.
27
+ */
28
+ readonly morality?: MoralityExtended | MoralityExtended[]
29
+
30
+ /**
31
+ * The level range this zone is recommended for.
32
+ */
33
+ readonly levelRange?: LevelRangeData
34
+
35
+ /**
36
+ * Freeform notes or tips about the zone.
37
+ */
38
+ readonly notes?: MarkdownString
39
+
40
+ /**
41
+ * List of external links. Wiki, forums, etc.
42
+ */
43
+ readonly links?: Link[]
44
+ }
@@ -0,0 +1,59 @@
1
+ export const ZONE_TYPE = [
2
+ /**
3
+ * The standard zone type, even if not technically occurring in the 'City' proper.
4
+ */
5
+ 'city',
6
+ /**
7
+ * An Ouroboros flashback to a zone as it was in a previous era.
8
+ */
9
+ 'echo',
10
+ /**
11
+ * Tutorial zon, usually inaccessible after leaving.
12
+ */
13
+ 'tutorial',
14
+ /**
15
+ * Trial zones, like the Abandoned Sewers trial.
16
+ */
17
+ 'trial',
18
+ /**
19
+ * Hazard zones like the Hollows.
20
+ */
21
+ 'hazard',
22
+ /**
23
+ * Mayhem mission zones.
24
+ */
25
+ 'mayhem',
26
+ /**
27
+ * Safeguard mission zones.
28
+ */
29
+ 'safeguard',
30
+ /**
31
+ * Exists inside a mission not covered by the other types.
32
+ */
33
+ 'mission',
34
+ /**
35
+ * Incarnate trial zones.
36
+ */
37
+ 'incarnate',
38
+ /**
39
+ * Cooprative zones where Heroes and Villains can team up for PvE content.
40
+ */
41
+ 'co-op',
42
+ /**
43
+ * PvP zones like Bloody Bay.
44
+ */
45
+ 'pvp',
46
+ /**
47
+ * Located in an arena PvP map.
48
+ */
49
+ 'arena',
50
+ /**
51
+ * A building, usually contained within another zone, like the AE buildings.
52
+ */
53
+ 'building',
54
+ /**
55
+ * Stuff like the (Phone only) zone.
56
+ */
57
+ 'other',
58
+ ] as const
59
+ export type ZoneType = typeof ZONE_TYPE[number]
@@ -0,0 +1,37 @@
1
+ type KeysOfType<T, V> = { [P in keyof T]: T[P] extends V ? P : never }[keyof T]
2
+
3
+ export class AbstractIndex<T> {
4
+ protected _values: T[] = []
5
+ protected _hashTable: Record<string, T> = {}
6
+
7
+ /**
8
+ * Create a new index.
9
+ * @param keyField The field of the values that will act as the key.
10
+ * @param values Values to index.
11
+ */
12
+ constructor(keyField: KeysOfType<T, string>, values: T[] | undefined) {
13
+ this._values = values ?? []
14
+ this._hashTable = {}
15
+ for (const value of this.values) {
16
+ const key = value[keyField] as string
17
+ if (this._hashTable[key] !== undefined) throw new Error(`Duplicate key [${key}]`)
18
+ this._hashTable[key] = value
19
+ }
20
+ }
21
+
22
+ /**
23
+ * Return all indexed values
24
+ */
25
+ get values(): T[] {
26
+ return this._values
27
+ }
28
+
29
+ /**
30
+ * Get a value from the index
31
+ * @param key Key string
32
+ */
33
+ get(key: string | undefined): T | undefined {
34
+ if (!key) return undefined
35
+ return this._hashTable[key]
36
+ }
37
+ }
@@ -0,0 +1,54 @@
1
+ import { ALIGNMENT, Alignment, AlignmentExtended } from '../api/alignment'
2
+
3
+ export class AlignmentList {
4
+ readonly #items: Set<Alignment>
5
+
6
+ readonly hero: boolean
7
+ readonly villain: boolean
8
+ readonly praetorian: boolean
9
+
10
+ readonly primal: boolean
11
+ readonly all: boolean
12
+
13
+ constructor(items?: AlignmentExtended[]) {
14
+ const set = new Set(items ?? [...ALIGNMENT])
15
+ this.hero = set.has('hero') || set.has('primal') || set.has('all')
16
+ this.villain = set.has('villain') || set.has('primal') || set.has('all')
17
+ this.praetorian = set.has('praetorian') || set.has('all')
18
+
19
+ this.primal = this.hero && this.villain
20
+ this.all = this.hero && this.villain && this.praetorian
21
+
22
+ this.#items = new Set()
23
+ if (this.hero) this.#items.add('hero')
24
+ if (this.villain) this.#items.add('villain')
25
+ if (this.praetorian) this.#items.add('praetorian')
26
+ }
27
+
28
+ get items(): Alignment[] {
29
+ return [...this.#items]
30
+ }
31
+
32
+ has(alignment?: AlignmentExtended): boolean {
33
+ switch (alignment) {
34
+ case 'hero': {
35
+ return this.hero
36
+ }
37
+ case 'villain': {
38
+ return this.villain
39
+ }
40
+ case 'praetorian': {
41
+ return this.praetorian
42
+ }
43
+ case 'primal' : {
44
+ return this.primal
45
+ }
46
+ case 'all': {
47
+ return this.all
48
+ }
49
+ default: {
50
+ return false
51
+ }
52
+ }
53
+ }
54
+ }