Slot machine animation

This commit is contained in:
Ilya Laktyushin 2020-08-16 13:20:06 +03:00
parent a1fb9f3f61
commit 7e643321cf
30 changed files with 1800 additions and 1537 deletions

View File

@ -730,6 +730,7 @@ plist_fragment(
<string>onionhttp</string> <string>onionhttp</string>
<string>ucbrowser</string> <string>ucbrowser</string>
<string>dolphin</string> <string>dolphin</string>
<string>instagram-stories</string>
</array> </array>
<key>LSRequiresIPhoneOS</key> <key>LSRequiresIPhoneOS</key>
<true/> <true/>

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -5735,3 +5735,5 @@ Any member of this group will be able to see messages in the channel.";
"Call.AudioRouteMute" = "Mute Yourself"; "Call.AudioRouteMute" = "Mute Yourself";
"AccessDenied.VideoCallCamera" = "Telegram needs access to your camera to make video calls.\n\nPlease go to Settings > Privacy > Camera and set Telegram to ON."; "AccessDenied.VideoCallCamera" = "Telegram needs access to your camera to make video calls.\n\nPlease go to Settings > Privacy > Camera and set Telegram to ON.";
"Call.AccountIsLoggedOnCurrentDevice" = "Sorry, you can't call %@ because that account is logged in to Telegram on the device you're using for the call.";

View File

@ -277,7 +277,12 @@ public final class DeviceAccess {
if status == PGCameraAuthorizationStatusRestricted { if status == PGCameraAuthorizationStatusRestricted {
text = presentationData.strings.AccessDenied_CameraRestricted text = presentationData.strings.AccessDenied_CameraRestricted
} else { } else {
switch cameraSubject {
case .video:
text = presentationData.strings.AccessDenied_Camera text = presentationData.strings.AccessDenied_Camera
case .videoCall:
text = presentationData.strings.AccessDenied_VideoCallCamera
}
} }
completion(false) completion(false)
present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: presentationData.strings.AccessDenied_Title, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_NotNow, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.AccessDenied_Settings, action: { present(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: presentationData.strings.AccessDenied_Title, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_NotNow, action: {}), TextAlertAction(type: .genericAction, title: presentationData.strings.AccessDenied_Settings, action: {

View File

@ -1523,9 +1523,9 @@ final class CallControllerNode: ViewControllerTracingNode, CallControllerNodePro
} else if let _ = self.keyPreviewNode { } else if let _ = self.keyPreviewNode {
self.backPressed() self.backPressed()
} else { } else {
if let expandedVideoNode = self.expandedVideoNode, let minimizedVideoNode = self.minimizedVideoNode { if self.hasVideoNodes {
let point = recognizer.location(in: recognizer.view) let point = recognizer.location(in: recognizer.view)
if minimizedVideoNode.frame.contains(point) { if let expandedVideoNode = self.expandedVideoNode, let minimizedVideoNode = self.minimizedVideoNode, minimizedVideoNode.frame.contains(point) {
if !self.areUserActionsDisabledNow() { if !self.areUserActionsDisabledNow() {
let copyView = minimizedVideoNode.view.snapshotView(afterScreenUpdates: false) let copyView = minimizedVideoNode.view.snapshotView(afterScreenUpdates: false)
copyView?.frame = minimizedVideoNode.frame copyView?.frame = minimizedVideoNode.frame

View File

@ -45,7 +45,7 @@ private func generateEmptyButtonImage(icon: UIImage?, strokeColor: UIColor?, fil
context.fill(imageRect) context.fill(imageRect)
} else { } else {
context.setBlendMode(.normal) context.setBlendMode(.normal)
context.draw(icon.cgImage!, in: imageRect) context.draw(generateTintedImage(image: icon, color: .white)!.cgImage!, in: imageRect)
} }
} }
}) })

View File

@ -228,6 +228,10 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
} }
if let telegramDice = self.telegramDice { if let telegramDice = self.telegramDice {
if telegramDice.emoji == "🎲" {
let animationNode = SlotMachineAnimationNode(context: item.context)
self.animationNode = animationNode
} else {
let animationNode = ManagedDiceAnimationNode(context: item.context, emoji: telegramDice.emoji.strippedEmoji) let animationNode = ManagedDiceAnimationNode(context: item.context, emoji: telegramDice.emoji.strippedEmoji)
if !item.message.effectivelyIncoming(item.context.account.peerId) { if !item.message.effectivelyIncoming(item.context.account.peerId) {
animationNode.success = { [weak self] in animationNode.success = { [weak self] in
@ -237,6 +241,7 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
} }
} }
self.animationNode = animationNode self.animationNode = animationNode
}
} else { } else {
let animationNode: AnimatedStickerNode let animationNode: AnimatedStickerNode
if let (node, parentNode, listNode, greetingCompletion) = item.controllerInteraction.greetingStickerNode(), let greetingStickerNode = node as? AnimatedStickerNode { if let (node, parentNode, listNode, greetingCompletion) = item.controllerInteraction.greetingStickerNode(), let greetingStickerNode = node as? AnimatedStickerNode {
@ -290,7 +295,13 @@ class ChatMessageAnimatedStickerItemNode: ChatMessageItemView {
self.setupNode(item: item) self.setupNode(item: item)
if let telegramDice = self.telegramDice, let diceNode = self.animationNode as? ManagedDiceAnimationNode { if let telegramDice = self.telegramDice, let diceNode = self.animationNode as? SlotMachineAnimationNode {
if let value = telegramDice.value {
diceNode.setState(value == 0 ? .rolling : .value(value, true))
} else {
diceNode.setState(.rolling)
}
} else if let telegramDice = self.telegramDice, let diceNode = self.animationNode as? ManagedDiceAnimationNode {
if let value = telegramDice.value { if let value = telegramDice.value {
diceNode.setState(value == 0 ? .rolling : .value(value, true)) diceNode.setState(value == 0 ? .rolling : .value(value, true))
} else { } else {

View File

@ -0,0 +1,241 @@
import Foundation
import Display
import AsyncDisplayKit
import Postbox
import SyncCore
import TelegramCore
import SwiftSignalKit
import AccountContext
import StickerResources
import ManagedAnimationNode
enum ReelValue {
case rolling
case bar
case berries
case lemon
case seven
case sevenWin
}
private func leftReelAnimationItem(value: ReelValue, immediate: Bool = false) -> ManagedAnimationItem {
let frames: ManagedAnimationFrameRange? = immediate ? .still(.end) : nil
switch value {
case .rolling:
return ManagedAnimationItem(source: .local("Slot_L_Spinning"), loop: true)
case .bar:
return ManagedAnimationItem(source: .local("Slot_L_Bar"), frames: frames, loop: false)
case .berries:
return ManagedAnimationItem(source: .local("Slot_L_Berries"), frames: frames, loop: false)
case .lemon:
return ManagedAnimationItem(source: .local("Slot_L_Lemon"), frames: frames, loop: false)
case .seven:
return ManagedAnimationItem(source: .local("Slot_L_7"), frames: frames, loop: false)
case .sevenWin:
return ManagedAnimationItem(source: .local("Slot_L_7_Win"), frames: frames, loop: false)
}
}
private func centerReelAnimationItem(value: ReelValue, immediate: Bool = false) -> ManagedAnimationItem {
let frames: ManagedAnimationFrameRange? = immediate ? .still(.end) : nil
switch value {
case .rolling:
return ManagedAnimationItem(source: .local("Slot_M_Spinning"), frames: frames, loop: true)
case .bar:
return ManagedAnimationItem(source: .local("Slot_M_Bar"), frames: frames, loop: false)
case .berries:
return ManagedAnimationItem(source: .local("Slot_M_Berries"), frames: frames, loop: false)
case .lemon:
return ManagedAnimationItem(source: .local("Slot_M_Lemon"), frames: frames, loop: false)
case .seven:
return ManagedAnimationItem(source: .local("Slot_M_7"), frames: frames, loop: false)
case .sevenWin:
return ManagedAnimationItem(source: .local("Slot_M_7_Win"), frames: frames, loop: false)
}
}
private func rightReelAnimationItem(value: ReelValue, immediate: Bool = false) -> ManagedAnimationItem {
let frames: ManagedAnimationFrameRange? = immediate ? .still(.end) : nil
switch value {
case .rolling:
return ManagedAnimationItem(source: .local("Slot_R_Spinning"), frames: frames, loop: true)
case .bar:
return ManagedAnimationItem(source: .local("Slot_R_Bar"), frames: frames, loop: false)
case .berries:
return ManagedAnimationItem(source: .local("Slot_R_Berries"), frames: frames, loop: false)
case .lemon:
return ManagedAnimationItem(source: .local("Slot_R_Lemon"), frames: frames, loop: false)
case .seven:
return ManagedAnimationItem(source: .local("Slot_R_7"), frames: frames, loop: false)
case .sevenWin:
return ManagedAnimationItem(source: .local("Slot_R_7_Win"), frames: frames, loop: false)
}
}
final class SlotMachineAnimationNode: ASDisplayNode, GenericAnimatedStickerNode {
private let context: AccountContext
private let backNode: ManagedAnimationNode
private let leftReelNode: ManagedAnimationNode
private let centerReelNode: ManagedAnimationNode
private let rightReelNode: ManagedAnimationNode
private let frontNode: ManagedAnimationNode
private var diceState: ManagedDiceAnimationState? = nil
private let disposables = DisposableSet()
init(context: AccountContext) {
self.context = context
let size = CGSize(width: 184.0, height: 184.0)
self.backNode = ManagedAnimationNode(size: size)
self.leftReelNode = ManagedAnimationNode(size: size)
self.centerReelNode = ManagedAnimationNode(size: size)
self.rightReelNode = ManagedAnimationNode(size: size)
self.frontNode = ManagedAnimationNode(size: size)
super.init()
self.addSubnode(self.backNode)
self.addSubnode(self.leftReelNode)
self.addSubnode(self.centerReelNode)
self.addSubnode(self.rightReelNode)
self.addSubnode(self.frontNode)
}
deinit {
self.disposables.dispose()
}
override func layout() {
super.layout()
self.backNode.frame = self.bounds
self.leftReelNode.frame = self.bounds
self.centerReelNode.frame = self.bounds
self.rightReelNode.frame = self.bounds
self.frontNode.frame = self.bounds
}
func setState(_ diceState: ManagedDiceAnimationState) {
let previousState = self.diceState
self.diceState = diceState
if let previousState = previousState {
switch previousState {
case .rolling:
switch diceState {
case let .value(value, _):
let l: ReelValue
let c: ReelValue
let r: ReelValue
switch value {
case 1:
l = .seven
c = .berries
r = .bar
case 2:
l = .berries
c = .berries
r = .bar
case 3:
l = .seven
c = .berries
r = .seven
case 4:
l = .bar
c = .lemon
r = .seven
case 5:
l = .berries
c = .berries
r = .berries
case 6:
l = .sevenWin
c = .sevenWin
r = .sevenWin
default:
l = .sevenWin
c = .sevenWin
r = .sevenWin
}
if value == 6 {
Queue.mainQueue().after(1.5) {
self.backNode.trackTo(item: ManagedAnimationItem(source: .local("Slot_Back_Win"), loop: false))
}
} else {
self.backNode.trackTo(item: ManagedAnimationItem(source: .local("Slot_Back_Idle"), loop: false))
}
self.leftReelNode.trackTo(item: leftReelAnimationItem(value: l))
self.centerReelNode.trackTo(item: centerReelAnimationItem(value: c))
self.rightReelNode.trackTo(item: rightReelAnimationItem(value: r))
self.frontNode.trackTo(item: ManagedAnimationItem(source: .local("Slot_Front_Pull"), frames: .still(.end), loop: false))
case .rolling:
break
}
case .value:
switch diceState {
case .rolling:
self.backNode.trackTo(item: ManagedAnimationItem(source: .local("Slot_Back_Idle"), loop: false))
self.leftReelNode.trackTo(item: leftReelAnimationItem(value: .rolling))
self.centerReelNode.trackTo(item: centerReelAnimationItem(value: .rolling))
self.rightReelNode.trackTo(item: rightReelAnimationItem(value: .rolling))
self.frontNode.trackTo(item: ManagedAnimationItem(source: .local("Slot_Front_Pull"), loop: false))
case .value:
break
}
}
} else {
switch diceState {
case let .value(value, immediate):
self.backNode.trackTo(item: ManagedAnimationItem(source: .local("Slot_Back_Idle"), loop: false))
let l: ReelValue
let c: ReelValue
let r: ReelValue
switch value {
case 1:
l = .seven
c = .berries
r = .bar
case 2:
l = .berries
c = .berries
r = .bar
case 3:
l = .seven
c = .berries
r = .seven
case 4:
l = .bar
c = .lemon
r = .seven
case 5:
l = .berries
c = .berries
r = .berries
case 6:
l = .sevenWin
c = .sevenWin
r = .sevenWin
default:
l = .sevenWin
c = .sevenWin
r = .sevenWin
}
self.leftReelNode.trackTo(item: leftReelAnimationItem(value: l, immediate: immediate))
self.centerReelNode.trackTo(item: centerReelAnimationItem(value: c, immediate: immediate))
self.rightReelNode.trackTo(item: rightReelAnimationItem(value: r, immediate: immediate))
let frames: ManagedAnimationFrameRange? = immediate ? .still(.end) : nil
self.frontNode.trackTo(item: ManagedAnimationItem(source: .local("Slot_Front_Pull"), frames: frames, loop: false))
case .rolling:
self.backNode.trackTo(item: ManagedAnimationItem(source: .local("Slot_Back_Idle"), loop: false))
self.leftReelNode.trackTo(item: leftReelAnimationItem(value: .rolling))
self.centerReelNode.trackTo(item: centerReelAnimationItem(value: .rolling))
self.rightReelNode.trackTo(item: rightReelAnimationItem(value: .rolling))
self.frontNode.trackTo(item: ManagedAnimationItem(source: .local("Slot_Front_Pull"), loop: false))
}
}
}
}