Makes MonsterDetail use a MonsterViewModel instead of the core data type Monster.
This commit is contained in:
@@ -34,7 +34,7 @@ public class AbilityViewModel: NSObject, ObservableObject, Identifiable, NSSecur
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func renderedText(_ monster: Monster) -> String {
|
func renderedText(_ monster: MonsterViewModel) -> String {
|
||||||
let strSave = monster.strengthModifier + monster.proficiencyBonus + 8
|
let strSave = monster.strengthModifier + monster.proficiencyBonus + 8
|
||||||
let dexSave = monster.dexterityModifier + monster.proficiencyBonus + 8
|
let dexSave = monster.dexterityModifier + monster.proficiencyBonus + 8
|
||||||
let conSave = monster.constitutionModifier + monster.proficiencyBonus + 8
|
let conSave = monster.constitutionModifier + monster.proficiencyBonus + 8
|
||||||
|
|||||||
@@ -125,4 +125,25 @@ class SkillViewModel: ObservableObject, Comparable, Hashable, Identifiable {
|
|||||||
newSkill.wrappedAdvantage = advantage
|
newSkill.wrappedAdvantage = advantage
|
||||||
return newSkill
|
return newSkill
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func modifier(forMonster: MonsterViewModel) -> Int {
|
||||||
|
let proficiencyBonus = Double(forMonster.proficiencyBonus)
|
||||||
|
let abilityScoreModifier = Double(forMonster.abilityModifierForAbilityScore(abilityScore))
|
||||||
|
switch proficiency {
|
||||||
|
case .none:
|
||||||
|
return Int(abilityScoreModifier)
|
||||||
|
case .proficient:
|
||||||
|
return Int(abilityScoreModifier + proficiencyBonus)
|
||||||
|
case .expertise:
|
||||||
|
return Int(abilityScoreModifier + 2 * proficiencyBonus)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func skillDescription(forMonster: MonsterViewModel) -> String {
|
||||||
|
var advantageLabel = Monster.advantageLabelStringForType(advantage)
|
||||||
|
if (advantageLabel != "") {
|
||||||
|
advantageLabel = " " + advantageLabel
|
||||||
|
}
|
||||||
|
return String(format: "%@ %+d%@", name, modifier(forMonster: forMonster), advantageLabel)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -20,7 +20,7 @@ struct Library: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
NavigationView{
|
NavigationView{
|
||||||
List(allMonsters) { monster in
|
List(allMonsters) { monster in
|
||||||
NavigationLink(destination: MonsterDetail(monster: monster)) {
|
NavigationLink(destination: MonsterDetailWrapper(monster: monster)) {
|
||||||
Text(monster.name ?? "")
|
Text(monster.name ?? "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -72,7 +72,7 @@ struct SmallAbilityScore: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct BasicInfoView: View {
|
struct BasicInfoView: View {
|
||||||
@ObservedObject var monster: Monster
|
@ObservedObject var monster: MonsterViewModel
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
let monsterMeta = monster.meta
|
let monsterMeta = monster.meta
|
||||||
@@ -113,7 +113,7 @@ struct BasicInfoView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct AbilityScoresView: View {
|
struct AbilityScoresView: View {
|
||||||
@ObservedObject var monster: Monster
|
@ObservedObject var monster: MonsterViewModel
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
SectionDivider()
|
SectionDivider()
|
||||||
@@ -131,7 +131,7 @@ struct AbilityScoresView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct ResistancesAndImmunitiesView: View {
|
struct ResistancesAndImmunitiesView: View {
|
||||||
@ObservedObject var monster: Monster
|
@ObservedObject var monster: MonsterViewModel
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
let monsterDamageVulnerabilitiesDescription = monster.damageVulnerabilitiesDescription
|
let monsterDamageVulnerabilitiesDescription = monster.damageVulnerabilitiesDescription
|
||||||
@@ -178,7 +178,7 @@ struct ResistancesAndImmunitiesView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct SavingThrowsAndSkillsView: View {
|
struct SavingThrowsAndSkillsView: View {
|
||||||
@ObservedObject var monster: Monster
|
@ObservedObject var monster: MonsterViewModel
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
let savingThrowsDescription = monster.savingThrowsDescription
|
let savingThrowsDescription = monster.savingThrowsDescription
|
||||||
@@ -200,37 +200,28 @@ struct SavingThrowsAndSkillsView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MonsterDetail: View {
|
struct MonsterDetailView: View {
|
||||||
let kTextColor: Color = Color(hex: 0x982818)
|
let kTextColor = Color(hex: 0x982818)
|
||||||
|
|
||||||
@ObservedObject var monster: Monster
|
var viewModel: MonsterViewModel
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
|
let monsterLanguagesDescription = viewModel.languagesDescription
|
||||||
|
let monsterChallengeRatingDescription = viewModel.challengeRatingDescription
|
||||||
|
|
||||||
ScrollView {
|
ScrollView {
|
||||||
// TODO: Consider adding an inmage here at the top
|
// TODO: Consider adding an inmage here at the top
|
||||||
VStack (alignment: .leading) {
|
VStack (alignment: .leading) {
|
||||||
let monsterLanguagesDescription = monster.languagesDescription
|
|
||||||
let monsterChallengeRatingDescription = monster.challengeRatingDescription
|
|
||||||
let monsterAbilities: [AbilityViewModel] = monster.abilities ?? []
|
|
||||||
let monsterActions: [AbilityViewModel] = monster.actions ?? []
|
|
||||||
let monsterLegendaryActions: [AbilityViewModel] = monster.legendaryActions ?? []
|
|
||||||
|
|
||||||
BasicInfoView(monster: monster)
|
|
||||||
|
|
||||||
// TODO: Find a way to hide unnecessarry dividiers.
|
// TODO: Find a way to hide unnecessarry dividiers.
|
||||||
// if sections 0, 1, 2, and 3 are present there should be a divider between each of them
|
// if sections 0, 1, 2, and 3 are present there should be a divider between each of them
|
||||||
// if section 1 is not present there should be one and only one divider between sections 0 and 2 as well as the one between 2 and 3
|
// if section 1 is not present there should be one and only one divider between sections 0 and 2 as well as the one between 2 and 3
|
||||||
// if sections 1 and 2 are not present there should be a single divider between sections 0 and 3
|
// if sections 1 and 2 are not present there should be a single divider between sections 0 and 3
|
||||||
|
|
||||||
|
BasicInfoView(monster: viewModel)
|
||||||
AbilityScoresView(monster: monster)
|
AbilityScoresView(monster: viewModel)
|
||||||
|
|
||||||
SectionDivider()
|
SectionDivider()
|
||||||
|
SavingThrowsAndSkillsView(monster: viewModel)
|
||||||
SavingThrowsAndSkillsView(monster: monster)
|
ResistancesAndImmunitiesView(monster: viewModel)
|
||||||
|
|
||||||
ResistancesAndImmunitiesView(monster: monster)
|
|
||||||
|
|
||||||
Group {
|
Group {
|
||||||
// Languages
|
// Languages
|
||||||
if (!monsterLanguagesDescription.isEmpty) {
|
if (!monsterLanguagesDescription.isEmpty) {
|
||||||
@@ -248,27 +239,27 @@ struct MonsterDetail: View {
|
|||||||
|
|
||||||
// Proficiency Bonus
|
// Proficiency Bonus
|
||||||
LabeledField("Proficiency Bonus") {
|
LabeledField("Proficiency Bonus") {
|
||||||
Text(String(monster.proficiencyBonus))
|
Text(String(viewModel.proficiencyBonus))
|
||||||
}
|
}
|
||||||
|
|
||||||
// Abilities
|
// Abilities
|
||||||
if (monsterAbilities.count > 0) {
|
if (viewModel.abilities.count > 0) {
|
||||||
ForEach(monsterAbilities) { ability in
|
ForEach(viewModel.abilities) { ability in
|
||||||
VStack {
|
VStack {
|
||||||
Markdown(Document(ability.renderedText(monster)))
|
Markdown(Document(ability.renderedText(viewModel)))
|
||||||
Divider()
|
Divider()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
if (monsterActions.count > 0) {
|
if (viewModel.actions.count > 0) {
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
Text("Actions")
|
Text("Actions")
|
||||||
.font(.system(size: 24, weight: .bold))
|
.font(.system(size: 24, weight: .bold))
|
||||||
ForEach(monsterActions) { action in
|
ForEach(viewModel.actions) { action in
|
||||||
VStack {
|
VStack {
|
||||||
Markdown(Document(action.renderedText(monster)))
|
Markdown(Document(action.renderedText(viewModel)))
|
||||||
Divider()
|
Divider()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -276,13 +267,13 @@ struct MonsterDetail: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Legendary Actions
|
// Legendary Actions
|
||||||
if (monsterLegendaryActions.count > 0) {
|
if (viewModel.legendaryActions.count > 0) {
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
Text("Legendary Actions")
|
Text("Legendary Actions")
|
||||||
.font(.system(size: 20, weight: .bold))
|
.font(.system(size: 20, weight: .bold))
|
||||||
ForEach(monsterLegendaryActions) { action in
|
ForEach(viewModel.legendaryActions) { action in
|
||||||
VStack {
|
VStack {
|
||||||
Markdown(Document(action.renderedText(monster)))
|
Markdown(Document(action.renderedText(viewModel)))
|
||||||
Divider()
|
Divider()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -293,6 +284,21 @@ struct MonsterDetail: View {
|
|||||||
.padding(.horizontal)
|
.padding(.horizontal)
|
||||||
.foregroundColor(kTextColor)
|
.foregroundColor(kTextColor)
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct MonsterDetailWrapper: View {
|
||||||
|
let kTextColor: Color = Color(hex: 0x982818)
|
||||||
|
|
||||||
|
@ObservedObject var monster: Monster
|
||||||
|
@StateObject private var viewModel = MonsterViewModel()
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
|
||||||
|
MonsterDetailView(viewModel: viewModel)
|
||||||
|
.onAppear(perform: {
|
||||||
|
viewModel.copyFromMonster(monster: monster)
|
||||||
|
})
|
||||||
.toolbar(content: {
|
.toolbar(content: {
|
||||||
ToolbarItem(placement: .primaryAction) {
|
ToolbarItem(placement: .primaryAction) {
|
||||||
NavigationLink("Edit", destination: EditMonster(monster: monster))
|
NavigationLink("Edit", destination: EditMonster(monster: monster))
|
||||||
@@ -307,7 +313,7 @@ struct MonsterDetail: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct MonsterDetail_Previews: PreviewProvider {
|
struct MonsterDetailWrapper_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
let context = PersistenceController.preview.container.viewContext
|
let context = PersistenceController.preview.container.viewContext
|
||||||
let monster = Monster.init(context: context)
|
let monster = Monster.init(context: context)
|
||||||
@@ -352,10 +358,10 @@ struct MonsterDetail_Previews: PreviewProvider {
|
|||||||
]
|
]
|
||||||
|
|
||||||
return Group {
|
return Group {
|
||||||
MonsterDetail(monster: monster)
|
MonsterDetailWrapper(monster: monster)
|
||||||
.environment(\.managedObjectContext, context)
|
.environment(\.managedObjectContext, context)
|
||||||
.previewDevice("iPod touch (7th generation)")
|
.previewDevice("iPod touch (7th generation)")
|
||||||
MonsterDetail(monster: monster)
|
MonsterDetailWrapper(monster: monster)
|
||||||
.environment(\.managedObjectContext, context)
|
.environment(\.managedObjectContext, context)
|
||||||
.previewDevice("iPad Pro (11-inch) (2nd generation)")
|
.previewDevice("iPad Pro (11-inch) (2nd generation)")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ struct Search: View {
|
|||||||
|
|
||||||
return false
|
return false
|
||||||
})) { monster in
|
})) { monster in
|
||||||
NavigationLink(destination: MonsterDetail(monster: monster)) {
|
NavigationLink(destination: MonsterDetailWrapper(monster: monster)) {
|
||||||
Text(monster.name ?? "")
|
Text(monster.name ?? "")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user