Adds languages to the editor.
This commit is contained in:
@@ -15,6 +15,9 @@
|
|||||||
E20209FC25D8E19100EFE733 /* MonsterViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E20209FA25D8E19100EFE733 /* MonsterViewModel.swift */; };
|
E20209FC25D8E19100EFE733 /* MonsterViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E20209FA25D8E19100EFE733 /* MonsterViewModel.swift */; };
|
||||||
E210B83A25B42D980083EAC5 /* MCProficiencyPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = E210B83925B42D980083EAC5 /* MCProficiencyPicker.swift */; };
|
E210B83A25B42D980083EAC5 /* MCProficiencyPicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = E210B83925B42D980083EAC5 /* MCProficiencyPicker.swift */; };
|
||||||
E210B83F25B42DAB0083EAC5 /* MCAdvantagePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = E210B83E25B42DAB0083EAC5 /* MCAdvantagePicker.swift */; };
|
E210B83F25B42DAB0083EAC5 /* MCAdvantagePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = E210B83E25B42DAB0083EAC5 /* MCAdvantagePicker.swift */; };
|
||||||
|
E216B791260C1FE800FB205F /* LanguageViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E216B790260C1FE800FB205F /* LanguageViewModel.swift */; };
|
||||||
|
E216B799260C2DF200FB205F /* EditLanguages.swift in Sources */ = {isa = PBXBuildFile; fileRef = E216B798260C2DF200FB205F /* EditLanguages.swift */; };
|
||||||
|
E216B79E260C396F00FB205F /* EditLanguage.swift in Sources */ = {isa = PBXBuildFile; fileRef = E216B79D260C396F00FB205F /* EditLanguage.swift */; };
|
||||||
E2182E6425B22F8A00DFAEF8 /* Monster+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2182E6225B22F8A00DFAEF8 /* Monster+CoreDataClass.swift */; };
|
E2182E6425B22F8A00DFAEF8 /* Monster+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2182E6225B22F8A00DFAEF8 /* Monster+CoreDataClass.swift */; };
|
||||||
E24ACE502607326E009BF703 /* EditBasicInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = E24ACE4F2607326E009BF703 /* EditBasicInfo.swift */; };
|
E24ACE502607326E009BF703 /* EditBasicInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = E24ACE4F2607326E009BF703 /* EditBasicInfo.swift */; };
|
||||||
E24ACE562607EE94009BF703 /* EditArmor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E24ACE552607EE94009BF703 /* EditArmor.swift */; };
|
E24ACE562607EE94009BF703 /* EditArmor.swift in Sources */ = {isa = PBXBuildFile; fileRef = E24ACE552607EE94009BF703 /* EditArmor.swift */; };
|
||||||
@@ -77,6 +80,9 @@
|
|||||||
E20209FA25D8E19100EFE733 /* MonsterViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MonsterViewModel.swift; sourceTree = "<group>"; };
|
E20209FA25D8E19100EFE733 /* MonsterViewModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MonsterViewModel.swift; sourceTree = "<group>"; };
|
||||||
E210B83925B42D980083EAC5 /* MCProficiencyPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MCProficiencyPicker.swift; sourceTree = "<group>"; };
|
E210B83925B42D980083EAC5 /* MCProficiencyPicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MCProficiencyPicker.swift; sourceTree = "<group>"; };
|
||||||
E210B83E25B42DAB0083EAC5 /* MCAdvantagePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MCAdvantagePicker.swift; sourceTree = "<group>"; };
|
E210B83E25B42DAB0083EAC5 /* MCAdvantagePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MCAdvantagePicker.swift; sourceTree = "<group>"; };
|
||||||
|
E216B790260C1FE800FB205F /* LanguageViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LanguageViewModel.swift; sourceTree = "<group>"; };
|
||||||
|
E216B798260C2DF200FB205F /* EditLanguages.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditLanguages.swift; sourceTree = "<group>"; };
|
||||||
|
E216B79D260C396F00FB205F /* EditLanguage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditLanguage.swift; sourceTree = "<group>"; };
|
||||||
E2182E6225B22F8A00DFAEF8 /* Monster+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Monster+CoreDataClass.swift"; sourceTree = "<group>"; };
|
E2182E6225B22F8A00DFAEF8 /* Monster+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Monster+CoreDataClass.swift"; sourceTree = "<group>"; };
|
||||||
E24ACE4F2607326E009BF703 /* EditBasicInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditBasicInfo.swift; sourceTree = "<group>"; };
|
E24ACE4F2607326E009BF703 /* EditBasicInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditBasicInfo.swift; sourceTree = "<group>"; };
|
||||||
E24ACE552607EE94009BF703 /* EditArmor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditArmor.swift; sourceTree = "<group>"; };
|
E24ACE552607EE94009BF703 /* EditArmor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditArmor.swift; sourceTree = "<group>"; };
|
||||||
@@ -180,15 +186,15 @@
|
|||||||
E2570FB725B1AC520055B23B /* MonsterCards */ = {
|
E2570FB725B1AC520055B23B /* MonsterCards */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
E2570FB825B1AC520055B23B /* MonsterCardsApp.swift */,
|
|
||||||
E2D473FB25B5328800CB36D7 /* Helpers */,
|
|
||||||
E257101225B1B2790055B23B /* Models */,
|
|
||||||
E2570FEB25B1ADA90055B23B /* Views */,
|
|
||||||
E2570FBC25B1AC550055B23B /* Assets.xcassets */,
|
E2570FBC25B1AC550055B23B /* Assets.xcassets */,
|
||||||
E2570FC125B1AC550055B23B /* Persistence.swift */,
|
E2D473FB25B5328800CB36D7 /* Helpers */,
|
||||||
E2570FC625B1AC550055B23B /* Info.plist */,
|
E2570FC625B1AC550055B23B /* Info.plist */,
|
||||||
|
E257101225B1B2790055B23B /* Models */,
|
||||||
E2570FC325B1AC550055B23B /* MonsterCards.xcdatamodeld */,
|
E2570FC325B1AC550055B23B /* MonsterCards.xcdatamodeld */,
|
||||||
|
E2570FB825B1AC520055B23B /* MonsterCardsApp.swift */,
|
||||||
|
E2570FC125B1AC550055B23B /* Persistence.swift */,
|
||||||
E2570FBE25B1AC550055B23B /* Preview Content */,
|
E2570FBE25B1AC550055B23B /* Preview Content */,
|
||||||
|
E2570FEB25B1ADA90055B23B /* Views */,
|
||||||
);
|
);
|
||||||
path = MonsterCards;
|
path = MonsterCards;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -228,12 +234,14 @@
|
|||||||
E24ACE5F2607F45E009BF703 /* EditAbilityScores.swift */,
|
E24ACE5F2607F45E009BF703 /* EditAbilityScores.swift */,
|
||||||
E24ACE552607EE94009BF703 /* EditArmor.swift */,
|
E24ACE552607EE94009BF703 /* EditArmor.swift */,
|
||||||
E24ACE4F2607326E009BF703 /* EditBasicInfo.swift */,
|
E24ACE4F2607326E009BF703 /* EditBasicInfo.swift */,
|
||||||
E2CB0DE526088CE400142591 /* EditStrings.swift */,
|
E216B79D260C396F00FB205F /* EditLanguage.swift */,
|
||||||
|
E216B798260C2DF200FB205F /* EditLanguages.swift */,
|
||||||
E2B5285825B3028700AAA69E /* EditMonster.swift */,
|
E2B5285825B3028700AAA69E /* EditMonster.swift */,
|
||||||
E24ACE642607F55D009BF703 /* EditSavingThrows.swift */,
|
E24ACE642607F55D009BF703 /* EditSavingThrows.swift */,
|
||||||
E2CB0DB226080C0500142591 /* EditSkill.swift */,
|
E2CB0DB226080C0500142591 /* EditSkill.swift */,
|
||||||
E24ACE692607F715009BF703 /* EditSkills.swift */,
|
E24ACE692607F715009BF703 /* EditSkills.swift */,
|
||||||
E24ACE5A2607F0F2009BF703 /* EditSpeed.swift */,
|
E24ACE5A2607F0F2009BF703 /* EditSpeed.swift */,
|
||||||
|
E2CB0DE526088CE400142591 /* EditStrings.swift */,
|
||||||
E2570FFE25B1AE180055B23B /* Library.swift */,
|
E2570FFE25B1AE180055B23B /* Library.swift */,
|
||||||
E2CB0DB726081A2F00142591 /* MCAbilityScorePicker.swift */,
|
E2CB0DB726081A2F00142591 /* MCAbilityScorePicker.swift */,
|
||||||
E210B83E25B42DAB0083EAC5 /* MCAdvantagePicker.swift */,
|
E210B83E25B42DAB0083EAC5 /* MCAdvantagePicker.swift */,
|
||||||
@@ -251,12 +259,13 @@
|
|||||||
E257101225B1B2790055B23B /* Models */ = {
|
E257101225B1B2790055B23B /* Models */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
E2CB0DE0260887ED00142591 /* StringViewModel.swift */,
|
|
||||||
E20209E625D8DEB600EFE733 /* Enums */,
|
E20209E625D8DEB600EFE733 /* Enums */,
|
||||||
|
E216B790260C1FE800FB205F /* LanguageViewModel.swift */,
|
||||||
E2182E6225B22F8A00DFAEF8 /* Monster+CoreDataClass.swift */,
|
E2182E6225B22F8A00DFAEF8 /* Monster+CoreDataClass.swift */,
|
||||||
E20209FA25D8E19100EFE733 /* MonsterViewModel.swift */,
|
E20209FA25D8E19100EFE733 /* MonsterViewModel.swift */,
|
||||||
E20209D225D8DD9600EFE733 /* Skill+CoreDataClass.swift */,
|
E20209D225D8DD9600EFE733 /* Skill+CoreDataClass.swift */,
|
||||||
E20209F925D8E19100EFE733 /* SkillViewModel.swift */,
|
E20209F925D8E19100EFE733 /* SkillViewModel.swift */,
|
||||||
|
E2CB0DE0260887ED00142591 /* StringViewModel.swift */,
|
||||||
);
|
);
|
||||||
path = Models;
|
path = Models;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -402,10 +411,12 @@
|
|||||||
E20209FB25D8E19100EFE733 /* SkillViewModel.swift in Sources */,
|
E20209FB25D8E19100EFE733 /* SkillViewModel.swift in Sources */,
|
||||||
E24ACE602607F45E009BF703 /* EditAbilityScores.swift in Sources */,
|
E24ACE602607F45E009BF703 /* EditAbilityScores.swift in Sources */,
|
||||||
E2570FC225B1AC550055B23B /* Persistence.swift in Sources */,
|
E2570FC225B1AC550055B23B /* Persistence.swift in Sources */,
|
||||||
|
E216B799260C2DF200FB205F /* EditLanguages.swift in Sources */,
|
||||||
E2570FBB25B1AC520055B23B /* ContentView.swift in Sources */,
|
E2570FBB25B1AC520055B23B /* ContentView.swift in Sources */,
|
||||||
E24ACE502607326E009BF703 /* EditBasicInfo.swift in Sources */,
|
E24ACE502607326E009BF703 /* EditBasicInfo.swift in Sources */,
|
||||||
E2570FC525B1AC550055B23B /* MonsterCards.xcdatamodeld in Sources */,
|
E2570FC525B1AC550055B23B /* MonsterCards.xcdatamodeld in Sources */,
|
||||||
E2182E6425B22F8A00DFAEF8 /* Monster+CoreDataClass.swift in Sources */,
|
E2182E6425B22F8A00DFAEF8 /* Monster+CoreDataClass.swift in Sources */,
|
||||||
|
E216B791260C1FE800FB205F /* LanguageViewModel.swift in Sources */,
|
||||||
E210B83A25B42D980083EAC5 /* MCProficiencyPicker.swift in Sources */,
|
E210B83A25B42D980083EAC5 /* MCProficiencyPicker.swift in Sources */,
|
||||||
E2570FF025B1ADC10055B23B /* Search.swift in Sources */,
|
E2570FF025B1ADC10055B23B /* Search.swift in Sources */,
|
||||||
E257100925B1B2480055B23B /* MonsterDetail.swift in Sources */,
|
E257100925B1B2480055B23B /* MonsterDetail.swift in Sources */,
|
||||||
@@ -420,6 +431,7 @@
|
|||||||
E24ACE6A2607F715009BF703 /* EditSkills.swift in Sources */,
|
E24ACE6A2607F715009BF703 /* EditSkills.swift in Sources */,
|
||||||
E20209FC25D8E19100EFE733 /* MonsterViewModel.swift in Sources */,
|
E20209FC25D8E19100EFE733 /* MonsterViewModel.swift in Sources */,
|
||||||
E2570FFF25B1AE180055B23B /* Library.swift in Sources */,
|
E2570FFF25B1AE180055B23B /* Library.swift in Sources */,
|
||||||
|
E216B79E260C396F00FB205F /* EditLanguage.swift in Sources */,
|
||||||
E2BD703125B3BBB90058ED69 /* MCStepperField.swift in Sources */,
|
E2BD703125B3BBB90058ED69 /* MCStepperField.swift in Sources */,
|
||||||
E2CB0DE626088CE400142591 /* EditStrings.swift in Sources */,
|
E2CB0DE626088CE400142591 /* EditStrings.swift in Sources */,
|
||||||
E2CB0DB326080C0500142591 /* EditSkill.swift in Sources */,
|
E2CB0DB326080C0500142591 /* EditSkill.swift in Sources */,
|
||||||
|
|||||||
79
MonsterCards/Models/LanguageViewModel.swift
Normal file
79
MonsterCards/Models/LanguageViewModel.swift
Normal file
@@ -0,0 +1,79 @@
|
|||||||
|
//
|
||||||
|
// LanguageViewModel.swift
|
||||||
|
// MonsterCards
|
||||||
|
//
|
||||||
|
// Created by Tom Hicks on 3/24/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
// TODO: split this into separate Model and ViewModel classes later.
|
||||||
|
public class LanguageViewModel : NSObject, ObservableObject, Comparable, Identifiable, NSSecureCoding {
|
||||||
|
public static var supportsSecureCoding = true
|
||||||
|
|
||||||
|
public func encode(with coder: NSCoder) {
|
||||||
|
coder.encode(self.name, forKey: "name")
|
||||||
|
coder.encode(self.speaks, forKey: "speaks")
|
||||||
|
}
|
||||||
|
|
||||||
|
public required init?(coder: NSCoder) {
|
||||||
|
self.name = coder.decodeObject(of: NSString.self, forKey: "name")! as String
|
||||||
|
self.speaks = coder.decodeBool(forKey: "speaks")
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func < (lhs: LanguageViewModel, rhs: LanguageViewModel) -> Bool {
|
||||||
|
lhs.name < rhs.name
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func == (lhs: LanguageViewModel, rhs: LanguageViewModel) -> Bool {
|
||||||
|
lhs.name == rhs.name &&
|
||||||
|
lhs.speaks == rhs.speaks
|
||||||
|
}
|
||||||
|
|
||||||
|
@Published var name: String
|
||||||
|
@Published var speaks: Bool
|
||||||
|
|
||||||
|
init(
|
||||||
|
_ name: String = "",
|
||||||
|
_ speaks: Bool = true
|
||||||
|
) {
|
||||||
|
self.name = name
|
||||||
|
self.speaks = speaks
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: figure out how to add this to the set of known transformers so it will work with transformer set to NSSecureUnarchiveFromDataTransformerName
|
||||||
|
@objc(LanguageViewModelValueTransformer)
|
||||||
|
public final class LanguageViewModelValueTransformer: ValueTransformer {
|
||||||
|
override public class func transformedValueClass() -> AnyClass {
|
||||||
|
return NSArray.self
|
||||||
|
}
|
||||||
|
|
||||||
|
override public class func allowsReverseTransformation() -> Bool {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
override public func transformedValue(_ value: Any?) -> Any? {
|
||||||
|
guard let language = value as? NSArray else { return nil }
|
||||||
|
|
||||||
|
do {
|
||||||
|
let data = try NSKeyedArchiver.archivedData(withRootObject: language, requiringSecureCoding: true)
|
||||||
|
return data
|
||||||
|
} catch {
|
||||||
|
assertionFailure("Failed to transform `LanguageViewModel` to `Data`")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override public func reverseTransformedValue(_ value: Any?) -> Any? {
|
||||||
|
guard let data = value as? NSData else { return nil }
|
||||||
|
|
||||||
|
do {
|
||||||
|
let language = try NSKeyedUnarchiver.unarchivedArrayOfObjects(ofClass: LanguageViewModel.self, from: data as Data)
|
||||||
|
return language
|
||||||
|
} catch {
|
||||||
|
assertionFailure("Failed to transform `Data` to `LanguageViewModel`")
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -698,8 +698,51 @@ public class Monster: NSManagedObject {
|
|||||||
|
|
||||||
var languagesDescription: String {
|
var languagesDescription: String {
|
||||||
get {
|
get {
|
||||||
let sortedLanguages = self.languagesArray.sorted()
|
let spokenLanguages = (self.languages ?? [])
|
||||||
return StringHelper.oxfordJoin(sortedLanguages, ", ", ", and ", " and ")
|
.filter({ $0.speaks })
|
||||||
|
.map({$0.name})
|
||||||
|
.sorted()
|
||||||
|
let understoodLanguages = (self.languages ?? [])
|
||||||
|
.filter({ !$0.speaks })
|
||||||
|
.map({$0.name})
|
||||||
|
.sorted()
|
||||||
|
|
||||||
|
let understandsButText = understandsBut?.isEmpty ?? false
|
||||||
|
? ""
|
||||||
|
: String(format: " but %@", understandsBut!)
|
||||||
|
|
||||||
|
let telepathyText = telepathy > 0
|
||||||
|
? String(format: ", telepathy %d ft.", telepathy)
|
||||||
|
: ""
|
||||||
|
|
||||||
|
if (spokenLanguages.count > 0) {
|
||||||
|
if (understoodLanguages.count > 0) {
|
||||||
|
return String(
|
||||||
|
format:"%@ and understands %@%@%@",
|
||||||
|
StringHelper.oxfordJoin(spokenLanguages),
|
||||||
|
StringHelper.oxfordJoin(understoodLanguages),
|
||||||
|
understandsButText,
|
||||||
|
telepathyText)
|
||||||
|
} else {
|
||||||
|
return String(
|
||||||
|
format: "%@%@%@",
|
||||||
|
StringHelper.oxfordJoin(spokenLanguages),
|
||||||
|
understandsButText,
|
||||||
|
telepathyText)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if (understoodLanguages.count > 0) {
|
||||||
|
return String(
|
||||||
|
format: "understands %@%@%@",
|
||||||
|
StringHelper.oxfordJoin(understoodLanguages),
|
||||||
|
understandsButText,
|
||||||
|
telepathyText)
|
||||||
|
} else if (telepathy > 0){
|
||||||
|
return String(format: "telepathy %d ft.", telepathy)
|
||||||
|
} else {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -54,6 +54,9 @@ class MonsterViewModel: ObservableObject {
|
|||||||
@Published var damageVulnerabilities: [StringViewModel]
|
@Published var damageVulnerabilities: [StringViewModel]
|
||||||
@Published var conditionImmunities: [StringViewModel]
|
@Published var conditionImmunities: [StringViewModel]
|
||||||
@Published var senses: [StringViewModel]
|
@Published var senses: [StringViewModel]
|
||||||
|
@Published var languages: [LanguageViewModel]
|
||||||
|
@Published var telepathy: Int64
|
||||||
|
@Published var understandsBut: String
|
||||||
|
|
||||||
init(_ rawMonster: Monster? = nil) {
|
init(_ rawMonster: Monster? = nil) {
|
||||||
self.name = ""
|
self.name = ""
|
||||||
@@ -100,6 +103,9 @@ class MonsterViewModel: ObservableObject {
|
|||||||
self.damageVulnerabilities = []
|
self.damageVulnerabilities = []
|
||||||
self.conditionImmunities = []
|
self.conditionImmunities = []
|
||||||
self.senses = []
|
self.senses = []
|
||||||
|
self.languages = []
|
||||||
|
self.telepathy = 0
|
||||||
|
self.understandsBut = ""
|
||||||
|
|
||||||
if (rawMonster != nil) {
|
if (rawMonster != nil) {
|
||||||
self.copyFromMonster(monster: rawMonster!)
|
self.copyFromMonster(monster: rawMonster!)
|
||||||
@@ -145,6 +151,8 @@ class MonsterViewModel: ObservableObject {
|
|||||||
self.charismaScore = monster.charismaScore
|
self.charismaScore = monster.charismaScore
|
||||||
self.charismaSavingThrowAdvantage = monster.charismaSavingThrowAdvantageEnum
|
self.charismaSavingThrowAdvantage = monster.charismaSavingThrowAdvantageEnum
|
||||||
self.charismaSavingThrowProficiency = monster.charismaSavingThrowProficiencyEnum
|
self.charismaSavingThrowProficiency = monster.charismaSavingThrowProficiencyEnum
|
||||||
|
self.telepathy = monster.telepathy
|
||||||
|
self.understandsBut = monster.understandsBut ?? ""
|
||||||
self.skills = (monster.skills?.allObjects.map {SkillViewModel(($0 as! Skill))})!.sorted()
|
self.skills = (monster.skills?.allObjects.map {SkillViewModel(($0 as! Skill))})!.sorted()
|
||||||
|
|
||||||
self.damageImmunities = (monster.damageImmunities ?? [])
|
self.damageImmunities = (monster.damageImmunities ?? [])
|
||||||
@@ -166,6 +174,9 @@ class MonsterViewModel: ObservableObject {
|
|||||||
self.senses = (monster.senses ?? [])
|
self.senses = (monster.senses ?? [])
|
||||||
.map {StringViewModel($0)}
|
.map {StringViewModel($0)}
|
||||||
.sorted()
|
.sorted()
|
||||||
|
|
||||||
|
self.languages = (monster.languages ?? [])
|
||||||
|
.sorted()
|
||||||
}
|
}
|
||||||
|
|
||||||
func copyToMonster(monster: Monster) {
|
func copyToMonster(monster: Monster) {
|
||||||
@@ -207,6 +218,8 @@ class MonsterViewModel: ObservableObject {
|
|||||||
monster.charismaScore = charismaScore
|
monster.charismaScore = charismaScore
|
||||||
monster.charismaSavingThrowAdvantageEnum = charismaSavingThrowAdvantage
|
monster.charismaSavingThrowAdvantageEnum = charismaSavingThrowAdvantage
|
||||||
monster.charismaSavingThrowProficiencyEnum = charismaSavingThrowProficiency
|
monster.charismaSavingThrowProficiencyEnum = charismaSavingThrowProficiency
|
||||||
|
monster.telepathy = telepathy
|
||||||
|
monster.understandsBut = understandsBut
|
||||||
|
|
||||||
// Remove missing skills from raw monster
|
// Remove missing skills from raw monster
|
||||||
monster.skills?.forEach {s in
|
monster.skills?.forEach {s in
|
||||||
@@ -233,5 +246,6 @@ class MonsterViewModel: ObservableObject {
|
|||||||
monster.damageResistances = damageResistances.map {$0.name}
|
monster.damageResistances = damageResistances.map {$0.name}
|
||||||
monster.damageVulnerabilities = damageVulnerabilities.map {$0.name}
|
monster.damageVulnerabilities = damageVulnerabilities.map {$0.name}
|
||||||
monster.senses = senses.map {$0.name}
|
monster.senses = senses.map {$0.name}
|
||||||
|
monster.languages = languages.map {LanguageViewModel($0.name, $0.speaks)}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -36,6 +36,7 @@
|
|||||||
<attribute name="intelligenceSavingThrowProficiency" attributeType="String" defaultValueString="none"/>
|
<attribute name="intelligenceSavingThrowProficiency" attributeType="String" defaultValueString="none"/>
|
||||||
<attribute name="intelligenceScore" attributeType="Integer 64" defaultValueString="10" usesScalarValueType="YES"/>
|
<attribute name="intelligenceScore" attributeType="Integer 64" defaultValueString="10" usesScalarValueType="YES"/>
|
||||||
<attribute name="isBlind" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
<attribute name="isBlind" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||||
|
<attribute name="languages" optional="YES" attributeType="Transformable" valueTransformerName="LanguageViewModelValueTransformer" customClassName="[LanguageViewModel]"/>
|
||||||
<attribute name="name" attributeType="String" defaultValueString="Unnamed Monster"/>
|
<attribute name="name" attributeType="String" defaultValueString="Unnamed Monster"/>
|
||||||
<attribute name="naturalArmorBonus" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
<attribute name="naturalArmorBonus" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||||
<attribute name="otherArmorDescription" attributeType="String" defaultValueString=""/>
|
<attribute name="otherArmorDescription" attributeType="String" defaultValueString=""/>
|
||||||
@@ -47,6 +48,7 @@
|
|||||||
<attribute name="strengthScore" attributeType="Integer 64" defaultValueString="10" usesScalarValueType="YES"/>
|
<attribute name="strengthScore" attributeType="Integer 64" defaultValueString="10" usesScalarValueType="YES"/>
|
||||||
<attribute name="subtype" attributeType="String" defaultValueString=""/>
|
<attribute name="subtype" attributeType="String" defaultValueString=""/>
|
||||||
<attribute name="swimSpeed" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
<attribute name="swimSpeed" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||||
|
<attribute name="telepathy" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||||
<attribute name="tremorsenseDistance" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
<attribute name="tremorsenseDistance" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||||
<attribute name="truesightDistance" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
<attribute name="truesightDistance" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||||
<attribute name="type" attributeType="String" defaultValueString=""/>
|
<attribute name="type" attributeType="String" defaultValueString=""/>
|
||||||
@@ -64,7 +66,7 @@
|
|||||||
<relationship name="monster" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Monster" inverseName="skills" inverseEntity="Monster"/>
|
<relationship name="monster" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Monster" inverseName="skills" inverseEntity="Monster"/>
|
||||||
</entity>
|
</entity>
|
||||||
<elements>
|
<elements>
|
||||||
<element name="Monster" positionX="-63" positionY="-18" width="128" height="839"/>
|
<element name="Monster" positionX="-63" positionY="-18" width="128" height="869"/>
|
||||||
<element name="Skill" positionX="-63" positionY="135" width="128" height="14"/>
|
<element name="Skill" positionX="-63" positionY="135" width="128" height="14"/>
|
||||||
</elements>
|
</elements>
|
||||||
</model>
|
</model>
|
||||||
@@ -38,19 +38,6 @@ struct PersistenceController {
|
|||||||
}
|
}
|
||||||
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
|
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
|
||||||
if let error = error as NSError? {
|
if let error = error as NSError? {
|
||||||
|
|
||||||
// NSPersistentStoreCoordinator.destroyPersistentStore(storeDes)
|
|
||||||
// Replace this implementation with code to handle the error appropriately.
|
|
||||||
// fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development.
|
|
||||||
|
|
||||||
/*
|
|
||||||
Typical reasons for an error here include:
|
|
||||||
* The parent directory does not exist, cannot be created, or disallows writing.
|
|
||||||
* The persistent store is not accessible, due to permissions or data protection when the device is locked.
|
|
||||||
* The device is out of space.
|
|
||||||
* The store could not be migrated to the current model version.
|
|
||||||
Check the error message to determine what the actual problem was.
|
|
||||||
*/
|
|
||||||
fatalError("Unresolved error \(error), \(error.userInfo)")
|
fatalError("Unresolved error \(error), \(error.userInfo)")
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|||||||
28
MonsterCards/Views/EditLanguage.swift
Normal file
28
MonsterCards/Views/EditLanguage.swift
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
//
|
||||||
|
// EditLanguage.swift
|
||||||
|
// MonsterCards
|
||||||
|
//
|
||||||
|
// Created by Tom Hicks on 3/24/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct EditLanguage: View {
|
||||||
|
@ObservedObject var viewModel: LanguageViewModel
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
MCTextField(
|
||||||
|
label: "Name",
|
||||||
|
value: $viewModel.name)
|
||||||
|
.autocapitalization(.none)
|
||||||
|
|
||||||
|
Toggle("Speaks", isOn: $viewModel.speaks)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct EditLanguage_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
let viewModel = LanguageViewModel()
|
||||||
|
EditLanguage(viewModel: viewModel)
|
||||||
|
}
|
||||||
|
}
|
||||||
48
MonsterCards/Views/EditLanguages.swift
Normal file
48
MonsterCards/Views/EditLanguages.swift
Normal file
@@ -0,0 +1,48 @@
|
|||||||
|
//
|
||||||
|
// EditLanguages.swift
|
||||||
|
// MonsterCards
|
||||||
|
//
|
||||||
|
// Created by Tom Hicks on 3/24/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct EditLanguages: View {
|
||||||
|
@ObservedObject var viewModel: MonsterViewModel
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
let sortedLanguages = viewModel.languages.sorted()
|
||||||
|
List {
|
||||||
|
MCTextField(
|
||||||
|
label: "Understands But",
|
||||||
|
value: $viewModel.understandsBut)
|
||||||
|
MCStepperField(label: "Telepathy", prefix: "", step: 5, suffix: " ft.", value: $viewModel.telepathy)
|
||||||
|
ForEach(sortedLanguages/*viewModel.languages*/) { language in
|
||||||
|
NavigationLink(language.name, destination: EditLanguage(viewModel: language))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
.toolbar(content: {
|
||||||
|
Button(
|
||||||
|
action: {
|
||||||
|
let newLanguage = LanguageViewModel("English")
|
||||||
|
viewModel.languages.append(newLanguage)
|
||||||
|
viewModel.languages = viewModel.languages.sorted()
|
||||||
|
},
|
||||||
|
label: {
|
||||||
|
Image(systemName: "plus")
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.onAppear(perform: {
|
||||||
|
viewModel.languages = viewModel.languages.sorted()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct EditLanguages_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
let viewModel = MonsterViewModel()
|
||||||
|
EditLanguages(viewModel: viewModel)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -65,6 +65,10 @@ struct EditMonster: View {
|
|||||||
NavigationLink(
|
NavigationLink(
|
||||||
"Senses",
|
"Senses",
|
||||||
destination: EditStrings(viewModel: monsterViewModel, path: \.senses, title: "Senses"))
|
destination: EditStrings(viewModel: monsterViewModel, path: \.senses, title: "Senses"))
|
||||||
|
|
||||||
|
NavigationLink(
|
||||||
|
"Languages",
|
||||||
|
destination: EditLanguages(viewModel: monsterViewModel))
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -302,6 +302,11 @@ struct MonsterDetail_Previews: PreviewProvider {
|
|||||||
monster.wisdomSavingThrowProficiencyEnum = ProficiencyType.proficient
|
monster.wisdomSavingThrowProficiencyEnum = ProficiencyType.proficient
|
||||||
monster.charismaSavingThrowAdvantageEnum = AdvantageType.disadvantage
|
monster.charismaSavingThrowAdvantageEnum = AdvantageType.disadvantage
|
||||||
monster.charismaSavingThrowProficiencyEnum = ProficiencyType.none
|
monster.charismaSavingThrowProficiencyEnum = ProficiencyType.none
|
||||||
|
monster.telepathy = 1
|
||||||
|
monster.languages = [
|
||||||
|
LanguageViewModel("English", true),
|
||||||
|
LanguageViewModel("French", false)
|
||||||
|
]
|
||||||
|
|
||||||
return Group {
|
return Group {
|
||||||
MonsterDetail(monster: monster)
|
MonsterDetail(monster: monster)
|
||||||
|
|||||||
Reference in New Issue
Block a user