Adds senses and passive perception.

Also makes modifier calculations return Int instead of Int64.
This commit is contained in:
2021-03-24 17:46:04 -07:00
parent 596186deaa
commit 3ec62789c6
7 changed files with 110 additions and 44 deletions

View File

@@ -45,4 +45,12 @@ class StringHelper {
return str!.containsCaseInsensitive(match)
}
static func safeEqualsIgnoreCase(_ str: String?, _ match: String) -> Bool {
if (str == nil) {
return false
}
return str!.lowercased() == match.lowercased()
}
}

View File

@@ -625,7 +625,7 @@ public class Monster: NSManagedObject {
}
}
// MARK: OTHER
// MARK: Immunities, Resistances, and Vulnerabilities
var damageVulnerabilitiesDescription: String {
get {
@@ -656,6 +656,40 @@ public class Monster: NSManagedObject {
}
}
// MARK: OTHER
var passivePerception: Int {
get {
let perceptionSkill = skillsArray.first(where: {
StringHelper.safeEqualsIgnoreCase($0.name, "Perception")
})
if (perceptionSkill == nil) {
return wisdomModifier
} else if (perceptionSkill?.wrappedProficiency == ProficiencyType.expertise) {
return wisdomModifier + proficiencyBonus + proficiencyBonus
} else if (perceptionSkill?.wrappedProficiency == ProficiencyType.proficient) {
return wisdomModifier + proficiencyBonus
} else {
return wisdomModifier
}
}
}
var sensesDescription: String {
get {
var modifiedSenses = self.senses?.sorted() ?? []
let hasPassivePerceptionSense = modifiedSenses.contains(where: {
$0.starts(with: "passive Perception")
})
if (!hasPassivePerceptionSense) {
let calculatedPassivePerception = String(format: "passive Perception %+d", passivePerception)
modifiedSenses.append(calculatedPassivePerception)
}
return modifiedSenses.sorted().joined(separator: ", ")
}
}
var languagesArray: [String] {
get {
return ["Common", "Goblin"]

View File

@@ -53,6 +53,7 @@ class MonsterViewModel: ObservableObject {
@Published var damageResistances: [StringViewModel]
@Published var damageVulnerabilities: [StringViewModel]
@Published var conditionImmunities: [StringViewModel]
@Published var senses: [StringViewModel]
init(_ rawMonster: Monster? = nil) {
self.name = ""
@@ -98,6 +99,7 @@ class MonsterViewModel: ObservableObject {
self.damageResistances = []
self.damageVulnerabilities = []
self.conditionImmunities = []
self.senses = []
if (rawMonster != nil) {
self.copyFromMonster(monster: rawMonster!)
@@ -160,6 +162,10 @@ class MonsterViewModel: ObservableObject {
self.conditionImmunities = (monster.conditionImmunities ?? [])
.map {StringViewModel($0)}
.sorted()
self.senses = (monster.senses ?? [])
.map {StringViewModel($0)}
.sorted()
}
func copyToMonster(monster: Monster) {
@@ -226,5 +232,6 @@ class MonsterViewModel: ObservableObject {
monster.damageImmunities = damageImmunities.map {$0.name}
monster.damageResistances = damageResistances.map {$0.name}
monster.damageVulnerabilities = damageVulnerabilities.map {$0.name}
monster.senses = senses.map {$0.name}
}
}

View File

@@ -48,17 +48,17 @@ public class Skill: NSManagedObject {
}
}
var modifier: Int64 {
var modifier: Int {
get {
let proficiencyBonus = Double(monster?.proficiencyBonus ?? 0)
let abilityScoreModifier = Double(monster?.abilityModifierForAbilityScore(wrappedAbilityScore) ?? 0)
switch wrappedProficiency {
case .none:
return Int64(abilityScoreModifier)
return Int(abilityScoreModifier)
case .proficient:
return Int64(abilityScoreModifier + proficiencyBonus)
return Int(abilityScoreModifier + proficiencyBonus)
case .expertise:
return Int64(abilityScoreModifier + 2 * proficiencyBonus)
return Int(abilityScoreModifier + 2 * proficiencyBonus)
}
}
}

View File

@@ -39,6 +39,7 @@
<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="senses" optional="YES" attributeType="Transformable" valueTransformerName="NSSecureUnarchiveFromDataTransformerName" customClassName="[String]"/>
<attribute name="shieldBonus" attributeType="Integer 64" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="size" attributeType="String" defaultValueString=""/>
<attribute name="strengthSavingThrowAdvantage" attributeType="String" defaultValueString="none"/>
@@ -63,7 +64,7 @@
<relationship name="monster" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="Monster" inverseName="skills" inverseEntity="Monster"/>
</entity>
<elements>
<element name="Monster" positionX="-63" positionY="-18" width="128" height="824"/>
<element name="Monster" positionX="-63" positionY="-18" width="128" height="839"/>
<element name="Skill" positionX="-63" positionY="135" width="128" height="14"/>
</elements>
</model>

View File

@@ -20,45 +20,53 @@ struct EditMonster: View {
var body: some View {
List {
NavigationLink(
"Basic Info",
destination: EditBasicInfo(monsterViewModel: monsterViewModel))
Group {
NavigationLink(
"Basic Info",
destination: EditBasicInfo(monsterViewModel: monsterViewModel))
NavigationLink(
"Armor",
destination: EditArmor(monsterViewModel: monsterViewModel))
NavigationLink(
"Armor",
destination: EditArmor(monsterViewModel: monsterViewModel))
NavigationLink(
"Speed",
destination: EditSpeed(monsterViewModel: monsterViewModel))
NavigationLink(
"Speed",
destination: EditSpeed(monsterViewModel: monsterViewModel))
NavigationLink(
"Ability Scores",
destination: EditAbilityScores(monsterViewModel: monsterViewModel))
NavigationLink(
"Ability Scores",
destination: EditAbilityScores(monsterViewModel: monsterViewModel))
NavigationLink(
"Saving Throws",
destination: EditSavingThrows(monsterViewModel: monsterViewModel))
NavigationLink(
"Saving Throws",
destination: EditSavingThrows(monsterViewModel: monsterViewModel))
NavigationLink(
"Skills",
destination: EditSkills(monsterViewModel: monsterViewModel))
NavigationLink(
"Skills",
destination: EditSkills(monsterViewModel: monsterViewModel))
NavigationLink(
"Condition Immunities",
destination: EditStrings(viewModel: monsterViewModel, path: \.conditionImmunities, title: "Condition Immunities"))
NavigationLink(
"Condition Immunities",
destination: EditStrings(viewModel: monsterViewModel, path: \.conditionImmunities, title: "Condition Immunities"))
NavigationLink(
"Damage Immunities",
destination: EditStrings(viewModel: monsterViewModel, path: \.damageImmunities, title: "Damage Immunities"))
NavigationLink(
"Damage Immunities",
destination: EditStrings(viewModel: monsterViewModel, path: \.damageImmunities, title: "Damage Immunities"))
NavigationLink(
"Damage Resistances",
destination: EditStrings(viewModel: monsterViewModel, path: \.damageResistances, title: "Damage Resistances"))
NavigationLink(
"Damage Resistances",
destination: EditStrings(viewModel: monsterViewModel, path: \.damageResistances, title: "Damage Resistances"))
NavigationLink(
"Damage Vulnerabilities",
destination: EditStrings(viewModel: monsterViewModel, path: \.damageVulnerabilities, title: "Damage Vulnerabilities"))
}
Group {
NavigationLink(
"Senses",
destination: EditStrings(viewModel: monsterViewModel, path: \.senses, title: "Senses"))
}
NavigationLink(
"Damage Vulnerabilities",
destination: EditStrings(viewModel: monsterViewModel, path: \.damageVulnerabilities, title: "Damage Vulnerabilities"))
}
.onAppear(perform: copyMonsterToLocal)
.toolbar(content: {

View File

@@ -137,6 +137,7 @@ struct ResistancesAndImmunitiesView: View {
let monsterDamageResistancesDescription = monster.damageResistancesDescription
let monsterDamageImmunitiesDescription = monster.damageImmunitiesDescription
let monsterConditionImmunitiesDescription = monster.conditionImmunitiesDescription
let monsterSensesDescription = monster.sensesDescription
// Damage Vulnerabilities
if (!monsterDamageVulnerabilitiesDescription.isEmpty) {
@@ -165,6 +166,13 @@ struct ResistancesAndImmunitiesView: View {
Text(monsterConditionImmunitiesDescription)
}
}
// Senses
if (!monsterSensesDescription.isEmpty) {
LabeledField("Senses") {
Text(monsterSensesDescription)
}
}
}
}