diff --git a/Bunch-O--Dice/MainView.swift b/Bunch-O--Dice/MainView.swift index 091db2a..f5905d6 100644 --- a/Bunch-O--Dice/MainView.swift +++ b/Bunch-O--Dice/MainView.swift @@ -223,6 +223,8 @@ struct MainView: View { .font(.system(size: controlSize)) Text("Modifier") } + .disabled(current.torrent || current.psychic) + .opacity((current.torrent || current.psychic) ? 0.4 : 1.0) Spacer() VStack { Text("Blow Up") @@ -231,16 +233,16 @@ struct MainView: View { Image(systemName: "die.face.6") .foregroundColor(.primary) } - .disabled(current.blowupUsed) Button(action: { current.applyBlowup(face: .five) }) { Image(systemName: "die.face.5") .foregroundColor(.primary) } - .disabled(current.blowupUsed) } .padding() .font(.system(size: controlSize)) } + .disabled(current.blowupUsed || current.torrent) + .opacity((current.blowupUsed || current.torrent) ? 0.4 : 1.0) Spacer() } HStack { @@ -258,29 +260,39 @@ struct MainView: View { .font(.system(size: controlSize)) } .padding(.horizontal) + .disabled(current.torrent) + .opacity(current.torrent ? 0.4 : 1.0) + HStack { + Toggle("Torrent", isOn: $current.torrent) + Spacer() + Toggle("Psychic", isOn: $current.psychic) + .disabled(current.torrent) + .opacity(current.torrent ? 0.4 : 1.0) + } + .padding(.horizontal) } .padding() } //MARK: die views @ViewBuilder func die(whichDie: Dice.DiceType) -> some View { - let size = 30.0 + let size = 22.0 let displayColorHit = (whichDie.rawValue >= current.cutoff.rawValue) ? Color.red : Color.primary let displayColorArmor = (whichDie.rawValue <= current.cutoff.rawValue) ? Color.red : Color.primary let displayColorArmorText = (whichDie.rawValue < current.cutoff.rawValue) ? Color.red : Color.primary - - HStack{ - Text(String(current.dice.value(which: whichDie))) - .foregroundColor(current.state == .armour ? - displayColorArmorText : displayColorHit) - .frame(minWidth: 44, alignment: .trailing) + + VStack(spacing: 2) { Image(systemName: "die.face." + String(whichDie.rawValue) + ".fill") .foregroundColor(current.state == .armour ? displayColorArmor : displayColorHit) - + Text(String(current.dice.value(which: whichDie))) + .foregroundColor(current.state == .armour ? + displayColorArmorText : displayColorHit) + .frame(minWidth: 30, alignment: .center) } .font(.system(size: size)) - .padding(2) + .padding(4) + .frame(maxWidth: .infinity) .gesture(TapGesture().onEnded { if current.state == .hit { if whichDie.rawValue != 1 { @@ -292,7 +304,7 @@ struct MainView: View { } @ViewBuilder func diePool() -> some View { - VStack(alignment: .leading) { + HStack(spacing: 0) { ForEach(Dice.DiceType.allCases.reversed(), id: \.rawValue) { i in die(whichDie: i) } @@ -325,37 +337,47 @@ struct MainView: View { if current.state == .armour {myCircle()} Text(RollState.armour.print())}} }.font(.system(size: 60)) - VStack{ - HStack { - VStack { - Button(action: { showFillView.toggle() }) { - VStack(spacing: 2) { - Text("Dice Pool") - .font(.caption) - Text(String(current.total)) - .font(.system(size: 28, weight: .black)) - } - .padding(.horizontal, 20) - .padding(.vertical, 8) - .background(Color(blue).opacity(0.35)) - .clipShape(RoundedRectangle(cornerRadius: 10)) - .foregroundColor(.primary) + VStack(spacing: 12) { + HStack(spacing: 12) { + Button(action: { showFillView.toggle() }) { + VStack(spacing: 4) { + Text("Dice Pool") + .font(.caption) + Text(String(current.total)) + .font(.system(size: 28, weight: .black)) } - .sheet(isPresented: $showFillView, - onDismiss: { current.resetPool() }) { - VStack { - AddToPool() - Button("Accept", action: {showFillView.toggle()}) - .padding(20) - } + .frame(maxWidth: .infinity) + .padding(.vertical, 12) + .background(Color.white) + .clipShape(RoundedRectangle(cornerRadius: 10)) + .foregroundColor(.black) + } + .sheet(isPresented: $showFillView, + onDismiss: { current.resetPool() }) { + VStack { + AddToPool() + Button("Accept", action: {showFillView.toggle()}) + .padding(20) } - Button(action: current.incrementState) { - buttons(color: red, label: current.state.text(), value: current.result) + } + Button(action: current.incrementState) { + VStack(spacing: 4) { + Text(current.state.text()) + .font(.caption) + Text(String(current.result)) + .font(.system(size: 28, weight: .black)) } - }.padding(.leading, padSize) - Spacer() - diePool().padding(.trailing, padSize) - }.frame(maxHeight: 285) + .frame(maxWidth: .infinity) + .padding(.vertical, 12) + .background(Color.white) + .clipShape(RoundedRectangle(cornerRadius: 10)) + .foregroundColor(.black) + } + } + .padding() + .background(Color.black) + .clipShape(RoundedRectangle(cornerRadius: 14)) + diePool() stateView() }.padding() } diff --git a/Bunch-O--Dice/Model/Dice.swift b/Bunch-O--Dice/Model/Dice.swift index d6055dd..7d03b7a 100644 --- a/Bunch-O--Dice/Model/Dice.swift +++ b/Bunch-O--Dice/Model/Dice.swift @@ -148,7 +148,7 @@ struct Dice { func armour(die: Int, invulnerable: Bool, save: Int, armourPenetration: Int) -> Int { var total = 0 - switch invulnerable ? save : (save - armourPenetration) { + switch invulnerable ? save : (save + armourPenetration) { case 7...: total = six + five + four + three + two + one case 6: diff --git a/Bunch-O--Dice/Model/RollState.swift b/Bunch-O--Dice/Model/RollState.swift index 04a0529..db77b5c 100644 --- a/Bunch-O--Dice/Model/RollState.swift +++ b/Bunch-O--Dice/Model/RollState.swift @@ -152,6 +152,8 @@ class Roll: ObservableObject { @Published var saveHasRerolled = false @Published var blowupUsed = false + @Published var torrent = false + @Published var psychic = false // calculated variables var totalDigits: [Int] { @@ -216,7 +218,9 @@ class Roll: ObservableObject { var hit: Int { get { - return self.dice.hit(die: total, cutoff: self.hitCutoff.rawValue, modifier: self.modifier.rawValue) + if torrent { return total } + let mod = psychic ? Dice.RollModifiers.none.rawValue : modifier.rawValue + return self.dice.hit(die: total, cutoff: self.hitCutoff.rawValue, modifier: mod) } } @@ -383,7 +387,7 @@ class Roll: ObservableObject { cutoff = woundCutoff case .armour: mode = saveRerollMode - cutoff = invulnerable ? armourSave : (armourSave - armourPenetration) + cutoff = invulnerable ? armourSave : (armourSave + armourPenetration) } switch mode { diff --git a/Goals.md b/Goals.md index a7358c5..53890f8 100644 --- a/Goals.md +++ b/Goals.md @@ -1,6 +1,6 @@ - [ ] Update graphics to clarify roll context (to hit / to wound / armor save) #focus/professional - - [ ] Add stacking flat hit/wound modifier input, capped at net ±1 #focus/professional - - [ ] Add reroll mode selector (none / reroll 1s / reroll all failed / reroll all) #focus/professional + - [x] Add stacking flat hit/wound modifier input, capped at net ±1 #focus/professional + - [x] Add reroll mode selector (none / reroll 1s / reroll all failed / reroll all) #focus/professional - [ ] Add critical-trigger layer keyed off unmodified natural 6s (Sustained Hits, Lethal Hits, Devastating Wounds, Anti-X) #focus/professional - [ ] Add "ignore all modifiers" flag (Psychic) #focus/professional - [ ] Add "auto-success, no roll" flag (Torrent) #focus/professional