diff --git a/app/src/module_game/java/com/chwl/app/game/core/BaseGameActivity.kt b/app/src/module_game/java/com/chwl/app/game/core/BaseGameActivity.kt index 32796378c..bc650590c 100644 --- a/app/src/module_game/java/com/chwl/app/game/core/BaseGameActivity.kt +++ b/app/src/module_game/java/com/chwl/app/game/core/BaseGameActivity.kt @@ -12,12 +12,19 @@ import androidx.viewbinding.ViewBinding import com.chwl.app.R import com.chwl.app.base.BaseViewBindingActivity import com.chwl.app.common.widget.dialog.DialogManager -import com.chwl.app.game.core.engine.GameEngineViewModel +import com.chwl.app.game.core.engine.GameEngineAbility import com.chwl.app.game.data.bean.GameInfoUiState +import com.chwl.app.game.data.bean.GameResultBean import com.chwl.app.game.ui.game.GameViewModel +import com.chwl.app.game.ui.result.GameResultDialog +import com.chwl.app.ui.pay.ChargeActivity import com.chwl.core.support.room.RoomContext import com.chwl.core.support.room.RoomView import com.chwl.core.support.room.RoomWidget +import com.chwl.core.utils.net.BalanceNotEnoughExeption +import com.chwl.core.utils.net.ServerException +import com.chwl.library.utils.ResUtil +import com.chwl.library.utils.SingleToastUtil import com.netease.nim.uikit.StatusBarUtil import kotlinx.coroutines.flow.collectLatest import kotlinx.coroutines.flow.zip @@ -26,25 +33,29 @@ import kotlinx.coroutines.launch abstract class BaseGameActivity : BaseViewBindingActivity(), RoomView { protected val viewModel: GameViewModel by viewModels() - protected val gameEngineViewModel: GameEngineViewModel by viewModels() + protected var widgets: HashMap = HashMap() protected val gameContext get() = viewModel.gameContextLiveData.value protected val stateAbility get() = gameContext?.findAbility(GameStateAbility::class.java.simpleName) + protected val gameEngineAbility + get() = gameContext?.findAbility( + GameEngineAbility::class.java.simpleName + ) private var gameInfoDialogManager: DialogManager? = null protected var loadingDialogManager: DialogManager? = null + protected var matchFailedDialogManager: DialogManager? = null + private var resultDialog: GameResultDialog? = null override fun init() { initView() initEvent() initObserver() initWidgets() - gameEngineViewModel.init(lifecycle) } protected open fun initView() { - gameInfoDialogManager = DialogManager(this) loadingDialogManager = DialogManager(this) } @@ -52,23 +63,32 @@ abstract class BaseGameActivity : BaseViewBindingActivity(), } protected open fun initObserver() { + lifecycleScope.launch { + viewModel.closeRoomFlow.collectLatest { + loadingDialogManager?.dismissDialog() + finish() + } + } viewModel.gameContextLiveData.observe(this) { onGameContext(it) } - gameEngineViewModel.gameViewLiveData.observe(this) { - onGameViewChanged(it) - } } protected open fun initWidgets() { } protected open fun onGameContext(gameContext: GameContext) { - gameEngineViewModel.setGameContext(gameContext) + gameContext.onViewAttach(this) stateAbility?.let { observeGameInfoUiState(it) - updateGameEngine(it) + + lifecycleScope.launch { + it.matchFailedFlow.collectLatest { + onMatchFailed() + } + } } + initGameEngine(gameContext) } private fun observeGameInfoUiState(stateAbility: GameStateAbility) { @@ -76,17 +96,26 @@ abstract class BaseGameActivity : BaseViewBindingActivity(), stateAbility.gameInfoUiStateFlow.collectLatest { when (it) { is GameInfoUiState.Loading -> { + if (gameInfoDialogManager == null) { + gameInfoDialogManager = DialogManager(context) + } gameInfoDialogManager?.showProgressDialog(context) } is GameInfoUiState.Success -> { + dialogManager?.dismissDialog() + resultDialog?.dismissAllowingStateLoss() gameInfoDialogManager?.dismissDialog() } is GameInfoUiState.Failed -> { gameInfoDialogManager?.dismissDialog() - it.throwable.message?.let { msg -> - toast(msg) + if (it.throwable is ServerException && it.throwable.code == BalanceNotEnoughExeption.code) { + onBalanceNotEnough() + } else { + it.throwable.message?.let { msg -> + toast(msg) + } } } @@ -100,8 +129,11 @@ abstract class BaseGameActivity : BaseViewBindingActivity(), } } - private fun updateGameEngine(stateAbility: GameStateAbility) { + protected open fun initGameEngine(gameContext: GameContext) { + val gameEngineAbility = + gameContext.findAbility(GameEngineAbility::class.java.simpleName) lifecycleScope.launch { + val stateAbility = stateAbility ?: return@launch stateAbility.gameIdFlow.zip(stateAbility.roomIdFlow) { gameId, roomId -> if (gameId?.toLongOrNull() != null && roomId != null) { Pair(gameId.toLong(), roomId) @@ -110,7 +142,7 @@ abstract class BaseGameActivity : BaseViewBindingActivity(), } }.collectLatest { if (it != null) { - gameEngineViewModel.loadGame( + gameEngineAbility?.loadGame( this@BaseGameActivity, it.second.toString(), it.first @@ -118,6 +150,71 @@ abstract class BaseGameActivity : BaseViewBindingActivity(), } } } + lifecycleScope.launch { + gameEngineAbility?.gameResultFlow?.collectLatest { + onGameResult(it) + } + } + lifecycleScope.launch { + gameEngineAbility?.gameViewFlow?.collectLatest { + onGameViewChanged(it) + } + } + } + + protected open fun onGameResult(list: List?) { + if (list == null) { + return + } + resultDialog = GameResultDialog(list, onClose = { + closeRoom() + }, onRestart = { + restart() + }) + resultDialog?.show(supportFragmentManager, "GAME_RESULT") + } + + private fun onMatchFailed() { + if (matchFailedDialogManager == null) { + matchFailedDialogManager = DialogManager(context) + } + matchFailedDialogManager?.showOkCancelDialog(null, + getString(R.string.game_match_failed), + getString(R.string.game_rematch), + getString(R.string.exit_text), false, false, object : + DialogManager.OkCancelDialogListener { + override fun onOk() { + restart() + } + + override fun onCancel() { + super.onCancel() + closeRoom() + } + }) + } + + protected open fun onBalanceNotEnough() { + dialogManager?.showOkCancelDialog( + ResUtil.getString(R.string.star_send_gift_balance), + ResUtil.getString(R.string.treasure_to_charge) + ) { + ChargeActivity.start(context) + } + } + + private fun closeRoom() { + loadingDialogManager?.showProgressDialog(this@BaseGameActivity) + viewModel.closeRoom() + } + + private fun restart() { + val intent = viewModel.getGameIntent() + if (intent == null) { + toast(R.string.data_error) + return + } + stateAbility?.restart(intent) } protected open fun onGameViewChanged(view: View?) { @@ -140,7 +237,7 @@ abstract class BaseGameActivity : BaseViewBindingActivity(), * 注册组件 */ protected open fun registerWidget(name: String, widget: RoomWidget) { - widgets.put(name, widget) + widgets[name] = widget widget.onStart(this) } @@ -193,5 +290,9 @@ abstract class BaseGameActivity : BaseViewBindingActivity(), gameInfoDialogManager = null loadingDialogManager?.dismissDialog() loadingDialogManager = null + resultDialog?.dismissAllowingStateLoss() + resultDialog = null + matchFailedDialogManager?.dismissDialog() + matchFailedDialogManager = null } } \ No newline at end of file diff --git a/app/src/module_game/java/com/chwl/app/game/core/GameContext.kt b/app/src/module_game/java/com/chwl/app/game/core/GameContext.kt index db5b678eb..f63b6daf2 100644 --- a/app/src/module_game/java/com/chwl/app/game/core/GameContext.kt +++ b/app/src/module_game/java/com/chwl/app/game/core/GameContext.kt @@ -1,5 +1,7 @@ package com.chwl.app.game.core +import com.chwl.app.game.core.engine.GameEngineAbility +import com.chwl.core.bean.game.GameRoomInfo import com.chwl.core.support.room.RoomAbility import com.chwl.core.support.room.RoomContext @@ -7,13 +9,18 @@ class GameContext(roomId: Long) : RoomContext(roomId) { override fun loadAbility(list: MutableMap) { super.loadAbility(list) - list.put(GameStateAbility::class.java.simpleName, GameStateAbility()) - list.put(GameIMEngineAbility::class.java.simpleName, GameIMEngineAbility()) - list.put(GameQueueAbility::class.java.simpleName, GameQueueAbility()) - list.put(GameMessageAbility::class.java.simpleName, GameMessageAbility()) + list[GameStateAbility::class.java.simpleName] = GameStateAbility() + list[GameEngineAbility::class.java.simpleName] = GameEngineAbility() + list[GameIMEngineAbility::class.java.simpleName] = GameIMEngineAbility() + list[GameQueueAbility::class.java.simpleName] = GameQueueAbility() + list[GameMessageAbility::class.java.simpleName] = GameMessageAbility() } override fun performStart() { super.performStart() } + + fun restart(roomInfo: GameRoomInfo) { + val stateAbility = findAbility(GameStateAbility::class.java.simpleName) + } } \ No newline at end of file diff --git a/app/src/module_game/java/com/chwl/app/game/core/GameQueueAbility.kt b/app/src/module_game/java/com/chwl/app/game/core/GameQueueAbility.kt index 7d7d8c981..1f0f43b92 100644 --- a/app/src/module_game/java/com/chwl/app/game/core/GameQueueAbility.kt +++ b/app/src/module_game/java/com/chwl/app/game/core/GameQueueAbility.kt @@ -16,4 +16,10 @@ class GameQueueAbility : RoomAbility() { suspend fun updateQueue(data: List?) { queueFlow.value = data } + + fun findQueueItem(uid: Long): RoomMicBean? { + return queueFlow.value?.firstOrNull { + it.micUser?.uid == uid + } + } } \ No newline at end of file diff --git a/app/src/module_game/java/com/chwl/app/game/core/GameStateAbility.kt b/app/src/module_game/java/com/chwl/app/game/core/GameStateAbility.kt index d3778781d..543ffe0fa 100644 --- a/app/src/module_game/java/com/chwl/app/game/core/GameStateAbility.kt +++ b/app/src/module_game/java/com/chwl/app/game/core/GameStateAbility.kt @@ -2,6 +2,7 @@ package com.chwl.app.game.core import com.chwl.app.game.data.GameModel2 import com.chwl.app.game.data.bean.GameInfoUiState +import com.chwl.app.game.ui.game.GameIntent import com.chwl.core.bean.game.GameRoomInfo import com.chwl.core.im.custom.bean.CustomAttachment import com.chwl.core.im.custom.bean.GameQueueChangedAttachment @@ -14,12 +15,26 @@ import kotlinx.coroutines.flow.MutableStateFlow class GameStateAbility : RoomAbility(), GameIMEngineAbility.Listener { + companion object { + // 匹配中 + const val STATE_MATCHING = 0 + + // 匹配成功 + const val STATE_MATCH_SUCCESS = 1 + + // 游戏结束 + const val STATE_GAME_END = 2 + + // 匹配失败 + const val STATE_MATCH_FAILED = 3 + } + /** * 游戏状态: 本地定义状态+匹配状态 * 本地定义状态:(NULL:不合法状态) * 匹配状态(服务端定义):(0:匹配中、1:匹配成功、2:游戏结束、3:匹配失败) */ - val gameStateFlow: MutableStateFlow = MutableStateFlow(0) + val gameStateFlow: MutableStateFlow = MutableStateFlow(STATE_MATCHING) val gameIconFlow = MutableStateFlow(null) @@ -64,6 +79,16 @@ class GameStateAbility : RoomAbility(), GameIMEngineAbility.Listener { } } + fun restart(intent: GameIntent) { + gameInfoUiStateFlow.value = GameInfoUiState.Loading + safeLaunch(onError = { + gameInfoUiStateFlow.value = GameInfoUiState.Failed(it, gameInfoFlow.value) + }) { + val roomId = GameModel2.startGame(intent.gameId.toString(), intent.gameMode) + requestRoomInfo() + } + } + private suspend fun syncRoomInfo(info: GameRoomInfo) { roomContext?.roomId = info.chatRoomId ?: 0 gameInfoFlow.value = info diff --git a/app/src/module_game/java/com/chwl/app/game/core/engine/GameEngineAbility.kt b/app/src/module_game/java/com/chwl/app/game/core/engine/GameEngineAbility.kt new file mode 100644 index 000000000..d8c0b17df --- /dev/null +++ b/app/src/module_game/java/com/chwl/app/game/core/engine/GameEngineAbility.kt @@ -0,0 +1,112 @@ +package com.chwl.app.game.core.engine + +import com.chwl.app.game.core.GameQueueAbility +import com.chwl.app.game.core.GameStateAbility +import com.chwl.app.game.data.bean.GameResultBean +import com.chwl.core.sud.state.SudMGPMGState +import com.chwl.core.support.room.RoomContext +import kotlinx.coroutines.flow.MutableSharedFlow +import kotlinx.coroutines.flow.collectLatest +import tech.sud.mgp.core.ISudFSMStateHandle + +class GameEngineAbility : SudEngineAbility() { + val gameResultFlow = MutableSharedFlow?>() + + private val stateAbility get() = roomContext?.findAbility(GameStateAbility::class.java.simpleName) + override fun onStart(context: RoomContext) { + super.onStart(context) + safeLaunch { + stateAbility?.gameStateFlow?.collectLatest { + logD("GameEngineAbility gameStateFlow state:${it}") + when (it) { + GameStateAbility.STATE_MATCH_SUCCESS -> { + autoPlayGame() + } + + GameStateAbility.STATE_MATCHING -> { + tryStopGame() + } + + GameStateAbility.STATE_MATCH_FAILED -> { + tryStopGame() + } + + GameStateAbility.STATE_GAME_END -> { + tryStopGame() + } + } + } + } + } + + private fun tryStopGame() { + } + + private fun autoPlayGame() { + val stateAbility = stateAbility ?: return + logD("autoPlayGame state:${stateAbility.gameStateFlow.value}") + if (stateAbility.gameStateFlow.value != GameStateAbility.STATE_MATCH_SUCCESS) { + return + } + val queueAbility = + roomContext?.findAbility(GameQueueAbility::class.java.simpleName) + ?: return + val queueSize = queueAbility.queueFlow.value?.size ?: 0 + if (queueSize == 0) { + return + } + logD("autoPlayGame queueSize:$queueSize size:${sudFSMMG.sudFSMMGCache.playerReadySet.size}") + if (sudFSMMG.sudFSMMGCache.playerReadySet.size >= queueSize) { + sudFSTAPP.notifyAPPCommonSelfPlaying(true, null, null) + } + } + + private fun autoJoinGame() { + sudFSTAPP.notifyAPPCommonSelfIn(true, -1, true, 1) + sudFSTAPP.notifyAPPCommonSelfReady(true) + } + + override fun onGameStarted() { + super.onGameStarted() + autoJoinGame() + } + + override fun onPlayerMGCommonPlayerReady( + handle: ISudFSMStateHandle?, + userId: String?, + model: SudMGPMGState.MGCommonPlayerReady? + ) { + super.onPlayerMGCommonPlayerReady(handle, userId, model) + autoPlayGame() + } + + override fun onGameMGCommonGameSettle( + handle: ISudFSMStateHandle, + model: SudMGPMGState.MGCommonGameSettle? + ) { + super.onGameMGCommonGameSettle(handle, model) + val list = ArrayList() + val queueAbility = + roomContext?.findAbility(GameQueueAbility::class.java.simpleName) + val stateAbility = + roomContext?.findAbility(GameStateAbility::class.java.simpleName) + val scoresList = stateAbility?.scoresFlow?.value + model?.results?.sortedBy { it.rank }?.forEachIndexed { index, it -> + val uid = it.uid?.toLong() ?: -1 + val queueItem = queueAbility?.findQueueItem(uid) + val scores = scoresList?.getOrNull(index) + val item = GameResultBean().apply { + this.uid = it.uid + this.rank = it.rank + this.score = it.score + this.nick = queueItem?.micUser?.nick + this.avatar = queueItem?.micUser?.avatar + this.coins = scores + } + list.add(item) + } + safeLaunch { + gameResultFlow.emit(list) + } + } +} \ No newline at end of file diff --git a/app/src/module_game/java/com/chwl/app/game/core/engine/GameEngineViewModel.kt b/app/src/module_game/java/com/chwl/app/game/core/engine/SudEngineAbility.kt similarity index 68% rename from app/src/module_game/java/com/chwl/app/game/core/engine/GameEngineViewModel.kt rename to app/src/module_game/java/com/chwl/app/game/core/engine/SudEngineAbility.kt index 4e1199072..d1344aaeb 100644 --- a/app/src/module_game/java/com/chwl/app/game/core/engine/GameEngineViewModel.kt +++ b/app/src/module_game/java/com/chwl/app/game/core/engine/SudEngineAbility.kt @@ -4,17 +4,12 @@ import android.annotation.SuppressLint import android.app.Activity import android.view.View import android.view.ViewTreeObserver -import android.widget.FrameLayout import androidx.lifecycle.Lifecycle -import androidx.lifecycle.LifecycleEventObserver import androidx.lifecycle.LifecycleOwner import androidx.lifecycle.MutableLiveData import com.chwl.app.R import com.chwl.app.avroom.game.AppConfig -import com.chwl.app.base.BaseViewModel -import com.chwl.app.game.core.GameContext import com.chwl.app.game.core.GameQueueAbility -import com.chwl.app.game.core.GameStateAbility import com.chwl.core.auth.AuthModel import com.chwl.core.room.game.GameModel import com.chwl.core.room.game.bean.GameCfg @@ -24,50 +19,36 @@ import com.chwl.core.sud.decorator.SudFSTAPPDecorator import com.chwl.core.sud.model.GameViewInfoModel import com.chwl.core.sud.state.MGStateResponse import com.chwl.core.sud.state.SudMGPMGState +import com.chwl.core.support.room.RoomAbility +import com.chwl.core.support.room.RoomContext import com.chwl.library.language.LanguageHelper import com.chwl.library.utils.SingleToastUtil import com.chwl.library.utils.json.JsonUtils import com.example.lib_utils.log.ILog +import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableStateFlow import tech.sud.mgp.core.ISudFSMStateHandle import tech.sud.mgp.core.ISudListenerInitSDK import tech.sud.mgp.core.SudMGP -import java.lang.IllegalStateException @SuppressLint("StaticFieldLeak") -open class GameEngineViewModel : BaseViewModel(), SudFSMMGListener, ILog, - LifecycleEventObserver { +open class SudEngineAbility : RoomAbility(), SudFSMMGListener, ILog { - val gameViewLiveData = MutableLiveData(null) - private var isRunning = true - private val sudFSMMG = SudFSMMGDecorator().apply { - setSudFSMMGListener(this@GameEngineViewModel) + val gameViewFlow = MutableStateFlow(null) + protected var isRunning = true + protected val sudFSMMG = SudFSMMGDecorator().apply { + setSudFSMMGListener(this@SudEngineAbility) } - private val sudFSTAPP = SudFSTAPPDecorator() - private var lifecycle: Lifecycle? = null + protected val sudFSTAPP = SudFSTAPPDecorator() private var gameViewRect: GameViewInfoModel.GameViewRectModel = GameViewInfoModel.GameViewRectModel() private var roomId: String = "" - private var gameContext: GameContext? = null - - fun init(lifecycle: Lifecycle) { - this.lifecycle = lifecycle - lifecycle.addObserver(this) - } - - fun setGameContext(gameContext: GameContext) { - this.gameContext = gameContext - } - fun loadGame(activity: Activity, roomId: String, gameId: Long) { logD("loadGame() roomId:$roomId gameId:$gameId") if (!this.isRunning) { return } - if (lifecycle == null) { - throw IllegalStateException("未初始化") - } this.roomId = roomId getGameCode({ initSDK(activity, gameId, it) @@ -104,7 +85,7 @@ open class GameEngineViewModel : BaseViewModel(), SudFSMMGListener, ILog, val iSudFSTAPP = SudMGP.loadMG(activity, userId, getRoomId(), code, gameId, getGameLanguage(), sudFSMMG) sudFSTAPP.setISudFSTAPP(iSudFSTAPP) - gameViewLiveData.value = iSudFSTAPP.gameView + gameViewFlow.value = iSudFSTAPP.gameView } fun setGameViewRect(rect: GameViewInfoModel.GameViewRectModel) { @@ -137,57 +118,50 @@ open class GameEngineViewModel : BaseViewModel(), SudFSMMGListener, ILog, handle.success(JsonUtils.toJson(response)) } - override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) { - if (event == Lifecycle.Event.ON_DESTROY) { - onViewDestroy() - } + override fun onStop(context: RoomContext) { + super.onStop(context) + destroyGame() } - private fun getRoomId() = roomId - - private fun getAppId() = AppConfig.APP_ID - - private fun getAppKey() = AppConfig.APP_KEY - - private fun isTestEnv() = AppConfig.isTestEnv - - private fun getGameLanguage(): String { - return when (LanguageHelper.getCurrentLanguageType()) { - LanguageHelper.ZH -> { - "zh-TW" + override fun onStateChanged(source: LifecycleOwner, event: Lifecycle.Event) { + super.onStateChanged(source, event) + when (event) { + Lifecycle.Event.ON_START -> { + sudFSTAPP.startMG() } - LanguageHelper.AR -> { - "ar-SA" + Lifecycle.Event.ON_STOP -> { + sudFSTAPP.stopMG() + } + + Lifecycle.Event.ON_RESUME -> { + sudFSTAPP.playMG() + } + + Lifecycle.Event.ON_PAUSE -> { + sudFSTAPP.pauseMG() + } + + Lifecycle.Event.ON_DESTROY -> { + destroyGame() } else -> { - "en-US" + } } } - override fun onCleared() { - super.onCleared() - onViewDestroy() - } - - private fun onViewDestroy() { - isRunning = false - lifecycle?.removeObserver(this) - lifecycle = null - } - override fun onGameStarted() { - autoJoinGame() } override fun onGameDestroyed() { + } - private fun autoJoinGame() { - sudFSTAPP.notifyAPPCommonSelfIn(true, -1, true, 1) - sudFSTAPP.notifyAPPCommonSelfReady(true) + private fun destroyGame() { + isRunning = false + sudFSTAPP.destroyMG() } override fun onExpireCode(handle: ISudFSMStateHandle, p1: String?) { @@ -200,7 +174,7 @@ open class GameEngineViewModel : BaseViewModel(), SudFSMMGListener, ILog, } override fun onGetGameViewInfo(handle: ISudFSMStateHandle, p1: String?) { - val gameLayout = gameViewLiveData.value + val gameLayout = gameViewFlow.value if (gameLayout == null) { handle.failure("gameLayout is NULL") return @@ -231,22 +205,27 @@ open class GameEngineViewModel : BaseViewModel(), SudFSMMGListener, ILog, sudFSTAPP.notifyAPPCommonSelfIn(true, -1, true, 1) } - override fun onPlayerMGCommonPlayerReady( - handle: ISudFSMStateHandle?, - userId: String?, - model: SudMGPMGState.MGCommonPlayerReady? - ) { - super.onPlayerMGCommonPlayerReady(handle, userId, model) - val queueAbility = - gameContext?.findAbility(GameQueueAbility::class.java.simpleName) - ?: return - val queueSize = queueAbility.queueFlow.value?.size ?: 0 - if (queueSize == 0) { - return - } - logD("onPlayerMGCommonPlayerReady queueSize:$queueSize size:${sudFSMMG.sudFSMMGCache.playerReadySet.size}") - if (sudFSMMG.sudFSMMGCache.playerReadySet.size >= queueSize) { - sudFSTAPP.notifyAPPCommonSelfPlaying(true, null, null) + protected open fun getRoomId() = roomId + + protected open fun getAppId() = AppConfig.APP_ID + + protected open fun getAppKey() = AppConfig.APP_KEY + + protected open fun isTestEnv() = AppConfig.isTestEnv + + protected open fun getGameLanguage(): String { + return when (LanguageHelper.getCurrentLanguageType()) { + LanguageHelper.ZH -> { + "zh-TW" + } + + LanguageHelper.AR -> { + "ar-SA" + } + + else -> { + "en-US" + } } } } \ No newline at end of file diff --git a/app/src/module_game/java/com/chwl/app/game/data/bean/GameResultBean.kt b/app/src/module_game/java/com/chwl/app/game/data/bean/GameResultBean.kt new file mode 100644 index 000000000..3b21bbffa --- /dev/null +++ b/app/src/module_game/java/com/chwl/app/game/data/bean/GameResultBean.kt @@ -0,0 +1,29 @@ +package com.chwl.app.game.data.bean + +import androidx.annotation.Keep +import java.io.Serializable +import java.math.BigDecimal + +@Keep +class GameResultBean : Serializable { + var rank: Int? = null + var uid: String? = null + var avatar: String? = null + var nick: String? = null + var score: Int? = null + var coins: Double? = null + + fun getCoinsStr(): String { + val coinsValue = coins ?: return "0" + try { + val bigDecimal = BigDecimal.valueOf(coinsValue) + val coinsStr = bigDecimal.stripTrailingZeros().toPlainString() + if (coinsValue > 0) { + return "+$coinsStr" + } + return coinsStr + } catch (e: Exception) { + return coins?.toString() ?: "0" + } + } +} \ No newline at end of file diff --git a/app/src/module_game/java/com/chwl/app/game/ui/game/GameActivity.kt b/app/src/module_game/java/com/chwl/app/game/ui/game/GameActivity.kt index bf91bdd7a..fb1cf2948 100644 --- a/app/src/module_game/java/com/chwl/app/game/ui/game/GameActivity.kt +++ b/app/src/module_game/java/com/chwl/app/game/ui/game/GameActivity.kt @@ -47,15 +47,6 @@ class GameActivity : BaseGameActivity(), RoomView, ILog { override fun initView() { super.initView() - binding.spaceGameRect.post { - val rect = GameViewInfoModel.GameViewRectModel().apply { - top = binding.spaceGameRect.top - bottom = binding.root.height - binding.spaceGameRect.bottom - } - logD("initGameEngine() height:${binding.root.height}") - logD("initGameEngine() top:${rect.top} bottom:${rect.bottom}") - gameEngineViewModel.setGameViewRect(rect) - } } override fun initEvent() { @@ -81,12 +72,19 @@ class GameActivity : BaseGameActivity(), RoomView, ILog { updateAward(it?.first()) } } + } - lifecycleScope.launch { - stateAbility.matchFailedFlow.collectLatest { - showMatchFailed() + override fun initGameEngine(gameContext: GameContext) { + binding.spaceGameRect.post { + val rect = GameViewInfoModel.GameViewRectModel().apply { + top = binding.spaceGameRect.top + bottom = binding.root.height - binding.spaceGameRect.bottom } + logD("initGameEngine() height:${binding.root.height}") + logD("initGameEngine() top:${rect.top} bottom:${rect.bottom}") + gameEngineAbility?.setGameViewRect(rect) } + super.initGameEngine(gameContext) } private fun updateAward(number: Double?) { @@ -106,19 +104,6 @@ class GameActivity : BaseGameActivity(), RoomView, ILog { override fun initObserver() { super.initObserver() - lifecycleScope.launch { - viewModel.closeRoomFlow.collectLatest { - loadingDialogManager?.dismissDialog() - finish() - } - } - - lifecycleScope.launch { - viewModel.restartFlow.collectLatest { - loadingDialogManager?.dismissDialog() - // TODO 待完善 - } - } } override fun initWidgets() { @@ -137,24 +122,6 @@ class GameActivity : BaseGameActivity(), RoomView, ILog { return binding.layoutGame } - private fun showMatchFailed() { - dialogManager.showOkCancelDialog(null, - getString(R.string.game_match_failed), - getString(R.string.game_rematch), - getString(R.string.exit_text), false, false, object : OkCancelDialogListener { - override fun onOk() { - loadingDialogManager?.showProgressDialog(this@GameActivity) - viewModel.restart() - } - - override fun onCancel() { - super.onCancel() - loadingDialogManager?.showProgressDialog(this@GameActivity) - viewModel.closeRoom() - } - }) - } - private fun showExitTips() { dialogManager.showOkCancelDialog(null, getString(R.string.game_exit_tips), diff --git a/app/src/module_game/java/com/chwl/app/game/ui/game/GameViewModel.kt b/app/src/module_game/java/com/chwl/app/game/ui/game/GameViewModel.kt index 76f6de027..878683483 100644 --- a/app/src/module_game/java/com/chwl/app/game/ui/game/GameViewModel.kt +++ b/app/src/module_game/java/com/chwl/app/game/ui/game/GameViewModel.kt @@ -4,6 +4,7 @@ import androidx.lifecycle.MutableLiveData import com.chwl.app.R import com.chwl.app.base.BaseViewModel import com.chwl.app.game.core.GameContext +import com.chwl.app.game.core.GameStateAbility import com.chwl.app.game.data.GameModel2 import com.chwl.core.bean.response.BeanResult import com.chwl.library.utils.ResUtil @@ -15,8 +16,6 @@ class GameViewModel : BaseViewModel() { val closeRoomFlow = MutableSharedFlow>() - val restartFlow = MutableSharedFlow>() - private var gameIntent: GameIntent? = null fun init(intent: GameIntent) { @@ -26,25 +25,15 @@ class GameViewModel : BaseViewModel() { gameContextLiveData.value = gameContext } + fun getGameIntent(): GameIntent? { + return gameIntent + } + override fun onCleared() { super.onCleared() gameContextLiveData.value?.performStop() } - fun restart() { - val intent = gameIntent - if (intent == null) { - safeLaunch { - restartFlow.emit(BeanResult.failed(NullPointerException(ResUtil.getString(R.string.utils_net_beanobserver_05)))) - } - return - } - safeLaunch { - val result = GameModel2.startGame(intent.gameId.toString(), intent.gameMode) - // TODO 待完善 - } - } - fun closeRoom() { val roomId = gameContextLiveData.value?.roomId if (roomId != null) { diff --git a/app/src/module_game/java/com/chwl/app/game/ui/result/GameResultAdapter.kt b/app/src/module_game/java/com/chwl/app/game/ui/result/GameResultAdapter.kt new file mode 100644 index 000000000..2d790e5e4 --- /dev/null +++ b/app/src/module_game/java/com/chwl/app/game/ui/result/GameResultAdapter.kt @@ -0,0 +1,37 @@ +package com.chwl.app.game.ui.result + +import android.view.View +import android.widget.ImageView +import android.widget.TextView +import com.chad.library.adapter.base.BaseQuickAdapter +import com.chad.library.adapter.base.BaseViewHolder +import com.chwl.app.R +import com.chwl.app.game.data.bean.GameResultBean +import com.chwl.app.ui.utils.loadAvatar + +class GameResultAdapter : + BaseQuickAdapter(R.layout.game_result_item) { + override fun convert(helper: BaseViewHolder, item: GameResultBean?) { + helper.setText(R.id.tv_name, item?.nick ?: "") + helper.setText(R.id.tv_coins, item?.getCoinsStr() ?: "0") + helper.getView(R.id.iv_user_avatar).loadAvatar(item?.avatar) + val rank = helper.bindingAdapterPosition + val rankView = helper.getView(R.id.tv_rank) + val rootView = helper.getView(R.id.layout_root) + if (rank == 0) { + rankView.setBackgroundResource(R.drawable.game_result_ic_top1) + rankView.text = "" + } else if (rank == 1) { + rankView.setBackgroundResource(R.drawable.game_result_ic_top2) + rankView.text = "" + } else { + rankView.background = null + rankView.text = "${(rank + 1)}" + } + if (rank % 2 == 0) { + rootView.setBackgroundResource(R.drawable.game_result_item_bg_top1) + } else { + rootView.setBackgroundResource(R.drawable.game_result_item_bg_top2) + } + } +} \ No newline at end of file diff --git a/app/src/module_game/java/com/chwl/app/game/ui/result/GameResultDialog.kt b/app/src/module_game/java/com/chwl/app/game/ui/result/GameResultDialog.kt new file mode 100644 index 000000000..c11f086c0 --- /dev/null +++ b/app/src/module_game/java/com/chwl/app/game/ui/result/GameResultDialog.kt @@ -0,0 +1,90 @@ +package com.chwl.app.game.ui.result + +import android.app.Dialog +import android.os.Bundle +import android.view.LayoutInflater +import android.view.View +import android.view.ViewGroup +import android.view.WindowManager +import androidx.fragment.app.DialogFragment +import com.chwl.app.R +import com.chwl.app.databinding.GameResultDialogBinding +import com.chwl.app.game.data.bean.GameResultBean +import com.chwl.app.ui.widget.recyclerview.decoration.SpacingDecoration +import com.chwl.core.auth.AuthModel +import com.chwl.library.common.util.Utils +import com.example.lib_utils.ktx.singleClick + +class GameResultDialog( + private val list: List, + private var onClose: (() -> Unit)?, + private var onRestart: (() -> Unit)? +) : DialogFragment() { + + private var binding: GameResultDialogBinding? = null + + private val adapter = GameResultAdapter() + + override fun onViewCreated(view: View, savedInstanceState: Bundle?) { + super.onViewCreated(view, savedInstanceState) + initView() + adapter.setNewData(list) + var isSuccess = false + if (list.firstOrNull()?.uid?.toLongOrNull() == AuthModel.get().currentUid) { + isSuccess = true + } + if (isSuccess) { + binding?.ivState?.setBackgroundResource(R.drawable.game_result_bg_win) + } else { + binding?.ivState?.setBackgroundResource(R.drawable.game_result_bg_lose) + } + } + + private fun initView() { + binding?.tvClose?.singleClick { + onClose?.invoke() + } + binding?.tvRestart?.singleClick { + onRestart?.invoke() + } + binding?.recyclerView?.addItemDecoration( + SpacingDecoration( + 0, + Utils.dip2px(context, 10f), + false + ) + ) + binding?.recyclerView?.adapter = adapter + } + + + override fun onDestroyView() { + super.onDestroyView() + onClose = null + onRestart = null + } + + override fun getTheme(): Int { + return R.style.full_screen_dialog + } + + override fun onCreateView( + inflater: LayoutInflater, + container: ViewGroup?, + savedInstanceState: Bundle? + ): View? { + binding = GameResultDialogBinding.inflate(LayoutInflater.from(context)) + return binding?.root + } + + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { + return super.onCreateDialog(savedInstanceState).apply { + this.setCancelable(false) + this.setCanceledOnTouchOutside(false) + window?.setLayout( + WindowManager.LayoutParams.MATCH_PARENT, + WindowManager.LayoutParams.MATCH_PARENT + ) + } + } +} \ No newline at end of file diff --git a/app/src/module_game/res/drawable-xxhdpi/game_result_avatar_border_top_1.png b/app/src/module_game/res/drawable-xxhdpi/game_result_avatar_border_top_1.png new file mode 100644 index 000000000..204e49435 Binary files /dev/null and b/app/src/module_game/res/drawable-xxhdpi/game_result_avatar_border_top_1.png differ diff --git a/app/src/module_game/res/drawable-xxhdpi/game_result_avatar_border_top_2.png b/app/src/module_game/res/drawable-xxhdpi/game_result_avatar_border_top_2.png new file mode 100644 index 000000000..8c2105929 Binary files /dev/null and b/app/src/module_game/res/drawable-xxhdpi/game_result_avatar_border_top_2.png differ diff --git a/app/src/module_game/res/drawable-xxhdpi/game_result_bg_close.png b/app/src/module_game/res/drawable-xxhdpi/game_result_bg_close.png new file mode 100644 index 000000000..cd6c0f469 Binary files /dev/null and b/app/src/module_game/res/drawable-xxhdpi/game_result_bg_close.png differ diff --git a/app/src/module_game/res/drawable-xxhdpi/game_result_bg_lose.png b/app/src/module_game/res/drawable-xxhdpi/game_result_bg_lose.png new file mode 100644 index 000000000..f05f09836 Binary files /dev/null and b/app/src/module_game/res/drawable-xxhdpi/game_result_bg_lose.png differ diff --git a/app/src/module_game/res/drawable-xxhdpi/game_result_bg_restart.png b/app/src/module_game/res/drawable-xxhdpi/game_result_bg_restart.png new file mode 100644 index 000000000..8f243343e Binary files /dev/null and b/app/src/module_game/res/drawable-xxhdpi/game_result_bg_restart.png differ diff --git a/app/src/module_game/res/drawable-xxhdpi/game_result_bg_win.png b/app/src/module_game/res/drawable-xxhdpi/game_result_bg_win.png new file mode 100644 index 000000000..b57b9e89b Binary files /dev/null and b/app/src/module_game/res/drawable-xxhdpi/game_result_bg_win.png differ diff --git a/app/src/module_game/res/drawable-xxhdpi/game_result_ic_top1.png b/app/src/module_game/res/drawable-xxhdpi/game_result_ic_top1.png new file mode 100644 index 000000000..20863c9d9 Binary files /dev/null and b/app/src/module_game/res/drawable-xxhdpi/game_result_ic_top1.png differ diff --git a/app/src/module_game/res/drawable-xxhdpi/game_result_ic_top2.png b/app/src/module_game/res/drawable-xxhdpi/game_result_ic_top2.png new file mode 100644 index 000000000..b2f811200 Binary files /dev/null and b/app/src/module_game/res/drawable-xxhdpi/game_result_ic_top2.png differ diff --git a/app/src/module_game/res/drawable-xxhdpi/game_result_item_bg_top1.png b/app/src/module_game/res/drawable-xxhdpi/game_result_item_bg_top1.png new file mode 100644 index 000000000..7cd093bda Binary files /dev/null and b/app/src/module_game/res/drawable-xxhdpi/game_result_item_bg_top1.png differ diff --git a/app/src/module_game/res/drawable-xxhdpi/game_result_item_bg_top2.png b/app/src/module_game/res/drawable-xxhdpi/game_result_item_bg_top2.png new file mode 100644 index 000000000..e214d3d84 Binary files /dev/null and b/app/src/module_game/res/drawable-xxhdpi/game_result_item_bg_top2.png differ diff --git a/app/src/module_game/res/drawable/game_result_bg_1.xml b/app/src/module_game/res/drawable/game_result_bg_1.xml new file mode 100644 index 000000000..457b1a5ff --- /dev/null +++ b/app/src/module_game/res/drawable/game_result_bg_1.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/module_game/res/drawable/game_result_bg_2.xml b/app/src/module_game/res/drawable/game_result_bg_2.xml new file mode 100644 index 000000000..7c4001972 --- /dev/null +++ b/app/src/module_game/res/drawable/game_result_bg_2.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/app/src/module_game/res/drawable/game_result_coins_top1.xml b/app/src/module_game/res/drawable/game_result_coins_top1.xml new file mode 100644 index 000000000..97a76c566 --- /dev/null +++ b/app/src/module_game/res/drawable/game_result_coins_top1.xml @@ -0,0 +1,13 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/module_game/res/drawable/game_result_coins_top2.xml b/app/src/module_game/res/drawable/game_result_coins_top2.xml new file mode 100644 index 000000000..af0188b4d --- /dev/null +++ b/app/src/module_game/res/drawable/game_result_coins_top2.xml @@ -0,0 +1,13 @@ + + + + + + + \ No newline at end of file diff --git a/app/src/module_game/res/layout/game_result_dialog.xml b/app/src/module_game/res/layout/game_result_dialog.xml new file mode 100644 index 000000000..84a6db603 --- /dev/null +++ b/app/src/module_game/res/layout/game_result_dialog.xml @@ -0,0 +1,96 @@ + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/module_game/res/layout/game_result_item.xml b/app/src/module_game/res/layout/game_result_item.xml new file mode 100644 index 000000000..c1fd6186f --- /dev/null +++ b/app/src/module_game/res/layout/game_result_item.xml @@ -0,0 +1,108 @@ + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/core/src/main/java/com/chwl/core/bean/game/GameRoomData.kt b/core/src/main/java/com/chwl/core/bean/game/GameRoomData.kt index 69c777728..e7b39b386 100644 --- a/core/src/main/java/com/chwl/core/bean/game/GameRoomData.kt +++ b/core/src/main/java/com/chwl/core/bean/game/GameRoomData.kt @@ -8,6 +8,7 @@ class GameRoomData : Serializable { val mgId: String? = null val gameRoomIcon: String? = null val configJson: String? = null + val gameSelectCfg: String? = null val scores: MutableList? = null // 匹配状态(0:匹配中、1:匹配成功、2:游戏结束、3:匹配失败)