| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303 |
- //
- // MOLinePKViewModel.swift
- // MiMoLive
- //
- // Created by OneeChan on 2025/9/23.
- //
- import Foundation
- extension MOLiveManager {
- static var curPkViewModel: MOLinePKViewModel? {
- curLive?.lineViewModel?.curPKViewModel
- }
- }
- @objc
- protocol MOLinePKViewModelDelegate {
- @objc optional func onLinePkStateChanged(viewModel: MOLinePKViewModel, state: LinePKState)
- @objc optional func onLinePkCampInfoChanged(viewModel: MOLinePKViewModel)
- @objc optional func onLinePkResult(viewModel: MOLinePKViewModel, result: LinePkResult)
- @objc optional func onLinePkSurrender(viewModel: MOLinePKViewModel)
- }
- @objcMembers
- class MOLinePKViewModel: NSObject {
- let pkMatchId: String
-
- private var pkUpdateTime: TimeInterval = 0
- private var pkRemain: Int = 0
- var curPkRemain: Int {
- max(pkRemain - Int((Date().timeIntervalSince1970 - pkUpdateTime)), 0)
- }
-
- private var pkPunishDuration: Int = 0
- var curPunishRemain: Int {
- if curState == .punishing {
- max(pkPunishDuration - Int((Date().timeIntervalSince1970 - pkUpdateTime)), 0)
- } else {
- 0
- }
- }
-
- private var camps: [MOLinePkCampInfo] = []
- private var campMap: [String: MOLinePkCampInfo] = [:] // camps 的 map 化
- var curOwnerCampInfo: MOLinePkCampInfo? {
- if let lineOwnerInfo = MOLiveManager.curLive?.lineViewModel?.curOwnerInfo,
- !lineOwnerInfo.pkCampCode.isEmpty,
- let info = campMap[lineOwnerInfo.pkCampCode] {
- return info
- }
- return camps.first(where: { $0.lastIsMyCamp })
- }
- var curPeerCampInfo: MOLinePkCampInfo? {
- if let lineOwnerInfo = MOLiveManager.curLive?.lineViewModel?.curOwnerInfo,
- !lineOwnerInfo.pkCampCode.isEmpty,
- let info = camps.first(
- where: { $0.campCode != lineOwnerInfo.pkCampCode
- }) {
- return info
- }
-
- return camps.first { !$0.lastIsMyCamp }
- }
-
- private(set) var curState: LinePKState = .none {
- didSet {
- if oldValue != curState {
- notifyLinePkStateChanged()
- if oldValue == .pking {
- notifyLinePkResult()
- }
- if curState == .punishing {
- startPunishTimeCountDown()
- } else if curState == .draw {
- startDrawTimeCountDown()
- }
- }
- }
- }
- private var delayTaskUUID: String?
-
- init(pkMatchId: String) {
- self.pkMatchId = pkMatchId
- super.init()
- }
-
- deinit {
- clear()
- }
-
- func clear() {
- guard curState != .none else { return }
- curState = .none
- MODelayTask.cancel(key: delayTaskUUID)
- }
-
- // roomInfo
- func updateInfo(roomInfo: MOLivePkLinkRoomInfoVo) {
- pkUpdateTime = roomInfo.now
- pkPunishDuration = roomInfo.pkPunishDuration
-
- MODelayTask.cancel(key: delayTaskUUID)
-
- if !roomInfo.camps.isEmpty {
- let campCodes = roomInfo.camps.map { $0.campCode }
- self.camps.removeAll { !campCodes.contains($0.campCode) } // 移除已不在列表的阵营信息
- roomInfo.camps.forEach { info in
- if let oldInfo = self.camps.first(where: { $0.campCode == info.campCode }) {
- oldInfo.updateBy(info: info)
- } else {
- let newCamp = MOLinePkCampInfo()
- newCamp.updateBy(info: info)
- self.camps.append(newCamp)
- }
- }
- campMap = self.camps.reduce(into: [String: MOLinePkCampInfo](), { partialResult, info in
- partialResult[info.campCode] = info
- })
- } else if let lastPkVo = roomInfo.lastPkVo {
- let campCodes = lastPkVo.camps.map { $0.campCode }
- self.camps.removeAll { !campCodes.contains($0.campCode) } // 移除已不在列表的阵营信息
- lastPkVo.camps.forEach { info in
- if let oldInfo = self.camps.first(where: { $0.campCode == info.campCode }) {
- oldInfo.updateBy(lastInfo: info)
- } else {
- let newCamp = MOLinePkCampInfo()
- newCamp.updateBy(lastInfo: info)
- self.camps.append(newCamp)
- }
- }
- campMap = self.camps.reduce(into: [String: MOLinePkCampInfo](), { partialResult, info in
- partialResult[info.campCode] = info
- })
- }
- notifyLinePkCampInfoChanged()
-
- let oldState = self.curState
- pkRemain = (roomInfo.pkEndTime - roomInfo.serviceTime) / 1000
- if curPkRemain > 0 {
- curState = .pking
- startPkTimeCountDown()
- } else if roomInfo.pkPunishDuration > 0 {
- curState = .punishing
- startPunishTimeCountDown()
- } else if curOwnerCampInfo?.pkStatus == .draw {
- curState = .draw
- startDrawTimeCountDown()
- } else {
- // 不在 PK 状态
- curState = .none
- return
- }
-
- if oldState != curState,
- roomInfo.lastPkVo != nil {
- notifyLinePkResult()
- }
- }
-
- // 38 推送
- func updateInfo(statusInfo: MORtmPkV2Status) {
- pkUpdateTime = statusInfo.now
- pkPunishDuration = statusInfo.pkPunishDuration
-
- MODelayTask.cancel(key: delayTaskUUID)
-
- let campCodes = statusInfo.pkCampInfos.map { $0.campCode }
-
- camps.removeAll { !campCodes.contains($0.campCode) } // 移除已不在列表的阵营信息
- statusInfo.pkCampInfos.forEach { info in
- if let oldInfo = camps.first(where: { $0.campCode == info.campCode }) {
- oldInfo.updateBy(linkInfo: info)
- } else {
- let newCamp = MOLinePkCampInfo()
- newCamp.updateBy(linkInfo: info)
- camps.append(newCamp)
- }
- }
- campMap = camps.reduce(into: [String: MOLinePkCampInfo](), { partialResult, info in
- partialResult[info.campCode] = info
- })
- notifyLinePkCampInfoChanged()
-
- if statusInfo.pkStatus == 1 {
- curState = .pking
- startPkTimeCountDown()
- } else if statusInfo.pkPunishDuration > 0 {
- curState = .punishing
- startPunishTimeCountDown()
- } else if curOwnerCampInfo?.pkStatus == .draw {
- curState = .draw
- startDrawTimeCountDown()
- } else {
- curState = .none
- }
- }
-
- static func loadContributeList(next: String, _ campCode: String, _ handler: @escaping (MOLivePkContributeVO?, Bool) -> Void) {
- MOHttpManager.shared().getLinePkContributeList(campCode, next, 50) { list, error in
- if error != nil {
- showNetError(err: error)
- return
- }
- handler(list, list?.list.count ?? 0 >= 50)
- }
- }
-
- func surrender(_ handler: @escaping (Bool) -> Void) {
- guard let lineViewModel = MOLiveManager.curLineViewModel else {
- handler(false)
- return
- }
- MOHttpManager.shared().surrenderPk(lineViewModel.curLineRoomId) { [weak self] error in
- guard error == nil else {
- showNetError(err: error)
- handler(false)
- return
- }
- handler(true)
-
- guard let self else { return }
- self.notifyLinePkSurrender()
- }
- }
-
- func handleTopUsersUpdate(_ info: MORtmPkV2StatusExpand) {
- info.camps.forEach {
- let camp = campMap[$0.campCode]
- camp?.updateBy(expandInfo: $0)
- }
-
- notifyLinePkCampInfoChanged()
- }
-
- private func settlePk() {
- guard let lineRoomId = MOLiveManager.curLineViewModel?.curLineRoomId else {
- return
- }
- MOHttpManager.shared().settlePk(lineRoomId, pkMatchId) { [weak self] info, error in
- if error != nil {
- showNetError(err: error)
- return
- }
- guard let self else { return }
- guard let info else {
- // 结算结果异常,直接结束 PK
- curState = .none
- return
- }
- self.updateInfo(statusInfo: info)
- }
- }
- }
- extension MOLinePKViewModel {
- private func startPkTimeCountDown() {
- guard MOLiveManager.curLive?.isOwner == true else { return }
-
- MODelayTask.cancel(key: delayTaskUUID)
- delayTaskUUID = MODelayTask.perform(delay: TimeInterval(curPkRemain)) { [weak self] in
- guard let self else { return }
- MODelayTask.perform(delay: 1) { [weak self] in
- guard let self else { return }
- self.settlePk()
- }
- }
- }
-
- private func startPunishTimeCountDown() {
- MODelayTask.cancel(key: delayTaskUUID)
- delayTaskUUID = MODelayTask.perform(delay: TimeInterval(pkPunishDuration)) { [weak self] in
- guard let self else { return }
- self.curState = .none
- }
- }
-
- private func startDrawTimeCountDown() {
- MODelayTask.cancel(key: delayTaskUUID)
- delayTaskUUID = MODelayTask.perform(delay: 10) { [weak self] in
- guard let self else { return }
- self.curState = .none
- }
- }
- }
- extension MOLinePKViewModel {
- private func notifyLinePkStateChanged() {
- MOEventDeliver.notifyEvent { $0.onLinePkStateChanged?(viewModel: self, state: curState) }
- }
-
- private func notifyLinePkCampInfoChanged() {
- MOEventDeliver.notifyEvent { $0.onLinePkCampInfoChanged?(viewModel: self) }
- }
-
- private func notifyLinePkResult() {
- guard let result = curOwnerCampInfo?.pkStatus else { return }
- MOEventDeliver.notifyEvent { $0.onLinePkResult?(viewModel: self, result: result) }
- }
-
- private func notifyLinePkSurrender() {
- MOEventDeliver.notifyEvent { $0.onLinePkSurrender?(viewModel: self) }
- }
- }
|