Skip to content

Commit c21fa04

Browse files
Introduce rapid fire and the Lock Screen camera
New people get a page in the intro after SHUTTER; everyone who had TABOR before sees a card on CATCH once, which opens the same page with a button to turn rapid fire on and the way to the Lock Screen button. A locked phone fires three shots onto a stack, then the cards turn over, the rarest last, using the developer's own stickers: Urbino 18 #8517, Solcity 12 #1075 and the Irizar. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 59e20e2 commit c21fa04

8 files changed

Lines changed: 350 additions & 6 deletions

File tree

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"images":[{"filename":"IntroStickerCommon.png","idiom":"universal"}],"info":{"author":"xcode","version":1}}
186 KB
Loading
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
{"images":[{"filename":"IntroStickerRare.png","idiom":"universal"}],"info":{"author":"xcode","version":1}}
241 KB
Loading

‎Tabor/Resources/Localizable.xcstrings‎

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10897,6 +10897,76 @@
1089710897
}
1089810898
}
1089910899
}
10900+
},
10901+
"Shoot first, look later.": {
10902+
"localizations": {
10903+
"pl": {
10904+
"stringUnit": {
10905+
"state": "translated",
10906+
"value": "Najpierw pstrykaj, potem oglądaj."
10907+
}
10908+
}
10909+
}
10910+
},
10911+
"Turn on RAPID and fire away: the shots pile up while TABOR reads them, then you flip them over like cards, the rarest last. Put the Catch button on your Lock Screen and the camera opens without unlocking; unlock to reveal what you caught.": {
10912+
"localizations": {
10913+
"pl": {
10914+
"stringUnit": {
10915+
"state": "translated",
10916+
"value": "Włącz SERYJNE i pstrykaj do woli: zdjęcia zbierają się na stosie, a TABOR je czyta. Potem odkrywasz je jak karty, najrzadszą na koniec. Dodaj przycisk Złap do ekranu blokady, a aparat otworzy się bez odblokowania; odblokuj, by je odkryć."
10917+
}
10918+
}
10919+
}
10920+
},
10921+
"NEW: RAPID FIRE": {
10922+
"localizations": {
10923+
"pl": {
10924+
"stringUnit": {
10925+
"state": "translated",
10926+
"value": "NOWOŚĆ: ZDJĘCIA SERYJNE"
10927+
}
10928+
}
10929+
}
10930+
},
10931+
"Shoot a bunch, flip them like cards": {
10932+
"localizations": {
10933+
"pl": {
10934+
"stringUnit": {
10935+
"state": "translated",
10936+
"value": "Pstrykaj seryjnie, odkrywaj jak karty"
10937+
}
10938+
}
10939+
}
10940+
},
10941+
"IT'S ON": {
10942+
"localizations": {
10943+
"pl": {
10944+
"stringUnit": {
10945+
"state": "translated",
10946+
"value": "WŁĄCZONE"
10947+
}
10948+
}
10949+
}
10950+
},
10951+
"TURN ON RAPID FIRE": {
10952+
"localizations": {
10953+
"pl": {
10954+
"stringUnit": {
10955+
"state": "translated",
10956+
"value": "WŁĄCZ ZDJĘCIA SERYJNE"
10957+
}
10958+
}
10959+
}
10960+
},
10961+
"PUT CATCH ON THE LOCK SCREEN": {
10962+
"localizations": {
10963+
"pl": {
10964+
"stringUnit": {
10965+
"state": "translated",
10966+
"value": "DODAJ ZŁAP DO EKRANU BLOKADY"
10967+
}
10968+
}
10969+
}
1090010970
}
1090110971
},
1090210972
"version": "1.0"

‎Tabor/Screens/CatchView.swift‎

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,8 @@ struct CatchView: View {
8282
/// Rapid fire: shots stack up and are dealt out as cards once you finish (#107).
8383
@AppStorage("rapidFire") private var rapid = false
8484
@State private var showControlHowTo = false
85+
@AppStorage(RapidTip.seenKey) private var rapidTipSeen = false
86+
@State private var showRapidTip = false
8587

8688
private let camera = CameraModel.shared
8789
private let live = LiveFleetService.shared
@@ -301,6 +303,7 @@ struct CatchView: View {
301303
}
302304
}
303305
.sheet(isPresented: $showControlHowTo) { CatchControlHowTo() }
306+
.sheet(isPresented: $showRapidTip) { RapidTipSheet() }
304307
.onChange(of: pickerItems) { _, items in
305308
guard !items.isEmpty else { return }
306309
let pickerGone = ContinuousClock.now + .milliseconds(600)
@@ -444,7 +447,15 @@ struct CatchView: View {
444447
}
445448

446449
@ViewBuilder private var controlTip: some View {
447-
if !controlTipSeen, catches >= CatchControlTip.afterCatches {
450+
if !rapidTipSeen, catches > 0 {
451+
// Only for people who had TABOR before rapid fire: the intro shows it to the rest.
452+
RapidTipCard {
453+
showRapidTip = true
454+
rapidTipSeen = true
455+
} dismiss: {
456+
withAnimation(.snappy) { rapidTipSeen = true }
457+
}
458+
} else if !controlTipSeen, catches >= CatchControlTip.afterCatches {
448459
CatchControlTipCard {
449460
showControlHowTo = true
450461
controlTipSeen = true

‎Tabor/Screens/OnboardingView.swift‎

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ struct OnboardingView: View {
1111
@State private var askedLocation = false
1212
private let location = LocationService.shared
1313

14-
enum Page: Int, CaseIterable { case welcome, book, rarity, camera, buttons, hunt, specials, wraps }
14+
enum Page: Int, CaseIterable { case welcome, book, rarity, camera, buttons, rapid, hunt, specials, wraps }
1515

1616
var body: some View {
1717
VStack(spacing: 0) {
@@ -34,12 +34,17 @@ struct OnboardingView: View {
3434
RarityPage().tag(Page.rarity)
3535
CameraPage().tag(Page.camera)
3636
ButtonsPage().tag(Page.buttons)
37+
RapidPage(active: page == .rapid).tag(Page.rapid)
3738
HuntPage().tag(Page.hunt)
3839
SpecialsPage(active: page == .specials).tag(Page.specials)
3940
WrapsPage(active: page == .wraps).tag(Page.wraps)
4041
}
4142
.tabViewStyle(.page(indexDisplayMode: .never))
42-
.onChange(of: page) { Haptics.shared.tick() }
43+
.onChange(of: page) {
44+
Haptics.shared.tick()
45+
// Seen here, CATCH needn't bring it up again.
46+
if page == .rapid { UserDefaults.standard.set(true, forKey: RapidTip.seenKey) }
47+
}
4348

4449
dots
4550
.padding(.bottom, 22)
@@ -101,7 +106,7 @@ struct OnboardingView: View {
101106
/// The permission this page is about hasn't been asked for yet.
102107
private var needsPermission: Bool {
103108
switch page {
104-
case .welcome, .book, .rarity, .specials, .wraps, .buttons: false
109+
case .welcome, .book, .rarity, .specials, .wraps, .buttons, .rapid: false
105110
case .camera: AVCaptureDevice.authorizationStatus(for: .video) == .notDetermined
106111
case .hunt: location.authorization == .notDetermined
107112
}
@@ -110,7 +115,7 @@ struct OnboardingView: View {
110115
private var primaryLabel: String {
111116
switch page {
112117
case .welcome: String(localized: "LET'S GO")
113-
case .book, .rarity, .buttons, .specials: String(localized: "NEXT")
118+
case .book, .rarity, .buttons, .rapid, .specials: String(localized: "NEXT")
114119
case .camera: needsPermission ? String(localized: "ALLOW CAMERA") : String(localized: "NEXT")
115120
case .hunt: needsPermission ? String(localized: "ALLOW LOCATION") : String(localized: "NEXT")
116121
case .wraps: String(localized: "START CATCHING")
@@ -119,7 +124,7 @@ struct OnboardingView: View {
119124

120125
private func primary() {
121126
switch page {
122-
case .welcome, .book, .rarity, .buttons, .specials:
127+
case .welcome, .book, .rarity, .buttons, .rapid, .specials:
123128
advance()
124129
case .camera:
125130
guard needsPermission else { return advance() }
@@ -676,6 +681,17 @@ private struct ButtonsPage: View {
676681
}
677682
}
678683

684+
/// Rapid fire and the Lock Screen camera, in the intro.
685+
private struct RapidPage: View {
686+
let active: Bool
687+
688+
var body: some View {
689+
PageLayout(kicker: RapidTip.kicker, title: RapidTip.title, text: RapidTip.text) {
690+
RapidArt(active: active)
691+
}
692+
}
693+
}
694+
679695
/// Camera-style corner brackets.
680696
private struct Brackets: Shape {
681697
func path(in r: CGRect) -> Path {

0 commit comments

Comments
 (0)