Convertes to Swift and SwiftUI
This commit is contained in:
File diff suppressed because it is too large
Load Diff
@@ -2,6 +2,6 @@
|
|||||||
<Workspace
|
<Workspace
|
||||||
version = "1.0">
|
version = "1.0">
|
||||||
<FileRef
|
<FileRef
|
||||||
location = "self:MonsterCards.xcodeproj">
|
location = "self:">
|
||||||
</FileRef>
|
</FileRef>
|
||||||
</Workspace>
|
</Workspace>
|
||||||
|
|||||||
@@ -1,98 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Scheme
|
|
||||||
LastUpgradeVersion = "1200"
|
|
||||||
version = "1.3">
|
|
||||||
<BuildAction
|
|
||||||
parallelizeBuildables = "YES"
|
|
||||||
buildImplicitDependencies = "YES">
|
|
||||||
<BuildActionEntries>
|
|
||||||
<BuildActionEntry
|
|
||||||
buildForTesting = "YES"
|
|
||||||
buildForRunning = "YES"
|
|
||||||
buildForProfiling = "YES"
|
|
||||||
buildForArchiving = "YES"
|
|
||||||
buildForAnalyzing = "YES">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "E2F7246F25005E89007D87ED"
|
|
||||||
BuildableName = "Monster Cards.app"
|
|
||||||
BlueprintName = "MonsterCards"
|
|
||||||
ReferencedContainer = "container:MonsterCards.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildActionEntry>
|
|
||||||
</BuildActionEntries>
|
|
||||||
</BuildAction>
|
|
||||||
<TestAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
|
||||||
<Testables>
|
|
||||||
<TestableReference
|
|
||||||
skipped = "NO">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "E2F7249025005E8A007D87ED"
|
|
||||||
BuildableName = "MonsterCardsTests.xctest"
|
|
||||||
BlueprintName = "MonsterCardsTests"
|
|
||||||
ReferencedContainer = "container:MonsterCards.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</TestableReference>
|
|
||||||
<TestableReference
|
|
||||||
skipped = "NO">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "E2F7249B25005E8A007D87ED"
|
|
||||||
BuildableName = "MonsterCardsUITests.xctest"
|
|
||||||
BlueprintName = "MonsterCardsUITests"
|
|
||||||
ReferencedContainer = "container:MonsterCards.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</TestableReference>
|
|
||||||
</Testables>
|
|
||||||
</TestAction>
|
|
||||||
<LaunchAction
|
|
||||||
buildConfiguration = "Debug"
|
|
||||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
|
||||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
|
||||||
launchStyle = "0"
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
ignoresPersistentStateOnLaunch = "NO"
|
|
||||||
debugDocumentVersioning = "YES"
|
|
||||||
debugServiceExtension = "internal"
|
|
||||||
allowLocationSimulation = "YES">
|
|
||||||
<BuildableProductRunnable
|
|
||||||
runnableDebuggingMode = "0">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "E2F7246F25005E89007D87ED"
|
|
||||||
BuildableName = "Monster Cards.app"
|
|
||||||
BlueprintName = "MonsterCards"
|
|
||||||
ReferencedContainer = "container:MonsterCards.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildableProductRunnable>
|
|
||||||
</LaunchAction>
|
|
||||||
<ProfileAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
|
||||||
savedToolIdentifier = ""
|
|
||||||
useCustomWorkingDirectory = "NO"
|
|
||||||
debugDocumentVersioning = "YES">
|
|
||||||
<BuildableProductRunnable
|
|
||||||
runnableDebuggingMode = "0">
|
|
||||||
<BuildableReference
|
|
||||||
BuildableIdentifier = "primary"
|
|
||||||
BlueprintIdentifier = "E2F7246F25005E89007D87ED"
|
|
||||||
BuildableName = "Monster Cards.app"
|
|
||||||
BlueprintName = "MonsterCards"
|
|
||||||
ReferencedContainer = "container:MonsterCards.xcodeproj">
|
|
||||||
</BuildableReference>
|
|
||||||
</BuildableProductRunnable>
|
|
||||||
</ProfileAction>
|
|
||||||
<AnalyzeAction
|
|
||||||
buildConfiguration = "Debug">
|
|
||||||
</AnalyzeAction>
|
|
||||||
<ArchiveAction
|
|
||||||
buildConfiguration = "Release"
|
|
||||||
revealArchiveInOrganizer = "YES">
|
|
||||||
</ArchiveAction>
|
|
||||||
</Scheme>
|
|
||||||
10
MonsterCards.xcworkspace/contents.xcworkspacedata
generated
10
MonsterCards.xcworkspace/contents.xcworkspacedata
generated
@@ -1,10 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<Workspace
|
|
||||||
version = "1.0">
|
|
||||||
<FileRef
|
|
||||||
location = "group:MonsterCards.xcodeproj">
|
|
||||||
</FileRef>
|
|
||||||
<FileRef
|
|
||||||
location = "group:Pods/Pods.xcodeproj">
|
|
||||||
</FileRef>
|
|
||||||
</Workspace>
|
|
||||||
@@ -1,8 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
|
||||||
<plist version="1.0">
|
|
||||||
<dict>
|
|
||||||
<key>IDEDidComputeMac32BitWarning</key>
|
|
||||||
<true/>
|
|
||||||
</dict>
|
|
||||||
</plist>
|
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
//
|
|
||||||
// AppDelegate.h
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/2/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
|
||||||
#import <CoreData/CoreData.h>
|
|
||||||
|
|
||||||
@interface AppDelegate : UIResponder <UIApplicationDelegate>
|
|
||||||
|
|
||||||
@property (readonly, strong) NSPersistentCloudKitContainer *persistentContainer;
|
|
||||||
|
|
||||||
- (void)saveContext;
|
|
||||||
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@@ -1,86 +0,0 @@
|
|||||||
//
|
|
||||||
// AppDelegate.m
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/2/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "AppDelegate.h"
|
|
||||||
|
|
||||||
@interface AppDelegate ()
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation AppDelegate
|
|
||||||
|
|
||||||
|
|
||||||
- (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions {
|
|
||||||
// Override point for customization after application launch.
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark - UISceneSession lifecycle
|
|
||||||
|
|
||||||
|
|
||||||
- (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options {
|
|
||||||
// Called when a new scene session is being created.
|
|
||||||
// Use this method to select a configuration to create the new scene with.
|
|
||||||
return [[UISceneConfiguration alloc] initWithName:@"Default Configuration" sessionRole:connectingSceneSession.role];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
- (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet<UISceneSession *> *)sceneSessions {
|
|
||||||
// Called when the user discards a scene session.
|
|
||||||
// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
|
|
||||||
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
#pragma mark - Core Data stack
|
|
||||||
|
|
||||||
@synthesize persistentContainer = _persistentContainer;
|
|
||||||
|
|
||||||
- (NSPersistentCloudKitContainer *)persistentContainer {
|
|
||||||
// The persistent container for the application. This implementation creates and returns a container, having loaded the store for the application to it.
|
|
||||||
@synchronized (self) {
|
|
||||||
if (_persistentContainer == nil) {
|
|
||||||
_persistentContainer = [[NSPersistentCloudKitContainer alloc] initWithName:@"MonsterCards"];
|
|
||||||
[_persistentContainer loadPersistentStoresWithCompletionHandler:^(NSPersistentStoreDescription *storeDescription, NSError *error) {
|
|
||||||
if (error != nil) {
|
|
||||||
// Replace this implementation with code to handle the error appropriately.
|
|
||||||
// abort() 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.
|
|
||||||
*/
|
|
||||||
NSLog(@"Unresolved error %@, %@", error, error.userInfo);
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return _persistentContainer;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - Core Data Saving support
|
|
||||||
|
|
||||||
- (void)saveContext {
|
|
||||||
NSManagedObjectContext *context = self.persistentContainer.viewContext;
|
|
||||||
NSError *error = nil;
|
|
||||||
if ([context hasChanges] && ![context save:&error]) {
|
|
||||||
// Replace this implementation with code to handle the error appropriately.
|
|
||||||
// abort() 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.
|
|
||||||
NSLog(@"Unresolved error %@, %@", error, error.userInfo);
|
|
||||||
abort();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"colors" : [
|
||||||
|
{
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
23
MonsterCards/Assets.xcassets/section-divider.imageset/Contents.json
vendored
Normal file
23
MonsterCards/Assets.xcassets/section-divider.imageset/Contents.json
vendored
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "section-divider.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "1x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "section-divider@2x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "2x"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"filename" : "section-divider@3x.png",
|
||||||
|
"idiom" : "universal",
|
||||||
|
"scale" : "3x"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Before Width: | Height: | Size: 338 B After Width: | Height: | Size: 338 B |
|
Before Width: | Height: | Size: 1001 B After Width: | Height: | Size: 1001 B |
|
Before Width: | Height: | Size: 2.7 KiB After Width: | Height: | Size: 2.7 KiB |
@@ -1,25 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="13122.16" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" launchScreen="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="01J-lp-oVM">
|
|
||||||
<dependencies>
|
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="13104.12"/>
|
|
||||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
|
||||||
</dependencies>
|
|
||||||
<scenes>
|
|
||||||
<!--View Controller-->
|
|
||||||
<scene sceneID="EHf-IW-A2E">
|
|
||||||
<objects>
|
|
||||||
<viewController id="01J-lp-oVM" sceneMemberID="viewController">
|
|
||||||
<view key="view" contentMode="scaleToFill" id="Ze5-6b-2t3">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="375" height="667"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
|
||||||
<color key="backgroundColor" xcode11CocoaTouchSystemColor="systemBackgroundColor" cocoaTouchSystemColor="whiteColor"/>
|
|
||||||
<viewLayoutGuide key="safeArea" id="6Tk-OE-BBY"/>
|
|
||||||
</view>
|
|
||||||
</viewController>
|
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="iYj-Kq-Ea1" userLabel="First Responder" sceneMemberID="firstResponder"/>
|
|
||||||
</objects>
|
|
||||||
<point key="canvasLocation" x="53" y="375"/>
|
|
||||||
</scene>
|
|
||||||
</scenes>
|
|
||||||
</document>
|
|
||||||
@@ -1,681 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<document type="com.apple.InterfaceBuilder3.CocoaTouch.Storyboard.XIB" version="3.0" toolsVersion="17156" targetRuntime="iOS.CocoaTouch" propertyAccessControl="none" useAutolayout="YES" useTraitCollections="YES" useSafeAreas="YES" colorMatched="YES" initialViewController="49e-Tb-3d3">
|
|
||||||
<device id="ipad11_0rounded" orientation="landscape" layout="fullscreen" appearance="light"/>
|
|
||||||
<dependencies>
|
|
||||||
<deployment identifier="iOS"/>
|
|
||||||
<plugIn identifier="com.apple.InterfaceBuilder.IBCocoaTouchPlugin" version="17125"/>
|
|
||||||
<capability name="Safe area layout guides" minToolsVersion="9.0"/>
|
|
||||||
<capability name="System colors in document resources" minToolsVersion="11.0"/>
|
|
||||||
<capability name="documents saved in the Xcode 8 format" minToolsVersion="8.0"/>
|
|
||||||
</dependencies>
|
|
||||||
<scenes>
|
|
||||||
<!--Tab Bar Controller-->
|
|
||||||
<scene sceneID="yl2-sM-qoP">
|
|
||||||
<objects>
|
|
||||||
<tabBarController id="49e-Tb-3d3" sceneMemberID="viewController">
|
|
||||||
<tabBar key="tabBar" contentMode="scaleToFill" id="W28-zg-YXA">
|
|
||||||
<rect key="frame" x="0.0" y="975" width="768" height="49"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMinY="YES"/>
|
|
||||||
<color key="backgroundColor" red="0.0" green="0.0" blue="0.0" alpha="0.0" colorSpace="custom" customColorSpace="sRGB"/>
|
|
||||||
</tabBar>
|
|
||||||
<connections>
|
|
||||||
<segue destination="ymH-E2-afX" kind="relationship" relationship="viewControllers" id="aRQ-Ld-n77"/>
|
|
||||||
<segue destination="giS-lS-9u3" kind="relationship" relationship="viewControllers" id="QFA-Ya-tDm"/>
|
|
||||||
<segue destination="hrp-Ji-OWx" kind="relationship" relationship="viewControllers" id="OpL-Bq-zuh"/>
|
|
||||||
<segue destination="k1r-8K-fba" kind="relationship" relationship="viewControllers" id="tX1-v4-ah1"/>
|
|
||||||
</connections>
|
|
||||||
</tabBarController>
|
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="HuB-VB-40B" sceneMemberID="firstResponder"/>
|
|
||||||
</objects>
|
|
||||||
<point key="canvasLocation" x="-1197" y="135"/>
|
|
||||||
</scene>
|
|
||||||
<!--Dashboard-->
|
|
||||||
<scene sceneID="qOG-lF-VxJ">
|
|
||||||
<objects>
|
|
||||||
<viewController id="giS-lS-9u3" customClass="DashboardViewController" sceneMemberID="viewController">
|
|
||||||
<view key="view" contentMode="scaleToFill" id="JUJ-wP-ffb">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="1194" height="834"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
|
||||||
<viewLayoutGuide key="safeArea" id="QQV-YX-2Yb"/>
|
|
||||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
|
||||||
</view>
|
|
||||||
<tabBarItem key="tabBarItem" title="Dashboard" image="rectangle.3.offgrid.fill" catalog="system" id="wgb-7v-3jq"/>
|
|
||||||
</viewController>
|
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="onm-5g-reZ" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
|
||||||
</objects>
|
|
||||||
<point key="canvasLocation" x="622" y="-243"/>
|
|
||||||
</scene>
|
|
||||||
<!--Collections-->
|
|
||||||
<scene sceneID="7BQ-Kv-Tfd">
|
|
||||||
<objects>
|
|
||||||
<viewController id="hrp-Ji-OWx" customClass="CollectionsViewController" sceneMemberID="viewController">
|
|
||||||
<view key="view" contentMode="scaleToFill" id="j0E-Ka-SnM">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="1194" height="834"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
|
||||||
<viewLayoutGuide key="safeArea" id="Rkq-9l-CBb"/>
|
|
||||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
|
||||||
</view>
|
|
||||||
<tabBarItem key="tabBarItem" title="Collections" image="tray.full.fill" catalog="system" id="7og-Zf-zGt"/>
|
|
||||||
</viewController>
|
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="gar-xb-BMe" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
|
||||||
</objects>
|
|
||||||
<point key="canvasLocation" x="622" y="513"/>
|
|
||||||
</scene>
|
|
||||||
<!--Search View Controller-->
|
|
||||||
<scene sceneID="gMb-gI-y2F">
|
|
||||||
<objects>
|
|
||||||
<tableViewController id="WmO-9m-qPj" customClass="SearchViewController" sceneMemberID="viewController">
|
|
||||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" id="oCq-Hl-UA9">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="1194" height="834"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
|
||||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
|
||||||
<searchBar key="tableHeaderView" contentMode="redraw" text="" id="fQb-XL-QZB">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="1554" height="44"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" flexibleMaxY="YES"/>
|
|
||||||
<textInputTraits key="textInputTraits"/>
|
|
||||||
<connections>
|
|
||||||
<outlet property="delegate" destination="WmO-9m-qPj" id="z6d-4x-mQ2"/>
|
|
||||||
</connections>
|
|
||||||
</searchBar>
|
|
||||||
<prototypes>
|
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="MonsterCell" id="T01-Aw-boG">
|
|
||||||
<rect key="frame" x="0.0" y="72" width="1194" height="43.5"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="T01-Aw-boG" id="eV3-pN-I0b">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="1194" height="43.5"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
</tableViewCellContentView>
|
|
||||||
<connections>
|
|
||||||
<segue destination="2B6-hx-mku" kind="show" identifier="ShowMonsterDetail" id="Ob1-Gh-ZDQ"/>
|
|
||||||
</connections>
|
|
||||||
</tableViewCell>
|
|
||||||
</prototypes>
|
|
||||||
<connections>
|
|
||||||
<outlet property="dataSource" destination="WmO-9m-qPj" id="g6M-G6-7uY"/>
|
|
||||||
<outlet property="delegate" destination="WmO-9m-qPj" id="tXc-lT-yN2"/>
|
|
||||||
</connections>
|
|
||||||
</tableView>
|
|
||||||
<navigationItem key="navigationItem" id="iaa-fm-F6D"/>
|
|
||||||
<simulatedTabBarMetrics key="simulatedBottomBarMetrics"/>
|
|
||||||
<connections>
|
|
||||||
<outlet property="searchBar" destination="fQb-XL-QZB" id="Xxv-bW-N10"/>
|
|
||||||
<outlet property="searchResults" destination="oCq-Hl-UA9" id="zpM-Yi-UyD"/>
|
|
||||||
</connections>
|
|
||||||
</tableViewController>
|
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="pju-ns-0Vf" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
|
||||||
</objects>
|
|
||||||
<point key="canvasLocation" x="1532" y="-999"/>
|
|
||||||
</scene>
|
|
||||||
<!--Search-->
|
|
||||||
<scene sceneID="CGm-bP-IV8">
|
|
||||||
<objects>
|
|
||||||
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="ymH-E2-afX" sceneMemberID="viewController">
|
|
||||||
<tabBarItem key="tabBarItem" title="Search" image="magnifyingglass" catalog="system" id="pkF-hG-DTJ"/>
|
|
||||||
<toolbarItems/>
|
|
||||||
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="0jh-Lf-VY3">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="1194" height="50"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
</navigationBar>
|
|
||||||
<nil name="viewControllers"/>
|
|
||||||
<connections>
|
|
||||||
<segue destination="WmO-9m-qPj" kind="relationship" relationship="rootViewController" id="cee-Qb-RoS"/>
|
|
||||||
</connections>
|
|
||||||
</navigationController>
|
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="eQU-y1-4NN" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
|
||||||
</objects>
|
|
||||||
<point key="canvasLocation" x="622" y="-999"/>
|
|
||||||
</scene>
|
|
||||||
<!--Library-->
|
|
||||||
<scene sceneID="5mp-gP-3px">
|
|
||||||
<objects>
|
|
||||||
<navigationController automaticallyAdjustsScrollViewInsets="NO" id="k1r-8K-fba" sceneMemberID="viewController">
|
|
||||||
<tabBarItem key="tabBarItem" title="Library" image="book.fill" catalog="system" id="Kz3-Xe-dOi"/>
|
|
||||||
<toolbarItems/>
|
|
||||||
<navigationBar key="navigationBar" contentMode="scaleToFill" insetsLayoutMarginsFromSafeArea="NO" id="MGB-oQ-Uth">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="1194" height="50"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
</navigationBar>
|
|
||||||
<nil name="viewControllers"/>
|
|
||||||
<connections>
|
|
||||||
<segue destination="5c8-8y-QZ5" kind="relationship" relationship="rootViewController" id="OAU-YH-mgF"/>
|
|
||||||
</connections>
|
|
||||||
</navigationController>
|
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="vS1-d2-ni5" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
|
||||||
</objects>
|
|
||||||
<point key="canvasLocation" x="622" y="1269"/>
|
|
||||||
</scene>
|
|
||||||
<!--Library-->
|
|
||||||
<scene sceneID="bsS-Pz-KZX">
|
|
||||||
<objects>
|
|
||||||
<tableViewController id="5c8-8y-QZ5" customClass="LibraryViewController" sceneMemberID="viewController">
|
|
||||||
<tableView key="view" clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" id="NgM-um-5Ur">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="1194" height="834"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
|
||||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
|
||||||
<prototypes>
|
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="MonsterCell" id="Boi-md-853">
|
|
||||||
<rect key="frame" x="0.0" y="28" width="1194" height="43.5"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="Boi-md-853" id="o5t-lM-bf4">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="1194" height="43.5"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
</tableViewCellContentView>
|
|
||||||
<connections>
|
|
||||||
<segue destination="2B6-hx-mku" kind="show" identifier="ShowMonsterDetail" id="W6E-7q-Wzb"/>
|
|
||||||
</connections>
|
|
||||||
</tableViewCell>
|
|
||||||
</prototypes>
|
|
||||||
<connections>
|
|
||||||
<outlet property="dataSource" destination="5c8-8y-QZ5" id="xtC-Yg-dwQ"/>
|
|
||||||
<outlet property="delegate" destination="5c8-8y-QZ5" id="6wi-Pi-KwG"/>
|
|
||||||
</connections>
|
|
||||||
</tableView>
|
|
||||||
<navigationItem key="navigationItem" title="Library" id="MnW-A3-Ua9">
|
|
||||||
<barButtonItem key="rightBarButtonItem" title="Item" image="plus" catalog="system" id="qDP-Tb-aMi">
|
|
||||||
<connections>
|
|
||||||
<action selector="addNewMonster:" destination="5c8-8y-QZ5" id="pQe-rL-fqA"/>
|
|
||||||
</connections>
|
|
||||||
</barButtonItem>
|
|
||||||
</navigationItem>
|
|
||||||
<connections>
|
|
||||||
<outlet property="monstersTable" destination="NgM-um-5Ur" id="HFK-4U-MeQ"/>
|
|
||||||
</connections>
|
|
||||||
</tableViewController>
|
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="32A-4i-7h9" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
|
||||||
</objects>
|
|
||||||
<point key="canvasLocation" x="1532" y="1269"/>
|
|
||||||
</scene>
|
|
||||||
<!--Monster View Controller-->
|
|
||||||
<scene sceneID="b1L-lx-eFe">
|
|
||||||
<objects>
|
|
||||||
<viewController id="2B6-hx-mku" customClass="MonsterViewController" sceneMemberID="viewController">
|
|
||||||
<view key="view" contentMode="scaleToFill" id="skY-EN-baw">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="1194" height="834"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
|
||||||
<subviews>
|
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="pZa-ia-7UT">
|
|
||||||
<rect key="frame" x="8" y="58" width="1178" height="14.5"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
|
||||||
<color key="textColor" systemColor="secondaryLabelColor"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="section-divider" translatesAutoresizingMaskIntoConstraints="NO" id="WYM-ya-Yje">
|
|
||||||
<rect key="frame" x="8" y="80.5" width="1178" height="10"/>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstAttribute="height" constant="10" id="1ld-SE-NlL"/>
|
|
||||||
</constraints>
|
|
||||||
</imageView>
|
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="p05-uG-AlV">
|
|
||||||
<rect key="frame" x="8" y="98.5" width="1178" height="17"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
|
||||||
<nil key="textColor"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Ae7-YW-3xk">
|
|
||||||
<rect key="frame" x="8" y="123.5" width="1178" height="17"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
|
||||||
<nil key="textColor"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="0HK-T4-KQW" userLabel="Monster Speed">
|
|
||||||
<rect key="frame" x="8" y="148.5" width="1178" height="17"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
|
||||||
<nil key="textColor"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="section-divider" translatesAutoresizingMaskIntoConstraints="NO" id="8yl-Bh-hgf">
|
|
||||||
<rect key="frame" x="8" y="173.5" width="1178" height="10"/>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstAttribute="height" constant="10" id="93X-Qh-bnv"/>
|
|
||||||
</constraints>
|
|
||||||
</imageView>
|
|
||||||
<stackView opaque="NO" contentMode="scaleToFill" distribution="fillEqually" translatesAutoresizingMaskIntoConstraints="NO" id="lBc-X0-1US" userLabel="Ability Scores">
|
|
||||||
<rect key="frame" x="0.0" y="191.5" width="1194" height="29"/>
|
|
||||||
<subviews>
|
|
||||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="rKw-op-tu1" userLabel="Strength">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="199" height="29"/>
|
|
||||||
<subviews>
|
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="STR" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="sJQ-m8-4rU" userLabel="STR">
|
|
||||||
<rect key="frame" x="87" y="0.0" width="25.5" height="14.5"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" weight="heavy" pointSize="12"/>
|
|
||||||
<color key="textColor" red="0.60784313729999995" green="0.15686274510000001" blue="0.094117647060000004" alpha="1" colorSpace="calibratedRGB"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Gfb-yf-Tbu" userLabel="StrScore">
|
|
||||||
<rect key="frame" x="84" y="14.5" width="31" height="14.5"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
|
||||||
<color key="textColor" red="0.60784313729999995" green="0.15686274510000001" blue="0.094117647060000004" alpha="1" colorSpace="calibratedRGB"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
</subviews>
|
|
||||||
</stackView>
|
|
||||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="odM-CK-6Gr" userLabel="Dexterity">
|
|
||||||
<rect key="frame" x="199" y="0.0" width="199" height="29"/>
|
|
||||||
<subviews>
|
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="DEX" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="OkX-5Z-BLZ" userLabel="DEX">
|
|
||||||
<rect key="frame" x="86.5" y="0.0" width="26" height="14.5"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" weight="heavy" pointSize="12"/>
|
|
||||||
<color key="textColor" red="0.60784313729999995" green="0.15686274510000001" blue="0.094117647060000004" alpha="1" colorSpace="calibratedRGB"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="1y6-3k-jnN" userLabel="DexScore">
|
|
||||||
<rect key="frame" x="84" y="14.5" width="31" height="14.5"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
|
||||||
<color key="textColor" red="0.60784313729999995" green="0.15686274510000001" blue="0.094117647060000004" alpha="1" colorSpace="calibratedRGB"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
</subviews>
|
|
||||||
</stackView>
|
|
||||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="8zm-3f-c3k" userLabel="Constitution">
|
|
||||||
<rect key="frame" x="398" y="0.0" width="199" height="29"/>
|
|
||||||
<subviews>
|
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="CON" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="H73-4Y-0aY" userLabel="CON">
|
|
||||||
<rect key="frame" x="85.5" y="0.0" width="28" height="14.5"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" weight="heavy" pointSize="12"/>
|
|
||||||
<color key="textColor" red="0.60784313729999995" green="0.15686274510000001" blue="0.094117647060000004" alpha="1" colorSpace="calibratedRGB"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="Koq-7S-VQO" userLabel="ConScore">
|
|
||||||
<rect key="frame" x="84" y="14.5" width="31" height="14.5"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
|
||||||
<color key="textColor" red="0.60784313729999995" green="0.15686274510000001" blue="0.094117647060000004" alpha="1" colorSpace="calibratedRGB"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
</subviews>
|
|
||||||
</stackView>
|
|
||||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="Ebv-nS-bwu" userLabel="Intelligence">
|
|
||||||
<rect key="frame" x="597" y="0.0" width="199" height="29"/>
|
|
||||||
<subviews>
|
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="INT" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="sRO-Ma-Wtu" userLabel="INT">
|
|
||||||
<rect key="frame" x="88.5" y="0.0" width="22" height="14.5"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" weight="heavy" pointSize="12"/>
|
|
||||||
<color key="textColor" red="0.60784313729999995" green="0.15686274510000001" blue="0.094117647060000004" alpha="1" colorSpace="calibratedRGB"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="p82-wJ-OUJ" userLabel="IntScore">
|
|
||||||
<rect key="frame" x="84" y="14.5" width="31" height="14.5"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
|
||||||
<color key="textColor" red="0.60784313729999995" green="0.15686274510000001" blue="0.094117647060000004" alpha="1" colorSpace="calibratedRGB"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
</subviews>
|
|
||||||
</stackView>
|
|
||||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="6Ba-BF-Q69" userLabel="Wisdom">
|
|
||||||
<rect key="frame" x="796" y="0.0" width="199" height="29"/>
|
|
||||||
<subviews>
|
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="WIS" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="63d-Ej-rif" userLabel="WIS">
|
|
||||||
<rect key="frame" x="87" y="0.0" width="25" height="14.5"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" weight="heavy" pointSize="12"/>
|
|
||||||
<color key="textColor" red="0.60784313729999995" green="0.15686274510000001" blue="0.094117647060000004" alpha="1" colorSpace="calibratedRGB"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="0lo-zE-s25" userLabel="WisScore">
|
|
||||||
<rect key="frame" x="84" y="14.5" width="31" height="14.5"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
|
||||||
<color key="textColor" red="0.60784313729999995" green="0.15686274510000001" blue="0.094117647060000004" alpha="1" colorSpace="calibratedRGB"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
</subviews>
|
|
||||||
</stackView>
|
|
||||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" alignment="center" translatesAutoresizingMaskIntoConstraints="NO" id="o2g-6F-Qaj" userLabel="Charisma">
|
|
||||||
<rect key="frame" x="995" y="0.0" width="199" height="29"/>
|
|
||||||
<subviews>
|
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="CHA" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="7ni-qo-7DG" userLabel="CHA">
|
|
||||||
<rect key="frame" x="85.5" y="0.0" width="28" height="14.5"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" weight="heavy" pointSize="12"/>
|
|
||||||
<color key="textColor" red="0.60784313729999995" green="0.15686274510000001" blue="0.094117647060000004" alpha="1" colorSpace="calibratedRGB"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="gz5-gL-ybS" userLabel="ChaScore">
|
|
||||||
<rect key="frame" x="84" y="14.5" width="31" height="14.5"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="12"/>
|
|
||||||
<color key="textColor" red="0.60784313729999995" green="0.15686274510000001" blue="0.094117647060000004" alpha="1" colorSpace="calibratedRGB"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
</subviews>
|
|
||||||
</stackView>
|
|
||||||
</subviews>
|
|
||||||
</stackView>
|
|
||||||
<imageView clipsSubviews="YES" userInteractionEnabled="NO" contentMode="scaleToFill" horizontalHuggingPriority="251" verticalHuggingPriority="251" image="section-divider" translatesAutoresizingMaskIntoConstraints="NO" id="zcw-26-kZQ">
|
|
||||||
<rect key="frame" x="8" y="228.5" width="1178" height="10"/>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstAttribute="height" constant="10" id="mAQ-Ry-dzJ"/>
|
|
||||||
</constraints>
|
|
||||||
</imageView>
|
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="wordWrap" numberOfLines="0" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="hMb-Iw-k4h">
|
|
||||||
<rect key="frame" x="8" y="246.5" width="1178" height="17"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
|
||||||
<nil key="textColor"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
</subviews>
|
|
||||||
<viewLayoutGuide key="safeArea" id="WIX-Yu-LXJ"/>
|
|
||||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstItem="pZa-ia-7UT" firstAttribute="leading" secondItem="WYM-ya-Yje" secondAttribute="leading" id="5Uq-aL-jK9"/>
|
|
||||||
<constraint firstItem="8yl-Bh-hgf" firstAttribute="centerX" secondItem="lBc-X0-1US" secondAttribute="centerX" id="6Lc-MB-cog"/>
|
|
||||||
<constraint firstItem="WIX-Yu-LXJ" firstAttribute="trailing" secondItem="hMb-Iw-k4h" secondAttribute="trailing" constant="8" id="7Ti-ID-py9"/>
|
|
||||||
<constraint firstItem="p05-uG-AlV" firstAttribute="trailing" secondItem="Ae7-YW-3xk" secondAttribute="trailing" id="DpK-wf-8qN"/>
|
|
||||||
<constraint firstItem="pZa-ia-7UT" firstAttribute="top" secondItem="WIX-Yu-LXJ" secondAttribute="top" constant="8" id="DpT-G9-lVC"/>
|
|
||||||
<constraint firstItem="pZa-ia-7UT" firstAttribute="trailing" secondItem="WYM-ya-Yje" secondAttribute="trailing" id="GdO-iX-qNf"/>
|
|
||||||
<constraint firstItem="lBc-X0-1US" firstAttribute="top" secondItem="8yl-Bh-hgf" secondAttribute="bottom" constant="8" symbolic="YES" id="Jva-26-0HW"/>
|
|
||||||
<constraint firstItem="8yl-Bh-hgf" firstAttribute="top" secondItem="0HK-T4-KQW" secondAttribute="bottom" constant="8" symbolic="YES" id="KEh-c7-PzK"/>
|
|
||||||
<constraint firstItem="8yl-Bh-hgf" firstAttribute="trailing" secondItem="zcw-26-kZQ" secondAttribute="trailing" id="KIJ-Ng-1ud"/>
|
|
||||||
<constraint firstItem="Ae7-YW-3xk" firstAttribute="trailing" secondItem="0HK-T4-KQW" secondAttribute="trailing" id="MCN-A6-9Zy"/>
|
|
||||||
<constraint firstItem="8yl-Bh-hgf" firstAttribute="leading" secondItem="zcw-26-kZQ" secondAttribute="leading" id="Mdw-9f-Xki"/>
|
|
||||||
<constraint firstItem="0HK-T4-KQW" firstAttribute="top" secondItem="Ae7-YW-3xk" secondAttribute="bottom" constant="8" symbolic="YES" id="NN6-F1-KAA"/>
|
|
||||||
<constraint firstItem="pZa-ia-7UT" firstAttribute="leading" secondItem="WIX-Yu-LXJ" secondAttribute="leading" constant="8" id="Pzo-wK-eiz"/>
|
|
||||||
<constraint firstItem="WYM-ya-Yje" firstAttribute="trailing" secondItem="p05-uG-AlV" secondAttribute="trailing" id="QYF-YD-yAJ"/>
|
|
||||||
<constraint firstItem="0HK-T4-KQW" firstAttribute="leading" secondItem="8yl-Bh-hgf" secondAttribute="leading" id="Zyv-3S-cKO"/>
|
|
||||||
<constraint firstItem="hMb-Iw-k4h" firstAttribute="leading" secondItem="WIX-Yu-LXJ" secondAttribute="leading" constant="8" id="cXv-Yu-4Za"/>
|
|
||||||
<constraint firstItem="Ae7-YW-3xk" firstAttribute="top" secondItem="p05-uG-AlV" secondAttribute="bottom" constant="8" symbolic="YES" id="cmv-YY-dax"/>
|
|
||||||
<constraint firstItem="lBc-X0-1US" firstAttribute="leading" secondItem="skY-EN-baw" secondAttribute="leading" id="dPJ-04-kH2"/>
|
|
||||||
<constraint firstItem="0HK-T4-KQW" firstAttribute="trailing" secondItem="8yl-Bh-hgf" secondAttribute="trailing" id="dTU-0J-ILa"/>
|
|
||||||
<constraint firstItem="WYM-ya-Yje" firstAttribute="leading" secondItem="p05-uG-AlV" secondAttribute="leading" id="ds0-tz-itq"/>
|
|
||||||
<constraint firstItem="Ae7-YW-3xk" firstAttribute="leading" secondItem="0HK-T4-KQW" secondAttribute="leading" id="n28-CV-L8l"/>
|
|
||||||
<constraint firstItem="p05-uG-AlV" firstAttribute="leading" secondItem="Ae7-YW-3xk" secondAttribute="leading" id="nHB-QQ-JPT"/>
|
|
||||||
<constraint firstItem="zcw-26-kZQ" firstAttribute="top" secondItem="lBc-X0-1US" secondAttribute="bottom" constant="8" symbolic="YES" id="q0O-jK-tzU"/>
|
|
||||||
<constraint firstAttribute="trailing" secondItem="lBc-X0-1US" secondAttribute="trailing" id="sTY-zt-Sac"/>
|
|
||||||
<constraint firstItem="hMb-Iw-k4h" firstAttribute="top" secondItem="zcw-26-kZQ" secondAttribute="bottom" constant="8" id="sk5-ZE-Vfd"/>
|
|
||||||
<constraint firstItem="WYM-ya-Yje" firstAttribute="top" secondItem="pZa-ia-7UT" secondAttribute="bottom" constant="8" symbolic="YES" id="thF-cF-gcb"/>
|
|
||||||
<constraint firstItem="p05-uG-AlV" firstAttribute="top" secondItem="WYM-ya-Yje" secondAttribute="bottom" constant="8" symbolic="YES" id="ydJ-o0-UE4"/>
|
|
||||||
</constraints>
|
|
||||||
</view>
|
|
||||||
<navigationItem key="navigationItem" id="U0U-wB-eVJ">
|
|
||||||
<barButtonItem key="rightBarButtonItem" title="Edit" id="JQh-6t-vKa">
|
|
||||||
<connections>
|
|
||||||
<segue destination="9au-4W-gkk" kind="show" identifier="EditMonster" id="h6n-Yx-e51"/>
|
|
||||||
</connections>
|
|
||||||
</barButtonItem>
|
|
||||||
</navigationItem>
|
|
||||||
<connections>
|
|
||||||
<outlet property="monsterArmorClass" destination="p05-uG-AlV" id="ISg-8R-AnX"/>
|
|
||||||
<outlet property="monsterCharisma" destination="gz5-gL-ybS" id="OIf-em-3nf"/>
|
|
||||||
<outlet property="monsterConstitution" destination="Koq-7S-VQO" id="Y6J-nO-R5x"/>
|
|
||||||
<outlet property="monsterDexterity" destination="1y6-3k-jnN" id="6rv-Fm-Jbt"/>
|
|
||||||
<outlet property="monsterHitPoints" destination="Ae7-YW-3xk" id="3og-CQ-jGe"/>
|
|
||||||
<outlet property="monsterIntelligence" destination="p82-wJ-OUJ" id="YzY-1R-Bje"/>
|
|
||||||
<outlet property="monsterMeta" destination="pZa-ia-7UT" id="QEV-cs-IEk"/>
|
|
||||||
<outlet property="monsterSavingThrows" destination="hMb-Iw-k4h" id="LYK-xX-GCc"/>
|
|
||||||
<outlet property="monsterSpeed" destination="0HK-T4-KQW" id="V8T-ZK-2aB"/>
|
|
||||||
<outlet property="monsterStrength" destination="Gfb-yf-Tbu" id="obD-QK-ZSo"/>
|
|
||||||
<outlet property="monsterWisdom" destination="0lo-zE-s25" id="Wy8-kG-8Gg"/>
|
|
||||||
</connections>
|
|
||||||
</viewController>
|
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="lvO-c7-FKV" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
|
||||||
</objects>
|
|
||||||
<point key="canvasLocation" x="2441" y="1269"/>
|
|
||||||
</scene>
|
|
||||||
<!--Edit Monster-->
|
|
||||||
<scene sceneID="Pm6-vB-wuj">
|
|
||||||
<objects>
|
|
||||||
<viewController id="9au-4W-gkk" customClass="EditMonsterViewController" sceneMemberID="viewController">
|
|
||||||
<view key="view" contentMode="scaleToFill" id="0ji-9h-I5j">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="1194" height="834"/>
|
|
||||||
<autoresizingMask key="autoresizingMask" widthSizable="YES" heightSizable="YES"/>
|
|
||||||
<subviews>
|
|
||||||
<tableView clipsSubviews="YES" contentMode="scaleToFill" alwaysBounceVertical="YES" dataMode="prototypes" style="plain" separatorStyle="default" rowHeight="-1" estimatedRowHeight="-1" sectionHeaderHeight="28" sectionFooterHeight="28" translatesAutoresizingMaskIntoConstraints="NO" id="Ph7-w5-jbm">
|
|
||||||
<rect key="frame" x="0.0" y="50" width="1194" height="719"/>
|
|
||||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
|
||||||
<prototypes>
|
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="MCRadioField" id="iSG-Tv-aCX" userLabel="MCRadioField" customClass="MCRadioFieldTableViewCell">
|
|
||||||
<rect key="frame" x="0.0" y="28" width="1194" height="92"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="iSG-Tv-aCX" id="scU-nK-qiI">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="1194" height="92"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
<subviews>
|
|
||||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" distribution="equalSpacing" translatesAutoresizingMaskIntoConstraints="NO" id="nTa-ey-RFM">
|
|
||||||
<rect key="frame" x="20" y="20" width="1154" height="52"/>
|
|
||||||
<subviews>
|
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="clip" baselineAdjustment="alignBaselines" minimumScaleFactor="0.25" translatesAutoresizingMaskIntoConstraints="NO" id="Sap-Qr-1ZU">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="1154" height="20.5"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
|
||||||
<nil key="textColor"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
<segmentedControl opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="top" segmentControlStyle="plain" selectedSegmentIndex="0" translatesAutoresizingMaskIntoConstraints="NO" id="rJq-eZ-wvm">
|
|
||||||
<rect key="frame" x="0.0" y="21" width="1154" height="32"/>
|
|
||||||
<segments>
|
|
||||||
<segment title="First"/>
|
|
||||||
<segment title="Second"/>
|
|
||||||
</segments>
|
|
||||||
<connections>
|
|
||||||
<action selector="selectedSegmentChanged:" destination="iSG-Tv-aCX" eventType="valueChanged" id="k8p-F7-ILf"/>
|
|
||||||
</connections>
|
|
||||||
</segmentedControl>
|
|
||||||
</subviews>
|
|
||||||
</stackView>
|
|
||||||
</subviews>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstItem="nTa-ey-RFM" firstAttribute="top" secondItem="scU-nK-qiI" secondAttribute="top" constant="20" id="VT0-G5-ofZ"/>
|
|
||||||
<constraint firstAttribute="bottom" secondItem="nTa-ey-RFM" secondAttribute="bottom" constant="20" id="XR5-nT-N05"/>
|
|
||||||
<constraint firstItem="nTa-ey-RFM" firstAttribute="leading" secondItem="scU-nK-qiI" secondAttribute="leading" constant="20" id="oB6-ry-kjt"/>
|
|
||||||
<constraint firstAttribute="trailing" secondItem="nTa-ey-RFM" secondAttribute="trailing" constant="20" id="p7Q-Bn-sSi"/>
|
|
||||||
</constraints>
|
|
||||||
</tableViewCellContentView>
|
|
||||||
<connections>
|
|
||||||
<outlet property="labelView" destination="Sap-Qr-1ZU" id="qJ3-Wb-qrb"/>
|
|
||||||
<outlet property="segmentedControl" destination="rJq-eZ-wvm" id="gKh-IH-iQO"/>
|
|
||||||
</connections>
|
|
||||||
</tableViewCell>
|
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="MCIntegerField" id="KwC-8P-1dy" userLabel="MCIntegerField" customClass="MCIntegerFieldTableViewCell">
|
|
||||||
<rect key="frame" x="0.0" y="120" width="1194" height="95.5"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="KwC-8P-1dy" id="Dwe-Tg-VQ6">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="1194" height="95.5"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
<subviews>
|
|
||||||
<stackView opaque="NO" contentMode="scaleToFill" axis="vertical" translatesAutoresizingMaskIntoConstraints="NO" id="tqg-e0-rNO">
|
|
||||||
<rect key="frame" x="20" y="20" width="1154" height="55.5"/>
|
|
||||||
<subviews>
|
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="clip" baselineAdjustment="alignBaselines" minimumScaleFactor="0.25" translatesAutoresizingMaskIntoConstraints="NO" id="7nN-Bo-Jgm">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="1154" height="21"/>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstAttribute="height" constant="21" id="x72-wj-jT3"/>
|
|
||||||
</constraints>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
|
||||||
<nil key="textColor"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
<stackView opaque="NO" contentMode="scaleToFill" translatesAutoresizingMaskIntoConstraints="NO" id="5Hz-MY-MYZ">
|
|
||||||
<rect key="frame" x="0.0" y="21" width="1154" height="34.5"/>
|
|
||||||
<subviews>
|
|
||||||
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" textAlignment="natural" minimumFontSize="10" clearButtonMode="whileEditing" translatesAutoresizingMaskIntoConstraints="NO" id="RCc-cF-BI3" userLabel="Text Field">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="1060" height="34.5"/>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstAttribute="height" constant="34" id="8Nj-FL-Ab6"/>
|
|
||||||
</constraints>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
|
||||||
<textInputTraits key="textInputTraits"/>
|
|
||||||
<connections>
|
|
||||||
<action selector="textFieldValueChanged:" destination="KwC-8P-1dy" eventType="valueChanged" id="5In-Xx-LJB"/>
|
|
||||||
</connections>
|
|
||||||
</textField>
|
|
||||||
<stepper opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" maximumValue="100" translatesAutoresizingMaskIntoConstraints="NO" id="vgc-Rg-Zc8">
|
|
||||||
<rect key="frame" x="1060" y="0.0" width="94" height="34.5"/>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstAttribute="height" constant="34" id="HhX-Qt-vVP"/>
|
|
||||||
</constraints>
|
|
||||||
<connections>
|
|
||||||
<action selector="stepperValueChanged:" destination="KwC-8P-1dy" eventType="valueChanged" id="BKh-dh-f3d"/>
|
|
||||||
</connections>
|
|
||||||
</stepper>
|
|
||||||
</subviews>
|
|
||||||
</stackView>
|
|
||||||
</subviews>
|
|
||||||
</stackView>
|
|
||||||
</subviews>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstAttribute="trailing" secondItem="tqg-e0-rNO" secondAttribute="trailing" constant="20" id="9U8-8r-ZOV"/>
|
|
||||||
<constraint firstItem="tqg-e0-rNO" firstAttribute="top" secondItem="Dwe-Tg-VQ6" secondAttribute="top" constant="20" id="Ctm-x1-7Uf"/>
|
|
||||||
<constraint firstAttribute="bottom" secondItem="tqg-e0-rNO" secondAttribute="bottom" constant="20" id="gHo-d9-oh1"/>
|
|
||||||
<constraint firstItem="tqg-e0-rNO" firstAttribute="leading" secondItem="Dwe-Tg-VQ6" secondAttribute="leading" constant="20" id="pSJ-yP-eoc"/>
|
|
||||||
</constraints>
|
|
||||||
</tableViewCellContentView>
|
|
||||||
<connections>
|
|
||||||
<outlet property="labelView" destination="7nN-Bo-Jgm" id="TC4-Xt-Rrb"/>
|
|
||||||
<outlet property="stepper" destination="vgc-Rg-Zc8" id="QT0-L8-npw"/>
|
|
||||||
<outlet property="textField" destination="RCc-cF-BI3" id="JpG-sb-BPS"/>
|
|
||||||
</connections>
|
|
||||||
</tableViewCell>
|
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="MCShortStringField" id="s66-ds-RzY" userLabel="MCShortStringField" customClass="MCShortStringFieldTableViewCell">
|
|
||||||
<rect key="frame" x="0.0" y="215.5" width="1194" height="74.5"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="s66-ds-RzY" id="hip-Sr-WMn">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="1194" height="74.5"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
<subviews>
|
|
||||||
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" textAlignment="natural" minimumFontSize="17" clearButtonMode="whileEditing" translatesAutoresizingMaskIntoConstraints="NO" id="vSp-Bb-6Ii">
|
|
||||||
<rect key="frame" x="20" y="20" width="1154" height="34.5"/>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstAttribute="height" constant="34" id="bOl-6v-0Mc"/>
|
|
||||||
<constraint firstAttribute="height" relation="greaterThanOrEqual" constant="34" id="wG5-Lz-PNz"/>
|
|
||||||
</constraints>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
|
||||||
<textInputTraits key="textInputTraits"/>
|
|
||||||
<connections>
|
|
||||||
<action selector="textFieldValueChanged:" destination="s66-ds-RzY" eventType="valueChanged" id="aXL-JN-6WT"/>
|
|
||||||
</connections>
|
|
||||||
</textField>
|
|
||||||
</subviews>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstItem="vSp-Bb-6Ii" firstAttribute="centerY" secondItem="hip-Sr-WMn" secondAttribute="centerY" id="8vy-Pl-D15"/>
|
|
||||||
<constraint firstItem="vSp-Bb-6Ii" firstAttribute="top" secondItem="hip-Sr-WMn" secondAttribute="top" constant="20" id="Olm-z3-U8k"/>
|
|
||||||
<constraint firstItem="vSp-Bb-6Ii" firstAttribute="leading" secondItem="hip-Sr-WMn" secondAttribute="leading" constant="20" id="QZR-Jm-4WV"/>
|
|
||||||
<constraint firstAttribute="bottom" secondItem="vSp-Bb-6Ii" secondAttribute="bottom" constant="20" id="oTQ-Be-G2j"/>
|
|
||||||
<constraint firstAttribute="trailing" secondItem="vSp-Bb-6Ii" secondAttribute="trailing" constant="20" id="x9A-uQ-TpO"/>
|
|
||||||
</constraints>
|
|
||||||
</tableViewCellContentView>
|
|
||||||
<connections>
|
|
||||||
<outlet property="textField" destination="vSp-Bb-6Ii" id="LsH-bK-Vns"/>
|
|
||||||
</connections>
|
|
||||||
</tableViewCell>
|
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="MCBooleanField" id="aNO-fN-IMT" userLabel="MCBooleanField" customClass="MCBooleanFieldTableViewCell">
|
|
||||||
<rect key="frame" x="0.0" y="290" width="1194" height="71.5"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="aNO-fN-IMT" id="S88-r0-N1u">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="1194" height="71.5"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
<subviews>
|
|
||||||
<stackView opaque="NO" contentMode="scaleToFill" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="mgS-vl-2ZB">
|
|
||||||
<rect key="frame" x="20" y="20" width="1154" height="31.5"/>
|
|
||||||
<subviews>
|
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="tsg-zB-HOv">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="1085" height="31.5"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
|
||||||
<nil key="textColor"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
<switch opaque="NO" contentMode="scaleToFill" horizontalHuggingPriority="750" verticalHuggingPriority="750" contentHorizontalAlignment="center" contentVerticalAlignment="center" on="YES" translatesAutoresizingMaskIntoConstraints="NO" id="L1a-6J-Yco">
|
|
||||||
<rect key="frame" x="1105" y="0.0" width="51" height="31.5"/>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstAttribute="width" constant="49" id="b2p-dO-cAL"/>
|
|
||||||
</constraints>
|
|
||||||
<connections>
|
|
||||||
<action selector="valueChanged:" destination="aNO-fN-IMT" eventType="valueChanged" id="uHk-k4-oPq"/>
|
|
||||||
</connections>
|
|
||||||
</switch>
|
|
||||||
</subviews>
|
|
||||||
</stackView>
|
|
||||||
</subviews>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstItem="mgS-vl-2ZB" firstAttribute="top" secondItem="S88-r0-N1u" secondAttribute="top" constant="20" id="Q94-qT-yUA"/>
|
|
||||||
<constraint firstItem="mgS-vl-2ZB" firstAttribute="leading" secondItem="S88-r0-N1u" secondAttribute="leading" constant="20" id="YCf-EG-kId"/>
|
|
||||||
<constraint firstAttribute="bottom" secondItem="mgS-vl-2ZB" secondAttribute="bottom" constant="20" id="a7T-95-BKz"/>
|
|
||||||
<constraint firstAttribute="trailing" secondItem="mgS-vl-2ZB" secondAttribute="trailing" constant="20" id="rE7-sz-lIl"/>
|
|
||||||
</constraints>
|
|
||||||
</tableViewCellContentView>
|
|
||||||
<connections>
|
|
||||||
<outlet property="labelView" destination="tsg-zB-HOv" id="ky7-dd-2zz"/>
|
|
||||||
<outlet property="switchView" destination="L1a-6J-Yco" id="tTF-vZ-9gZ"/>
|
|
||||||
</connections>
|
|
||||||
</tableViewCell>
|
|
||||||
<tableViewCell clipsSubviews="YES" contentMode="scaleToFill" preservesSuperviewLayoutMargins="YES" selectionStyle="default" indentationWidth="10" reuseIdentifier="MCSelectField" id="PQF-mC-UlI" userLabel="MCSelectField" customClass="MCSelectFieldTableViewCell">
|
|
||||||
<rect key="frame" x="0.0" y="361.5" width="1194" height="74.5"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
<tableViewCellContentView key="contentView" opaque="NO" clipsSubviews="YES" multipleTouchEnabled="YES" contentMode="center" preservesSuperviewLayoutMargins="YES" insetsLayoutMarginsFromSafeArea="NO" tableViewCell="PQF-mC-UlI" id="Wcu-Sa-z2O">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="1194" height="74.5"/>
|
|
||||||
<autoresizingMask key="autoresizingMask"/>
|
|
||||||
<subviews>
|
|
||||||
<stackView opaque="NO" contentMode="scaleToFill" spacing="20" translatesAutoresizingMaskIntoConstraints="NO" id="NMZ-Q6-qaW">
|
|
||||||
<rect key="frame" x="20" y="20" width="1154" height="34.5"/>
|
|
||||||
<subviews>
|
|
||||||
<label opaque="NO" userInteractionEnabled="NO" contentMode="left" horizontalHuggingPriority="251" verticalHuggingPriority="251" text="Label" textAlignment="natural" lineBreakMode="tailTruncation" baselineAdjustment="alignBaselines" adjustsFontSizeToFit="NO" translatesAutoresizingMaskIntoConstraints="NO" id="lik-sI-94j" userLabel="Label View">
|
|
||||||
<rect key="frame" x="0.0" y="0.0" width="41.5" height="34.5"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="17"/>
|
|
||||||
<nil key="textColor"/>
|
|
||||||
<nil key="highlightedColor"/>
|
|
||||||
</label>
|
|
||||||
<textField opaque="NO" contentMode="scaleToFill" contentHorizontalAlignment="left" contentVerticalAlignment="center" borderStyle="roundedRect" textAlignment="natural" minimumFontSize="17" translatesAutoresizingMaskIntoConstraints="NO" id="DFa-Hd-laX" userLabel="Text Field">
|
|
||||||
<rect key="frame" x="61.5" y="0.0" width="1092.5" height="34.5"/>
|
|
||||||
<fontDescription key="fontDescription" type="system" pointSize="14"/>
|
|
||||||
<textInputTraits key="textInputTraits"/>
|
|
||||||
</textField>
|
|
||||||
</subviews>
|
|
||||||
</stackView>
|
|
||||||
</subviews>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstAttribute="trailing" secondItem="NMZ-Q6-qaW" secondAttribute="trailing" constant="20" id="5M4-bs-UBx"/>
|
|
||||||
<constraint firstAttribute="bottom" secondItem="NMZ-Q6-qaW" secondAttribute="bottom" constant="20" id="8UW-EN-g5N"/>
|
|
||||||
<constraint firstItem="NMZ-Q6-qaW" firstAttribute="top" secondItem="Wcu-Sa-z2O" secondAttribute="top" constant="20" id="uKb-SO-18n"/>
|
|
||||||
<constraint firstItem="NMZ-Q6-qaW" firstAttribute="leading" secondItem="Wcu-Sa-z2O" secondAttribute="leading" constant="20" id="uxf-ZS-UgY"/>
|
|
||||||
</constraints>
|
|
||||||
</tableViewCellContentView>
|
|
||||||
<connections>
|
|
||||||
<outlet property="labelView" destination="lik-sI-94j" id="TYU-vE-Agy"/>
|
|
||||||
<outlet property="textField" destination="DFa-Hd-laX" id="8fm-vK-dar"/>
|
|
||||||
</connections>
|
|
||||||
</tableViewCell>
|
|
||||||
</prototypes>
|
|
||||||
</tableView>
|
|
||||||
</subviews>
|
|
||||||
<viewLayoutGuide key="safeArea" id="PhC-ja-AcN"/>
|
|
||||||
<color key="backgroundColor" systemColor="systemBackgroundColor"/>
|
|
||||||
<constraints>
|
|
||||||
<constraint firstItem="Ph7-w5-jbm" firstAttribute="leading" secondItem="PhC-ja-AcN" secondAttribute="leading" id="Z21-h9-5uc"/>
|
|
||||||
<constraint firstItem="PhC-ja-AcN" firstAttribute="bottom" secondItem="Ph7-w5-jbm" secondAttribute="bottom" id="sSZ-aH-KLX"/>
|
|
||||||
<constraint firstItem="Ph7-w5-jbm" firstAttribute="top" secondItem="PhC-ja-AcN" secondAttribute="top" id="utK-QF-3ZS"/>
|
|
||||||
<constraint firstItem="PhC-ja-AcN" firstAttribute="trailing" secondItem="Ph7-w5-jbm" secondAttribute="trailing" id="y6Z-Sy-84h"/>
|
|
||||||
</constraints>
|
|
||||||
</view>
|
|
||||||
<navigationItem key="navigationItem" title="Edit Monster" id="6DN-Xf-XAZ">
|
|
||||||
<barButtonItem key="leftBarButtonItem" systemItem="cancel" id="4Ld-nj-3La">
|
|
||||||
<connections>
|
|
||||||
<segue destination="IHZ-ur-HNo" kind="unwind" identifier="DiscardChanges" unwindAction="unwindWithSegue:" id="bO7-E6-0Qd"/>
|
|
||||||
</connections>
|
|
||||||
</barButtonItem>
|
|
||||||
<barButtonItem key="rightBarButtonItem" title="Save" id="fCy-Er-XgT">
|
|
||||||
<connections>
|
|
||||||
<segue destination="IHZ-ur-HNo" kind="unwind" identifier="SaveChanges" unwindAction="unwindWithSegue:" id="zmX-J1-j2M"/>
|
|
||||||
</connections>
|
|
||||||
</barButtonItem>
|
|
||||||
</navigationItem>
|
|
||||||
<connections>
|
|
||||||
<outlet property="monsterTableView" destination="Ph7-w5-jbm" id="O5X-1g-Uvp"/>
|
|
||||||
</connections>
|
|
||||||
</viewController>
|
|
||||||
<placeholder placeholderIdentifier="IBFirstResponder" id="cpm-3g-PFo" userLabel="First Responder" customClass="UIResponder" sceneMemberID="firstResponder"/>
|
|
||||||
<exit id="IHZ-ur-HNo" userLabel="Exit" sceneMemberID="exit"/>
|
|
||||||
</objects>
|
|
||||||
<point key="canvasLocation" x="3347.826086956522" y="1268.9732142857142"/>
|
|
||||||
</scene>
|
|
||||||
</scenes>
|
|
||||||
<inferredMetricsTieBreakers>
|
|
||||||
<segue reference="W6E-7q-Wzb"/>
|
|
||||||
</inferredMetricsTieBreakers>
|
|
||||||
<resources>
|
|
||||||
<image name="book.fill" catalog="system" width="128" height="102"/>
|
|
||||||
<image name="magnifyingglass" catalog="system" width="128" height="115"/>
|
|
||||||
<image name="plus" catalog="system" width="128" height="113"/>
|
|
||||||
<image name="rectangle.3.offgrid.fill" catalog="system" width="128" height="81"/>
|
|
||||||
<image name="section-divider" width="800" height="20"/>
|
|
||||||
<image name="tray.full.fill" catalog="system" width="128" height="88"/>
|
|
||||||
<systemColor name="secondaryLabelColor">
|
|
||||||
<color red="0.23529411764705882" green="0.23529411764705882" blue="0.2627450980392157" alpha="0.59999999999999998" colorSpace="custom" customColorSpace="sRGB"/>
|
|
||||||
</systemColor>
|
|
||||||
<systemColor name="systemBackgroundColor">
|
|
||||||
<color white="1" alpha="1" colorSpace="custom" customColorSpace="genericGamma22GrayColorSpace"/>
|
|
||||||
</systemColor>
|
|
||||||
</resources>
|
|
||||||
</document>
|
|
||||||
20
MonsterCards/Helpers/Color+Hex.swift
Normal file
20
MonsterCards/Helpers/Color+Hex.swift
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
//
|
||||||
|
// Color+Hex.swift
|
||||||
|
// MonsterCards
|
||||||
|
//
|
||||||
|
// Created by Tom Hicks on 1/17/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
extension Color {
|
||||||
|
init(hex: UInt, alpha: Double = 1) {
|
||||||
|
self.init(
|
||||||
|
.sRGB,
|
||||||
|
red: Double((hex >> 16) & 0xff) / 255,
|
||||||
|
green: Double((hex >> 8) & 0xff) / 255,
|
||||||
|
blue: Double((hex >> 0) & 0xff) / 255,
|
||||||
|
opacity: alpha)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
//
|
|
||||||
// HtmlHelper.h
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/12/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
@interface HTMLHelper : NSObject
|
|
||||||
|
|
||||||
+(NSAttributedString*)attributedStringFromHTML:(NSString*)html;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
||||||
@@ -1,24 +0,0 @@
|
|||||||
//
|
|
||||||
// HtmlHelper.m
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/12/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "HTMLHelper.h"
|
|
||||||
@import UIKit;
|
|
||||||
|
|
||||||
@implementation HTMLHelper
|
|
||||||
|
|
||||||
+ (NSAttributedString*)attributedStringFromHTML:(NSString *)htmlString {
|
|
||||||
NSData *data = [htmlString dataUsingEncoding:NSUTF8StringEncoding allowLossyConversion:YES];
|
|
||||||
NSDictionary *options = @{NSDocumentTypeDocumentAttribute: NSHTMLTextDocumentType,
|
|
||||||
NSCharacterEncodingDocumentAttribute: @(NSUTF8StringEncoding)};
|
|
||||||
return [[NSAttributedString alloc] initWithData:data
|
|
||||||
options:options
|
|
||||||
documentAttributes:nil
|
|
||||||
error:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,51 +0,0 @@
|
|||||||
//
|
|
||||||
// JSONHelper.h
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/15/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
@interface JSONHelper : NSObject
|
|
||||||
|
|
||||||
+(NSString*)readStringFromDictionary:(NSDictionary*)dictionary forKey:(NSString*)key;
|
|
||||||
+(NSString*)readStringFromDictionary:(NSDictionary*)dictionary forKey:(NSString*)key withDefaultValue:(NSString* _Nullable)defaultValue;
|
|
||||||
+(NSNumber*)readNumberFromDictionary:(NSDictionary*)dictionary forKey:(NSString*)key;
|
|
||||||
+(NSNumber*)readNumberFromDictionary:(NSDictionary*)dictionary forKey:(NSString*)key withDefaultValue:(NSNumber* _Nullable)defaultValue;
|
|
||||||
+(int)readIntFromDictionary:(NSDictionary*)dictionary forKey:(NSString*)key;
|
|
||||||
+(int)readIntFromDictionary:(NSDictionary*)dictionary forKey:(NSString*)key withDefaultValue:(int)defaultValue;
|
|
||||||
+(BOOL)readBoolFromDictionary:(NSDictionary*)dictionary forKey:(NSString*)key;
|
|
||||||
+(BOOL)readBoolFromDictionary:(NSDictionary*)dictionary forKey:(NSString*)key withDefaultValue:(BOOL)defaultValue;
|
|
||||||
+(NSDictionary*)readDictionaryFromDictionary:(NSDictionary*)dictionary forKey:(NSString*)key;
|
|
||||||
+(NSDictionary*)readDictionaryFromDictionary:(NSDictionary*)dictionary forKey:(NSString*)key withDefaultValue:(NSDictionary* _Nullable)defaultValue;
|
|
||||||
+(NSArray*)readArrayFromDictionary:(NSDictionary*)dictionary forKey:(NSString*)key;
|
|
||||||
+(NSArray*)readArrayFromDictionary:(NSDictionary*)dictionary forKey:(NSString*)key withDefaultValue:(NSArray* _Nullable)defaultValue;
|
|
||||||
|
|
||||||
+(NSString*)readStringFromArray:(NSArray*)array forIndex:(NSUInteger)index;
|
|
||||||
+(NSString*)readStringFromArray:(NSArray*)array forIndex:(NSUInteger)index withDefaultValue:(NSString* _Nullable)defaultValue;
|
|
||||||
+(NSNumber*)readNumberFromArray:(NSArray*)array forIndex:(NSUInteger)index;
|
|
||||||
+(NSNumber*)readNumberFromArray:(NSArray*)array forIndex:(NSUInteger)index withDefaultValue:(NSNumber* _Nullable)defaultValue;
|
|
||||||
+(int)readIntFromArray:(NSArray*)array forIndex:(NSUInteger)index;
|
|
||||||
+(int)readIntFromArray:(NSArray*)array forIndex:(NSUInteger)index withDefaultValue:(int)defaultValue;
|
|
||||||
+(BOOL)readBoolFromArray:(NSArray*)array forIndex:(NSUInteger)index;
|
|
||||||
+(BOOL)readBoolFromArray:(NSArray*)array forIndex:(NSUInteger)index withDefaultValue:(BOOL)defaultValue;
|
|
||||||
+(NSDictionary*)readDictionaryFromArray:(NSArray*)array forIndex:(NSUInteger)index;
|
|
||||||
+(NSDictionary*)readDictionaryFromArray:(NSArray*)array forIndex:(NSUInteger)index withDefaultValue:(NSDictionary* _Nullable)defaultValue;
|
|
||||||
+(NSArray*)readArrayFromArray:(NSArray*)array forIndex:(NSUInteger)index;
|
|
||||||
+(NSArray*)readArrayFromArray:(NSArray*)array forIndex:(NSUInteger)index withDefaultValue:(NSArray* _Nullable)defaultValue;
|
|
||||||
|
|
||||||
+(id)parseJSONString:(NSString*)jsonString;
|
|
||||||
+(NSDictionary*)parseJSONStringAsDictionary:(NSString*)jsonString;
|
|
||||||
+(NSArray*)parseJSONStringAsArray:(NSString*)jsonString;
|
|
||||||
|
|
||||||
+(id)parseJSONData:(NSData*)jsonData;
|
|
||||||
+(NSDictionary*)parseJSONDataAsDictionary:(NSData*)jsonData;
|
|
||||||
+(NSArray*)parseJSONDataAsArray:(NSData*)jsonData;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
||||||
@@ -1,227 +0,0 @@
|
|||||||
//
|
|
||||||
// JSONHelper.m
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/15/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "JSONHelper.h"
|
|
||||||
|
|
||||||
@implementation JSONHelper
|
|
||||||
|
|
||||||
NSString* coerceObjectToString(NSObject *object, NSString *defaultValue) {
|
|
||||||
if ([object isKindOfClass:[NSString class]]) {
|
|
||||||
return (NSString*)object;
|
|
||||||
}
|
|
||||||
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSNumber* coerceObjectToNumber(NSObject *object, NSNumber *defaultValue) {
|
|
||||||
if ([object isKindOfClass:[NSNumber class]]) {
|
|
||||||
return (NSNumber*)object;
|
|
||||||
}
|
|
||||||
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
int coerceObjectToInt(NSObject *object, int defaultValue) {
|
|
||||||
if ([object isKindOfClass:[NSNumber class]]) {
|
|
||||||
return [(NSNumber*)object intValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
if ([object isKindOfClass:[NSString class]]) {
|
|
||||||
NSScanner *scanner;
|
|
||||||
int temp;
|
|
||||||
scanner = [NSScanner scannerWithString:(NSString*)object];
|
|
||||||
if ([scanner scanInt:&temp]) {
|
|
||||||
return temp;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
BOOL coerceObjectToBool(NSObject *object, BOOL defaultValue) {
|
|
||||||
if ([object isKindOfClass:[NSNumber class]]) {
|
|
||||||
return [(NSNumber*)object boolValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
+(NSString*)readStringFromDictionary:(NSDictionary*)dictionary forKey:(NSString*)key {
|
|
||||||
return [JSONHelper readStringFromDictionary:dictionary forKey:key withDefaultValue:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
+(NSString*)readStringFromDictionary:(NSDictionary*)dictionary forKey:(NSString*)key withDefaultValue:(NSString* _Nullable)defaultValue {
|
|
||||||
NSObject *object = [dictionary objectForKey:key];
|
|
||||||
return coerceObjectToString(object, defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
+(NSNumber*)readNumberFromDictionary:(NSDictionary*)dictionary forKey:(NSString*)key {
|
|
||||||
return [JSONHelper readNumberFromDictionary:dictionary forKey:key withDefaultValue:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
+(NSNumber*)readNumberFromDictionary:(NSDictionary*)dictionary forKey:(NSString*)key withDefaultValue:(NSNumber* _Nullable)defaultValue {
|
|
||||||
NSObject *object = [dictionary objectForKey:key];
|
|
||||||
return coerceObjectToNumber(object, defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
+(int)readIntFromDictionary:(NSDictionary*)dictionary forKey:(NSString*)key {
|
|
||||||
return [JSONHelper readIntFromDictionary:dictionary forKey:key withDefaultValue:0];
|
|
||||||
}
|
|
||||||
|
|
||||||
+(int)readIntFromDictionary:(NSDictionary*)dictionary forKey:(NSString*)key withDefaultValue:(int)defaultValue {
|
|
||||||
NSObject *object = [dictionary objectForKey:key];
|
|
||||||
return coerceObjectToInt(object, defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
+(BOOL)readBoolFromDictionary:(NSDictionary*)dictionary forKey:(NSString*)key {
|
|
||||||
return [JSONHelper readBoolFromDictionary:dictionary forKey:key withDefaultValue:NO];
|
|
||||||
}
|
|
||||||
|
|
||||||
+(BOOL)readBoolFromDictionary:(NSDictionary*)dictionary forKey:(NSString*)key withDefaultValue:(BOOL)defaultValue {
|
|
||||||
NSObject *object = [dictionary objectForKey:key];
|
|
||||||
return coerceObjectToBool(object, defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
+(NSDictionary*)readDictionaryFromDictionary:(NSDictionary*)dictionary forKey:(NSString*)key {
|
|
||||||
return [JSONHelper readDictionaryFromDictionary:dictionary forKey:key withDefaultValue:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
+(NSDictionary*)readDictionaryFromDictionary:(NSDictionary*)dictionary forKey:(NSString*)key withDefaultValue:(NSDictionary* _Nullable)defaultValue {
|
|
||||||
NSObject *object = [dictionary objectForKey:key];
|
|
||||||
if ([object isKindOfClass:[NSDictionary class]]) {
|
|
||||||
return (NSDictionary*)object;
|
|
||||||
}
|
|
||||||
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
+(NSArray*)readArrayFromDictionary:(NSDictionary*)dictionary forKey:(NSString*)key {
|
|
||||||
return [JSONHelper readArrayFromDictionary:dictionary forKey:key withDefaultValue:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
+(NSArray*)readArrayFromDictionary:(NSDictionary*)dictionary forKey:(NSString*)key withDefaultValue:(NSArray* _Nullable)defaultValue {
|
|
||||||
NSObject *object = [dictionary objectForKey:key];
|
|
||||||
if ([object isKindOfClass:[NSArray class]]) {
|
|
||||||
return (NSArray*)object;
|
|
||||||
}
|
|
||||||
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
+(NSString*)readStringFromArray:(NSArray*)array forIndex:(NSUInteger)index{
|
|
||||||
return [JSONHelper readStringFromArray:array forIndex:index withDefaultValue:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
+(NSString*)readStringFromArray:(NSArray*)array forIndex:(NSUInteger)index withDefaultValue:(NSString* _Nullable)defaultValue {
|
|
||||||
NSObject *object = [array objectAtIndex:index];
|
|
||||||
return coerceObjectToString(object, defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
+(NSNumber*)readNumberFromArray:(NSArray*)array forIndex:(NSUInteger)index {
|
|
||||||
return [JSONHelper readNumberFromArray:array forIndex:index withDefaultValue:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
+(NSNumber*)readNumberFromArray:(NSArray*)array forIndex:(NSUInteger)index withDefaultValue:(NSNumber* _Nullable)defaultValue {
|
|
||||||
NSObject *object = [array objectAtIndex:index];
|
|
||||||
return coerceObjectToNumber(object, defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
+(int)readIntFromArray:(NSArray*)array forIndex:(NSUInteger)index {
|
|
||||||
return [JSONHelper readIntFromArray:array forIndex:index withDefaultValue:0];
|
|
||||||
}
|
|
||||||
|
|
||||||
+(int)readIntFromArray:(NSArray*)array forIndex:(NSUInteger)index withDefaultValue:(int)defaultValue {
|
|
||||||
NSObject *object = [array objectAtIndex:index];
|
|
||||||
return coerceObjectToInt(object, defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
+(BOOL)readBoolFromArray:(NSArray*)array forIndex:(NSUInteger)index {
|
|
||||||
return [JSONHelper readBoolFromArray:array forIndex:index withDefaultValue:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
+(BOOL)readBoolFromArray:(NSArray*)array forIndex:(NSUInteger)index withDefaultValue:(BOOL)defaultValue {
|
|
||||||
NSObject *object = [array objectAtIndex:index];
|
|
||||||
return coerceObjectToBool(object, defaultValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
+(NSDictionary*)readDictionaryFromArray:(NSArray*)array forIndex:(NSUInteger)index {
|
|
||||||
return [JSONHelper readDictionaryFromArray:array forIndex:index withDefaultValue:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
+(NSDictionary*)readDictionaryFromArray:(NSArray*)array forIndex:(NSUInteger)index withDefaultValue:(NSDictionary* _Nullable)defaultValue {
|
|
||||||
NSObject *object = [array objectAtIndex:index];
|
|
||||||
if ([object isKindOfClass:[NSDictionary class]]) {
|
|
||||||
return (NSDictionary*)object;
|
|
||||||
}
|
|
||||||
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
+(NSArray*)readArrayFromArray:(NSArray*)array forIndex:(NSUInteger)index {
|
|
||||||
return [JSONHelper readArrayFromArray:array forIndex:index withDefaultValue:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
+(NSArray*)readArrayFromArray:(NSArray*)array forIndex:(NSUInteger)index withDefaultValue:(NSArray* _Nullable)defaultValue {
|
|
||||||
NSObject *object = [array objectAtIndex:index];
|
|
||||||
if ([object isKindOfClass:[NSArray class]]) {
|
|
||||||
return (NSArray*)object;
|
|
||||||
}
|
|
||||||
|
|
||||||
return defaultValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
+(id)parseJSONString:(NSString*)jsonString {
|
|
||||||
NSData* jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
|
|
||||||
NSArray *jsonRoot = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:nil];
|
|
||||||
return jsonRoot;
|
|
||||||
}
|
|
||||||
|
|
||||||
+(NSDictionary*)parseJSONStringAsDictionary:(NSString*)jsonString {
|
|
||||||
NSData* jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
|
|
||||||
NSDictionary *jsonRoot = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:nil];
|
|
||||||
if (![jsonRoot isKindOfClass:[NSDictionary class]]) {
|
|
||||||
return nil;
|
|
||||||
} else {
|
|
||||||
return jsonRoot;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
+(NSArray*)parseJSONStringAsArray:(NSString*)jsonString {
|
|
||||||
NSData* jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
|
|
||||||
NSArray *jsonRoot = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:nil];
|
|
||||||
if (![jsonRoot isKindOfClass:[NSArray class]]) {
|
|
||||||
return nil;
|
|
||||||
} else {
|
|
||||||
return jsonRoot;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
+(id)parseJSONData:(NSData*)jsonData {
|
|
||||||
NSArray *jsonRoot = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:nil];
|
|
||||||
return jsonRoot;
|
|
||||||
}
|
|
||||||
|
|
||||||
+(NSDictionary*)parseJSONDataAsDictionary:(NSData*)jsonData {
|
|
||||||
NSDictionary *jsonRoot = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:nil];
|
|
||||||
if (![jsonRoot isKindOfClass:[NSDictionary class]]) {
|
|
||||||
return nil;
|
|
||||||
} else {
|
|
||||||
return jsonRoot;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
+(NSArray*)parseJSONDataAsArray:(NSData*)jsonData {
|
|
||||||
NSArray *jsonRoot = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:nil];
|
|
||||||
if (![jsonRoot isKindOfClass:[NSArray class]]) {
|
|
||||||
return nil;
|
|
||||||
} else {
|
|
||||||
return jsonRoot;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
//
|
|
||||||
// StringHelper.h
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/5/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
@interface StringHelper : NSObject
|
|
||||||
|
|
||||||
+(BOOL)isStringNilOrEmpty:(NSString*)theString;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
//
|
|
||||||
// StringHelper.m
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/5/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "StringHelper.h"
|
|
||||||
|
|
||||||
@implementation StringHelper
|
|
||||||
|
|
||||||
+(BOOL)isStringNilOrEmpty:(NSString*)theString {
|
|
||||||
if (nil == theString) {
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (theString.length < 1) {
|
|
||||||
return YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -25,40 +25,16 @@
|
|||||||
<key>UIApplicationSceneManifest</key>
|
<key>UIApplicationSceneManifest</key>
|
||||||
<dict>
|
<dict>
|
||||||
<key>UIApplicationSupportsMultipleScenes</key>
|
<key>UIApplicationSupportsMultipleScenes</key>
|
||||||
<false/>
|
<true/>
|
||||||
<key>UISceneConfigurations</key>
|
|
||||||
<dict>
|
|
||||||
<key>UIWindowSceneSessionRoleApplication</key>
|
|
||||||
<array>
|
|
||||||
<dict>
|
|
||||||
<key>UISceneConfigurationName</key>
|
|
||||||
<string>Default Configuration</string>
|
|
||||||
<key>UISceneDelegateClassName</key>
|
|
||||||
<string>SceneDelegate</string>
|
|
||||||
<key>UISceneStoryboardFile</key>
|
|
||||||
<string>Main</string>
|
|
||||||
</dict>
|
|
||||||
</array>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
</dict>
|
||||||
<key>UILaunchStoryboardName</key>
|
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||||
<string>LaunchScreen</string>
|
<true/>
|
||||||
<key>UIMainStoryboardFile</key>
|
<key>UILaunchScreen</key>
|
||||||
<string>Main</string>
|
<dict/>
|
||||||
<key>UIRequiredDeviceCapabilities</key>
|
<key>UIRequiredDeviceCapabilities</key>
|
||||||
<array>
|
<array>
|
||||||
<string>armv7</string>
|
<string>armv7</string>
|
||||||
</array>
|
</array>
|
||||||
<key>UIStatusBarTintParameters</key>
|
|
||||||
<dict>
|
|
||||||
<key>UINavigationBar</key>
|
|
||||||
<dict>
|
|
||||||
<key>Style</key>
|
|
||||||
<string>UIBarStyleDefault</string>
|
|
||||||
<key>Translucent</key>
|
|
||||||
<false/>
|
|
||||||
</dict>
|
|
||||||
</dict>
|
|
||||||
<key>UISupportedInterfaceOrientations</key>
|
<key>UISupportedInterfaceOrientations</key>
|
||||||
<array>
|
<array>
|
||||||
<string>UIInterfaceOrientationPortrait</string>
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
|
|||||||
@@ -1,22 +0,0 @@
|
|||||||
//
|
|
||||||
// Ability.h
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/4/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
@interface Ability : NSObject
|
|
||||||
|
|
||||||
@property NSString* name;
|
|
||||||
@property NSString* abilityDescription;
|
|
||||||
|
|
||||||
-(id)initWithName: (NSString*)name andDescription: (NSString*)description;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
//
|
|
||||||
// Ability.m
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/4/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "Ability.h"
|
|
||||||
|
|
||||||
@implementation Ability
|
|
||||||
|
|
||||||
-(id)init {
|
|
||||||
self = [super init];
|
|
||||||
|
|
||||||
self.name = @"";
|
|
||||||
self.abilityDescription = @"";
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(id)initWithName: (NSString*)name andDescription: (NSString*)description {
|
|
||||||
self = [super init];
|
|
||||||
|
|
||||||
self.name = name;
|
|
||||||
self.abilityDescription = description;
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
//
|
|
||||||
// Action.h
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/4/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
@interface Action : NSObject
|
|
||||||
|
|
||||||
@property NSString* name;
|
|
||||||
@property NSString* actionDescription;
|
|
||||||
|
|
||||||
-(id)initWithName: (NSString*)name andDescription: (NSString*)description;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
//
|
|
||||||
// Action.m
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/4/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "Action.h"
|
|
||||||
|
|
||||||
@implementation Action
|
|
||||||
|
|
||||||
-(id)init {
|
|
||||||
self = [super init];
|
|
||||||
|
|
||||||
self.name = @"";
|
|
||||||
self.actionDescription = @"";
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(id)initWithName: (NSString*)name andDescription: (NSString*)description {
|
|
||||||
self = [super init];
|
|
||||||
|
|
||||||
self.name = name;
|
|
||||||
self.actionDescription = description;
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
27
MonsterCards/Models/AdvantageType.swift
Normal file
27
MonsterCards/Models/AdvantageType.swift
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
//
|
||||||
|
// AdvantageType.swift
|
||||||
|
// MonsterCards
|
||||||
|
//
|
||||||
|
// Created by Tom Hicks on 1/17/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
enum AdvantageType: String, CaseIterable, Identifiable {
|
||||||
|
case none = "none"
|
||||||
|
case advantage = "advantage"
|
||||||
|
case disadvantage = "disadvantage"
|
||||||
|
|
||||||
|
var id: AdvantageType { self }
|
||||||
|
|
||||||
|
var displayName: String {
|
||||||
|
switch self {
|
||||||
|
case .none:
|
||||||
|
return "None"
|
||||||
|
case .advantage:
|
||||||
|
return "Advantage"
|
||||||
|
case .disadvantage:
|
||||||
|
return "Disadvantage"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
//
|
|
||||||
// DamageType.h
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/4/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
@interface DamageType : NSObject
|
|
||||||
|
|
||||||
@property NSString* name;
|
|
||||||
@property NSString* note;
|
|
||||||
@property NSString* type;
|
|
||||||
|
|
||||||
-(id)initWithName: (NSString*)name note: (NSString*)note andType: (NSString*)type;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
//
|
|
||||||
// DamageType.m
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/4/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "DamageType.h"
|
|
||||||
|
|
||||||
@implementation DamageType
|
|
||||||
|
|
||||||
-(id)init {
|
|
||||||
self = [super init];
|
|
||||||
|
|
||||||
self.name = @"";
|
|
||||||
self.note = @"";
|
|
||||||
self.type = @"";
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(id)initWithName: (NSString*)name note: (NSString*)note andType: (NSString*)type{
|
|
||||||
self = [super init];
|
|
||||||
|
|
||||||
self.name = name;
|
|
||||||
self.note = note;
|
|
||||||
self.type = type;
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,23 +0,0 @@
|
|||||||
//
|
|
||||||
// Language.h
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/4/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
@interface Language : NSObject
|
|
||||||
|
|
||||||
@property NSString* name;
|
|
||||||
@property BOOL speaks;
|
|
||||||
|
|
||||||
-(id)initWithName:(NSString*)name
|
|
||||||
andSpeaks:(BOOL)canSpeak;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
//
|
|
||||||
// Language.m
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/4/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "Language.h"
|
|
||||||
|
|
||||||
@implementation Language
|
|
||||||
|
|
||||||
-(id)init {
|
|
||||||
self = [super init];
|
|
||||||
|
|
||||||
self.name = @"";
|
|
||||||
self.speaks = YES;
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(id)initWithName:(NSString*)name
|
|
||||||
andSpeaks:(BOOL)canSpeak {
|
|
||||||
self = [super init];
|
|
||||||
|
|
||||||
self.name = name;
|
|
||||||
self.speaks = canSpeak;
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
701
MonsterCards/Models/Monster+CoreDataClass.swift
Normal file
701
MonsterCards/Models/Monster+CoreDataClass.swift
Normal file
@@ -0,0 +1,701 @@
|
|||||||
|
//
|
||||||
|
// Monster+CoreDataClass.swift
|
||||||
|
// MonsterCards
|
||||||
|
//
|
||||||
|
// Created by Tom Hicks on 1/15/21.
|
||||||
|
//
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import CoreData
|
||||||
|
|
||||||
|
@objc(Monster)
|
||||||
|
public class Monster: NSManagedObject {
|
||||||
|
convenience init(context: NSManagedObjectContext, name: String, size: String, type: String, subtype: String, alignment: String) {
|
||||||
|
self.init(context:context)
|
||||||
|
self.name = name;
|
||||||
|
self.size = size;
|
||||||
|
self.type = type;
|
||||||
|
self.subtype = subtype;
|
||||||
|
self.alignment = alignment;
|
||||||
|
}
|
||||||
|
|
||||||
|
// hasCustomProficiencyBonus
|
||||||
|
// telepathy int in json but seems like it should be a bool
|
||||||
|
|
||||||
|
let kBaseArmorClassUnarmored = 10;
|
||||||
|
let kBaseArmorClassMageArmor = 13;
|
||||||
|
let kBaseArmorClassPadded = 11;
|
||||||
|
let kBaseArmorClassLeather = 11;
|
||||||
|
let kBaseArmorClassStudded = 12;
|
||||||
|
let kBaseArmorClassHide = 12;
|
||||||
|
let kBaseArmorClassChainShirt = 13;
|
||||||
|
let kBaseArmorClassScaleMail = 14;
|
||||||
|
let kBaseArmorClassBreastplate = 14;
|
||||||
|
let kBaseArmorClassHalfPlate = 15;
|
||||||
|
let kBaseArmorClassRingMail = 14;
|
||||||
|
let kBaseArmorClassChainMail = 16;
|
||||||
|
let kBaseArmorClassSplintMail = 17;
|
||||||
|
let kBaseArmorClassPlate = 18;
|
||||||
|
|
||||||
|
// MARK: Basic Info
|
||||||
|
|
||||||
|
var meta: String {
|
||||||
|
get {
|
||||||
|
// size type (subtype) alignment
|
||||||
|
var parts: [String] = []
|
||||||
|
|
||||||
|
if (!(self.size?.isEmpty ?? false)) {
|
||||||
|
parts.append(self.size!)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(self.type?.isEmpty ?? false)) {
|
||||||
|
parts.append(self.type!)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(self.subtype?.isEmpty ?? false)) {
|
||||||
|
parts.append(String.init(format: "(%@)", arguments: [self.subtype!]))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!(self.alignment?.isEmpty ?? false)) {
|
||||||
|
parts.append(self.alignment!)
|
||||||
|
}
|
||||||
|
|
||||||
|
return parts.joined(separator: " ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var sizeEnum: SizeType {
|
||||||
|
get {
|
||||||
|
return SizeType.init(rawValue: size ?? "") ?? .medium
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
size = newValue.rawValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var hitPoints: String {
|
||||||
|
get {
|
||||||
|
if (hasCustomHP) {
|
||||||
|
return customHP ?? "";
|
||||||
|
} else {
|
||||||
|
let dieSize = Double(Monster.hitDieForSize(sizeEnum))
|
||||||
|
let conMod = Double(constitutionModifier)
|
||||||
|
let level1HP = Double(dieSize + conMod)
|
||||||
|
let extraLevels = Double(hitDice - 1)
|
||||||
|
let levelNHP = (dieSize + 1.0) / 2.0 + conMod
|
||||||
|
let extraLevelsHP = extraLevels * levelNHP
|
||||||
|
let hpTotal = Int(ceil(level1HP + extraLevelsHP))
|
||||||
|
// let hpTotal = Int(max(1, ceil(dieSize + conMod + (hitDice - 1) * ((dieSize + 1) / 2.0 + conMod))))
|
||||||
|
let conBonus = Int(conMod) * Int(hitDice)
|
||||||
|
return String(format: "%d (%dd%d%+d)", hpTotal, hitDice, dieSize, conBonus)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var speed: String {
|
||||||
|
get {
|
||||||
|
if (hasCustomSpeed) {
|
||||||
|
return customSpeed ?? ""
|
||||||
|
} else {
|
||||||
|
var parts: [String] = []
|
||||||
|
|
||||||
|
if (baseSpeed > 0) {
|
||||||
|
parts.append("\(baseSpeed) ft.")
|
||||||
|
}
|
||||||
|
if (burrowSpeed > 0) {
|
||||||
|
parts.append("burrow \(burrowSpeed) ft.")
|
||||||
|
}
|
||||||
|
if (climbSpeed > 0) {
|
||||||
|
parts.append("climb \(climbSpeed) ft.")
|
||||||
|
}
|
||||||
|
if (flySpeed > 0) {
|
||||||
|
parts.append("fly \(flySpeed) ft.\(canHover ? " (hover)": "")")
|
||||||
|
}
|
||||||
|
if (swimSpeed > 0) {
|
||||||
|
parts.append("swim \(swimSpeed) ft.")
|
||||||
|
}
|
||||||
|
|
||||||
|
return parts.joined(separator: ", ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class func hitDieForSize(_ size: SizeType) -> Int {
|
||||||
|
switch size {
|
||||||
|
case .tiny: return 4
|
||||||
|
case .small: return 6
|
||||||
|
case .medium: return 8
|
||||||
|
case .large: return 10
|
||||||
|
case .huge: return 12
|
||||||
|
case .gargantuan: return 20
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Ability Scores
|
||||||
|
class func abilityModifierForScore(_ score: Int) -> Int {
|
||||||
|
return Int(floor(Double((score - 10)) / 2.0))
|
||||||
|
}
|
||||||
|
|
||||||
|
var strengthModifier: Int {
|
||||||
|
get {
|
||||||
|
return Monster.abilityModifierForScore(Int(strengthScore))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var dexterityModifier: Int {
|
||||||
|
get {
|
||||||
|
return Monster.abilityModifierForScore(Int(dexterityScore))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var constitutionModifier: Int {
|
||||||
|
get {
|
||||||
|
return Monster.abilityModifierForScore(Int(constitutionScore))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var intelligenceModifier: Int {
|
||||||
|
get {
|
||||||
|
return Monster.abilityModifierForScore(Int(intelligenceScore))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var wisdomModifier: Int {
|
||||||
|
get {
|
||||||
|
return Monster.abilityModifierForScore(Int(wisdomScore))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var charismaModifier: Int {
|
||||||
|
get {
|
||||||
|
return Monster.abilityModifierForScore(Int(charismaScore))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var strengthDescription: String {
|
||||||
|
get {
|
||||||
|
return String(format: "%d (%+d)", strengthScore, strengthModifier)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var dexterityDescription: String {
|
||||||
|
get {
|
||||||
|
return String(format: "%d (%+d)", dexterityScore, dexterityModifier)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var constitutionDescription: String {
|
||||||
|
get {
|
||||||
|
return String(format: "%d (%+d)", constitutionScore, constitutionModifier)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var intelligenceDescription: String {
|
||||||
|
get {
|
||||||
|
return String(format: "%d (%+d)", intelligenceScore, intelligenceModifier)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var wisdomDescription: String {
|
||||||
|
get {
|
||||||
|
return String(format: "%d (%+d)", wisdomScore, wisdomModifier)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var charismaDescription: String {
|
||||||
|
get {
|
||||||
|
return String(format: "%d (%+d)", charismaScore, charismaModifier)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Armor
|
||||||
|
|
||||||
|
var armorTypeEnum: ArmorType {
|
||||||
|
get {
|
||||||
|
return ArmorType.init(rawValue: armorType ?? "none") ?? .none
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
armorType = newValue.rawValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var armorClassDescription: String {
|
||||||
|
get {
|
||||||
|
let hasShield = shieldBonus != 0
|
||||||
|
var armorClassTotal = 0
|
||||||
|
if (armorTypeEnum == ArmorType.none) {
|
||||||
|
// 10 + dexMod + 2 for shieldBonus "15" or "17 (shield)"
|
||||||
|
armorClassTotal = kBaseArmorClassUnarmored + dexterityModifier + Int(shieldBonus)
|
||||||
|
return "\(armorClassTotal)\(hasShield ? " (shield)" : "")"
|
||||||
|
} else if (armorTypeEnum == .naturalArmor) {
|
||||||
|
// 10 + dexMod + naturalArmorBonus + 2 for shieldBonus "16 (natural armor)" or "18 (natural armor, shield)"
|
||||||
|
armorClassTotal = kBaseArmorClassUnarmored + dexterityModifier + Int(naturalArmorBonus) + Int(shieldBonus)
|
||||||
|
return "\(armorClassTotal) (natural armor\(hasShield ? " (shield)" : ""))"
|
||||||
|
} else if (armorTypeEnum == .mageArmor) {
|
||||||
|
// 10 + dexMod + 2 for shield + 3 for mage armor "15 (18 with mage armor)" or 17 (shield, 20 with mage armor)
|
||||||
|
armorClassTotal = kBaseArmorClassUnarmored + dexterityModifier + Int(shieldBonus)
|
||||||
|
let acWithMageArmor = kBaseArmorClassMageArmor + dexterityModifier + Int(shieldBonus)
|
||||||
|
return String(format: "%d (%@%d with mage armor)", armorClassTotal, (hasShield ? "shield, " : ""), acWithMageArmor)
|
||||||
|
} else if (armorTypeEnum == .padded) {
|
||||||
|
// 11 + dexMod + 2 for shield "18 (padded armor, shield)"
|
||||||
|
armorClassTotal = kBaseArmorClassPadded + dexterityModifier + Int(shieldBonus)
|
||||||
|
return String(format: "%d (padded%@)", armorClassTotal, (hasShield ? "shield, " : ""))
|
||||||
|
} else if (armorTypeEnum == .leather) {
|
||||||
|
// 11 + dexMod + 2 for shield "18 (leather, shield)"
|
||||||
|
armorClassTotal = kBaseArmorClassLeather + dexterityModifier + Int(shieldBonus)
|
||||||
|
return String(format:"%d (leather%@)", armorClassTotal, (hasShield ? "shield, " : ""))
|
||||||
|
} else if (armorTypeEnum == .studdedLeather) {
|
||||||
|
// 12 + dexMod +2 for shield "17 (studded leather)"
|
||||||
|
armorClassTotal = kBaseArmorClassStudded + dexterityModifier + Int(shieldBonus)
|
||||||
|
return String(format: "%d (studded leather%@)", armorClassTotal, (hasShield ? "shield, " : ""))
|
||||||
|
} else if (armorTypeEnum == .hide) {
|
||||||
|
// 12 + Min(2, dexMod) + 2 for shield "12 (hide armor)"
|
||||||
|
armorClassTotal = kBaseArmorClassHide + min(2, dexterityModifier) + Int(shieldBonus)
|
||||||
|
return String(format: "%d (hide%@)", armorClassTotal, (hasShield ? ", shield" : ""))
|
||||||
|
} else if (armorTypeEnum == .chainShirt) {
|
||||||
|
// 13 + Min(2, dexMod) + 2 for shield "12 (chain shirt)"
|
||||||
|
armorClassTotal = kBaseArmorClassChainShirt + min(2, dexterityModifier) + Int(shieldBonus)
|
||||||
|
return String(format: "%d (chain shirt%@)", armorClassTotal, (hasShield ? ", shield" : ""))
|
||||||
|
} else if (armorTypeEnum == .scaleMail) {
|
||||||
|
// 14 + Min(2, dexMod) + 2 for shield "14 (scale mail)"
|
||||||
|
armorClassTotal = kBaseArmorClassScaleMail + min(2, dexterityModifier) + Int(shieldBonus)
|
||||||
|
return String(format: "%d (scale mail%@)", armorClassTotal, (hasShield ? ", shield" : ""))
|
||||||
|
} else if (armorTypeEnum == .breastplate) {
|
||||||
|
// 14 + Min(2, dexMod) + 2 for shield "16 (breastplate)"
|
||||||
|
armorClassTotal = kBaseArmorClassBreastplate + min(2, dexterityModifier) + Int(shieldBonus)
|
||||||
|
return String(format: "%d (breastplate%@)", armorClassTotal, (hasShield ? ", shield" : ""))
|
||||||
|
} else if (armorTypeEnum == .halfPlate) {
|
||||||
|
// 15 + Min(2, dexMod) + 2 for shield "17 (half plate)"
|
||||||
|
armorClassTotal = kBaseArmorClassHalfPlate + min(2, dexterityModifier) + Int(shieldBonus)
|
||||||
|
return String(format: "%d (half plate%@)", armorClassTotal, (hasShield ? ", shield" : ""))
|
||||||
|
} else if (armorTypeEnum == .ringMail) {
|
||||||
|
// 14 + 2 for shield "14 (ring mail)
|
||||||
|
armorClassTotal = kBaseArmorClassRingMail + Int(shieldBonus)
|
||||||
|
return String(format: "%d (ring mail%@)", armorClassTotal, (hasShield ? ", shield" : ""))
|
||||||
|
} else if (armorTypeEnum == .chainMail) {
|
||||||
|
// 16 + 2 for shield "16 (chain mail)"
|
||||||
|
armorClassTotal = kBaseArmorClassChainMail + Int(shieldBonus)
|
||||||
|
return String(format: "%d (chain mail%@)", armorClassTotal, (hasShield ? ", shield" : ""))
|
||||||
|
} else if (armorTypeEnum == .splintMail) {
|
||||||
|
// 17 + 2 for shield "17 (splint)"
|
||||||
|
armorClassTotal = kBaseArmorClassSplintMail + Int(shieldBonus)
|
||||||
|
return String(format: "%d (splint%@)", armorClassTotal, (hasShield ? ", shield" : ""))
|
||||||
|
} else if (armorTypeEnum == .plateMail) {
|
||||||
|
// 18 + 2 for shield "18 (plate)"
|
||||||
|
armorClassTotal = kBaseArmorClassPlate + Int(shieldBonus)
|
||||||
|
return String(format: "%d (plate%@)", armorClassTotal, (hasShield ? ", shield" : ""))
|
||||||
|
} else if (armorTypeEnum == .other) {
|
||||||
|
// pure string value shield check does nothing just copies the string from otherArmorDesc
|
||||||
|
return otherArmorDescription ?? "";
|
||||||
|
} else {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// MARK: Challenge Rating / Proficiency Bonus
|
||||||
|
|
||||||
|
var challengeRatingEnum: ChallengeRating {
|
||||||
|
get {
|
||||||
|
return ChallengeRating.init(rawValue: challengeRating ?? "1") ?? .one
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
challengeRating = newValue.rawValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var proficiencyBonus: Int {
|
||||||
|
return 5
|
||||||
|
// switch challengeRatingEnum {
|
||||||
|
// case .custom:
|
||||||
|
// return Int(customProficiencyBonus)
|
||||||
|
// case .zero:
|
||||||
|
// fallthrough
|
||||||
|
// case .oneEighth:
|
||||||
|
// fallthrough
|
||||||
|
// case .oneQuarter:
|
||||||
|
// fallthrough
|
||||||
|
// case .oneHalf:
|
||||||
|
// fallthrough
|
||||||
|
// case .one:
|
||||||
|
// fallthrough
|
||||||
|
// case .two:
|
||||||
|
// fallthrough
|
||||||
|
// case .three:
|
||||||
|
// fallthrough
|
||||||
|
// case .four:
|
||||||
|
// return 2
|
||||||
|
// case .five:
|
||||||
|
// fallthrough
|
||||||
|
// case .six:
|
||||||
|
// fallthrough
|
||||||
|
// case .seven:
|
||||||
|
// fallthrough
|
||||||
|
// case .eight:
|
||||||
|
// return 3
|
||||||
|
// case .nine:
|
||||||
|
// fallthrough
|
||||||
|
// case .ten:
|
||||||
|
// fallthrough
|
||||||
|
// case .eleven:
|
||||||
|
// fallthrough
|
||||||
|
// case .twelve:
|
||||||
|
// return 4
|
||||||
|
// case .thirteen:
|
||||||
|
// fallthrough
|
||||||
|
// case .fourteen:
|
||||||
|
// fallthrough
|
||||||
|
// case .fifteen:
|
||||||
|
// fallthrough
|
||||||
|
// case .sixteen:
|
||||||
|
// return 5
|
||||||
|
// case .seventeen:
|
||||||
|
// fallthrough
|
||||||
|
// case .eighteen:
|
||||||
|
// fallthrough
|
||||||
|
// case .nineteen:
|
||||||
|
// fallthrough
|
||||||
|
// case .twenty:
|
||||||
|
// return 6
|
||||||
|
// case .twentyOne:
|
||||||
|
// fallthrough
|
||||||
|
// case .twentyTwo:
|
||||||
|
// fallthrough
|
||||||
|
// case .twentyThree:
|
||||||
|
// fallthrough
|
||||||
|
// case .twentyFour:
|
||||||
|
// return 7
|
||||||
|
// case .twentyFive:
|
||||||
|
// fallthrough
|
||||||
|
// case .twentySix:
|
||||||
|
// fallthrough
|
||||||
|
// case .twentySeven:
|
||||||
|
// fallthrough
|
||||||
|
// case .twentyEight:
|
||||||
|
// return 8
|
||||||
|
// case .twentyNine:
|
||||||
|
// fallthrough
|
||||||
|
// case .thirty:
|
||||||
|
// return 9
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
func proficiencyBonusForType(_ profType: ProficiencyType) -> Int {
|
||||||
|
switch profType {
|
||||||
|
case .none:
|
||||||
|
return 0
|
||||||
|
case .proficient:
|
||||||
|
return proficiencyBonus
|
||||||
|
case .expertise:
|
||||||
|
return proficiencyBonus * 2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: Saving Throws
|
||||||
|
|
||||||
|
var savingThrowsDescription: String {
|
||||||
|
get {
|
||||||
|
// TODO: port from objective-c
|
||||||
|
var parts: [String] = []
|
||||||
|
var name: String
|
||||||
|
var advantage: String
|
||||||
|
var bonus: Int
|
||||||
|
|
||||||
|
if (strengthSavingThrowAdvantageEnum != .none || strengthSavingThrowProficiencyEnum != .none) {
|
||||||
|
name = "Strength"
|
||||||
|
bonus = strengthModifier + proficiencyBonusForType(strengthSavingThrowProficiencyEnum)
|
||||||
|
advantage = Monster.advantageLabelStringForType(strengthSavingThrowAdvantageEnum)
|
||||||
|
if (!advantage.isEmpty) {
|
||||||
|
advantage = " " + advantage
|
||||||
|
}
|
||||||
|
parts.append(String(format: "%@ %+d%@", name, bonus, advantage))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (dexteritySavingThrowAdvantageEnum != .none || dexteritySavingThrowProficiencyEnum != .none) {
|
||||||
|
name = "Dexterity"
|
||||||
|
bonus = dexterityModifier + proficiencyBonusForType(dexteritySavingThrowProficiencyEnum)
|
||||||
|
advantage = Monster.advantageLabelStringForType(dexteritySavingThrowAdvantageEnum)
|
||||||
|
if (!advantage.isEmpty) {
|
||||||
|
advantage = " " + advantage
|
||||||
|
}
|
||||||
|
parts.append(String(format: "%@ %+d%@", name, bonus, advantage))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (constitutionSavingThrowAdvantageEnum != .none || constitutionSavingThrowProficiencyEnum != .none) {
|
||||||
|
name = "Constitution"
|
||||||
|
bonus = constitutionModifier + proficiencyBonusForType(constitutionSavingThrowProficiencyEnum)
|
||||||
|
advantage = Monster.advantageLabelStringForType(constitutionSavingThrowAdvantageEnum)
|
||||||
|
if (!advantage.isEmpty) {
|
||||||
|
advantage = " " + advantage
|
||||||
|
}
|
||||||
|
parts.append(String(format: "%@ %+d%@", name, bonus, advantage))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (intelligenceSavingThrowAdvantageEnum != .none || intelligenceSavingThrowProficiencyEnum != .none) {
|
||||||
|
name = "Intelligence"
|
||||||
|
bonus = intelligenceModifier + proficiencyBonusForType(intelligenceSavingThrowProficiencyEnum)
|
||||||
|
advantage = Monster.advantageLabelStringForType(intelligenceSavingThrowAdvantageEnum)
|
||||||
|
if (!advantage.isEmpty) {
|
||||||
|
advantage = " " + advantage
|
||||||
|
}
|
||||||
|
parts.append(String(format: "%@ %+d%@", name, bonus, advantage))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (wisdomSavingThrowAdvantageEnum != .none || wisdomSavingThrowProficiencyEnum != .none) {
|
||||||
|
name = "Wisdom"
|
||||||
|
bonus = wisdomModifier + proficiencyBonusForType(wisdomSavingThrowProficiencyEnum)
|
||||||
|
advantage = Monster.advantageLabelStringForType(wisdomSavingThrowAdvantageEnum)
|
||||||
|
if (!advantage.isEmpty) {
|
||||||
|
advantage = " " + advantage
|
||||||
|
}
|
||||||
|
parts.append(String(format: "%@ %+d%@", name, bonus, advantage))
|
||||||
|
}
|
||||||
|
|
||||||
|
if (charismaSavingThrowAdvantageEnum != .none || charismaSavingThrowProficiencyEnum != .none) {
|
||||||
|
name = "Charisma"
|
||||||
|
bonus = charismaModifier + proficiencyBonusForType(charismaSavingThrowProficiencyEnum)
|
||||||
|
advantage = Monster.advantageLabelStringForType(charismaSavingThrowAdvantageEnum)
|
||||||
|
if (!advantage.isEmpty) {
|
||||||
|
advantage = " " + advantage
|
||||||
|
}
|
||||||
|
parts.append(String(format: "%@ %+d%@", name, bonus, advantage))
|
||||||
|
}
|
||||||
|
|
||||||
|
return parts.joined(separator: ", ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var strengthSavingThrowProficiencyEnum: ProficiencyType {
|
||||||
|
get {
|
||||||
|
return ProficiencyType.init(rawValue: strengthSavingThrowProficiency ?? "") ?? .none
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
strengthSavingThrowProficiency = newValue.rawValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var strengthSavingThrowAdvantageEnum: AdvantageType {
|
||||||
|
get {
|
||||||
|
return AdvantageType.init(rawValue: strengthSavingThrowAdvantage ?? "") ?? .none
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
strengthSavingThrowAdvantage = newValue.rawValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var dexteritySavingThrowProficiencyEnum: ProficiencyType {
|
||||||
|
get {
|
||||||
|
return ProficiencyType.init(rawValue: dexteritySavingThrowProficiency ?? "") ?? .none
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
dexteritySavingThrowProficiency = newValue.rawValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var dexteritySavingThrowAdvantageEnum: AdvantageType {
|
||||||
|
get {
|
||||||
|
return AdvantageType.init(rawValue: dexteritySavingThrowAdvantage ?? "") ?? .none
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
dexteritySavingThrowAdvantage = newValue.rawValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var constitutionSavingThrowProficiencyEnum: ProficiencyType {
|
||||||
|
get {
|
||||||
|
return ProficiencyType.init(rawValue: constitutionSavingThrowProficiency ?? "") ?? .none
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
constitutionSavingThrowProficiency = newValue.rawValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var constitutionSavingThrowAdvantageEnum: AdvantageType {
|
||||||
|
get {
|
||||||
|
return AdvantageType.init(rawValue: constitutionSavingThrowAdvantage ?? "") ?? .none
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
constitutionSavingThrowAdvantage = newValue.rawValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var intelligenceSavingThrowProficiencyEnum: ProficiencyType {
|
||||||
|
get {
|
||||||
|
return ProficiencyType.init(rawValue: intelligenceSavingThrowProficiency ?? "") ?? .none
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
intelligenceSavingThrowProficiency = newValue.rawValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var intelligenceSavingThrowAdvantageEnum: AdvantageType {
|
||||||
|
get {
|
||||||
|
return AdvantageType.init(rawValue: intelligenceSavingThrowAdvantage ?? "") ?? .none
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
intelligenceSavingThrowAdvantage = newValue.rawValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var wisdomSavingThrowProficiencyEnum: ProficiencyType {
|
||||||
|
get {
|
||||||
|
return ProficiencyType.init(rawValue: wisdomSavingThrowProficiency ?? "") ?? .none
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
wisdomSavingThrowProficiency = newValue.rawValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var wisdomSavingThrowAdvantageEnum: AdvantageType {
|
||||||
|
get {
|
||||||
|
return AdvantageType.init(rawValue: wisdomSavingThrowAdvantage ?? "") ?? .none
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
wisdomSavingThrowAdvantage = newValue.rawValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var charismaSavingThrowProficiencyEnum: ProficiencyType {
|
||||||
|
get {
|
||||||
|
return ProficiencyType.init(rawValue: charismaSavingThrowProficiency ?? "") ?? .none
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
charismaSavingThrowProficiency = newValue.rawValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var charismaSavingThrowAdvantageEnum: AdvantageType {
|
||||||
|
get {
|
||||||
|
return AdvantageType.init(rawValue: charismaSavingThrowAdvantage ?? "") ?? .none
|
||||||
|
}
|
||||||
|
set {
|
||||||
|
charismaSavingThrowAdvantage = newValue.rawValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// MARK: Misc Helpers
|
||||||
|
|
||||||
|
class func advantageLabelStringForType(_ advType: AdvantageType) -> String {
|
||||||
|
switch advType {
|
||||||
|
case .none:
|
||||||
|
return ""
|
||||||
|
case .advantage:
|
||||||
|
return "(A)"
|
||||||
|
case .disadvantage:
|
||||||
|
return "(D)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// MARK: End
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ArmorType: String, CaseIterable, Identifiable {
|
||||||
|
case none = "none"
|
||||||
|
case naturalArmor = "natural armor"
|
||||||
|
case mageArmor = "mage armor"
|
||||||
|
case padded = "padded"
|
||||||
|
case leather = "leather"
|
||||||
|
case studdedLeather = "studded"
|
||||||
|
case hide = "hide"
|
||||||
|
case chainShirt = "chain shirt"
|
||||||
|
case scaleMail = "scale mail"
|
||||||
|
case breastplate = "breastplate"
|
||||||
|
case halfPlate = "half plate"
|
||||||
|
case ringMail = "ring mail"
|
||||||
|
case chainMail = "chain mail"
|
||||||
|
case splintMail = "splint"
|
||||||
|
case plateMail = "plate"
|
||||||
|
case other = "other"
|
||||||
|
|
||||||
|
var id: ArmorType { self }
|
||||||
|
|
||||||
|
var displayName: String {
|
||||||
|
switch self {
|
||||||
|
case .none: return "None"
|
||||||
|
case .naturalArmor: return "Natural Armor"
|
||||||
|
case .mageArmor: return "Mage Armor"
|
||||||
|
case .padded: return "Padded"
|
||||||
|
case .leather: return "Leather"
|
||||||
|
case .studdedLeather: return "Studded Leather"
|
||||||
|
case .hide: return "Hide"
|
||||||
|
case .chainShirt: return "Chain Shirt"
|
||||||
|
case .scaleMail: return "Scale Mail"
|
||||||
|
case .breastplate: return "Breastplate"
|
||||||
|
case .halfPlate: return "Half Plate"
|
||||||
|
case .ringMail: return "Ring Mail"
|
||||||
|
case .chainMail: return "Chain Mail"
|
||||||
|
case .splintMail: return "Splint Mail"
|
||||||
|
case .plateMail: return "Plate Mail"
|
||||||
|
case .other: return "Other"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum SizeType: String, CaseIterable, Identifiable {
|
||||||
|
case tiny = "tiny"
|
||||||
|
case small = "small"
|
||||||
|
case medium = "medium"
|
||||||
|
case large = "large"
|
||||||
|
case huge = "huge"
|
||||||
|
case gargantuan = "gargantuan"
|
||||||
|
|
||||||
|
var id: SizeType { self }
|
||||||
|
|
||||||
|
var displayName: String {
|
||||||
|
switch self {
|
||||||
|
case .tiny: return "Tiny"
|
||||||
|
case .small: return "Small"
|
||||||
|
case .medium: return "Medium"
|
||||||
|
case .large: return "Large"
|
||||||
|
case .huge: return "Huge"
|
||||||
|
case .gargantuan: return "gargantuan"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ChallengeRating: String, CaseIterable, Identifiable {
|
||||||
|
case zero = "0"
|
||||||
|
case oneEighth = "1/8"
|
||||||
|
case oneQuarter = "1/4"
|
||||||
|
case oneHalf = "1/2"
|
||||||
|
case one = "1"
|
||||||
|
case two = "2"
|
||||||
|
case three = "3"
|
||||||
|
case four = "4"
|
||||||
|
case five = "5"
|
||||||
|
case six = "6"
|
||||||
|
case seven = "7"
|
||||||
|
case eight = "8"
|
||||||
|
case nine = "9"
|
||||||
|
case ten = "10"
|
||||||
|
case eleven = "11"
|
||||||
|
case twelve = "12"
|
||||||
|
case thirteen = "13"
|
||||||
|
case fourteen = "14"
|
||||||
|
case fifteen = "15"
|
||||||
|
case sixteen = "16"
|
||||||
|
case seventeen = "17"
|
||||||
|
case eighteen = "18"
|
||||||
|
case nineteen = "19"
|
||||||
|
case twenty = "20"
|
||||||
|
case twentyOne = "21"
|
||||||
|
case twentyTwo = "22"
|
||||||
|
case twentyThree = "23"
|
||||||
|
case twentyFour = "24"
|
||||||
|
case twentyFive = "25"
|
||||||
|
case twentySix = "26"
|
||||||
|
case twentySeven = "27"
|
||||||
|
case twentyEight = "28"
|
||||||
|
case twentyNine = "29"
|
||||||
|
case thirty = "30"
|
||||||
|
case custom = "*"
|
||||||
|
|
||||||
|
var id: ChallengeRating { self }
|
||||||
|
|
||||||
|
// Probably don't need this
|
||||||
|
var displayName: String {
|
||||||
|
return rawValue
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,160 +0,0 @@
|
|||||||
//
|
|
||||||
// Monster.h
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/4/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
#import <CoreData/CoreData.h>
|
|
||||||
#import "Ability.h"
|
|
||||||
#import "Action.h"
|
|
||||||
#import "DamageType.h"
|
|
||||||
#import "Language.h"
|
|
||||||
#import "SavingThrow.h"
|
|
||||||
#import "Skill.h"
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
extern NSString* const kMonsterSizeTiny;
|
|
||||||
extern NSString* const kMonsterSizeSmall;
|
|
||||||
extern NSString* const kMonsterSizeMedium;
|
|
||||||
extern NSString* const kMonsterSizeLarge;
|
|
||||||
extern NSString* const kMonsterSizeHuge;
|
|
||||||
extern NSString* const kMonsterSizeGargantuan;
|
|
||||||
|
|
||||||
extern const int kArmorClassUnarmored;
|
|
||||||
extern const int kArmorClassMageArmor;
|
|
||||||
extern const int kArmorClassPadded;
|
|
||||||
extern const int kArmorClassLeather;
|
|
||||||
extern const int kArmorClassStudded;
|
|
||||||
extern const int kArmorClassHide;
|
|
||||||
extern const int kArmorClassChainShirt;
|
|
||||||
extern const int kArmorClassScaleMail;
|
|
||||||
extern const int kArmorClassBreastplate;
|
|
||||||
extern const int kArmorClassHalfPlate;
|
|
||||||
extern const int kArmorClassRingMail;
|
|
||||||
extern const int kArmorClassChainMail;
|
|
||||||
extern const int kArmorClassSplintMail;
|
|
||||||
extern const int kArmorClassPlate;
|
|
||||||
|
|
||||||
extern NSString* const kArmorNameNone;
|
|
||||||
extern NSString* const kArmorNameNaturalArmor;
|
|
||||||
extern NSString* const kArmorNameMageArmor;
|
|
||||||
extern NSString* const kArmorNamePadded;
|
|
||||||
extern NSString* const kArmorNameLeather;
|
|
||||||
extern NSString* const kArmorNameStuddedLeather;
|
|
||||||
extern NSString* const kArmorNameHide;
|
|
||||||
extern NSString* const kArmorNameChainShirt;
|
|
||||||
extern NSString* const kArmorNameScaleMail;
|
|
||||||
extern NSString* const kArmorNameBreastplate;
|
|
||||||
extern NSString* const kArmorNameHalfPlate;
|
|
||||||
extern NSString* const kArmorNameRingMail;
|
|
||||||
extern NSString* const kArmorNameChainMail;
|
|
||||||
extern NSString* const kArmorNameSplintMail;
|
|
||||||
extern NSString* const kArmorNamePlateMail;
|
|
||||||
extern NSString* const kArmorNameOther;
|
|
||||||
|
|
||||||
extern NSString *const kProficiencyTypeNone;
|
|
||||||
extern NSString *const kProficiencyTypeProficient;
|
|
||||||
extern NSString *const kProficiencyTypeExpertise;
|
|
||||||
|
|
||||||
extern NSString *const kAdvantageTypeNone;
|
|
||||||
extern NSString *const kAdvantageTypeAdvantage;
|
|
||||||
extern NSString *const kAdvantageTypeDisadvantage;
|
|
||||||
|
|
||||||
@class Skill;
|
|
||||||
|
|
||||||
@interface Monster : NSManagedObject
|
|
||||||
|
|
||||||
// speedDescription
|
|
||||||
@property NSString *challengeRating;
|
|
||||||
@property NSString *customChallengeRating;
|
|
||||||
@property NSString *blindsightDistance;
|
|
||||||
@property NSString *darkvisionDistance;
|
|
||||||
@property NSString *tremorsenseDistance;
|
|
||||||
@property NSString *truesightDistance;
|
|
||||||
@property NSString *understandsBut;
|
|
||||||
|
|
||||||
@property int naturalArmorBonus;
|
|
||||||
@property int customProficiencyBonus;
|
|
||||||
// Shouldn't this be a BOOL?
|
|
||||||
@property int telepathy;
|
|
||||||
|
|
||||||
@property BOOL isBlind;
|
|
||||||
|
|
||||||
+(int)abilityModifierForScore: (int)score;
|
|
||||||
+(int)hitDieForSize: (NSString*)size;
|
|
||||||
|
|
||||||
-(id)initWithJSONString:(NSString*)jsonString andContext:(NSManagedObjectContext*)context;
|
|
||||||
-(id)initWithJSONData:(NSData*)jsonData andContext:(NSManagedObjectContext*)context;
|
|
||||||
-(id)initWithMonster:(Monster*)monster;
|
|
||||||
-(void)copyFromMonster:(Monster*)monster;
|
|
||||||
|
|
||||||
-(NSString*)meta;
|
|
||||||
-(int)abilityScoreForAbilityScoreName: (NSString*)abilityScoreName;
|
|
||||||
-(int)abilityModifierForAbilityScoreName: (NSString*)abilityScoreName;
|
|
||||||
-(int)strengthModifier;
|
|
||||||
-(int)dexterityModifier;
|
|
||||||
-(int)constitutionModifier;
|
|
||||||
-(int)intelligenceModifier;
|
|
||||||
-(int)wisdomModifier;
|
|
||||||
-(int)charismaModifier;
|
|
||||||
//getArmorClass
|
|
||||||
-(NSString*)armorClassDescription;
|
|
||||||
//getHitPoints
|
|
||||||
-(NSString*)hitPointsDescription;
|
|
||||||
//getSpeedText
|
|
||||||
-(NSString*)speedDescription;
|
|
||||||
-(NSString*)strengthDescription;
|
|
||||||
-(NSString*)dexterityDescription;
|
|
||||||
-(NSString*)constitutionDescription;
|
|
||||||
-(NSString*)intelligenceDescription;
|
|
||||||
-(NSString*)wisdomDescription;
|
|
||||||
-(NSString*)charismaDescription;
|
|
||||||
-(NSSet*)savingThrows;
|
|
||||||
-(void)addSavingThrow: (SavingThrow*)savingThrow;
|
|
||||||
-(void)remvoeSavingThrow: (SavingThrow*)savingThrow;
|
|
||||||
-(void)clearSavingThrows;
|
|
||||||
-(NSString*)savingThrowsDescription;
|
|
||||||
-(int)proficiencyBonus;
|
|
||||||
-(int)proficiencyBonusForType:(NSString*)proficiencyType;
|
|
||||||
-(void)addSkill: (Skill*)skill;
|
|
||||||
-(void)removeSkill: (Skill*)skill;
|
|
||||||
-(void)clearSkills;
|
|
||||||
-(NSString*)skillsDescription;
|
|
||||||
-(void)addDamageType: (DamageType*)damageType;
|
|
||||||
-(void)removeDamageType: (DamageType*)damageType;
|
|
||||||
-(void)clearDamageTypes;
|
|
||||||
-(NSString*)damageImmunitiesDescription;
|
|
||||||
-(NSString*)damageResistancesDescription;
|
|
||||||
-(NSString*)damageVulnerabilitiesDescription;
|
|
||||||
-(void)addConditionImmunity: (NSString*)condition;
|
|
||||||
-(void)removeConditionImmunity: (NSString*)condition;
|
|
||||||
-(void)clearConditionImmunities;
|
|
||||||
-(NSString*)conditionImmunitiesDescription;
|
|
||||||
-(NSString*)sensesDescription;
|
|
||||||
-(void)addLanguage: (Language*)language;
|
|
||||||
-(void)removeLanguage: (Language*)language;
|
|
||||||
-(void)clearLanguages;
|
|
||||||
-(NSString*)languagesDescription;
|
|
||||||
-(NSString*)challengeRatingDescription;
|
|
||||||
-(void)addAbility: (Ability*)ability;
|
|
||||||
-(void)removeAbility: (Ability*)ability;
|
|
||||||
-(void)clearAbilities;
|
|
||||||
-(NSArray*)abilityDescriptions;
|
|
||||||
-(void)addAction: (Action*)action;
|
|
||||||
-(void)removeAction: (Action*)action;
|
|
||||||
-(void)clearActions;
|
|
||||||
-(NSArray*)actionDescriptions;
|
|
||||||
-(NSString*)placeholderReplacedText: (NSString*)text;
|
|
||||||
-(int)savingThrowForAbilityScoreName: (NSString*)abilityScoreName;
|
|
||||||
-(int)spellSaveDCForAbilityScoreName: (NSString*)abilityScoreName;
|
|
||||||
-(int)attackBonusForAbilityScoreName: (NSString*)abilityScoreName;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
||||||
|
|
||||||
#import "Monster+CoreDataProperties.h"
|
|
||||||
@@ -1,699 +0,0 @@
|
|||||||
//
|
|
||||||
// Monster.m
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/4/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "Monster.h"
|
|
||||||
#import "StringHelper.h"
|
|
||||||
#import "JSONHelper.h"
|
|
||||||
|
|
||||||
@implementation Monster
|
|
||||||
|
|
||||||
@synthesize blindsightDistance;
|
|
||||||
// TODO: move this to Core Data and add to editor
|
|
||||||
@synthesize challengeRating;
|
|
||||||
// TODO: move this to Core Data and add to editor
|
|
||||||
@synthesize customChallengeRating;
|
|
||||||
// TODO: move this to Core Data and add to editor
|
|
||||||
@synthesize customProficiencyBonus;
|
|
||||||
@synthesize darkvisionDistance;
|
|
||||||
@synthesize isBlind;
|
|
||||||
@synthesize naturalArmorBonus;
|
|
||||||
@synthesize telepathy;
|
|
||||||
@synthesize tremorsenseDistance;
|
|
||||||
@synthesize truesightDistance;
|
|
||||||
@synthesize understandsBut;
|
|
||||||
|
|
||||||
const int kArmorClassUnarmored = 10;
|
|
||||||
const int kArmorClassMageArmor = kArmorClassUnarmored + 3;
|
|
||||||
const int kArmorClassPadded = kArmorClassUnarmored + 1;
|
|
||||||
const int kArmorClassLeather = kArmorClassUnarmored + 1;
|
|
||||||
const int kArmorClassStudded = kArmorClassUnarmored + 2;
|
|
||||||
const int kArmorClassHide = kArmorClassUnarmored + 2;
|
|
||||||
const int kArmorClassChainShirt = kArmorClassUnarmored + 3;
|
|
||||||
const int kArmorClassScaleMail = kArmorClassUnarmored + 4;
|
|
||||||
const int kArmorClassBreastplate = kArmorClassUnarmored + 4;
|
|
||||||
const int kArmorClassHalfPlate = kArmorClassUnarmored + 5;
|
|
||||||
const int kArmorClassRingMail = kArmorClassUnarmored + 4;
|
|
||||||
const int kArmorClassChainMail = kArmorClassUnarmored + 6;
|
|
||||||
const int kArmorClassSplintMail = kArmorClassUnarmored + 7;
|
|
||||||
const int kArmorClassPlate = kArmorClassUnarmored + 8;
|
|
||||||
|
|
||||||
NSString* const kArmorNameNone = @"none";
|
|
||||||
NSString* const kArmorNameNaturalArmor = @"natural armor";
|
|
||||||
NSString* const kArmorNameMageArmor = @"mage armor";
|
|
||||||
NSString* const kArmorNamePadded = @"padded";
|
|
||||||
NSString* const kArmorNameLeather = @"leather";
|
|
||||||
NSString* const kArmorNameStuddedLeather = @"studded";
|
|
||||||
NSString* const kArmorNameHide = @"hide";
|
|
||||||
NSString* const kArmorNameChainShirt = @"chain shirt";
|
|
||||||
NSString* const kArmorNameScaleMail = @"scale mail";
|
|
||||||
NSString* const kArmorNameBreastplate = @"breastplate";
|
|
||||||
NSString* const kArmorNameHalfPlate = @"half plate";
|
|
||||||
NSString* const kArmorNameRingMail = @"ring mail";
|
|
||||||
NSString* const kArmorNameChainMail = @"chain mail";
|
|
||||||
NSString* const kArmorNameSplintMail = @"splint";
|
|
||||||
NSString* const kArmorNamePlateMail = @"plate";
|
|
||||||
NSString* const kArmorNameOther = @"other";
|
|
||||||
|
|
||||||
NSString* const kMonsterSizeTiny = @"tiny";
|
|
||||||
NSString* const kMonsterSizeSmall = @"small";
|
|
||||||
NSString* const kMonsterSizeMedium = @"medium";
|
|
||||||
NSString* const kMonsterSizeLarge = @"large";
|
|
||||||
NSString* const kMonsterSizeHuge = @"huge";
|
|
||||||
NSString* const kMonsterSizeGargantuan = @"gargantuan";
|
|
||||||
|
|
||||||
NSString *const kProficiencyTypeNone = @"none";
|
|
||||||
NSString *const kProficiencyTypeProficient = @"proficient";
|
|
||||||
NSString *const kProficiencyTypeExpertise = @"expertise";
|
|
||||||
|
|
||||||
NSString *const kAdvantageTypeNone = @"none";
|
|
||||||
NSString *const kAdvantageTypeAdvantage = @"advantage";
|
|
||||||
NSString *const kAdvantageTypeDisadvantage = @"disadvantage";
|
|
||||||
|
|
||||||
+(int)abilityModifierForScore: (int)score {
|
|
||||||
return (int)floor((score - 10) / 2.0);
|
|
||||||
}
|
|
||||||
|
|
||||||
+(int)hitDieForSize: (NSString*)size{
|
|
||||||
if ([kMonsterSizeTiny isEqualToString:size]) {
|
|
||||||
return 4;
|
|
||||||
} else if ([kMonsterSizeSmall isEqualToString:size]) {
|
|
||||||
return 6;
|
|
||||||
} else if ([kMonsterSizeMedium isEqualToString:size]) {
|
|
||||||
return 8;
|
|
||||||
} else if ([kMonsterSizeLarge isEqualToString:size]) {
|
|
||||||
return 10;
|
|
||||||
} else if ([kMonsterSizeHuge isEqualToString:size]) {
|
|
||||||
return 12;
|
|
||||||
} else if ([kMonsterSizeGargantuan isEqualToString:size]) {
|
|
||||||
return 20;
|
|
||||||
} else {
|
|
||||||
return 8;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
-(id)init {
|
|
||||||
self = [super init];
|
|
||||||
|
|
||||||
self.name = @"";
|
|
||||||
self.size = @"";
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(id)initWithJSONString: (NSString*)jsonString andContext:(NSManagedObjectContext*)context {
|
|
||||||
NSData* jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
|
|
||||||
|
|
||||||
return [self initWithJSONData:jsonData andContext:context];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(id)initWithJSONData: (NSData*)jsonData andContext:(NSManagedObjectContext*)context {
|
|
||||||
self = [self initWithContext:context];
|
|
||||||
|
|
||||||
NSDictionary *jsonRoot = [JSONHelper parseJSONDataAsDictionary:jsonData];
|
|
||||||
|
|
||||||
self.name = [JSONHelper readStringFromDictionary:jsonRoot forKey:@"name" withDefaultValue:@""];
|
|
||||||
self.size = [JSONHelper readStringFromDictionary:jsonRoot forKey:@"size" withDefaultValue:@""];
|
|
||||||
self.type = [JSONHelper readStringFromDictionary:jsonRoot forKey:@"type" withDefaultValue:@""];
|
|
||||||
self.subtype = [JSONHelper readStringFromDictionary:jsonRoot forKey:@"tag" withDefaultValue:@""];
|
|
||||||
self.hpText = [JSONHelper readStringFromDictionary:jsonRoot forKey:@"hpText" withDefaultValue:@""];
|
|
||||||
|
|
||||||
self.alignment = [JSONHelper readStringFromDictionary:jsonRoot forKey:@"alignment" withDefaultValue:@""];
|
|
||||||
self.armorType = [JSONHelper readStringFromDictionary:jsonRoot forKey:@"armorName" withDefaultValue:@""];
|
|
||||||
self.otherArmorDescription = [JSONHelper readStringFromDictionary:jsonRoot forKey:@"otherArmorDesc" withDefaultValue:@""];
|
|
||||||
self.strengthScore = [JSONHelper readIntFromDictionary:jsonRoot forKey:@"strPoints" withDefaultValue:10];
|
|
||||||
self.dexterityScore = [JSONHelper readIntFromDictionary:jsonRoot forKey:@"dexPoints" withDefaultValue:10];
|
|
||||||
self.constitutionScore = [JSONHelper readIntFromDictionary:jsonRoot forKey:@"conPoints" withDefaultValue:10];
|
|
||||||
self.intelligenceScore = [JSONHelper readIntFromDictionary:jsonRoot forKey:@"intPoints" withDefaultValue:10];
|
|
||||||
self.wisdomScore = [JSONHelper readIntFromDictionary:jsonRoot forKey:@"wisPoints" withDefaultValue:10];
|
|
||||||
self.charismaScore = [JSONHelper readIntFromDictionary:jsonRoot forKey:@"chaPoints" withDefaultValue:10];
|
|
||||||
self.shieldBonus = [JSONHelper readIntFromDictionary:jsonRoot forKey:@"shieldBonus" withDefaultValue:0];
|
|
||||||
self.hitDice = [JSONHelper readIntFromDictionary:jsonRoot forKey:@"hitDice" withDefaultValue:0];
|
|
||||||
|
|
||||||
self.customHP = [JSONHelper readBoolFromDictionary:jsonRoot forKey:@"customHP" withDefaultValue:NO];
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(id)initWithMonster:(Monster* _Nonnull)monster {
|
|
||||||
self = [self initWithContext:monster.managedObjectContext];
|
|
||||||
|
|
||||||
[self copyFromMonster:monster];
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(NSString*)meta {
|
|
||||||
// "${size} ${type} (${subtype}) ${alignment}"
|
|
||||||
|
|
||||||
NSMutableArray *parts = [NSMutableArray arrayWithCapacity:4];
|
|
||||||
if (![StringHelper isStringNilOrEmpty:self.size]) {
|
|
||||||
[parts addObject:self.size];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (![StringHelper isStringNilOrEmpty:self.type]) {
|
|
||||||
[parts addObject:self.type];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (![StringHelper isStringNilOrEmpty:self.subtype]) {
|
|
||||||
[parts addObject:[NSString stringWithFormat:@"(%@)", self.subtype]];
|
|
||||||
}
|
|
||||||
|
|
||||||
if (![StringHelper isStringNilOrEmpty:self.alignment]) {
|
|
||||||
[parts addObject:self.alignment];
|
|
||||||
}
|
|
||||||
|
|
||||||
return [parts componentsJoinedByString:@" "];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(int)abilityScoreForAbilityScoreName: (NSString*)abilityScoreName {
|
|
||||||
@throw [[NSException alloc] initWithName:@"unimplemented" reason:@"Method not implemented." userInfo:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(int)abilityModifierForAbilityScoreName: (NSString*)abilityScoreName {
|
|
||||||
@throw [[NSException alloc] initWithName:@"unimplemented" reason:@"Method not implemented." userInfo:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(int)strengthModifier {
|
|
||||||
return [Monster abilityModifierForScore:self.strengthScore];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(int)dexterityModifier {
|
|
||||||
return [Monster abilityModifierForScore:self.dexterityScore];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(int)constitutionModifier {
|
|
||||||
return [Monster abilityModifierForScore:self.constitutionScore];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(int)intelligenceModifier {
|
|
||||||
return [Monster abilityModifierForScore:self.intelligenceScore];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(int)wisdomModifier {
|
|
||||||
return [Monster abilityModifierForScore:self.wisdomScore];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(int)charismaModifier {
|
|
||||||
return [Monster abilityModifierForScore:self.charismaScore];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
//getArmorClass
|
|
||||||
-(NSString*)armorClassDescription {
|
|
||||||
BOOL hasShield = [self shieldBonus] != 0;
|
|
||||||
NSString *armorType = [self armorType];
|
|
||||||
if ([StringHelper isStringNilOrEmpty:armorType] || [kArmorNameNone isEqualToString:armorType]) {
|
|
||||||
// 10 + dexMod + 2 for shieldBonus "15" or "17 (shield)"
|
|
||||||
int armorClass = kArmorClassUnarmored + self.dexterityModifier + self.shieldBonus;
|
|
||||||
return [NSString stringWithFormat:@"%d%@", armorClass, (hasShield ? @" (shield)" : @"")];
|
|
||||||
} else if ([kArmorNameNaturalArmor isEqualToString:armorType]) {
|
|
||||||
// 10 + dexMod + naturalArmorBonus + 2 for shieldBonus "16 (natural armor)" or "18 (natural armor, shield)"
|
|
||||||
int armorClass = kArmorClassUnarmored + self.dexterityModifier + self.naturalArmorBonus + self.shieldBonus;
|
|
||||||
return [NSString stringWithFormat:@"%d (natural armor%@)", armorClass, (hasShield ? @" (shield)" : @"")];
|
|
||||||
} else if ([kArmorNameMageArmor isEqualToString:armorType]) {
|
|
||||||
// 10 + dexMod + 2 for shield + 3 for mage armor "15 (18 with mage armor)" or 17 (shield, 20 with mage armor)
|
|
||||||
int armorClass = kArmorClassUnarmored + self.dexterityModifier + self.shieldBonus;
|
|
||||||
int armorClassWithMageArmor = kArmorClassMageArmor + self.dexterityModifier + self.shieldBonus;
|
|
||||||
return [NSString stringWithFormat:@"%d (%@%d with mage armor)", armorClass, (hasShield ? @"shield, " : @""), armorClassWithMageArmor];
|
|
||||||
} else if ([kArmorNamePadded isEqualToString:armorType]) {
|
|
||||||
// 11 + dexMod + 2 for shield "18 (padded armor, shield)"
|
|
||||||
int armorClass = kArmorClassPadded + self.dexterityModifier + self.shieldBonus;
|
|
||||||
return [NSString stringWithFormat:@"%d (padded%@)", armorClass, (hasShield ? @"shield, " : @"")];
|
|
||||||
} else if ([kArmorNameLeather isEqualToString:armorType]) {
|
|
||||||
// 11 + dexMod + 2 for shield "18 (leather, shield)"
|
|
||||||
int armorClass = kArmorClassLeather + self.dexterityModifier + self.shieldBonus;
|
|
||||||
return [NSString stringWithFormat:@"%d (leather%@)", armorClass, (hasShield ? @"shield, " : @"")];
|
|
||||||
} else if ([kArmorNameStuddedLeather isEqualToString:armorType]) {
|
|
||||||
// 12 + dexMod +2 for shield "17 (studded leather)"
|
|
||||||
int armorClass = kArmorClassStudded + self.dexterityModifier + self.shieldBonus;
|
|
||||||
return [NSString stringWithFormat:@"%d (studded leather%@)", armorClass, (hasShield ? @"shield, " : @"")];
|
|
||||||
} else if ([kArmorNameHide isEqualToString:armorType]) {
|
|
||||||
// 12 + Min(2, dexMod) + 2 for shield "12 (hide armor)"
|
|
||||||
int armorClass = kArmorClassHide + MIN(2, self.dexterityModifier) + self.shieldBonus;
|
|
||||||
return [NSString stringWithFormat:@"%d (hide%@)", armorClass, (hasShield ? @", shield" : @"")];
|
|
||||||
} else if ([kArmorNameChainShirt isEqualToString:armorType]) {
|
|
||||||
// 13 + Min(2, dexMod) + 2 for shield "12 (chain shirt)"
|
|
||||||
int armorClass = kArmorClassChainShirt + MIN(2, self.dexterityModifier) + self.shieldBonus;
|
|
||||||
return [NSString stringWithFormat:@"%d (chain shirt%@)", armorClass, (hasShield ? @", shield" : @"")];
|
|
||||||
} else if ([kArmorNameScaleMail isEqualToString:armorType]) {
|
|
||||||
// 14 + Min(2, dexMod) + 2 for shield "14 (scale mail)"
|
|
||||||
int armorClass = kArmorClassScaleMail + MIN(2, self.dexterityModifier) + self.shieldBonus;
|
|
||||||
return [NSString stringWithFormat:@"%d (scale mail%@)", armorClass, (hasShield ? @", shield" : @"")];
|
|
||||||
} else if ([kArmorNameBreastplate isEqualToString:armorType]) {
|
|
||||||
// 14 + Min(2, dexMod) + 2 for shield "16 (breastplate)"
|
|
||||||
int armorClass = kArmorClassBreastplate + MIN(2, self.dexterityModifier) + self.shieldBonus;
|
|
||||||
return [NSString stringWithFormat:@"%d (breastplate%@)", armorClass, (hasShield ? @", shield" : @"")];
|
|
||||||
} else if ([kArmorNameHalfPlate isEqualToString:armorType]) {
|
|
||||||
// 15 + Min(2, dexMod) + 2 for shield "17 (half plate)"
|
|
||||||
int armorClass = kArmorClassHalfPlate + MIN(2, self.dexterityModifier) + self.shieldBonus;
|
|
||||||
return [NSString stringWithFormat:@"%d (half plate%@)", armorClass, (hasShield ? @", shield" : @"")];
|
|
||||||
} else if ([kArmorNameRingMail isEqualToString:armorType]) {
|
|
||||||
// 14 + 2 for shield "14 (ring mail)
|
|
||||||
int armorClass = kArmorClassRingMail + self.shieldBonus;
|
|
||||||
return [NSString stringWithFormat:@"%d (ring mail%@)", armorClass, (hasShield ? @", shield" : @"")];
|
|
||||||
} else if ([kArmorNameChainMail isEqualToString:armorType]) {
|
|
||||||
// 16 + 2 for shield "16 (chain mail)"
|
|
||||||
int armorClass = kArmorClassChainMail + self.shieldBonus;
|
|
||||||
return [NSString stringWithFormat:@"%d (chain mail%@)", armorClass, (hasShield ? @", shield" : @"")];
|
|
||||||
} else if ([kArmorNameSplintMail isEqualToString:armorType]) {
|
|
||||||
// 17 + 2 for shield "17 (splint)"
|
|
||||||
int armorClass = kArmorClassSplintMail + self.shieldBonus;
|
|
||||||
return [NSString stringWithFormat:@"%d (splint%@)", armorClass, (hasShield ? @", shield" : @"")];
|
|
||||||
} else if ([kArmorNamePlateMail isEqualToString:armorType]) {
|
|
||||||
// 18 + 2 for shield "18 (plate)"
|
|
||||||
int armorClass = kArmorClassPlate + self.shieldBonus;
|
|
||||||
return [NSString stringWithFormat:@"%d (plate%@)", armorClass, (hasShield ? @", shield" : @"")];
|
|
||||||
} else if ([kArmorNameOther isEqualToString:armorType]) {
|
|
||||||
// pure string value shield check does nothing just copies the string from otherArmorDesc
|
|
||||||
return self.otherArmorDescription;
|
|
||||||
} else {
|
|
||||||
return @"";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//getHitPoints
|
|
||||||
-(NSString*)hitPointsDescription {
|
|
||||||
if (self.customHP) {
|
|
||||||
return self.hpText;
|
|
||||||
} else {
|
|
||||||
int dieSize = [Monster hitDieForSize:self.size];
|
|
||||||
int conMod = self.constitutionModifier;
|
|
||||||
int hpTotal = (int)MAX(1, ceil(dieSize + conMod + (self.hitDice - 1) * ((dieSize + 1) / 2.0 + conMod)));
|
|
||||||
int conBonus = conMod * self.hitDice;
|
|
||||||
return [NSString stringWithFormat:@"%d (%dd%d%+d)", hpTotal, self.hitDice, dieSize, conBonus];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
-(NSString*)speedDescription {
|
|
||||||
if (self.hasCustomSpeed) {
|
|
||||||
return self.customSpeed;
|
|
||||||
} else {
|
|
||||||
NSMutableArray* parts = [[NSMutableArray alloc] init];
|
|
||||||
if (self.baseSpeed > 0) {
|
|
||||||
[parts addObject:[NSString stringWithFormat:@"%d ft.", self.baseSpeed]];
|
|
||||||
}
|
|
||||||
if (self.burrowSpeed > 0) {
|
|
||||||
[parts addObject:[NSString stringWithFormat:@"burrow %d ft.", self.burrowSpeed]];
|
|
||||||
}
|
|
||||||
if (self.climbSpeed > 0) {
|
|
||||||
[parts addObject:[NSString stringWithFormat:@"climb %d ft.", self.climbSpeed]];
|
|
||||||
}
|
|
||||||
if (self.flySpeed > 0) {
|
|
||||||
[parts addObject:[NSString stringWithFormat:@"fly %d ft.%@", self.flySpeed, self.canHover ? @" (hover)" : @""]];
|
|
||||||
}
|
|
||||||
if (self.swimSpeed > 0) {
|
|
||||||
[parts addObject:[NSString stringWithFormat:@"swim %d ft.", self.swimSpeed]];
|
|
||||||
}
|
|
||||||
return [parts componentsJoinedByString:@" "];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
-(NSString*)strengthDescription {
|
|
||||||
return [NSString stringWithFormat:@"%d (%+d)", self.strengthScore, self.strengthModifier];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(NSString*)dexterityDescription {
|
|
||||||
return [NSString stringWithFormat:@"%d (%+d)", self.dexterityScore, self.dexterityModifier];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(NSString*)constitutionDescription {
|
|
||||||
return [NSString stringWithFormat:@"%d (%+d)", self.constitutionScore, self.constitutionModifier];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(NSString*)intelligenceDescription {
|
|
||||||
return [NSString stringWithFormat:@"%d (%+d)", self.intelligenceScore, self.intelligenceModifier];
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
-(NSString*)wisdomDescription {
|
|
||||||
return [NSString stringWithFormat:@"%d (%+d)", self.wisdomScore, self.wisdomModifier];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(NSString*)charismaDescription {
|
|
||||||
return [NSString stringWithFormat:@"%d (%+d)", self.charismaScore, self.charismaModifier];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(NSSet*)savingThrows {
|
|
||||||
@throw [[NSException alloc] initWithName:@"unimplemented" reason:@"Method not implemented." userInfo:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)addSavingThrow: (SavingThrow*)savingThrow {
|
|
||||||
@throw [[NSException alloc] initWithName:@"unimplemented" reason:@"Method not implemented." userInfo:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)remvoeSavingThrow: (SavingThrow*)savingThrow {
|
|
||||||
@throw [[NSException alloc] initWithName:@"unimplemented" reason:@"Method not implemented." userInfo:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)clearSavingThrows {
|
|
||||||
@throw [[NSException alloc] initWithName:@"unimplemented" reason:@"Method not implemented." userInfo:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(int)proficiencyBonusForType:(NSString*)proficiencyType {
|
|
||||||
if ([kProficiencyTypeNone isEqualToString:proficiencyType]) {
|
|
||||||
return 0;
|
|
||||||
} else if ([kProficiencyTypeProficient isEqualToString:proficiencyType]) {
|
|
||||||
return self.proficiencyBonus;
|
|
||||||
} else if ([kProficiencyTypeExpertise isEqualToString:proficiencyType]) {
|
|
||||||
return self.proficiencyBonus * 2;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
+(NSString*)advantageLabelStringForType:(NSString*)advantageType {
|
|
||||||
if ([kAdvantageTypeNone isEqualToString:advantageType]) {
|
|
||||||
return @"";
|
|
||||||
} else if ([kAdvantageTypeAdvantage isEqualToString:advantageType]) {
|
|
||||||
return @"(A)";
|
|
||||||
} else if ([kAdvantageTypeDisadvantage isEqualToString:advantageType]) {
|
|
||||||
return @"(D)";
|
|
||||||
} else {
|
|
||||||
return @"";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
-(NSString*)savingThrowsDescription {
|
|
||||||
NSMutableArray *parts = [[NSMutableArray alloc] init];
|
|
||||||
NSString *name;
|
|
||||||
NSString *advantage;
|
|
||||||
int bonus;
|
|
||||||
if (
|
|
||||||
![kProficiencyTypeNone isEqualToString:self.strengthSavingThrowProficiency] ||
|
|
||||||
![kAdvantageTypeNone isEqualToString:self.strengthSavingThrowAdvantage]
|
|
||||||
) {
|
|
||||||
name = NSLocalizedString(@"Strength", @"");
|
|
||||||
bonus = self.strengthModifier + [self proficiencyBonusForType:self.strengthSavingThrowProficiency];
|
|
||||||
advantage = [Monster advantageLabelStringForType:self.strengthSavingThrowAdvantage];
|
|
||||||
if (advantage) {
|
|
||||||
advantage = [@" " stringByAppendingString:advantage];
|
|
||||||
}
|
|
||||||
[parts addObject:[NSString stringWithFormat:@"%@ %+d%@", name, bonus, advantage]];
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
![kProficiencyTypeNone isEqualToString:self.dexteritySavingThrowProficiency] ||
|
|
||||||
![kAdvantageTypeNone isEqualToString:self.dexteritySavingThrowAdvantage]
|
|
||||||
) {
|
|
||||||
name = NSLocalizedString(@"Dexterity", @"");
|
|
||||||
bonus = self.dexterityModifier + [self proficiencyBonusForType:self.dexteritySavingThrowProficiency];
|
|
||||||
advantage = [Monster advantageLabelStringForType:self.dexteritySavingThrowAdvantage];
|
|
||||||
if (advantage) {
|
|
||||||
advantage = [@" " stringByAppendingString:advantage];
|
|
||||||
}
|
|
||||||
[parts addObject:[NSString stringWithFormat:@"%@ %+d%@", name, bonus, advantage]];
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
![kProficiencyTypeNone isEqualToString:self.constitutionSavingThrowProficiency] ||
|
|
||||||
![kAdvantageTypeNone isEqualToString:self.constitutionSavingThrowAdvantage]
|
|
||||||
) {
|
|
||||||
name = NSLocalizedString(@"Constitution", @"");
|
|
||||||
bonus = self.constitutionModifier + [self proficiencyBonusForType:self.constitutionSavingThrowProficiency];
|
|
||||||
advantage = [Monster advantageLabelStringForType:self.constitutionSavingThrowAdvantage];
|
|
||||||
if (advantage) {
|
|
||||||
advantage = [@" " stringByAppendingString:advantage];
|
|
||||||
}
|
|
||||||
[parts addObject:[NSString stringWithFormat:@"%@ %+d%@", name, bonus, advantage]];
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
![kProficiencyTypeNone isEqualToString:self.intelligenceSavingThrowProficiency] ||
|
|
||||||
![kAdvantageTypeNone isEqualToString:self.intelligenceSavingThrowAdvantage]
|
|
||||||
) {
|
|
||||||
name = NSLocalizedString(@"Intelligence", @"");
|
|
||||||
bonus = self.intelligenceModifier + [self proficiencyBonusForType:self.intelligenceSavingThrowProficiency];
|
|
||||||
advantage = [Monster advantageLabelStringForType:self.intelligenceSavingThrowAdvantage];
|
|
||||||
if (advantage) {
|
|
||||||
advantage = [@" " stringByAppendingString:advantage];
|
|
||||||
}
|
|
||||||
[parts addObject:[NSString stringWithFormat:@"%@ %+d%@", name, bonus, advantage]];
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
![kProficiencyTypeNone isEqualToString:self.wisdomSavingThrowProficiency] ||
|
|
||||||
![kAdvantageTypeNone isEqualToString:self.wisdomSavingThrowAdvantage]
|
|
||||||
) {
|
|
||||||
name = NSLocalizedString(@"Wisdom", @"");
|
|
||||||
bonus = self.wisdomModifier + [self proficiencyBonusForType:self.wisdomSavingThrowProficiency];
|
|
||||||
advantage = [Monster advantageLabelStringForType:self.wisdomSavingThrowAdvantage];
|
|
||||||
if (advantage) {
|
|
||||||
advantage = [@" " stringByAppendingString:advantage];
|
|
||||||
}
|
|
||||||
[parts addObject:[NSString stringWithFormat:@"%@ %+d%@", name, bonus, advantage]];
|
|
||||||
}
|
|
||||||
if (
|
|
||||||
![kProficiencyTypeNone isEqualToString:self.charismaSavingThrowProficiency] ||
|
|
||||||
![kAdvantageTypeNone isEqualToString:self.charismaSavingThrowAdvantage]
|
|
||||||
) {
|
|
||||||
name = NSLocalizedString(@"Charisma", @"");
|
|
||||||
bonus = self.charismaModifier + [self proficiencyBonusForType:self.charismaSavingThrowProficiency];
|
|
||||||
advantage = [Monster advantageLabelStringForType:self.charismaSavingThrowAdvantage];
|
|
||||||
if (advantage) {
|
|
||||||
advantage = [@" " stringByAppendingString:advantage];
|
|
||||||
}
|
|
||||||
[parts addObject:[NSString stringWithFormat:@"%@ %+d%@", name, bonus, advantage]];
|
|
||||||
}
|
|
||||||
return [parts componentsJoinedByString:@", "];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(int)proficiencyBonus {
|
|
||||||
if ([@"*" isEqualToString:challengeRating]) {
|
|
||||||
return customProficiencyBonus;
|
|
||||||
} else if (
|
|
||||||
[@"0" isEqualToString:challengeRating] ||
|
|
||||||
[@"1/8" isEqualToString:challengeRating] ||
|
|
||||||
[@"1/4" isEqualToString:challengeRating] ||
|
|
||||||
[@"1/2" isEqualToString:challengeRating] ||
|
|
||||||
[@"1" isEqualToString:challengeRating] ||
|
|
||||||
[@"2" isEqualToString:challengeRating] ||
|
|
||||||
[@"3" isEqualToString:challengeRating] ||
|
|
||||||
[@"4" isEqualToString:challengeRating]
|
|
||||||
) {
|
|
||||||
return 2;
|
|
||||||
} else if (
|
|
||||||
[@"5" isEqualToString:challengeRating] ||
|
|
||||||
[@"6" isEqualToString:challengeRating] ||
|
|
||||||
[@"7" isEqualToString:challengeRating] ||
|
|
||||||
[@"8" isEqualToString:challengeRating])
|
|
||||||
{
|
|
||||||
return 3;
|
|
||||||
} else if (
|
|
||||||
[@"9" isEqualToString:challengeRating] ||
|
|
||||||
[@"10" isEqualToString:challengeRating] ||
|
|
||||||
[@"11" isEqualToString:challengeRating] ||
|
|
||||||
[@"12" isEqualToString:challengeRating])
|
|
||||||
{
|
|
||||||
return 4;
|
|
||||||
} else if (
|
|
||||||
[@"13" isEqualToString:challengeRating] ||
|
|
||||||
[@"14" isEqualToString:challengeRating] ||
|
|
||||||
[@"15" isEqualToString:challengeRating] ||
|
|
||||||
[@"16" isEqualToString:challengeRating])
|
|
||||||
{
|
|
||||||
return 5;
|
|
||||||
} else if (
|
|
||||||
[@"17" isEqualToString:challengeRating] ||
|
|
||||||
[@"18" isEqualToString:challengeRating] ||
|
|
||||||
[@"19" isEqualToString:challengeRating] ||
|
|
||||||
[@"20" isEqualToString:challengeRating])
|
|
||||||
{
|
|
||||||
return 6;
|
|
||||||
} else if (
|
|
||||||
[@"21" isEqualToString:challengeRating] ||
|
|
||||||
[@"22" isEqualToString:challengeRating] ||
|
|
||||||
[@"23" isEqualToString:challengeRating] ||
|
|
||||||
[@"24" isEqualToString:challengeRating])
|
|
||||||
{
|
|
||||||
return 7;
|
|
||||||
} else if (
|
|
||||||
[@"25" isEqualToString:challengeRating] ||
|
|
||||||
[@"26" isEqualToString:challengeRating] ||
|
|
||||||
[@"27" isEqualToString:challengeRating] ||
|
|
||||||
[@"28" isEqualToString:challengeRating])
|
|
||||||
{
|
|
||||||
return 8;
|
|
||||||
} else if (
|
|
||||||
[@"29" isEqualToString:challengeRating] ||
|
|
||||||
[@"30" isEqualToString:challengeRating])
|
|
||||||
{
|
|
||||||
return 9;
|
|
||||||
} else {
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)addSkill: (Skill*)skill {
|
|
||||||
@throw [[NSException alloc] initWithName:@"unimplemented" reason:@"Method not implemented." userInfo:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)removeSkill: (Skill*)skill {
|
|
||||||
@throw [[NSException alloc] initWithName:@"unimplemented" reason:@"Method not implemented." userInfo:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)clearSkills {
|
|
||||||
@throw [[NSException alloc] initWithName:@"unimplemented" reason:@"Method not implemented." userInfo:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(NSString*)skillsDescription {
|
|
||||||
@throw [[NSException alloc] initWithName:@"unimplemented" reason:@"Method not implemented." userInfo:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)addDamageType: (DamageType*)damageType {
|
|
||||||
@throw [[NSException alloc] initWithName:@"unimplemented" reason:@"Method not implemented." userInfo:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)removeDamageType: (DamageType*)damageType {
|
|
||||||
@throw [[NSException alloc] initWithName:@"unimplemented" reason:@"Method not implemented." userInfo:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)clearDamageTypes {
|
|
||||||
@throw [[NSException alloc] initWithName:@"unimplemented" reason:@"Method not implemented." userInfo:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(NSString*)damageImmunitiesDescription {
|
|
||||||
@throw [[NSException alloc] initWithName:@"unimplemented" reason:@"Method not implemented." userInfo:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(NSString*)damageResistancesDescription {
|
|
||||||
@throw [[NSException alloc] initWithName:@"unimplemented" reason:@"Method not implemented." userInfo:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(NSString*)damageVulnerabilitiesDescription {
|
|
||||||
@throw [[NSException alloc] initWithName:@"unimplemented" reason:@"Method not implemented." userInfo:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)addConditionImmunity: (NSString*)condition {
|
|
||||||
@throw [[NSException alloc] initWithName:@"unimplemented" reason:@"Method not implemented." userInfo:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)removeConditionImmunity: (NSString*)condition {
|
|
||||||
@throw [[NSException alloc] initWithName:@"unimplemented" reason:@"Method not implemented." userInfo:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)clearConditionImmunities {
|
|
||||||
@throw [[NSException alloc] initWithName:@"unimplemented" reason:@"Method not implemented." userInfo:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(NSString*)conditionImmunitiesDescription {
|
|
||||||
@throw [[NSException alloc] initWithName:@"unimplemented" reason:@"Method not implemented." userInfo:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(NSString*)sensesDescription {
|
|
||||||
@throw [[NSException alloc] initWithName:@"unimplemented" reason:@"Method not implemented." userInfo:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)addLanguage: (Language*)language {
|
|
||||||
@throw [[NSException alloc] initWithName:@"unimplemented" reason:@"Method not implemented." userInfo:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)removeLanguage: (Language*)language {
|
|
||||||
@throw [[NSException alloc] initWithName:@"unimplemented" reason:@"Method not implemented." userInfo:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)clearLanguages {
|
|
||||||
@throw [[NSException alloc] initWithName:@"unimplemented" reason:@"Method not implemented." userInfo:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(NSString*)languagesDescription {
|
|
||||||
@throw [[NSException alloc] initWithName:@"unimplemented" reason:@"Method not implemented." userInfo:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(NSString*)challengeRatingDescription {
|
|
||||||
@throw [[NSException alloc] initWithName:@"unimplemented" reason:@"Method not implemented." userInfo:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)addAbility: (Ability*)ability {
|
|
||||||
@throw [[NSException alloc] initWithName:@"unimplemented" reason:@"Method not implemented." userInfo:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)removeAbility: (Ability*)ability {
|
|
||||||
@throw [[NSException alloc] initWithName:@"unimplemented" reason:@"Method not implemented." userInfo:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)clearAbilities {
|
|
||||||
@throw [[NSException alloc] initWithName:@"unimplemented" reason:@"Method not implemented." userInfo:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(NSArray*)abilityDescriptions {
|
|
||||||
@throw [[NSException alloc] initWithName:@"unimplemented" reason:@"Method not implemented." userInfo:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)addAction: (Action*)action {
|
|
||||||
@throw [[NSException alloc] initWithName:@"unimplemented" reason:@"Method not implemented." userInfo:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)removeAction: (Action*)action {
|
|
||||||
@throw [[NSException alloc] initWithName:@"unimplemented" reason:@"Method not implemented." userInfo:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)clearActions {
|
|
||||||
@throw [[NSException alloc] initWithName:@"unimplemented" reason:@"Method not implemented." userInfo:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(NSArray*)actionDescriptions {
|
|
||||||
@throw [[NSException alloc] initWithName:@"unimplemented" reason:@"Method not implemented." userInfo:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(NSString*)placeholderReplacedText: (NSString*)text {
|
|
||||||
@throw [[NSException alloc] initWithName:@"unimplemented" reason:@"Method not implemented." userInfo:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(int)savingThrowForAbilityScoreName: (NSString*)abilityScoreName {
|
|
||||||
@throw [[NSException alloc] initWithName:@"unimplemented" reason:@"Method not implemented." userInfo:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(int)spellSaveDCForAbilityScoreName: (NSString*)abilityScoreName {
|
|
||||||
@throw [[NSException alloc] initWithName:@"unimplemented" reason:@"Method not implemented." userInfo:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(int)attackBonusForAbilityScoreName: (NSString*)abilityScoreName {
|
|
||||||
@throw [[NSException alloc] initWithName:@"unimplemented" reason:@"Method not implemented." userInfo:nil];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)copyFromMonster:(Monster*)monster {
|
|
||||||
self.name = monster.name;
|
|
||||||
self.size = monster.size;
|
|
||||||
self.type = monster.type;
|
|
||||||
self.subtype = monster.subtype;
|
|
||||||
self.alignment = monster.alignment;
|
|
||||||
self.strengthScore = monster.strengthScore;
|
|
||||||
self.dexterityScore = monster.dexterityScore;
|
|
||||||
self.constitutionScore = monster.constitutionScore;
|
|
||||||
self.intelligenceScore = monster.intelligenceScore;
|
|
||||||
self.wisdomScore = monster.wisdomScore;
|
|
||||||
self.charismaScore = monster.charismaScore;
|
|
||||||
self.armorType = monster.armorType;
|
|
||||||
self.otherArmorDescription = monster.otherArmorDescription;
|
|
||||||
self.shieldBonus = monster.shieldBonus;
|
|
||||||
self.customHP = monster.customHP;
|
|
||||||
self.hitDice = monster.hitDice;
|
|
||||||
self.hpText = monster.hpText;
|
|
||||||
self.baseSpeed = monster.baseSpeed;
|
|
||||||
self.burrowSpeed = monster.burrowSpeed;
|
|
||||||
self.climbSpeed = monster.climbSpeed;
|
|
||||||
self.flySpeed = monster.flySpeed;
|
|
||||||
self.canHover = monster.canHover;
|
|
||||||
self.swimSpeed = monster.swimSpeed;
|
|
||||||
self.hasCustomSpeed = monster.hasCustomSpeed;
|
|
||||||
self.customSpeed = monster.customSpeed;
|
|
||||||
self.armorType = monster.armorType;
|
|
||||||
self.naturalArmorBonus = monster.naturalArmorBonus;
|
|
||||||
self.hasShield = monster.hasShield;
|
|
||||||
self.customArmor = monster.customArmor;
|
|
||||||
self.strengthSavingThrowAdvantage = monster.strengthSavingThrowAdvantage;
|
|
||||||
self.strengthSavingThrowProficiency = monster.strengthSavingThrowProficiency;
|
|
||||||
self.dexteritySavingThrowAdvantage = monster.dexteritySavingThrowAdvantage;
|
|
||||||
self.dexteritySavingThrowProficiency = monster.dexteritySavingThrowProficiency;
|
|
||||||
self.constitutionSavingThrowAdvantage = monster.constitutionSavingThrowAdvantage;
|
|
||||||
self.constitutionSavingThrowProficiency = monster.constitutionSavingThrowProficiency;
|
|
||||||
self.intelligenceSavingThrowAdvantage = monster.intelligenceSavingThrowAdvantage;
|
|
||||||
self.intelligenceSavingThrowProficiency = monster.intelligenceSavingThrowProficiency;
|
|
||||||
self.wisdomSavingThrowAdvantage = monster.wisdomSavingThrowAdvantage;
|
|
||||||
self.wisdomSavingThrowProficiency = monster.wisdomSavingThrowProficiency;
|
|
||||||
self.charismaSavingThrowAdvantage = monster.charismaSavingThrowAdvantage;
|
|
||||||
self.charismaSavingThrowProficiency = monster.charismaSavingThrowProficiency;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
27
MonsterCards/Models/ProficiencyType.swift
Normal file
27
MonsterCards/Models/ProficiencyType.swift
Normal file
@@ -0,0 +1,27 @@
|
|||||||
|
//
|
||||||
|
// ProficiencyType.swift
|
||||||
|
// MonsterCards
|
||||||
|
//
|
||||||
|
// Created by Tom Hicks on 1/17/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
|
||||||
|
enum ProficiencyType: String, CaseIterable, Identifiable {
|
||||||
|
case none = "none"
|
||||||
|
case proficient = "proficient"
|
||||||
|
case expertise = "expertise"
|
||||||
|
|
||||||
|
var id: ProficiencyType { self }
|
||||||
|
|
||||||
|
var displayName: String {
|
||||||
|
switch self {
|
||||||
|
case .none:
|
||||||
|
return "None"
|
||||||
|
case .proficient:
|
||||||
|
return "Proficient"
|
||||||
|
case .expertise:
|
||||||
|
return "Expertise"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
//
|
|
||||||
// SavingThrow.h
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/4/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
@interface SavingThrow : NSObject
|
|
||||||
|
|
||||||
@property NSString* name;
|
|
||||||
@property int order;
|
|
||||||
|
|
||||||
-(id)initWithName: (NSString*)name andOrder: (int)order;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
||||||
@@ -1,31 +0,0 @@
|
|||||||
//
|
|
||||||
// SavingThrow.m
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/4/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "SavingThrow.h"
|
|
||||||
|
|
||||||
@implementation SavingThrow
|
|
||||||
|
|
||||||
-(id)init {
|
|
||||||
self = [super init];
|
|
||||||
|
|
||||||
self.name = @"";
|
|
||||||
self.order = -1;
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(id)initWithName: (NSString*)name andOrder: (int)order {
|
|
||||||
self = [super init];
|
|
||||||
|
|
||||||
self.name = name;
|
|
||||||
self.order = order;
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,27 +0,0 @@
|
|||||||
//
|
|
||||||
// Skill.h
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/4/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
#import "Monster.h"
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
@class Monster;
|
|
||||||
|
|
||||||
@interface Skill : NSObject
|
|
||||||
|
|
||||||
@property NSString* name;
|
|
||||||
@property NSString* abilityScoreName;
|
|
||||||
@property NSString* notes;
|
|
||||||
|
|
||||||
-(id)initWithName: (NSString*)name abilityScoreName:(NSString*)abilityScoreName andNotes:(NSString*)notes;
|
|
||||||
-(int)skillBonusForMonster: (Monster*)monster;
|
|
||||||
-(NSString*)textForMonster: (Monster*)monster;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
||||||
@@ -1,50 +0,0 @@
|
|||||||
//
|
|
||||||
// Skill.m
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/4/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "Skill.h"
|
|
||||||
|
|
||||||
@implementation Skill
|
|
||||||
|
|
||||||
-(id)init {
|
|
||||||
self = [super init];
|
|
||||||
|
|
||||||
self.name = @"";
|
|
||||||
self.abilityScoreName = @"";
|
|
||||||
self.notes = @"";
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(id)initWithName: (NSString*)name abilityScoreName:(NSString*)abilityScoreName andNotes:(NSString*)notes{
|
|
||||||
self = [super init];
|
|
||||||
|
|
||||||
self.name = name;
|
|
||||||
self.abilityScoreName = abilityScoreName;
|
|
||||||
self.notes = notes;
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(int)skillBonusForMonster: (Monster*)monster {
|
|
||||||
int bonus = [monster abilityModifierForAbilityScoreName: self.abilityScoreName];
|
|
||||||
if ([@" (ex)" isEqualToString:self.notes]) {
|
|
||||||
bonus += 2 * monster.proficiencyBonus;
|
|
||||||
} else {
|
|
||||||
bonus += monster.proficiencyBonus;
|
|
||||||
}
|
|
||||||
return bonus;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(NSString*)textForMonster: (Monster*)monster {
|
|
||||||
int bonus = [self skillBonusForMonster:monster];
|
|
||||||
|
|
||||||
// [self.name localizedUppercaseString]
|
|
||||||
return [NSString stringWithFormat:@"%@%@ %d", [[self.name substringToIndex:1] localizedUppercaseString], [self.name substringFromIndex:1], bonus];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,48 +1,56 @@
|
|||||||
<?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="17192" systemVersion="19H2" minimumToolsVersion="Automatic" sourceLanguage="Objective-C" usedWithCloudKit="YES" userDefinedModelVersionIdentifier="">
|
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="17192" systemVersion="19H114" 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="alignment" attributeType="String" defaultValueString=""/>
|
<attribute name="alignment" attributeType="String" defaultValueString=""/>
|
||||||
<attribute name="armorType" attributeType="String" defaultValueString="none"/>
|
<attribute name="armorType" attributeType="String" defaultValueString=""/>
|
||||||
<attribute name="baseSpeed" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
|
<attribute name="baseSpeed" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||||
<attribute name="burrowSpeed" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
|
<attribute name="blindsightDistance" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||||
|
<attribute name="burrowSpeed" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||||
<attribute name="canHover" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
<attribute name="canHover" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||||
<attribute name="charismaSavingThrowAdvantage" attributeType="String" defaultValueString="none"/>
|
<attribute name="challengeRating" attributeType="String" defaultValueString=""/>
|
||||||
|
<attribute name="charismaSavingThrowAdvantage" attributeType="String" defaultValueString=""/>
|
||||||
<attribute name="charismaSavingThrowProficiency" attributeType="String" defaultValueString="none"/>
|
<attribute name="charismaSavingThrowProficiency" attributeType="String" defaultValueString="none"/>
|
||||||
<attribute name="charismaScore" attributeType="Integer 16" defaultValueString="10" usesScalarValueType="YES"/>
|
<attribute name="charismaScore" attributeType="Integer 64" defaultValueString="10" usesScalarValueType="YES"/>
|
||||||
<attribute name="climbSpeed" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
|
<attribute name="climbSpeed" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||||
<attribute name="constitutionSavingThrowAdvantage" attributeType="String" defaultValueString="none"/>
|
<attribute name="constitutionSavingThrowAdvantage" attributeType="String" defaultValueString="none"/>
|
||||||
<attribute name="constitutionSavingThrowProficiency" attributeType="String" defaultValueString="none"/>
|
<attribute name="constitutionSavingThrowProficiency" attributeType="String" defaultValueString="none"/>
|
||||||
<attribute name="constitutionScore" attributeType="Integer 16" defaultValueString="10" usesScalarValueType="YES"/>
|
<attribute name="constitutionScore" attributeType="Integer 64" defaultValueString="10" usesScalarValueType="YES"/>
|
||||||
<attribute name="customArmor" attributeType="String" defaultValueString=""/>
|
<attribute name="customArmor" attributeType="String" defaultValueString=""/>
|
||||||
<attribute name="customHP" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
<attribute name="customHP" attributeType="String" defaultValueString=""/>
|
||||||
|
<attribute name="customProficiencyBonus" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||||
<attribute name="customSpeed" attributeType="String" defaultValueString=""/>
|
<attribute name="customSpeed" attributeType="String" defaultValueString=""/>
|
||||||
|
<attribute name="darkvisionDistance" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||||
<attribute name="dexteritySavingThrowAdvantage" attributeType="String" defaultValueString="none"/>
|
<attribute name="dexteritySavingThrowAdvantage" attributeType="String" defaultValueString="none"/>
|
||||||
<attribute name="dexteritySavingThrowProficiency" attributeType="String" defaultValueString="none" customClassName="none"/>
|
<attribute name="dexteritySavingThrowProficiency" attributeType="String" defaultValueString="none"/>
|
||||||
<attribute name="dexterityScore" attributeType="Integer 16" defaultValueString="10" usesScalarValueType="YES"/>
|
<attribute name="dexterityScore" attributeType="Integer 64" defaultValueString="10" usesScalarValueType="YES"/>
|
||||||
<attribute name="flySpeed" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
|
<attribute name="flySpeed" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||||
|
<attribute name="hasCustomHP" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||||
<attribute name="hasCustomSpeed" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
<attribute name="hasCustomSpeed" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||||
<attribute name="hasShield" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
<attribute name="hasShield" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||||
<attribute name="hitDice" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
|
<attribute name="hitDice" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||||
<attribute name="hpText" attributeType="String" defaultValueString=""/>
|
|
||||||
<attribute name="intelligenceSavingThrowAdvantage" attributeType="String" defaultValueString="none"/>
|
<attribute name="intelligenceSavingThrowAdvantage" attributeType="String" defaultValueString="none"/>
|
||||||
<attribute name="intelligenceSavingThrowProficiency" attributeType="String" defaultValueString="none"/>
|
<attribute name="intelligenceSavingThrowProficiency" attributeType="String" defaultValueString="none"/>
|
||||||
<attribute name="intelligenceScore" attributeType="Integer 16" defaultValueString="10" usesScalarValueType="YES"/>
|
<attribute name="intelligenceScore" attributeType="Integer 64" defaultValueString="10" usesScalarValueType="YES"/>
|
||||||
<attribute name="name" attributeType="String" defaultValueString=""/>
|
<attribute name="isBlind" attributeType="Boolean" defaultValueString="NO" usesScalarValueType="YES"/>
|
||||||
<attribute name="natrualArmorBonus" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
|
<attribute name="name" attributeType="String" defaultValueString="Unnamed Monster"/>
|
||||||
|
<attribute name="naturalArmorBonus" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||||
<attribute name="otherArmorDescription" attributeType="String" defaultValueString=""/>
|
<attribute name="otherArmorDescription" attributeType="String" defaultValueString=""/>
|
||||||
<attribute name="shieldBonus" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
|
<attribute name="shieldBonus" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
|
||||||
<attribute name="size" attributeType="String" defaultValueString=""/>
|
<attribute name="size" attributeType="String" defaultValueString=""/>
|
||||||
<attribute name="strengthSavingThrowAdvantage" attributeType="String" defaultValueString="none"/>
|
<attribute name="strengthSavingThrowAdvantage" attributeType="String" defaultValueString="none"/>
|
||||||
<attribute name="strengthSavingThrowProficiency" attributeType="String" defaultValueString="none"/>
|
<attribute name="strengthSavingThrowProficiency" attributeType="String" defaultValueString="none"/>
|
||||||
<attribute name="strengthScore" attributeType="Integer 16" 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 16" defaultValueString="0" usesScalarValueType="YES"/>
|
<attribute name="swimSpeed" 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="type" attributeType="String" defaultValueString=""/>
|
<attribute name="type" attributeType="String" defaultValueString=""/>
|
||||||
|
<attribute name="understandsBut" attributeType="String" defaultValueString=""/>
|
||||||
<attribute name="wisdomSavingThrowAdvantage" attributeType="String" defaultValueString="none"/>
|
<attribute name="wisdomSavingThrowAdvantage" attributeType="String" defaultValueString="none"/>
|
||||||
<attribute name="wisdomSavingThrowProficiency" attributeType="String" defaultValueString="none"/>
|
<attribute name="wisdomSavingThrowProficiency" attributeType="String" defaultValueString="none"/>
|
||||||
<attribute name="wisdomScore" attributeType="Integer 16" defaultValueString="10" usesScalarValueType="YES"/>
|
<attribute name="wisdomScore" attributeType="Integer 64" defaultValueString="10" usesScalarValueType="YES"/>
|
||||||
</entity>
|
</entity>
|
||||||
<elements>
|
<elements>
|
||||||
<element name="Monster" positionX="-63" positionY="-18" width="128" height="643"/>
|
<element name="Monster" positionX="-63" positionY="-18" width="128" height="763"/>
|
||||||
</elements>
|
</elements>
|
||||||
</model>
|
</model>
|
||||||
20
MonsterCards/MonsterCardsApp.swift
Normal file
20
MonsterCards/MonsterCardsApp.swift
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
//
|
||||||
|
// MonsterCardsApp.swift
|
||||||
|
// MonsterCards
|
||||||
|
//
|
||||||
|
// Created by Tom Hicks on 1/15/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
@main
|
||||||
|
struct MonsterCardsApp: App {
|
||||||
|
let persistenceController = PersistenceController.shared
|
||||||
|
|
||||||
|
var body: some Scene {
|
||||||
|
WindowGroup {
|
||||||
|
ContentView()
|
||||||
|
.environment(\.managedObjectContext, persistenceController.container.viewContext)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
58
MonsterCards/Persistence.swift
Normal file
58
MonsterCards/Persistence.swift
Normal file
@@ -0,0 +1,58 @@
|
|||||||
|
//
|
||||||
|
// Persistence.swift
|
||||||
|
// MonsterCards
|
||||||
|
//
|
||||||
|
// Created by Tom Hicks on 1/15/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
import CoreData
|
||||||
|
|
||||||
|
struct PersistenceController {
|
||||||
|
static let shared = PersistenceController()
|
||||||
|
|
||||||
|
static var preview: PersistenceController = {
|
||||||
|
let result = PersistenceController(inMemory: true)
|
||||||
|
let viewContext = result.container.viewContext
|
||||||
|
let monsters: [Monster] = [
|
||||||
|
Monster(context:viewContext, name: "Ted", size: "Huge", type: "humanoid", subtype: "any race", alignment: "any alignment"),
|
||||||
|
Monster(context:viewContext, name: "Steve", size: "Huge", type: "humanoid", subtype: "any race", alignment: "any alignment"),
|
||||||
|
Monster(context:viewContext, name: "Dave", size: "Huge", type: "humanoid", subtype: "any race", alignment: "any alignment")
|
||||||
|
]
|
||||||
|
do {
|
||||||
|
try viewContext.save()
|
||||||
|
} catch {
|
||||||
|
// 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.
|
||||||
|
let nsError = error as NSError
|
||||||
|
// fatalError("TOMHICKS_Unresolved error \(nsError), \(nsError.userInfo)")
|
||||||
|
}
|
||||||
|
return result
|
||||||
|
}()
|
||||||
|
|
||||||
|
let container: NSPersistentCloudKitContainer
|
||||||
|
|
||||||
|
init(inMemory: Bool = false) {
|
||||||
|
container = NSPersistentCloudKitContainer(name: "MonsterCards")
|
||||||
|
if inMemory {
|
||||||
|
container.persistentStoreDescriptions.first!.url = URL(fileURLWithPath: "/dev/null")
|
||||||
|
}
|
||||||
|
container.loadPersistentStores(completionHandler: { (storeDescription, error) in
|
||||||
|
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)")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,6 @@
|
|||||||
|
{
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,16 +0,0 @@
|
|||||||
//
|
|
||||||
// SceneDelegate.h
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/2/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
|
||||||
|
|
||||||
@interface SceneDelegate : UIResponder <UIWindowSceneDelegate>
|
|
||||||
|
|
||||||
@property (strong, nonatomic) UIWindow * window;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@@ -1,62 +0,0 @@
|
|||||||
//
|
|
||||||
// SceneDelegate.m
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/2/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "SceneDelegate.h"
|
|
||||||
#import "AppDelegate.h"
|
|
||||||
|
|
||||||
@interface SceneDelegate ()
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation SceneDelegate
|
|
||||||
|
|
||||||
|
|
||||||
- (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions {
|
|
||||||
// Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`.
|
|
||||||
// If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
|
|
||||||
// This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead).
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
- (void)sceneDidDisconnect:(UIScene *)scene {
|
|
||||||
// Called as the scene is being released by the system.
|
|
||||||
// This occurs shortly after the scene enters the background, or when its session is discarded.
|
|
||||||
// Release any resources associated with this scene that can be re-created the next time the scene connects.
|
|
||||||
// The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead).
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
- (void)sceneDidBecomeActive:(UIScene *)scene {
|
|
||||||
// Called when the scene has moved from an inactive state to an active state.
|
|
||||||
// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
- (void)sceneWillResignActive:(UIScene *)scene {
|
|
||||||
// Called when the scene will move from an active state to an inactive state.
|
|
||||||
// This may occur due to temporary interruptions (ex. an incoming phone call).
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
- (void)sceneWillEnterForeground:(UIScene *)scene {
|
|
||||||
// Called as the scene transitions from the background to the foreground.
|
|
||||||
// Use this method to undo the changes made on entering the background.
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
- (void)sceneDidEnterBackground:(UIScene *)scene {
|
|
||||||
// Called as the scene transitions from the foreground to the background.
|
|
||||||
// Use this method to save data, release shared resources, and store enough scene-specific state information
|
|
||||||
// to restore the scene back to its current state.
|
|
||||||
|
|
||||||
// Save changes in the application's managed object context when the application transitions to the background.
|
|
||||||
[(AppDelegate *)UIApplication.sharedApplication.delegate saveContext];
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@end
|
|
||||||
21
MonsterCards/Views/Collections.swift
Normal file
21
MonsterCards/Views/Collections.swift
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
//
|
||||||
|
// Collections.swift
|
||||||
|
// MonsterCards
|
||||||
|
//
|
||||||
|
// Created by Tom Hicks on 1/15/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct Collections: View {
|
||||||
|
// @State var allCollections: [MonsterCollection] = []
|
||||||
|
var body: some View {
|
||||||
|
Text("Collections")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Collections_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
Collections().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
//
|
|
||||||
// CollectionsViewController.h
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/4/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
@interface CollectionsViewController : UIViewController
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
//
|
|
||||||
// CollectionsViewController.m
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/4/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "CollectionsViewController.h"
|
|
||||||
|
|
||||||
@interface CollectionsViewController ()
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation CollectionsViewController
|
|
||||||
|
|
||||||
- (void)viewDidLoad {
|
|
||||||
[super viewDidLoad];
|
|
||||||
// Do any additional setup after loading the view.
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
#pragma mark - Navigation
|
|
||||||
|
|
||||||
// In a storyboard-based application, you will often want to do a little preparation before navigation
|
|
||||||
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
|
|
||||||
// Get the new view controller using [segue destinationViewController].
|
|
||||||
// Pass the selected object to the new view controller.
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
@end
|
|
||||||
106
MonsterCards/Views/ContentView.swift
Normal file
106
MonsterCards/Views/ContentView.swift
Normal file
@@ -0,0 +1,106 @@
|
|||||||
|
//
|
||||||
|
// ContentView.swift
|
||||||
|
// MonsterCards
|
||||||
|
//
|
||||||
|
// Created by Tom Hicks on 1/15/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
import CoreData
|
||||||
|
|
||||||
|
struct ContentView: View {
|
||||||
|
@Environment(\.managedObjectContext) private var viewContext
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
TabView {
|
||||||
|
Search()
|
||||||
|
.tabItem {
|
||||||
|
Image(systemName: "magnifyingglass")
|
||||||
|
Text("Search")
|
||||||
|
}
|
||||||
|
Dashboard()
|
||||||
|
.tabItem {
|
||||||
|
Image(systemName: "rectangle.3.offgrid.fill")
|
||||||
|
Text("Dashboard")
|
||||||
|
|
||||||
|
}
|
||||||
|
Collections()
|
||||||
|
.tabItem {
|
||||||
|
Image(systemName: "tray.full.fill")
|
||||||
|
Text("Collections")
|
||||||
|
}
|
||||||
|
Library()
|
||||||
|
.tabItem {
|
||||||
|
Image(systemName: "book.fill")
|
||||||
|
Text("Library")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// @FetchRequest(
|
||||||
|
// sortDescriptors: [NSSortDescriptor(keyPath: \Item.timestamp, ascending: true)],
|
||||||
|
// animation: .default)
|
||||||
|
// private var items: FetchedResults<Item>
|
||||||
|
//
|
||||||
|
// var body: some View {
|
||||||
|
// List {
|
||||||
|
// ForEach(items) { item in
|
||||||
|
// Text("Item at \(item.timestamp!, formatter: itemFormatter)")
|
||||||
|
// }
|
||||||
|
// .onDelete(perform: deleteItems)
|
||||||
|
// }
|
||||||
|
// .toolbar {
|
||||||
|
// #if os(iOS)
|
||||||
|
// EditButton()
|
||||||
|
// #endif
|
||||||
|
//
|
||||||
|
// Button(action: addItem) {
|
||||||
|
// Label("Add Item", systemImage: "plus")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private func addItem() {
|
||||||
|
// withAnimation {
|
||||||
|
// let newItem = Item(context: viewContext)
|
||||||
|
// newItem.timestamp = Date()
|
||||||
|
//
|
||||||
|
// do {
|
||||||
|
// try viewContext.save()
|
||||||
|
// } catch {
|
||||||
|
// // 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.
|
||||||
|
// let nsError = error as NSError
|
||||||
|
// fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// private func deleteItems(offsets: IndexSet) {
|
||||||
|
// withAnimation {
|
||||||
|
// offsets.map { items[$0] }.forEach(viewContext.delete)
|
||||||
|
//
|
||||||
|
// do {
|
||||||
|
// try viewContext.save()
|
||||||
|
// } catch {
|
||||||
|
// // 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.
|
||||||
|
// let nsError = error as NSError
|
||||||
|
// fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
//private let itemFormatter: DateFormatter = {
|
||||||
|
// let formatter = DateFormatter()
|
||||||
|
// formatter.dateStyle = .short
|
||||||
|
// formatter.timeStyle = .medium
|
||||||
|
// return formatter
|
||||||
|
//}()
|
||||||
|
|
||||||
|
struct ContentView_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
ContentView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
|
||||||
|
}
|
||||||
|
}
|
||||||
20
MonsterCards/Views/Dashboard.swift
Normal file
20
MonsterCards/Views/Dashboard.swift
Normal file
@@ -0,0 +1,20 @@
|
|||||||
|
//
|
||||||
|
// Dashboard.swift
|
||||||
|
// MonsterCards
|
||||||
|
//
|
||||||
|
// Created by Tom Hicks on 1/15/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct Dashboard: View {
|
||||||
|
var body: some View {
|
||||||
|
Text("Dashboard")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Dashboard_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
Dashboard().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
//
|
|
||||||
// DashboardViewController.h
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/4/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
@interface DashboardViewController : UIViewController
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
||||||
@@ -1,32 +0,0 @@
|
|||||||
//
|
|
||||||
// DashboardViewController.m
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/4/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "DashboardViewController.h"
|
|
||||||
|
|
||||||
@interface DashboardViewController ()
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation DashboardViewController
|
|
||||||
|
|
||||||
- (void)viewDidLoad {
|
|
||||||
[super viewDidLoad];
|
|
||||||
// Do any additional setup after loading the view.
|
|
||||||
}
|
|
||||||
|
|
||||||
/*
|
|
||||||
#pragma mark - Navigation
|
|
||||||
|
|
||||||
// In a storyboard-based application, you will often want to do a little preparation before navigation
|
|
||||||
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
|
|
||||||
// Get the new view controller using [segue destinationViewController].
|
|
||||||
// Pass the selected object to the new view controller.
|
|
||||||
}
|
|
||||||
*/
|
|
||||||
|
|
||||||
@end
|
|
||||||
425
MonsterCards/Views/EditMonster.swift
Normal file
425
MonsterCards/Views/EditMonster.swift
Normal file
@@ -0,0 +1,425 @@
|
|||||||
|
//
|
||||||
|
// EditMonster.swift
|
||||||
|
// MonsterCards
|
||||||
|
//
|
||||||
|
// Created by Tom Hicks on 1/16/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
import CoreData
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct EditMonster: View {
|
||||||
|
// TODO: add challengeRating/challengeRatingEnum and customChallengeRating maybe in basicInfo
|
||||||
|
@Environment(\.presentationMode) var presentationMode: Binding<PresentationMode>
|
||||||
|
@Environment(\.managedObjectContext) private var viewContext
|
||||||
|
|
||||||
|
var monster: Monster
|
||||||
|
|
||||||
|
@State private var monsterName: String = ""
|
||||||
|
@State private var monsterSize: String = ""
|
||||||
|
@State private var monsterType: String = ""
|
||||||
|
@State private var monsterSubtype: String = ""
|
||||||
|
@State private var monsterAlignment: String = ""
|
||||||
|
@State private var monsterHitDice: Int64 = 0
|
||||||
|
@State private var monsterHasCustomHP: Bool = false
|
||||||
|
@State private var monsterCustomHP: String = ""
|
||||||
|
@State private var monsterArmorType: String = ""
|
||||||
|
@State private var monsterHasShield: Bool = false
|
||||||
|
@State private var monsterNaturalArmorBonus: Int64 = 0
|
||||||
|
@State private var monsterCustomArmor: String = ""
|
||||||
|
@State private var monsterBaseSpeed: Int64 = 0
|
||||||
|
@State private var monsterBurrowSpeed: Int64 = 0
|
||||||
|
@State private var monsterClimbSpeed: Int64 = 0
|
||||||
|
@State private var monsterFlySpeed: Int64 = 0
|
||||||
|
@State private var monsterCanHover: Bool = false
|
||||||
|
@State private var monsterSwimSpeed: Int64 = 0
|
||||||
|
@State private var monsterHasCustomSpeed: Bool = false
|
||||||
|
@State private var monsterCustomSpeed: String = ""
|
||||||
|
@State private var monsterStrengthScore: Int64 = 10
|
||||||
|
@State private var monsterDexterityScore: Int64 = 10
|
||||||
|
@State private var monsterConstitutionScore: Int64 = 10
|
||||||
|
@State private var monsterIntelligenceScore: Int64 = 10
|
||||||
|
@State private var monsterWisdomScore: Int64 = 10
|
||||||
|
@State private var monsterCharismaScore: Int64 = 10
|
||||||
|
|
||||||
|
@State private var monsterStrengthSavingThrowProficiency: ProficiencyType = .none
|
||||||
|
@State private var monsterStrengthSavingThrowAdvantage: AdvantageType = .none
|
||||||
|
@State private var monsterDexteritySavingThrowProficiency: ProficiencyType = .none
|
||||||
|
@State private var monsterDexteritySavingThrowAdvantage: AdvantageType = .none
|
||||||
|
@State private var monsterConstitutionSavingThrowProficiency: ProficiencyType = .none
|
||||||
|
@State private var monsterConstitutionSavingThrowAdvantage: AdvantageType = .none
|
||||||
|
@State private var monsterIntelligenceSavingThrowProficiency: ProficiencyType = .none
|
||||||
|
@State private var monsterIntelligenceSavingThrowAdvantage: AdvantageType = .none
|
||||||
|
@State private var monsterWisdomSavingThrowProficiency: ProficiencyType = .none
|
||||||
|
@State private var monsterWisdomSavingThrowAdvantage: AdvantageType = .none
|
||||||
|
@State private var monsterCharismaSavingThrowProficiency: ProficiencyType = .none
|
||||||
|
@State private var monsterCharismaSavingThrowAdvantage: AdvantageType = .none
|
||||||
|
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
List {
|
||||||
|
Section(header: Text("Basic Info")) {
|
||||||
|
// Editable Text field bound to monster.name
|
||||||
|
MCTextField(
|
||||||
|
label: "Name",
|
||||||
|
value: $monsterName)
|
||||||
|
|
||||||
|
// Editable Text field bound to monster.size
|
||||||
|
MCTextField(
|
||||||
|
label: "Size",
|
||||||
|
value: $monsterSize)
|
||||||
|
|
||||||
|
// Editable Text field bound to monster.type
|
||||||
|
MCTextField(
|
||||||
|
label: "Type",
|
||||||
|
value: $monsterType)
|
||||||
|
|
||||||
|
// Editable Text field bound to monster.subType
|
||||||
|
MCTextField(
|
||||||
|
label: "Subtype",
|
||||||
|
value: $monsterSubtype)
|
||||||
|
|
||||||
|
// Editable Text field bound to monster.alignment
|
||||||
|
MCTextField(
|
||||||
|
label: "Alignment",
|
||||||
|
value: $monsterAlignment)
|
||||||
|
|
||||||
|
// Number with -/+ buttons bound to monster.hitDice
|
||||||
|
MCStepperField(
|
||||||
|
label: "Hit Dice",
|
||||||
|
value: $monsterHitDice)
|
||||||
|
|
||||||
|
// Toggle bound to monster.hasCustomHP?
|
||||||
|
Toggle(
|
||||||
|
"Has Custom HP",
|
||||||
|
isOn:$monsterHasCustomHP)
|
||||||
|
|
||||||
|
// Editable Text field bound to monster.customHpText?
|
||||||
|
MCTextField(
|
||||||
|
label: "Custom HP",
|
||||||
|
value: $monsterCustomHP)
|
||||||
|
|
||||||
|
}
|
||||||
|
.textCase(nil)
|
||||||
|
|
||||||
|
Section(header: Text("Armor")) {
|
||||||
|
// Armor Type select bound to monster.armorType?
|
||||||
|
// TODO: this should be a select/dropdown
|
||||||
|
MCTextField(
|
||||||
|
label: "Armor Type",
|
||||||
|
value: $monsterArmorType)
|
||||||
|
|
||||||
|
// Toggle bound to monster.hasShield?
|
||||||
|
Toggle(
|
||||||
|
"Has Shield",
|
||||||
|
isOn: $monsterHasShield)
|
||||||
|
|
||||||
|
// Number with -/+ buttons bound to monster.naturalArmorBonus
|
||||||
|
MCStepperField(
|
||||||
|
label: "Natural Armor Bonus",
|
||||||
|
value: $monsterNaturalArmorBonus)
|
||||||
|
|
||||||
|
// Editable Text field bound to monster.customArmorText?
|
||||||
|
MCTextField(
|
||||||
|
label: "Custom Armor",
|
||||||
|
value: $monsterCustomArmor)
|
||||||
|
|
||||||
|
}
|
||||||
|
.textCase(nil)
|
||||||
|
|
||||||
|
Section(header: Text("Speed")) {
|
||||||
|
// Number bound to monster.baseSpeed
|
||||||
|
MCStepperField(
|
||||||
|
label: "Base",
|
||||||
|
step: 5,
|
||||||
|
suffix: " ft.",
|
||||||
|
value: $monsterBaseSpeed)
|
||||||
|
|
||||||
|
// Number bound to monster.burrowSpeed
|
||||||
|
MCStepperField(
|
||||||
|
label: "Burrow",
|
||||||
|
step: 5,
|
||||||
|
suffix: " ft.",
|
||||||
|
value: $monsterBurrowSpeed)
|
||||||
|
|
||||||
|
// Number bound to monster.climbSpeed
|
||||||
|
MCStepperField(
|
||||||
|
label: "Climb",
|
||||||
|
step: 5,
|
||||||
|
suffix: " ft.",
|
||||||
|
value: $monsterClimbSpeed)
|
||||||
|
|
||||||
|
// Number bound to monster.flySpeed
|
||||||
|
MCStepperField(
|
||||||
|
label: "Fly",
|
||||||
|
step: 5,
|
||||||
|
suffix: " ft.",
|
||||||
|
value: $monsterFlySpeed)
|
||||||
|
|
||||||
|
// Toggle bound to monster.canHover
|
||||||
|
Toggle(
|
||||||
|
"Can Hover",
|
||||||
|
isOn: $monsterCanHover)
|
||||||
|
|
||||||
|
// Number bound to monster.swimSpeed
|
||||||
|
MCStepperField(
|
||||||
|
label: "Swim",
|
||||||
|
step: 5,
|
||||||
|
suffix: " ft.",
|
||||||
|
value: $monsterSwimSpeed)
|
||||||
|
|
||||||
|
// Toggle bound to monster.hasCustomSpeed
|
||||||
|
Toggle(
|
||||||
|
"Has Custom Speed",
|
||||||
|
isOn: $monsterHasCustomSpeed)
|
||||||
|
|
||||||
|
// Editable Text field bound to monster.customSpeedText
|
||||||
|
MCTextField(
|
||||||
|
label: "Custom Speed",
|
||||||
|
value: $monsterCustomSpeed)
|
||||||
|
}
|
||||||
|
.textCase(nil)
|
||||||
|
|
||||||
|
Section(header: Text("Ability Scores")) {
|
||||||
|
MCStepperField(
|
||||||
|
label: "STR",
|
||||||
|
value: $monsterStrengthScore)
|
||||||
|
MCStepperField(
|
||||||
|
label: "DEX",
|
||||||
|
value: $monsterDexterityScore)
|
||||||
|
MCStepperField(
|
||||||
|
label: "CON",
|
||||||
|
value: $monsterConstitutionScore)
|
||||||
|
MCStepperField(
|
||||||
|
label: "INT",
|
||||||
|
value: $monsterIntelligenceScore)
|
||||||
|
MCStepperField(
|
||||||
|
label: "WIS",
|
||||||
|
value: $monsterWisdomScore)
|
||||||
|
MCStepperField(
|
||||||
|
label: "CHA",
|
||||||
|
value: $monsterCharismaScore)
|
||||||
|
}
|
||||||
|
.textCase(nil)
|
||||||
|
|
||||||
|
Section(header: Text("Saving Throws")) {
|
||||||
|
VStack {
|
||||||
|
MCAdvantagePicker(
|
||||||
|
label: "Strength Advantage",
|
||||||
|
value: $monsterStrengthSavingThrowAdvantage)
|
||||||
|
|
||||||
|
MCProficiencyPicker(
|
||||||
|
label: "Strength Proficiency",
|
||||||
|
value: $monsterStrengthSavingThrowProficiency)
|
||||||
|
}
|
||||||
|
VStack {
|
||||||
|
MCAdvantagePicker(
|
||||||
|
label: "Dexterity Advantage",
|
||||||
|
value: $monsterDexteritySavingThrowAdvantage)
|
||||||
|
|
||||||
|
MCProficiencyPicker(
|
||||||
|
label: "Dexterity Proficiency",
|
||||||
|
value: $monsterDexteritySavingThrowProficiency)
|
||||||
|
}
|
||||||
|
VStack {
|
||||||
|
MCAdvantagePicker(
|
||||||
|
label: "Constitution Advantage",
|
||||||
|
value: $monsterConstitutionSavingThrowAdvantage)
|
||||||
|
|
||||||
|
MCProficiencyPicker(
|
||||||
|
label: "Constitution Proficiency",
|
||||||
|
value: $monsterConstitutionSavingThrowProficiency)
|
||||||
|
}
|
||||||
|
VStack {
|
||||||
|
MCAdvantagePicker(
|
||||||
|
label: "Intelligence Advantage",
|
||||||
|
value: $monsterIntelligenceSavingThrowAdvantage)
|
||||||
|
|
||||||
|
MCProficiencyPicker(
|
||||||
|
label: "Intelligence Proficiency",
|
||||||
|
value: $monsterIntelligenceSavingThrowProficiency)
|
||||||
|
}
|
||||||
|
VStack {
|
||||||
|
MCAdvantagePicker(
|
||||||
|
label: "Wisdom Advantage",
|
||||||
|
value: $monsterWisdomSavingThrowAdvantage)
|
||||||
|
|
||||||
|
MCProficiencyPicker(
|
||||||
|
label: "Wisdom Proficiency",
|
||||||
|
value: $monsterWisdomSavingThrowProficiency)
|
||||||
|
}
|
||||||
|
VStack {
|
||||||
|
MCAdvantagePicker(
|
||||||
|
label: "Charisma Advantage",
|
||||||
|
value: $monsterCharismaSavingThrowAdvantage)
|
||||||
|
|
||||||
|
MCProficiencyPicker(
|
||||||
|
label: "Charisma Proficiency",
|
||||||
|
value: $monsterCharismaSavingThrowProficiency)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.textCase(nil)
|
||||||
|
}
|
||||||
|
.onAppear(perform: copyMonsterToLocal)
|
||||||
|
.toolbar(content: {
|
||||||
|
ToolbarItem(placement: .primaryAction) {
|
||||||
|
Button("Save", action: saveMonster)
|
||||||
|
}
|
||||||
|
ToolbarItem(placement: ToolbarItemPlacement.cancellationAction) {
|
||||||
|
Button("Cancel", action: cancel)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.navigationTitle(monster.name ?? "")
|
||||||
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
.navigationBarBackButtonHidden(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func dismissView() {
|
||||||
|
self.presentationMode.wrappedValue.dismiss()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func saveMonster() {
|
||||||
|
copyLocalToMonster()
|
||||||
|
|
||||||
|
do {
|
||||||
|
try viewContext.save()
|
||||||
|
} catch {
|
||||||
|
// 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.
|
||||||
|
let nsError = error as NSError
|
||||||
|
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
|
||||||
|
}
|
||||||
|
// TODO: save coredata context
|
||||||
|
dismissView()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func cancel() {
|
||||||
|
dismissView()
|
||||||
|
}
|
||||||
|
|
||||||
|
private func copyMonsterToLocal() {
|
||||||
|
monsterName = monster.name ?? ""
|
||||||
|
monsterSize = monster.size ?? ""
|
||||||
|
monsterType = monster.type ?? ""
|
||||||
|
monsterSubtype = monster.subtype ?? ""
|
||||||
|
monsterAlignment = monster.alignment ?? ""
|
||||||
|
monsterHitDice = monster.hitDice
|
||||||
|
monsterHasCustomHP = monster.hasCustomHP
|
||||||
|
monsterCustomHP = monster.customHP ?? ""
|
||||||
|
monsterArmorType = monster.armorType ?? ""
|
||||||
|
monsterHasShield = monster.hasShield
|
||||||
|
monsterNaturalArmorBonus = monster.naturalArmorBonus
|
||||||
|
monsterCustomArmor = monster.customArmor ?? ""
|
||||||
|
monsterBaseSpeed = monster.baseSpeed
|
||||||
|
monsterBurrowSpeed = monster.burrowSpeed
|
||||||
|
monsterClimbSpeed = monster.climbSpeed
|
||||||
|
monsterFlySpeed = monster.flySpeed
|
||||||
|
monsterCanHover = monster.canHover
|
||||||
|
monsterSwimSpeed = monster.swimSpeed
|
||||||
|
monsterHasCustomSpeed = monster.hasCustomSpeed
|
||||||
|
monsterCustomSpeed = monster.customSpeed ?? ""
|
||||||
|
monsterStrengthScore = monster.strengthScore
|
||||||
|
monsterDexterityScore = monster.dexterityScore
|
||||||
|
monsterConstitutionScore = monster.constitutionScore
|
||||||
|
monsterIntelligenceScore = monster.intelligenceScore
|
||||||
|
monsterWisdomScore = monster.wisdomScore
|
||||||
|
monsterCharismaScore = monster.charismaScore
|
||||||
|
monsterStrengthSavingThrowProficiency = ProficiencyType.init(rawValue: monster.strengthSavingThrowProficiency ?? "") ?? .none
|
||||||
|
monsterStrengthSavingThrowAdvantage = AdvantageType(rawValue: monster.strengthSavingThrowAdvantage ?? "") ?? .none
|
||||||
|
monsterDexteritySavingThrowProficiency = ProficiencyType(rawValue: monster.dexteritySavingThrowProficiency ?? "") ?? .none
|
||||||
|
monsterDexteritySavingThrowAdvantage = AdvantageType(rawValue: monster.dexteritySavingThrowAdvantage ?? "") ?? .none
|
||||||
|
monsterConstitutionSavingThrowProficiency = ProficiencyType(rawValue: monster.constitutionSavingThrowProficiency ?? "") ?? .none
|
||||||
|
monsterConstitutionSavingThrowAdvantage = AdvantageType(rawValue: monster.constitutionSavingThrowAdvantage ?? "") ?? .none
|
||||||
|
monsterIntelligenceSavingThrowProficiency = ProficiencyType(rawValue: monster.intelligenceSavingThrowProficiency ?? "") ?? .none
|
||||||
|
monsterIntelligenceSavingThrowAdvantage = AdvantageType(rawValue: monster.intelligenceSavingThrowAdvantage ?? "") ?? .none
|
||||||
|
monsterWisdomSavingThrowProficiency = ProficiencyType(rawValue: monster.wisdomSavingThrowProficiency ?? "") ?? .none
|
||||||
|
monsterWisdomSavingThrowAdvantage = AdvantageType(rawValue: monster.wisdomSavingThrowAdvantage ?? "") ?? .none
|
||||||
|
monsterCharismaSavingThrowProficiency = ProficiencyType(rawValue: monster.charismaSavingThrowProficiency ?? "") ?? .none
|
||||||
|
monsterCharismaSavingThrowAdvantage = AdvantageType(rawValue: monster.charismaSavingThrowAdvantage ?? "") ?? .none
|
||||||
|
}
|
||||||
|
|
||||||
|
private func copyLocalToMonster() {
|
||||||
|
monster.name = monsterName
|
||||||
|
monster.size = monsterSize
|
||||||
|
monster.type = monsterType
|
||||||
|
monster.subtype = monsterSubtype
|
||||||
|
monster.alignment = monsterAlignment
|
||||||
|
monster.hitDice = monsterHitDice
|
||||||
|
monster.hasCustomHP = monsterHasCustomHP
|
||||||
|
monster.customHP = monsterCustomHP
|
||||||
|
monster.armorType = monsterArmorType
|
||||||
|
monster.hasShield = monsterHasShield
|
||||||
|
monster.naturalArmorBonus = monsterNaturalArmorBonus
|
||||||
|
monster.customArmor = monsterCustomArmor
|
||||||
|
monster.baseSpeed = monsterBaseSpeed
|
||||||
|
monster.burrowSpeed = monsterBurrowSpeed
|
||||||
|
monster.climbSpeed = monsterClimbSpeed
|
||||||
|
monster.flySpeed = monsterFlySpeed
|
||||||
|
monster.canHover = monsterCanHover
|
||||||
|
monster.swimSpeed = monsterSwimSpeed
|
||||||
|
monster.hasCustomSpeed = monsterHasCustomSpeed
|
||||||
|
monster.customSpeed = monsterCustomSpeed
|
||||||
|
monster.strengthScore = monsterStrengthScore
|
||||||
|
monster.dexterityScore = monsterDexterityScore
|
||||||
|
monster.constitutionScore = monsterConstitutionScore
|
||||||
|
monster.intelligenceScore = monsterIntelligenceScore
|
||||||
|
monster.wisdomScore = monsterWisdomScore
|
||||||
|
monster.charismaScore = monsterCharismaScore
|
||||||
|
monster.strengthSavingThrowProficiency = monsterStrengthSavingThrowProficiency.rawValue
|
||||||
|
monster.strengthSavingThrowAdvantage = monsterStrengthSavingThrowAdvantage.rawValue
|
||||||
|
monster.dexteritySavingThrowProficiency = monsterDexteritySavingThrowProficiency.rawValue
|
||||||
|
monster.dexteritySavingThrowAdvantage = monsterDexteritySavingThrowAdvantage.rawValue
|
||||||
|
monster.constitutionSavingThrowProficiency = monsterConstitutionSavingThrowProficiency.rawValue
|
||||||
|
monster.constitutionSavingThrowAdvantage = monsterConstitutionSavingThrowAdvantage.rawValue
|
||||||
|
monster.intelligenceSavingThrowProficiency = monsterIntelligenceSavingThrowProficiency.rawValue
|
||||||
|
monster.intelligenceSavingThrowAdvantage = monsterIntelligenceSavingThrowAdvantage.rawValue
|
||||||
|
monster.wisdomSavingThrowProficiency = monsterWisdomSavingThrowProficiency.rawValue
|
||||||
|
monster.wisdomSavingThrowAdvantage = monsterWisdomSavingThrowAdvantage.rawValue
|
||||||
|
monster.charismaSavingThrowProficiency = monsterCharismaSavingThrowProficiency.rawValue
|
||||||
|
monster.charismaSavingThrowAdvantage = monsterCharismaSavingThrowAdvantage.rawValue
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct EditMonster_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
let context = PersistenceController.preview.container.viewContext
|
||||||
|
let monster = Monster.init(context: context)
|
||||||
|
|
||||||
|
monster.name = "Steve"
|
||||||
|
monster.size = "Medium"
|
||||||
|
monster.type = "humanoid"
|
||||||
|
monster.subtype = "human"
|
||||||
|
monster.alignment = "LG"
|
||||||
|
monster.hitDice = 6
|
||||||
|
monster.hasCustomHP = true
|
||||||
|
monster.customHP = "12 (1d10)+2"
|
||||||
|
monster.baseSpeed = 5
|
||||||
|
monster.burrowSpeed = 10
|
||||||
|
monster.climbSpeed = 15
|
||||||
|
monster.flySpeed = 20
|
||||||
|
monster.swimSpeed = 25
|
||||||
|
monster.canHover = true
|
||||||
|
monster.hasCustomSpeed = false
|
||||||
|
monster.customSpeed = "walk: 5 ft."
|
||||||
|
monster.strengthScore = 8
|
||||||
|
monster.dexterityScore = 10
|
||||||
|
monster.constitutionScore = 12
|
||||||
|
monster.intelligenceScore = 14
|
||||||
|
monster.wisdomScore = 16
|
||||||
|
monster.charismaScore = 18
|
||||||
|
monster.strengthSavingThrowAdvantage = AdvantageType.none.rawValue
|
||||||
|
monster.strengthSavingThrowProficiency = ProficiencyType.none.rawValue
|
||||||
|
monster.dexteritySavingThrowAdvantage = AdvantageType.advantage.rawValue
|
||||||
|
monster.dexteritySavingThrowProficiency = ProficiencyType.proficient.rawValue
|
||||||
|
monster.constitutionSavingThrowAdvantage = AdvantageType.disadvantage.rawValue
|
||||||
|
monster.constitutionSavingThrowProficiency = ProficiencyType.expertise.rawValue
|
||||||
|
monster.intelligenceSavingThrowAdvantage = AdvantageType.none.rawValue
|
||||||
|
monster.intelligenceSavingThrowProficiency = ProficiencyType.expertise.rawValue
|
||||||
|
monster.wisdomSavingThrowAdvantage = AdvantageType.advantage.rawValue
|
||||||
|
monster.wisdomSavingThrowProficiency = ProficiencyType.proficient.rawValue
|
||||||
|
monster.charismaSavingThrowAdvantage = AdvantageType.disadvantage.rawValue
|
||||||
|
monster.charismaSavingThrowProficiency = ProficiencyType.none.rawValue
|
||||||
|
|
||||||
|
return EditMonster(monster: monster).environment(\.managedObjectContext, context)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
//
|
|
||||||
// EditMonsterViewController.h
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/8/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
|
||||||
#import "Monster.h"
|
|
||||||
#import "MCShortStringFieldTableViewCell.h"
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
@interface EditMonsterViewController : UIViewController <UITableViewDelegate, UITableViewDataSource, MCFormFieldDelegate>
|
|
||||||
|
|
||||||
@property Monster* originalMonster;
|
|
||||||
@property (weak, nonatomic) IBOutlet UITableView *monsterTableView;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
||||||
@@ -1,719 +0,0 @@
|
|||||||
//
|
|
||||||
// EditMonsterViewController.m
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/8/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "EditMonsterViewController.h"
|
|
||||||
#import "MCBooleanFieldTableViewCell.h"
|
|
||||||
#import "MCIntegerFieldTableViewCell.h"
|
|
||||||
#import "MCRadioFieldTableViewCell.h"
|
|
||||||
#import "MCSelectFieldTableViewCell.h"
|
|
||||||
#import "MCShortStringFieldTableViewCell.h"
|
|
||||||
#import "AppDelegate.h"
|
|
||||||
|
|
||||||
@interface EditMonsterViewController ()
|
|
||||||
|
|
||||||
@property Monster* editingMonster;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
const int kSectionIndexBasicInfo = 0;
|
|
||||||
const int kSectionIndexArmor = 1;
|
|
||||||
const int kSectionIndexSpeed = 2;
|
|
||||||
const int kSectionIndexAbilityScores = 3;
|
|
||||||
const int kSectionIndexSavingThrows = 4;
|
|
||||||
|
|
||||||
const int kBasicInfoSectionRowIndexName = 0;
|
|
||||||
const int kBasicInfoSectionRowIndexSize = 1;
|
|
||||||
const int kBasicInfoSectionRowIndexType = 2;
|
|
||||||
const int kBasicInfoSectionRowIndexSubtype = 3;
|
|
||||||
const int kBasicInfoSectionRowIndexAlignment = 4;
|
|
||||||
const int kBasicInfoSectionRowIndexHitDice = 5;
|
|
||||||
const int kBasicInfoSectionRowIndexCustomHP = 6;
|
|
||||||
const int kBasicInfoSectionRowIndexCustomHPText = 7;
|
|
||||||
|
|
||||||
const int kArmorSectionRowIndexArmorType = 0;
|
|
||||||
const int kArmorSectionRowIndexHasShield = 1;
|
|
||||||
const int kArmorSectionRowIndexNaturalArmorBonus = 2;
|
|
||||||
const int kArmorSectionRowIndexCustomArmor = 3;
|
|
||||||
|
|
||||||
const int kSpeedSectionRowIndexBaseSpeed = 0;
|
|
||||||
const int kSpeedSectionRowIndexBurrowSpeed = 1;
|
|
||||||
const int kSpeedSectionRowIndexClimbSpeed = 2;
|
|
||||||
const int kSpeedSectionRowIndexFlySpeed = 3;
|
|
||||||
const int kSpeedSectionRowIndexCanHover = 4;
|
|
||||||
const int kSpeedSectionRowIndexSwimSpeed = 5;
|
|
||||||
const int kSpeedSectionRowIndexHasCustomSpeed = 6;
|
|
||||||
const int kSpeedSectionRowIndexCustomSpeed = 7;
|
|
||||||
|
|
||||||
const int kAbilityScoreSectionRowIndexStrength = 0;
|
|
||||||
const int kAbilityScoreSectionRowIndexDexterity = 1;
|
|
||||||
const int kAbilityScoreSectionRowIndexConstitution = 2;
|
|
||||||
const int kAbilityScoreSectionRowIndexIntelligence = 3;
|
|
||||||
const int kAbilityScoreSectionRowIndexWisdom = 4;
|
|
||||||
const int kAbilityScoreSectionRowIndexCharisma = 5;
|
|
||||||
|
|
||||||
const int kSavingThrowsSectionRowIndexStrengthProficiency = 0;
|
|
||||||
const int kSavingThrowsSectionRowIndexStrengthAdvantage = 1;
|
|
||||||
const int kSavingThrowsSectionRowIndexDexterityProficiency = 2;
|
|
||||||
const int kSavingThrowsSectionRowIndexDexterityAdvantage = 3;
|
|
||||||
const int kSavingThrowsSectionRowIndexConstitutionProficiency = 4;
|
|
||||||
const int kSavingThrowsSectionRowIndexConstitutionAdvantage = 5;
|
|
||||||
const int kSavingThrowsSectionRowIndexIntelligenceProficiency = 6;
|
|
||||||
const int kSavingThrowsSectionRowIndexIntelligenceAdvantage = 7;
|
|
||||||
const int kSavingThrowsSectionRowIndexWisdomProficiency = 8;
|
|
||||||
const int kSavingThrowsSectionRowIndexWisdomAdvantage = 9;
|
|
||||||
const int kSavingThrowsSectionRowIndexCharismaProficiency = 10;
|
|
||||||
const int kSavingThrowsSectionRowIndexCharismaAdvantage = 11;
|
|
||||||
|
|
||||||
NSString *const kIdentifierName = @"monster.name";
|
|
||||||
NSString *const kIdentifierSize = @"monster.size";
|
|
||||||
NSString *const kIdentifierType = @"monster.type";
|
|
||||||
NSString *const kIdentifierSubtype = @"monster.subtype";
|
|
||||||
NSString *const kIdentifierAlignment = @"monster.alignment";
|
|
||||||
NSString *const kIdentifierCustomHP = @"monster.customHPText";
|
|
||||||
NSString *const kIdentifierCustomSpeed = @"monster.customSpeed";
|
|
||||||
NSString *const kIdentifierCustomArmor = @"monster.customArmor";
|
|
||||||
NSString *const kIdentifierStrengthScore = @"monster.strengthScore";
|
|
||||||
NSString *const kIdentiferDexterityScore = @"monster.dexterityScore";
|
|
||||||
NSString *const kIdentifierConstitutionScore = @"monster.constitutionScore";
|
|
||||||
NSString *const kIdentifierIntelligenceScore = @"monster.intelligenceScore";
|
|
||||||
NSString *const kIdentifierWisdomScore = @"monster.wisdomScore";
|
|
||||||
NSString *const kIdentifierCharismaScore = @"monster.charismaScore";
|
|
||||||
NSString *const kIdentifierHitDice = @"monster.hitDice";
|
|
||||||
NSString *const kIdentifierBaseSpeed = @"monster.baseSpeed";
|
|
||||||
NSString *const kIdentifierBurrowSpeed = @"monster.burrowSpeed";
|
|
||||||
NSString *const kIdentifierClimbSpeed = @"monster.climbSpeed";
|
|
||||||
NSString *const kIdentifierFlySpeed = @"monster.flySpeed";
|
|
||||||
NSString *const kIdentifierSwimSpeed = @"monster.swimSpeed";
|
|
||||||
NSString *const kIdentifierNaturalArmorBonus = @"monster.naturalArmorBonus";
|
|
||||||
NSString *const kIdentifierHasCustomHP = @"monster.customHP";
|
|
||||||
NSString *const kIdentifierCanHover = @"monster.canHover";
|
|
||||||
NSString *const kIdentifierHasCustomSpeed = @"monster.hasCustomSpeed";
|
|
||||||
NSString *const kIdentifierHasShield = @"monster.hasShield";
|
|
||||||
NSString *const kIdentifierArmorType = @"monster.armorType";
|
|
||||||
NSString *const kIdentifierStrengthSavingThrowAdvantage = @"monster.savingThrows.strength.advantage";
|
|
||||||
NSString *const kIdentifierStrengthSavingThrowProficiency = @"monster.savingThrows.strength.proficiency";
|
|
||||||
NSString *const kIdentifierDexteritySavingThrowAdvantage = @"monster.savingThrows.dexterity.advantage";
|
|
||||||
NSString *const kIdentifierDexteritySavingThrowProficiency = @"monster.savingThrows.dexterity.proficiency";
|
|
||||||
NSString *const kIdentifierConstitutionSavingThrowAdvantage = @"monster.savingThrows.constitution.advantage";
|
|
||||||
NSString *const kIdentifierConstitutionSavingThrowProficiency = @"monster.savingThrows.constitution.proficiency";
|
|
||||||
NSString *const kIdentifierIntelligenceSavingThrowAdvantage = @"monster.savingThrows.intelligence.advantage";
|
|
||||||
NSString *const kIdentifierIntelligenceSavingThrowProficiency = @"monster.savingThrows.intelligence.proficiency";
|
|
||||||
NSString *const kIdentifierWisdomSavingThrowAdvantage = @"monster.savingThrows.wisdom.advantage";
|
|
||||||
NSString *const kIdentifierWisdomSavingThrowProficiency = @"monster.savingThrows.wisdom.proficiency";
|
|
||||||
NSString *const kIdentifierCharismaSavingThrowAdvantage = @"monster.savingThrows.charisma.advantage";
|
|
||||||
NSString *const kIdentifierCharismaSavingThrowProficiency = @"monster.savingThrows.charisma.proficiency";
|
|
||||||
|
|
||||||
@implementation EditMonsterViewController {
|
|
||||||
NSManagedObjectContext *_context;
|
|
||||||
NSArray<MCChoice*>* _armorTypes;
|
|
||||||
NSArray<MCChoice*>* _proficiencyTypes;
|
|
||||||
NSArray<MCChoice*>* _advantageTypes;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)viewDidLoad {
|
|
||||||
[super viewDidLoad];
|
|
||||||
AppDelegate *appDelegate = (AppDelegate*)UIApplication.sharedApplication.delegate;
|
|
||||||
_context = appDelegate.persistentContainer.viewContext;
|
|
||||||
|
|
||||||
_armorTypes = [NSArray arrayWithObjects:
|
|
||||||
[MCChoice choiceWithLabel:NSLocalizedString(@"None", @"")
|
|
||||||
andValue:kArmorNameNone],
|
|
||||||
[MCChoice choiceWithLabel:NSLocalizedString(@"Natural Armor", @"")
|
|
||||||
andValue:kArmorNameNaturalArmor],
|
|
||||||
[MCChoice choiceWithLabel:NSLocalizedString(@"Mage Armor", @"")
|
|
||||||
andValue:kArmorNameMageArmor],
|
|
||||||
[MCChoice choiceWithLabel:NSLocalizedString(@"Padded", @"")
|
|
||||||
andValue:kArmorNamePadded],
|
|
||||||
[MCChoice choiceWithLabel:NSLocalizedString(@"Leather", @"")
|
|
||||||
andValue:kArmorNameLeather],
|
|
||||||
[MCChoice choiceWithLabel:NSLocalizedString(@"Studded", @"")
|
|
||||||
andValue:kArmorNameStuddedLeather],
|
|
||||||
[MCChoice choiceWithLabel:NSLocalizedString(@"Hide", @"")
|
|
||||||
andValue:kArmorNameHide],
|
|
||||||
[MCChoice choiceWithLabel:NSLocalizedString(@"Chain Shirt", @"")
|
|
||||||
andValue:kArmorNameChainShirt],
|
|
||||||
[MCChoice choiceWithLabel:NSLocalizedString(@"Scale Mail", @"")
|
|
||||||
andValue:kArmorNameScaleMail],
|
|
||||||
[MCChoice choiceWithLabel:NSLocalizedString(@"Breastplate", @"")
|
|
||||||
andValue:kArmorNameBreastplate],
|
|
||||||
[MCChoice choiceWithLabel:NSLocalizedString(@"Half Plate", @"")
|
|
||||||
andValue:kArmorNameHalfPlate],
|
|
||||||
[MCChoice choiceWithLabel:NSLocalizedString(@"Ring Mail", @"")
|
|
||||||
andValue:kArmorNameRingMail],
|
|
||||||
[MCChoice choiceWithLabel:NSLocalizedString(@"Chain Mail", @"")
|
|
||||||
andValue:kArmorNameChainMail],
|
|
||||||
[MCChoice choiceWithLabel:NSLocalizedString(@"Splint", @"")
|
|
||||||
andValue:kArmorNameSplintMail],
|
|
||||||
[MCChoice choiceWithLabel:NSLocalizedString(@"Plate", @"")
|
|
||||||
andValue:kArmorNamePlateMail],
|
|
||||||
[MCChoice choiceWithLabel:NSLocalizedString(@"Other", @"")
|
|
||||||
andValue:kArmorNameOther],
|
|
||||||
nil];
|
|
||||||
_proficiencyTypes = [NSArray arrayWithObjects:
|
|
||||||
[MCChoice choiceWithLabel:NSLocalizedString(@"None", @"")
|
|
||||||
andValue:kProficiencyTypeNone],
|
|
||||||
[MCChoice choiceWithLabel:NSLocalizedString(@"Proficient", @"")
|
|
||||||
andValue:kProficiencyTypeProficient],
|
|
||||||
[MCChoice choiceWithLabel:NSLocalizedString(@"Expertise", @"")
|
|
||||||
andValue:kProficiencyTypeExpertise],
|
|
||||||
nil];
|
|
||||||
_advantageTypes = [NSArray arrayWithObjects:
|
|
||||||
[MCChoice choiceWithLabel:NSLocalizedString(@"None", @"")
|
|
||||||
andValue:kAdvantageTypeNone],
|
|
||||||
[MCChoice choiceWithLabel:NSLocalizedString(@"Advantage", @"")
|
|
||||||
andValue:kAdvantageTypeAdvantage],
|
|
||||||
[MCChoice choiceWithLabel:NSLocalizedString(@"Disadvantage", @"")
|
|
||||||
andValue:kAdvantageTypeDisadvantage],
|
|
||||||
nil];
|
|
||||||
|
|
||||||
self.monsterTableView.allowsSelection = NO;
|
|
||||||
self.monsterTableView.allowsSelectionDuringEditing = NO;
|
|
||||||
self.monsterTableView.dataSource = self;
|
|
||||||
self.monsterTableView.delegate = self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)viewWillAppear:(BOOL)animated {
|
|
||||||
[super viewWillAppear:animated];
|
|
||||||
self.editingMonster = [[Monster alloc] initWithMonster:self.originalMonster];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (UITableViewCell*) makeSafeCell {
|
|
||||||
#if DEBUG
|
|
||||||
return nil;
|
|
||||||
#else
|
|
||||||
return [[UITableViewCell alloc] init];
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
|
|
||||||
- (MCShortStringFieldTableViewCell*) makeShortStringCellFromTableView:(UITableView*)tableView
|
|
||||||
withIdentifier:(NSString*)identifier
|
|
||||||
label:(NSString*)label
|
|
||||||
andInitialValue:(NSString*)initialValue {
|
|
||||||
MCShortStringFieldTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MCShortStringField"];
|
|
||||||
if (!cell || ![cell isKindOfClass:[MCShortStringFieldTableViewCell class]]) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
cell.delegate = self;
|
|
||||||
cell.identifier = identifier;
|
|
||||||
cell.label = label;
|
|
||||||
cell.value = initialValue;
|
|
||||||
|
|
||||||
// TODO: move these to better properties on MCShortStringFieldTableViewCell they should be stored via label and initialValue/value.
|
|
||||||
cell.textField.text = initialValue;
|
|
||||||
cell.textField.placeholder = label;
|
|
||||||
|
|
||||||
return cell;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (MCIntegerFieldTableViewCell*) makeIntegerCellFromTableView:(UITableView*)tableView
|
|
||||||
withIdentifier:(NSString*)identifier
|
|
||||||
label:(NSString*)label
|
|
||||||
andInitialValue:(int)initialValue {
|
|
||||||
MCIntegerFieldTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MCIntegerField"];
|
|
||||||
if (!cell || ![cell isKindOfClass:[MCIntegerFieldTableViewCell class]]) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
cell.delegate = self;
|
|
||||||
cell.identifier = identifier;
|
|
||||||
cell.label = label;
|
|
||||||
cell.value = initialValue;
|
|
||||||
|
|
||||||
return cell;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (MCBooleanFieldTableViewCell*) makeBooleanCellFromTableView:(UITableView*)tableView
|
|
||||||
withIdentifier:(NSString*)identifier
|
|
||||||
label:(NSString*)label
|
|
||||||
andInitialValue:(BOOL)initialValue {
|
|
||||||
MCBooleanFieldTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MCBooleanField"];
|
|
||||||
if (!cell || ![cell isKindOfClass:[MCBooleanFieldTableViewCell class]]) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
cell.delegate = self;
|
|
||||||
cell.identifier = identifier;
|
|
||||||
cell.label = label;
|
|
||||||
cell.value = initialValue;
|
|
||||||
|
|
||||||
return cell;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (MCSelectFieldTableViewCell*) makeSelectCellFromTableView:(UITableView*)tableView
|
|
||||||
withIdentifier:(NSString*)identifier
|
|
||||||
label:(NSString*)label
|
|
||||||
initialValue:(NSObject*)initialValue
|
|
||||||
andChoices:(NSArray*)choices {
|
|
||||||
MCSelectFieldTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MCSelectField"];
|
|
||||||
if (!cell || ![cell isKindOfClass:[MCSelectFieldTableViewCell class]]) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
cell.delegate = self;
|
|
||||||
cell.identifier = identifier;
|
|
||||||
cell.label = label;
|
|
||||||
cell.selectedValue = initialValue;
|
|
||||||
cell.choices = choices;
|
|
||||||
|
|
||||||
return cell;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (MCRadioFieldTableViewCell*) makeRadioCellFromTableView:(UITableView*)tableView
|
|
||||||
withIdentifier:(NSString*)identifier
|
|
||||||
label:(NSString*)label
|
|
||||||
initialValue:(NSObject*)initialValue
|
|
||||||
andChoices:(NSArray*)choices {
|
|
||||||
MCRadioFieldTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:@"MCRadioField"];
|
|
||||||
if (!cell || ![cell isKindOfClass:[MCRadioFieldTableViewCell class]]) {
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
cell.delegate = self;
|
|
||||||
cell.identifier = identifier;
|
|
||||||
cell.label = label;
|
|
||||||
// TODO: possibly swap these two
|
|
||||||
cell.selectedValue = initialValue;
|
|
||||||
cell.choices = choices;
|
|
||||||
|
|
||||||
return cell;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - Navigation
|
|
||||||
|
|
||||||
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
|
|
||||||
if ([@"DiscardChanges" isEqualToString:segue.identifier]) {
|
|
||||||
[_context rollback];
|
|
||||||
} else if ([@"SaveChanges" isEqualToString:segue.identifier]) {
|
|
||||||
// TODO: this should use a method on originalMonster to copy values from editingMonster or pass the new monster back some way. Core Data would save and probably trigger a refresh in the receiving view.
|
|
||||||
[self.originalMonster copyFromMonster:self.editingMonster];
|
|
||||||
[_context refreshObject:self.editingMonster mergeChanges:NO];
|
|
||||||
[_context save:nil];
|
|
||||||
} else {
|
|
||||||
NSLog(@"Unknown Segue %@", segue.identifier);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - UITableViewDataSource
|
|
||||||
|
|
||||||
- (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section {
|
|
||||||
switch(section) {
|
|
||||||
case kSectionIndexBasicInfo:
|
|
||||||
// Section 0 is basic info
|
|
||||||
// * Name
|
|
||||||
// * Size
|
|
||||||
// * Type
|
|
||||||
// * Subtype
|
|
||||||
// * Alignment
|
|
||||||
return 8;
|
|
||||||
case kSectionIndexArmor:
|
|
||||||
return 4;
|
|
||||||
case kSectionIndexSpeed:
|
|
||||||
return 8;
|
|
||||||
case kSectionIndexAbilityScores:
|
|
||||||
return 6;
|
|
||||||
case kSectionIndexSavingThrows:
|
|
||||||
return 12; // 12
|
|
||||||
default:
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView
|
|
||||||
{
|
|
||||||
return 5;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSString *)tableView:(UITableView *)tableView
|
|
||||||
titleForHeaderInSection:(NSInteger)section {
|
|
||||||
switch(section) {
|
|
||||||
case kSectionIndexBasicInfo:
|
|
||||||
return NSLocalizedString(@"Basic Info", @"Section title");
|
|
||||||
case kSectionIndexArmor:
|
|
||||||
return NSLocalizedString(@"Armor", @"Section title");
|
|
||||||
case kSectionIndexSpeed:
|
|
||||||
return NSLocalizedString(@"Speed", @"Section title");
|
|
||||||
case kSectionIndexAbilityScores:
|
|
||||||
return NSLocalizedString(@"Ability Scores", @"Section title");
|
|
||||||
case kSectionIndexSavingThrows:
|
|
||||||
return NSLocalizedString(@"Saving Throws", @"Section title");
|
|
||||||
default:
|
|
||||||
return nil;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(nonnull NSIndexPath *)indexPath {
|
|
||||||
|
|
||||||
UITableViewCell *newCell = nil;
|
|
||||||
|
|
||||||
switch (indexPath.section) {
|
|
||||||
case kSectionIndexBasicInfo:
|
|
||||||
switch (indexPath.row) {
|
|
||||||
case kBasicInfoSectionRowIndexName:
|
|
||||||
newCell = [self makeShortStringCellFromTableView:self.monsterTableView
|
|
||||||
withIdentifier:kIdentifierName
|
|
||||||
label:NSLocalizedString(@"Name", @"Placeholder text for the name of a monster or NPC.")
|
|
||||||
andInitialValue:self.editingMonster.name];
|
|
||||||
break;
|
|
||||||
case kBasicInfoSectionRowIndexSize:
|
|
||||||
newCell = [self makeShortStringCellFromTableView:self.monsterTableView
|
|
||||||
withIdentifier:kIdentifierSize
|
|
||||||
label:NSLocalizedString(@"Size", @"Placehodler text for the size of a monster or NPC.")
|
|
||||||
andInitialValue:self.editingMonster.size];
|
|
||||||
break;
|
|
||||||
case kBasicInfoSectionRowIndexType:
|
|
||||||
newCell = [self makeShortStringCellFromTableView:self.monsterTableView
|
|
||||||
withIdentifier:kIdentifierType
|
|
||||||
label:NSLocalizedString(@"Type", @"Placehodler text for the type of a monster or NPC.")
|
|
||||||
andInitialValue:self.editingMonster.type];
|
|
||||||
break;
|
|
||||||
case kBasicInfoSectionRowIndexSubtype:
|
|
||||||
newCell = [self makeShortStringCellFromTableView:self.monsterTableView
|
|
||||||
withIdentifier:kIdentifierSubtype
|
|
||||||
label:NSLocalizedString(@"Subtype", @"Placeholder text for the subtype of a monster or NPC.")
|
|
||||||
andInitialValue:self.editingMonster.subtype];
|
|
||||||
break;
|
|
||||||
case kBasicInfoSectionRowIndexAlignment:
|
|
||||||
newCell = [self makeShortStringCellFromTableView:self.monsterTableView
|
|
||||||
withIdentifier:kIdentifierAlignment
|
|
||||||
label: NSLocalizedString(@"Alignment", @"Placeholder text for the alignment of a monster or NPC.")
|
|
||||||
andInitialValue:self.editingMonster.alignment];
|
|
||||||
break;
|
|
||||||
case kBasicInfoSectionRowIndexHitDice:
|
|
||||||
newCell = [self makeIntegerCellFromTableView:self.monsterTableView
|
|
||||||
withIdentifier:kIdentifierHitDice
|
|
||||||
label:NSLocalizedString(@"Hit Dice", @"")
|
|
||||||
andInitialValue:self.editingMonster.hitDice];
|
|
||||||
break;
|
|
||||||
case kBasicInfoSectionRowIndexCustomHP:
|
|
||||||
newCell = [self makeBooleanCellFromTableView:self.monsterTableView
|
|
||||||
withIdentifier:kIdentifierHasCustomHP
|
|
||||||
label:NSLocalizedString(@"Custom HP", @"")
|
|
||||||
andInitialValue:self.editingMonster.customHP];
|
|
||||||
break;
|
|
||||||
case kBasicInfoSectionRowIndexCustomHPText:
|
|
||||||
newCell = [self makeShortStringCellFromTableView:self.monsterTableView
|
|
||||||
withIdentifier:kIdentifierCustomHP
|
|
||||||
label:NSLocalizedString(@"Custom HP Text", @"")
|
|
||||||
andInitialValue:self.editingMonster.hpText];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case kSectionIndexArmor:
|
|
||||||
switch (indexPath.row) {
|
|
||||||
case kArmorSectionRowIndexArmorType:
|
|
||||||
newCell = [self makeSelectCellFromTableView:self.monsterTableView
|
|
||||||
withIdentifier:kIdentifierArmorType
|
|
||||||
label:NSLocalizedString(@"Type", @"")
|
|
||||||
initialValue:self.editingMonster.armorType
|
|
||||||
andChoices:_armorTypes];
|
|
||||||
break;
|
|
||||||
case kArmorSectionRowIndexHasShield:
|
|
||||||
newCell = [self makeBooleanCellFromTableView:self.monsterTableView
|
|
||||||
withIdentifier:kIdentifierHasShield
|
|
||||||
label:NSLocalizedString(@"Shield", @"")
|
|
||||||
andInitialValue:self.editingMonster.hasShield];
|
|
||||||
break;
|
|
||||||
case kArmorSectionRowIndexCustomArmor:
|
|
||||||
newCell = [self makeShortStringCellFromTableView:self.monsterTableView
|
|
||||||
withIdentifier:kIdentifierCustomArmor
|
|
||||||
label:NSLocalizedString(@"Custom Armor", @"")
|
|
||||||
andInitialValue:self.editingMonster.customArmor];
|
|
||||||
break;
|
|
||||||
case kArmorSectionRowIndexNaturalArmorBonus:
|
|
||||||
newCell = [self makeIntegerCellFromTableView:self.monsterTableView
|
|
||||||
withIdentifier:kIdentifierNaturalArmorBonus
|
|
||||||
label:NSLocalizedString(@"Natural Armor Bonus", @"")
|
|
||||||
andInitialValue:self.editingMonster.naturalArmorBonus];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case kSectionIndexSpeed:
|
|
||||||
switch (indexPath.row) {
|
|
||||||
case kSpeedSectionRowIndexBaseSpeed:
|
|
||||||
newCell = [self makeIntegerCellFromTableView:self.monsterTableView
|
|
||||||
withIdentifier:kIdentifierBaseSpeed
|
|
||||||
label:NSLocalizedString(@"Base", @"")
|
|
||||||
andInitialValue:self.editingMonster.baseSpeed];
|
|
||||||
break;
|
|
||||||
case kSpeedSectionRowIndexBurrowSpeed:
|
|
||||||
newCell = [self makeIntegerCellFromTableView:self.monsterTableView
|
|
||||||
withIdentifier:kIdentifierBurrowSpeed
|
|
||||||
label:NSLocalizedString(@"Burrow", @"")
|
|
||||||
andInitialValue:self.editingMonster.burrowSpeed];
|
|
||||||
break;
|
|
||||||
case kSpeedSectionRowIndexClimbSpeed:
|
|
||||||
newCell = [self makeIntegerCellFromTableView:self.monsterTableView
|
|
||||||
withIdentifier:kIdentifierClimbSpeed
|
|
||||||
label:NSLocalizedString(@"Climb", @"")
|
|
||||||
andInitialValue:self.editingMonster.climbSpeed];
|
|
||||||
break;
|
|
||||||
case kSpeedSectionRowIndexFlySpeed:
|
|
||||||
newCell = [self makeIntegerCellFromTableView:self.monsterTableView
|
|
||||||
withIdentifier:kIdentifierFlySpeed
|
|
||||||
label:NSLocalizedString(@"Fly", @"")
|
|
||||||
andInitialValue:self.editingMonster.flySpeed];
|
|
||||||
break;
|
|
||||||
case kSpeedSectionRowIndexCanHover:
|
|
||||||
newCell = [self makeBooleanCellFromTableView:self.monsterTableView
|
|
||||||
withIdentifier:kIdentifierCanHover
|
|
||||||
label:NSLocalizedString(@"Hover", @"")
|
|
||||||
andInitialValue:self.editingMonster.canHover];
|
|
||||||
break;
|
|
||||||
case kSpeedSectionRowIndexSwimSpeed:
|
|
||||||
newCell = [self makeIntegerCellFromTableView:self.monsterTableView
|
|
||||||
withIdentifier:kIdentifierSwimSpeed
|
|
||||||
label:NSLocalizedString(@"Swim", @"")
|
|
||||||
andInitialValue:self.editingMonster.swimSpeed];
|
|
||||||
break;
|
|
||||||
case kSpeedSectionRowIndexHasCustomSpeed:
|
|
||||||
newCell = [self makeBooleanCellFromTableView:self.monsterTableView
|
|
||||||
withIdentifier:kIdentifierHasCustomSpeed
|
|
||||||
label:NSLocalizedString(@"Custom Speed", @"")
|
|
||||||
andInitialValue:self.editingMonster.hasCustomSpeed];
|
|
||||||
break;
|
|
||||||
case kSpeedSectionRowIndexCustomSpeed:
|
|
||||||
newCell = [self makeShortStringCellFromTableView:self.monsterTableView
|
|
||||||
withIdentifier:kIdentifierCustomSpeed
|
|
||||||
label:NSLocalizedString(@"Custom Speed", @"")
|
|
||||||
andInitialValue:self.editingMonster.customSpeed];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case kSectionIndexAbilityScores:
|
|
||||||
switch (indexPath.row) {
|
|
||||||
case kAbilityScoreSectionRowIndexStrength:
|
|
||||||
newCell = [self makeIntegerCellFromTableView:self.monsterTableView
|
|
||||||
withIdentifier:kIdentifierStrengthScore
|
|
||||||
label:NSLocalizedString(@"STR", @"Placeholder abbreviation for the strength score of a monster or NPC.")
|
|
||||||
andInitialValue:self.editingMonster.strengthScore];
|
|
||||||
break;
|
|
||||||
case kAbilityScoreSectionRowIndexDexterity:
|
|
||||||
newCell = [self makeIntegerCellFromTableView:self.monsterTableView
|
|
||||||
withIdentifier:kIdentiferDexterityScore
|
|
||||||
label:NSLocalizedString(@"DEX", @"Placeholder abbreviation for the dexterity score of a monster or NPC.")
|
|
||||||
andInitialValue:self.editingMonster.dexterityScore];
|
|
||||||
break;
|
|
||||||
case kAbilityScoreSectionRowIndexConstitution:
|
|
||||||
newCell = [self makeIntegerCellFromTableView:self.monsterTableView
|
|
||||||
withIdentifier:kIdentifierConstitutionScore
|
|
||||||
label:NSLocalizedString(@"CON", @"Placeholder abbreviation for the constitution score of a monster or NPC.")
|
|
||||||
andInitialValue:self.editingMonster.constitutionScore];
|
|
||||||
break;
|
|
||||||
case kAbilityScoreSectionRowIndexIntelligence:
|
|
||||||
newCell = [self makeIntegerCellFromTableView:self.monsterTableView
|
|
||||||
withIdentifier:kIdentifierIntelligenceScore
|
|
||||||
label:NSLocalizedString(@"INT", @"Placeholder abbreviation for the intelligence score of a monster or NPC.")
|
|
||||||
andInitialValue:self.editingMonster.intelligenceScore];
|
|
||||||
break;
|
|
||||||
case kAbilityScoreSectionRowIndexWisdom:
|
|
||||||
newCell = [self makeIntegerCellFromTableView:self.monsterTableView
|
|
||||||
withIdentifier:kIdentifierWisdomScore
|
|
||||||
label:NSLocalizedString(@"WIS", @"Placeholder abbreviation for the wisdom score of a monster or NPC.")
|
|
||||||
andInitialValue:self.editingMonster.wisdomScore];
|
|
||||||
break;
|
|
||||||
case kAbilityScoreSectionRowIndexCharisma:
|
|
||||||
newCell = [self makeIntegerCellFromTableView:self.monsterTableView
|
|
||||||
withIdentifier:kIdentifierCharismaScore
|
|
||||||
label:NSLocalizedString(@"CHA", @"Placeholder abbreviation for the charisma score of a monster or NPC.")
|
|
||||||
andInitialValue:self.editingMonster.charismaScore];
|
|
||||||
break;
|
|
||||||
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
case kSectionIndexSavingThrows:
|
|
||||||
switch (indexPath.row) {
|
|
||||||
case kSavingThrowsSectionRowIndexStrengthProficiency:
|
|
||||||
newCell = [self makeRadioCellFromTableView:self.monsterTableView
|
|
||||||
withIdentifier:kIdentifierStrengthSavingThrowProficiency
|
|
||||||
label:NSLocalizedString(@"Strength Proficiency", @"")
|
|
||||||
initialValue:self.editingMonster.strengthSavingThrowProficiency
|
|
||||||
andChoices:_proficiencyTypes];
|
|
||||||
break;
|
|
||||||
case kSavingThrowsSectionRowIndexStrengthAdvantage:
|
|
||||||
newCell = [self makeRadioCellFromTableView:self.monsterTableView
|
|
||||||
withIdentifier:kIdentifierStrengthSavingThrowAdvantage
|
|
||||||
label:NSLocalizedString(@"Strength Advantage", @"")
|
|
||||||
initialValue:self.editingMonster.strengthSavingThrowAdvantage
|
|
||||||
andChoices:_advantageTypes];
|
|
||||||
break;
|
|
||||||
case kSavingThrowsSectionRowIndexDexterityProficiency:
|
|
||||||
newCell = [self makeRadioCellFromTableView:self.monsterTableView
|
|
||||||
withIdentifier:kIdentifierDexteritySavingThrowProficiency
|
|
||||||
label:NSLocalizedString(@"Dexterity Proficiency", @"")
|
|
||||||
initialValue:self.editingMonster.dexteritySavingThrowProficiency
|
|
||||||
andChoices:_proficiencyTypes];
|
|
||||||
break;
|
|
||||||
case kSavingThrowsSectionRowIndexDexterityAdvantage:
|
|
||||||
newCell = [self makeRadioCellFromTableView:self.monsterTableView
|
|
||||||
withIdentifier:kIdentifierDexteritySavingThrowAdvantage
|
|
||||||
label:NSLocalizedString(@"Dexterity Advantage", @"")
|
|
||||||
initialValue:self.editingMonster.dexteritySavingThrowAdvantage
|
|
||||||
andChoices:_advantageTypes];
|
|
||||||
break;
|
|
||||||
case kSavingThrowsSectionRowIndexConstitutionProficiency:
|
|
||||||
newCell = [self makeRadioCellFromTableView:self.monsterTableView
|
|
||||||
withIdentifier:kIdentifierConstitutionSavingThrowProficiency
|
|
||||||
label:NSLocalizedString(@"Constitution Proficiency", @"")
|
|
||||||
initialValue:self.editingMonster.constitutionSavingThrowProficiency
|
|
||||||
andChoices:_proficiencyTypes];
|
|
||||||
break;
|
|
||||||
case kSavingThrowsSectionRowIndexConstitutionAdvantage:
|
|
||||||
newCell = [self makeRadioCellFromTableView:self.monsterTableView
|
|
||||||
withIdentifier:kIdentifierConstitutionSavingThrowAdvantage
|
|
||||||
label:NSLocalizedString(@"Constitution Advantage", @"")
|
|
||||||
initialValue:self.editingMonster.constitutionSavingThrowAdvantage
|
|
||||||
andChoices:_advantageTypes];
|
|
||||||
break;
|
|
||||||
case kSavingThrowsSectionRowIndexIntelligenceProficiency:
|
|
||||||
newCell = [self makeRadioCellFromTableView:self.monsterTableView
|
|
||||||
withIdentifier:kIdentifierIntelligenceSavingThrowProficiency
|
|
||||||
label:NSLocalizedString(@"Intelligence Proficiency", @"")
|
|
||||||
initialValue:self.editingMonster.intelligenceSavingThrowProficiency
|
|
||||||
andChoices:_proficiencyTypes];
|
|
||||||
break;
|
|
||||||
case kSavingThrowsSectionRowIndexIntelligenceAdvantage:
|
|
||||||
newCell = [self makeRadioCellFromTableView:self.monsterTableView
|
|
||||||
withIdentifier:kIdentifierIntelligenceSavingThrowAdvantage
|
|
||||||
label:NSLocalizedString(@"Intelligence Advantage", @"")
|
|
||||||
initialValue:self.editingMonster.intelligenceSavingThrowAdvantage
|
|
||||||
andChoices:_advantageTypes];
|
|
||||||
break;
|
|
||||||
case kSavingThrowsSectionRowIndexWisdomProficiency:
|
|
||||||
newCell = [self makeRadioCellFromTableView:self.monsterTableView
|
|
||||||
withIdentifier:kIdentifierWisdomSavingThrowProficiency
|
|
||||||
label:NSLocalizedString(@"Wisdom Proficiency", @"")
|
|
||||||
initialValue:self.editingMonster.wisdomSavingThrowProficiency
|
|
||||||
andChoices:_proficiencyTypes];
|
|
||||||
break;
|
|
||||||
case kSavingThrowsSectionRowIndexWisdomAdvantage:
|
|
||||||
newCell = [self makeRadioCellFromTableView:self.monsterTableView
|
|
||||||
withIdentifier:kIdentifierWisdomSavingThrowAdvantage
|
|
||||||
label:NSLocalizedString(@"Wisdom Advantage", @"")
|
|
||||||
initialValue:self.editingMonster.wisdomSavingThrowAdvantage
|
|
||||||
andChoices:_advantageTypes];
|
|
||||||
break;
|
|
||||||
case kSavingThrowsSectionRowIndexCharismaProficiency:
|
|
||||||
newCell = [self makeRadioCellFromTableView:self.monsterTableView
|
|
||||||
withIdentifier:kIdentifierCharismaSavingThrowProficiency
|
|
||||||
label:NSLocalizedString(@"Charisma Proficiency", @"")
|
|
||||||
initialValue:self.editingMonster.charismaSavingThrowProficiency
|
|
||||||
andChoices:_proficiencyTypes];
|
|
||||||
break;
|
|
||||||
case kSavingThrowsSectionRowIndexCharismaAdvantage:
|
|
||||||
newCell = [self makeRadioCellFromTableView:self.monsterTableView
|
|
||||||
withIdentifier:kIdentifierCharismaSavingThrowAdvantage
|
|
||||||
label:NSLocalizedString(@"Charisma Advantage", @"")
|
|
||||||
initialValue:self.editingMonster.charismaSavingThrowAdvantage
|
|
||||||
andChoices:_advantageTypes];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!newCell) {
|
|
||||||
NSLog(@"ERROR: Unable to build a cell for %@", indexPath);
|
|
||||||
newCell = [self makeSafeCell];
|
|
||||||
}
|
|
||||||
|
|
||||||
return newCell;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - MCShortStringFieldDelegate
|
|
||||||
|
|
||||||
- (void)editableValueDidChange:(NSObject*)value forIdentifier:(NSString*)identifier andType:(NSString*)type {
|
|
||||||
if ([kMCFieldValueTypeString isEqualToString:type]) {
|
|
||||||
if ([kIdentifierName isEqualToString:identifier]) {
|
|
||||||
self.editingMonster.name = (NSString*)value;
|
|
||||||
} else if ([kIdentifierSize isEqualToString:identifier]) {
|
|
||||||
self.editingMonster.size = (NSString*)value;
|
|
||||||
} else if ([kIdentifierType isEqualToString:identifier]) {
|
|
||||||
self.editingMonster.type = (NSString*)value;
|
|
||||||
} else if ([kIdentifierSubtype isEqualToString:identifier]) {
|
|
||||||
self.editingMonster.subtype = (NSString*)value;
|
|
||||||
} else if ([kIdentifierAlignment isEqualToString:identifier]) {
|
|
||||||
self.editingMonster.alignment = (NSString*)value;
|
|
||||||
} else if ([kIdentifierCustomHP isEqualToString:identifier]) {
|
|
||||||
self.editingMonster.hpText = (NSString*)value;
|
|
||||||
} else if ([kIdentifierCustomSpeed isEqualToString:identifier]) {
|
|
||||||
self.editingMonster.customSpeed = (NSString*)value;
|
|
||||||
} else if ([kIdentifierCustomArmor isEqualToString:identifier]) {
|
|
||||||
self.editingMonster.customArmor = (NSString*)value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ([kMCFieldValueTypeInteger isEqualToString:type]) {
|
|
||||||
if ([kIdentifierStrengthScore isEqualToString:identifier]) {
|
|
||||||
self.editingMonster.strengthScore = [(NSNumber*)value intValue];
|
|
||||||
} else if ([kIdentiferDexterityScore isEqualToString:identifier]) {
|
|
||||||
self.editingMonster.dexterityScore = [(NSNumber*)value intValue];
|
|
||||||
} else if ([kIdentifierConstitutionScore isEqualToString:identifier]) {
|
|
||||||
self.editingMonster.constitutionScore = [(NSNumber*)value intValue];
|
|
||||||
} else if ([kIdentifierIntelligenceScore isEqualToString:identifier]) {
|
|
||||||
self.editingMonster.intelligenceScore = [(NSNumber*)value intValue];
|
|
||||||
} else if ([kIdentifierWisdomScore isEqualToString:identifier]) {
|
|
||||||
self.editingMonster.wisdomScore = [(NSNumber*)value intValue];
|
|
||||||
} else if ([kIdentifierCharismaScore isEqualToString:identifier]) {
|
|
||||||
self.editingMonster.charismaScore = [(NSNumber*)value intValue];
|
|
||||||
} else if ([kIdentifierHitDice isEqualToString:identifier]) {
|
|
||||||
self.editingMonster.hitDice = [(NSNumber*)value intValue];
|
|
||||||
} else if ([kIdentifierBaseSpeed isEqualToString:identifier]) {
|
|
||||||
self.editingMonster.baseSpeed = [(NSNumber*)value intValue];
|
|
||||||
} else if ([kIdentifierBurrowSpeed isEqualToString:identifier]) {
|
|
||||||
self.editingMonster.burrowSpeed = [(NSNumber*)value intValue];
|
|
||||||
} else if ([kIdentifierClimbSpeed isEqualToString:identifier]) {
|
|
||||||
self.editingMonster.climbSpeed = [(NSNumber*)value intValue];
|
|
||||||
} else if ([kIdentifierFlySpeed isEqualToString:identifier]) {
|
|
||||||
self.editingMonster.flySpeed = [(NSNumber*)value intValue];
|
|
||||||
} else if ([kIdentifierSwimSpeed isEqualToString:identifier]) {
|
|
||||||
self.editingMonster.swimSpeed = [(NSNumber*)value intValue];
|
|
||||||
} else if ([kIdentifierNaturalArmorBonus isEqualToString:identifier]) {
|
|
||||||
self.editingMonster.naturalArmorBonus = [(NSNumber*)value intValue];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ([kMCFieldValueTypeBoolean isEqualToString:type]) {
|
|
||||||
if ([kIdentifierHasCustomHP isEqualToString:identifier]) {
|
|
||||||
self.editingMonster.customHP = [(NSNumber*)value boolValue];
|
|
||||||
} else if ([kIdentifierCanHover isEqualToString:identifier]) {
|
|
||||||
self.editingMonster.canHover = [(NSNumber*)value boolValue];
|
|
||||||
} else if ([kIdentifierHasCustomSpeed isEqualToString:identifier]) {
|
|
||||||
self.editingMonster.hasCustomSpeed = [(NSNumber*)value boolValue];
|
|
||||||
} else if ([kIdentifierHasShield isEqualToString:identifier]) {
|
|
||||||
self.editingMonster.hasShield = [(NSNumber*)value boolValue];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if ([kMCFieldValueTypeChoice isEqualToString:type]) {
|
|
||||||
if ([kIdentifierArmorType isEqualToString:identifier]) {
|
|
||||||
self.editingMonster.armorType = (NSString*)value;
|
|
||||||
} else if ([kIdentifierStrengthSavingThrowAdvantage isEqualToString:identifier]) {
|
|
||||||
self.editingMonster.strengthSavingThrowAdvantage = (NSString*)value;
|
|
||||||
} else if ([kIdentifierStrengthSavingThrowProficiency isEqualToString:identifier]) {
|
|
||||||
self.editingMonster.strengthSavingThrowProficiency = (NSString*)value;
|
|
||||||
} else if ([kIdentifierDexteritySavingThrowAdvantage isEqualToString:identifier]) {
|
|
||||||
self.editingMonster.dexteritySavingThrowAdvantage = (NSString*)value;
|
|
||||||
} else if ([kIdentifierDexteritySavingThrowProficiency isEqualToString:identifier]) {
|
|
||||||
self.editingMonster.dexteritySavingThrowProficiency = (NSString*)value;
|
|
||||||
} else if ([kIdentifierConstitutionSavingThrowAdvantage isEqualToString:identifier]) {
|
|
||||||
self.editingMonster.constitutionSavingThrowAdvantage = (NSString*)value;
|
|
||||||
} else if ([kIdentifierConstitutionSavingThrowProficiency isEqualToString:identifier]) {
|
|
||||||
self.editingMonster.constitutionSavingThrowProficiency = (NSString*)value;
|
|
||||||
} else if ([kIdentifierIntelligenceSavingThrowAdvantage isEqualToString:identifier]) {
|
|
||||||
self.editingMonster.intelligenceSavingThrowAdvantage = (NSString*)value;
|
|
||||||
} else if ([kIdentifierIntelligenceSavingThrowProficiency isEqualToString:identifier]) {
|
|
||||||
self.editingMonster.intelligenceSavingThrowProficiency = (NSString*)value;
|
|
||||||
} else if ([kIdentifierWisdomSavingThrowAdvantage isEqualToString:identifier]) {
|
|
||||||
self.editingMonster.wisdomSavingThrowAdvantage = (NSString*)value;
|
|
||||||
} else if ([kIdentifierWisdomSavingThrowProficiency isEqualToString:identifier]) {
|
|
||||||
self.editingMonster.wisdomSavingThrowProficiency = (NSString*)value;
|
|
||||||
} else if ([kIdentifierCharismaSavingThrowAdvantage isEqualToString:identifier]) {
|
|
||||||
self.editingMonster.charismaSavingThrowAdvantage = (NSString*)value;
|
|
||||||
} else if ([kIdentifierCharismaSavingThrowProficiency isEqualToString:identifier]) {
|
|
||||||
self.editingMonster.charismaSavingThrowProficiency = (NSString*)value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (CGFloat)tableView:(UITableView *)tableView heightForRowAtIndexPath:(NSIndexPath *)indexPath {
|
|
||||||
return UITableViewAutomaticDimension;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,26 +0,0 @@
|
|||||||
//
|
|
||||||
// MCBooleanFieldTableViewCell.h
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/25/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
|
||||||
#import "MCFormFieldDelegate.h"
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
@interface MCBooleanFieldTableViewCell : UITableViewCell
|
|
||||||
|
|
||||||
@property NSString* identifier;
|
|
||||||
@property NSString* label;
|
|
||||||
@property BOOL value;
|
|
||||||
|
|
||||||
@property (weak, nonatomic) id<MCFormFieldDelegate> delegate;
|
|
||||||
@property (weak, nonatomic) IBOutlet UILabel *labelView;
|
|
||||||
@property (weak, nonatomic) IBOutlet UISwitch *switchView;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
||||||
@@ -1,58 +0,0 @@
|
|||||||
//
|
|
||||||
// MCBooleanFieldTableViewCell.m
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/25/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "MCBooleanFieldTableViewCell.h"
|
|
||||||
|
|
||||||
@implementation MCBooleanFieldTableViewCell
|
|
||||||
|
|
||||||
@synthesize value = _value;
|
|
||||||
|
|
||||||
- (void)setValue:(BOOL)value {
|
|
||||||
if (value != _value) {
|
|
||||||
_value = value;
|
|
||||||
if (self.switchView) {
|
|
||||||
self.switchView.on = _value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (BOOL)value {
|
|
||||||
return _value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@synthesize label = _label;
|
|
||||||
|
|
||||||
- (void)setLabel:(NSString*)label {
|
|
||||||
if (![_label isEqualToString:label]) {
|
|
||||||
_label = label;
|
|
||||||
if (self.labelView) {
|
|
||||||
self.labelView.text = label;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSString*)label {
|
|
||||||
return _label;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)awakeFromNib {
|
|
||||||
[super awakeFromNib];
|
|
||||||
self.switchView.on = self.value;
|
|
||||||
self.labelView.text = self.label;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)valueChanged:(id)sender {
|
|
||||||
self.value = self.switchView.on;
|
|
||||||
if (self.delegate != nil) {
|
|
||||||
[self.delegate editableValueDidChange:[NSNumber numberWithBool:self.value]
|
|
||||||
forIdentifier:self.identifier
|
|
||||||
andType:kMCFieldValueTypeBoolean];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
//
|
|
||||||
// MCChoice.h
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/26/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
@interface MCChoice : NSObject
|
|
||||||
|
|
||||||
@property NSString* label;
|
|
||||||
@property NSObject* value;
|
|
||||||
|
|
||||||
+(id)choiceWithLabel:(NSString*)label
|
|
||||||
andValue:(NSObject*)value;
|
|
||||||
|
|
||||||
-(id)initWithLabel:(NSString*)label
|
|
||||||
andValue:(NSObject*)value;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
//
|
|
||||||
// MCChoice.m
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/26/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "MCChoice.h"
|
|
||||||
|
|
||||||
@implementation MCChoice
|
|
||||||
|
|
||||||
+(id)choiceWithLabel:(NSString*)label
|
|
||||||
andValue:(NSObject*)value {
|
|
||||||
return [[MCChoice alloc] initWithLabel:label
|
|
||||||
andValue:value];
|
|
||||||
}
|
|
||||||
|
|
||||||
-(id)initWithLabel:(NSString*)label
|
|
||||||
andValue:(NSObject*)value {
|
|
||||||
self = [super init];
|
|
||||||
|
|
||||||
self.label = label;
|
|
||||||
self.value = value;
|
|
||||||
|
|
||||||
return self;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,17 +0,0 @@
|
|||||||
//
|
|
||||||
// MCFormFieldConstants.h
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/17/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef MCFormFieldConstants_h
|
|
||||||
#define MCFormFieldConstants_h
|
|
||||||
|
|
||||||
extern NSString* const kMCFieldValueTypeInteger;
|
|
||||||
extern NSString* const kMCFieldValueTypeString;
|
|
||||||
extern NSString* const kMCFieldValueTypeBoolean;
|
|
||||||
extern NSString* const kMCFieldValueTypeChoice;
|
|
||||||
|
|
||||||
#endif /* MCFormFieldConstants_h */
|
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
//
|
|
||||||
// MCFormFieldConstants.m
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/17/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <Foundation/Foundation.h>
|
|
||||||
#import "MCFormFieldConstants.h"
|
|
||||||
|
|
||||||
NSString* const kMCFieldValueTypeInteger = @"Integer";
|
|
||||||
NSString* const kMCFieldValueTypeString = @"String";
|
|
||||||
NSString* const kMCFieldValueTypeBoolean = @"Boolean";
|
|
||||||
NSString* const kMCFieldValueTypeChoice = @"Choice";
|
|
||||||
@@ -1,22 +0,0 @@
|
|||||||
//
|
|
||||||
// MCFormFieldDelegate.h
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/9/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#ifndef MCFormFieldDelegate_h
|
|
||||||
#define MCFormFieldDelegate_h
|
|
||||||
|
|
||||||
#import "MCFormFieldConstants.h"
|
|
||||||
|
|
||||||
@protocol MCFormFieldDelegate <NSObject>
|
|
||||||
|
|
||||||
@optional
|
|
||||||
-(void)editableValueDidChange:(NSObject*)value forIdentifier:(NSString*)identifier andType:(NSString*)type;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
|
|
||||||
#endif /* MCFormFieldDelegate_h */
|
|
||||||
@@ -1,28 +0,0 @@
|
|||||||
//
|
|
||||||
// MCIntegerFieldTableViewCell.h
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/17/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
|
||||||
#import "MCFormFieldDelegate.h"
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
@interface MCIntegerFieldTableViewCell : UITableViewCell <UITextFieldDelegate>
|
|
||||||
|
|
||||||
@property NSString* identifier;
|
|
||||||
@property NSString* label;
|
|
||||||
@property int value;
|
|
||||||
|
|
||||||
@property (weak, nonatomic) id<MCFormFieldDelegate> delegate;
|
|
||||||
@property (weak, nonatomic) IBOutlet UITextField *textField;
|
|
||||||
@property (weak, nonatomic) IBOutlet UIStepper *stepper;
|
|
||||||
@property (weak, nonatomic) IBOutlet UILabel *labelView;
|
|
||||||
- (IBAction)stepperValueChanged:(id)sender;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
||||||
@@ -1,72 +0,0 @@
|
|||||||
//
|
|
||||||
// MCIntegerFieldTableViewCell.m
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/17/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "MCIntegerFieldTableViewCell.h"
|
|
||||||
|
|
||||||
@implementation MCIntegerFieldTableViewCell
|
|
||||||
|
|
||||||
@synthesize value = _value;
|
|
||||||
|
|
||||||
-(void) setValue:(int)number {
|
|
||||||
if (_value != number) {
|
|
||||||
NSNumber *newValue = [NSNumber numberWithInt:number];
|
|
||||||
_value = number;
|
|
||||||
if (self.textField) {
|
|
||||||
self.textField.text = [newValue stringValue];
|
|
||||||
}
|
|
||||||
if (self.stepper) {
|
|
||||||
self.stepper.value = number;
|
|
||||||
}
|
|
||||||
if (self.delegate) {
|
|
||||||
[self.delegate editableValueDidChange:newValue
|
|
||||||
forIdentifier:self.identifier
|
|
||||||
andType:kMCFieldValueTypeInteger];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (int) value {
|
|
||||||
return _value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@synthesize label = _label;
|
|
||||||
|
|
||||||
- (void)setLabel:(NSString*)label {
|
|
||||||
if (![_label isEqualToString:label]) {
|
|
||||||
_label = label;
|
|
||||||
if (self.textField) {
|
|
||||||
self.textField.placeholder = label;
|
|
||||||
}
|
|
||||||
if (self.labelView) {
|
|
||||||
self.labelView.text = label;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSString*)label {
|
|
||||||
return _label;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)awakeFromNib {
|
|
||||||
[super awakeFromNib];
|
|
||||||
[self.textField addTarget:self
|
|
||||||
action:@selector(textFieldValueChanged:)
|
|
||||||
forControlEvents:UIControlEventEditingChanged];
|
|
||||||
self.textField.text = [[NSNumber numberWithInt:_value] stringValue];
|
|
||||||
self.stepper.value = _value;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)textFieldValueChanged:(UITextField*)textField {
|
|
||||||
self.value = [textField.text intValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)stepperValueChanged:(id)sender {
|
|
||||||
self.value = self.stepper.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
//
|
|
||||||
// MCRadioFieldTableViewCell.h
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/26/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
|
||||||
#import "MCFormFieldDelegate.h"
|
|
||||||
#import "MCChoice.h"
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
@interface MCRadioFieldTableViewCell : UITableViewCell
|
|
||||||
|
|
||||||
@property NSString* identifier;
|
|
||||||
@property NSString* label;
|
|
||||||
@property NSObject* selectedValue;
|
|
||||||
@property NSArray<MCChoice*>* choices;
|
|
||||||
|
|
||||||
@property (weak, nonatomic) id<MCFormFieldDelegate> delegate;
|
|
||||||
@property (weak, nonatomic) IBOutlet UILabel *labelView;
|
|
||||||
@property (weak, nonatomic) IBOutlet UISegmentedControl *segmentedControl;
|
|
||||||
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
||||||
@@ -1,131 +0,0 @@
|
|||||||
//
|
|
||||||
// MCRadioFieldTableViewCell.m
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/26/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "MCRadioFieldTableViewCell.h"
|
|
||||||
|
|
||||||
@implementation MCRadioFieldTableViewCell {
|
|
||||||
MCChoice* _selectedChoice;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(MCChoice*)findChoiceWithValue:(NSObject*)value
|
|
||||||
inArray:(NSArray*)array {
|
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id element, NSDictionary *bindings) {
|
|
||||||
if (![element isKindOfClass:[MCChoice class]]) {
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
MCChoice *choice = (MCChoice*)element;
|
|
||||||
return [choice.value isEqual:value];
|
|
||||||
}];
|
|
||||||
NSArray<MCChoice*> *matchingChoices = [array filteredArrayUsingPredicate:predicate];
|
|
||||||
MCChoice *foundChoice = matchingChoices.count > 0 ? matchingChoices.firstObject : nil;
|
|
||||||
return foundChoice;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)notifyChangedValue {
|
|
||||||
NSUInteger selectedIndex = [_choices indexOfObject:_selectedChoice];
|
|
||||||
[self.segmentedControl setSelectedSegmentIndex:selectedIndex];
|
|
||||||
|
|
||||||
if (_delegate) {
|
|
||||||
[_delegate editableValueDidChange:_selectedValue
|
|
||||||
forIdentifier:_identifier
|
|
||||||
andType:kMCFieldValueTypeChoice];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)updateSegments {
|
|
||||||
if (_segmentedControl) {
|
|
||||||
[_segmentedControl removeAllSegments];
|
|
||||||
int index = 0;
|
|
||||||
for (MCChoice *choice in _choices) {
|
|
||||||
[_segmentedControl insertSegmentWithTitle:choice.label atIndex:index animated:NO];
|
|
||||||
index++;
|
|
||||||
}
|
|
||||||
_segmentedControl.selectedSegmentIndex = [_choices indexOfObject:_selectedChoice];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@synthesize choices = _choices;
|
|
||||||
-(void)setChoices:(NSArray<MCChoice*>*)choices {
|
|
||||||
MCChoice *foundChoice = [self findChoiceWithValue:_selectedValue
|
|
||||||
inArray:choices];
|
|
||||||
if ([_choices isEqualToArray:choices]) {
|
|
||||||
// Choices are equivalent so selected value. Pointer may have changed but content hasn't.
|
|
||||||
_selectedChoice = foundChoice;
|
|
||||||
} else if (foundChoice) {
|
|
||||||
// Choices are different but selected value is in the new choices. Pointer may have changed but content hasn't.
|
|
||||||
_selectedChoice = foundChoice;
|
|
||||||
} else {
|
|
||||||
// Choices are different and selected value is not in the new choices. Select the first choice or nil if there are none.
|
|
||||||
_selectedChoice = [choices firstObject];
|
|
||||||
}
|
|
||||||
_choices = choices;
|
|
||||||
|
|
||||||
if (_selectedValue != foundChoice.value) {
|
|
||||||
self.selectedValue = foundChoice.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
[self updateSegments];
|
|
||||||
}
|
|
||||||
-(NSArray<MCChoice*>*)choices {
|
|
||||||
return _choices;
|
|
||||||
}
|
|
||||||
|
|
||||||
@synthesize label = _label;
|
|
||||||
-(void)setLabel:(NSString*)label {
|
|
||||||
if (![_label isEqualToString:label]) {
|
|
||||||
_label = label;
|
|
||||||
}
|
|
||||||
if (_labelView && ![_labelView.text isEqualToString:label]) {
|
|
||||||
_labelView.text = label;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-(NSString*)label {
|
|
||||||
return _label;
|
|
||||||
}
|
|
||||||
|
|
||||||
@synthesize selectedValue = _selectedValue;
|
|
||||||
-(void)setSelectedValue:(NSObject*)value {
|
|
||||||
NSObject *newValue = nil;
|
|
||||||
MCChoice *foundChoice = [self findChoiceWithValue:value inArray:_choices];
|
|
||||||
if (!_choices) {
|
|
||||||
newValue = value;
|
|
||||||
} else if (!foundChoice) {
|
|
||||||
newValue = nil;
|
|
||||||
} else {
|
|
||||||
newValue = foundChoice.value;
|
|
||||||
}
|
|
||||||
_selectedChoice = foundChoice;
|
|
||||||
if (_selectedValue != newValue) {
|
|
||||||
_selectedValue = newValue;
|
|
||||||
[self notifyChangedValue];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-(NSObject*)selectedValue {
|
|
||||||
return _selectedValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)awakeFromNib {
|
|
||||||
[super awakeFromNib];
|
|
||||||
// Initialization code
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
|
|
||||||
[super setSelected:selected animated:animated];
|
|
||||||
|
|
||||||
// Configure the view for the selected state
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)selectedSegmentChanged:(id)sender {
|
|
||||||
NSInteger selectedIndex = _segmentedControl.selectedSegmentIndex;
|
|
||||||
MCChoice *newChoice = [_choices objectAtIndex:selectedIndex];
|
|
||||||
_selectedChoice = newChoice;
|
|
||||||
_selectedValue = _selectedChoice.value;
|
|
||||||
[self notifyChangedValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,29 +0,0 @@
|
|||||||
//
|
|
||||||
// MCSelectFieldTableViewCell.h
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/26/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
|
||||||
#import "MCFormFieldDelegate.h"
|
|
||||||
#import "MCChoice.h"
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
@interface MCSelectFieldTableViewCell : UITableViewCell<UIPickerViewDataSource, UIPickerViewDelegate, UITextFieldDelegate>
|
|
||||||
|
|
||||||
@property NSString* identifier;
|
|
||||||
@property NSString* label;
|
|
||||||
@property NSObject* selectedValue;
|
|
||||||
@property NSArray<MCChoice*>* choices;
|
|
||||||
|
|
||||||
@property (weak, nonatomic) id<MCFormFieldDelegate> delegate;
|
|
||||||
@property (weak, nonatomic) IBOutlet UITextField *textField;
|
|
||||||
@property (weak, nonatomic) IBOutlet UILabel *labelView;
|
|
||||||
@property (nonatomic) UIPickerView *pickerView;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
||||||
@@ -1,182 +0,0 @@
|
|||||||
//
|
|
||||||
// MCSelectFieldTableViewCell.m
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/26/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "MCSelectFieldTableViewCell.h"
|
|
||||||
|
|
||||||
@implementation MCSelectFieldTableViewCell {
|
|
||||||
MCChoice* _selectedChoice;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(MCChoice*)findChoiceWithValue:(NSObject*)value
|
|
||||||
inArray:(NSArray*)array {
|
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id element, NSDictionary *bindings) {
|
|
||||||
if (![element isKindOfClass:[MCChoice class]]) {
|
|
||||||
return NO;
|
|
||||||
}
|
|
||||||
MCChoice *choice = (MCChoice*)element;
|
|
||||||
return [choice.value isEqual:value];
|
|
||||||
}];
|
|
||||||
NSArray<MCChoice*> *matchingChoices = [array filteredArrayUsingPredicate:predicate];
|
|
||||||
MCChoice *foundChoice = matchingChoices.count > 0 ? matchingChoices.firstObject : nil;
|
|
||||||
return foundChoice;
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)notifyChangedValue {
|
|
||||||
[self updateView];
|
|
||||||
|
|
||||||
if (_delegate) {
|
|
||||||
[_delegate editableValueDidChange:_selectedValue
|
|
||||||
forIdentifier:_identifier
|
|
||||||
andType:kMCFieldValueTypeChoice];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
-(void)updateView {
|
|
||||||
self.textField.text = _selectedChoice.label;
|
|
||||||
if (_choices && _choices.count > 0) {
|
|
||||||
NSInteger selectedRow = [_choices indexOfObject:_selectedChoice];
|
|
||||||
if (selectedRow != NSNotFound) {
|
|
||||||
[self.pickerView selectRow:selectedRow inComponent:0 animated:YES];
|
|
||||||
} else {
|
|
||||||
[self.pickerView selectRow:0 inComponent:0 animated:YES];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@synthesize choices = _choices;
|
|
||||||
-(void)setChoices:(NSArray<MCChoice*>*)choices {
|
|
||||||
MCChoice *foundChoice = [self findChoiceWithValue:_selectedValue
|
|
||||||
inArray:choices];
|
|
||||||
if ([_choices isEqualToArray:choices]) {
|
|
||||||
// Choices are equivalent so selected value. Pointer may have changed but content hasn't.
|
|
||||||
_selectedChoice = foundChoice;
|
|
||||||
} else if (foundChoice) {
|
|
||||||
// Choices are different but selected value is in the new choices. Pointer may have changed but content hasn't.
|
|
||||||
_selectedChoice = foundChoice;
|
|
||||||
} else {
|
|
||||||
// Choices are different and selected value is not in the new choices. Select the first choice or nil if there are none.
|
|
||||||
_selectedChoice = [choices firstObject];
|
|
||||||
}
|
|
||||||
_choices = choices;
|
|
||||||
|
|
||||||
if (_selectedValue != foundChoice.value) {
|
|
||||||
self.selectedValue = foundChoice.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
[self updateView];
|
|
||||||
}
|
|
||||||
-(NSArray<MCChoice*>*)choices {
|
|
||||||
return _choices;
|
|
||||||
}
|
|
||||||
|
|
||||||
@synthesize label = _label;
|
|
||||||
-(void)setLabel:(NSString*)label {
|
|
||||||
if (![_label isEqualToString:label]) {
|
|
||||||
_label = label;
|
|
||||||
}
|
|
||||||
if (_labelView && ![_labelView.text isEqualToString:label]) {
|
|
||||||
_labelView.text = label;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-(NSString*)label {
|
|
||||||
return _label;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@synthesize selectedValue = _selectedValue;
|
|
||||||
-(void)setSelectedValue:(NSObject*)value {
|
|
||||||
NSObject *newValue = nil;
|
|
||||||
MCChoice *foundChoice = [self findChoiceWithValue:value inArray:_choices];
|
|
||||||
if (!_choices) {
|
|
||||||
newValue = value;
|
|
||||||
} else if (!foundChoice) {
|
|
||||||
foundChoice = [_choices firstObject];
|
|
||||||
newValue = foundChoice.value;
|
|
||||||
} else {
|
|
||||||
newValue = foundChoice.value;
|
|
||||||
}
|
|
||||||
_selectedChoice = foundChoice;
|
|
||||||
if (_selectedValue != newValue) {
|
|
||||||
_selectedValue = newValue;
|
|
||||||
[self notifyChangedValue];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
-(NSObject*)selectedValue {
|
|
||||||
return _selectedValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)awakeFromNib {
|
|
||||||
[super awakeFromNib];
|
|
||||||
self.pickerView = [[UIPickerView alloc] init];
|
|
||||||
self.pickerView.delegate = self;
|
|
||||||
self.pickerView.dataSource = self;
|
|
||||||
self.textField.inputView = self.pickerView;
|
|
||||||
self.pickerView.translatesAutoresizingMaskIntoConstraints = NO;
|
|
||||||
|
|
||||||
UIToolbar *toolbar = [[UIToolbar alloc] init];
|
|
||||||
[toolbar sizeToFit];
|
|
||||||
|
|
||||||
UIBarButtonItem *button =
|
|
||||||
[[UIBarButtonItem alloc] initWithTitle:NSLocalizedString(@"Done", @"Button label")
|
|
||||||
style:UIBarButtonItemStylePlain
|
|
||||||
target:self
|
|
||||||
action:@selector(choiceSelected)];
|
|
||||||
[toolbar setItems:@[button]
|
|
||||||
animated:true];
|
|
||||||
[toolbar setUserInteractionEnabled:YES];
|
|
||||||
self.textField.inputAccessoryView = toolbar;
|
|
||||||
self.textField.hidden = NO;
|
|
||||||
self.textField.text = _selectedChoice.label;
|
|
||||||
self.textField.delegate = self;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setSelected:(BOOL)selected animated:(BOOL)animated {
|
|
||||||
[super setSelected:selected animated:animated];
|
|
||||||
|
|
||||||
// Configure the view for the selected state
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)choiceSelected {
|
|
||||||
[self endEditing:true];
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - UIPickerViewDataSource
|
|
||||||
|
|
||||||
- (NSInteger)numberOfComponentsInPickerView:(UIPickerView *)pickerView {
|
|
||||||
return 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSInteger)pickerView:(UIPickerView *)pickerView
|
|
||||||
numberOfRowsInComponent:(NSInteger)component {
|
|
||||||
return [_choices count];
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - UIPickerViewDelegate
|
|
||||||
|
|
||||||
- (NSString *)pickerView:(UIPickerView *)pickerView
|
|
||||||
titleForRow:(NSInteger)row
|
|
||||||
forComponent:(NSInteger)component {
|
|
||||||
return [_choices objectAtIndex:row].label;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)pickerView:(UIPickerView *)pickerView
|
|
||||||
didSelectRow:(NSInteger)row
|
|
||||||
inComponent:(NSInteger)component {
|
|
||||||
|
|
||||||
_selectedChoice = [_choices objectAtIndex:row];
|
|
||||||
self.textField.text = _selectedChoice.label;
|
|
||||||
self.selectedValue = _selectedChoice.value;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - UITextFieldDelegate
|
|
||||||
|
|
||||||
- (void)textFieldDidBeginEditing:(UITextField *)textField {
|
|
||||||
[self updateView];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,25 +0,0 @@
|
|||||||
//
|
|
||||||
// EditableShortStringTableViewCell.h
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/9/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
|
||||||
#import "MCFormFieldDelegate.h"
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
@interface MCShortStringFieldTableViewCell : UITableViewCell
|
|
||||||
|
|
||||||
@property NSString* identifier;
|
|
||||||
@property NSString* label;
|
|
||||||
@property NSString* value;
|
|
||||||
|
|
||||||
@property (weak, nonatomic) id<MCFormFieldDelegate> delegate;
|
|
||||||
@property (weak, nonatomic) IBOutlet UITextField *textField;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
||||||
@@ -1,57 +0,0 @@
|
|||||||
//
|
|
||||||
// EditableShortStringTableViewCell.m
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/9/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "MCShortStringFieldTableViewCell.h"
|
|
||||||
|
|
||||||
@implementation MCShortStringFieldTableViewCell
|
|
||||||
|
|
||||||
@synthesize value = _value;
|
|
||||||
|
|
||||||
- (void)setValue:(NSString*)value {
|
|
||||||
if (![_value isEqualToString:value]) {
|
|
||||||
_value = value;
|
|
||||||
if (self.textField) {
|
|
||||||
self.textField.text = value;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSString*)value {
|
|
||||||
return _value;
|
|
||||||
}
|
|
||||||
|
|
||||||
@synthesize label = _label;
|
|
||||||
|
|
||||||
- (void)setLabel:(NSString*)label {
|
|
||||||
if (![_label isEqualToString:label]) {
|
|
||||||
_label = label;
|
|
||||||
if (self.textField) {
|
|
||||||
self.textField.placeholder = label;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (NSString*)label {
|
|
||||||
return _label;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)awakeFromNib {
|
|
||||||
[super awakeFromNib];
|
|
||||||
[self.textField addTarget:self action:@selector(valueChanged:) forControlEvents:UIControlEventEditingChanged];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)valueChanged:(UITextField*)textField {
|
|
||||||
NSString *newValue = textField.text;
|
|
||||||
if (self.delegate != nil) {
|
|
||||||
[self.delegate editableValueDidChange:newValue
|
|
||||||
forIdentifier:self.identifier
|
|
||||||
andType:kMCFieldValueTypeString];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
60
MonsterCards/Views/Library.swift
Normal file
60
MonsterCards/Views/Library.swift
Normal file
@@ -0,0 +1,60 @@
|
|||||||
|
//
|
||||||
|
// Library.swift
|
||||||
|
// MonsterCards
|
||||||
|
//
|
||||||
|
// Created by Tom Hicks on 1/15/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct Library: View {
|
||||||
|
@Environment(\.managedObjectContext) private var viewContext
|
||||||
|
|
||||||
|
@FetchRequest(
|
||||||
|
sortDescriptors: [
|
||||||
|
NSSortDescriptor(keyPath: \Monster.name, ascending: true),
|
||||||
|
],
|
||||||
|
animation: .default)
|
||||||
|
var allMonsters: FetchedResults<Monster>
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
NavigationView{
|
||||||
|
List(allMonsters) { monster in
|
||||||
|
NavigationLink(destination: MonsterDetail(monster: monster)) {
|
||||||
|
Text(monster.name ?? "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.navigationTitle("Library")
|
||||||
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
.toolbar(content: {
|
||||||
|
ToolbarItem(placement: .primaryAction) {
|
||||||
|
Button(action: addMonster) {
|
||||||
|
Image(systemName:"plus")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func addMonster() {
|
||||||
|
withAnimation {
|
||||||
|
let newItem = Monster(context: viewContext)
|
||||||
|
newItem.name = "Unnamed Monster"
|
||||||
|
|
||||||
|
do {
|
||||||
|
try viewContext.save()
|
||||||
|
} catch {
|
||||||
|
// 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.
|
||||||
|
let nsError = error as NSError
|
||||||
|
fatalError("Unresolved error \(nsError), \(nsError.userInfo)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Library_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
return Library().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
//
|
|
||||||
// LibraryViewController.h
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/4/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
@interface LibraryViewController : UITableViewController <UITableViewDelegate, UITableViewDataSource>
|
|
||||||
|
|
||||||
@property (strong, nonatomic) IBOutlet UITableView *monstersTable;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
||||||
@@ -1,94 +0,0 @@
|
|||||||
//
|
|
||||||
// LibraryViewController.m
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/4/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "LibraryViewController.h"
|
|
||||||
#import "Monster.h"
|
|
||||||
#import "MonsterViewController.h"
|
|
||||||
#import "AppDelegate.h"
|
|
||||||
|
|
||||||
@interface LibraryViewController ()
|
|
||||||
|
|
||||||
@property NSArray* allMonsters;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation LibraryViewController {
|
|
||||||
NSManagedObjectContext *_context;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)viewDidLoad {
|
|
||||||
[super viewDidLoad];
|
|
||||||
AppDelegate *appDelegate = (AppDelegate*)UIApplication.sharedApplication.delegate;
|
|
||||||
_context = appDelegate.persistentContainer.viewContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)viewWillAppear:(BOOL)animated {
|
|
||||||
self.allMonsters = [_context executeFetchRequest:[Monster fetchRequest] error:nil];
|
|
||||||
[self.monstersTable reloadData];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)addNewMonster:(id)sender {
|
|
||||||
Monster *monster = [[Monster alloc] initWithContext:_context];
|
|
||||||
monster.name = NSLocalizedString(@"Unnamed Monster", @"The default name of a new monster.");
|
|
||||||
self.allMonsters = [self.allMonsters arrayByAddingObject:monster];
|
|
||||||
//DispatchQueue.main.async{"code here"}
|
|
||||||
[_context save:nil];
|
|
||||||
[self.monstersTable reloadData];
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - Navigation
|
|
||||||
|
|
||||||
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
|
|
||||||
if ([@"ShowMonsterDetail" isEqualToString:segue.identifier]) {
|
|
||||||
NSIndexPath *indexPath = [self.tableView indexPathForSelectedRow];
|
|
||||||
if ([segue.destinationViewController isKindOfClass:[MonsterViewController class]]) {
|
|
||||||
MonsterViewController *vc = (MonsterViewController*)segue.destinationViewController;
|
|
||||||
vc.monster = [self.allMonsters objectAtIndex:indexPath.row];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - UITableViewDataSource
|
|
||||||
|
|
||||||
- (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section {
|
|
||||||
return [self.allMonsters count];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(nonnull NSIndexPath *)indexPath {
|
|
||||||
static NSString *simpleTableIdentifier = @"MonsterCell";
|
|
||||||
|
|
||||||
UITableViewCell *cell = [self.tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
|
|
||||||
|
|
||||||
if (cell == nil) {
|
|
||||||
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
|
|
||||||
}
|
|
||||||
|
|
||||||
Monster *monster = (Monster*)[self.allMonsters objectAtIndex:indexPath.row];
|
|
||||||
|
|
||||||
cell.textLabel.text = monster.name;
|
|
||||||
|
|
||||||
return cell;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - UITableViewDelegate
|
|
||||||
|
|
||||||
- (UISwipeActionsConfiguration *)tableView:(UITableView *)tableView
|
|
||||||
trailingSwipeActionsConfigurationForRowAtIndexPath:(NSIndexPath *)indexPath {
|
|
||||||
UIContextualAction *action = [UIContextualAction contextualActionWithStyle:UIContextualActionStyleDestructive title:NSLocalizedString(@"Delete", @"Command to delete an object.") handler:^(UIContextualAction *action, __kindof UIView *sourceView, void (^completionHandler)(BOOL actionPerformed)) {
|
|
||||||
Monster *monster = [self.allMonsters objectAtIndex:indexPath.row];
|
|
||||||
[self->_context deleteObject:monster];
|
|
||||||
[self->_context save:nil];
|
|
||||||
self.allMonsters = [self->_context executeFetchRequest:[Monster fetchRequest] error:nil];
|
|
||||||
[self.tableView reloadData];
|
|
||||||
}];
|
|
||||||
|
|
||||||
UISwipeActionsConfiguration *config = [UISwipeActionsConfiguration configurationWithActions:[NSArray arrayWithObject:action]];
|
|
||||||
return config;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
34
MonsterCards/Views/MCAdvantagePicker.swift
Normal file
34
MonsterCards/Views/MCAdvantagePicker.swift
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
//
|
||||||
|
// MCAdvantagePicker.swift
|
||||||
|
// MonsterCards
|
||||||
|
//
|
||||||
|
// Created by Tom Hicks on 1/17/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct MCAdvantagePicker: View {
|
||||||
|
var label: String = ""
|
||||||
|
var value: Binding<AdvantageType>
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text(label)
|
||||||
|
.font(.caption2)
|
||||||
|
Picker(
|
||||||
|
selection: value,
|
||||||
|
label: Text(label)) {
|
||||||
|
ForEach(AdvantageType.allCases) { advType in
|
||||||
|
Text(advType.displayName).tag(advType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.pickerStyle(SegmentedPickerStyle())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MCAdvantagePicker_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
MCAdvantagePicker(value: .constant(AdvantageType.none))
|
||||||
|
}
|
||||||
|
}
|
||||||
34
MonsterCards/Views/MCProficiencyPicker.swift
Normal file
34
MonsterCards/Views/MCProficiencyPicker.swift
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
//
|
||||||
|
// MCProficiencyPicker.swift
|
||||||
|
// MonsterCards
|
||||||
|
//
|
||||||
|
// Created by Tom Hicks on 1/17/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct MCProficiencyPicker: View {
|
||||||
|
var label: String = ""
|
||||||
|
var value: Binding<ProficiencyType>
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text(label)
|
||||||
|
.font(.caption2)
|
||||||
|
Picker(
|
||||||
|
selection: value,
|
||||||
|
label: Text(label)) {
|
||||||
|
ForEach(ProficiencyType.allCases) { profType in
|
||||||
|
Text(profType.displayName).tag(profType)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.pickerStyle(SegmentedPickerStyle())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MCProficiencyPicker_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
MCProficiencyPicker(value: .constant(ProficiencyType.none))
|
||||||
|
}
|
||||||
|
}
|
||||||
37
MonsterCards/Views/MCStepperField.swift
Normal file
37
MonsterCards/Views/MCStepperField.swift
Normal file
@@ -0,0 +1,37 @@
|
|||||||
|
//
|
||||||
|
// MCStepperField.swift
|
||||||
|
// MonsterCards
|
||||||
|
//
|
||||||
|
// Created by Tom Hicks on 1/16/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct MCStepperField: View {
|
||||||
|
var label: String = ""
|
||||||
|
var prefix: String = ""
|
||||||
|
var step: Int = 1
|
||||||
|
var suffix: String = ""
|
||||||
|
var value: Binding<Int64>
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text(label)
|
||||||
|
.font(.caption2)
|
||||||
|
Stepper(
|
||||||
|
value: value,
|
||||||
|
step: step
|
||||||
|
) {
|
||||||
|
Text("\(prefix)\(value.wrappedValue)\(suffix)" as String)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MCStepperField_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
MCStepperField(
|
||||||
|
label: "Hit Dice",
|
||||||
|
value: .constant(4))
|
||||||
|
}
|
||||||
|
}
|
||||||
28
MonsterCards/Views/MCTextField.swift
Normal file
28
MonsterCards/Views/MCTextField.swift
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
//
|
||||||
|
// MCTextField.swift
|
||||||
|
// MonsterCards
|
||||||
|
//
|
||||||
|
// Created by Tom Hicks on 1/16/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct MCTextField: View {
|
||||||
|
var label: String
|
||||||
|
var value: Binding<String>
|
||||||
|
var body: some View {
|
||||||
|
VStack(alignment: .leading) {
|
||||||
|
Text(label)
|
||||||
|
.font(.caption2)
|
||||||
|
TextField(label, text: value)
|
||||||
|
.autocapitalization(/*@START_MENU_TOKEN@*/.none/*@END_MENU_TOKEN@*/)
|
||||||
|
// .padding(.top, -4)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MCTextField_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
MCTextField(label: "Name", value: .constant("Ted"))
|
||||||
|
}
|
||||||
|
}
|
||||||
177
MonsterCards/Views/MonsterDetail.swift
Normal file
177
MonsterCards/Views/MonsterDetail.swift
Normal file
@@ -0,0 +1,177 @@
|
|||||||
|
//
|
||||||
|
// MonsterDetail.swift
|
||||||
|
// MonsterCards
|
||||||
|
//
|
||||||
|
// Created by Tom Hicks on 1/15/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct LabeledField<Content: View>: View {
|
||||||
|
let content: Content
|
||||||
|
let label: String
|
||||||
|
|
||||||
|
@inlinable public init(
|
||||||
|
_ label: String,
|
||||||
|
@ViewBuilder content: () -> Content) {
|
||||||
|
self.content = content()
|
||||||
|
self.label = label
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
HStack(alignment: .top) {
|
||||||
|
Text(label)
|
||||||
|
.fontWeight(.bold)
|
||||||
|
content
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SectionDivider: View {
|
||||||
|
var body: some View {
|
||||||
|
Image("section-divider") // divider
|
||||||
|
.resizable()
|
||||||
|
.scaledToFit()
|
||||||
|
.padding(.vertical, 4)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct SmallAbilityScore: View {
|
||||||
|
var label: String
|
||||||
|
var score: Int64
|
||||||
|
var modifier: Int
|
||||||
|
|
||||||
|
public init(
|
||||||
|
_ label: String,
|
||||||
|
_ score: Int64,
|
||||||
|
_ modifier: Int) {
|
||||||
|
self.label = label
|
||||||
|
self.score = score
|
||||||
|
self.modifier = modifier
|
||||||
|
}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
VStack {
|
||||||
|
Text(label)
|
||||||
|
.fontWeight(.bold)
|
||||||
|
Text(String(format: "%d (%+d)", score, modifier))
|
||||||
|
}
|
||||||
|
.frame(maxWidth: .infinity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MonsterDetail: View {
|
||||||
|
let kTextColor: Color = Color(hex: 0x982818)
|
||||||
|
|
||||||
|
var monster: Monster
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
ScrollView {
|
||||||
|
VStack (alignment: .leading) {
|
||||||
|
// meta: "(large humanoid (elf) lawful evil"
|
||||||
|
Text(monster.meta)
|
||||||
|
.font(.subheadline)
|
||||||
|
.foregroundColor(.secondary)
|
||||||
|
|
||||||
|
SectionDivider()
|
||||||
|
|
||||||
|
// AC
|
||||||
|
LabeledField("Armor Class") {
|
||||||
|
Text(monster.armorClassDescription)// armor class
|
||||||
|
}
|
||||||
|
// HP
|
||||||
|
LabeledField("Hit Points") {
|
||||||
|
Text(monster.hitPoints) // hit points
|
||||||
|
}
|
||||||
|
// Speed
|
||||||
|
LabeledField("Speed") {
|
||||||
|
Text(monster.speed) // speed
|
||||||
|
}
|
||||||
|
|
||||||
|
SectionDivider()
|
||||||
|
|
||||||
|
// Ability Scores
|
||||||
|
HStack {
|
||||||
|
SmallAbilityScore("STR", monster.strengthScore, monster.strengthModifier)
|
||||||
|
SmallAbilityScore("DEX", monster.dexterityScore, monster.dexterityModifier)
|
||||||
|
SmallAbilityScore("CON", monster.constitutionScore, monster.constitutionModifier)
|
||||||
|
SmallAbilityScore("INT", monster.intelligenceScore, monster.intelligenceModifier)
|
||||||
|
SmallAbilityScore("WIS", monster.wisdomScore, monster.wisdomModifier)
|
||||||
|
SmallAbilityScore("CHA", monster.charismaScore, monster.charismaModifier)
|
||||||
|
}
|
||||||
|
|
||||||
|
SectionDivider()
|
||||||
|
|
||||||
|
let savingThrowsDescription = monster.savingThrowsDescription
|
||||||
|
if (!savingThrowsDescription.isEmpty) {
|
||||||
|
LabeledField("Saving Throws") {
|
||||||
|
Text(monster.savingThrowsDescription)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.padding(.horizontal)
|
||||||
|
.foregroundColor(kTextColor)
|
||||||
|
}
|
||||||
|
.toolbar(content: {
|
||||||
|
ToolbarItem(placement: .primaryAction) {
|
||||||
|
NavigationLink("Edit", destination: EditMonster(monster: monster))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.navigationTitle(monster.name ?? "")
|
||||||
|
.navigationBarTitleDisplayMode(.inline)
|
||||||
|
}
|
||||||
|
|
||||||
|
private func editMonster() {
|
||||||
|
print("Edit Monster pressed")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MonsterDetail_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
let context = PersistenceController.preview.container.viewContext
|
||||||
|
let monster = Monster.init(context: context)
|
||||||
|
monster.name = "Steve"
|
||||||
|
monster.size = "Medium"
|
||||||
|
monster.type = "humanoid"
|
||||||
|
monster.subtype = "human"
|
||||||
|
monster.alignment = "LG"
|
||||||
|
monster.hitDice = 6
|
||||||
|
monster.hasCustomHP = true
|
||||||
|
monster.customHP = "12 (1d10)+2"
|
||||||
|
monster.baseSpeed = 5
|
||||||
|
monster.burrowSpeed = 10
|
||||||
|
monster.climbSpeed = 15
|
||||||
|
monster.flySpeed = 20
|
||||||
|
monster.swimSpeed = 25
|
||||||
|
monster.canHover = true
|
||||||
|
monster.hasCustomSpeed = false
|
||||||
|
monster.customSpeed = "walk: 5 ft."
|
||||||
|
monster.strengthScore = 8
|
||||||
|
monster.dexterityScore = 10
|
||||||
|
monster.constitutionScore = 12
|
||||||
|
monster.intelligenceScore = 14
|
||||||
|
monster.wisdomScore = 16
|
||||||
|
monster.charismaScore = 18
|
||||||
|
monster.strengthSavingThrowAdvantageEnum = AdvantageType.none
|
||||||
|
monster.strengthSavingThrowProficiencyEnum = ProficiencyType.none
|
||||||
|
monster.dexteritySavingThrowAdvantageEnum = AdvantageType.advantage
|
||||||
|
monster.dexteritySavingThrowProficiencyEnum = ProficiencyType.proficient
|
||||||
|
monster.constitutionSavingThrowAdvantageEnum = AdvantageType.disadvantage
|
||||||
|
monster.constitutionSavingThrowProficiencyEnum = ProficiencyType.expertise
|
||||||
|
monster.intelligenceSavingThrowAdvantageEnum = AdvantageType.none
|
||||||
|
monster.intelligenceSavingThrowProficiencyEnum = ProficiencyType.expertise
|
||||||
|
monster.wisdomSavingThrowAdvantageEnum = AdvantageType.advantage
|
||||||
|
monster.wisdomSavingThrowProficiencyEnum = ProficiencyType.proficient
|
||||||
|
monster.charismaSavingThrowAdvantageEnum = AdvantageType.disadvantage
|
||||||
|
monster.charismaSavingThrowProficiencyEnum = ProficiencyType.none
|
||||||
|
|
||||||
|
return Group {
|
||||||
|
MonsterDetail(monster: monster)
|
||||||
|
.environment(\.managedObjectContext, context)
|
||||||
|
.previewDevice("iPod touch (7th generation)")
|
||||||
|
MonsterDetail(monster: monster)
|
||||||
|
.environment(\.managedObjectContext, context)
|
||||||
|
.previewDevice("iPad Pro (11-inch) (2nd generation)")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,33 +0,0 @@
|
|||||||
//
|
|
||||||
// MonsterViewController.h
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/4/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
|
||||||
#import "Monster.h"
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
@interface MonsterViewController : UIViewController
|
|
||||||
|
|
||||||
@property (weak, nonatomic) IBOutlet UILabel *monsterName;
|
|
||||||
@property (weak, nonatomic) IBOutlet UILabel *monsterMeta;
|
|
||||||
@property (weak, nonatomic) IBOutlet UILabel *monsterArmorClass;
|
|
||||||
@property (weak, nonatomic) IBOutlet UILabel *monsterHitPoints;
|
|
||||||
@property (weak, nonatomic) IBOutlet UILabel *monsterSpeed;
|
|
||||||
@property (weak, nonatomic) IBOutlet UILabel *monsterStrength;
|
|
||||||
@property (weak, nonatomic) IBOutlet UILabel *monsterDexterity;
|
|
||||||
@property (weak, nonatomic) IBOutlet UILabel *monsterConstitution;
|
|
||||||
@property (weak, nonatomic) IBOutlet UILabel *monsterIntelligence;
|
|
||||||
@property (weak, nonatomic) IBOutlet UILabel *monsterWisdom;
|
|
||||||
@property (weak, nonatomic) IBOutlet UILabel *monsterCharisma;
|
|
||||||
@property (weak, nonatomic) IBOutlet UILabel *monsterSavingThrows;
|
|
||||||
|
|
||||||
@property Monster* monster;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
||||||
@@ -1,134 +0,0 @@
|
|||||||
//
|
|
||||||
// MonsterViewController.m
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/4/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "MonsterViewController.h"
|
|
||||||
#import "EditMonsterViewController.h"
|
|
||||||
#import "HTMLHelper.h"
|
|
||||||
#import "AppDelegate.h"
|
|
||||||
|
|
||||||
@interface MonsterViewController ()
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
NSString *const defaultFontFamily = @"helvetica";
|
|
||||||
NSString *const defaultFontSize = @"12pt";
|
|
||||||
NSString *const defaultTextColor = @"#9B2818";
|
|
||||||
|
|
||||||
NSString* makeHTMLFragmentString(NSString* format, ...) {
|
|
||||||
va_list args;
|
|
||||||
va_start(args, format);
|
|
||||||
NSString *childString = [[NSString alloc] initWithFormat:format arguments:args];
|
|
||||||
va_end(args);
|
|
||||||
|
|
||||||
NSString *formattedString = [NSString stringWithFormat:@"<span style=\"font-family: %@; font-size: %@; color: %@;\">%@</span>", defaultFontFamily, defaultFontSize, defaultTextColor, childString];
|
|
||||||
return formattedString;
|
|
||||||
}
|
|
||||||
|
|
||||||
@implementation MonsterViewController {
|
|
||||||
NSManagedObjectContext *_context;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)viewDidLoad {
|
|
||||||
[super viewDidLoad];
|
|
||||||
AppDelegate *appDelegate = (AppDelegate*)UIApplication.sharedApplication.delegate;
|
|
||||||
_context = appDelegate.persistentContainer.viewContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)viewWillAppear:(BOOL)animated {
|
|
||||||
[_context refreshObject:self.monster mergeChanges:NO];
|
|
||||||
if (self.monsterName != nil) {
|
|
||||||
self.monsterName.text = self.monster.name;
|
|
||||||
} else if (self.navigationItem != nil) {
|
|
||||||
self.navigationItem.title = self.monster.name;
|
|
||||||
}
|
|
||||||
if (self.monsterName != nil) {
|
|
||||||
self.monsterName.text = self.monster.name;
|
|
||||||
} else if (self.navigationItem != nil) {
|
|
||||||
if (self.monster.name == nil) {
|
|
||||||
self.navigationItem.title = @"Unnamed Monster";
|
|
||||||
} else {
|
|
||||||
self.navigationItem.title = self.monster.name;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (self.monsterMeta != nil) {
|
|
||||||
NSString *metaText = self.monster.meta;
|
|
||||||
if (metaText == nil) {
|
|
||||||
self.monsterMeta.text = @"";
|
|
||||||
} else {
|
|
||||||
self.monsterMeta.text = metaText;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (self.monsterArmorClass != nil) {
|
|
||||||
NSString *armorClassDescription = self.monster.armorClassDescription;
|
|
||||||
if (armorClassDescription == nil) {
|
|
||||||
self.monsterArmorClass.text = @"";
|
|
||||||
} else {
|
|
||||||
self.monsterArmorClass.attributedText = [HTMLHelper attributedStringFromHTML:makeHTMLFragmentString(@"<b>Armor Class</b> %@</span>", armorClassDescription)];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (self.monsterHitPoints != nil) {
|
|
||||||
NSString *hitPointsDescription = self.monster.hitPointsDescription;
|
|
||||||
if (hitPointsDescription == nil) {
|
|
||||||
self.monsterHitPoints.text = @"";
|
|
||||||
} else {
|
|
||||||
self.monsterHitPoints.attributedText = [HTMLHelper attributedStringFromHTML:makeHTMLFragmentString(@"<b>Hit Points</b> %@", hitPointsDescription)];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (self.monsterSpeed != nil) {
|
|
||||||
NSString *speedDescription = self.monster.speedDescription;
|
|
||||||
if (speedDescription == nil) {
|
|
||||||
self.monsterSpeed.text = @"";
|
|
||||||
} else {
|
|
||||||
self.monsterSpeed.attributedText = [HTMLHelper attributedStringFromHTML:makeHTMLFragmentString(@"<b>Speed</b> %@", speedDescription)];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (self.monsterStrength) {
|
|
||||||
self.monsterStrength.text = self.monster.strengthDescription;
|
|
||||||
}
|
|
||||||
if (self.monsterDexterity) {
|
|
||||||
self.monsterDexterity.text = self.monster.dexterityDescription;
|
|
||||||
}
|
|
||||||
if (self.monsterConstitution) {
|
|
||||||
self.monsterConstitution.text = self.monster.constitutionDescription;
|
|
||||||
}
|
|
||||||
if (self.monsterIntelligence) {
|
|
||||||
self.monsterIntelligence.text = self.monster.intelligenceDescription;
|
|
||||||
}
|
|
||||||
if (self.monsterWisdom) {
|
|
||||||
self.monsterWisdom.text = self.monster.wisdomDescription;
|
|
||||||
}
|
|
||||||
if (self.monsterCharisma) {
|
|
||||||
self.monsterCharisma.text = self.monster.charismaDescription;
|
|
||||||
}
|
|
||||||
if (self.monsterSavingThrows) {
|
|
||||||
NSString *savingThrowsDescription = self.monster.savingThrowsDescription;
|
|
||||||
if (savingThrowsDescription == nil) {
|
|
||||||
self.monsterSavingThrows.text = @"";
|
|
||||||
} else {
|
|
||||||
self.monsterSavingThrows.attributedText = [HTMLHelper attributedStringFromHTML:makeHTMLFragmentString(@"<b>Saving Throws</b> %@", savingThrowsDescription)];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (IBAction)unwindWithSegue:(UIStoryboardSegue *)unwindSegue {
|
|
||||||
// UIViewController *sourceViewController = unwindSegue.sourceViewController;
|
|
||||||
// Use data from the view controller which initiated the unwind segue
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - Navigation
|
|
||||||
|
|
||||||
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
|
|
||||||
if ([@"EditMonster" isEqualToString:segue.identifier]) {
|
|
||||||
if ([segue.destinationViewController isKindOfClass:[EditMonsterViewController class]]) {
|
|
||||||
EditMonsterViewController *vc = (EditMonsterViewController*)segue.destinationViewController;
|
|
||||||
vc.originalMonster = self.monster;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
41
MonsterCards/Views/Search.swift
Normal file
41
MonsterCards/Views/Search.swift
Normal file
@@ -0,0 +1,41 @@
|
|||||||
|
//
|
||||||
|
// Search.swift
|
||||||
|
// MonsterCards
|
||||||
|
//
|
||||||
|
// Created by Tom Hicks on 1/15/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct Search: View {
|
||||||
|
@State private var searchText = ""
|
||||||
|
|
||||||
|
@Environment(\.managedObjectContext) var managedObjectContext
|
||||||
|
@FetchRequest(
|
||||||
|
sortDescriptors: [
|
||||||
|
NSSortDescriptor(keyPath: \Monster.name, ascending: true),
|
||||||
|
],
|
||||||
|
animation: .default)
|
||||||
|
var allMonsters: FetchedResults<Monster>
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
NavigationView {
|
||||||
|
List {
|
||||||
|
SearchBar(text: $searchText)
|
||||||
|
.padding(.top, -30)
|
||||||
|
|
||||||
|
ForEach(allMonsters.filter({searchText.isEmpty ? true : $0.name?.containsCaseInsensitive(searchText) ?? false })) { monster in
|
||||||
|
NavigationLink(destination: MonsterDetail(monster: monster)) {
|
||||||
|
Text(monster.name ?? "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Search_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
Search().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext)
|
||||||
|
}
|
||||||
|
}
|
||||||
63
MonsterCards/Views/SearchBar.swift
Normal file
63
MonsterCards/Views/SearchBar.swift
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
//
|
||||||
|
// SearchBar.swift
|
||||||
|
// MonsterCards
|
||||||
|
//
|
||||||
|
// Created by Tom Hicks on 1/15/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
import Foundation
|
||||||
|
import UIKit
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
struct SearchBar: UIViewRepresentable {
|
||||||
|
|
||||||
|
@Binding var text: String
|
||||||
|
|
||||||
|
class Coordinator: NSObject, UISearchBarDelegate {
|
||||||
|
@Binding var text: String
|
||||||
|
|
||||||
|
init(text: Binding<String>) {
|
||||||
|
_text = text
|
||||||
|
}
|
||||||
|
|
||||||
|
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
|
||||||
|
text = searchText
|
||||||
|
}
|
||||||
|
|
||||||
|
func searchBarSearchButtonClicked(_ searchBar: UISearchBar) {
|
||||||
|
UIApplication.shared.endEditing()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeCoordinator() -> SearchBar.Coordinator {
|
||||||
|
return Coordinator(text: $text)
|
||||||
|
}
|
||||||
|
|
||||||
|
func makeUIView(context: UIViewRepresentableContext<SearchBar>) -> UISearchBar {
|
||||||
|
let searchBar = UISearchBar(frame: .zero)
|
||||||
|
searchBar.delegate = context.coordinator
|
||||||
|
searchBar.autocapitalizationType = .none
|
||||||
|
return searchBar
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateUIView(_ uiView: UISearchBar, context: UIViewRepresentableContext<SearchBar>) {
|
||||||
|
uiView.text = text
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension String {
|
||||||
|
func containsCaseInsensitive(_ string: String) -> Bool {
|
||||||
|
return self.localizedCaseInsensitiveContains(string)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
extension UIApplication {
|
||||||
|
func endEditing() {
|
||||||
|
sendAction(#selector(UIResponder.resignFirstResponder), to: nil, from: nil, for: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
struct SearchBar_Previews: PreviewProvider {
|
||||||
|
static var previews: some View {
|
||||||
|
SearchBar(text: .constant(""))
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,20 +0,0 @@
|
|||||||
//
|
|
||||||
// SearchViewController.h
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/4/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_BEGIN
|
|
||||||
|
|
||||||
@interface SearchViewController : UITableViewController <UISearchBarDelegate, UITableViewDelegate, UITableViewDataSource>
|
|
||||||
|
|
||||||
@property (weak, nonatomic) IBOutlet UISearchBar *searchBar;
|
|
||||||
@property (weak, nonatomic) IBOutlet UITableView *searchResults;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
NS_ASSUME_NONNULL_END
|
|
||||||
@@ -1,105 +0,0 @@
|
|||||||
//
|
|
||||||
// SearchViewController.m
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/4/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import "SearchViewController.h"
|
|
||||||
#import "MonsterViewController.h"
|
|
||||||
#import "Monster.h"
|
|
||||||
#import "AppDelegate.h"
|
|
||||||
|
|
||||||
@interface SearchViewController ()
|
|
||||||
|
|
||||||
@property NSArray* allMonsters;
|
|
||||||
@property NSArray* foundMonsters;
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation SearchViewController {
|
|
||||||
NSManagedObjectContext *_context;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)viewDidLoad {
|
|
||||||
[super viewDidLoad];
|
|
||||||
AppDelegate *appDelegate = (AppDelegate*)UIApplication.sharedApplication.delegate;
|
|
||||||
_context = appDelegate.persistentContainer.viewContext;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)viewWillAppear:(BOOL)animated {
|
|
||||||
NSString *searchText = nil;
|
|
||||||
if (self.searchBar != nil) {
|
|
||||||
searchText = self.searchBar.text;
|
|
||||||
}
|
|
||||||
self.allMonsters = [_context executeFetchRequest:[Monster fetchRequest] error:nil];
|
|
||||||
self.foundMonsters = [self filterAllMonstersWithText:searchText];
|
|
||||||
[self.tableView reloadData];
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - Navigation
|
|
||||||
|
|
||||||
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender {
|
|
||||||
if ([@"ShowMonsterDetail" isEqualToString:segue.identifier]) {
|
|
||||||
NSIndexPath *indexPath = [self.searchResults indexPathForSelectedRow];
|
|
||||||
if ([segue.destinationViewController isKindOfClass:[MonsterViewController class]]) {
|
|
||||||
MonsterViewController *vc = (MonsterViewController*)segue.destinationViewController;
|
|
||||||
vc.monster = [self.foundMonsters objectAtIndex:indexPath.row];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - UITableViewDataSource
|
|
||||||
|
|
||||||
- (NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section {
|
|
||||||
return [self.foundMonsters count];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (UITableViewCell*)tableView:(UITableView*)tableView cellForRowAtIndexPath:(nonnull NSIndexPath *)indexPath {
|
|
||||||
static NSString *simpleTableIdentifier = @"MonsterCell";
|
|
||||||
|
|
||||||
UITableViewCell *cell = [self.searchResults dequeueReusableCellWithIdentifier:simpleTableIdentifier];
|
|
||||||
|
|
||||||
if (cell == nil) {
|
|
||||||
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
|
|
||||||
}
|
|
||||||
|
|
||||||
Monster *monster = (Monster*)[self.foundMonsters objectAtIndex:indexPath.row];
|
|
||||||
|
|
||||||
cell.textLabel.text = monster.name;
|
|
||||||
|
|
||||||
return cell;
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - UISearchBarDelegate
|
|
||||||
|
|
||||||
- (NSArray*)filterAllMonstersWithText:(NSString *)searchText {
|
|
||||||
if (searchText != nil && ![@"" isEqualToString:searchText]) {
|
|
||||||
NSPredicate *predicate = [NSPredicate predicateWithBlock:^BOOL(id item, NSDictionary *bindings) {
|
|
||||||
if (![item isKindOfClass:[Monster class]]) {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
Monster *monster = (Monster*)item;
|
|
||||||
|
|
||||||
if ([monster.name localizedCaseInsensitiveContainsString:searchText]) {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}];
|
|
||||||
return [self.allMonsters filteredArrayUsingPredicate:predicate];
|
|
||||||
} else {
|
|
||||||
return self.allMonsters;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)searchBar:(UISearchBar *)searchBar
|
|
||||||
textDidChange:(NSString *)searchText {
|
|
||||||
|
|
||||||
self.foundMonsters = [self filterAllMonstersWithText:searchText];
|
|
||||||
|
|
||||||
[self.tableView reloadData];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,19 +0,0 @@
|
|||||||
//
|
|
||||||
// main.m
|
|
||||||
// MonsterCards
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/2/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <UIKit/UIKit.h>
|
|
||||||
#import "AppDelegate.h"
|
|
||||||
|
|
||||||
int main(int argc, char * argv[]) {
|
|
||||||
NSString * appDelegateClassName;
|
|
||||||
@autoreleasepool {
|
|
||||||
// Setup code that might create autoreleased objects goes here.
|
|
||||||
appDelegateClassName = NSStringFromClass([AppDelegate class]);
|
|
||||||
}
|
|
||||||
return UIApplicationMain(argc, argv, nil, appDelegateClassName);
|
|
||||||
}
|
|
||||||
@@ -1,697 +0,0 @@
|
|||||||
//
|
|
||||||
// JSONHelperTests.m
|
|
||||||
// MonsterCardsTests
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/15/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <XCTest/XCTest.h>
|
|
||||||
#import "JSONHelper.h"
|
|
||||||
|
|
||||||
@interface JSONHelperTests : XCTestCase
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation JSONHelperTests {
|
|
||||||
NSString *_jsonStringKey;
|
|
||||||
NSString *_jsonStringValue;
|
|
||||||
NSString *_jsonStringFragment;
|
|
||||||
NSString *_jsonIntegerKey;
|
|
||||||
NSNumber *_jsonIntegerValue;
|
|
||||||
NSString *_jsonIntegerFragment;
|
|
||||||
NSString *_jsonBooleanKey;
|
|
||||||
BOOL _jsonBooleanValue;
|
|
||||||
NSString *_jsonBooleanFragment;
|
|
||||||
NSString *_jsonDictionaryKey;
|
|
||||||
NSDictionary *_jsonDictionaryValue;
|
|
||||||
NSString *_jsonDictionaryFragment;
|
|
||||||
NSString *_jsonDictionaryStringValue;
|
|
||||||
NSString *_jsonArrayKey;
|
|
||||||
NSArray *_jsonArrayValue;
|
|
||||||
NSString *_jsonArrayFragment;
|
|
||||||
NSString *_jsonArrayStringValue;
|
|
||||||
}
|
|
||||||
|
|
||||||
NSString* escapeStringForJSON(NSString *unescaped) {
|
|
||||||
return [[unescaped stringByReplacingOccurrencesOfString:@"\\" withString:@"\\\\"] stringByReplacingOccurrencesOfString:@"\"" withString:@"\\\""];
|
|
||||||
}
|
|
||||||
|
|
||||||
NSDictionary* readJSONDictionaryFromString(NSString *jsonString) {
|
|
||||||
NSData* jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
|
|
||||||
NSDictionary *jsonRoot = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:nil];
|
|
||||||
if (![jsonRoot isKindOfClass:[NSDictionary class]]) {
|
|
||||||
return nil;
|
|
||||||
} else {
|
|
||||||
return jsonRoot;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NSArray* readJSONArrayFromString(NSString *jsonString) {
|
|
||||||
NSData* jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
|
|
||||||
NSArray *jsonRoot = [NSJSONSerialization JSONObjectWithData:jsonData options:0 error:nil];
|
|
||||||
if (![jsonRoot isKindOfClass:[NSArray class]]) {
|
|
||||||
return nil;
|
|
||||||
} else {
|
|
||||||
return jsonRoot;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setUp {
|
|
||||||
_jsonStringKey = @"my_string";
|
|
||||||
_jsonStringValue = @"Hello, World!";
|
|
||||||
_jsonStringFragment = [NSString stringWithFormat:@"\"%@\":\"%@\"", escapeStringForJSON(_jsonStringKey), escapeStringForJSON(_jsonStringValue)];
|
|
||||||
_jsonIntegerKey = @"my_int";
|
|
||||||
_jsonIntegerValue = @12345;
|
|
||||||
_jsonIntegerFragment = [NSString stringWithFormat:@"\"%@\":%@", escapeStringForJSON(_jsonIntegerKey), [_jsonIntegerValue stringValue]];
|
|
||||||
_jsonBooleanKey = @"my_bool";
|
|
||||||
_jsonBooleanValue = YES;
|
|
||||||
_jsonBooleanFragment = [NSString stringWithFormat:@"\"%@\":true", escapeStringForJSON(_jsonBooleanKey)];
|
|
||||||
_jsonDictionaryKey = @"my_dictionary";
|
|
||||||
_jsonDictionaryValue = @{_jsonStringKey: _jsonStringValue};
|
|
||||||
_jsonDictionaryStringValue = [NSString stringWithFormat:@"{%@}", _jsonStringFragment];
|
|
||||||
_jsonDictionaryFragment = [NSString stringWithFormat:@"\"%@\":%@", escapeStringForJSON(_jsonDictionaryKey), _jsonDictionaryStringValue];
|
|
||||||
_jsonArrayKey = @"my_array";
|
|
||||||
_jsonArrayValue = @[_jsonStringValue];
|
|
||||||
_jsonArrayStringValue = [NSString stringWithFormat:@"[\"%@\"]", _jsonStringValue];
|
|
||||||
_jsonArrayFragment = [NSString stringWithFormat:@"\"%@\":%@", escapeStringForJSON(_jsonArrayKey), _jsonArrayStringValue];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)tearDown {
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - Strings in Dictionaries
|
|
||||||
|
|
||||||
- (void)testReadStringFromDictionaryReturnsNilIfKeyNotPresent {
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"{%@}", _jsonIntegerFragment];
|
|
||||||
NSDictionary *jsonRoot = readJSONDictionaryFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
NSString *readString = [JSONHelper readStringFromDictionary:jsonRoot forKey:_jsonStringKey];
|
|
||||||
XCTAssertNil(readString);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testReadStringFromDictionaryWithDefaultReturnsDefaultIfKeyNotPresent {
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"{%@}", _jsonIntegerFragment];
|
|
||||||
NSDictionary *jsonRoot = readJSONDictionaryFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
NSString *readString = [JSONHelper readStringFromDictionary:jsonRoot forKey:_jsonStringKey withDefaultValue:_jsonStringValue];
|
|
||||||
XCTAssertEqualObjects(_jsonStringValue, readString);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void) testReadStringFromDictionaryReturnsCorrectValue {
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"{%@}", _jsonStringFragment];
|
|
||||||
NSDictionary *jsonRoot = readJSONDictionaryFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
NSString *readString = [JSONHelper readStringFromDictionary:jsonRoot forKey:_jsonStringKey];
|
|
||||||
XCTAssertEqualObjects(_jsonStringValue, readString);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testReadStringFromDictionaryWithDefaultReturnsCorrectValue {
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"{%@}", _jsonStringFragment];
|
|
||||||
NSDictionary *jsonRoot = readJSONDictionaryFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
NSString *readString = [JSONHelper readStringFromDictionary:jsonRoot forKey:_jsonStringKey withDefaultValue:@"Some other string"];
|
|
||||||
XCTAssertEqualObjects(_jsonStringValue, readString);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void) testReadStringFromDictionaryReturnsNilIfWrongType {
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"{\"%@\":%@}", _jsonStringKey, _jsonIntegerValue];
|
|
||||||
NSDictionary *jsonRoot = readJSONDictionaryFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
NSString *readString = [JSONHelper readStringFromDictionary:jsonRoot forKey:_jsonStringKey];
|
|
||||||
XCTAssertNil(readString);
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - Strings in Arrays
|
|
||||||
|
|
||||||
- (void)testReadStringFromArrayReturnsNilIfNotAString {
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"[%@]", _jsonIntegerValue];
|
|
||||||
NSArray *jsonRoot = readJSONArrayFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
NSString *readString = [JSONHelper readStringFromArray:jsonRoot forIndex:0];
|
|
||||||
XCTAssertNil(readString);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testReadStringFromArrayWithDefaultReturnsDefaultValueIfNotAString {
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"[%@]", _jsonIntegerValue];
|
|
||||||
NSArray *jsonRoot = readJSONArrayFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
NSString *readString = [JSONHelper readStringFromArray:jsonRoot forIndex:0 withDefaultValue:_jsonStringValue];
|
|
||||||
XCTAssertEqualObjects(_jsonStringValue, readString);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testReadStringFromArrayThrowsIfIndexOutOfRange {
|
|
||||||
// TODO: Decide if this should throw or return nil
|
|
||||||
NSString *jsonString = @"[]";
|
|
||||||
NSArray *jsonRoot = readJSONArrayFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
XCTAssertThrows([JSONHelper readStringFromArray:jsonRoot forIndex:0]);
|
|
||||||
XCTAssertThrows([JSONHelper readStringFromArray:jsonRoot forIndex:-1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testReadStringFromArrayReturnsCorrectValue {
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"[\"%@\"]", _jsonStringValue];
|
|
||||||
NSArray *jsonRoot = readJSONArrayFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
NSString *readString = [JSONHelper readStringFromArray:jsonRoot forIndex:0];
|
|
||||||
XCTAssertEqualObjects(_jsonStringValue, readString);
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - Integers in Dictionaries
|
|
||||||
|
|
||||||
- (void)testReadIntegerFromDictionaryReturnsNilIfKeyNotPresent {
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"{%@}", _jsonStringFragment];
|
|
||||||
NSDictionary *jsonRoot = readJSONDictionaryFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
NSNumber *readNumber = [JSONHelper readNumberFromDictionary:jsonRoot forKey:_jsonIntegerKey];
|
|
||||||
XCTAssertNil(readNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testReadIntegerFromDictionaryWithDefaultReturnsDefaultIfKeyNotPresent {
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"{%@}", _jsonStringFragment];
|
|
||||||
NSDictionary *jsonRoot = readJSONDictionaryFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
NSNumber *readNumber = [JSONHelper readNumberFromDictionary:jsonRoot forKey:_jsonIntegerKey withDefaultValue:_jsonIntegerValue];
|
|
||||||
XCTAssertEqualObjects(_jsonIntegerValue, readNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void) testReadIntegerFromDictionaryReturnsCorrectValue {
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"{%@}", _jsonIntegerFragment];
|
|
||||||
NSDictionary *jsonRoot = readJSONDictionaryFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
NSNumber *readNumber = [JSONHelper readNumberFromDictionary:jsonRoot forKey:_jsonIntegerKey];
|
|
||||||
XCTAssertEqualObjects(_jsonIntegerValue, readNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testReadIntegerFromDictionaryWithDefaultReturnsCorrectValue {
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"{%@}", _jsonIntegerFragment];
|
|
||||||
NSDictionary *jsonRoot = readJSONDictionaryFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
NSNumber *readNumber = [JSONHelper readNumberFromDictionary:jsonRoot forKey:_jsonIntegerKey withDefaultValue:@67890];
|
|
||||||
XCTAssertEqualObjects(_jsonIntegerValue, readNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void) testReadIntegerFromDictionaryReturnsNilIfWrongType {
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"{\"%@\":\"%@\"}", _jsonIntegerKey, _jsonStringValue];
|
|
||||||
NSDictionary *jsonRoot = readJSONDictionaryFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
NSNumber *readNumber = [JSONHelper readNumberFromDictionary:jsonRoot forKey:_jsonIntegerKey];
|
|
||||||
XCTAssertNil(readNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testReadIntFromDictionaryReturnsZeroIfKeyNotPresent {
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"{%@}", _jsonStringFragment];
|
|
||||||
NSDictionary *jsonRoot = readJSONDictionaryFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
int readNumber = [JSONHelper readIntFromDictionary:jsonRoot forKey:_jsonIntegerKey];
|
|
||||||
XCTAssertEqual(0, readNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testReadIntFromDictionaryWithDefaultReturnsDefaultIfKeyNotPresent {
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"{%@}", _jsonStringFragment];
|
|
||||||
NSDictionary *jsonRoot = readJSONDictionaryFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
int readNumber = [JSONHelper readIntFromDictionary:jsonRoot forKey:_jsonIntegerKey withDefaultValue:[_jsonIntegerValue intValue]];
|
|
||||||
XCTAssertEqual([_jsonIntegerValue intValue], readNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void) testReadIntFromDictionaryReturnsCorrectValue {
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"{%@}", _jsonIntegerFragment];
|
|
||||||
NSDictionary *jsonRoot = readJSONDictionaryFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
int readNumber = [JSONHelper readIntFromDictionary:jsonRoot forKey:_jsonIntegerKey];
|
|
||||||
XCTAssertEqual([_jsonIntegerValue intValue], readNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testReadIntFromDictionaryWithDefaultReturnsCorrectValue {
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"{%@}", _jsonIntegerFragment];
|
|
||||||
NSDictionary *jsonRoot = readJSONDictionaryFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
int readNumber = [JSONHelper readIntFromDictionary:jsonRoot forKey:_jsonIntegerKey withDefaultValue:67890];
|
|
||||||
XCTAssertEqual([_jsonIntegerValue intValue], readNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void) testReadIntFromDictionaryReturnsZeroIfWrongType {
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"{\"%@\":\"%@\"}", _jsonIntegerKey, _jsonStringValue];
|
|
||||||
NSDictionary *jsonRoot = readJSONDictionaryFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
int readNumber = [JSONHelper readIntFromDictionary:jsonRoot forKey:_jsonIntegerKey];
|
|
||||||
XCTAssertEqual(0, readNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - Dictionaries in Dictionaries
|
|
||||||
|
|
||||||
- (void)testReadDictionaryFromDictionaryReturnsNilIfKeyNotPresent {
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"{%@}", _jsonStringFragment];
|
|
||||||
NSDictionary *jsonRoot = readJSONDictionaryFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
|
|
||||||
NSDictionary *readValue = [JSONHelper readDictionaryFromDictionary:jsonRoot forKey:_jsonDictionaryKey];
|
|
||||||
XCTAssertNil(readValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testReadDictionaryFromDictionaryWithDefaultReturnsDefaultIfKeyNotPresent{
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"{%@}", _jsonStringFragment];
|
|
||||||
NSDictionary *jsonRoot = readJSONDictionaryFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
NSDictionary *readValue = [JSONHelper readDictionaryFromDictionary:jsonRoot forKey:_jsonDictionaryKey withDefaultValue:@{}];
|
|
||||||
XCTAssertEqualObjects(@{}, readValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testReadDictionaryFromDictionaryReturnsCorrectValue {
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"{%@}", _jsonDictionaryFragment];
|
|
||||||
NSDictionary *jsonRoot = readJSONDictionaryFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
NSDictionary *readValue = [JSONHelper readDictionaryFromDictionary:jsonRoot forKey:_jsonDictionaryKey];
|
|
||||||
XCTAssertEqualObjects(_jsonDictionaryValue, readValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testReadDictionaryFromDictionaryWithDefaultReturnsCorrectValue {
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"{%@}", _jsonIntegerFragment];
|
|
||||||
NSDictionary *jsonRoot = readJSONDictionaryFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
NSDictionary *readValue = [JSONHelper readDictionaryFromDictionary:jsonRoot forKey:_jsonDictionaryKey withDefaultValue:@{}];
|
|
||||||
XCTAssertEqualObjects(@{}, readValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testReadDictionaryFromDictionaryReturnsNilIfWrongType {
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"{\"%@\":\"%@\"}", _jsonDictionaryKey, _jsonStringValue];
|
|
||||||
NSDictionary *jsonRoot = readJSONDictionaryFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
NSDictionary *readValue = [JSONHelper readDictionaryFromDictionary:jsonRoot forKey:_jsonDictionaryKey];
|
|
||||||
XCTAssertNil(readValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - Arrays in Dictionaries
|
|
||||||
|
|
||||||
- (void)testReadArrayFromDictionaryReturnsNilIfKeyNotPresent {
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"{%@}", _jsonStringFragment];
|
|
||||||
NSDictionary *jsonRoot = readJSONDictionaryFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
|
|
||||||
NSArray *readValue = [JSONHelper readArrayFromDictionary:jsonRoot forKey:_jsonArrayKey];
|
|
||||||
XCTAssertNil(readValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testReadArrayFromDictionaryWithDefaultReturnsDefaultIfKeyNotPresent{
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"{%@}", _jsonStringFragment];
|
|
||||||
NSDictionary *jsonRoot = readJSONDictionaryFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
NSArray *readValue = [JSONHelper readArrayFromDictionary:jsonRoot forKey:_jsonArrayKey withDefaultValue:@[]];
|
|
||||||
XCTAssertEqualObjects(@[], readValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testReadArrayFromDictionaryReturnsCorrectValue {
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"{%@}", _jsonArrayFragment];
|
|
||||||
NSDictionary *jsonRoot = readJSONDictionaryFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
NSArray *readValue = [JSONHelper readArrayFromDictionary:jsonRoot forKey:_jsonArrayKey];
|
|
||||||
XCTAssertEqualObjects(_jsonArrayValue, readValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testReadArrayFromDictionaryWithDefaultReturnsCorrectValue {
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"{%@}", _jsonIntegerFragment];
|
|
||||||
NSDictionary *jsonRoot = readJSONDictionaryFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
NSArray *readValue = [JSONHelper readArrayFromDictionary:jsonRoot forKey:_jsonArrayKey withDefaultValue:@[]];
|
|
||||||
XCTAssertEqualObjects(@[], readValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testReadArrayFromDictionaryReturnsNilIfWrongType {
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"{\"%@\":\"%@\"}", _jsonArrayKey, _jsonStringValue];
|
|
||||||
NSDictionary *jsonRoot = readJSONDictionaryFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
NSArray *readValue = [JSONHelper readArrayFromDictionary:jsonRoot forKey:_jsonArrayKey];
|
|
||||||
XCTAssertNil(readValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - Integers in Arrays
|
|
||||||
|
|
||||||
- (void)testReadIntegerFromArrayReturnsNilIfNotAnInteger {
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"[\"%@\"]", _jsonStringValue];
|
|
||||||
NSArray *jsonRoot = readJSONArrayFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
NSNumber *readNumber = [JSONHelper readNumberFromArray:jsonRoot forIndex:0];
|
|
||||||
XCTAssertNil(readNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testReadIntegerFromArrayWithDefaultReturnsDefaultValueIfNotAnInteger {
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"[\"%@\"]", _jsonStringValue];
|
|
||||||
NSArray *jsonRoot = readJSONArrayFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
NSNumber *readNumber = [JSONHelper readNumberFromArray:jsonRoot forIndex:0 withDefaultValue:_jsonIntegerValue];
|
|
||||||
XCTAssertEqualObjects(_jsonIntegerValue, readNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testReadIntegerFromArrayThrowsIfIndexOutOfRange {
|
|
||||||
// TODO: Decide if this should throw or return nil
|
|
||||||
NSString *jsonString = @"[]";
|
|
||||||
NSArray *jsonRoot = readJSONArrayFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
XCTAssertThrows([JSONHelper readNumberFromArray:jsonRoot forIndex:0]);
|
|
||||||
XCTAssertThrows([JSONHelper readNumberFromArray:jsonRoot forIndex:-1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testReadIntegerFromArrayReturnsCorrectValue {
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"[%@]", _jsonIntegerValue];
|
|
||||||
NSArray *jsonRoot = readJSONArrayFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
NSNumber *readNumber = [JSONHelper readNumberFromArray:jsonRoot forIndex:0];
|
|
||||||
XCTAssertEqualObjects(_jsonIntegerValue, readNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testReadIntFromArrayReturnsNilIfNotAnInteger {
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"[\"%@\"]", _jsonStringValue];
|
|
||||||
NSArray *jsonRoot = readJSONArrayFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
int readNumber = [JSONHelper readIntFromArray:jsonRoot forIndex:0];
|
|
||||||
XCTAssertEqual(0, readNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testReadIntFromArrayWithDefaultReturnsDefaultValueIfNotAnInteger {
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"[\"%@\"]", _jsonStringValue];
|
|
||||||
NSArray *jsonRoot = readJSONArrayFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
int readNumber = [JSONHelper readIntFromArray:jsonRoot forIndex:0 withDefaultValue:[_jsonIntegerValue intValue]];
|
|
||||||
XCTAssertEqual([_jsonIntegerValue intValue], readNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testReadIntFromArrayThrowsIfIndexOutOfRange {
|
|
||||||
// TODO: Decide if this should throw or return 0
|
|
||||||
NSString *jsonString = @"[]";
|
|
||||||
NSArray *jsonRoot = readJSONArrayFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
XCTAssertThrows([JSONHelper readIntFromArray:jsonRoot forIndex:0]);
|
|
||||||
XCTAssertThrows([JSONHelper readIntFromArray:jsonRoot forIndex:-1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testReadIntFromArrayReturnsCorrectValue {
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"[%@]", _jsonIntegerValue];
|
|
||||||
NSArray *jsonRoot = readJSONArrayFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
int readNumber = [JSONHelper readIntFromArray:jsonRoot forIndex:0];
|
|
||||||
XCTAssertEqual([_jsonIntegerValue intValue], readNumber);
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - BOOLs in Dictionaries
|
|
||||||
|
|
||||||
- (void)testReadBoolFromDictionaryReturnsFalseIfKeyNotPresent {
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"{%@}", _jsonStringFragment];
|
|
||||||
NSDictionary *jsonRoot = readJSONDictionaryFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
BOOL readValue = [JSONHelper readBoolFromDictionary:jsonRoot forKey:_jsonBooleanKey];
|
|
||||||
XCTAssertEqual(0, readValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testReadBoolFromDictionaryWithDefaultReturnsDefaultIfKeyNotPresent {
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"{%@}", _jsonStringFragment];
|
|
||||||
NSDictionary *jsonRoot = readJSONDictionaryFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
BOOL readValue = [JSONHelper readBoolFromDictionary:jsonRoot forKey:_jsonIntegerKey withDefaultValue:_jsonBooleanValue];
|
|
||||||
XCTAssertEqual(_jsonBooleanValue, readValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void) testReadBoolFromDictionaryReturnsCorrectValue {
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"{%@}", _jsonBooleanFragment];
|
|
||||||
NSDictionary *jsonRoot = readJSONDictionaryFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
BOOL readValue = [JSONHelper readBoolFromDictionary:jsonRoot forKey:_jsonBooleanKey];
|
|
||||||
XCTAssertEqual(_jsonBooleanValue, readValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testReadBoolFromDictionaryWithDefaultReturnsCorrectValue {
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"{%@}", _jsonBooleanFragment];
|
|
||||||
NSDictionary *jsonRoot = readJSONDictionaryFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
BOOL readValue = [JSONHelper readBoolFromDictionary:jsonRoot forKey:_jsonBooleanKey withDefaultValue:NO];
|
|
||||||
XCTAssertEqual(_jsonBooleanValue, readValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void) testReadBoolFromDictionaryReturnsFalseIfWrongType {
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"{\"%@\":\"%@\"}", _jsonIntegerKey, _jsonStringValue];
|
|
||||||
NSDictionary *jsonRoot = readJSONDictionaryFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
BOOL readValue = [JSONHelper readBoolFromDictionary:jsonRoot forKey:_jsonIntegerKey];
|
|
||||||
XCTAssertEqual(NO, readValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - BOOLs in Arrays
|
|
||||||
|
|
||||||
- (void)testReadBoolFromArrayReturnsFalseIfNotCoercable {
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"[\"%@\"]", _jsonStringValue];
|
|
||||||
NSArray *jsonRoot = readJSONArrayFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
BOOL readValue = [JSONHelper readBoolFromArray:jsonRoot forIndex:0];
|
|
||||||
XCTAssertEqual(NO, readValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testReadBoolFromArrayWithDefaultReturnsDefaultValueIfNotCoercable {
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"[\"%@\"]", _jsonStringValue];
|
|
||||||
NSArray *jsonRoot = readJSONArrayFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
BOOL readValue = [JSONHelper readIntFromArray:jsonRoot forIndex:0 withDefaultValue:YES];
|
|
||||||
XCTAssertEqual(YES, readValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testReadBoolFromArrayThrowsIfIndexOutOfRange {
|
|
||||||
// TODO: Decide if this should throw or return 0
|
|
||||||
NSString *jsonString = @"[]";
|
|
||||||
NSArray *jsonRoot = readJSONArrayFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
XCTAssertThrows([JSONHelper readBoolFromArray:jsonRoot forIndex:0]);
|
|
||||||
XCTAssertThrows([JSONHelper readBoolFromArray:jsonRoot forIndex:-1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testReadBoolFromArrayReturnsCorrectValue {
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"[%s]", _jsonBooleanValue ? "true" : "false"];
|
|
||||||
NSArray *jsonRoot = readJSONArrayFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
BOOL readValue = [JSONHelper readBoolFromArray:jsonRoot forIndex:0];
|
|
||||||
XCTAssertEqual(_jsonBooleanValue, readValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - Dictionaries in Arrays
|
|
||||||
|
|
||||||
- (void)testReadDictionaryFromArrayReturnsNilIfNotCoercable {
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"[\"%@\"]", _jsonStringValue];
|
|
||||||
NSArray *jsonRoot = readJSONArrayFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
NSDictionary *readValue = [JSONHelper readDictionaryFromArray:jsonRoot forIndex:0];
|
|
||||||
XCTAssertNil(readValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testReadDictionaryFromArrayWithDefaultReturnsDefaultValueIfNotCoercable {
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"[\"%@\"]", _jsonStringValue];
|
|
||||||
NSArray *jsonRoot = readJSONArrayFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
NSDictionary *readValue = [JSONHelper readDictionaryFromArray:jsonRoot forIndex:0 withDefaultValue:@{}];
|
|
||||||
XCTAssertEqualObjects(@{}, readValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testReadDictionaryFromArrayThrowsIfIndexOutOfRange {
|
|
||||||
// TODO: Decide if this should throw or return nil
|
|
||||||
NSString *jsonString = @"[]";
|
|
||||||
NSArray *jsonRoot = readJSONArrayFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
XCTAssertThrows([JSONHelper readDictionaryFromArray:jsonRoot forIndex:0]);
|
|
||||||
XCTAssertThrows([JSONHelper readDictionaryFromArray:jsonRoot forIndex:-1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testReadDictionaryFromArrayReturnsCorrectValue {
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"[%@]", _jsonDictionaryStringValue];
|
|
||||||
NSArray *jsonRoot = readJSONArrayFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
NSDictionary *readValue = [JSONHelper readDictionaryFromArray:jsonRoot forIndex:0];
|
|
||||||
XCTAssertEqualObjects(_jsonDictionaryValue, readValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - Arrays in Arrays
|
|
||||||
|
|
||||||
- (void)testReadArrayFromArrayReturnsNilIfNotCoercable {
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"[\"%@\"]", _jsonStringValue];
|
|
||||||
NSArray *jsonRoot = readJSONArrayFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
NSArray *readValue = [JSONHelper readArrayFromArray:jsonRoot forIndex:0];
|
|
||||||
XCTAssertNil(readValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testReadArrayFromArrayWithDefaultReturnsDefaultValueIfNotCoercable {
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"[\"%@\"]", _jsonStringValue];
|
|
||||||
NSArray *jsonRoot = readJSONArrayFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
NSArray *readValue = [JSONHelper readArrayFromArray:jsonRoot forIndex:0 withDefaultValue:@[]];
|
|
||||||
XCTAssertEqualObjects(@[], readValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testReadArrayFromArrayThrowsIfIndexOutOfRange {
|
|
||||||
// TODO: Decide if this should throw or return nil
|
|
||||||
NSString *jsonString = @"[]";
|
|
||||||
NSArray *jsonRoot = readJSONArrayFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
XCTAssertThrows([JSONHelper readArrayFromArray:jsonRoot forIndex:0]);
|
|
||||||
XCTAssertThrows([JSONHelper readArrayFromArray:jsonRoot forIndex:-1]);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testReadArrayFromArrayReturnsCorrectValue {
|
|
||||||
NSString *jsonString = [NSString stringWithFormat:@"[%@]", _jsonArrayStringValue];
|
|
||||||
NSArray *jsonRoot = readJSONArrayFromString(jsonString);
|
|
||||||
XCTAssertNotNil(jsonRoot);
|
|
||||||
|
|
||||||
NSArray *readValue = [JSONHelper readArrayFromArray:jsonRoot forIndex:0];
|
|
||||||
XCTAssertEqualObjects(_jsonArrayValue, readValue);
|
|
||||||
}
|
|
||||||
|
|
||||||
#pragma mark - JSON parsing
|
|
||||||
|
|
||||||
- (void)testParseJSONDataReturnsDictionary {
|
|
||||||
NSString *jsonString = @"{\"a\":1,\"b\":2}";
|
|
||||||
NSData* jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
|
|
||||||
|
|
||||||
id parsedResult = [JSONHelper parseJSONData:jsonData];
|
|
||||||
XCTAssertNotNil(parsedResult);
|
|
||||||
XCTAssertTrue([parsedResult isKindOfClass:[NSDictionary class]]);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testParseJSONDataReturnsArray {
|
|
||||||
NSString *jsonString = @"[\"a\",1,\"b\",2]";
|
|
||||||
NSData* jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
|
|
||||||
|
|
||||||
id parsedResult = [JSONHelper parseJSONData:jsonData];
|
|
||||||
XCTAssertNotNil(parsedResult);
|
|
||||||
XCTAssertTrue([parsedResult isKindOfClass:[NSArray class]]);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testParseJSONDataAsDictionaryReturnsDictionary {
|
|
||||||
NSString *jsonString = @"{\"a\":1,\"b\":2}";
|
|
||||||
NSData* jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
|
|
||||||
|
|
||||||
NSDictionary *parsedResult = [JSONHelper parseJSONDataAsDictionary:jsonData];
|
|
||||||
XCTAssertNotNil(parsedResult);
|
|
||||||
XCTAssertTrue([parsedResult isKindOfClass:[NSDictionary class]]);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testParseJSONDataAsDictionaryReturnsNilIfNotDictionary {
|
|
||||||
NSString *jsonString = @"[\"a\",1,\"b\",2]";
|
|
||||||
NSData* jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
|
|
||||||
|
|
||||||
id parsedResult = [JSONHelper parseJSONDataAsDictionary:jsonData];
|
|
||||||
XCTAssertNil(parsedResult);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testParseJSONDataAsArrayReturnsArray {
|
|
||||||
NSString *jsonString = @"[\"a\",1,\"b\",2]";
|
|
||||||
NSData* jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
|
|
||||||
|
|
||||||
id parsedResult = [JSONHelper parseJSONDataAsArray:jsonData];
|
|
||||||
XCTAssertNotNil(parsedResult);
|
|
||||||
XCTAssertTrue([parsedResult isKindOfClass:[NSArray class]]);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testParseJSONDataAsArrayReturnsNilIfNotArray {
|
|
||||||
NSString *jsonString = @"{\"a\":1,\"b\":2}";
|
|
||||||
NSData* jsonData = [jsonString dataUsingEncoding:NSUTF8StringEncoding];
|
|
||||||
|
|
||||||
id parsedResult = [JSONHelper parseJSONDataAsArray:jsonData];
|
|
||||||
XCTAssertNil(parsedResult);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testParseJSONStringReturnsDictionary {
|
|
||||||
NSString *jsonString = @"{\"a\":1,\"b\":2}";
|
|
||||||
|
|
||||||
id parsedResult = [JSONHelper parseJSONString:jsonString];
|
|
||||||
XCTAssertNotNil(parsedResult);
|
|
||||||
XCTAssertTrue([parsedResult isKindOfClass:[NSDictionary class]]);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testParseJSONStringReturnsArray {
|
|
||||||
NSString *jsonString = @"[\"a\",1,\"b\",2]";
|
|
||||||
|
|
||||||
id parsedResult = [JSONHelper parseJSONString:jsonString];
|
|
||||||
XCTAssertNotNil(parsedResult);
|
|
||||||
XCTAssertTrue([parsedResult isKindOfClass:[NSArray class]]);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testParseJSONStringAsDictionaryReturnsDictionary {
|
|
||||||
NSString *jsonString = @"{\"a\":1,\"b\":2}";
|
|
||||||
|
|
||||||
NSDictionary *parsedResult = [JSONHelper parseJSONStringAsDictionary:jsonString];
|
|
||||||
XCTAssertNotNil(parsedResult);
|
|
||||||
XCTAssertTrue([parsedResult isKindOfClass:[NSDictionary class]]);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testParseJSONStringAsDictionaryReturnsNilIfNotDictionary {
|
|
||||||
NSString *jsonString = @"[\"a\",1,\"b\",2]";
|
|
||||||
|
|
||||||
id parsedResult = [JSONHelper parseJSONStringAsDictionary:jsonString];
|
|
||||||
XCTAssertNil(parsedResult);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testParseJSONStringAsArrayReturnsArray {
|
|
||||||
NSString *jsonString = @"[\"a\",1,\"b\",2]";
|
|
||||||
|
|
||||||
id parsedResult = [JSONHelper parseJSONStringAsArray:jsonString];
|
|
||||||
XCTAssertNotNil(parsedResult);
|
|
||||||
XCTAssertTrue([parsedResult isKindOfClass:[NSArray class]]);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testParseJSONStringAsArrayReturnsNilIfNotArray {
|
|
||||||
NSString *jsonString = @"{\"a\":1,\"b\":2}";
|
|
||||||
|
|
||||||
id parsedResult = [JSONHelper parseJSONStringAsArray:jsonString];
|
|
||||||
XCTAssertNil(parsedResult);
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
//
|
|
||||||
// AbilityTests.m
|
|
||||||
// MonsterCardsTests
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/5/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <XCTest/XCTest.h>
|
|
||||||
#import "Ability.h"
|
|
||||||
|
|
||||||
@interface AbilityTests : XCTestCase
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation AbilityTests {
|
|
||||||
Ability *_ability;
|
|
||||||
NSString *_name;
|
|
||||||
NSString *_description;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setUp {
|
|
||||||
_ability = [[Ability alloc] init];
|
|
||||||
_name = @"My Ability Name";
|
|
||||||
_description = @"This is my ability description.";
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)tearDown {
|
|
||||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testDefaultInitializer {
|
|
||||||
XCTAssertEqualObjects(@"", _ability.name);
|
|
||||||
XCTAssertEqualObjects(@"", _ability.abilityDescription);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testNameGetterAndSetter {
|
|
||||||
_ability.name = _name;
|
|
||||||
XCTAssertEqualObjects(_name, _ability.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testDescriptionGetterAndSetter {
|
|
||||||
_ability.abilityDescription = _description;
|
|
||||||
XCTAssertEqualObjects(_description, _ability.abilityDescription);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testInitWithNameAndDescription {
|
|
||||||
_ability = [[Ability alloc] initWithName:_name andDescription:_description];
|
|
||||||
|
|
||||||
XCTAssertEqualObjects(_name, _ability.name);
|
|
||||||
XCTAssertEqualObjects(_description, _ability.abilityDescription);
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,54 +0,0 @@
|
|||||||
//
|
|
||||||
// ActionTests.m
|
|
||||||
// MonsterCardsTests
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/5/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <XCTest/XCTest.h>
|
|
||||||
#import "Action.h"
|
|
||||||
|
|
||||||
@interface ActionTests : XCTestCase
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation ActionTests {
|
|
||||||
Action *_action;
|
|
||||||
NSString *_name;
|
|
||||||
NSString *_description;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setUp {
|
|
||||||
_action = [[Action alloc] init];
|
|
||||||
_name = @"My Action Name";
|
|
||||||
_description = @"This is my action description.";
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)tearDown {
|
|
||||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testDefaultInitializer {
|
|
||||||
XCTAssertEqualObjects(@"", _action.name);
|
|
||||||
XCTAssertEqualObjects(@"", _action.actionDescription);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testNameGetterAndSetter {
|
|
||||||
_action.name = _name;
|
|
||||||
XCTAssertEqualObjects(_name, _action.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testDescriptionGetterAndSetter {
|
|
||||||
_action.actionDescription = _description;
|
|
||||||
XCTAssertEqualObjects(_description, _action.actionDescription);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testInitWithNameAndDescription {
|
|
||||||
_action = [[Action alloc] initWithName:_name andDescription:_description];
|
|
||||||
|
|
||||||
XCTAssertEqualObjects(_name, _action.name);
|
|
||||||
XCTAssertEqualObjects(_description, _action.actionDescription);
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,61 +0,0 @@
|
|||||||
//
|
|
||||||
// DamageTypeTests.m
|
|
||||||
// MonsterCardsTests
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/5/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <XCTest/XCTest.h>
|
|
||||||
#import "DamageType.h"
|
|
||||||
|
|
||||||
@interface DamageTypeTests : XCTestCase {
|
|
||||||
DamageType *_damageType;
|
|
||||||
NSString *_name;
|
|
||||||
NSString *_note;
|
|
||||||
NSString *_type;
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation DamageTypeTests
|
|
||||||
|
|
||||||
- (void)setUp {
|
|
||||||
_damageType = [[DamageType alloc] init];
|
|
||||||
_name = @"My Damage Type";
|
|
||||||
_note = @"A note";
|
|
||||||
_type = @"A type";
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)tearDown {}
|
|
||||||
|
|
||||||
- (void)testDefaultInitializer {
|
|
||||||
XCTAssertEqualObjects(@"", _damageType.name);
|
|
||||||
XCTAssertEqualObjects(@"", _damageType.note);
|
|
||||||
XCTAssertEqualObjects(@"", _damageType.type);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testInitWithNameNoteAndType {
|
|
||||||
_damageType = [[DamageType alloc] initWithName:_name note:_note andType:_type];
|
|
||||||
|
|
||||||
XCTAssertEqualObjects(_name, _damageType.name);
|
|
||||||
XCTAssertEqualObjects(_note, _damageType.note);
|
|
||||||
XCTAssertEqualObjects(_type, _damageType.type);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testNameGetterAndSetter {
|
|
||||||
_damageType.name = _name;
|
|
||||||
XCTAssertEqualObjects(_name, _damageType.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testNoteGetterAndSetter {
|
|
||||||
_damageType.note = _note;
|
|
||||||
XCTAssertEqualObjects(_note, _damageType.note);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testTypeGetterAndSetter {
|
|
||||||
_damageType.type = _type;
|
|
||||||
XCTAssertEqualObjects(_type, _damageType.type);
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,56 +0,0 @@
|
|||||||
//
|
|
||||||
// Language.m
|
|
||||||
// MonsterCardsTests
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/5/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <XCTest/XCTest.h>
|
|
||||||
#import "Language.h"
|
|
||||||
|
|
||||||
@interface LanguageTests : XCTestCase
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation LanguageTests {
|
|
||||||
Language *_language;
|
|
||||||
NSString *_name;
|
|
||||||
BOOL _canSpeak;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setUp {
|
|
||||||
_language = [[Language alloc] init];
|
|
||||||
_name = @"English";
|
|
||||||
_canSpeak = YES;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)tearDown {
|
|
||||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testDefaultInitializer {
|
|
||||||
XCTAssertNotNil(_language);
|
|
||||||
XCTAssertEqualObjects(@"", _language.name);
|
|
||||||
XCTAssertEqual(YES, _language.speaks);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testInitWithNameAndSpeaks {
|
|
||||||
_language = [[Language alloc] initWithName:_name andSpeaks:_canSpeak];
|
|
||||||
|
|
||||||
XCTAssertNotNil(_language);
|
|
||||||
XCTAssertEqualObjects(_name, _language.name);
|
|
||||||
XCTAssertEqual(_canSpeak, _language.speaks);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testNameGetterAndSetter {
|
|
||||||
_language.name = _name;
|
|
||||||
XCTAssertEqualObjects(_name, _language.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testSpeaksGetterAndSetter {
|
|
||||||
_language.speaks = NO;
|
|
||||||
XCTAssertEqual(NO, _language.speaks);
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,438 +0,0 @@
|
|||||||
//
|
|
||||||
// MonsterTests.m
|
|
||||||
// MonsterCardsTests
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/5/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <XCTest/XCTest.h>
|
|
||||||
@import OCHamcrest;
|
|
||||||
@import OCMockito;
|
|
||||||
#import "Monster.h"
|
|
||||||
|
|
||||||
@interface MonsterTests : XCTestCase
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation MonsterTests {
|
|
||||||
Monster *_monster;
|
|
||||||
NSManagedObjectContext *_context;
|
|
||||||
NSString *_jsonString;
|
|
||||||
NSData *_jsonData;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setUp {
|
|
||||||
_context = nil;
|
|
||||||
_monster = [[Monster alloc] initWithContext:_context];
|
|
||||||
_jsonString = @"{\"name\":\"Acolyte\",\"size\":\"medium\",\"type\":\"humanoid\",\"tag\":\"any race\",\"alignment\":\"any alignment\",\"strPoints\":8,\"dexPoints\":10,\"conPoints\":12,\"intPoints\":14,\"wisPoints\":16,\"chaPoints\":18,\"armorName\":\"none\",\"otherArmorDesc\":\"10\",\"shieldBonus\":2,\"hitDice\":3,\"customHP\":true,\"hpText\":\"1234 (1d1+magic)\"}";
|
|
||||||
_jsonData = [_jsonString dataUsingEncoding:NSUTF8StringEncoding];
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)tearDown {
|
|
||||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testDefaultInitializer {
|
|
||||||
XCTAssertNotNil(_monster);
|
|
||||||
XCTAssertEqualObjects(@"", _monster.name);
|
|
||||||
XCTAssertEqualObjects(@"", _monster.size);
|
|
||||||
XCTAssertEqualObjects(@"", _monster.type);
|
|
||||||
XCTAssertEqualObjects(@"", _monster.subtype);
|
|
||||||
XCTAssertEqualObjects(@"", _monster.alignment);
|
|
||||||
XCTAssertEqual(10, _monster.strengthScore);
|
|
||||||
XCTAssertEqual(10, _monster.dexterityScore);
|
|
||||||
XCTAssertEqual(10, _monster.constitutionScore);
|
|
||||||
XCTAssertEqual(10, _monster.intelligenceScore);
|
|
||||||
XCTAssertEqual(10, _monster.wisdomScore);
|
|
||||||
XCTAssertEqual(10, _monster.charismaScore);
|
|
||||||
XCTAssertEqualObjects(@"", _monster.armorType);
|
|
||||||
XCTAssertEqualObjects(@"", _monster.otherArmorDescription);
|
|
||||||
XCTAssertEqual(0, _monster.shieldBonus);
|
|
||||||
XCTAssertEqual(NO, _monster.customHP);
|
|
||||||
XCTAssertEqual(0, _monster.hitDice);
|
|
||||||
XCTAssertEqualObjects(@"", _monster.hpText);}
|
|
||||||
|
|
||||||
- (void)testInitWithJSONString {
|
|
||||||
_monster = [[Monster alloc] initWithJSONString:_jsonString andContext:_context];
|
|
||||||
|
|
||||||
XCTAssertNotNil(_monster);
|
|
||||||
XCTAssertEqualObjects(@"Acolyte", _monster.name);
|
|
||||||
XCTAssertEqualObjects(@"medium", _monster.size);
|
|
||||||
XCTAssertEqualObjects(@"humanoid", _monster.type);
|
|
||||||
XCTAssertEqualObjects(@"any race", _monster.subtype);
|
|
||||||
XCTAssertEqualObjects(@"any alignment", _monster.alignment);
|
|
||||||
XCTAssertEqual(8, _monster.strengthScore);
|
|
||||||
XCTAssertEqual(10, _monster.dexterityScore);
|
|
||||||
XCTAssertEqual(12, _monster.constitutionScore);
|
|
||||||
XCTAssertEqual(14, _monster.intelligenceScore);
|
|
||||||
XCTAssertEqual(16, _monster.wisdomScore);
|
|
||||||
XCTAssertEqual(18, _monster.charismaScore);
|
|
||||||
XCTAssertEqualObjects(@"none", _monster.armorType);
|
|
||||||
XCTAssertEqualObjects(@"10", _monster.otherArmorDescription);
|
|
||||||
XCTAssertEqual(2, _monster.shieldBonus);
|
|
||||||
XCTAssertEqual(YES, _monster.customHP);
|
|
||||||
XCTAssertEqual(3, _monster.hitDice);
|
|
||||||
XCTAssertEqualObjects(@"1234 (1d1+magic)", _monster.hpText);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testInitWithEmptyJSONString {
|
|
||||||
_monster = [[Monster alloc] initWithJSONString:@"{}" andContext:_context];
|
|
||||||
|
|
||||||
XCTAssertNotNil(_monster);
|
|
||||||
XCTAssertEqualObjects(@"", _monster.name);
|
|
||||||
XCTAssertEqualObjects(@"", _monster.size);
|
|
||||||
XCTAssertEqualObjects(@"", _monster.type);
|
|
||||||
XCTAssertEqualObjects(@"", _monster.subtype);
|
|
||||||
XCTAssertEqualObjects(@"", _monster.alignment);
|
|
||||||
XCTAssertEqual(10, _monster.strengthScore);
|
|
||||||
XCTAssertEqual(10, _monster.dexterityScore);
|
|
||||||
XCTAssertEqual(10, _monster.constitutionScore);
|
|
||||||
XCTAssertEqual(10, _monster.intelligenceScore);
|
|
||||||
XCTAssertEqual(10, _monster.wisdomScore);
|
|
||||||
XCTAssertEqual(10, _monster.charismaScore);
|
|
||||||
XCTAssertEqualObjects(@"", _monster.armorType);
|
|
||||||
XCTAssertEqualObjects(@"", _monster.otherArmorDescription);
|
|
||||||
XCTAssertEqual(0, _monster.shieldBonus);
|
|
||||||
XCTAssertEqual(NO, _monster.customHP);
|
|
||||||
XCTAssertEqual(0, _monster.hitDice);
|
|
||||||
XCTAssertEqualObjects(@"", _monster.hpText);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testInitWithJSONData {
|
|
||||||
_monster = [[Monster alloc] initWithJSONData:_jsonData andContext:_context];
|
|
||||||
|
|
||||||
XCTAssertNotNil(_monster);
|
|
||||||
XCTAssertEqualObjects(@"Acolyte", _monster.name);
|
|
||||||
XCTAssertEqualObjects(@"medium", _monster.size);
|
|
||||||
XCTAssertEqualObjects(@"humanoid", _monster.type);
|
|
||||||
XCTAssertEqualObjects(@"any race", _monster.subtype);
|
|
||||||
XCTAssertEqualObjects(@"any alignment", _monster.alignment);
|
|
||||||
XCTAssertEqual(8, _monster.strengthScore);
|
|
||||||
XCTAssertEqual(10, _monster.dexterityScore);
|
|
||||||
XCTAssertEqual(12, _monster.constitutionScore);
|
|
||||||
XCTAssertEqual(14, _monster.intelligenceScore);
|
|
||||||
XCTAssertEqual(16, _monster.wisdomScore);
|
|
||||||
XCTAssertEqual(18, _monster.charismaScore);
|
|
||||||
XCTAssertEqualObjects(@"none", _monster.armorType);
|
|
||||||
XCTAssertEqualObjects(@"10", _monster.otherArmorDescription);
|
|
||||||
XCTAssertEqual(2, _monster.shieldBonus);
|
|
||||||
XCTAssertEqual(YES, _monster.customHP);
|
|
||||||
XCTAssertEqual(3, _monster.hitDice);
|
|
||||||
XCTAssertEqualObjects(@"1234 (1d1+magic)", _monster.hpText);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testNameGetterAndSetter {
|
|
||||||
NSString *name = @"Pixie";
|
|
||||||
_monster.name = name;
|
|
||||||
XCTAssertEqualObjects(name, _monster.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testSizeGetterAndSetter {
|
|
||||||
NSString *size = @"huge";
|
|
||||||
_monster.size = size;
|
|
||||||
XCTAssertEqualObjects(size, _monster.size);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testTypeGetterAndSetter {
|
|
||||||
NSString *type = @"fey";
|
|
||||||
_monster.type = type;
|
|
||||||
XCTAssertEqualObjects(type, _monster.type);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testSubtypeGetterAndSetter {
|
|
||||||
NSString *subtype = @"elf";
|
|
||||||
_monster.subtype = subtype;
|
|
||||||
XCTAssertEqualObjects(subtype, _monster.subtype);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testAlignmentGetterAndSetter {
|
|
||||||
NSString *alignment = @"chaotic good";
|
|
||||||
_monster.alignment = alignment;
|
|
||||||
XCTAssertEqualObjects(alignment, _monster.alignment);
|
|
||||||
}
|
|
||||||
- (void)testCopyFromMonster {
|
|
||||||
Monster *otherMonster = [[Monster alloc] initWithJSONString:_jsonString andContext:_context];
|
|
||||||
[_monster copyFromMonster:otherMonster];
|
|
||||||
|
|
||||||
XCTAssertNotNil(_monster);
|
|
||||||
XCTAssertEqualObjects(@"Acolyte", _monster.name);
|
|
||||||
XCTAssertEqualObjects(@"medium", _monster.size);
|
|
||||||
XCTAssertEqualObjects(@"humanoid", _monster.type);
|
|
||||||
XCTAssertEqualObjects(@"any race", _monster.subtype);
|
|
||||||
XCTAssertEqualObjects(@"any alignment", _monster.alignment);
|
|
||||||
XCTAssertEqual(8, _monster.strengthScore);
|
|
||||||
XCTAssertEqual(10, _monster.dexterityScore);
|
|
||||||
XCTAssertEqual(12, _monster.constitutionScore);
|
|
||||||
XCTAssertEqual(14, _monster.intelligenceScore);
|
|
||||||
XCTAssertEqual(16, _monster.wisdomScore);
|
|
||||||
XCTAssertEqual(18, _monster.charismaScore);
|
|
||||||
XCTAssertEqualObjects(@"none", _monster.armorType);
|
|
||||||
XCTAssertEqualObjects(@"10", _monster.otherArmorDescription);
|
|
||||||
XCTAssertEqual(2, _monster.shieldBonus);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testMetaWithNoFieldsSet {
|
|
||||||
|
|
||||||
XCTAssertEqualObjects(@"", _monster.meta);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testMetaWithSize {
|
|
||||||
_monster.size = @"large";
|
|
||||||
XCTAssertEqualObjects(@"large", _monster.meta);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testMetaWithType {
|
|
||||||
_monster.type = @"humanoid";
|
|
||||||
XCTAssertEqualObjects(@"humanoid", _monster.meta);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testMetaWithSizeAndType {
|
|
||||||
_monster.size = @"large";
|
|
||||||
_monster.type = @"humanoid";
|
|
||||||
XCTAssertEqualObjects(@"large humanoid", _monster.meta);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testMetaWithSubtype {
|
|
||||||
_monster.subtype = @"elf";
|
|
||||||
XCTAssertEqualObjects(@"(elf)", _monster.meta);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testMetaWithSizeAndSubtype {
|
|
||||||
_monster.size = @"large";
|
|
||||||
_monster.subtype = @"elf";
|
|
||||||
XCTAssertEqualObjects(@"large (elf)", _monster.meta);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testMetaWithTypeAndSubtype {
|
|
||||||
_monster.type = @"humanoid";
|
|
||||||
_monster.subtype = @"elf";
|
|
||||||
XCTAssertEqualObjects(@"humanoid (elf)", _monster.meta);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testMetaWithSizeTypeAndSubtype {
|
|
||||||
_monster.size = @"large";
|
|
||||||
_monster.type = @"humanoid";
|
|
||||||
_monster.subtype = @"elf";
|
|
||||||
XCTAssertEqualObjects(@"large humanoid (elf)", _monster.meta);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testMetaWithAlignment {
|
|
||||||
_monster.alignment = @"chaotic good";
|
|
||||||
XCTAssertEqualObjects(@"chaotic good", _monster.meta);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testMetaWithSizeAndAlignment {
|
|
||||||
_monster.size = @"large";
|
|
||||||
_monster.alignment = @"chaotic good";
|
|
||||||
XCTAssertEqualObjects(@"large chaotic good", _monster.meta);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testMetaWithTypeAndAlignment {
|
|
||||||
_monster.type = @"humanoid";
|
|
||||||
_monster.alignment = @"chaotic good";
|
|
||||||
XCTAssertEqualObjects(@"humanoid chaotic good", _monster.meta);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testMetaWithSizeTypeAndAlignment {
|
|
||||||
_monster.size = @"large";
|
|
||||||
_monster.type = @"humanoid";
|
|
||||||
_monster.alignment = @"chaotic good";
|
|
||||||
XCTAssertEqualObjects(@"large humanoid chaotic good", _monster.meta);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testMetaWithSizeSubtypeAndAlignment {
|
|
||||||
_monster.size = @"large";
|
|
||||||
_monster.subtype = @"elf";
|
|
||||||
_monster.alignment = @"chaotic good";
|
|
||||||
XCTAssertEqualObjects(@"large (elf) chaotic good", _monster.meta);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testMetaWithTypeSubtypeAndAlignment {
|
|
||||||
_monster.type = @"humanoid";
|
|
||||||
_monster.subtype = @"elf";
|
|
||||||
_monster.alignment = @"chaotic good";
|
|
||||||
XCTAssertEqualObjects(@"humanoid (elf) chaotic good", _monster.meta);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testMetaWithSizeTypeSubtypeAndAlignment {
|
|
||||||
_monster.size = @"large";
|
|
||||||
_monster.type = @"humanoid";
|
|
||||||
_monster.subtype = @"elf";
|
|
||||||
_monster.alignment = @"chaotic good";
|
|
||||||
XCTAssertEqualObjects(@"large humanoid (elf) chaotic good", _monster.meta);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testAbilityModifierForScore {
|
|
||||||
XCTAssertEqual(-6, [Monster abilityModifierForScore:-1]);
|
|
||||||
XCTAssertEqual(-5, [Monster abilityModifierForScore:0]);
|
|
||||||
XCTAssertEqual(-5, [Monster abilityModifierForScore:1]);
|
|
||||||
XCTAssertEqual(-4, [Monster abilityModifierForScore:2]);
|
|
||||||
XCTAssertEqual(-4, [Monster abilityModifierForScore:3]);
|
|
||||||
XCTAssertEqual(-3, [Monster abilityModifierForScore:4]);
|
|
||||||
XCTAssertEqual(-3, [Monster abilityModifierForScore:5]);
|
|
||||||
XCTAssertEqual(-2, [Monster abilityModifierForScore:6]);
|
|
||||||
XCTAssertEqual(-2, [Monster abilityModifierForScore:7]);
|
|
||||||
XCTAssertEqual(-1, [Monster abilityModifierForScore:8]);
|
|
||||||
XCTAssertEqual(-1, [Monster abilityModifierForScore:9]);
|
|
||||||
XCTAssertEqual(0, [Monster abilityModifierForScore:10]);
|
|
||||||
XCTAssertEqual(0, [Monster abilityModifierForScore:11]);
|
|
||||||
XCTAssertEqual(1, [Monster abilityModifierForScore:12]);
|
|
||||||
XCTAssertEqual(1, [Monster abilityModifierForScore:13]);
|
|
||||||
XCTAssertEqual(2, [Monster abilityModifierForScore:14]);
|
|
||||||
XCTAssertEqual(2, [Monster abilityModifierForScore:15]);
|
|
||||||
XCTAssertEqual(3, [Monster abilityModifierForScore:16]);
|
|
||||||
XCTAssertEqual(3, [Monster abilityModifierForScore:17]);
|
|
||||||
XCTAssertEqual(4, [Monster abilityModifierForScore:18]);
|
|
||||||
XCTAssertEqual(4, [Monster abilityModifierForScore:19]);
|
|
||||||
XCTAssertEqual(5, [Monster abilityModifierForScore:20]);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testStrengthScoreGetterAndSetter {
|
|
||||||
_monster.strengthScore = 11;
|
|
||||||
XCTAssertEqual(11, _monster.strengthScore);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testStrengthModifier {
|
|
||||||
_monster.strengthScore = 9;
|
|
||||||
XCTAssertEqual(-1, _monster.strengthModifier);
|
|
||||||
|
|
||||||
_monster.strengthScore = 10;
|
|
||||||
XCTAssertEqual(0, _monster.strengthModifier);
|
|
||||||
|
|
||||||
_monster.strengthScore = 12;
|
|
||||||
XCTAssertEqual(1, _monster.strengthModifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testDexterityScoreGetterAndSetter {
|
|
||||||
_monster.dexterityScore = 11;
|
|
||||||
XCTAssertEqual(11, _monster.dexterityScore);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testDexterityModifier {
|
|
||||||
_monster.dexterityScore = 9;
|
|
||||||
XCTAssertEqual(-1, _monster.dexterityModifier);
|
|
||||||
|
|
||||||
_monster.dexterityScore = 10;
|
|
||||||
XCTAssertEqual(0, _monster.dexterityModifier);
|
|
||||||
|
|
||||||
_monster.dexterityScore = 12;
|
|
||||||
XCTAssertEqual(1, _monster.dexterityModifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testConstitutionScoreGetterAndSetter {
|
|
||||||
_monster.constitutionScore = 11;
|
|
||||||
XCTAssertEqual(11, _monster.constitutionScore);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testConstitutionModifier {
|
|
||||||
_monster.constitutionScore = 9;
|
|
||||||
XCTAssertEqual(-1, _monster.constitutionModifier);
|
|
||||||
|
|
||||||
_monster.constitutionScore = 10;
|
|
||||||
XCTAssertEqual(0, _monster.constitutionModifier);
|
|
||||||
|
|
||||||
_monster.constitutionScore = 12;
|
|
||||||
XCTAssertEqual(1, _monster.constitutionModifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testIntelligenceScoreGetterAndSetter {
|
|
||||||
_monster.intelligenceScore = 11;
|
|
||||||
XCTAssertEqual(11, _monster.intelligenceScore);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testIntelligenceModifier {
|
|
||||||
_monster.intelligenceScore = 9;
|
|
||||||
XCTAssertEqual(-1, _monster.intelligenceModifier);
|
|
||||||
|
|
||||||
_monster.intelligenceScore = 10;
|
|
||||||
XCTAssertEqual(0, _monster.intelligenceModifier);
|
|
||||||
|
|
||||||
_monster.intelligenceScore = 12;
|
|
||||||
XCTAssertEqual(1, _monster.intelligenceModifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testWisdomScoreGetterAndSetter {
|
|
||||||
_monster.wisdomScore = 11;
|
|
||||||
XCTAssertEqual(11, _monster.wisdomScore);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testWisdomModifier {
|
|
||||||
_monster.wisdomScore = 9;
|
|
||||||
XCTAssertEqual(-1, _monster.wisdomModifier);
|
|
||||||
|
|
||||||
_monster.wisdomScore = 10;
|
|
||||||
XCTAssertEqual(0, _monster.wisdomModifier);
|
|
||||||
|
|
||||||
_monster.wisdomScore = 12;
|
|
||||||
XCTAssertEqual(1, _monster.wisdomModifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testCharismaScoreGetterAndSetter {
|
|
||||||
_monster.charismaScore = 11;
|
|
||||||
XCTAssertEqual(11, _monster.charismaScore);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testCharismaModifier {
|
|
||||||
_monster.charismaScore = 9;
|
|
||||||
XCTAssertEqual(-1, _monster.charismaModifier);
|
|
||||||
|
|
||||||
_monster.charismaScore = 10;
|
|
||||||
XCTAssertEqual(0, _monster.charismaModifier);
|
|
||||||
|
|
||||||
_monster.charismaScore = 12;
|
|
||||||
XCTAssertEqual(1, _monster.charismaModifier);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testGetterAndSetterForArmorName {
|
|
||||||
_monster.armorType = @"dandelion";
|
|
||||||
XCTAssertEqualObjects(@"dandelion", _monster.armorType);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testGetterAndSetterForOtherArmorDescription {
|
|
||||||
_monster.otherArmorDescription = @"green";
|
|
||||||
XCTAssertEqualObjects(@"green", _monster.otherArmorDescription);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testHitDieForSizeReturnsExpectedValuesForKnownSizes {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testHitDieForSizeReutnrsEightForUnknownSizes {
|
|
||||||
XCTAssertEqual(4, [Monster hitDieForSize:kMonsterSizeTiny]);
|
|
||||||
XCTAssertEqual(6, [Monster hitDieForSize:kMonsterSizeSmall]);
|
|
||||||
XCTAssertEqual(8, [Monster hitDieForSize:kMonsterSizeMedium]);
|
|
||||||
XCTAssertEqual(10, [Monster hitDieForSize:kMonsterSizeLarge]);
|
|
||||||
XCTAssertEqual(12, [Monster hitDieForSize:kMonsterSizeHuge]);
|
|
||||||
XCTAssertEqual(20, [Monster hitDieForSize:kMonsterSizeGargantuan]);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testCustomHPGetterAndSetter {
|
|
||||||
XCTAssertEqual(8, [Monster hitDieForSize:nil]);
|
|
||||||
XCTAssertEqual(8, [Monster hitDieForSize:@""]);
|
|
||||||
XCTAssertEqual(8, [Monster hitDieForSize:@"unknown size"]);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testHitDiceGetterAndSetter {
|
|
||||||
_monster.hitDice = 9;
|
|
||||||
XCTAssertEqual(9, _monster.hitDice);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testHPTextGetterAndSetter {
|
|
||||||
_monster.hpText = @"This is my HP.";
|
|
||||||
XCTAssertEqualObjects(@"This is my HP.", _monster.hpText);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testHitPointsDescriptionWithCustomHP {
|
|
||||||
_monster = [[Monster alloc] initWithJSONString:_jsonString andContext:_context];
|
|
||||||
_monster.customHP = YES;
|
|
||||||
XCTAssertEqualObjects(@"1234 (1d1+magic)", _monster.hitPointsDescription);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testHitPointsDescriptionWithCalculatedHP {
|
|
||||||
_monster = [[Monster alloc] initWithJSONString:_jsonString andContext:_context];
|
|
||||||
_monster.customHP = NO;
|
|
||||||
XCTAssertEqualObjects(@"20 (3d8+3)", _monster.hitPointsDescription);
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
//
|
|
||||||
// SavingThrowTests.m
|
|
||||||
// MonsterCardsTests
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/5/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <XCTest/XCTest.h>
|
|
||||||
#import "SavingThrow.h"
|
|
||||||
|
|
||||||
@interface SavingThrowTests : XCTestCase
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation SavingThrowTests {
|
|
||||||
SavingThrow *_savingThrow;
|
|
||||||
NSString *_name;
|
|
||||||
int _order;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setUp {
|
|
||||||
_savingThrow = [[SavingThrow alloc] init];
|
|
||||||
_name = @"str";
|
|
||||||
_order = 9;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)tearDown {
|
|
||||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testDefaultInitializer {
|
|
||||||
XCTAssertNotNil(_savingThrow);
|
|
||||||
XCTAssertEqualObjects(@"", _savingThrow.name);
|
|
||||||
XCTAssertEqual(-1, _savingThrow.order);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testInitWithNameAndOrder {
|
|
||||||
_savingThrow = [[SavingThrow alloc] initWithName:_name andOrder:_order];
|
|
||||||
XCTAssertNotNil(_savingThrow);
|
|
||||||
XCTAssertEqualObjects(_name, _savingThrow.name);
|
|
||||||
XCTAssertEqual(_order, _savingThrow.order);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testNameGetterAndSetter {
|
|
||||||
_savingThrow.name = _name;
|
|
||||||
XCTAssertEqualObjects(_name, _savingThrow.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testOrderGetterAndSetter {
|
|
||||||
_savingThrow.order = _order;
|
|
||||||
XCTAssertEqual(_order, _savingThrow.order);
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,106 +0,0 @@
|
|||||||
//
|
|
||||||
// SkillTests.m
|
|
||||||
// MonsterCardsTests
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/5/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <XCTest/XCTest.h>
|
|
||||||
@import OCMockito;
|
|
||||||
@import OCHamcrest;
|
|
||||||
#import "Skill.h"
|
|
||||||
|
|
||||||
@interface SkillTests : XCTestCase
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation SkillTests {
|
|
||||||
Skill *_skill;
|
|
||||||
NSString *_name;
|
|
||||||
NSString *_abilityScoreName;
|
|
||||||
NSString *_notes;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setUp {
|
|
||||||
_skill = [[Skill alloc] init];
|
|
||||||
_name = @"pranking";
|
|
||||||
_abilityScoreName = @"str";
|
|
||||||
_notes = @"some notes";
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)tearDown {
|
|
||||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testDefaultInitializer {
|
|
||||||
XCTAssertNotNil(_skill);
|
|
||||||
XCTAssertEqualObjects(@"", _skill.name);
|
|
||||||
XCTAssertEqualObjects(@"", _skill.abilityScoreName);
|
|
||||||
XCTAssertEqualObjects(@"", _skill.notes);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testInitWithNameAbilityScoreNameAndNotes {
|
|
||||||
_skill = [[Skill alloc] initWithName:_name abilityScoreName:_abilityScoreName andNotes:_notes];
|
|
||||||
XCTAssertNotNil(_skill);
|
|
||||||
XCTAssertEqualObjects(_name, _skill.name);
|
|
||||||
XCTAssertEqualObjects(_abilityScoreName, _skill.abilityScoreName);
|
|
||||||
XCTAssertEqualObjects(_notes, _skill.notes);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testNameGetterAndSetter {
|
|
||||||
_skill.name = _name;
|
|
||||||
XCTAssertEqualObjects(_name, _skill.name);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testAbilityScoreNameGetterAndSetter {
|
|
||||||
_skill.abilityScoreName = _abilityScoreName;
|
|
||||||
XCTAssertEqualObjects(_abilityScoreName, _skill.abilityScoreName);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testNotesGetterAndSetter {
|
|
||||||
_skill.notes = _notes;
|
|
||||||
XCTAssertEqualObjects(_notes, _skill.notes);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testSkillBonusForMonster {
|
|
||||||
Monster *monster = mock([Monster class]);
|
|
||||||
[given([monster abilityModifierForAbilityScoreName:_abilityScoreName]) willReturnInt:1];
|
|
||||||
stubProperty(monster, proficiencyBonus, @2);
|
|
||||||
|
|
||||||
_skill = [[Skill alloc] initWithName:_name abilityScoreName:_abilityScoreName andNotes:_notes];
|
|
||||||
|
|
||||||
XCTAssertEqual(3, [_skill skillBonusForMonster:monster]);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testSkillBonusForMonsterWithExpertise {
|
|
||||||
Monster *monster = mock([Monster class]);
|
|
||||||
[given([monster abilityModifierForAbilityScoreName:_abilityScoreName]) willReturnInt:1];
|
|
||||||
stubProperty(monster, proficiencyBonus, @2);
|
|
||||||
|
|
||||||
_skill = [[Skill alloc] initWithName:_name abilityScoreName:_abilityScoreName andNotes:@" (ex)"];
|
|
||||||
|
|
||||||
XCTAssertEqual(5, [_skill skillBonusForMonster:monster]);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testTextForMonster {
|
|
||||||
Monster *monster = mock([Monster class]);
|
|
||||||
[given([monster abilityModifierForAbilityScoreName:_abilityScoreName]) willReturnInt:1];
|
|
||||||
stubProperty(monster, proficiencyBonus, @2);
|
|
||||||
|
|
||||||
_skill = [[Skill alloc] initWithName:_name abilityScoreName:_abilityScoreName andNotes:_notes];
|
|
||||||
|
|
||||||
XCTAssertEqualObjects(@"Pranking 3", [_skill textForMonster:monster]);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testTextForMonsterWithExpertise {
|
|
||||||
Monster *monster = mock([Monster class]);
|
|
||||||
[given([monster abilityModifierForAbilityScoreName:_abilityScoreName]) willReturnInt:1];
|
|
||||||
stubProperty(monster, proficiencyBonus, @2);
|
|
||||||
|
|
||||||
_skill = [[Skill alloc] initWithName:_name abilityScoreName:_abilityScoreName andNotes:@" (ex)"];
|
|
||||||
|
|
||||||
XCTAssertEqualObjects(@"Pranking 5", [_skill textForMonster:monster]);
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,37 +0,0 @@
|
|||||||
//
|
|
||||||
// MonsterCardsTests.m
|
|
||||||
// MonsterCardsTests
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/2/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <XCTest/XCTest.h>
|
|
||||||
|
|
||||||
@interface MonsterCardsTests : XCTestCase
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation MonsterCardsTests
|
|
||||||
|
|
||||||
- (void)setUp {
|
|
||||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)tearDown {
|
|
||||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testExample {
|
|
||||||
// This is an example of a functional test case.
|
|
||||||
// Use XCTAssert and related functions to verify your tests produce the correct results.
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testPerformanceExample {
|
|
||||||
// This is an example of a performance test case.
|
|
||||||
[self measureBlock:^{
|
|
||||||
// Put the code you want to measure the time of here.
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
33
MonsterCardsTests/MonsterCardsTests.swift
Normal file
33
MonsterCardsTests/MonsterCardsTests.swift
Normal file
@@ -0,0 +1,33 @@
|
|||||||
|
//
|
||||||
|
// MonsterCardsTests.swift
|
||||||
|
// MonsterCardsTests
|
||||||
|
//
|
||||||
|
// Created by Tom Hicks on 1/15/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
@testable import MonsterCards
|
||||||
|
|
||||||
|
class MonsterCardsTests: XCTestCase {
|
||||||
|
|
||||||
|
override func setUpWithError() throws {
|
||||||
|
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||||
|
}
|
||||||
|
|
||||||
|
override func tearDownWithError() throws {
|
||||||
|
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||||
|
}
|
||||||
|
|
||||||
|
func testExample() throws {
|
||||||
|
// This is an example of a functional test case.
|
||||||
|
// Use XCTAssert and related functions to verify your tests produce the correct results.
|
||||||
|
}
|
||||||
|
|
||||||
|
func testPerformanceExample() throws {
|
||||||
|
// This is an example of a performance test case.
|
||||||
|
self.measure {
|
||||||
|
// Put the code you want to measure the time of here.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -1,130 +0,0 @@
|
|||||||
//
|
|
||||||
// EditMonsterViewControllerTests.m
|
|
||||||
// MonsterCardsTests
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/12/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <XCTest/XCTest.h>
|
|
||||||
@import OCMockito;
|
|
||||||
@import OCHamcrest;
|
|
||||||
#import "MCShortStringFieldTableViewCell.h"
|
|
||||||
#import "EditMonsterViewController.h"
|
|
||||||
#import "AppDelegate.h"
|
|
||||||
|
|
||||||
@interface EditMonsterViewControllerTests : XCTestCase
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation EditMonsterViewControllerTests {
|
|
||||||
EditMonsterViewController *_viewController;
|
|
||||||
NSManagedObjectContext *_context;
|
|
||||||
AppDelegate *_appDelegate;
|
|
||||||
NSPersistentCloudKitContainer *_persistentContainer;
|
|
||||||
Monster *_monster;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)setUp {
|
|
||||||
_monster = mock([Monster class]);
|
|
||||||
_viewController = [[EditMonsterViewController alloc] init];
|
|
||||||
_context = mock([NSManagedObjectContext class]);
|
|
||||||
_appDelegate = mock([AppDelegate class]);
|
|
||||||
_persistentContainer = mock([NSPersistentCloudKitContainer class]);
|
|
||||||
|
|
||||||
UIApplication.sharedApplication.delegate = _appDelegate;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)tearDown {
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testRendersSubtypeCell {
|
|
||||||
UITableView *monstersTableView = mock([UITableView class]);
|
|
||||||
NSIndexPath *path = [NSIndexPath indexPathForRow:3 inSection:0];
|
|
||||||
MCShortStringFieldTableViewCell *shortStringCell = [[MCShortStringFieldTableViewCell alloc] init];
|
|
||||||
UITextField *textField = [[UITextField alloc] init];
|
|
||||||
shortStringCell.textField = textField;
|
|
||||||
|
|
||||||
[given([monstersTableView dequeueReusableCellWithIdentifier:@"MCShortStringField"]) willReturn:shortStringCell];
|
|
||||||
|
|
||||||
_monster.subtype = @"elf";
|
|
||||||
_viewController.originalMonster = _monster;
|
|
||||||
_viewController.monsterTableView = monstersTableView;
|
|
||||||
[_viewController viewDidLoad];
|
|
||||||
[_viewController viewWillAppear:NO];
|
|
||||||
|
|
||||||
UITableViewCell *cell = [_viewController tableView:monstersTableView cellForRowAtIndexPath:path];
|
|
||||||
|
|
||||||
XCTAssertNotNil(cell);
|
|
||||||
|
|
||||||
XCTAssertTrue([cell isKindOfClass:[MCShortStringFieldTableViewCell class]]);
|
|
||||||
shortStringCell = (MCShortStringFieldTableViewCell*)cell;
|
|
||||||
XCTAssertEqualObjects(@"monster.subtype", shortStringCell.identifier);
|
|
||||||
XCTAssertEqualObjects(@"Subtype", shortStringCell.textField.placeholder);
|
|
||||||
XCTAssertEqualObjects(@"", shortStringCell.textField.text);
|
|
||||||
XCTAssertEqual(_viewController, shortStringCell.delegate);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testEditingSubtype {
|
|
||||||
UIViewController *destinationVC = mock([UIViewController class]);
|
|
||||||
UIStoryboardSegue *segue = [UIStoryboardSegue segueWithIdentifier:@"SaveChanges" source:_viewController destination:destinationVC performHandler:^{}];
|
|
||||||
|
|
||||||
_monster = [[Monster alloc] initWithContext:_context];
|
|
||||||
_monster.subtype = @"elf";
|
|
||||||
_viewController.originalMonster = _monster;
|
|
||||||
[_viewController viewDidLoad];
|
|
||||||
[_viewController viewWillAppear:NO];
|
|
||||||
|
|
||||||
[_viewController editableValueDidChange:@"newValue" forIdentifier:@"monster.subtype" andType:@"String"];
|
|
||||||
|
|
||||||
[_viewController prepareForSegue:segue sender:nil];
|
|
||||||
|
|
||||||
XCTAssertEqualObjects(@"newValue", _viewController.originalMonster.subtype);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testRendersAlignmentCell {
|
|
||||||
UITableView *monstersTableView = mock([UITableView class]);
|
|
||||||
NSIndexPath *path = [NSIndexPath indexPathForRow:4 inSection:0];
|
|
||||||
MCShortStringFieldTableViewCell *shortStringCell = [[MCShortStringFieldTableViewCell alloc] init];
|
|
||||||
UITextField *textField = [[UITextField alloc] init];
|
|
||||||
shortStringCell.textField = textField;
|
|
||||||
|
|
||||||
[given([monstersTableView dequeueReusableCellWithIdentifier:@"MCShortStringField"]) willReturn:shortStringCell];
|
|
||||||
|
|
||||||
_monster.alignment = @"chaotic good";
|
|
||||||
_viewController.originalMonster = _monster;
|
|
||||||
_viewController.monsterTableView = monstersTableView;
|
|
||||||
[_viewController viewDidLoad];
|
|
||||||
[_viewController viewWillAppear:NO];
|
|
||||||
|
|
||||||
UITableViewCell *cell = [_viewController tableView:monstersTableView cellForRowAtIndexPath:path];
|
|
||||||
|
|
||||||
XCTAssertNotNil(cell);
|
|
||||||
|
|
||||||
XCTAssertTrue([cell isKindOfClass:[MCShortStringFieldTableViewCell class]]);
|
|
||||||
shortStringCell = (MCShortStringFieldTableViewCell*)cell;
|
|
||||||
XCTAssertEqualObjects(@"monster.alignment", shortStringCell.identifier);
|
|
||||||
XCTAssertEqualObjects(@"Alignment", shortStringCell.textField.placeholder);
|
|
||||||
XCTAssertEqualObjects(@"", shortStringCell.textField.text);
|
|
||||||
XCTAssertEqual(_viewController, shortStringCell.delegate);
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testEditingAlignment {
|
|
||||||
UIViewController *destinationVC = mock([UIViewController class]);
|
|
||||||
UIStoryboardSegue *segue = [UIStoryboardSegue segueWithIdentifier:@"SaveChanges" source:_viewController destination:destinationVC performHandler:^{}];
|
|
||||||
|
|
||||||
_monster = [[Monster alloc] initWithContext:_context];
|
|
||||||
_monster.alignment = @"chaotic good";
|
|
||||||
_viewController.originalMonster = _monster;
|
|
||||||
[_viewController viewDidLoad];
|
|
||||||
[_viewController viewWillAppear:NO];
|
|
||||||
|
|
||||||
[_viewController editableValueDidChange:@"newValue" forIdentifier:@"monster.alignment" andType:@"String"];
|
|
||||||
|
|
||||||
[_viewController prepareForSegue:segue sender:nil];
|
|
||||||
|
|
||||||
XCTAssertEqualObjects(@"newValue", _viewController.originalMonster.alignment);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@end
|
|
||||||
@@ -1,48 +0,0 @@
|
|||||||
//
|
|
||||||
// MonsterCardsUITests.m
|
|
||||||
// MonsterCardsUITests
|
|
||||||
//
|
|
||||||
// Created by Tom Hicks on 9/2/20.
|
|
||||||
// Copyright © 2020 Tom Hicks. All rights reserved.
|
|
||||||
//
|
|
||||||
|
|
||||||
#import <XCTest/XCTest.h>
|
|
||||||
|
|
||||||
@interface MonsterCardsUITests : XCTestCase
|
|
||||||
|
|
||||||
@end
|
|
||||||
|
|
||||||
@implementation MonsterCardsUITests
|
|
||||||
|
|
||||||
- (void)setUp {
|
|
||||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
|
||||||
|
|
||||||
// In UI tests it is usually best to stop immediately when a failure occurs.
|
|
||||||
self.continueAfterFailure = NO;
|
|
||||||
|
|
||||||
// In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)tearDown {
|
|
||||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testExample {
|
|
||||||
// UI tests must launch the application that they test.
|
|
||||||
XCUIApplication *app = [[XCUIApplication alloc] init];
|
|
||||||
[app launch];
|
|
||||||
|
|
||||||
// Use recording to get started writing UI tests.
|
|
||||||
// Use XCTAssert and related functions to verify your tests produce the correct results.
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)testLaunchPerformance {
|
|
||||||
if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, *)) {
|
|
||||||
// This measures how long it takes to launch your application.
|
|
||||||
[self measureWithMetrics:@[XCTOSSignpostMetric.applicationLaunchMetric] block:^{
|
|
||||||
[[[XCUIApplication alloc] init] launch];
|
|
||||||
}];
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@end
|
|
||||||
42
MonsterCardsUITests/MonsterCardsUITests.swift
Normal file
42
MonsterCardsUITests/MonsterCardsUITests.swift
Normal file
@@ -0,0 +1,42 @@
|
|||||||
|
//
|
||||||
|
// MonsterCardsUITests.swift
|
||||||
|
// MonsterCardsUITests
|
||||||
|
//
|
||||||
|
// Created by Tom Hicks on 1/15/21.
|
||||||
|
//
|
||||||
|
|
||||||
|
import XCTest
|
||||||
|
|
||||||
|
class MonsterCardsUITests: XCTestCase {
|
||||||
|
|
||||||
|
override func setUpWithError() throws {
|
||||||
|
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||||
|
|
||||||
|
// In UI tests it is usually best to stop immediately when a failure occurs.
|
||||||
|
continueAfterFailure = false
|
||||||
|
|
||||||
|
// In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
|
||||||
|
}
|
||||||
|
|
||||||
|
override func tearDownWithError() throws {
|
||||||
|
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||||
|
}
|
||||||
|
|
||||||
|
func testExample() throws {
|
||||||
|
// UI tests must launch the application that they test.
|
||||||
|
let app = XCUIApplication()
|
||||||
|
app.launch()
|
||||||
|
|
||||||
|
// Use recording to get started writing UI tests.
|
||||||
|
// Use XCTAssert and related functions to verify your tests produce the correct results.
|
||||||
|
}
|
||||||
|
|
||||||
|
func testLaunchPerformance() throws {
|
||||||
|
if #available(macOS 10.15, iOS 13.0, tvOS 13.0, *) {
|
||||||
|
// This measures how long it takes to launch your application.
|
||||||
|
measure(metrics: [XCTApplicationLaunchMetric()]) {
|
||||||
|
XCUIApplication().launch()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
23
Podfile
23
Podfile
@@ -1,23 +0,0 @@
|
|||||||
# Uncomment the next line to define a global platform for your project
|
|
||||||
platform :ios, '12.0'
|
|
||||||
|
|
||||||
target 'MonsterCards' do
|
|
||||||
# Comment the next line if you don't want to use dynamic frameworks
|
|
||||||
use_frameworks!
|
|
||||||
|
|
||||||
# Pods for MonsterCards
|
|
||||||
|
|
||||||
target 'MonsterCardsTests' do
|
|
||||||
inherit! :search_paths
|
|
||||||
# Pods for testing
|
|
||||||
use_frameworks!
|
|
||||||
pod 'OCMockito', '~> 5.0'
|
|
||||||
end
|
|
||||||
|
|
||||||
target 'MonsterCardsUITests' do
|
|
||||||
# Pods for testing
|
|
||||||
use_frameworks!
|
|
||||||
pod 'OCMockito', '~> 5.0'
|
|
||||||
end
|
|
||||||
|
|
||||||
end
|
|
||||||
20
Podfile.lock
20
Podfile.lock
@@ -1,20 +0,0 @@
|
|||||||
PODS:
|
|
||||||
- OCHamcrest (7.1.2)
|
|
||||||
- OCMockito (5.1.3):
|
|
||||||
- OCHamcrest (~> 7.0)
|
|
||||||
|
|
||||||
DEPENDENCIES:
|
|
||||||
- OCMockito (~> 5.0)
|
|
||||||
|
|
||||||
SPEC REPOS:
|
|
||||||
trunk:
|
|
||||||
- OCHamcrest
|
|
||||||
- OCMockito
|
|
||||||
|
|
||||||
SPEC CHECKSUMS:
|
|
||||||
OCHamcrest: b284c9592c28c1e4025a8542e67ea41a635d0d73
|
|
||||||
OCMockito: 677cbb4a18fd492b5a4fb10144dada4de5ddb877
|
|
||||||
|
|
||||||
PODFILE CHECKSUM: 36e78345fa3ece507b6ca1b4a3033ab665fec396
|
|
||||||
|
|
||||||
COCOAPODS: 1.9.3
|
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user