diff --git a/Examples/CharacterGenerator/CharacterGenerator/CharacterGenerator/Configuration/Configuration.json b/Examples/CharacterGenerator/CharacterGenerator/CharacterGenerator/Configuration/Configuration.json index 1252ee6..6207c32 100644 --- a/Examples/CharacterGenerator/CharacterGenerator/CharacterGenerator/Configuration/Configuration.json +++ b/Examples/CharacterGenerator/CharacterGenerator/CharacterGenerator/Configuration/Configuration.json @@ -2,6 +2,7 @@ "currencies": ["Currencies"], "skills": ["Skills"], "backgrounds": ["Backgrounds"], + "creature types": ["CreatureTypes"], "classes": ["Classes"], "species": ["Species"], "species names": "SpeciesNames" diff --git a/Examples/CharacterGenerator/CharacterGenerator/CharacterGenerator/Configuration/CreatureTypes.json b/Examples/CharacterGenerator/CharacterGenerator/CharacterGenerator/Configuration/CreatureTypes.json new file mode 100644 index 0000000..42d5a5d --- /dev/null +++ b/Examples/CharacterGenerator/CharacterGenerator/CharacterGenerator/Configuration/CreatureTypes.json @@ -0,0 +1,18 @@ +{ + "creature types": [ + { "name": "Aberration" }, + { "name": "Beast" }, + { "name": "Celestial" }, + { "name": "Construct" }, + { "name": "Dragon" }, + { "name": "Elemental" }, + { "name": "Fey" }, + { "name": "Fiend" }, + { "name": "Giant" }, + { "name": "Humanoid", "is default": true }, + { "name": "Monstrosity" }, + { "name": "Ooze" }, + { "name": "Plant" }, + { "name": "Undead" } + ] +} diff --git a/Examples/CharacterGenerator/CharacterGenerator/CharacterGenerator/Configuration/Species.json b/Examples/CharacterGenerator/CharacterGenerator/CharacterGenerator/Configuration/Species.json index e717b81..68e61f8 100644 --- a/Examples/CharacterGenerator/CharacterGenerator/CharacterGenerator/Configuration/Species.json +++ b/Examples/CharacterGenerator/CharacterGenerator/CharacterGenerator/Configuration/Species.json @@ -1,20 +1,4 @@ { - "creature types": [ - { "name": "Aberration" }, - { "name": "Beast" }, - { "name": "Celestial" }, - { "name": "Construct" }, - { "name": "Dragon" }, - { "name": "Elemental" }, - { "name": "Fey" }, - { "name": "Fiend" }, - { "name": "Giant" }, - { "name": "Humanoid", "is default": true }, - { "name": "Monstrosity" }, - { "name": "Ooze" }, - { "name": "Plant" }, - { "name": "Undead" } - ], "species": [ { "name": "Aasimar", diff --git a/Sources/RolePlayingCore/Configuration/Configuration.swift b/Sources/RolePlayingCore/Configuration/Configuration.swift index cec418e..4b015d9 100644 --- a/Sources/RolePlayingCore/Configuration/Configuration.swift +++ b/Sources/RolePlayingCore/Configuration/Configuration.swift @@ -14,6 +14,7 @@ public struct ConfigurationFiles: Decodable { let currencies: [String] let skills: [String] let backgrounds: [String] + let creatureTypes: [String] let species: [String] let classes: [String] let players: [String]? @@ -23,6 +24,7 @@ public struct ConfigurationFiles: Decodable { case currencies case skills case backgrounds + case creatureTypes = "creature types" case species case classes case players @@ -37,8 +39,9 @@ public struct Configuration { public var configurationFiles: ConfigurationFiles public var currencies = Currencies() - public var backgrounds = Backgrounds() public var skills = Skills() + public var backgrounds = Backgrounds() + public var creatureTypes = CreatureTypes() public var species = Species() public var classes = Classes() public var players = Players() @@ -72,25 +75,22 @@ public struct Configuration { self.backgrounds.add(backgrounds.all) } + for creatureTypesFile in configurationFiles.creatureTypes { + let jsonData = try bundle.loadJSON(creatureTypesFile) + let creatureTypes = try jsonDecoder.decode(CreatureTypes.self, from: jsonData) + self.creatureTypes.add(creatureTypes.all) + } + for speciesFile in configurationFiles.species { let jsonData = try bundle.loadJSON(speciesFile) let species = try jsonDecoder.decode(Species.self, from: jsonData, configuration: self) - self.species.species += species.species + self.species.add(species) } for classFile in configurationFiles.classes { let jsonData = try bundle.loadJSON(classFile) let classes = try jsonDecoder.decode(Classes.self, from: jsonData, configuration: self) - self.classes.classes += classes.classes - - // Update the shared classes experience points table, then - // update all of the classes to point to it. TODO: improve this. - if let experiencePoints = classes.experiencePoints { - self.classes.experiencePoints = experiencePoints - for (index, _) in self.classes.classes.enumerated() { - self.classes.classes[index].experiencePoints = experiencePoints - } - } + self.classes.add(classes) } if let playersFiles = configurationFiles.players { diff --git a/Sources/RolePlayingCore/Player/Backgrounds.swift b/Sources/RolePlayingCore/Player/Backgrounds.swift index bb3729f..f224d04 100644 --- a/Sources/RolePlayingCore/Player/Backgrounds.swift +++ b/Sources/RolePlayingCore/Player/Backgrounds.swift @@ -24,7 +24,8 @@ public struct Backgrounds: CodableWithConfiguration { /// Adds the array of background traits to the collection. mutating func add(_ backgrounds: [BackgroundTraits]) { - allBackgrounds = Dictionary(backgrounds.map { ($0.name, $0) }, uniquingKeysWith: { _, last in last }) + let mappedBackgrounds = Dictionary(backgrounds.map { ($0.name, $0) }, uniquingKeysWith: { _, last in last }) + allBackgrounds.merge(mappedBackgrounds, uniquingKeysWith: { _, last in last }) } /// Accesses a background traits instance by name. diff --git a/Sources/RolePlayingCore/Player/ClassTraits.swift b/Sources/RolePlayingCore/Player/ClassTraits.swift index 2155215..798d356 100644 --- a/Sources/RolePlayingCore/Player/ClassTraits.swift +++ b/Sources/RolePlayingCore/Player/ClassTraits.swift @@ -119,16 +119,16 @@ extension ClassTraits: CodableWithConfiguration { let primaryAbility = try values.decodeIfPresent([Ability].self, forKey: .primaryAbility) let alternatePrimaryAbility = try values.decodeIfPresent([Ability].self, forKey: .alternatePrimaryAbility) let savingThrows = try values.decodeIfPresent([Ability].self, forKey: .savingThrows) - let startingSkillCount: Int? = try values.decodeIfPresent(Int.self, forKey: .startingSkillCount) + let startingSkillCount = try values.decodeIfPresent(Int.self, forKey: .startingSkillCount) // Decode skill proficiency names and resolve them using configuration let skillNames = try values.decodeIfPresent([String].self, forKey: .skillProficiencies) ?? [] - let resolvedSkills: [Skill] = try skillNames.skills(from: configuration.skills) + let resolvedSkills = try skillNames.skills(from: configuration.skills) let weaponProficiencies = try values.decodeIfPresent([String].self, forKey: .weaponProficiencies) let toolProficiencies = try values.decodeIfPresent([String].self, forKey: .toolProficiencies) - let armorTraining: [String]? = try values.decodeIfPresent([String].self, forKey: .armorTraining) - let startingEquipment: [[String]]? = try values.decodeIfPresent([[String]].self, forKey: .startingEquipment) + let armorTraining = try values.decodeIfPresent([String].self, forKey: .armorTraining) + let startingEquipment = try values.decodeIfPresent([[String]].self, forKey: .startingEquipment) let experiencePoints = try values.decodeIfPresent([Int].self, forKey: .experiencePoints) diff --git a/Sources/RolePlayingCore/Player/Classes.swift b/Sources/RolePlayingCore/Player/Classes.swift index 7e826fa..79e4131 100644 --- a/Sources/RolePlayingCore/Player/Classes.swift +++ b/Sources/RolePlayingCore/Player/Classes.swift @@ -11,40 +11,69 @@ import Foundation /// A collection of class traits. public struct Classes: CodableWithConfiguration { - public var classes: [ClassTraits] + /// A dictionary of class traits indexed by name. + private var allClasses: [String: ClassTraits] = [:] + + /// An array of class traits. + public var all: [ClassTraits] { Array(allClasses.values) } + + /// An optional table of the minimum experience points required to reach the next level. public var experiencePoints: [Int]? - public init(_ classes: [ClassTraits] = []) { - self.classes = classes + /// Returns an instance of a collection of background traits., optionally with a shared experience points table. + public init(_ classes: [ClassTraits] = [], experiencePoints: [Int]? = nil) { + add(classes, experiencePoints) } - private enum CodingKeys: String, CodingKey { - case classes - case experiencePoints = "experience points" + /// Adds the array of class traits to the collection, and optionally updates all experience points with a shared experience points table. + mutating func add(_ classes: [ClassTraits], _ experiencePoints: [Int]? = nil) { + let mappedClasses = Dictionary(classes.map { ($0.name, $0) }, uniquingKeysWith: { _, last in last }) + allClasses.merge(mappedClasses, uniquingKeysWith: { _, last in last }) + + if let experiencePoints { + self.experiencePoints = experiencePoints + for name in allClasses.keys { + self.allClasses[name]?.experiencePoints = experiencePoints + } + } + } + + /// Adds the collection of class traits to the collection, and if present, optionally updates all experience points with a shared experience points table. + mutating func add(_ classes: Classes) { + add(classes.all, classes.experiencePoints) } - public func find(_ className: String?) -> ClassTraits? { - return classes.first(where: { $0.name == className }) + /// Accesses a class traits instance by name. + public subscript(className: String) -> ClassTraits? { + return allClasses[className] } - public var count: Int { classes.count } + /// Returns the number of class traits in the collection. + public var count: Int { allClasses.count } + /// Accesses a class traits instance by index. public subscript(index: Int) -> ClassTraits? { - guard index >= 0 && index < classes.count else { return nil } - return classes[index] + guard index >= 0 && index < count else { return nil } + return all[index] } // MARK: CodableWithConfiguration conformance + private enum CodingKeys: String, CodingKey { + case classes + case experiencePoints = "experience points" + } + public init(from decoder: Decoder, configuration: Configuration) throws { let values = try decoder.container(keyedBy: CodingKeys.self) - self.classes = try values.decode([ClassTraits].self, forKey: .classes, configuration: configuration) + let classes = try values.decode([ClassTraits].self, forKey: .classes, configuration: configuration) + add(classes) self.experiencePoints = try values.decodeIfPresent([Int].self, forKey: .experiencePoints) } public func encode(to encoder: Encoder, configuration: Configuration) throws { var container = encoder.container(keyedBy: CodingKeys.self) - try container.encode(classes, forKey: .classes, configuration: configuration) + try container.encode(all, forKey: .classes, configuration: configuration) try container.encodeIfPresent(experiencePoints, forKey: .experiencePoints) } } diff --git a/Sources/RolePlayingCore/Player/CreatureType.swift b/Sources/RolePlayingCore/Player/CreatureType.swift index 468c6bf..82e815d 100644 --- a/Sources/RolePlayingCore/Player/CreatureType.swift +++ b/Sources/RolePlayingCore/Player/CreatureType.swift @@ -22,3 +22,41 @@ public struct CreatureType: Sendable { extension CreatureType: Codable { } +extension CreatureType: Hashable { } + +public struct CreatureTypes: Codable, Sendable { + + private var allCreatureTypes: [String: CreatureType] = [:] + + public var all: [CreatureType] { Array(allCreatureTypes.values) } + + public var defaultCreatureType: CreatureType { + all.first(where: { $0.isDefault != nil && $0.isDefault! })! //?? CreatureType("Humanoid") + } + + public init (_ creatureTypes: [CreatureType] = []) { + add(creatureTypes) + } + + mutating func add(_ creatureTypes: [CreatureType]) { + let mappedCreatureTypes = Dictionary(creatureTypes.map { ($0.name, $0) }, uniquingKeysWith: { _, last in last }) + allCreatureTypes.merge(mappedCreatureTypes, uniquingKeysWith: { _, last in last }) + } + + // MARK: Codable conformance + + private enum CodingKeys: String, CodingKey { + case creatureTypes = "creature types" + } + + public init(from decoder: Decoder) throws { + let values = try decoder.container(keyedBy: CodingKeys.self) + let creatureTypes = try values.decode([CreatureType].self, forKey: .creatureTypes) + add(creatureTypes) + } + + public func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(all, forKey: .creatureTypes) + } +} diff --git a/Sources/RolePlayingCore/Player/Player.swift b/Sources/RolePlayingCore/Player/Player.swift index 7c4ffd8..63731cf 100644 --- a/Sources/RolePlayingCore/Player/Player.swift +++ b/Sources/RolePlayingCore/Player/Player.swift @@ -161,12 +161,12 @@ public class Player: CodableWithConfiguration { } // Resolve speciesTraits from configuration - guard let speciesTraits = configuration.species.find(speciesName) else { + guard let speciesTraits = configuration.species[speciesName] else { throw missingTypeError("species", speciesName) } // Resolve classTraits from configuration - guard let classTraits = configuration.classes.find(className) else { + guard let classTraits = configuration.classes[className] else { throw missingTypeError("class", className) } diff --git a/Sources/RolePlayingCore/Player/Skills.swift b/Sources/RolePlayingCore/Player/Skills.swift index 3c834ef..b352841 100644 --- a/Sources/RolePlayingCore/Player/Skills.swift +++ b/Sources/RolePlayingCore/Player/Skills.swift @@ -32,7 +32,8 @@ public struct Skills: Codable { /// Adds the array of skills to the collection. mutating func add(_ skills: [Skill]) { - allSkills = Dictionary(skills.map { ($0.name, $0) }, uniquingKeysWith: { _, last in last }) + let mappedSkills = Dictionary(skills.map { ($0.name, $0) }, uniquingKeysWith: { _, last in last }) + allSkills.merge(mappedSkills, uniquingKeysWith: { _, last in last }) } /// Accesses a skill by name. diff --git a/Sources/RolePlayingCore/Player/Species.swift b/Sources/RolePlayingCore/Player/Species.swift index 5dfc715..87cb480 100644 --- a/Sources/RolePlayingCore/Player/Species.swift +++ b/Sources/RolePlayingCore/Player/Species.swift @@ -11,49 +11,56 @@ import Foundation /// A collection of species traits, including subspecies. public class Species: CodableWithConfiguration { + /// All of the species and subspecies traits as a flattened dictionary, indexed by species name. + private var allSpecies: [String: SpeciesTraits] = [:] + /// Accesses all of the species and subspecies that have been loaded. - public var species = [SpeciesTraits]() + public var all: [SpeciesTraits] { Array(allSpecies.values) } - public var creatureTypes = [CreatureType]() + /// Creates a Species instance. + public init(_ species: [SpeciesTraits] = []) { + add(species) + } - public var defaultCreatureType: CreatureType { - creatureTypes.first(where: { $0.isDefault != nil && $0.isDefault! }) ?? CreatureType("Humanoid") + public func add(_ species: [SpeciesTraits]) { + let mappedSpecies = Dictionary(species.map { ($0.name, $0) }, uniquingKeysWith: { _, last in last }) + allSpecies.merge(mappedSpecies, uniquingKeysWith: { _, last in last }) } - /// Creates a Species instance. - public init() { } + public func add(_ species: Species) { + add(species.all) + } /// Returns all of the leaf species (species that contain no subspecies). public var leafSpecies: [SpeciesTraits] { - return species.filter { $0.subspecies.isEmpty } + return all.filter { $0.subspecies.isEmpty } } /// Returns the species matching the specified name, or nil if not present. - public func find(_ speciesName: String) -> SpeciesTraits? { - return species.first(where: { $0.name == speciesName }) + public subscript(speciesName: String) -> SpeciesTraits? { + return allSpecies[speciesName] } - public var count: Int { species.count } + public var count: Int { allSpecies.count } public subscript(index: Int) -> SpeciesTraits? { - guard index >= 0 && index < species.count else { return nil } - return species[index] + guard index >= 0 && index < count else { return nil } + return all[index] } public func randomElementByIndex(using generator: inout G) -> SpeciesTraits { - return species.randomElementByIndex(using: &generator)! + return all.randomElementByIndex(using: &generator)! } + // MARK: CodableWithConfiguration support + enum CodingKeys: String, CodingKey { case species - case creatureTypes = "creature types" } /// Overridden to stitch together subspecies embedded in species. public required init(from decoder: Decoder, configuration: Configuration) throws { let root = try decoder.container(keyedBy: CodingKeys.self) - let creatureTypes = try root.decodeIfPresent([CreatureType].self, forKey: .creatureTypes) - self.creatureTypes = creatureTypes ?? [] var leaf = try root.nestedUnkeyedContainer(forKey: .species) @@ -68,17 +75,14 @@ public class Species: CodableWithConfiguration { } } - self.species = species + add(species) } public func encode(to encoder: any Encoder, configuration: Configuration) throws { var root = encoder.container(keyedBy: CodingKeys.self) - if !creatureTypes.isEmpty { - try root.encode(creatureTypes, forKey: .creatureTypes) - } var leaf = root.nestedUnkeyedContainer(forKey: .species) - let rootSpecies = self.species.filter { $0.parentName == nil } + let rootSpecies = all.filter { $0.parentName == nil } for speciesTraits in rootSpecies { try leaf.encode(speciesTraits, configuration: configuration) } diff --git a/Sources/RolePlayingCore/Player/SpeciesTraits.swift b/Sources/RolePlayingCore/Player/SpeciesTraits.swift index 79ac703..1c1835e 100644 --- a/Sources/RolePlayingCore/Player/SpeciesTraits.swift +++ b/Sources/RolePlayingCore/Player/SpeciesTraits.swift @@ -80,7 +80,7 @@ extension SpeciesTraits: CodableWithConfiguration { self.name = name self.plural = plural self.aliases = aliases ?? [] - self.creatureType = creatureType != nil ? CreatureType(creatureType!) : configuration.species.defaultCreatureType + self.creatureType = creatureType != nil ? CreatureType(creatureType!) : configuration.creatureTypes.defaultCreatureType self.descriptiveTraits = descriptiveTraits ?? [:] self.lifespan = lifespan self.baseSizes = baseSizes ?? ["4-7"] diff --git a/Tests/RolePlayingCoreTests/ClassesTests.swift b/Tests/RolePlayingCoreTests/ClassesTests.swift index 12a27e6..79f7dc4 100644 --- a/Tests/RolePlayingCoreTests/ClassesTests.swift +++ b/Tests/RolePlayingCoreTests/ClassesTests.swift @@ -25,16 +25,15 @@ struct ClassesTests { func defaultClasses() throws { let jsonData = try bundle.loadJSON("TestClasses") let classes = try decoder.decode(Classes.self, from: jsonData, configuration: configuration) - #expect(classes.classes.count == 4, "classes count failed") + #expect(classes.all.count == 4, "classes count failed") #expect(classes.count == 4, "classes count failed") #expect(classes[0] != nil, "class by index failed") #expect(classes.experiencePoints?.count == 20, "array of experience points failed") // Test finding a class by name - #expect(classes.find("Fighter") != nil, "Fighter should be non-nil") - #expect(classes.find("Foo") == nil, "Foo should be nil") - #expect(classes.find(nil) == nil, "nil class name should find nil") + #expect(classes["Fighter"] != nil, "Fighter should be non-nil") + #expect(classes["Foo"] == nil, "Foo should be nil") } @Test("Uncommon classes") @@ -42,7 +41,7 @@ struct ClassesTests { let jsonData = try bundle.loadJSON("TestMoreClasses") let classes = try decoder.decode(Classes.self, from: jsonData, configuration: configuration) - #expect(classes.classes.count == 8, "classes count failed") + #expect(classes.count == 8, "classes count failed") } } diff --git a/Tests/RolePlayingCoreTests/ConfigurationTests.swift b/Tests/RolePlayingCoreTests/ConfigurationTests.swift index ae153d9..8442096 100644 --- a/Tests/RolePlayingCoreTests/ConfigurationTests.swift +++ b/Tests/RolePlayingCoreTests/ConfigurationTests.swift @@ -25,7 +25,7 @@ struct ConfigurationTests { for ability in abilities { var importantFor = [String]() - for classTraits in configuration.classes.classes { + for classTraits in configuration.classes.all { if classTraits.primaryAbility.contains(ability) { importantFor.append(classTraits.name) } diff --git a/Tests/RolePlayingCoreTests/SpeciesNamesTests.swift b/Tests/RolePlayingCoreTests/SpeciesNamesTests.swift index 481d3a2..3ecbdd2 100644 --- a/Tests/RolePlayingCoreTests/SpeciesNamesTests.swift +++ b/Tests/RolePlayingCoreTests/SpeciesNamesTests.swift @@ -36,25 +36,25 @@ struct SpeciesNamesTests { let moreSpecies = try decoder.decode(Species.self, from: moreJsonData, configuration: configuration) let allSpecies = Species() - allSpecies.species = species.species + moreSpecies.species + allSpecies.add(species.all + moreSpecies.all) // TODO: random names are hard; for now, get code coverage. - let human = try #require(allSpecies.find("Human")) + let human = try #require(allSpecies["Human"]) _ = speciesNames.randomName(speciesTraits: human, gender: .female) - let elf = try #require(allSpecies.find("Elf")) + let elf = try #require(allSpecies["Elf"]) _ = speciesNames.randomName(speciesTraits: elf, gender: .male) - let mountainDwarf = try #require(allSpecies.find("Mountain Dwarf")) + let mountainDwarf = try #require(allSpecies["Mountain Dwarf"]) _ = speciesNames.randomName(speciesTraits: mountainDwarf, gender: nil) - let stout = try #require(allSpecies.find("Stout")) + let stout = try #require(allSpecies["Stout"]) _ = speciesNames.randomName(speciesTraits: stout, gender: nil) - let dragonborn = try #require(allSpecies.find("Dragonborn")) + let dragonborn = try #require(allSpecies["Dragonborn"]) _ = speciesNames.randomName(speciesTraits: dragonborn, gender: nil) - let tiefling = try #require(allSpecies.find("Tiefling")) + let tiefling = try #require(allSpecies["Tiefling"]) _ = speciesNames.randomName(speciesTraits: tiefling, gender: nil) let encoder = JSONEncoder() diff --git a/Tests/RolePlayingCoreTests/SpeciesTests.swift b/Tests/RolePlayingCoreTests/SpeciesTests.swift index 04b77e0..0da6457 100644 --- a/Tests/RolePlayingCoreTests/SpeciesTests.swift +++ b/Tests/RolePlayingCoreTests/SpeciesTests.swift @@ -24,7 +24,7 @@ struct SpeciesTests { @Test("Default initialization creates empty species") func defaultInit() async throws { let species = Species() - #expect(species.species.count == 0, "default init") + #expect(species.count == 0, "default init") } @Test("Load and parse species from JSON file") @@ -37,8 +37,8 @@ struct SpeciesTests { #expect(species[0] != nil, "species by index") // Test finding a species by name - #expect(species.find("Human") != nil, "Fighter should be non-nil") - #expect(species.find("Foo") == nil, "Foo should be nil") + #expect(species["Human"] != nil, "Fighter should be non-nil") + #expect(species["Foo"] == nil, "Foo should be nil") } @Test("Load uncommon species from JSON file") @@ -47,6 +47,6 @@ struct SpeciesTests { let species = try decoder.decode(Species.self, from: jsonData, configuration: configuration) // There should be 5 species plus 2 subspecies - #expect(species.species.count == 5, "all species") + #expect(species.count == 5, "all species") } } diff --git a/Tests/RolePlayingCoreTests/SpeciesTraitsTests.swift b/Tests/RolePlayingCoreTests/SpeciesTraitsTests.swift index f7bcc3e..ecd56f3 100644 --- a/Tests/RolePlayingCoreTests/SpeciesTraitsTests.swift +++ b/Tests/RolePlayingCoreTests/SpeciesTraitsTests.swift @@ -159,14 +159,14 @@ struct SpeciesTraitsTests { @Test("Encode subspecies traits with blending") func encodingSubspeciesTraits() async throws { - let speciesTraits = SpeciesTraits(name: "Human", plural: "Humans", aliases: [], creatureType: configuration.species.defaultCreatureType, descriptiveTraits: [:], lifespan: 90, darkVision: 0, speed: 45) + let speciesTraits = SpeciesTraits(name: "Human", plural: "Humans", aliases: [], creatureType: configuration.creatureTypes.defaultCreatureType, descriptiveTraits: [:], lifespan: 90, darkVision: 0, speed: 45) let encoder = JSONEncoder() // Test 1: Subspecies with blended traits do { var copyOfSpeciesTraits = speciesTraits - var subspeciesTraits = SpeciesTraits(name: "Subhuman", plural: "Subhumans", creatureType: configuration.species.defaultCreatureType, lifespan: 45, darkVision: 0, speed: 30) + var subspeciesTraits = SpeciesTraits(name: "Subhuman", plural: "Subhumans", creatureType: configuration.creatureTypes.defaultCreatureType, lifespan: 45, darkVision: 0, speed: 30) subspeciesTraits.blendTraits(from: copyOfSpeciesTraits) copyOfSpeciesTraits.subspecies.append(subspeciesTraits) @@ -193,7 +193,7 @@ struct SpeciesTraitsTests { // Test 2: Subspecies with different overrides do { var copyOfSpeciesTraits = speciesTraits - let subspeciesTraits = SpeciesTraits(name: "Subhuman", plural: "Subhumans", aliases: ["Minions"], creatureType: configuration.species.defaultCreatureType, descriptiveTraits: ["background": "Something"], lifespan: 45, darkVision: 10, speed: 45) + let subspeciesTraits = SpeciesTraits(name: "Subhuman", plural: "Subhumans", aliases: ["Minions"], creatureType: configuration.creatureTypes.defaultCreatureType, descriptiveTraits: ["background": "Something"], lifespan: 45, darkVision: 10, speed: 45) copyOfSpeciesTraits.subspecies.append(subspeciesTraits) let encoded = try encoder.encode(copyOfSpeciesTraits, configuration: configuration) diff --git a/Tests/RolePlayingCoreTests/TestResources/TestCharacterGenerator.json b/Tests/RolePlayingCoreTests/TestResources/TestCharacterGenerator.json index 9d84902..1260253 100644 --- a/Tests/RolePlayingCoreTests/TestResources/TestCharacterGenerator.json +++ b/Tests/RolePlayingCoreTests/TestResources/TestCharacterGenerator.json @@ -2,6 +2,7 @@ "currencies": ["TestCurrencies"], "skills": ["TestSkills"], "backgrounds": ["TestBackgrounds"], + "creature types": ["TestCreatureTypes"], "classes": ["TestClasses", "TestMoreClasses"], "species": ["TestSpecies", "TestMoreSpecies"], "players": ["TestPlayers"], diff --git a/Tests/RolePlayingCoreTests/TestResources/TestClassesConfiguration.json b/Tests/RolePlayingCoreTests/TestResources/TestClassesConfiguration.json index 09157d3..0f6c136 100644 --- a/Tests/RolePlayingCoreTests/TestResources/TestClassesConfiguration.json +++ b/Tests/RolePlayingCoreTests/TestResources/TestClassesConfiguration.json @@ -2,6 +2,7 @@ "currencies": ["TestCurrencies"], "skills": ["TestSkills"], "backgrounds": [], + "creature types": [], "classes": [], "species": [], "players": [] diff --git a/Tests/RolePlayingCoreTests/TestResources/TestConfiguration.json b/Tests/RolePlayingCoreTests/TestResources/TestConfiguration.json index ffddca6..f594493 100644 --- a/Tests/RolePlayingCoreTests/TestResources/TestConfiguration.json +++ b/Tests/RolePlayingCoreTests/TestResources/TestConfiguration.json @@ -2,6 +2,7 @@ "currencies": ["TestCurrencies"], "skills": ["TestSkills"], "backgrounds": ["TestBackgrounds"], + "creature types": ["TestCreatureTypes"], "classes": ["TestClasses", "TestMoreClasses"], "species": ["TestSpecies", "TestMoreSpecies"], "players": ["TestPlayers"] diff --git a/Tests/RolePlayingCoreTests/TestResources/TestCreatureTypes.json b/Tests/RolePlayingCoreTests/TestResources/TestCreatureTypes.json new file mode 100644 index 0000000..42d5a5d --- /dev/null +++ b/Tests/RolePlayingCoreTests/TestResources/TestCreatureTypes.json @@ -0,0 +1,18 @@ +{ + "creature types": [ + { "name": "Aberration" }, + { "name": "Beast" }, + { "name": "Celestial" }, + { "name": "Construct" }, + { "name": "Dragon" }, + { "name": "Elemental" }, + { "name": "Fey" }, + { "name": "Fiend" }, + { "name": "Giant" }, + { "name": "Humanoid", "is default": true }, + { "name": "Monstrosity" }, + { "name": "Ooze" }, + { "name": "Plant" }, + { "name": "Undead" } + ] +} diff --git a/Tests/RolePlayingCoreTests/TestResources/TestSpecies.json b/Tests/RolePlayingCoreTests/TestResources/TestSpecies.json index 8aff22b..abd5b6c 100644 --- a/Tests/RolePlayingCoreTests/TestResources/TestSpecies.json +++ b/Tests/RolePlayingCoreTests/TestResources/TestSpecies.json @@ -1,20 +1,4 @@ { - "creature types": [ - { "name": "Aberration" }, - { "name": "Beast" }, - { "name": "Celestial" }, - { "name": "Construct" }, - { "name": "Dragon" }, - { "name": "Elemental" }, - { "name": "Fey" }, - { "name": "Fiend" }, - { "name": "Giant" }, - { "name": "Humanoid", "is default": true }, - { "name": "Monstrosity" }, - { "name": "Ooze" }, - { "name": "Plant" }, - { "name": "Undead" } - ], "species": [ { "name": "Dwarf",