Adds MarkdownUI dependency and abilities.

This commit is contained in:
2021-03-25 01:22:37 -07:00
parent 89376e85a1
commit 429eb7aca8
11 changed files with 415 additions and 7 deletions

View File

@@ -3,7 +3,7 @@
archiveVersion = 1; archiveVersion = 1;
classes = { classes = {
}; };
objectVersion = 50; objectVersion = 52;
objects = { objects = {
/* Begin PBXBuildFile section */ /* Begin PBXBuildFile section */
@@ -28,6 +28,10 @@
E24ACE602607F45E009BF703 /* EditAbilityScores.swift in Sources */ = {isa = PBXBuildFile; fileRef = E24ACE5F2607F45E009BF703 /* EditAbilityScores.swift */; }; E24ACE602607F45E009BF703 /* EditAbilityScores.swift in Sources */ = {isa = PBXBuildFile; fileRef = E24ACE5F2607F45E009BF703 /* EditAbilityScores.swift */; };
E24ACE652607F55D009BF703 /* EditSavingThrows.swift in Sources */ = {isa = PBXBuildFile; fileRef = E24ACE642607F55D009BF703 /* EditSavingThrows.swift */; }; E24ACE652607F55D009BF703 /* EditSavingThrows.swift in Sources */ = {isa = PBXBuildFile; fileRef = E24ACE642607F55D009BF703 /* EditSavingThrows.swift */; };
E24ACE6A2607F715009BF703 /* EditSkills.swift in Sources */ = {isa = PBXBuildFile; fileRef = E24ACE692607F715009BF703 /* EditSkills.swift */; }; E24ACE6A2607F715009BF703 /* EditSkills.swift in Sources */ = {isa = PBXBuildFile; fileRef = E24ACE692607F715009BF703 /* EditSkills.swift */; };
E254F901260D07C1009295A5 /* MarkdownUI in Frameworks */ = {isa = PBXBuildFile; productRef = E254F900260D07C1009295A5 /* MarkdownUI */; };
E254F906260D0818009295A5 /* AbilityViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = E254F905260D0818009295A5 /* AbilityViewModel.swift */; };
E254F90E260D19A0009295A5 /* EditAbilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = E254F90D260D19A0009295A5 /* EditAbilities.swift */; };
E254F913260D1F6D009295A5 /* EditAbility.swift in Sources */ = {isa = PBXBuildFile; fileRef = E254F912260D1F6D009295A5 /* EditAbility.swift */; };
E2570FB925B1AC520055B23B /* MonsterCardsApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2570FB825B1AC520055B23B /* MonsterCardsApp.swift */; }; E2570FB925B1AC520055B23B /* MonsterCardsApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2570FB825B1AC520055B23B /* MonsterCardsApp.swift */; };
E2570FBB25B1AC520055B23B /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2570FBA25B1AC520055B23B /* ContentView.swift */; }; E2570FBB25B1AC520055B23B /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E2570FBA25B1AC520055B23B /* ContentView.swift */; };
E2570FBD25B1AC550055B23B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E2570FBC25B1AC550055B23B /* Assets.xcassets */; }; E2570FBD25B1AC550055B23B /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E2570FBC25B1AC550055B23B /* Assets.xcassets */; };
@@ -96,6 +100,9 @@
E24ACE5F2607F45E009BF703 /* EditAbilityScores.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditAbilityScores.swift; sourceTree = "<group>"; }; E24ACE5F2607F45E009BF703 /* EditAbilityScores.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditAbilityScores.swift; sourceTree = "<group>"; };
E24ACE642607F55D009BF703 /* EditSavingThrows.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditSavingThrows.swift; sourceTree = "<group>"; }; E24ACE642607F55D009BF703 /* EditSavingThrows.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditSavingThrows.swift; sourceTree = "<group>"; };
E24ACE692607F715009BF703 /* EditSkills.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditSkills.swift; sourceTree = "<group>"; }; E24ACE692607F715009BF703 /* EditSkills.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditSkills.swift; sourceTree = "<group>"; };
E254F905260D0818009295A5 /* AbilityViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AbilityViewModel.swift; sourceTree = "<group>"; };
E254F90D260D19A0009295A5 /* EditAbilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditAbilities.swift; sourceTree = "<group>"; };
E254F912260D1F6D009295A5 /* EditAbility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EditAbility.swift; sourceTree = "<group>"; };
E2570FB525B1AC520055B23B /* MonsterCards.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MonsterCards.app; sourceTree = BUILT_PRODUCTS_DIR; }; E2570FB525B1AC520055B23B /* MonsterCards.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = MonsterCards.app; sourceTree = BUILT_PRODUCTS_DIR; };
E2570FB825B1AC520055B23B /* MonsterCardsApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MonsterCardsApp.swift; sourceTree = "<group>"; }; E2570FB825B1AC520055B23B /* MonsterCardsApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MonsterCardsApp.swift; sourceTree = "<group>"; };
E2570FBA25B1AC520055B23B /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; }; E2570FBA25B1AC520055B23B /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
@@ -136,6 +143,7 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
E254F901260D07C1009295A5 /* MarkdownUI in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -237,6 +245,8 @@
E2570FF925B1AE020055B23B /* Collections.swift */, E2570FF925B1AE020055B23B /* Collections.swift */,
E2570FBA25B1AC520055B23B /* ContentView.swift */, E2570FBA25B1AC520055B23B /* ContentView.swift */,
E2570FF425B1ADEB0055B23B /* Dashboard.swift */, E2570FF425B1ADEB0055B23B /* Dashboard.swift */,
E254F90D260D19A0009295A5 /* EditAbilities.swift */,
E254F912260D1F6D009295A5 /* EditAbility.swift */,
E24ACE5F2607F45E009BF703 /* EditAbilityScores.swift */, E24ACE5F2607F45E009BF703 /* EditAbilityScores.swift */,
E24ACE552607EE94009BF703 /* EditArmor.swift */, E24ACE552607EE94009BF703 /* EditArmor.swift */,
E24ACE4F2607326E009BF703 /* EditBasicInfo.swift */, E24ACE4F2607326E009BF703 /* EditBasicInfo.swift */,
@@ -267,6 +277,7 @@
E257101225B1B2790055B23B /* Models */ = { E257101225B1B2790055B23B /* Models */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
E254F905260D0818009295A5 /* AbilityViewModel.swift */,
E216B7B6260C5A9800FB205F /* ChallengeRatingViewModel.swift */, E216B7B6260C5A9800FB205F /* ChallengeRatingViewModel.swift */,
E20209E625D8DEB600EFE733 /* Enums */, E20209E625D8DEB600EFE733 /* Enums */,
E216B790260C1FE800FB205F /* LanguageViewModel.swift */, E216B790260C1FE800FB205F /* LanguageViewModel.swift */,
@@ -304,6 +315,9 @@
dependencies = ( dependencies = (
); );
name = MonsterCards; name = MonsterCards;
packageProductDependencies = (
E254F900260D07C1009295A5 /* MarkdownUI */,
);
productName = MonsterCards; productName = MonsterCards;
productReference = E2570FB525B1AC520055B23B /* MonsterCards.app */; productReference = E2570FB525B1AC520055B23B /* MonsterCards.app */;
productType = "com.apple.product-type.application"; productType = "com.apple.product-type.application";
@@ -375,6 +389,9 @@
Base, Base,
); );
mainGroup = E2570FAC25B1AC520055B23B; mainGroup = E2570FAC25B1AC520055B23B;
packageReferences = (
E254F8FF260D07C1009295A5 /* XCRemoteSwiftPackageReference "MarkdownUI" */,
);
productRefGroup = E2570FB625B1AC520055B23B /* Products */; productRefGroup = E2570FB625B1AC520055B23B /* Products */;
projectDirPath = ""; projectDirPath = "";
projectRoot = ""; projectRoot = "";
@@ -423,6 +440,7 @@
E216B799260C2DF200FB205F /* EditLanguages.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 */,
E254F90E260D19A0009295A5 /* EditAbilities.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 */, E216B791260C1FE800FB205F /* LanguageViewModel.swift in Sources */,
@@ -449,9 +467,11 @@
E2CB0DE1260887ED00142591 /* StringViewModel.swift in Sources */, E2CB0DE1260887ED00142591 /* StringViewModel.swift in Sources */,
E20209F425D8E04300EFE733 /* ProficiencyType.swift in Sources */, E20209F425D8E04300EFE733 /* ProficiencyType.swift in Sources */,
E2CB0DC526086E5F00142591 /* SizeType.swift in Sources */, E2CB0DC526086E5F00142591 /* SizeType.swift in Sources */,
E254F906260D0818009295A5 /* AbilityViewModel.swift in Sources */,
E2570FFA25B1AE020055B23B /* Collections.swift in Sources */, E2570FFA25B1AE020055B23B /* Collections.swift in Sources */,
E24ACE5B2607F0F2009BF703 /* EditSpeed.swift in Sources */, E24ACE5B2607F0F2009BF703 /* EditSpeed.swift in Sources */,
E2570FB925B1AC520055B23B /* MonsterCardsApp.swift in Sources */, E2570FB925B1AC520055B23B /* MonsterCardsApp.swift in Sources */,
E254F913260D1F6D009295A5 /* EditAbility.swift in Sources */,
E216B7B7260C5A9800FB205F /* ChallengeRatingViewModel.swift in Sources */, E216B7B7260C5A9800FB205F /* ChallengeRatingViewModel.swift in Sources */,
E20209D325D8DD9600EFE733 /* Skill+CoreDataClass.swift in Sources */, E20209D325D8DD9600EFE733 /* Skill+CoreDataClass.swift in Sources */,
E24ACE652607F55D009BF703 /* EditSavingThrows.swift in Sources */, E24ACE652607F55D009BF703 /* EditSavingThrows.swift in Sources */,
@@ -781,6 +801,25 @@
}; };
/* End XCConfigurationList section */ /* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */
E254F8FF260D07C1009295A5 /* XCRemoteSwiftPackageReference "MarkdownUI" */ = {
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/gonzalezreal/MarkdownUI";
requirement = {
kind = upToNextMinorVersion;
minimumVersion = 0.5.1;
};
};
/* End XCRemoteSwiftPackageReference section */
/* Begin XCSwiftPackageProductDependency section */
E254F900260D07C1009295A5 /* MarkdownUI */ = {
isa = XCSwiftPackageProductDependency;
package = E254F8FF260D07C1009295A5 /* XCRemoteSwiftPackageReference "MarkdownUI" */;
productName = MarkdownUI;
};
/* End XCSwiftPackageProductDependency section */
/* Begin XCVersionGroup section */ /* Begin XCVersionGroup section */
E2570FC325B1AC550055B23B /* MonsterCards.xcdatamodeld */ = { E2570FC325B1AC550055B23B /* MonsterCards.xcdatamodeld */ = {
isa = XCVersionGroup; isa = XCVersionGroup;

View File

@@ -0,0 +1,70 @@
{
"object": {
"pins": [
{
"package": "AttributedText",
"repositoryURL": "https://github.com/gonzalezreal/AttributedText",
"state": {
"branch": null,
"revision": "bf076de48dbb2172525486936d512e1bba062642",
"version": "0.3.0"
}
},
{
"package": "combine-schedulers",
"repositoryURL": "https://github.com/pointfreeco/combine-schedulers",
"state": {
"branch": null,
"revision": "f1250faa1c1436ca83950ce676a4fe97a309a457",
"version": "0.4.1"
}
},
{
"package": "MarkdownUI",
"repositoryURL": "https://github.com/gonzalezreal/MarkdownUI",
"state": {
"branch": null,
"revision": "e8931e37dcf777b4c03ca76aa09c10cf246a2ced",
"version": "0.5.1"
}
},
{
"package": "NetworkImage",
"repositoryURL": "https://github.com/gonzalezreal/NetworkImage",
"state": {
"branch": null,
"revision": "15582b821cb097012b41b83d6219717926ec4ed6",
"version": "2.1.0"
}
},
{
"package": "cmark",
"repositoryURL": "https://github.com/SwiftDocOrg/swift-cmark.git",
"state": {
"branch": null,
"revision": "9c8096a23f44794bde297452d87c455fc4f76d42",
"version": "0.29.0+20210102.9c8096a"
}
},
{
"package": "SwiftCommonMark",
"repositoryURL": "https://github.com/gonzalezreal/SwiftCommonMark",
"state": {
"branch": null,
"revision": "f1575c37110a386e50da3208a04266b398bcefaa",
"version": "0.1.1"
}
},
{
"package": "xctest-dynamic-overlay",
"repositoryURL": "https://github.com/pointfreeco/xctest-dynamic-overlay",
"state": {
"branch": null,
"revision": "603974e3909ad4b48ba04aad7e0ceee4f077a518",
"version": "0.1.0"
}
}
]
},
"version": 1
}

View File

@@ -4,6 +4,69 @@
<dict> <dict>
<key>SchemeUserState</key> <key>SchemeUserState</key>
<dict> <dict>
<key>AttributedText_iOS (Playground) 1.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>11</integer>
</dict>
<key>AttributedText_iOS (Playground) 2.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>12</integer>
</dict>
<key>AttributedText_iOS (Playground).xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>10</integer>
</dict>
<key>AttributedText_macOS (Playground) 1.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>8</integer>
</dict>
<key>AttributedText_macOS (Playground) 2.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>9</integer>
</dict>
<key>AttributedText_macOS (Playground).xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>7</integer>
</dict>
<key>AttributedText_tvOS (Playground) 1.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>14</integer>
</dict>
<key>AttributedText_tvOS (Playground) 2.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>15</integer>
</dict>
<key>AttributedText_tvOS (Playground).xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>13</integer>
</dict>
<key>MonsterCards.xcscheme_^#shared#^_</key> <key>MonsterCards.xcscheme_^#shared#^_</key>
<dict> <dict>
<key>orderHint</key> <key>orderHint</key>

View File

@@ -0,0 +1,116 @@
//
// AbilityViewModel.swift
// MonsterCards
//
// Created by Tom Hicks on 3/25/21.
//
import Foundation
public class AbilityViewModel: NSObject, ObservableObject, Identifiable, NSSecureCoding {
public static var supportsSecureCoding = true
public func encode(with coder: NSCoder) {
coder.encode(self.name, forKey: "name")
coder.encode(self.abilityDescription, forKey: "abilityDescription")
}
public required init?(coder: NSCoder) {
self.name = coder.decodeObject(of: NSString.self, forKey: "name")! as String
self.abilityDescription = coder.decodeObject(of: NSString.self, forKey: "abilityDescription")! as String
}
@Published public var name: String
@Published public var abilityDescription: String
public init(_ name: String = "", _ abilityDescription: String = "") {
self.name = name
self.abilityDescription = abilityDescription
}
public var fullText: String {
get {
return String(format: "___%@:___ %@", name, abilityDescription)
}
}
public func renderedText(_ monster: Monster) -> String {
let strSave = monster.strengthModifier + monster.proficiencyBonus + 8
let dexSave = monster.dexterityModifier + monster.proficiencyBonus + 8
let conSave = monster.constitutionModifier + monster.proficiencyBonus + 8
let intSave = monster.intelligenceModifier + monster.proficiencyBonus + 8
let wisSave = monster.wisdomModifier + monster.proficiencyBonus + 8
let chaSave = monster.charismaModifier + monster.proficiencyBonus + 8
let strAttack = monster.strengthModifier + monster.proficiencyBonus
let dexAttack = monster.dexterityModifier + monster.proficiencyBonus
let conAttack = monster.constitutionModifier + monster.proficiencyBonus
let intAttack = monster.intelligenceModifier + monster.proficiencyBonus
let wisAttack = monster.wisdomModifier + monster.proficiencyBonus
let chaAttack = monster.charismaModifier + monster.proficiencyBonus
// TODO: find the other options and implement them [WIS], [WIS STAT], [WIS DMG], [WIS STAT 1d12]
let finalText = fullText
.replacingOccurrences(of: "[STR SAVE]", with: String(strSave))
.replacingOccurrences(of: "[DEX SAVE]", with: String(dexSave))
.replacingOccurrences(of: "[CON SAVE]", with: String(conSave))
.replacingOccurrences(of: "[INT SAVE]", with: String(intSave))
.replacingOccurrences(of: "[WIS SAVE]", with: String(wisSave))
.replacingOccurrences(of: "[CHA SAVE]", with: String(chaSave))
.replacingOccurrences(of: "[STR ATK]", with: String(strAttack))
.replacingOccurrences(of: "[DEX ATK]", with: String(dexAttack))
.replacingOccurrences(of: "[CON ATK]", with: String(conAttack))
.replacingOccurrences(of: "[INT ATK]", with: String(intAttack))
.replacingOccurrences(of: "[WIS ATK]", with: String(wisAttack))
.replacingOccurrences(of: "[CHA ATK]", with: String(chaAttack))
return finalText
}
}
extension AbilityViewModel: Comparable {
public static func < (lhs: AbilityViewModel, rhs: AbilityViewModel) -> Bool {
lhs.name < rhs.name
}
public static func == (lhs: AbilityViewModel, rhs: AbilityViewModel) -> Bool {
lhs.name == rhs.name &&
lhs.abilityDescription == rhs.abilityDescription
}
}
// TODO: figure out how to add this to the set of known transformers so it will work with transformer set to NSSecureUnarchiveFromDataTransformerName
@objc(AbilityViewModelValueTransformer)
public final class AbilityViewModelValueTransformer: 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 `AbilityViewModel` 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: AbilityViewModel.self, from: data as Data)
return language
} catch {
assertionFailure("Failed to transform `Data` to `AbilityViewModel`")
return nil
}
}
}

View File

@@ -60,6 +60,7 @@ class MonsterViewModel: ObservableObject {
@Published var challengeRating: ChallengeRating @Published var challengeRating: ChallengeRating
@Published var customChallengeRating: String @Published var customChallengeRating: String
@Published var customProficiencyBonus: Int64 @Published var customProficiencyBonus: Int64
@Published var abilities: [AbilityViewModel]
init(_ rawMonster: Monster? = nil) { init(_ rawMonster: Monster? = nil) {
self.name = "" self.name = ""
@@ -112,6 +113,7 @@ class MonsterViewModel: ObservableObject {
self.challengeRating = .one self.challengeRating = .one
self.customChallengeRating = "" self.customChallengeRating = ""
self.customProficiencyBonus = 0 self.customProficiencyBonus = 0
self.abilities = []
if (rawMonster != nil) { if (rawMonster != nil) {
self.copyFromMonster(monster: rawMonster!) self.copyFromMonster(monster: rawMonster!)
@@ -186,7 +188,12 @@ class MonsterViewModel: ObservableObject {
.sorted() .sorted()
self.languages = (monster.languages ?? []) self.languages = (monster.languages ?? [])
.map {LanguageViewModel($0.name, $0.speaks)}
.sorted() .sorted()
// These are manually sorted in the UI
self.abilities = (monster.abilities ?? [])
.map {AbilityViewModel($0.name, $0.abilityDescription)}
} }
func copyToMonster(monster: Monster) { func copyToMonster(monster: Monster) {
@@ -262,5 +269,7 @@ class MonsterViewModel: ObservableObject {
// This is necessary so core data sees the language objects as changed. Without it they won't be persisted. // This is necessary so core data sees the language objects as changed. Without it they won't be persisted.
monster.languages = languages.map {LanguageViewModel($0.name, $0.speaks)} monster.languages = languages.map {LanguageViewModel($0.name, $0.speaks)}
monster.abilities = abilities.map {AbilityViewModel($0.name, $0.abilityDescription)}
} }
} }

View File

@@ -1,6 +1,7 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="17709" systemVersion="20D91" minimumToolsVersion="Automatic" sourceLanguage="Swift" usedWithCloudKit="YES" userDefinedModelVersionIdentifier=""> <model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="17709" systemVersion="20D91" minimumToolsVersion="Automatic" sourceLanguage="Swift" usedWithCloudKit="YES" userDefinedModelVersionIdentifier="">
<entity name="Monster" representedClassName="Monster" syncable="YES" codeGenerationType="category"> <entity name="Monster" representedClassName="Monster" syncable="YES" codeGenerationType="category">
<attribute name="abilities" optional="YES" attributeType="Transformable" valueTransformerName="AbilityViewModelValueTransformer" customClassName="[AbilityViewModel]"/>
<attribute name="alignment" attributeType="String" defaultValueString=""/> <attribute name="alignment" attributeType="String" defaultValueString=""/>
<attribute name="armorType" attributeType="String" defaultValueString=""/> <attribute name="armorType" attributeType="String" defaultValueString=""/>
<attribute name="baseSpeed" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/> <attribute name="baseSpeed" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
@@ -67,7 +68,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="884"/> <element name="Monster" positionX="-63" positionY="-18" width="128" height="899"/>
<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>

View File

@@ -0,0 +1,64 @@
//
// EditAbilities.swift
// MonsterCards
//
// Created by Tom Hicks on 3/25/21.
//
import SwiftUI
struct EditAbilities: View {
@ObservedObject var viewModel: MonsterViewModel
var path: ReferenceWritableKeyPath<MonsterViewModel, [AbilityViewModel]>
var title: String
var body: some View {
List {
ForEach(viewModel[keyPath: path]) { ability in
NavigationLink(
ability.name,
destination: EditAbility(viewModel: ability))
}
.onDelete(perform: { indexSet in
for index in indexSet {
viewModel[keyPath: path].remove(at: index)
}
})
.onMove(perform: { indices, newOffset in
viewModel[keyPath: path].move(fromOffsets: indices, toOffset: newOffset)
})
}
.toolbar(content: {
ToolbarItemGroup(placement: .navigationBarTrailing) {
EditButton()
Button(
action: {
let newAbility = AbilityViewModel()
viewModel[keyPath: path].append(newAbility)
viewModel[keyPath: path] = viewModel[keyPath: path].sorted()
},
label: {
Image(systemName: "plus")
}
)
}
})
.onAppear(perform: {
viewModel[keyPath: path] = viewModel[keyPath: path].sorted()
})
.navigationTitle(title)
}
}
struct EditAbilities_Previews: PreviewProvider {
static var previews: some View {
let viewModel = MonsterViewModel()
EditAbilities(
viewModel: viewModel,
path: \.abilities,
title: "Abilities")
}
}

View File

@@ -0,0 +1,29 @@
//
// EditAbility.swift
// MonsterCards
//
// Created by Tom Hicks on 3/25/21.
//
import SwiftUI
struct EditAbility: View {
@ObservedObject var viewModel: AbilityViewModel
var body: some View {
VStack {
MCTextField(
label: "Name",
value: $viewModel.name)
TextEditor(text: $viewModel.abilityDescription)
}
}
}
struct EditAbility_Previews: PreviewProvider {
static var previews: some View {
let viewModel = AbilityViewModel()
EditAbility(viewModel: viewModel)
}
}

View File

@@ -73,6 +73,9 @@ struct EditMonster: View {
NavigationLink( NavigationLink(
"Challenge Rating", "Challenge Rating",
destination: EditChallengeRating(viewModel: monsterViewModel)) destination: EditChallengeRating(viewModel: monsterViewModel))
NavigationLink(
"Abilities", destination: EditAbilities(viewModel: monsterViewModel, path: \.abilities, title: "Abilities"))
} }
} }

View File

@@ -33,8 +33,8 @@ struct EditStrings: View {
.toolbar(content: { .toolbar(content: {
Button( Button(
action: { action: {
let newDamageType = StringViewModel() let newString = StringViewModel()
viewModel[keyPath: path].append(newDamageType) viewModel[keyPath: path].append(newString)
viewModel[keyPath: path] = viewModel[keyPath: path].sorted() viewModel[keyPath: path] = viewModel[keyPath: path].sorted()
}, },
label: { label: {
@@ -44,13 +44,17 @@ struct EditStrings: View {
}) })
.onAppear(perform: { .onAppear(perform: {
viewModel[keyPath: path] = viewModel[keyPath: path].sorted() viewModel[keyPath: path] = viewModel[keyPath: path].sorted()
}).navigationTitle(title) })
.navigationTitle(title)
} }
} }
struct EditStrings_Previews: PreviewProvider { struct EditStrings_Previews: PreviewProvider {
static var previews: some View { static var previews: some View {
let viewModel = MonsterViewModel() let viewModel = MonsterViewModel()
EditStrings(viewModel: viewModel, path: \.damageImmunities, title: "Damage Types") EditStrings(
viewModel: viewModel,
path: \.damageImmunities,
title: "Damage Types")
} }
} }

View File

@@ -6,6 +6,7 @@
// //
import SwiftUI import SwiftUI
import MarkdownUI
struct LabeledField<Content: View>: View { struct LabeledField<Content: View>: View {
@Environment(\.horizontalSizeClass) var sizeClass @Environment(\.horizontalSizeClass) var sizeClass
@@ -209,6 +210,7 @@ struct MonsterDetail: View {
VStack (alignment: .leading) { VStack (alignment: .leading) {
let monsterLanguagesDescription = monster.languagesDescription let monsterLanguagesDescription = monster.languagesDescription
let monsterChallengeRatingDescription = monster.challengeRatingDescription let monsterChallengeRatingDescription = monster.challengeRatingDescription
let monsterAbilities: [AbilityViewModel] = monster.abilities ?? []
BasicInfoView(monster: monster) BasicInfoView(monster: monster)
@@ -243,7 +245,15 @@ struct MonsterDetail: View {
} }
// Abilities // Abilities
if (monsterAbilities.count > 0) {
ForEach(monsterAbilities) { ability in
VStack {
Markdown(Document(ability.renderedText(monster)/*.fullText*/))
Divider()
}
}
}
// Actions // Actions
// Legendary Actions // Legendary Actions