Skip to content

Commit 17d0b3e

Browse files
committed
Implement support for more fields
Handle the various tags and properties of Creature that aren't defined by entry.json, and aren't related to copying, versions, or variants.
1 parent 9e38e29 commit 17d0b3e

File tree

10 files changed

+1292
-73
lines changed

10 files changed

+1292
-73
lines changed

Sources/FifthEdition/Bestiary.swift

Lines changed: 145 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -59,12 +59,6 @@ public struct Creature: Equatable, Sendable {
5959
case choice(Set<FifthEdition.CreatureType>)
6060
}
6161

62-
public enum SidekickType: String, CaseIterable, Codable, Sendable {
63-
case expert
64-
case spellcaster
65-
case warrior
66-
}
67-
6862
@Init(label: "_")
6963
public var type: Choice
7064

@@ -174,6 +168,11 @@ public struct Creature: Equatable, Sendable {
174168
public var page: Page? = nil
175169
public var otherSources: Set<Source>? = nil
176170
public var additionalSources: Set<Source>? = nil
171+
public var inSrd: SrdReference? = nil
172+
public var inSrd52: SrdReference? = nil
173+
public var inBasicRules: Bool? = nil
174+
public var inBasicRules2024: Bool? = nil
175+
public var isLegacy: Bool? = nil
177176

178177
public var reprintedAs: Set<Reprint>? = nil
179178
public var isReprinted: Bool? = nil
@@ -203,7 +202,6 @@ public struct Creature: Equatable, Sendable {
203202
public var save: Save? = nil
204203
public var skill: SkillSet? = nil
205204
public var tool: ToolSet? = nil
206-
public var gear: [Gear]? = nil
207205
public var senses: [String]? = nil
208206
public var passive: Passive? = nil
209207
public var languages: Set<String>? = nil
@@ -214,6 +212,84 @@ public struct Creature: Equatable, Sendable {
214212
public var damageResistance: Set<DamageResistance>? = nil
215213
public var damageImmunity: Set<DamageImmunity>? = nil
216214
public var conditionImmunity: Set<ConditionImmunity>? = nil
215+
216+
public var conditionInflict: Set<Condition>? = nil
217+
public var conditionInflictLegendary: Set<Condition>? = nil
218+
public var conditionInflictSpell: Set<Condition>? = nil
219+
220+
public var savingThrowForced: Set<SavingThrow>? = nil
221+
public var savingThrowForcedLegendary: Set<SavingThrow>? = nil
222+
public var savingThrowForcedSpell: Set<SavingThrow>? = nil
223+
224+
public var damageDealt: Set<DamageType>? = nil
225+
public var damageDealtLegendary: Set<DamageType>? = nil
226+
public var damageDealtSpell: Set<DamageType>? = nil
227+
228+
public var environment: Set<Environment>? = nil
229+
230+
public var actionTags: Set<ActionTag>? = nil
231+
public var languageTags: Set<LanguageTag>? = nil
232+
public var miscTags: Set<MiscTag>? = nil
233+
public var senseTags: Set<SenseTag>? = nil
234+
public var spellcastingTags: Set<SpellcastingTag>? = nil
235+
public var traitTags: Set<TraitTag>? = nil
236+
237+
// TODO: spellcasting, array of entry.json/entrySpellcasting
238+
// TODO: trait, array of { name, entries: entry.json, type: entries/inset?, sort: Int? }
239+
240+
// TODO: public var actionNote: String? = nil
241+
// TODO: actionHeader, array of entry.json
242+
// TODO: action, array of { name, entries: entry.json }
243+
244+
// TODO: public var bonusNote: String? = nil
245+
// TODO: bonusHeader, array of entry.json
246+
// TODO: bonus, array of { name, entries: entry.json }
247+
248+
// TODO: public var reactionNote: String? = nil
249+
// TODO: reactionHeader, array of entry.json
250+
// TODO: reaction, array of { name, entries: entry.json }
251+
252+
// TODO: legendaryGroup, should be easy, array of {name, source}
253+
254+
// TODO: legendaryActions, ? defs/_legendaryActions
255+
// TODO: legendaryActionsLair, ? defs/_legendaryActions
256+
// TODO: legendaryHeader, array of entry.json
257+
// TODO: legendary, array of { name?, entries: entry.json }
258+
259+
// TODO: mythicHeader, array of entry.json
260+
// TODO: mythic, array of { name?, entries: entry.json }
261+
262+
// TODO: footer, array of entry.json
263+
264+
public var attachedItems: Set<String>? = nil
265+
public var gear: [Gear]? = nil
266+
public var treasure: Set<Treasure>? = nil
267+
268+
public var dragonCastingColor: DragonColor? = nil
269+
public var dragonAge: DragonAge? = nil
270+
271+
public var summonedBySpell: String? = nil
272+
public var summonedBySpellLevel: Int? = nil
273+
public var summonedByClass: String? = nil
274+
public var summonedScaleByPlayerLevel: Bool? = nil
275+
276+
public var canBeFamiliar: Bool? = nil
277+
public var isNamedCreature: Bool? = nil
278+
public var isNPC: Bool? = nil
279+
280+
public var hasToken: Bool? = nil
281+
public var token: Token? = nil
282+
public var tokenCredit: String? = nil
283+
public var isTokenCustom: Bool? = nil
284+
public var foundryTokenScale: Float? = nil
285+
public var hasFluff: Bool? = nil
286+
public var hasFluffImages: Bool? = nil
287+
public var art: [ArtItem]? = nil
288+
public var soundClip: MediaHref? = nil
289+
290+
// TODO: variant, ? defs/entryVariantBestiary
291+
// TODO: _isCopy Bool
292+
// TODO: _versions, array /creatureVersion
217293
}
218294

219295
// MARK: - Codable
@@ -231,6 +307,11 @@ extension Creature: Codable {
231307
case additionalSources
232308
case reprintedAs
233309
case isReprinted
310+
case inSrd = "srd"
311+
case inSrd52 = "srd52"
312+
case inBasicRules = "basicRules"
313+
case inBasicRules2024 = "basicRules2024"
314+
case isLegacy = "legacy"
234315
case level
235316
case size
236317
case sizeNote
@@ -249,7 +330,6 @@ extension Creature: Codable {
249330
case cha
250331
case save
251332
case skill
252-
case gear
253333
case tool
254334
case senses
255335
case passive
@@ -260,6 +340,43 @@ extension Creature: Codable {
260340
case damageResistance = "resist"
261341
case damageImmunity = "immune"
262342
case conditionImmunity = "conditionImmune"
343+
case conditionInflict
344+
case conditionInflictLegendary
345+
case conditionInflictSpell
346+
case savingThrowForced
347+
case savingThrowForcedLegendary
348+
case savingThrowForcedSpell
349+
case damageDealt = "damageTags"
350+
case damageDealtLegendary = "damageTagsLegendary"
351+
case damageDealtSpell = "damageTagsSpell"
352+
case environment
353+
case actionTags
354+
case languageTags
355+
case miscTags
356+
case senseTags
357+
case spellcastingTags
358+
case traitTags
359+
case attachedItems
360+
case gear
361+
case treasure
362+
case dragonCastingColor
363+
case dragonAge
364+
case summonedBySpell
365+
case summonedBySpellLevel
366+
case summonedByClass
367+
case summonedScaleByPlayerLevel
368+
case canBeFamiliar = "familiar"
369+
case isNamedCreature
370+
case isNPC
371+
case hasToken
372+
case token
373+
case tokenCredit
374+
case isTokenCustom = "tokenCustom"
375+
case foundryTokenScale
376+
case hasFluff
377+
case hasFluffImages
378+
case art = "altArt"
379+
case soundClip
263380
}
264381
}
265382

@@ -682,7 +799,6 @@ extension Creature.DamageVulnerability: Codable {
682799
try container.encodeIfPresent(cond, forKey: .conditional)
683800
}
684801
}
685-
686802
}
687803

688804
extension Creature.Gear: Codable {
@@ -869,8 +985,18 @@ extension Creature.SkillSet: Codable {
869985

870986
public init(from decoder: any Decoder) throws {
871987
let container = try decoder.container(keyedBy: CodingKeys.self)
872-
for key in CodingKeys.allCases {
873-
skills[key.value!] = try container.decodeIfPresent(String.self, forKey: key)
988+
for key in container.allKeys {
989+
if key.stringValue == "other" {
990+
continue
991+
}
992+
993+
guard let value = key.value else {
994+
throw DecodingError.dataCorruptedError(
995+
forKey: key,
996+
in: container,
997+
debugDescription: "Unknown skill: \(key)")
998+
}
999+
skills[value] = try container.decodeIfPresent(String.self, forKey: key)
8741000
}
8751001

8761002
if var array = try? container.nestedUnkeyedContainer(forKey: CodingKeys(stringValue: "other")) {
@@ -906,16 +1032,21 @@ extension Creature.SkillSet: Codable {
9061032
}
9071033
}
9081034
}
909-
9101035
}
9111036

9121037
extension Creature.ToolSet: Codable {
9131038
typealias CodingKeys = EnumCodingKey<Tool>
9141039

9151040
public init(from decoder: any Decoder) throws {
9161041
let container = try decoder.container(keyedBy: CodingKeys.self)
917-
for key in CodingKeys.allCases {
918-
tools[key.value!] = try container.decodeIfPresent(String.self, forKey: key)
1042+
for key in container.allKeys {
1043+
guard let value = key.value else {
1044+
throw DecodingError.dataCorruptedError(
1045+
forKey: key,
1046+
in: container,
1047+
debugDescription: "Unknown tool: \(key)")
1048+
}
1049+
tools[value] = try container.decodeIfPresent(String.self, forKey: key)
9191050
}
9201051
}
9211052

@@ -925,6 +1056,4 @@ extension Creature.ToolSet: Codable {
9251056
try container.encodeIfPresent(tools[key.value!], forKey: key)
9261057
}
9271058
}
928-
9291059
}
930-

Sources/FifthEdition/Entry.swift

Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
//
2+
// Entry.swift
3+
// FifthEdition
4+
//
5+
// Created by Scott James Remnant on 12/28/25.
6+
//
7+
8+
import Foundation
9+
10+
public enum MediaHref: Equatable, Sendable {
11+
case path(String)
12+
case url(URL)
13+
}
14+
15+
// MARK: - Codable
16+
17+
extension MediaHref: Codable {
18+
enum CodingKeys: String, CodingKey {
19+
case type
20+
case path
21+
case url
22+
}
23+
24+
public init(from decoder: any Decoder) throws {
25+
let container = try decoder.container(keyedBy: CodingKeys.self)
26+
if let url = try? container.decode(URL.self, forKey: .url) {
27+
self = .url(url)
28+
} else {
29+
self = .path(try container.decode(String.self, forKey: .path))
30+
}
31+
}
32+
33+
public func encode(to encoder: any Encoder) throws {
34+
var container = encoder.container(keyedBy: CodingKeys.self)
35+
switch self {
36+
case .url(let url):
37+
try container.encode("external", forKey: .type)
38+
try container.encode(url, forKey: .url)
39+
case .path(let path):
40+
try container.encode("internal", forKey: .type)
41+
try container.encode(path, forKey: .path)
42+
}
43+
}
44+
45+
}

0 commit comments

Comments
 (0)