Example #1
0
class FishPlayerBuffer(object):
    """回馈赛的buffer"""
    def __init__(self, player, bufferId):
        self.player = player
        self.bufferId = bufferId
        self._state = BUFFER_STATE.NOEFFECT
        self.startTime = 0
        self.bufferConf = config.getPlayerBufferConf(bufferId)
        self.checkTimer = FTLoopTimer(self.bufferConf["delayTime"], -1,
                                      self._bufferStart)
        self.checkTimer.start()

        self.sendMsgTimer = None

    def getBufferId(self):
        return self.bufferId

    def isStart(self):
        return self._state == BUFFER_STATE.START

    def _bufferStart(self):
        if self.checkTimer:
            self.checkTimer.cancel()
        self._state = BUFFER_STATE.START
        self.startTime = int(time.time())
        pass
class MultipleFishGroup(object):
    """
    倍率鱼群(回馈赛专用)
    """
    def __init__(self, table):
        self.table = table
        self._tableRank = 0
        self._nextGroupTimer = None

    def clearTimer(self):
        if self._nextGroupTimer:
            self._nextGroupTimer.cancel()
            self._nextGroupTimer = None

    def initGroup(self, tableRank):
        """初始化倍率鱼群"""
        self._tableRank = tableRank
        self._setNextGroupTimer()

    def _setNextGroupTimer(self):
        """启动倍率鱼定时器"""
        self.clearTimer()
        interval = random.randint(25, int((1 - self._tableRank) * 30 + 30))
        self._nextGroupTimer = FTLoopTimer(interval, 0,
                                           self._addMultipleFishGroup)
        self._nextGroupTimer.start()

    def _addMultipleFishGroup(self):
        """添加倍率鱼群"""
        randomNum = random.randint(1, 10000)
        for multipleFishMap in config.getRandomMultipleFishConf(
                self.table.runConfig.fishPool):
            probb = multipleFishMap["probb"]
            if probb[0] <= randomNum <= probb[-1]:
                fishType = multipleFishMap["fishType"]
                allMultipleGroupIds = self.table.runConfig.allMultipleGroupIds
                groupId = random.choice(allMultipleGroupIds[fishType])
                if ftlog.is_debug():
                    ftlog.debug("_addMultipleFishGroup", fishType,
                                allMultipleGroupIds, groupId)
                self.table.insertFishGroup(groupId)
                self._setNextGroupTimer()
                break

    def cancelNextGroupTimer(self):
        """取消定时器"""
        if self._nextGroupTimer:
            self._nextGroupTimer.cancel()
            self._nextGroupTimer = None
Example #3
0
class SportOdds(object):
    '''欧指筛选'''
    ODDS = ['17', '308', '255', '256', '254']

    def __init__(self, sportlottery):
        self.sportlottery = sportlottery

        self.timer = FTLoopTimer(10, -1, self._getHttpOdds)
        self.timer.start()

        if ftlog.is_debug():
            ftlog.debug('SportOdds', 'sportObj=', self.sportlottery.toDict())

    def _getHttpOdds(self):
        if ftlog.is_debug():
            ftlog.debug('SportOdds', '_getHttpOdds=',
                        self.sportlottery.toDict())

        contents = http7Mgethdaoddsinfo(self.sportlottery.matchId)
        if not contents or len(contents) == 0:
            return

        if contents.get('GameId') != str(self.sportlottery.matchId):
            ftlog.warn('SportOdds httpOdds GameId not match', 'matchId=',
                       self.sportlottery.matchId, 'GameId=',
                       contents.get('GameId'))
            return

        Datas = contents.get('Datas')
        if not Datas or len(Datas) == 0:
            ftlog.warn('SportOdds httpOdds Datas empty', 'Datas=', Datas,
                       'contents=', contents)
            return

        tag = 0
        for data in Datas:
            if data.get('Cid') in self.ODDS:
                odds = data.get('Data', [])
                if odds and len(odds) >= 3:
                    self.sportlottery.odds = odds[:3]
                    self.sportlottery.save()
                    if self.timer:
                        self.timer.cancel()

                    tag = 1
                    break
        if self.sportlottery and tag == 1:
            self.sportlottery.clearSportOdds()
Example #4
0
class ScriptBase(object):

    @classmethod
    def createScript(cls, player):
        script = None
        fishPool = player.table.runConfig.fishPool
        conf = config.getRobotConf("robotScript").get(str(fishPool), {})
        scriptsConf = conf.get("scripts", [])
        if conf.get("enable", 0) == 1 and len(scriptsConf) > 0:
            level = 0
            fireRange = []
            idleRange = []
            leaveRange = []
            probb = random.randint(0, 10000)
            for script in scriptsConf:
                if script.get("probb", 0) == 0:
                    continue
                if probb > script.get("probb", 0) > 0:
                    probb -= script["probb"]
                else:
                    level = script["level"]
                    fireRange = script["fireRange"]
                    idleRange = script["idleRange"]
                    leaveRange = script["leaveRange"]
            if level == 1:
                from newfish.script.script_easy import ScriptEasy1
                script = ScriptEasy1(player, fireRange, idleRange, leaveRange)#Script%s%d % ("Easy", level)
        ftlog.debug("createScript", player.userId, fishPool, scriptsConf, (script is not None))
        return script

    def __init__(self, player, fireRange, idleRange, leaveRange):
        self.player = player
        self.userId = player.userId
        self.player.table.clip_add(player.userId, player.seatId)
        ftlog.debug("__init__", player.userId, player.chip, player.clip)
        self.fireInterval = config.getGunLevelConf(self.nowGunLevel, self.player.table.gameMode).get("interval", 0.3)
        self.updateTimer = None
        self.updateTimer = FTLoopTimer(5, -1, self._update)
        self.updateTimer.start()
        self.fireTimer = None
        self.fireTargetPos = [0, 0]
        self.bulletId = 0
        # 空闲次数
        self.idleCount = 0
        # 开火次数
        self.fireCount = 0
        # 等待离开
        self.waitLeaveCount = 0
        # 表情
        self.exp = ["1", "2", "3", "4", "5", "6", "7", "8", "9"]
        self.fireRange = fireRange
        self.idleRange = idleRange
        self.leaveRange = leaveRange
        self.updateCount = 0
        self.needClear = False
        # 全是机器人的计数
        self.allRobotCount = 0

    @property
    def table(self):
        return self.player.table

    # @property
    # def userId(self):
    #     return self.player.userId

    @property
    def seatId(self):
        return self.player.seatId

    @property
    def nowGunLevel(self):
        return self.player.nowGunLevel

    @property
    def clip(self):
        return self.player.clip

    def clear(self):
        self._clearData()

    def _clearData(self):
        ftlog.debug("_clearData", self.userId, self.table.tableId)
        if self.updateTimer:
            self.updateTimer.cancel()
            self.updateTimer = None
        else:
            return
        if self.fireTimer:
            self.fireTimer.cancel()
        self.bulletId = 1
        self.fireCount = 0
        self.idleCount = 0
        self.waitLeaveCount = 0
        robotutil.sendRobotNotifyShutDownByUserId(self.table, self.userId)
        self.table.clearPlayer(self.player)

    def _update(self):
        pass

    def _updateFireTarget(self):
        pass

    def _fire(self):
        self.bulletId += 1
        self.bulletId %= 90
        if self.bulletId == 0:
            self.bulletId += 1
        self.player.lastActionTime = int(time.time())
        canFire = self.table.fire(self.userId, self.seatId, self.fireTargetPos, self.nowGunLevel, self.bulletId, 0, self.player.lastActionTime, 0)
        self.fireCount -= 1
        if canFire and self.fireCount > 0:
            self.fireTimer = FTLoopTimer(self.fireInterval, 0, self._fire)
            self.fireTimer.start()
        else:
            self.fireCount = 0
            self.fireTimer = None
            if canFire:
                self.idleCount = random.randint(self.idleRange[0], self.idleRange[1])
                ftlog.debug("_fire, idle", self.userId, self.idleCount)
            else:
                self.waitLeaveCount = random.randint(self.leaveRange[0], self.leaveRange[1])
                ftlog.debug("_fire, wait leave", self.userId, self.waitLeaveCount)

        # ftlog.debug("_update", self.userId, self.seatId, self.nowGunLevel, self.bulletId,
        #             self.chip, self.fireCount, self.fireTargetPos)

    def _isNeedCancelTimer(self):
        if self.updateTimer and (self.userId == 0 or self.clip == 0 or self.needClear or self.allRobotCount == 12):
            ftlog.debug("_isNeedCancelTimer", self.userId, self.clip, self.needClear, self.allRobotCount)
            self._clearData()
            return True
        self.updateCount += 1
        if self.updateCount % 12 == 0:
            conf = config.getRobotConf("robotScript").get(str(self.table.runConfig.fishPool), {})
            # 关闭机器人
            if conf.get("enable", 0) == 0:
                self.needClear = True
                return True

        for player in self.table.players:
            if player and player.isRobotUser is not True:
                self.allRobotCount = 0
                break
        else:
            self.allRobotCount += 1
        return False

    def _isWaitLeave(self):
        if self.waitLeaveCount > 0:
            self.waitLeaveCount -= 1
            if self.waitLeaveCount == 0:
                ftlog.debug("_isWaitLeave", self.userId)
                self._clearData()
            return True
        return False

    def _isIdle(self):
        if self.idleCount > 0:
            self.idleCount -= 1
            return True
        return False

    def _chat(self, isFace=1):
        chatMsg = random.choice(self.exp)
        mo = MsgPack()
        mo.setCmd("table_chat")
        mo.setResult("gameId", FISH_GAMEID)
        mo.setResult("userId", self.userId)
        mo.setResult("seatId", self.seatId)
        mo.setResult("isFace", isFace)
        mo.setResult("chatMsg", chatMsg)
        GameMsg.sendMsg(mo, self.table.getBroadcastUids())

    def _setFireTimer(self):
        ftlog.debug("_setFireTimer", self.userId, (self.fireTimer is not None), self.fireCount, self.fireInterval)
        if self.fireTimer is None:
            self.fireCount = random.randint(self.fireRange[0], self.fireRange[1])
            self.fireTimer = FTLoopTimer(self.fireInterval, 0, self._fire)
            self.fireTimer.start()
Example #5
0
class MajiangQuickTable(TYTable):
    """
    1)负责框架桌子资源的管理,对接赛制/自建桌
    2)负责处理用户的上行消息处理
    3)麻将的具体逻辑,在逻辑类中处理
    4)负责联网玩法用户准备情况的管理,条件合适的时候开始游戏
    5)MajiangTable就是核心玩法里联网的action_handler
    """

    def __init__(self, tableId, room):
        super(MajiangQuickTable, self).__init__(room, tableId)
        self._owner = room
        self._players = []
        self.__actionHandler = ActionHandlerFactory.getActionHandler(MRunMode.LONGNET)

        # 初始化seat
        for seat in range(self.maxSeatN):
            self.seats[seat] = TYSeat(self)

        # 牌桌配置
        self._roomConf = self.room.roomConf
        ftlog.debug('MajiangQuickTable.init roomConf->', self._roomConf)

        self._tableConf = copy.deepcopy(self._roomConf.get('tableConf', {}))
        self._tableConf[MFTDefine.IS_CREATE] = self.getRoomConfig(MFTDefine.IS_CREATE, 0)
        ftlog.debug('MajiangQuickTable.init tableConf->', self._tableConf)

        # 创建逻辑配桌类
        self.__tableType = self.getRoomConfig('tableType', 'normal')
        self.__playMode = self.getRoomConfig('playMode', 'guobiao')
        ftlog.debug('MajiangQuickTable.init playMode:', self.playMode)
        # 逻辑牌桌
        self.__table_observer = None
        # 下面这部分代码改为在具体的麻将工程里执行,以实现不同牌桌逻辑的拆分
        # self.logic_table = MajiangTableLogic(self.maxSeatN, self.playMode, MRunMode.LONGNET)
        # self.logic_table.setTableConfig(self._tableConf)
        # # 设置牌桌公共信息,tableId & roomId
        # self.logic_table.msgProcessor.setInfo(self.gameId, self.roomId, self.tableId, self.playMode, self.tableType, self.maxSeatN)
        # self.logic_table.msgProcessor.setRoomInfo(self._roomConf, self._tableConf)

        # # 给action handler设置table
        # self.__actionHandler.setTable(self.logic_table)

        # 初始化定时器,个数是座位数加1
        self.__timer = MajiangTableTimer(self.maxSeatN + 1, self)
        # 循环定时器,用于处理牌桌托管状态
        self.__looper_timer = None

        ftlog.debug('MajiangQuickTable create ok, roomId:', self.roomId, ' tableId:', self.tableId)

    @property
    def tableObserver(self):
        return self.__table_observer

    def setTableObserver(self, observer):
        """设置牌桌观察者"""
        self.__table_observer = observer
        self.logic_table.setTableObserver(self.tableObserver)

    @property
    def tableTimer(self):
        return self.__timer

    @property
    def tableLooperTimer(self):
        return self.__looper_timer

    @property
    def actionHander(self):
        """行为解析处理器"""
        return self.__actionHandler

    @property
    def playMode(self):
        """玩法"""
        return self.__playMode

    @property
    def tableType(self):
        """牌桌类型"""
        return self.__tableType

    def changeFrameSeatToMJSeatId(self, frameSeat):
        """将框架的seatId转化为麻将的座位号
        获取座位号时,框架返回的是1,2,3,内部存储的索引是0,1,2
        麻将的座位号是0,1,2
        """
        return frameSeat - 1

    def changeMJSeatToFrameSeat(self, mjSeat):
        """将麻将的座位号转化为框架的座位号"""
        return mjSeat + 1

    def getRoomConfig(self, name, defaultValue):
        """获取房间配置"""
        return self._roomConf.get(name, defaultValue)

    def getTableConfig(self, name, defaultValue):
        """获取table配置"""
        return self._tableConf.get(name, defaultValue)

    def CheckSeatId(self, seatId, userId=None):
        """校验座位号"""
        seatValid = (seatId >= 0) and (seatId < self.maxSeatN)
        if not seatValid:
            return False

        if not userId:
            return seatValid

        if self.seats[seatId][TYSeat.INDEX_SEATE_USERID] != userId:
            return False

        return True

    def doTableChat(self, userId, seatId, isFace, voiceIdx, chatMsg):
        """doTableChat非父类的接口,可以抽象至麻将的table基类中"""
        ftlog.debug('MajiangQuickTable.doTableChat userId:', userId,
                    ' seatId:', seatId,
                    ' ifFace:', isFace if isFace else "1",
                    ' voiceIdx:', voiceIdx if voiceIdx else "1",
                    ' chatMsg:', chatMsg if chatMsg else "1")

        if 'type' in chatMsg and chatMsg['type'] == 2:
            # 表情
            if not self.process_interactive_expression(userId, seatId, chatMsg):
                return
        # 语音/文字
        self._doTableChat(userId, seatId, isFace, voiceIdx, chatMsg)

    def _doTableChat(self, userId, seatId, isFace, voiceIdx, chatMsg):
        """
        聊天的逻辑
        1)文字聊天
        {
            "cmd": "table_chat",
            "params": {
                "roomId": 7108021001,
                "tableId": 71080210010100,
                "seatId": 1,
                "isFace": 0,
                "msg": {
                    "seatId": 1,
                    "type": 0,
                    "content": "abc"
                },
                "gameId": 710,
                "userId": 10856,
                "clientId": "IOS_3.91_tuyoo.appStore,weixinPay,alipay.0-hall6.appStore.huanle"
            }
        }
        
        2)语音聊天
        {
            "cmd": "table_chat",
            "params": {
                "roomId": 7108021001,
                "tableId": 71080210010100,
                "seatId": 1,
                "isFace": 0,
                "msg": {
                    "seatId": 1,
                    "type": 2,
                    "emoId": 1,
                    "targetSeatId": 0
                },
                "gameId": 710,
                "userId": 10856,
                "clientId": "      IOS_3.91_tuyoo.appStore,weixinPay,alipay.0-hall6.appStore.huanle"
            }
        }
        """
        if not chatMsg:
            return

        if isFace == 0 and 'type' in chatMsg and chatMsg['type'] == 0:  # 麻将文字聊天消息
            content = chatMsg['content']
            filterContent = keywords.replace(content)
            chatMsg['content'] = filterContent

        if isFace == 0:
            users = self.logic_table.getBroadCastUIDs()
            self.logic_table.msgProcessor.table_chat_broadcast(userId, self.gameId, voiceIdx, chatMsg, users)
        else:
            for seat in self.maxSeatN:
                player = self.logic_table.getPlayer(seat)
                self.logic_table.msgProcessor.table_chat_to_face(userId, self.gameId, voiceIdx, chatMsg, player)

    def process_interactive_expression(self, uid, seatId, chat_msg):
        """处理消费金币的表情"""
        targetSeatId = chat_msg.get('targetSeatId', -1)
        if not self.CheckSeatId(targetSeatId, None):
            return False

        target_player_uid = self.seats[targetSeatId][TYSeat.INDEX_SEATE_USERID]
        return MTableExpression.process_interactive_expression(uid
                                                               , self.gameId
                                                               , seatId
                                                               , chat_msg
                                                               , target_player_uid
                                                               , self.getTableConfig('base_chip', 0))

    def _doSit(self, msg, userId, seatId, clientId):
        '''玩家操作, 尝试再当前的某个座位上坐下'''
        ftlog.debug('==_doSit=msg=', msg, ' seatId:', seatId, 'tableId:', self.tableId)
        seatId = self.changeFrameSeatToMJSeatId(seatId)
        self.doSitDown(userId, seatId, msg, clientId)

    def doSitDown(self, userId, seatId, msg, clientId):
        """
        用户坐到某个桌子上,逻辑处理:如果是非重连用户,将用户坐下的消息广播给
        其它已经坐下的用户,然后将当前的桌子信息发送给新来用户
        继承自table类
        这是的seatId为游戏的座位号
        
        返回值:
        1)是否做下
        2)是否断线重连
        """
        ftlog.debug('>>MajiangQuickTable.doSitDown seatId =', seatId, ', userId = ', userId, ' tableId:', self.tableId)

        if (seatId != -1) and (userId != self.seats[seatId][TYSeat.INDEX_SEATE_USERID]):
            onlinedata.removeOnlineLoc(userId, self.roomId, self.tableId)
            ftlog.warn('reconnecting user id is not matched', 'seats =', self.seats, ' tableId:', self.tableId)
            return False

        frameSeatId = self.findIdleSeat(userId)
        ftlog.debug('MajiangQuickTable.doSitDown userId:', userId, ' findSeatId:', frameSeatId)

        sitRe = True
        if 0 == frameSeatId:
            ftlog.debug('MajiangQuickTable.doSitDown now seats:', self.seats)
            sendPopTipMsg(userId, '对不起,该房间已满员')
            self.logic_table.msgProcessor.quick_start_err(userId)
            sitRe = False
        elif 0 > frameSeatId:
            # 补发tableInfo
            seatId = self.getSeatIdByUserId(userId)
            if seatId < 0:
                onlinedata.removeOnlineLoc(userId, self.roomId, self.tableId)
            else:
                self.sendMsgTableInfo(msg, userId, seatId, True)
        elif frameSeatId > 0:
            isReady = self.getTableConfig(MTDefine.READY_AFTER_SIT, 0)
            gameSeatId = self.changeFrameSeatToMJSeatId(frameSeatId)
            # 设置座位的状态
            self.seats[gameSeatId][TYSeat.INDEX_SEATE_USERID] = userId
            # 快速桌用户坐下就是准备状态
            self.seats[gameSeatId][
                TYSeat.INDEX_SEATE_STATE] = TYSeat.SEAT_STATE_READY if isReady else TYSeat.SEAT_STATE_WAIT
            # 添加玩家
            tPlayer = TYPlayer(self, gameSeatId)
            self.players[gameSeatId] = tPlayer
            ftlog.debug('MajiangQuickTable.doSitDown user:'******' seat in:', gameSeatId
                        , ' now seats:', self.seats
                        , ' now playersNum:', self.playersNum)

            # 向牌桌添加用户:联网/机器人
            if TYPlayer.isHuman(userId):
                locResult = onlinedata.addOnlineLoc(userId, self.roomId, self.tableId, frameSeatId)
                ftlog.info('MajiangQuickTable.doSitDown, add online loc userId:', userId
                           , ' roomId:', self.roomId
                           , ' tableId:', self.tableId
                           , ' frameSeatId:', frameSeatId
                           , ' locResult:', locResult)

                _name, _purl, _sex, _coin = userdata.getAttrs(userId, ['name', 'purl', 'sex', 'coin'])
                player = MPlayer(_name, _sex, userId, 0, _purl, _coin, clientId)
                # 快速桌 默认坐下就是准备状态 默认非托管状态
                self.logic_table.addPlayer(player, gameSeatId, isReady, False)
                # 发送location消息
                self.logic_table.msgProcessor.send_location_message(gameSeatId, userId)
            else:
                from difang.majiang2.resource import resource
                robot = resource.getRobotByUserId(userId)
                if robot:
                    player = MPlayer(robot['name'], robot['sex'], userId, 0, robot['purl'], robot['coin'])
                    # 机器人默认准备 默认托管状态
                    self.logic_table.addPlayer(player, gameSeatId, True, True)

            # 座位号调整,框架返回时进行了加1的操作,调整还原
            self.room.updateTableScore(self.getTableScore(), self.tableId)
            self.sendMsgTableInfo(msg, userId, self.getSeatIdByUserId(userId), False)
            if self.playersNum != self.maxSeatN:
                # 一次召唤一个机器人
                self.setTimerOfDispatchRobot()
                uids = self.logic_table.getBroadCastUIDs()
                self.logic_table.msgProcessor.sendTableEvent(self.playersNum, userId, gameSeatId, uids)
            else:
                # 人满了,启动定时器
                self.setTimerHandleAutoDecideAction()

        return sitRe

    def sendMsgTableInfo(self, message, userId, seatId, isReconnect, isHost=False):
        """玩家坐下后,给玩家发送table_info,拉进游戏"""
        self.logic_table.sendMsgTableInfo(seatId, isReconnect)
        if isReconnect:
            self.logic_table.msgProcessor.table_call_latest_msg(seatId)
        self.logic_table.setPlayerLeave(seatId, False)
        self.logic_table.sendEnterMessage()
        self.logic_table.msgProcessor.broadcastUserSit(seatId, userId, isReconnect, isHost,
                                                       self.logic_table.getBroadCastUIDs())

    def setTimerHandleAutoDecideAction(self):
        """定时器,处理托管的行为"""
        ftlog.debug('MajiangQuickTable.setTimerHandleAutoDecideAction tableId:', self.tableId)
        if not self.__looper_timer:
            self.__looper_timer = FTLoopTimer(1, -1, self.handle_auto_decide_action)
        self.__looper_timer.start()

    def setTimerOfDispatchRobot(self):
        """设置定时器加派机器人"""
        ftlog.debug('MajiangQuickTable.setTimerOfDispatchRobot tableId:', self.tableId)
        robotMode = self.getRoomConfig('hasrobot', 1)
        if robotMode != 1:
            return

        message = self.logic_table.msgProcessor.getMsgDispatchRobot(self.playersNum)
        robot_interval = majiang_conf.getRobotInterval(self.gameId)

        self.__timer.setupTimer(self.maxSeatN  # 调用的定时器编号
                                , robot_interval  # 定时时间
                                , message)  # 定时回调的消息

    def _handle_dispatch_virtual_player(self):
        """处理分派机器人"""
        self.__timer.cancelTimer(self.maxSeatN)

        playerCount = runcmd.getMsgPack().getParam('player_count')
        if (self.playersNum == playerCount):
            ftlog.debug("dispatch_virtual_player", playerCount)
            robotRandom = random.randint(1, 9999)
            from difang.majiang2.resource import resource
            robot = resource.getRobot(robotRandom)
            self.doSitDown(robot['userId'], -1, None, 'robot_3.7_-hall6-robot')
        else:
            ftlog.debug('player_count is changed, no need to dispacth virtual player')

    @locked
    def handle_auto_decide_action(self):
        """处理托管"""
        ftlog.debug('$$$$$$$$$$$$$$$$$MajiangQuickTable.handle_auto_decide_action')
        if self.logic_table.isGameOver():
            """游戏结束,通知玩家离开,站起,重置牌桌"""
            ftlog.debug('MajiangQuickTable.handle_auto_decide_action gameOver, clearTable... tableId:', self.tableId)
            self.clearTable(True)
            return

        self.actionHander.updateTimeOut(-1)
        self.actionHander.doAutoAction()

    def _doTableCall(self, msg, userId, seatId, action, clientId):
        """
        继承父类,处理table_call消息
        """
        try:
            ftlog.debug('MajiangQuickTable handle table_call message, tableId:', self.tableId, ' seatId:', seatId,
                        ' action:', action)
            if not self.CheckSeatId(seatId, userId):
                ftlog.warn("handle table_call, seatId is invalid,", action, seatId)
                return

            if action == 'play':
                # 出牌
                self.actionHander.handleTableCallPlay(userId, seatId, msg)
            elif action == 'chi':
                # 吃牌
                self.actionHander.handleTableCallChi(userId, seatId, msg)
            elif action == 'peng':
                # 碰牌
                self.actionHander.handleTableCallPeng(userId, seatId, msg)
            elif action == 'gang':
                # 杠牌
                self.actionHander.handleTableCallGang(userId, seatId, msg)
            elif action == 'grabTing':
                # 抢听
                self.actionHander.handleTableCallGrabTing(userId, seatId, msg)
            elif action == 'win':
                # 和牌
                self.actionHander.handleTableCallWin(userId, seatId, msg)
            elif action == 'pass':
                # 过牌
                self.actionHander.handleTableCallpass(userId, seatId, msg)
            elif action == 'dispatch_virtual_player':
                # 分配机器人
                self._handle_dispatch_virtual_player()
            elif action == 'grabHuGang':
                # 抢杠和
                self.actionHander.handleTableCallGrabHuGang(userId, seatId, msg)
            elif action == 'fanpigu':
                self.actionHander.handleTableCallFanpigu(userId, seatId, msg)
            elif action == 'ping':
                # 获取网速
                self.actionHander.handleTableCallPing(userId, seatId, msg)
            elif action == 'exchange':
                # 换牌
                self.actionHander.handleTableCallExchange(userId, seatId, msg)
            else:
                ftlog.debug('MajiangQuickTable._doTableCall unprocessed message:', msg)
        except:
            ftlog.error("_doTableCall error clear table")
            self.clearTable(True)

    def kickOffUser(self, userId, seatId, sendLeave):
        """让一个玩家leave"""
        ftlog.debug('MajiangQuickTable.kickOffUser userId:', userId, ' seatId:', seatId)
        onlinedata.removeOnlineLoc(userId, self.roomId, self.tableId)
        # uids = self.logic_table.getBroadCastUIDs()
        # 临时注释掉 待整体确认 taoxc
        # self.logic_table.msgProcessor.table_leave(userId, seatId, uids)
        # 游戏开始之后的退出,客户端不需要再收到退出消息 客户端的退出由其自身控制 sendLeave = False
        # 游戏未开始时房主解散了房间才需要向客户端发消息 sendLeave = True
        if sendLeave:
            uids = self.logic_table.getBroadCastUIDs()
            self.logic_table.msgProcessor.create_table_dissolve(userId, seatId, 'dissolve', uids)
        self.logic_table.removePlayer(seatId)
        self.seats[seatId] = TYSeat(self)
        self.players[seatId] = None

    def _doLeave(self, msg, userId, clientId):
        reason = msg.getParam("reason", TYRoom.LEAVE_ROOM_REASON_ACTIVE)
        ftlog.debug("majiang_quick_table_doLeave msg:", msg)
        if reason == TYRoom.LEAVE_ROOM_REASON_LOST_CONNECTION:
            self.logic_table.sendLeaveMessage(userId)
            return {"isOK": False, "reason": reason}

    def _doStandUp(self, msg, userId, seatId, reason, clientId):
        '''
        玩家操作, 尝试离开当前的座位
        子类需要自行判定userId和seatId是否吻合
        快速麻将桌的站起比较简单
        牌局没开始,站起
        牌局已开始,不处理,超时托管
        '''
        if self.logic_table.isPlaying():
            # 用户托管
            ftlog.debug('MajiangQuickTable.standup, user stand up, set trustTee...')
            self.logic_table.setAutoDecideValue(seatId, True)
        else:
            ftlog.info('MajiangQuickTable.standup removeOnlineLoc userId:', userId
                       , ' roomId:', self.roomId
                       , ' tableId:', self.tableId
                       , ' seatId:', seatId
                       , ' reason:', reason)
            self.kickOffUser(userId, seatId, True)

    def _doTableManage(self, msg, action):
        '''桌子内部处理所有的table_manage命令'''
        ftlog.debug("majiang_quick_table_doTableManage msg:", msg, "action", action)
        result = {'isOK': True}
        if action == 'leave':
            userId = msg.getParam('userId')
            self.logic_table.sendLeaveMessage(userId)
            seatId = self.getSeatIdByUserId(userId)
            if seatId >= 0:
                self.logic_table.setPlayerLeave(seatId, True)
                self._doStandUp(None, userId, seatId, -1, '')
        elif action == 'clear_table':
            self.clearTable(True)
        elif action == 'tableTiles':
            if self.logic_table.isPlaying():
                self.logic_table.printTableTiles()
        return result

    def getSeatIdByUserId(self, userId):
        """根据userId获取座位号"""
        for index in range(self.maxSeatN):
            if self.seats[index][TYSeat.INDEX_SEATE_USERID] == userId:
                return index
        return -1

    def clearTable(self, sendLeave):
        """清理桌子"""
        ftlog.debug('MajiangQuickTable.clearTable tableId:', self.tableId
                    , ' now seats: ', self.seats)
        self.__timer.cancelTimerAll()
        if self.__looper_timer:
            self.__looper_timer.cancel()
            self.__looper_timer = None
        # 清理用户座位
        for seatId in range(self.maxSeatN):
            if self.seats[seatId][TYSeat.INDEX_SEATE_USERID] != 0:
                self.kickOffUser(self.seats[seatId][TYSeat.INDEX_SEATE_USERID], seatId, sendLeave)
        # 结束游戏
        self.logic_table.reset()

        # 释放桌子
        self.room.updateTableScore(self.getTableScore(), self.tableId)

    def saveRecordAfterClear(self, score):
        """score为每盘的分的数组,形如[[-1,0,1,0],[2,1,1,0]]"""
        scoreInfo = {}

        ftlog.debug("saveRecordAfterClear score:", score)
        playerCount = self.logic_table.playerCount
        sendScore = [[] for _ in range(playerCount)]
        for roundScore in score:
            for i in range(0, playerCount):
                sendScore[i].append(roundScore[i])
        ftlog.debug("saveRecordAfterClear sendScore:", sendScore)
        ftId = self.logic_table.tableConfig[MFTDefine.FTID]
        for i in range(0, playerCount):
            cp = self.logic_table.player[i]
            if cp:
                if 'budget' not in scoreInfo:
                    scoreInfo['budget'] = {}
                import time
                if 'time' not in scoreInfo:
                    scoreInfo['time'] = int(time.time())
                if 'playMode' not in scoreInfo:
                    scoreInfo['playMode'] = self.playMode
                if 'tableId' not in scoreInfo:
                    scoreInfo['tableId'] = self.tableId
                if 'tableNo' not in scoreInfo:
                    scoreInfo['tableNo'] = ftId
                if 'defaultScore' not in scoreInfo:
                    scoreInfo['defaultScore'] = 1
                if 'recordUrls' not in scoreInfo:
                    scoreInfo['recordUrls'] = self.logic_table.recordUrls
                if cp.userId not in scoreInfo['budget']:
                    scoreInfo['budget'][cp.userId] = {}
                scoreInfo['budget'][cp.userId]['uid'] = cp.userId
                scoreInfo['budget'][cp.userId]['name'] = cp.name
                scoreInfo['budget'][cp.userId]['score'] = sendScore[i]
                if self.logic_table.tableResult:
                    if self.logic_table.tableResult.score:
                        scoreInfo['budget'][cp.userId]['deltaScoreList'] = \
                            self.logic_table.tableResult.score[i]
                    else:
                        scoreInfo['budget'][cp.userId]['deltaScoreList'] = 0
                else:
                    scoreInfo['budget'][cp.userId]['deltaScoreList'] = 0
        MJCreateTableRecord.saveRecord(scoreInfo, ftId, self.gameId)
Example #6
0
class Heartbeat(object):
    """心跳的类"""
    ST_IDLE = 0
    ST_START = 1
    ST_STOP = 2

    def __init__(self, target, interval):
        self._target = target
        self._state = Heartbeat.ST_IDLE
        self._count = 0
        self._postTaskList = []
        self._timer = None
        self._interval = interval
        self._init = False

    def start(self):
        assert (self._state == Heartbeat.ST_IDLE)
        self._state = Heartbeat.ST_START
        self._timer = FTLoopTimer(0, 0, self._onInit)
        self._timer.start()

    def stop(self):
        """停止定时器"""
        if self._state != Heartbeat.ST_STOP:
            self._state = Heartbeat.ST_STOP
            if self._timer:
                self._timer.cancel()
            self._timer = None

    @property
    def count(self):
        """次数"""
        return self._count

    def postCall(self, func, *args, **kwargs):
        """处理回调用"""
        self.postTask(functools.partial(func, *args, **kwargs))

    def postTask(self, task):
        """添加任务"""
        if self._state != Heartbeat.ST_STOP:
            self._postTaskList.append(task)
            if self._init and self._timer:
                self._timer.cancel()
                self._timer = FTLoopTimer(0, 0, self._onTimeout)
                self._timer.start()

    def _onInit(self):
        """启动定时器"""
        try:
            self._timer = None
            interval = self._target.onInit()        # 上层类函数初始化
            if interval:
                self._interval = interval           # 间隔时间
            self._scheduleTimer()
        except:
            ftlog.error("Heartbeat._onInit")

    def _onTimeout(self):
        """时间到点的定时器"""
        try:
            self._timer = None
            self._count += 1                        # 执行次数
            self._processPostTaskList()             # 处理任务集合list
            interval = self._target.onHeartbeat()   # 上层函数的心跳
            if interval is not None:
                self._interval = interval
        except:
            self._interval = 1
            ftlog.error("Heartbeat._onTimeout")
        self._scheduleTimer()

    def _scheduleTimer(self):
        """设置特定时间的定时器"""
        if self._state == Heartbeat.ST_START:
            interval = 0 if self._postTaskList else self._interval
            self._timer = FTLoopTimer(interval, 0, self._onTimeout)
            self._timer.start()

    def _processPostTaskList(self):
        """处理任务函数"""
        taskList = self._postTaskList
        self._postTaskList = []
        for task in taskList:
            try:
                task()
            except:
                ftlog.error("task=", task)
Example #7
0
class TaskBase(object):

    def __init__(self, player, taskConf, taskSystem, taskData=None):
        self.player = player
        self.userId = player.userId
        self.taskId = taskConf["taskId"]
        self.taskConfig = taskConf

        self.taskSystem = taskSystem
        self.taskData = taskData or self._getInitMainData()
        self.taskInterval = taskConf["timeLong"]
        self.helpProgress = taskData.get("helpProgress", 0) if taskData else 0
        self.waitTime = taskData.get("waitTime", 0) if taskData else taskConf["waitTime"]
        self.taskActivateTimer = None       # 任务激活
        self.sendTaskInfoTimer = None       # 发送任务信息
        self.updateTaskInfoTimer = None     # 刷新任务进度
        self.userEndTaskTimer = None        # 任务结束倒计时
        self.isSendMsg = False
        self.receiveRewards = None
        self.catchBetNum = taskData.get("catchBetNum", 0) if taskData else 0
        self._reload()

    def clear(self):
        """清理定时器"""
        if self.taskActivateTimer:
            self.taskActivateTimer.cancel()
        if self.sendTaskInfoTimer:
            self.sendTaskInfoTimer.cancel()
        if self.updateTaskInfoTimer:
            self.updateTaskInfoTimer.cancel()
        if self.userEndTaskTimer:
            self.userEndTaskTimer.cancel()

    def _reload(self):
        self.recordStartTime = 0
        if not self.isTaskOver():
            if self.taskData.get("type", 0) and self.taskData.get("type", 0) != self.taskConfig["type"]:
                self.taskData = self._getInitMainData()
                return
            if self.taskConfig["target"] != self.taskData["target"]:        # 未完成的任务任务目标发生变化后重置存档
                self.taskData = self._getInitMainData()
                return
            if self.taskConfig["targetNum"] != self.taskData["targetNum"] and self.taskData.get("type", 0) not in [0, TaskType.HoldCoin]:
                self.taskData = self._getInitMainData()
                return

    def _getInitMainData(self):
        """获取初始化主线数据"""
        targetNum = self.taskConfig["targetNum"]
        if util.isNewbieRoom(self.player.table.typeName) and self.taskConfig["type"] == TaskType.HoldCoin:
            targetNum += self.player.allChip // 100 * 100
        return {
            "type": self.taskConfig["type"],
            "progress": 0,
            "state": TaskState.NotOpen,
            "failTime": 0,
            "targetNum": targetNum,
            "target": self.taskConfig["target"]
        }

    def _addProgress(self, value, isMe, progress=0, isTargetAddition=False):
        """增加任务进度"""
        assert (self.taskData)
        if value == 0 or self.taskData["state"] != TaskState.Update:                            # value
            return
        if ftlog.is_debug():
            ftlog.debug("_addProgress1", "value =", value, "isMe =", isMe, "progress =", progress, "isTargetAddition =", isTargetAddition, "helpProgress =", self.helpProgress)
        if isMe:
            value *= self.taskSystem.taskExpedite
            progress_ = min(self.taskData["progress"] + value, 999999999999)
            self._updateProgress(progress_, isMe)
        else:
            if progress > 0 or isTargetAddition:
                if progress:
                    self.helpProgress += progress
                else:
                    self.helpProgress += value
                progress_ = min(self.taskData["progress"] + value, 999999999999)
                self._updateProgress(progress_, isMe)
                if ftlog.is_debug():
                    ftlog.debug("_addProgress2", "helpProgress =", self.helpProgress, "totalProgress =", progress_)

    def _updateProgress(self, value, isMe):
        """
        更新任务进度
        """
        if self.taskData["progress"] != value:
            self.taskData["progress"] = value
            self.taskData["time"] = int(time.time())
            if self._isComplete():
                self.taskEnd()
            else:
                if isMe:
                    self.sendUserTaskInfo()
                else:                               # 好友助力任务进度延迟刷新
                    self.isSendMsg = True

    def _receiveTaskReward(self):
        """领取奖励"""
        assert (self.taskData)
        if self.isTaskOver():  # 已领取
            return 1, None
        targetCount = self.taskData["targetNum"]
        if self.taskData["progress"] < targetCount:  # 未达成
            return 1, None
        _rewards = self._getDropItems()
        if not _rewards:
            _rewards = self.taskConfig["rewards"]     # 普通奖励
        if _rewards and util.isChestRewardId(_rewards[0]["name"]):      # 宝箱奖励(新手宝箱奖励固定)
            if util.isNewbieRoom(self.player.table.typeName):
                _rewards = [
                    {"name": 101, "count": 500},
                    {"name": 1145, "count": 1},
                    {"name": 1149, "count": 1}
                    # {"name": 1137, "count": 3}
                ]
            else:
                from newfish.entity.chest import chest_system
                _rewards = chest_system.getChestRewards(self.userId, _rewards[0]["name"])

        rewards = []
        for _, reward in enumerate(_rewards):
            if reward["name"] <= 0:
                continue
            rwDict = {}
            rwDict["name"] = reward["name"]
            rwDict["count"] = reward["count"]
            rewards.append(rwDict)

        code = 0
        self.taskData["state"] = TaskState.Success  # 改变状态
        if rewards:
            code = util.addRewards(self.userId, rewards, "BI_NFISH_TABLE_TASK_REWARDS", int(self.taskId))
        return code, rewards

    def _getDropItems(self):
        """获取掉落的道具"""
        realRewards = []
        rewards = self.taskConfig["rewards"]
        for reward in rewards:
            dropId = reward["name"]
            dropConf = config.getDropConf(dropId)
            if dropConf:
                _, rds = drop_system.getDropItem(dropId)
                realRewards.append(rds)
        return realRewards

    def _getCatchFishCoin(self, event, target=0):
        """获取捕鱼的金币"""
        totalCoin = 0  # 总金币数不包括招财珠
        gunSkinMul = event.gunSkinMul
        fishCoins = {}
        outCoinsNum = 0
        for gainMap in event.gain:
            fishType = int(gainMap["fishType"])
            itemId = gainMap["itemId"]
            itemCount = gainMap["count"] / gunSkinMul
            if itemId == config.CHIP_KINDID:  # 金币
                totalCoin += itemCount
                if fishType // 1000 == 14:
                    fishType = fishType % 14000 + 11000
                fishCoins[str(fishType)] = fishCoins.setdefault(str(fishType), 0) + itemCount
                if target and itemCount >= target:
                    outCoinsNum += 1
        return totalCoin, fishCoins, outCoinsNum

    def dealCatchEvent(self, event, tableMultiple, coinAddition, playersNum):
        """
        处理捕鱼事件
        :param event: 捕获事件详情
        :param tableMultiple: 房间倍率
        :param coinAddition: 当处于累计捕获金币任务时的进度加成
        :param playersNum: 当前桌内玩家数量
        :return:
        """
        if self.taskConfig["type"] in [TaskType.UseSkillNum, TaskType.ComboNum]:
           return
        if self.taskData["state"] != TaskState.Update:
            return
        isMe = event.userId == self.userId
        fishTypes = event.fishTypes  # 鱼种类
        wpId = event.wpId
        gunSkinMul = event.gunSkinMul
        target = self.taskConfig["target"]
        progress = 0
        isTargetAddition = False
        if self.taskConfig["type"] in [TaskType.CatchFishCoin, TaskType.UseSkillCatchFishCoin]:
            progress = coinAddition
        elif self.taskConfig["type"] in [TaskType.CatchFishNum, TaskType.CatchBossNum,
                                         TaskType.FishNumCoinHigh, TaskType.BetFishNum,
                                         TaskType.CatchRainbowFishNum, TaskType.CatchRedPacketFishNum]:
            friendHelpPlayerMultiple = config.getCommonValueByKey("friendHelpPlayerMultiple", {}).get(str(playersNum - 1), 0)
            probb = (friendHelpPlayerMultiple * (self.taskData["targetNum"] + 5) / self.taskData["targetNum"]) * 10000
            randInt = random.randint(1, 10000)
            if randInt <= probb:
                isTargetAddition = True
        if self.taskConfig["type"] == TaskType.CatchFishCoin:  # 1捕获xx鱼达金币数
            totalCoin, fishCoins, _ = self._getCatchFishCoin(event)
            if target:
                self._addProgress(fishCoins.get(str(target), 0), isMe, progress=progress, isTargetAddition=isTargetAddition)
            else:
                self._addProgress(totalCoin, isMe, progress=progress, isTargetAddition=isTargetAddition)

        elif self.taskConfig["type"] == TaskType.CatchFishNum:  # 2捕获鱼个数
            if target:
                betTarget = 14000 + int(target) % 11000
                fishNum = fishTypes.count(target) + fishTypes.count(betTarget)
                self._addProgress(fishNum, isMe, progress=progress, isTargetAddition=isTargetAddition)
            else:
                self._addProgress(len(fishTypes), isMe, progress=progress, isTargetAddition=isTargetAddition)
            return
        elif self.taskConfig["type"] == TaskType.CatchBossNum:  # 3捕获boss个数
            bossNum = 0  # boss个数
            for fishType in fishTypes:
                fishConf = config.getFishConf(fishType, self.taskSystem.table.typeName, tableMultiple)
                if fishConf["type"] in config.BOSS_FISH_TYPE:
                    bossNum += 1
            self._addProgress(bossNum, isMe, progress=progress, isTargetAddition=isTargetAddition)
            return

        elif self.taskConfig["type"] == TaskType.FishNumCoinHigh:  # 4多少金币以上的鱼
            _, _, num = self._getCatchFishCoin(event, target)
            self._addProgress(num, isMe, progress=progress, isTargetAddition=isTargetAddition)
            return
        elif self.taskConfig["type"] == TaskType.BetFishNum: # 5--多少只倍率鱼
            betFishMap = {}
            for gainMap in event.gain:
                fishConf = config.getFishConf(gainMap["fishType"], self.taskSystem.table.typeName, tableMultiple)
                itemId = gainMap["itemId"]
                itemCount = gainMap["count"]

                if fishConf["type"] in config.MULTIPLE_FISH_TYPE:
                    if itemId == config.CHIP_KINDID:  # 金币
                        bet = itemCount / fishConf["score"] / tableMultiple / gunSkinMul
                        betFishMap[str(bet)] = betFishMap.get(str(bet), 0) + 1
            betNum = 0
            for bet in betFishMap:
                if int(bet) >= target:
                    betNum += betFishMap[bet]
            self._addProgress(betNum, isMe, progress=progress, isTargetAddition=isTargetAddition)

        elif self.taskConfig["type"] == TaskType.UseSkillCatchFishNum:  # 8 使用技能捕获鱼数
            wpType = util.getWeaponType(wpId)
            if wpType in [config.SKILL_WEAPON_TYPE,
                          config.RB_FIRE_WEAPON_TYPE,
                          config.RB_BOMB_WEAPON_TYPE]:
                if target:
                    self._addProgress(fishTypes.count(target), isMe, progress=progress, isTargetAddition=isTargetAddition)
                else:
                    self._addProgress(len(fishTypes), isMe, progress=progress, isTargetAddition=isTargetAddition)

        elif self.taskConfig["type"] == TaskType.UseSkillCatchFishCoin:  # 9 使用技能捕获XX金币类型的鱼数
            totalCoin, fishCoins, _ = self._getCatchFishCoin(event)  # 总金币数不包括招财珠
            wpType = util.getWeaponType(wpId)
            if wpType in [config.SKILL_WEAPON_TYPE,
                          config.RB_FIRE_WEAPON_TYPE,
                          config.RB_BOMB_WEAPON_TYPE]:
                if target:
                    self._addProgress(fishCoins.get(str(target), 0), isMe, progress=progress, isTargetAddition=isTargetAddition)
                else:
                    self._addProgress(totalCoin, isMe, progress=progress, isTargetAddition=isTargetAddition)
        elif self.taskConfig["type"] == TaskType.CatchFishNumByOneFire:  # 10 1网捕获多少鱼
            if len(fishTypes) >= target:
                value = 1
                if not isMe:
                    return
                self._addProgress(value, isMe, progress=progress, isTargetAddition=isTargetAddition)
        elif self.taskConfig["type"] == TaskType.CatchRainbowFishNum:   # 捕获彩虹鱼
            rainbowNum = 0
            for fishType in fishTypes:
                fishConf = config.getFishConf(fishType, self.taskSystem.table.typeName, tableMultiple)
                if fishConf["type"] in config.RAINBOW_FISH_TYPE:
                    rainbowNum += 1
            self._addProgress(rainbowNum, isMe, progress=progress, isTargetAddition=isTargetAddition)
        elif self.taskConfig["type"] == TaskType.CatchRedPacketFishNum: # 捕获红包券鱼
            redPacketNum = 0
            for fishType in fishTypes:
                fishConf = config.getFishConf(fishType, self.taskSystem.table.typeName, tableMultiple)
                if fishConf["type"] == 4:
                    redPacketNum += 1
            self._addProgress(redPacketNum, isMe, progress=progress, isTargetAddition=isTargetAddition)

    def dealUseSkillEvent(self, event):
        """使用技能事件"""
        if self.taskData["state"] != TaskState.Update:
            return
        if self.taskConfig["type"] == TaskType.UseSkillNum:
            target = self.taskConfig["target"]
            if target == 0:
                self._addProgress(1, event.userId == self.userId)
            else:
                if event.skillId == target:
                    self._addProgress(1, event.userId == self.userId)

    def dealUseSkillItem(self, event):
        """使用技能道具"""
        if self.taskData["state"] != TaskState.Update:
            return
        if self.taskConfig["type"] == TaskType.UserSkillItem:
            self._addProgress(1, event.userId == self.userId)

    def dealGetPearl(self, pearl):
        """获得珍珠"""
        if self.taskData["state"] != TaskState.Update:
            return
        if self.taskConfig["type"] == TaskType.CatchPearlNum:
            self._addProgress(pearl, True)

    def dealCommboEvent(self, event):
        """处理连击事件"""
        if self.taskData["state"] != TaskState.Update:
            return
        if self.taskConfig["type"] == TaskType.ComboNum:
            if event.comboNum >= self.taskConfig["target"]:
                self._addProgress(1, event.userId == self.userId)

    def refreshHoldCoin(self, coin):
        """刷新持有金币数量"""
        if self.taskData["state"] != TaskState.Update:
            return
        if ftlog.is_debug():
            ftlog.debug("refreshHoldCoin", self.userId, coin)
        if self.taskConfig["type"] == TaskType.HoldCoin:
            self._updateProgress(coin, True)

    def refreshLevel(self, level):
        """刷新等级"""
        if self.taskData["state"] != TaskState.Update:
            return
        ftlog.debug("refreshLevel", self.userId, level)
        if self.taskConfig["type"] == TaskType.UpgradeLevel:
            self._updateProgress(level, True)

    def getTaskId(self):
        """
        获取任务Id
        """
        return self.taskId

    def taskStart(self, interval=0, delay=0):
        """
        任务开始
        """
        if ftlog.is_debug():
            ftlog.debug("taskBase_______taskStart", self.taskId)

        if self.taskActivateTimer:
            self.taskActivateTimer.cancel()
        if delay > 0:
            taskActivateInterval = max(interval, Task_Delay_Ready_Time)
            self.taskActivateTimer = FTLoopTimer(taskActivateInterval, 0, self._taskActivate, delay)
            self.taskActivateTimer.start()
        elif self.taskConfig["timeLong"] > 0:
            if util.isNewbieRoom(self.player.table.typeName) and self.taskSystem.taskModel == TaskModel.Main:
                time_ = Task_Red_Ready_Time
            else:
                time_ = Task_Normal_Ready_Time
            taskActivateInterval = max(interval, time_)
            taskInfoInterval = max(interval - time_, 0)
            self.delaySendUserTaskInfo(taskInfoInterval)
            self.taskActivateTimer = FTLoopTimer(taskActivateInterval, 0, self._taskActivate)
            self.taskActivateTimer.start()
        else:
            self.taskActivateTimer = FTLoopTimer(interval, 0, self._taskActivate)
            self.taskActivateTimer.start()
        from newfish.game import TGFish
        from newfish.entity.event import TableTaskStartEvent
        event = TableTaskStartEvent(self.userId, FISH_GAMEID, self.taskSystem.table.tableId, self.getTaskId())
        TGFish.getEventBus().publishEvent(event)

    def _taskActivate(self, delay=0):
        """任务激活"""
        # 未完成前3个中期目标时不触发渔场任务.
        if self.taskSystem.needCheckMidTermTargetStates:
            if not self.taskSystem.top3MidTermTargetFinished:
                self.taskSystem.top3MidTermTargetFinished = True
                for taskClass in self.player.achieveSystem.holdAssetTasks:
                    if taskClass.taskId < 1003 or (taskClass.taskId == 1003 and not taskClass.isComplete()):
                        self.taskSystem.top3MidTermTargetFinished = False
                        break
            if not self.taskSystem.top3MidTermTargetFinished:
                if self.taskActivateTimer:
                    self.taskActivateTimer.cancel()
                self.taskActivateTimer = FTLoopTimer(60, 0, self._taskActivate)
                self.taskActivateTimer.start()
                if ftlog.is_debug():
                    ftlog.debug("_taskActivate, need delay !", self.player.userId, self.taskId)
                return
        if ftlog.is_debug():
            ftlog.debug("_taskActivate->", delay, self.waitTime)
        self.taskData["state"] = TaskState.Start
        if delay > 0:
            self.taskData["state"] = TaskState.Update
            self.taskInterval = delay
        self.recordStartTime = int(time.time())
        self.clear()
        if self.taskInterval != 0:
            self.userEndTaskTimer = FTLoopTimer(self.taskInterval, 0, self.taskEnd)
            self.userEndTaskTimer.start()
        if self.taskSystem.isFriendRoom():
            self.updateTaskInfoTimer = FTLoopTimer(1, -1, self.sendTimerUpdate)
            self.updateTaskInfoTimer.start()
        self.sendUserTaskInfo()
        self.taskData["state"] = TaskState.Update

    def getTaskFailTime(self):
        """
        当前任务已经失败多少次
        """
        return self.taskData["failTime"]

    def getTaskInfo(self):
        """
        获取返回给客户端的任务数据
        """
        progress = min(self.taskData["progress"], self.taskData["targetNum"])
        progress = int(progress) if progress - int(progress) == 0 else progress
        meProgress = progress - self.helpProgress
        meProgress = int(meProgress) if meProgress - int(meProgress) == 0 else meProgress
        taskInfo = {}
        taskInfo["taskId"] = self.taskConfig["taskId"]
        taskInfo["isLimitTime"] = self.isLimitTime()
        taskInfo["shareMode"] = 1   # 1 if not self.isLimitTime() or util.isLocationLimit(self.userId) else 0
        taskInfo["progress"] = [int(progress), int(meProgress), self.taskData["targetNum"]]
        taskInfo["state"] = self.taskData["state"]
        taskInfodescId = self.taskConfig["desc"]
        taskInfo["desc"] = config.getMultiLangTextConf(str(taskInfodescId), lang=util.getLanguage(self.userId))
        taskInfo["reward"] = self.taskConfig["rewards"]
        if self.receiveRewards:
            taskInfo["realReward"] = self.receiveRewards
        taskInfo["target"] = self.taskConfig["target"]
        taskInfo["suggestTarget"] = self.taskConfig["suggestTarget"]
        timeLeft = self.recordStartTime + self.taskInterval - int(time.time())
        taskInfo["timeLeft"] = timeLeft if timeLeft >= 0 else self.taskInterval
        taskInfo["timeLong"] = self.taskInterval
        taskInfo["failTimeConf"] = self.taskConfig["failTime"]
        taskInfo["failTime"] = self.taskData["failTime"]        # 失败次数
        taskInfo["isNextLimitTime"] = self.taskSystem.isNextLimitTime(self.getTaskId())
        taskInfo["model"] = TaskModel.Red if util.isNewbieRoom(self.player.table.typeName) and self.taskSystem.taskModel == TaskModel.Main else self.taskSystem.taskModel
        taskInfo["type"] = self.taskConfig["type"]
        if self.taskSystem.taskModel in [TaskModel.Red, TaskModel.Main]:
            taskInfo["index"] = self.taskSystem.allMainTaskIds.index(self.taskId)
        return taskInfo

    def getTaskData(self):
        """
        获取存储的任务数据
        """
        saveData = {}
        saveData["type"] = self.taskConfig["type"]
        saveData["taskId"] = self.taskId
        saveData["progress"] = self.taskData["progress"]
        saveData["helpProgress"] = self.helpProgress
        saveData["state"] = self.taskData["state"]
        saveData["target"] = self.taskConfig["target"]
        saveData["targetNum"] = self.taskData["targetNum"]
        saveData["failTime"] = self.taskData["failTime"]
        saveData["waitTime"] = int(self.taskActivateTimer.getTimeOut()) if self.taskActivateTimer else 0
        if self.catchBetNum:
            saveData["catchBetNum"] = self.catchBetNum
        return saveData

    def taskEnd(self):
        """
        任务结束
        """
        if ftlog.is_debug():
            ftlog.debug("taskBase_______taskEnd", self.taskId, self.userId, self.taskData["state"])
        beforeState = self.taskData["state"]
        self.clear()
        if beforeState == TaskState.Update:
            isComplete = self._isComplete()
            if isComplete:
                _, rewards = self._receiveTaskReward()
                self.receiveRewards = rewards
                self.taskSystem.saveRedState()  # 保存红包
            else:
                self.taskSystem.taskExpedite = 1
                self.taskData["state"] = TaskState.Fail
                if self.isLimitTime():
                    self.taskData["failTime"] += 1
            from newfish.game import TGFish
            from newfish.entity.event import TableTaskEndEvent
            event = TableTaskEndEvent(self.userId, FISH_GAMEID, self.taskSystem.table.tableId, self.getTaskId(),
                                      isComplete,  self.isLimitTime(), self.receiveRewards)
            TGFish.getEventBus().publishEvent(event)
            # 上传当前完成任务进度
            if self.taskSystem.taskModel == TaskModel.Main and self.taskSystem.isRedRoom():
                ftlog.debug("BI_NFISH_THOUSAND_RED_TASK_PROGRESS--->000", self.getTaskId())
                bireport.reportGameEvent("BI_NFISH_GE_THOUSAND_RED_TASK", self.userId,FISH_GAMEID,
                                self.taskSystem.table.roomId, self.taskSystem.table.tableId,
                                self.getTaskId(), int(isComplete), 0,  0, [], config.CLIENTID_ROBOT)
                # 新手引导步骤完成
                if self.taskConfig["type"] == TaskType.UpgradeLevel:
                    util.addGuideStep(self.player.userId, config.NEWBIE_GUIDE_GUN_UP, self.player.clientId)
        self.sendUserTaskInfo()

    def sendTimerUpdate(self):
        """
        任务进度刷新
        """
        if self.taskConfig["type"] == TaskType.HoldCoin:
            self.taskSystem.refreshHoldCoin(self.player.holdCoin)
        elif self.taskConfig["type"] == TaskType.UpgradeLevel:
            gunLevel = self.player.gunLevel - 2100
            self.taskSystem.refreshLevel(gunLevel)
        if self.isSendMsg:
            self.sendUserTaskInfo()

    def delaySendUserTaskInfo(self, interval=0):
        """
        延时发送任务信息
        """
        if self.sendTaskInfoTimer:
            self.sendTaskInfoTimer.cancel()
        self.sendTaskInfoTimer = FTLoopTimer(interval, 0, self.sendUserTaskInfo)
        self.sendTaskInfoTimer.start()

    def sendUserTaskInfo(self):
        """
        发送任务信息
        """
        self.isSendMsg = False
        message = MsgPack()
        message.setCmd("table_task_info")
        message.setResult("gameId", FISH_GAMEID)
        message.setResult("userId", self.userId)
        taskInfo = self.getTaskInfo()
        message.setResult("taskInfo", taskInfo)
        router.sendToUser(message, self.userId)

    def _isComplete(self):
        """
        任务是否完成
        """
        assert (self.taskData)
        targetCount = self.taskData["targetNum"]
        return self.taskData["progress"] >= targetCount

    def isTaskSuccess(self):
        """
        任务成功
        """
        return self.taskData["state"] == TaskState.Success

    def getTaskConf(self):
        assert (self.taskConfig)
        return self.taskConfig

    def isTaskOver(self):
        """任务结束"""
        return self.taskData["state"] in [TaskState.Success, TaskState.Fail]

    def getTaskState(self):
        return self.taskData["state"]

    def getTaskTargets(self):
        if self.taskData["state"] != TaskState.Update:
            return []
        if self.taskConfig["type"] in [TaskType.CatchFishNum, TaskType.CatchFishCoin,
                                       TaskType.UseSkillCatchFishNum, TaskType.UseSkillCatchFishCoin]:
            if self.taskConfig["target"] != 0:
                return [self.taskConfig["target"]]
        return []

    def isLimitTime(self):
        """是否限制了时间"""
        if not self.taskConfig:
            return 0
        return 1 if self.taskConfig.get("timeLong", 0) != 0 else 0
Example #8
0
class HttpGetNdayAfterMatchs(object):
    '''N日后的比赛列表'''
    def __init__(self, interval, ndays, callback):
        self.httpContents = []
        self.callback = callback

        self.now = datetime.datetime.now()
        self._afterday = (self.now +
                          datetime.timedelta(days=ndays)).strftime('%Y-%m-%d')

        self.timer = FTLoopTimer(interval, -1, self._checkResult)
        self.timer.start()

    def __str__(self):
        return '%s:%s' % (self.now, self._afterday)

    def __repr__(self):
        return self.__str__()

    @property
    def key(self):
        return self.__str__()

    def _checkDate(self):
        return self.now.date() == datetime.datetime.now().date()

    def _checkResult(self):
        if ftlog.is_debug():
            ftlog.debug('HttpGetNdayAfterMatchs', 'now=', self.now,
                        '_afterday=', self._afterday)

        if not self._checkDate():
            ftlog.info('hallsportlottery.HttpNdayAfterMatchList',
                       '_checkDate=', self.now.date(), 'nowDate=',
                       datetime.datetime.now().date())
            if self.timer:
                self.timer.cancel()

            if self.callback:
                self.callback(self)

            return

        contents = http7Mgetschedulebydate(self._afterday)
        if contents and len(contents) > 0:
            if self.timer:
                self.timer.cancel()

            self.doContents(contents)

            if self.callback:
                self.callback(self)

    def doContents(self, contents):
        leagues = SportlotteryConf.leaguekeys()
        teams = SportlotteryConf.teamskeys()
        focusTeams = SportlotteryConf.focusTeamskeys()

        schedule = contents.get('Schedule')
        if schedule and len(schedule) > 0:
            for matchDict in schedule:
                match = matchDict['Id']
                if match and len(match) >= 4:
                    if match[1] not in leagues:
                        continue
                    if match[2] not in teams or match[3] not in teams:
                        continue
                    if match[2] not in focusTeams and match[
                            3] not in focusTeams:
                        continue

                    d = {
                        'matchId': match[0],
                        'leagueId': match[1],
                        'homeTeamId': match[2],
                        'awayTeamId': match[3],
                        'timestamp': matchDict['Date'] / 1000
                    }

                    self.httpContents.append(d)
class FishTimeMatchTable(FishMultipleTable):
    def __init__(self, room, tableId):
        super(FishTimeMatchTable, self).__init__(room, tableId)
        self.clearTableData()
        # 用户离线等待时间
        self._offlineWaitSeconds = 600
        # 用户空闲超时时间
        self._idleTimeOutSeconds = 600
        # 用户无子弹时超时时间
        self._inactiveTimeOutSeconds = 600
        # 准备倒计时
        self._readySeconds = 5
        # 排名刷新间隔
        self._loopSeconds = 5
        # 初始化定时器
        self._readyTimer = None
        self._startTimer = None
        self._loopTimer = None
        # 比赛技能
        self._matchSkills = None
        # 可用action
        self.actionMap = {
            "robot_leave": self._robotLeave,
            "catch": self._verifyCatch,
            "skill_use": self._skill_use,
            "skill_install": self._skill_install,
            "skill_replace": self._skill_replace,
            "smile": self.doTableSmilies,
            "honor_push": self._honor_push,
            "honor_replace": self._honor_replace,
            "guns_list": self._guns_list,
            "guns_pool": self._guns_pool,
            "treasure_rewards": self._getTreasureRewards,
            "item_use": self.item_use,  # 使用道具技能
        }
        self._logger = Logger()
        self._logger.add("cls=", self.__class__.__name__)
        self._logger.add("gameId", self.gameId)
        self._logger.add("roomId", room.roomId)
        self._logger.add("tableId", tableId)
        self._logger.add("matchId", room.bigmatchId)

    def clearTableData(self):
        """
        清理桌子数据和状态
        """
        # 比赛状态
        self._matchState = MatchState.DEFAULT
        # 比赛桌详情
        self._match_table_info = None
        # 比赛任务数据
        self._usersData = {}

    def clearAllTimer(self):
        """
        清理所有定时器
        """
        if self._logger.isDebug():
            self._logger.debug("clearAllTimer, tableId =", self.tableId)
        if self._readyTimer:
            self._readyTimer.cancel()
            self._readyTimer = None
        if self._startTimer:
            self._startTimer.cancel()
            self._startTimer = None
        if self._loopTimer:
            self._loopTimer.cancel()
            self._loopTimer = None
        if self.bufferFishGroup:
            self.bufferFishGroup.cancelNextGroupTimer()
        if self.multipleFishGroup:
            self.multipleFishGroup.cancelNextGroupTimer()

    def startFishGroup(self):
        """
        启动鱼阵
        """
        if self.runConfig.allNormalGroupIds:
            self.normalFishGroup = NormalFishGroup(self)
        # buffer鱼初始化
        if self.runConfig.allBufferGroupIds:
            self.bufferFishGroup = BufferFishGroup(self)
        # 随机倍率鱼初始化
        if self.runConfig.allMultipleGroupIds:
            self.multipleFishGroup = MultipleFishGroup(self)

    def createPlayer(self, table, seatIndex, clientId):
        """
        新创建Player对象
        """
        return FishTimeMatchPlayer(table, seatIndex, clientId)

    def _doTableManage(self, msg, action):
        """
        处理来自大比赛的table_manage命令
        """
        if action == "m_table_start":
            self.doMatchTableStart(msg)
        elif action == "m_table_info":
            self.doUpdateMatchTableInfo(msg)
        elif action == "m_table_update":
            self.doUpdateMatchRankInfo(msg)
        elif action == "m_table_clear":
            self.doMatchTableClear(msg)
        elif action == "m_table_over":
            self.doMatchOver(msg)
        elif action == "m_user_giveup":
            self.doUserGiveup(msg)
        else:
            super(FishTimeMatchTable, self)._doTableManage(msg, action)

    def doMatchTableStart(self, msg):
        """开始比赛桌子启动"""
        if self._logger.isDebug():
            self._logger.debug("doMatchTableStart", "msg=", msg)
        table_info = msg.getKey("params")

        self._doUpdateTableInfo(table_info)
        self._doMatchQuickStart()  # 开始
        self.bufferFishGroup and self.bufferFishGroup.initGroup(
            self._match_table_info["tableRankRatio"])
        self.multipleFishGroup and self.multipleFishGroup.initGroup(
            self._match_table_info["tableRankRatio"])
        if self._logger.isDebug():
            self._logger.debug("doMatchTableStart, tableId =", self.tableId,
                               "readyTimer =", self._readyTimer)
        if not self._readyTimer:
            self._matchState = MatchState.READY
            self._readyTimer = FTLoopTimer(self._readySeconds, 0,
                                           self._matchStartTime)
            self._readyTimer.start()

        if self._logger.isDebug():
            self._logger.debug("doMatchTableStart OK", "msg=", msg)

    def doUpdateMatchTableInfo(self, msg):
        """更新比赛桌子信息"""
        if self._logger.isDebug():
            self._logger.debug("doUpdateMatchTableInfo", "msg=", msg)
        table_info = msg.getKey("params")
        self._doUpdateTableInfo(table_info)

    def doUpdateMatchRankInfo(self, msg):
        """更新排行榜信息"""
        if self._logger.isDebug():
            self._logger.debug("doUpdateMatchRankInfo", "msg=", msg)
        rank_info = msg.getKey("params")
        self._doUpdateRankInfo(rank_info)

    def doMatchTableClear(self, msg):
        """清理比赛桌子"""
        if self._logger.isDebug():
            self._logger.debug("doMatchTableClear", "msg=", msg)
        params = msg.getKey("params")
        matchId = params.get("matchId", -1)
        if matchId != self.room.bigmatchId:
            self._logger.error("doMatchTableClear", "msg=", msg, "err=",
                               "DiffMatchId")
            return
        self._doMatchTableClear()

    def _doMatchTableClear(self):
        # 清理本桌玩家的在线状态
        for player in self.players:
            if player and player.userId > 0:
                self._clearPlayer(None, player.userId, player.seatId)
        self.clearTableData()
        self.clearAllTimer()

    def doMatchOver(self, msg):
        """比赛完成"""
        if self._logger.isDebug():
            self._logger.debug("doMatchOver", "msg=", msg)
        params = msg.getKey("params")
        matchId = params.get("matchId", -1)
        if matchId != self.room.bigmatchId:
            self._logger.error("doMatchOver", "msg=", msg, "err=",
                               "DiffMatchId")
            return
        self._doMatchTableClear()

    def doUserGiveup(self, msg):
        """放弃比赛"""
        if self._logger.isDebug():
            self._logger.debug("doUserGiveup", "msg=", msg)
        params = msg.getKey("params")
        userId = params.get("userId", -1)
        matchId = params.get("matchId", -1)
        if matchId != self.room.bigmatchId:
            self._logger.error("doUserGiveup", "msg=", msg, "err=",
                               "DiffMatchId")
        player = self.getPlayer(userId)
        from newfish.entity.event import MatchGiveUpEvent
        from newfish.game import TGFish
        event = MatchGiveUpEvent(userId, FISH_GAMEID, self.room.bigmatchId)
        TGFish.getEventBus().publishEvent(event)
        if player:
            self._clearPlayer(None, player.userId, player.seatId)
        if self.playersNum == 0:
            self._doMatchTableClear()

    def _doSit(self, msg, userId, seatId, clientId):
        """
        玩家操作, 尝试再当前的某个座位上坐下
        """
        ret = self._doSitDown(msg, userId, seatId, clientId)
        return ret

    def _doSitDown(self, msg, userId, seatId, clientId):
        """
        比赛牌桌只有玩家断线重连时才会触发坐下操作,既重新坐回牌桌
        """
        if seatId != 0:
            if self.seats[seatId - 1].userId == 0:
                onlinedata.removeOnlineLoc(userId, self.roomId, self.tableId)
                ftlog.warn("reconnect user is cleaned from table", "seats =",
                           self.seats)
                return False
            elif userId != self.seats[seatId - 1].userId:
                onlinedata.removeOnlineLoc(userId, self.roomId, self.tableId)
                ftlog.warn("reconnect user id is not matched", "seats =",
                           self.seats)
                return False
            else:
                ftlog.info("user reconect, userId:", userId)
                onlinedata.addOnlineLoc(userId, self.roomId, self.tableId,
                                        seatId)
                self.players[seatId - 1].offline = 1
                self.players[seatId - 1].clientId = clientId
                self.players[seatId - 1].lang = util.getLanguage(
                    userId, clientId)
                self.players[seatId - 1].refreshGunSkin()
                self._sendTableInfo(userId, seatId)
                self._updateMatchInfo(userId)
                self._updateMatchRank(userId)
                self._updateMatchTask(userId)
                self.players[seatId - 1].dealEnterTable()
                self.players[seatId - 1].enterTime = int(time.time())
                self.players[seatId - 1].offline = 0
                from newfish.game import TGFish
                event = EnterTableEvent(userId, FISH_GAMEID, self.roomId,
                                        self.tableId, seatId, 1)
                TGFish.getEventBus().publishEvent(event)
                return True
        else:
            for i in range(len(self.seats)):
                if self.seats[i].userId == userId:
                    ftlog.info("lost user reconect, userId:", userId, "i =", i)
                    onlinedata.addOnlineLoc(userId, self.roomId, self.tableId,
                                            i + 1)
                    self.players[i].offline = 1
                    self.players[i].clientId = clientId
                    self.players[i].lang = util.getLanguage(userId, clientId)
                    self.players[i].refreshGunSkin()
                    self._sendTableInfo(userId, i + 1)
                    self._updateMatchInfo(userId)
                    self._updateMatchRank(userId)
                    self._updateMatchTask(userId)
                    self.players[i].dealEnterTable()
                    self.players[i].enterTime = int(time.time())
                    self.players[i].offline = 0
                    from newfish.game import TGFish
                    event = EnterTableEvent(userId, FISH_GAMEID, self.roomId,
                                            self.tableId, seatId, 1)
                    TGFish.getEventBus().publishEvent(event)
                    return True

    def _doUpdateTableInfo(self, tableInfo):
        """比赛参数|获取比赛技能"""
        self._match_table_info = tableInfo
        self._matchSkills = self._match_table_info.get("skills")

    def _doUpdateRankInfo(self, rankInfo):
        """更新排名信息"""
        seats = rankInfo["seats"]
        for seat in seats:
            userId = seat["userId"]
            rank = seat["rank"]
            player = self.getPlayer(userId)
            if player:
                player.rank = rank
                self._updateMatchRank(userId)

    def _doMatchQuickStart(self):
        """比赛快速开始"""
        seats = self._match_table_info["seats"]
        for seat in seats:
            userId = seat["userId"]
            seatId = seat["seatId"]
            clientId = util.getClientId(userId)
            player = self.getPlayer(userId)
            if not player:
                self.doMatchSitDown(userId, seatId, clientId)
                player = self.getPlayer(userId)
                if player:
                    player.currentTask = [
                        "time_match", self.roomId, 1,
                        copy.deepcopy(self._match_table_info["targets"])
                    ]
                    self._usersData[userId] = {
                        "uid": userId,
                        "targets":
                        copy.deepcopy(self._match_table_info["targets"]),
                        "results": {}
                    }

    def doMatchSitDown(self, userId, seatId, clientId):
        """比赛入座"""
        self.seats[seatId - 1].userId = userId
        self.players[seatId - 1] = self.createPlayer(self, seatId - 1,
                                                     clientId)  # 创建玩家
        self.players[seatId -
                     1].clip = self._match_table_info["bullet"]  # 玩家子弹
        onlinedata.addOnlineLoc(userId, self.roomId, self.tableId, seatId)
        self._sendTableInfo(userId, seatId)  # 发送table_info
        self._broadcastPlayerSit(userId, seatId)  # 广播玩家坐下
        self.players[seatId - 1].enterTime = int(time.time())
        self.players[seatId - 1].offline = 0
        from newfish.game import TGFish
        event = EnterTableEvent(userId, FISH_GAMEID, self.roomId, self.tableId,
                                seatId)
        TGFish.getEventBus().publishEvent(event)
        bireport.reportGameEvent("BI_NFISH_TABLE_ENTER", userId, FISH_GAMEID,
                                 self.roomId, self.tableId,
                                 self.players[seatId - 1].level, 0, 0, 0, [],
                                 clientId)

    def getCostBullet(self, gunId, gunLevel, wpConf, clientId):
        """获取消耗的子弹"""
        costBullet = 1
        return costBullet

    def _broadcastPlayerLeave(self, userId, seatId):
        """广播玩家离开"""
        msg = MsgPack()
        msg.setCmd("leave")
        msg.setResult("gameId", FISH_GAMEID)
        msg.setResult("userId", userId)
        msg.setResult("seatId", seatId)
        GameMsg.sendMsg(msg, self.getBroadcastUids(userId))

    def _matchStartTime(self):
        """
        比赛开始,设置比赛结束时间点和更新排名机制
        """
        self._matchState = MatchState.START
        for player in self.players:
            if player:
                self._updateMatchInfo(player.userId)
                self._updateMatchRank(player.userId)
        if self._logger.isDebug():
            self._logger.debug("_matchStartTime, tableId =", self.tableId,
                               "startTimer =", self._startTimer, "loopTimer =",
                               self._loopTimer)
        self._startTimer = FTLoopTimer(self.runConfig.playingTime, 0,
                                       self._matchTimeUp)
        self._startTimer.start()
        self._loopTimer = FTLoopTimer(self._loopSeconds, -1,
                                      self._matchUpdateRank)
        self._loopTimer.start()

    def _matchTimeUp(self):
        """
        比赛结束,处理结果,清理玩家
        """
        if self._loopTimer:
            self._loopTimer.cancel()
        self._matchState = MatchState.END
        FTLoopTimer(0.5, 0, self.room.matchPlugin.doWinLose, self.room,
                    self).start()
        for player in self.players:
            if player and player.userId:
                player.clearTimer()

    def _matchUpdateRank(self):
        """
        定时更新排名机制
        """
        if self.getRobotUserCount() == self.playersNum:
            for player in self.players:
                if player and player.userId <= config.ROBOT_MAX_USER_ID:
                    maxScore = int(self._match_table_info["bullet"] * 1.2)
                    score = random.randint(maxScore / 2, maxScore)
                    gainChip = score / (self.runConfig.playingTime / 5)
                    player.catchBudget(gainChip, 0, [])
        self.room.matchPlugin.doUpdate(self.room, self)
        if int(self._startTimer.getTimeOut()) <= self._loopSeconds:
            self._loopTimer.cancel()

    def _updateMatchInfo(self, userId):
        """更新比赛信息"""
        player = self.getPlayer(userId)
        if player and player.userId:
            msg = MsgPack()
            msg.setCmd("m_info")
            msg.setResult("gameId", FISH_GAMEID)
            msg.setResult("roomId", self.roomId)
            msg.setResult("tableId", self.tableId)
            msg.setResult("seatId", player.seatId)
            msg.setResult("userId", player.userId)
            msg.setResult("timeLong", self.runConfig.playingTime)
            msg.setResult(
                "timeLeft",
                int(self._startTimer.getTimeOut())
                if self._startTimer else self.runConfig.playingTime)
            msg.setResult("targets", self._match_table_info["targets"])
            GameMsg.sendMsg(msg, player.userId)

    def _updateMatchRank(self, userId):
        """比赛排名"""
        player = self.getPlayer(userId)
        if player and player.userId and self._match_table_info:
            msg = MsgPack()
            msg.setCmd("m_update")
            msg.setResult("gameId", FISH_GAMEID)
            msg.setResult("roomId", self.roomId)
            msg.setResult("tableId", self.tableId)
            msg.setResult("seatId", player.seatId)
            msg.setResult("userId", player.userId)
            msg.setResult("rank",
                          [player.rank, self._match_table_info["playerCount"]])
            GameMsg.sendMsg(msg, player.userId)

    def _updateMatchTask(self, userId):
        """更新比赛任务"""
        player = self.getPlayer(userId)
        usersData = self._usersData.get(userId, {})
        if player and usersData and usersData["results"]:
            msg = MsgPack()
            msg.setCmd("m_task")
            msg.setResult("gameId", FISH_GAMEID)
            msg.setResult("roomId", self.roomId)
            msg.setResult("tableId", self.tableId)
            msg.setResult("seatId", player.seatId)
            msg.setResult("userId", player.userId)
            msg.setResult("targets", usersData["results"])
            GameMsg.sendMsg(msg, player.userId)

    def getProbbCoefficient(self, player, fishInfo):
        """概率基数"""
        if fishInfo["type"] in [3, 21] or fishInfo["multiple"] > 1:
            j1 = player.matchLuckyValue / 7500.0 + 1.0 / 3
            c = self._match_table_info["realPlayerCount"]
            k = float(4 * c) / (3 * c - 1)
            b = 1 - float(2 * c) / (3 * c - 1)
            j2 = 1  #k * min(player.rank, c) / c + b
            j = (j1 + j2) * 0.5
            if ftlog.is_debug():
                ftlog.debug("getProbbCoefficient", player, fishInfo,
                            "luckyValue =", player.matchLuckyValue, "rank =",
                            player.rank, "j1 =", j1, "c =", c, "k =", k, "b =",
                            b, "j2 =", j2, "j =", j)
            return j
        return 1

    def dealKillFishGain(self,
                         fId,
                         player,
                         fpMultiple,
                         gunMultiple=1,
                         bufferCoinAdd=1,
                         wpType=None,
                         extends=None,
                         gunX=1):
        """
        处理打死鱼获得的奖励
        :param fId: 被捕获鱼的ID
        :param player: 捕鱼者
        :param fpMultiple: 渔场倍率
        :param gunMultiple: 炮的倍率
        :param bufferCoinAdd: buffer加成金币系数
        :param wpType: 武器类型
        :param extends: 扩展数据
        :param gunX: 炮的倍数
        """
        gainChip = 0
        gain = []
        gainMap = {}
        fishType = self.fishMap[fId]["fishType"]
        fixedMultiple = self.fishMap[fId]["multiple"]
        fishConf = config.getMatchFishConf(fishType)
        if fishConf["score"] > 0:
            gainMap["fId"] = fId
            gainMap["fishType"] = fishType
            taskMultiple = 1
            target1 = self._match_table_info["targets"].get("target1", 0)
            target2 = self._match_table_info["targets"].get("target2", 0)
            multipleTarget1 = 14000 + target1 % 11000
            multipleTarget2 = 14000 + target2 % 11000
            if fishType == target1 or fishType == target2 or \
               fishType == multipleTarget1 or fishType == multipleTarget2:
                taskMultiple = fishConf.get("multiple", 1)
            if fishConf["type"] in config.MULTIPLE_FISH_TYPE:
                multiple = self.getMultipleFishMultiple(
                    player, fishConf, fpMultiple, gunMultiple, gunX)
                gainMap["itemId"] = CHIP_KINDID
                gainMap["count"] = int(fishConf["score"] * fpMultiple * gunX *
                                       taskMultiple * multiple * bufferCoinAdd)
                gainMap["fishMultiple"] = multiple
                gainChip = int(gainMap["count"])
                gain.append(gainMap)
            else:
                gainMap["itemId"] = CHIP_KINDID
                gainMap["count"] = int(fishConf["score"] * fpMultiple * gunX *
                                       taskMultiple * fixedMultiple *
                                       bufferCoinAdd)
                gainChip = int(gainMap["count"])
                gain.append(gainMap)
        else:
            if fishConf["type"] in config.BUFFER_FISH_TYPE:  # 捕获buffer鱼
                bufferId = player.getCatchBufferId(fishConf["itemId"])
                if bufferId > 0:
                    gainMap["fId"] = fId
                    gainMap["fishType"] = fishType
                    gainMap["itemId"] = bufferId
                    gainMap["count"] = 1
                gain.append(gainMap)
        return gainChip, gain, 0

    def getMultipleFishMultiple(self, player, fishConf, fpMultiple,
                                gunMultiple, gunX):
        """
        获得倍率鱼的倍率
        """
        randInt = random.randint(1, 10000)
        for multipleMap in config.getMatchMultipleFishConf(
                self.runConfig.fishPool, player.matchLuckyValue):
            probb = multipleMap["probb"]
            if probb[0] <= randInt <= probb[-1]:
                return multipleMap["multiple"]
        return 1

    def _verifyCatch(self, msg, userId, seatId):
        if self._matchState == MatchState.END:
            return
        super(FishTimeMatchTable, self)._verifyCatch(msg, userId, seatId)

    def dealCatch(self,
                  bulletId,
                  wpId,
                  player,
                  catch,
                  gain,
                  gainChip,
                  exp,
                  fpMultiple,
                  extends=None,
                  skillId=0,
                  stageId=0,
                  isFraud=False,
                  skillType=0):
        """处理捕获"""
        if self._matchState == MatchState.END:
            return
        self._retVerifyCatch(player,
                             bulletId,
                             catch,
                             gain,
                             extends,
                             skillId,
                             stageId,
                             fpMultiple,
                             isFraud=isFraud,
                             skillType=skillType)
        gainCoupon = 0
        items = []
        for gainMap in gain:
            fishConf = config.getFishConf(gainMap["fishType"], self.typeName,
                                          fpMultiple)
            if fishConf["type"] in config.BUFFER_FISH_TYPE:
                player.addOneBufferId(gainMap["itemId"])
            if fishConf["type"] in config.LOG_OUTPUT_FISH_TYPE:
                ftlog.info("dealCatch->fishType", "userId =", player.userId,
                           "fishType =", fishConf["type"], "wpId =", wpId,
                           "gainMap =", gainMap, "gainChip =", gainChip)
        player.catchBudget(gainChip, gainCoupon, items, wpId=wpId)
        self._afterCatch(bulletId,
                         wpId,
                         player,
                         catch,
                         gain,
                         gainChip,
                         fpMultiple,
                         extends,
                         skillType=skillType)

    def _afterCatch(self,
                    bulletId,
                    wpId,
                    player,
                    catch,
                    gain,
                    gainChip,
                    fpMultiple,
                    extends=None,
                    skillId=0,
                    isFraud=False,
                    skillType=0,
                    catchFishMultiple=None):
        """捕获之后"""
        fishTypes = []
        for catchMap in catch:
            if catchMap["reason"] == 0:
                fId = catchMap["fId"]
                self.setFishDied(fId)
                fishType = self.fishMap[fId]["fishType"]
                fishTypes.append(fishType)
        event = CatchEvent(player.userId, FISH_GAMEID, self.roomId,
                           self.tableId, fishTypes, wpId, gainChip, fpMultiple)
        self._dealCatchEvent(event)

    def _dealCatchEvent(self, event):
        """处理捕获事件"""
        if event.tableId == self.tableId:
            usersData = self._usersData.get(event.userId, {})
            if not usersData:
                ftlog.debug("_dealCatchEvent->invalid userId", event.userId)
                return
            fishTypes = event.fishTypes
            targets = usersData["targets"]
            target1 = targets.get("target1", 0)
            multipleTarget1 = 14000 + target1 % 11000
            target2 = targets.get("target2", 0)
            multipleTarget2 = 14000 + target2 % 11000
            if (target1 in fishTypes or multipleTarget1
                    in fishTypes) and target1 not in usersData["results"]:
                usersData["results"][target1] = 0
            if (target2 in fishTypes or multipleTarget2
                    in fishTypes) and target2 not in usersData["results"]:
                usersData["results"][target2] = 0
            if target1 in fishTypes or multipleTarget1 in fishTypes:
                score = fishTypes.count(target1) + fishTypes.count(
                    multipleTarget1)
                usersData["results"][target1] += score
            if target2 in fishTypes or multipleTarget2 in fishTypes:
                score = fishTypes.count(target2) + fishTypes.count(
                    multipleTarget2)
                usersData["results"][target2] += score
            self._updateMatchTask(event.userId)
class ShareFishGroup(object):
    """
    分享宝箱鱼群
    """
    def __init__(self, table):
        self.table = table
        self._interval = 60         # 间隔
        self._totalTime = 0
        self.checkTimer = FTLoopTimer(self._interval, -1, self._checkCondition)
        self.checkTimer.start()

    def clearTimer(self):
        if self.checkTimer:
            self.checkTimer.cancel()
            self.checkTimer = None

    def _checkCondition(self):
        """检查条件"""
        self._totalTime += self._interval
        # 所有人可见的宝箱出现时间600s
        if self._totalTime % 600 == 0:
            groupId = self._getFishGroupId()
            if groupId:
                self._addShareFishGroup(groupId)
        else:
            # 玩家个人宝箱出现时间
            for player in self.table.players:
                if player:
                    appearCount, playTime, expiresTime, state = self._getShareData(player.userId)
                    if ftlog.is_debug():
                        ftlog.debug("ShareFishGroup_checkCondition", player.userId, appearCount, playTime, expiresTime)
                    appearMinTime = 180 + 120 * appearCount
                    appearMaxTime = 240 + 120 * appearCount
                    groupId = self._getFishGroupId()
                    if (appearMinTime <= playTime * 60 <= appearMaxTime and time.time() > expiresTime and
                       state == share_system.ShareRewardState.Unavailable and groupId):
                        self._addShareFishGroup(groupId, player.userId)

    def _getShareData(self, userId):
        """获取分享宝箱鱼的数据"""
        shareClass = RandomChest(userId)
        return shareClass.shareData[share_system.INDEX_OTHER_DATA].get("appearCount", 0), \
               shareClass.shareData[share_system.INDEX_OTHER_DATA].get("playTime", 0), \
               shareClass.shareData[share_system.INDEX_OTHER_DATA].get("expiresTime", 0), \
               shareClass.shareData[share_system.INDEX_OTHER_DATA].get("state", 0)

    def addAppearCount(self, userId=None):
        """添加分享鱼出现的次数"""
        if userId:
            shareClass = RandomChest(userId)                # 随机分享宝箱
            appearCount = shareClass.shareData[share_system.INDEX_OTHER_DATA].get("appearCount", 0) + 1
            shareClass.shareData[share_system.INDEX_OTHER_DATA]["appearCount"] = appearCount
            shareClass.shareData[share_system.INDEX_OTHER_DATA]["playTime"] = 0
            shareClass.saveData()

    def _getFishGroupId(self):
        """获取鱼群Id"""
        shareFishConf = config.getShareFishConf(self.table.runConfig.fishPool)      # 获取分享宝箱鱼配置
        randInt = random.randint(1, 10000)
        for fish in shareFishConf["fishes"]:
            probb = fish["probb"]
            if probb[0] <= randInt <= probb[1]:
                fishType = fish["fishType"]
                allShareGroupIds = self.table.runConfig.allShareGroupIds
                groupId = random.choice(allShareGroupIds[fishType])
                return groupId
        return None

    def _addShareFishGroup(self, groupId, userId=None):
        """添加分享宝箱鱼群"""
        if ftlog.is_debug():
            ftlog.debug("_addShareFishGroup", groupId, userId)
        self.table.insertFishGroup(groupId, userId=userId, sendUserId=userId)
        self.addAppearCount(userId)
Example #11
0
class FishGroup(object):
    """
    鱼群对象
    """
    def __init__(self,
                 conf,
                 enterTime,
                 serverGroupId,
                 startFishId,
                 position=None,
                 gameResolution=None,
                 deadCallback=None):
        """
        :param conf: 鱼阵配置文件 {"id": "autofill_11092_21_3", "fishes": [{"fishType": 11092, "enterTime": 0.0, "exitTime": 25.0}], "totalTime": 25.0}
        :param enterTime: 该鱼群入场时间(渔场运行后的第n秒)
        :param serverGroupId: 鱼群ID(每新增一个鱼群自增加1)
        :param startFishId: 该鱼群第一条鱼的鱼ID
        :param position: 指定出现位置
        :param gameResolution: 召唤该鱼群的玩家的游戏分辨率
        :param deadCallback: 鱼群死亡回调函数
        """
        self.position = position if position else [0, 0]
        self.gameResolution = gameResolution if gameResolution else []
        self.enterTime = enterTime
        self.startFishId = startFishId
        # 鱼群文件名字
        self.id = conf.get("id")
        # 鱼群自增ID
        self.serverGroupId = serverGroupId
        # 鱼群类型
        self.type = self.id.split("_")[1] if self.id.startswith(
            "call_") else self.id.split("_")[0]
        # 鱼群存活时间
        self.totalTime = conf.get("totalTime")
        # 鱼群中的所有鱼
        self.fishes = conf.get("fishes")
        # 鱼群中鱼的数量
        self.fishCount = len(self.fishes)
        # 该鱼群的出场时间
        self.exitTime = self.enterTime + self.totalTime
        # 该鱼群最后一条鱼的鱼ID
        self.endFishId = startFishId + self.fishCount - 1
        self.maxEnterTime = self._getMaxEnterTime()
        # 该鱼群是否已被清除
        self.isClear = False
        # 鱼群被冰冻后延长的存活时间
        self.addTime = 0
        # Boss鱼群延迟出现时间
        self.extendGroupTime = 0
        # 鱼群死亡定时器
        self.deadTimer = None
        # 鱼群死亡回调
        self.deadCallback = deadCallback
        if self.deadCallback:
            self.deadTimer = FTLoopTimer(self.totalTime, 0, self.deadCallback,
                                         self)
            self.deadTimer.start()

    def clear(self):
        """
        清除定时器等数据
        """
        if self.deadTimer:
            self.deadTimer.cancel()
            self.deadTimer = None
        self.isClear = True

    def isExist(self, nowTableTime):
        """
        该鱼群在当前时刻是否存在
        """
        return self.enterTime <= nowTableTime <= self.exitTime + self.addTime

    def fishExist(self, nowTableTime, fishId):
        """
        该鱼群中是否存在某条鱼
        """
        fish = self.fishes[fishId - self.startFishId]
        enterTime = self.enterTime + fish["enterTime"]
        exitTime = self.enterTime + fish["exitTime"] + self.addTime
        return enterTime <= nowTableTime <= exitTime

    def isAlive(self, nowTableTime, table=None):
        """
        该鱼群是否存活(包含特殊鱼及已生成但即将出现的鱼群)
        """
        # 客户端特殊处理的鱼群且鱼群中鱼的数量不多时,判断鱼群是否存活看其中鱼的存活状态
        if table and self.type in SPECIAL_ALIVE_TYPE:
            for fId in xrange(self.startFishId, self.endFishId + 1):
                isOK = table.findFish(fId)
                if isOK:
                    return isOK
            return False
        # 一般鱼群,判断鱼群是否存活看鱼群的整体退出时间,因为其中鱼的数量过多,避免循环查找
        return nowTableTime < self.exitTime + self.addTime

    def isVisible(self, table, userId):
        """
        该鱼群对某玩家是否可见
        """
        # 新手任务期间玩家自己可见的鱼.
        if self.type == "share" or self.type == "newbie" or self.type == "coupon" \
                or self.id.startswith("tuition_44499") or self.id.startswith("autofill_72025"):
            for fId in xrange(self.startFishId, self.endFishId + 1):
                if fId in table.fishMap:
                    if table.fishMap[fId]["owner"] is None or table.fishMap[
                            fId]["owner"] == userId:
                        sendUsersList = table.fishMap[fId].get("sendUsersList")
                        if not sendUsersList or userId in sendUsersList:
                            return True
                        break
            return False
        return True

    def isCleared(self):
        """
        该鱼群是否已被清除
        """
        return self.isClear

    def _getMaxEnterTime(self):
        """
        获得该鱼群最后一条鱼在该鱼阵文件中的入场时间
        """
        fishEnterTimes = []
        for fish in self.fishes:
            fishEnterTimes.append(fish.get("enterTime"))
        fishEnterTimes.sort()
        return fishEnterTimes[-1]

    def getNextGroupTime(self):
        """
        获得下个鱼群的入场时间
        """
        return round(self.maxEnterTime + self.enterTime, 2)

    def desc(self):
        """
        鱼群详情
        """
        info = [
            str(self.id), "enter:",
            str(self.enterTime), "exit:",
            str(self.exitTime), "startfishId:",
            str(self.startFishId), "endFishId:",
            str(self.endFishId), "addTime:",
            str(self.addTime)
        ]
        info = " ".join(info)
        return info

    def adjust(self, addTime):
        """
        调整鱼群存活时间
        """
        self.addTime += addTime
        self.extendGroupTime += addTime
        if self.deadTimer:
            interval = self.deadTimer.getTimeOut() + self.addTime
            if interval > 0:
                self.deadTimer.reset(interval)

    def getFishExitTime(self, fishType):
        """
        获取指定一条鱼的离场时间
        """
        for fish in self.fishes:
            if fishType == fish["fishType"]:
                return fish["exitTime"]
        return 0
Example #12
0
class BoxFishGroup(SuperBossFishGroup):
    """
    宝箱怪鱼阵
    """
    def __init__(self, table):
        super(BoxFishGroup, self).__init__(table)
        self._interval = 300  # 宝箱怪出生间隔. 600 5分钟
        self._bBossFishType = 71201  # 宝箱儿子
        self._mBossFishType = 71202  # 宝箱妈妈
        self._fBossFishType = 71203  # 宝箱爸爸
        self._startTS = 0  # 宝箱怪出现的时间戳.
        self._fBossAppearTS = 0  # 宝箱爸爸出现的最晚时间戳.
        self._isBossShowTimeStage = 0  # showtime是boss出现前30秒(stage=0x1000), bBoss(0x1), mBoss(0x10), fBoss(0x100). 暂停状态(-0x1)
        self._hasBorned = []  # 已经出生的宝箱boss
        self._initConfTimer = None  # 初始化配置定时器
        self._nextTimer = None  # 下次填充鱼的时间戳
        self._autofillTimer = {}  # 自动填充的时间
        self._clearTimer = None  # 清理宝箱的定时器.
        self._group = {}  # 渔群信息
        self._initConf()

    def _initConf(self):
        if self._initConfTimer:
            self._initConfTimer.cancel()
            self._initConfTimer = None
        self.boxConf = self.table.room.roomConf.get("boxConf")
        self._cron = FTCron(self.boxConf["cronTime"])
        self._interval = self._cron.getNextLater()
        if self._interval >= 0:
            self._setTimer()  # 启动定时器
            self._initConfTimer = FTLoopTimer(self._interval + 1, 0,
                                              self._initConf)
            self._initConfTimer.start()
        else:
            ftlog.error("BoxFishGroup initConf error",
                        self._cron.getTimeList())

    def _addBossShowTimeStage(self, val):
        """添加boss展示的阶段"""
        self._isBossShowTimeStage |= val

    def _removeBossShowTimeStage(self, val):
        """移除boss展示阶段"""
        self._isBossShowTimeStage &= ~val
        if ftlog.is_debug():
            ftlog.debug("BoxFishGroup._removeBossShowTimeStage =",
                        self.table.tableId, self._isBossShowTimeStage)

    def _clearData(self, isSendMsg=True, fishType=0, isEnd=0.0):
        """
        boss出生前清理相关数据
        """
        self._isBossShowTimeStage = 0
        self._hasBorned = []
        for _timer in self._autofillTimer.values():
            if _timer:
                _timer.cancel()
                _timer = None
        self._autofillTimer = {}
        if self._clearTimer:
            self._clearTimer.cancel()
            self._clearTimer = None
        # 清理鱼阵.
        for _group in self._group.values():
            if _group and self.table.fishGroupSystem:
                self.table.deleteFishGroup(_group)
        self._group = {}
        if isEnd:
            self.addTideFishGroup(isEnd)
        if isSendMsg:
            msg = MsgPack()
            msg.setCmd("superboss_end")
            msg.setResult("gameId", config.FISH_GAMEID)
            msg.setResult("roomId", self.table.roomId)
            msg.setResult("tableId", self.table.tableId)
            msg.setResult("type", "box")  # 宝箱
            msg.setResult("fishType", fishType)  # 鱼Id
            GameMsg.sendMsg(msg, self.table.getBroadcastUids())

    def isAppear(self):
        """
        boss即将出现或已经出现
        """
        if ftlog.is_debug():
            ftlog.debug("BoxFishGroup", self.table.tableId,
                        self._isBossShowTimeStage)
        return self._isBossShowTimeStage & 0x1000 > 0 or self._isBossShowTimeStage & 0x111 > 0

    def _setTimer(self):
        """
        设置boss出现时的计时器
        """
        if self._nextTimer:
            self._nextTimer.cancel()
            self._nextTimer = None
        if self._interval >= 0:
            self._nextTimer = FTLoopTimer(self._interval, 0,
                                          self._addFishGroup)
            self._nextTimer.start()
            self.appear()
            FTLoopTimer(max(self._interval - self.boxConf["tipTime"], 0), 0,
                        self._addBossShowTimeStage, 0x1000).start()  # 提示的时间

    def _addFishGroup(self):
        """
        添加boss鱼阵
        """
        self._clearData(False)
        # 渔场内人数不满足时不出生宝箱怪.
        if self.table.playersNum < self.table.room.roomConf[
                "superBossMinSeatN"]:
            return
        self._startTS = int(time.time())  # 宝箱怪出现的时间
        self._fBossAppearTS = self._startTS + 90  # 宝箱爸爸出现的时间
        for fishType in [self._bBossFishType, self._mBossFishType]:
            self._addBoss(fishType)  # 宝箱儿子 宝箱妈妈
        # 超出boss存活时间后清理boss.
        if self.boxConf["maxAliveTime"] > 0:  # 最大的存活时长
            self._clearTimer = FTLoopTimer(self.boxConf["maxAliveTime"] + 2, 0,
                                           self._clearData, True, 0, 0.1)
            self._clearTimer.start()

    def _addBoss(self, fishType, isSysTimerCall=True):
        """
        添加宝箱boss
        """
        if self._isBossShowTimeStage == -0x1:
            self._isBossShowTimeStage = 0
        if self._autofillTimer.get(fishType):
            self._autofillTimer[fishType].cancel()
            self._autofillTimer[fishType] = None
            # 处理冰冻自动填充时机延后逻辑.
            if self._group.get(
                    fishType) and not isSysTimerCall and self._group[
                        fishType].extendGroupTime > 0:
                self._autofillTimer[fishType] = FTLoopTimer(
                    self._group[fishType].extendGroupTime, 0, self._addBoss,
                    fishType, False)
                self._autofillTimer[fishType].start()
                self._group[fishType].extendGroupTime = 0
                return
        # boss超出最大存在时间后不再出现.
        if int(time.time()) >= self._startTS + self.boxConf["maxAliveTime"]:
            if fishType == self._bBossFishType:
                self._removeBossShowTimeStage(0x1)
            elif fishType == self._mBossFishType:
                self._removeBossShowTimeStage(0x10)
            else:
                self._removeBossShowTimeStage(0x100)
            return
        self._group[fishType] = None
        # 使用出生路径.
        if fishType not in self._hasBorned:
            self._hasBorned.append(fishType)
            _bossGroupIds = self.table.runConfig.allSuperBossBornGroupIds[
                fishType]
        elif self._isBossShowTimeStage & 0x11 != 0x11:  # bBoss或mBoss只有一个存在,则使用加速鱼阵.
            _bossGroupIds = self.table.runConfig.allSuperBossFastMoveGroupIds.get(
                fishType)
            if not _bossGroupIds:
                _bossGroupIds = self.table.runConfig.allSuperBossGroupIds[
                    fishType]
        else:
            _bossGroupIds = self.table.runConfig.allSuperBossGroupIds[fishType]
        if _bossGroupIds:
            _bossGroupId = random.choice(_bossGroupIds)
            self._group[fishType] = self.table.insertFishGroup(_bossGroupId)
            if self._group[fishType]:
                self._autofillTimer[fishType] = FTLoopTimer(
                    self._group[fishType].totalTime + 1, 0, self._addBoss,
                    fishType, False)
                self._autofillTimer[fishType].start()
                if fishType == self._bBossFishType:
                    self._addBossShowTimeStage(0x1)
                elif fishType == self._mBossFishType:
                    self._addBossShowTimeStage(0x10)
                else:
                    self._addBossShowTimeStage(0x100)
                return self._group[fishType]
        ftlog.error("superboss_fish_group.BoxFishGroup, error, tableId =",
                    self.table.tableId, fishType, self._hasBorned)
        return

    def triggerCatchFishEvent(self, event):
        """
        处理捕获事件
        """
        _fishType = 0
        isBoxBossCatched = False
        if self._bBossFishType in event.fishTypes and self._group.get(
                self._bBossFishType):
            if ftlog.is_debug():
                ftlog.debug("superboss_fish_group.BoxFishGroup, tableId =",
                            self.table.tableId, "ft =", self._bBossFishType,
                            self._isBossShowTimeStage)
            isBoxBossCatched = True
            _fishType = self._bBossFishType
            self._group[self._bBossFishType] = None
            self._removeBossShowTimeStage(0x1)

        if self._mBossFishType in event.fishTypes and self._group.get(
                self._mBossFishType):
            if ftlog.is_debug():
                ftlog.debug("superboss_fish_group.BoxFishGroup, tableId =",
                            self.table.tableId, "ft =", self._mBossFishType,
                            self._isBossShowTimeStage)
            isBoxBossCatched = True
            _fishType = self._mBossFishType
            self._group[self._mBossFishType] = None
            self._removeBossShowTimeStage(0x10)

        if isBoxBossCatched:
            if self._autofillTimer.get(_fishType):
                self._autofillTimer[_fishType].cancel()
                self._autofillTimer[_fishType] = None

        if isBoxBossCatched and self._isBossShowTimeStage == 0:  # 捕获宝箱宝宝和宝箱妈妈后,如果时间充裕就出生宝箱爸爸.
            if int(time.time()) < self._fBossAppearTS:
                self._isBossShowTimeStage = -0x1
                FTLoopTimer(self.boxConf["fDelayTime"], 0, self._addBoss,
                            self._fBossFishType).start()  # 宝箱爸爸
            else:  # 时间不够则结束boss状态.
                self._clearData(isSendMsg=True, fishType=_fishType, isEnd=5.0)

        if self._fBossFishType in event.fishTypes:
            if not self._group.get(
                    self._fBossFishType):  # 宝箱爸爸被捕获时可能刚好超时,所以此时就不要再爆炸了.
                return
            stageCount = 0
            for catchMap in event.catch:
                fishInfo = self.table.fishMap[catchMap["fId"]]
                fishType = fishInfo["fishType"]
                if catchMap["reason"] == 0 and fishType == self._fBossFishType:
                    stageCount = catchMap.get("stageCount")
                    break
            self._group[self._fBossFishType] = None
            self._removeBossShowTimeStage(0x100)
            if self._autofillTimer.get(self._fBossFishType):
                self._autofillTimer[self._fBossFishType].cancel()
                self._autofillTimer[self._fBossFishType] = None
            if self._clearTimer:
                self._clearTimer.cancel()
                self._clearTimer = None
            if stageCount > 1:
                msg = MsgPack()
                msg.setCmd("superboss_explosion_info")  # 爆炸信息
                msg.setResult("gameId", config.FISH_GAMEID)
                msg.setResult("roomId", self.table.roomId)
                msg.setResult("tableId", self.table.tableId)
                explosionPos = range(1, 5)  # 选择狂暴落点索引. [1,2,3,4]
                random.shuffle(explosionPos)
                explosionPos.insert(0, 0)
                explosionPos.extend(random.sample(explosionPos[:-1], 1))
                msg.setResult("explosionPos", explosionPos[:stageCount])
                GameMsg.sendMsg(msg, self.table.getBroadcastUids())
                FTLoopTimer(
                    stageCount * self.boxConf["stageTime"] +
                    self.boxConf["endDelayTime"], 0, self._clearData, True,
                    self._fBossFishType,
                    self.boxConf["tideDelayTime"]).start()

    def addTideFishGroup(self, delayTime=0.1):
        """添加鱼潮"""
        if delayTime > 0:
            FTLoopTimer(delayTime, 0, self.leave).start()  # 鱼潮延迟出来的时间

    def dealEnterTable(self, userId):
        """
        玩家进入渔场时发送
        """
        # 当前阶段boss开始出现的时间戳.
        startTS = 0
        if self._isBossShowTimeStage & 0x111 != 0:
            startTS = self._startTS
        msg = MsgPack()
        msg.setCmd("box_info")
        msg.setResult("gameId", config.FISH_GAMEID)
        msg.setResult("roomId", self.table.roomId)
        msg.setResult("tableId", self.table.tableId)
        msg.setResult("startTS", startTS)
        GameMsg.sendMsg(msg, userId)
class CouponFishGroup(object):
    """
    奖券鱼鱼群
    """
    def __init__(self, table):
        self.table = table
        self._lastUtilAppearTime = int(time.time())  # 上一次添加所有人可见红包奖券出现的事件
        self.checkTimer = FTLoopTimer(10, -1, self._checkCondition)  # 检查条件
        self.checkTimer.start()
        self._clearUserCouponFishData()  # 清理用户奖券数据
        self._registerEvent()  # 注册事件

    def clearTimer(self):
        """清理定时器"""
        if self.checkTimer:
            self.checkTimer.cancel()
            self.checkTimer = None
        for player in self.table.players:
            if player and player.userId:
                self._clearPlayerTimer(player.userId)  # 清理玩家定时器

    def _clearUserCouponFishData(self):
        """
        初始化个人可见奖券鱼相关数据
        """
        self._playerCouponTimerDict = {}  # 玩家奖券定时器
        for player in self.table.players:
            if player and player.userId:
                self._clearPlayerTimer(player.userId)

    def _clearPlayerTimer(self, userId):
        """
        清除与player相关的定时器和鱼阵
        """
        timer = self._playerCouponTimerDict.get(userId)
        if timer:
            timer.cancel()
            self._playerCouponTimerDict.pop(userId)

    def _checkCondition(self):
        """
        定时器条件检查
        """
        self._checkUtilConditionNew()

    def _checkUtilConditionNew(self):
        """
        检查定时的奖券鱼
        """
        couponFishConf = config.getCouponFishConf(
            self.table.runConfig.fishPool)
        if not couponFishConf:
            return
        interval = time.time() - self._lastUtilAppearTime
        couponRange = couponFishConf.get("couponRange")
        if interval >= couponFishConf["maxSecond"]:
            if ftlog.is_debug():
                ftlog.debug("_checkUtilConditionNew, interval =", interval,
                            couponRange, self.table.tableId)
            randInt = random.randint(1, 10000)
            for fish in couponFishConf["fishes"]:
                probb = fish["probb"]
                if probb[0] <= randInt <= probb[1]:
                    fishType = fish["fishType"]
                    allCouponGroupIds = self.table.runConfig.allCouponGroupIds
                    groupId = random.choice(allCouponGroupIds[fishType])
                    self._addUtilCouponFishGroupNew(groupId, couponRange)
                    break

    def _addUtilCouponFishGroupNew(self, groupId, couponRange):
        """
        添加定时出生的奖券鱼
        """
        self._lastUtilAppearTime = time.time()
        if not couponRange:
            if ftlog.is_debug():
                ftlog.debug("_addUtilCouponFishGroupNew, groupId =", groupId,
                            "tableId =", self.table.tableId)
            self.table.insertFishGroup(groupId)
            return
        sendUserList = []
        for player in self.table.players:
            if player and player.userId > 0:
                totalEntityAmount = gamedata.getGameAttr(
                    player.userId, FISH_GAMEID, GameData.totalEntityAmount)
                totalEntityAmount = float(
                    totalEntityAmount) if totalEntityAmount else 0
                if couponRange[0] <= totalEntityAmount < couponRange[1]:
                    sendUserList.append(player.userId)
        if ftlog.is_debug():
            ftlog.debug("_addUtilCouponFishGroupNew, groupId =", groupId,
                        "couponRange =", couponRange, "sendUserList =",
                        sendUserList, "tableId =", self.table.tableId)
        if sendUserList:
            self.table.insertFishGroup(groupId, sendUserId=sendUserList)

    def _checkUserConditionNew(self, userId):
        """
        检查个人可见奖券鱼出现条件是否满足
        """
        userCouponFishConf = config.getUserCouponFishConf(
            self.table.runConfig.fishPool)
        groupId, interval, totalBullet = self._isAppearUserCouponFishNew(
            userId, userCouponFishConf)
        if groupId:
            timer = FTLoopTimer(interval, 0, self._addUserCouponFishGroupNew,
                                groupId, userId, totalBullet)
            timer.start()
            self._playerCouponTimerDict[userId] = timer
            if ftlog.is_debug():
                ftlog.debug("_checkUserConditionNew->userId =", userId,
                            "groupId =", groupId, "interval =", interval,
                            "totalBullet =", totalBullet, "tableId =",
                            self.table.tableId)

    def _isAppearUserCouponFishNew(self, userId, userCouponFishConf):
        """
        个人奖券鱼是否出现
        """
        groupId = None
        interval = 0
        totalBullet = 0
        if not userCouponFishConf:
            return groupId, interval, totalBullet
        catchCountDict = gamedata.getGameAttrJson(
            userId, FISH_GAMEID, GameData.catchUserCouponFishCount, {})
        catchCount = catchCountDict.get(str(self.table.runConfig.fishPool), 0)
        rechargeAmount = hallvip.userVipSystem.getUserVip(userId).vipExp // 10
        totalEntityAmount = gamedata.getGameAttr(userId, FISH_GAMEID,
                                                 GameData.totalEntityAmount)
        totalEntityAmount = float(
            totalEntityAmount) if totalEntityAmount else 0
        for conf in userCouponFishConf:
            if catchCount >= conf["limitCount"]:
                continue
            section = conf["sections"]
            if ftlog.is_debug():
                ftlog.debug("_isAppearUserCouponFishNew->tabId=",
                            self.table.tableId, "userId =", userId,
                            "groupId =", groupId, "section =", section)
            # 充值金额大于等于指定值,获得金额小于指定值
            if len(section) == 3 and rechargeAmount >= section[0] and section[
                    1] <= totalEntityAmount < section[2]:
                randInt = random.randint(1, 10000)
                for fish in conf["fishes"]:
                    probb = fish["probb"]
                    if probb[0] <= randInt <= probb[1]:
                        fishType = fish["fishType"]
                        allCouponGroupIds = self.table.runConfig.allCouponGroupIds
                        groupId = random.choice(allCouponGroupIds[fishType])
                        interval = random.randint(conf["minSecond"],
                                                  conf["maxSecond"])
                        totalBullet = conf["totalBullet"]
                break
        if ftlog.is_debug():
            ftlog.debug("_isAppearUserCouponFishNew->tabId=",
                        self.table.tableId, "userId =", userId, "groupId =",
                        groupId, "catchCountDict =", catchCountDict,
                        "rechargeAmount =", rechargeAmount,
                        "totalEntityAmount =", totalEntityAmount, "interval =",
                        interval, "totalBullet =", totalBullet)
        return groupId, interval, totalBullet

    def _addUserCouponFishGroupNew(self, groupId, userId, totalBullet):
        """
        添加个人可见的奖券鱼
        """
        player = self.table.getPlayer(userId)
        self._clearPlayerTimer(userId)
        if player:
            playerBullet = player.couponConsumeClip
            if playerBullet < totalBullet:
                timer = FTLoopTimer(10, 0, self._addUserCouponFishGroupNew,
                                    groupId, userId, totalBullet)
                timer.start()
                self._playerCouponTimerDict[userId] = timer
                if ftlog.is_debug():
                    ftlog.debug(
                        "_addUserCouponFishGroupNew, check later, tableId =",
                        self.table.tableId, "userId =", userId,
                        "playerBullet =", playerBullet, "totalBullet =",
                        totalBullet)
            else:
                player.resetCouponConsumeClip()
                if ftlog.is_debug():
                    ftlog.debug("_addUserCouponFishGroupNew, tabId =",
                                self.table.tableId, "userId =", userId,
                                "groupId =", groupId)
                self.table.insertFishGroup(groupId,
                                           userId=userId,
                                           sendUserId=userId)
                self._checkUserConditionNew(userId)

    def _dealEnterTable(self, event):
        """
        处理进入事件
        """
        if event.tableId == self.table.tableId and not event.reconnect:
            self._checkUserConditionNew(event.userId)

    def _dealLeaveTable(self, event):
        """
        处理离开事件
        """
        if event.tableId == self.table.tableId:
            self._clearPlayerTimer(event.userId)

    def _registerEvent(self):
        """
        注册监听事件
        """
        from newfish.game import TGFish
        TGFish.getEventBus().subscribe(EnterTableEvent, self._dealEnterTable)
        TGFish.getEventBus().subscribe(LeaveTableEvent, self._dealLeaveTable)
class QueenFishGroup(SuperBossFishGroup):
    """
    龙女王鱼阵
    """
    def __init__(self, table):
        super(QueenFishGroup, self).__init__(table)
        self._interval = 300  # boss出生间隔
        self._maskFishType = 74215  # 女王保护罩
        self._fishType = 74207  # 龙女王
        self._idx = 0  # 阶段boss的索引          3、4、5、1、2
        self._startTS = 0  # boss出现的时间戳
        self._nextTimer = None  # 下一个boss出生的定时器
        self._isBossShowTimeStage = 0  # showtime是boss出现前30秒 有保护罩(stage=0x1000) 第一阶段(0x1), 受伤阶段(0x10), 没有保护罩(0x100)
        self._initConfTimer = None  # 初始化配置定时器
        self._autofillTimer = None  # boss被捕获后是否自动填充
        self._clearTimer = None  # 清理鱼阵的定时器.
        self._modQuitTimer = None  # 120s内没有打掉保护罩、龙女王退场
        self._group = None  # 鱼群对象
        self._initConf()

    def _initConf(self):
        if self._initConfTimer:
            self._initConfTimer.cancel()
            self._initConfTimer = None
        self.queenConf = self.table.room.roomConf.get("queenConf")
        self._cron = FTCron(self.queenConf["cronTime"])
        self._interval = self._cron.getNextLater()
        if self._interval > 0:
            self._setTimer()  # 启动定时器
            self._initConfTimer = FTLoopTimer(self._interval + 1, 0,
                                              self._initConf)
            self._initConfTimer.start()
        else:
            ftlog.error("QueenFishGroup initConf error",
                        self._cron.getTimeList())

    def _addBossShowTimeStage(self, val):
        """展示boss的状态"""
        self._isBossShowTimeStage |= val

    def _removeBossShowTimeStage(self, val):
        """删除boss状态"""
        self._isBossShowTimeStage &= ~val

    def _clearData(self, isSendMsg=True, fishType=0, isEnd=0.0):
        """
        boss出生前清理相关数据
        """
        self._isBossShowTimeStage = 0
        if self._autofillTimer:
            self._autofillTimer.cancel()
        self._autofillTimer = None
        if self._clearTimer:
            self._clearTimer.cancel()
        self._clearTimer = None
        if self._modQuitTimer:
            self._modQuitTimer.cancel()
        self._modQuitTimer = None
        # 清理鱼阵.
        if self._group and self.table.fishGroupSystem:
            self.table.deleteFishGroup(self._group)
        self._group = None
        if isEnd:
            self.addTideFishGroup(isEnd)
        if isSendMsg:
            msg = MsgPack()
            msg.setCmd("queen_end")
            msg.setResult("gameId", config.FISH_GAMEID)
            msg.setResult("roomId", self.table.roomId)
            msg.setResult("tableId", self.table.tableId)
            msg.setResult("type", "queen")
            msg.setResult("fishType", fishType)
            GameMsg.sendMsg(msg, self.table.getBroadcastUids())

    def isAppear(self):
        """
        boss即将出现或已经出现
        """
        return self._isBossShowTimeStage & 0x1000 > 0 or self._isBossShowTimeStage & 0x111 > 0

    def _setTimer(self):
        """
        设置boss出现时的计时器
        """
        if self._nextTimer:
            self._nextTimer.cancel()
        self._nextTimer = None
        if self._interval >= 0:
            self._nextTimer = FTLoopTimer(self._interval, 0,
                                          self._addFishGroup)  # 添加初始化鱼群
            self._nextTimer.start()
            self.appear()
            FTLoopTimer(max(self._interval - self.queenConf["tipTime"],
                            0), 0, self._addBossShowTimeStage,
                        0x1000).start()  # boss即将出现 1次

    def _addFishGroup(self):
        """
        添加boss鱼阵
        """
        self._clearData(False)  # 清理数据
        self._startTS = int(time.time())  # boss出现的时间戳
        self._addBoss()  # 添加boss
        if self.queenConf["addHurtTime"] > 0:
            self._modQuitTimer = FTLoopTimer(self.queenConf["addHurtTime"], 0,
                                             self._clearData, True,
                                             self._maskFishType,
                                             0.1)  # 2分钟后没有打掉保护罩,自动退场
            self._modQuitTimer.start()
        # 超出boss存活时间后清理boss.
        if self.queenConf["maxAliveTime"] > 0:
            self._clearTimer = FTLoopTimer(self.queenConf["maxAliveTime"], 0,
                                           self._clearData, True,
                                           self._fishType, 0.1)
            self._clearTimer.start()

    def _addBoss(self, isSysTimerCall=True, nextStage=0x1000):
        """
        添加宝箱boss
        """
        if nextStage != self._isBossShowTimeStage:
            self._removeBossShowTimeStage(self._isBossShowTimeStage)
            self._addBossShowTimeStage(nextStage)
        stage = 0x1000
        if self._isBossShowTimeStage == 0x1000:  # 出生鱼阵  有保护罩.
            fishType = self._maskFishType
            _bossGroupIds = self.table.runConfig.allSuperBossBornGroupIds[
                fishType]
            stage = 0x1
        elif self._isBossShowTimeStage == 0x1:  # 第一阶段  有保护罩.
            fishType = self._maskFishType
            _bossGroupIds = self.random_boss_groupid(fishType)
            if not isSysTimerCall:
                stage = 0x1
            else:
                stage = 0x10
        elif self._isBossShowTimeStage == 0x10:  # 受伤鱼阵  无保护罩.
            fishType = self._fishType
            _bossGroupIds = self.table.runConfig.allSuperBossBornGroupIds[
                fishType]
            stage = 0x100
        elif self._isBossShowTimeStage == 0x100:  # 第二阶段  无保护罩.
            fishType = self._fishType
            _bossGroupIds = self.random_boss_groupid(fishType)
            stage = 0x100
        else:
            return
        # nowTime = int(time.time())
        # boss超出最大存在时间后不再出现.
        # if nowTime >= self._startTS + self.queenConf["maxAliveTime"]:
        #     self._removeBossShowTimeStage(self._isBossShowTimeStage)
        #     return
        if self._autofillTimer:
            self._autofillTimer.cancel()
            self._autofillTimer = None
            # 处理冰冻自动填充时机延后逻辑.
            if self._group and not isSysTimerCall and self._group.extendGroupTime > 0:  # 该鱼群冰冻延长的时间
                self._autofillTimer = FTLoopTimer(self._group.extendGroupTime,
                                                  0, self._addBoss, False,
                                                  stage)
                self._autofillTimer.start()
                self._group.extendGroupTime = 0
                return
        _bossGroupId = random.choice(_bossGroupIds)
        if not _bossGroupId:
            return
        self._group = self.table.insertFishGroup(_bossGroupId)
        if self._group:
            # 龙女王的离场时间
            interval = self._group.getFishExitTime(fishType)
            self._autofillTimer = FTLoopTimer(interval, 0, self._addBoss,
                                              False, stage)  # 自动填充下一阶段的鱼阵
            self._autofillTimer.start()
            if ftlog.is_debug():
                ftlog.debug("QueenFishGroup _addBoss", self.table.tableId,
                            _bossGroupId, stage, interval)
        else:
            ftlog.error("QueenFishGroup _addBoss error", _bossGroupId)

    def random_boss_groupid(self, fishType):
        """
        第一阶段和第二阶段随机一类鱼群  在再该类鱼群中随机一个鱼群
        统计一类鱼1、2、3、4、5
        出现顺序为3、4、5、1、2
                4、5、1、2、3
        """
        groupMap = {}
        _bossGroupIds = self.table.runConfig.allSuperBossGroupIds[fishType]
        for group_name in _bossGroupIds:
            group = int(group_name.rsplit("_")[-2])
            if group not in groupMap:
                groupMap[group] = [group_name]
            else:
                groupMap[group].append(group_name)
        if not self._idx:
            num = random.randint(1, len(groupMap.keys()))
            self._idx = num
        else:
            if self._idx + 1 in groupMap.keys():
                self._idx += 1
            else:
                self._idx = min(groupMap.keys())
        _bossGroupIds = groupMap[self._idx]
        return _bossGroupIds

    def triggerCatchFishEvent(self, event):
        """
        处理捕获事件
        """
        if self._maskFishType in event.fishTypes and self._isBossShowTimeStage in [
                0x1000, 0x1
        ] and self._group:
            if self._autofillTimer:  # 女王保护罩 出生鱼阵 第一鱼阵
                self._autofillTimer.cancel()
            self._autofillTimer = None
            if self._modQuitTimer:
                self._modQuitTimer.cancel()
            self._modQuitTimer = None
            self._addBoss(False, 0x10)
            return

        # boss被捕获时可能刚好超时, 所以此时就不要再爆炸了.
        if self._fishType in event.fishTypes and self._isBossShowTimeStage in [
                0x10, 0x100
        ] and self._group:  # 龙女王不带保护罩 第二阶段
            if self._autofillTimer:  # 女王保护罩 出生鱼阵 第一鱼阵
                self._autofillTimer.cancel()
            self._autofillTimer = None
            if self._clearTimer:
                self._clearTimer.cancel()
            self._clearTimer = None
            stageCount = 0
            for catchMap in event.catch:
                fishInfo = self.table.fishMap[catchMap["fId"]]
                fishType = fishInfo["fishType"]
                if catchMap["reason"] == 0 and fishType == self._fishType:
                    stageCount = catchMap.get("stageCount")
                    break
            FTLoopTimer(
                stageCount * self.queenConf["stageTime"] +
                self.queenConf["endDelayTime"], 0, self._clearData, True,
                self._fishType, self.queenConf["tideDelayTime"]).start()

    def addTideFishGroup(self, delayTime=0.1):
        """
        添加鱼潮
        """
        if delayTime > 0:
            FTLoopTimer(delayTime, 0, self.leave).start()

    def dealEnterTable(self, userId):
        """
        玩家进入渔场时发送
        """
        # 当前阶段boss开始出现的时间戳.
        startTS = 0
        if self._isBossShowTimeStage & 0x111 != 0:
            startTS = self._startTS
        msg = MsgPack()
        msg.setCmd("queen_info")
        msg.setResult("gameId", config.FISH_GAMEID)
        msg.setResult("roomId", self.table.roomId)
        msg.setResult("tableId", self.table.tableId)
        msg.setResult("startTS", startTS)
        GameMsg.sendMsg(msg, userId)
Example #15
0
class MainQuest(object):
    """
    渔场内主线任务
    """
    def __init__(self, player):
        self.player = player
        self.holdCoin = player.holdCoin
        self.refreshCoinTimer = None
        self.refreshTaskTimer = None
        self.currTask = getCurrTaskInfo(self.player.userId)
        self.allHoldCoinTask = config.getMainQuestTasksConfByType(
            self.player.clientId, QuestType.HoldCoin)
        self.isFinishHoldCoinTask = isFinishAllTask(self.player.userId,
                                                    QuestType.HoldCoin)
        if not self.isFinishHoldCoinTask:
            # 刷新持有金币任务
            self.refreshCoinTimer = FTLoopTimer(1, -1, self.refreshaHoldCoin)
            self.refreshCoinTimer.start()
            # 刷新任务状态
            self.refreshTaskTimer = FTLoopTimer(60, -1, self.refreshTaskState)
            self.refreshTaskTimer.start()

    def clear(self):
        if self.refreshCoinTimer:
            self.refreshCoinTimer.cancel()
            self.refreshCoinTimer = None
        if self.refreshTaskTimer:
            self.refreshTaskTimer.cancel()
            self.refreshTaskTimer = None

    def dealEnterTable(self, isReconnect):
        """
        进入渔场
        """
        self.refreshTaskState()
        pushCurrTask(self.player.userId)

    def dealLeaveTable(self):
        """
        离开渔场
        """
        self.syncTaskData()
        self.clear()

    def syncTaskData(self):
        """
        同步任务数据
        """
        setQuestTypeData(self.player.userId, QuestType.HoldCoin, self.holdCoin,
                         True)

    def refreshTaskState(self):
        """
        刷新任务状态
        """
        self.syncTaskData()
        self.currTask = getCurrTaskInfo(self.player.userId)
        self.isFinishHoldCoinTask = isFinishAllTask(self.player.userId,
                                                    QuestType.HoldCoin)
        if self.isFinishHoldCoinTask:
            self.clear()

    def refreshaHoldCoin(self):
        """
        刷新持有金币任务数据
        """
        if self.holdCoin != self.player.holdCoin:
            self.holdCoin = self.player.holdCoin
            finishTask = None
            for taskConf in self.allHoldCoinTask:
                if self.holdCoin >= taskConf["num"]:
                    finishTask = taskConf
            if finishTask:
                self.syncTaskData()
                self.allHoldCoinTask.remove(finishTask)
            if self.currTask and self.currTask[
                    "type"] == QuestType.HoldCoin and self.currTask[
                        "state"] == QuestState.Default:
                taskConf = config.getMainQuestTaskConf(self.player.clientId,
                                                       self.currTask["taskId"])
                self.currTask["progress"] = [
                    min(self.holdCoin, taskConf["num"]), taskConf["num"]
                ]
                pushCurrTask(self.player.userId, task=self.currTask)

    def getQuestRewardsInTable(self, taskId):
        """
        在渔场内领取主线任务奖励
        """
        getQuestRewards(self.player.userId, self.player.clientId, taskId)
        self.refreshTaskState()
Example #16
0
class SportResult(object):
    '''赛果'''
    def __init__(self, sportlottery):
        self.sportlottery = sportlottery
        self.date = datetime.datetime.fromtimestamp(
            self.sportlottery.timestamp).strftime('%Y-%m-%d')

        interval = self.sportlottery.timestamp - int(time.time())
        if interval > 0:
            FTLoopTimer(interval + (45 + 15 + 45 + 5) * 60, 0,
                        self._startTimer).start()
        else:
            if interval + (45 + 15 + 45 + 5) * 60 <= 0:
                self._startTimer()
            else:
                FTLoopTimer(interval + (45 + 15 + 45 + 5) * 60, 0,
                            self._startTimer).start()

        if ftlog.is_debug():
            ftlog.debug('SportResult', 'sportObj=', self.sportlottery.toDict())

    def _startTimer(self):
        self.timer = FTLoopTimer(60, -1, self._getHttp)
        self.timer.start()

    def _getHttp(self):
        if ftlog.is_debug():
            ftlog.debug('SportResult', '_getHttp=', self.sportlottery.toDict())

        contents = http7Mgetschedulebydate(self.date)
        if not contents or len(contents) == 0:
            return

        tag = 0
        schedule = contents.get('Schedule')
        if not schedule or len(schedule) == 0:
            return

        for matchDict in schedule:
            match = matchDict.get('Id')
            if not match or len(match) < 4:
                continue
            if match[0] == int(self.sportlottery.matchId):
                if 'Score' in matchDict:
                    self.sportlottery.score = matchDict['Score']
                    if self.sportlottery.score and len(
                            self.sportlottery.score
                    ) > 0 and self.sportlottery.status not in RESULTS:
                        self.sportlottery.status = 4

                    self.sportlottery.save()

                    if self.sportlottery.isResult:
                        if self.timer:
                            self.timer.cancel()

                        tag = 1
                    break

        if self.sportlottery and tag == 1:
            self.sportlottery.clearSportResult()
Example #17
0
class Sportlottery(object):
    '''体育竞猜一场比赛实例'''
    def __init__(self):
        # 比赛id
        self.matchId = None
        # 联赛id
        self.leagueId = None
        # 主队id
        self.homeTeamId = None
        # 客队id
        self.awayTeamId = None
        # 比赛时间戳
        self.timestamp = None
        # 比赛状态
        self.status = None
        # 定时器
        self.timer = None
        # 比分
        self.score = None
        # 欧盘指数
        self.sportOdds = None
        # 赛果
        self.sportResult = None

    def fromDict(self, d):
        self.matchId = d['matchId']
        self.leagueId = d['leagueId']
        self.homeTeamId = d['homeTeamId']
        self.awayTeamId = d['awayTeamId']
        self.timestamp = d['timestamp']
        self.status = d.get('status', 0)  # 未开始
        self.score = d.get('score', [0, 0])  # 比分
        self.odds = d.get('odds', ['0', '0', '0'])

    def toDict(self):
        return {
            'matchId': self.matchId,
            'leagueId': self.leagueId,
            'homeTeamId': self.homeTeamId,
            'awayTeamId': self.awayTeamId,
            'timestamp': self.timestamp,
            'status': self.status,
            'score': self.score,
            'odds': self.odds
        }

    def save(self):
        date = datetime.datetime.fromtimestamp(
            self.timestamp).strftime('%Y-%m-%d')
        daobase.executeMixCmd('hset', leagueDaykey % date, self.matchId,
                              strutil.dumps(self.toDict()))

        if ftlog.is_debug():
            ftlog.debug('Sportlottery save', 'date=', date, 'matchId=',
                        self.matchId, 'toDict=', self.toDict())

    def clearSportOdds(self):
        if self.sportOdds:
            del self.sportOdds
            self.sportOdds = None

    def clearSportResult(self):
        if self.sportResult:
            del self.sportResult
            self.sportResult = None

    def start(self):
        if self.isResult:
            return

        if ftlog.is_debug():
            ftlog.debug('Sportlottery start', 'toDict=', self.toDict())

        if self.odds == ['0', '0', '0']:
            self.sportOdds = SportOdds(self)

        self.sportResult = SportResult(self)

        interval = self.timestamp - int(time.time())
        if interval > 0:
            FTLoopTimer(interval, 0, self._startTimer).start()
        else:
            self._startTimer()

    def _startTimer(self):
        self.timer = FTLoopTimer(5 * 60, -1, self._getHttpStatus)
        self.timer.start()

    @property
    def isResult(self):
        return self.status in RESULTS

    def _getHttpStatus(self):
        contents = http7Mgetlivedata()

        if ftlog.is_debug():
            ftlog.debug('Sportlottery _getHttpStatus', 'toDict=',
                        self.toDict())

        if not contents or len(contents) == 0:
            ftlog.info('Sportlottery._getHttpStatus',
                       'http7Mgetlivedata contents empty', contents)
            return

        liveData = contents.get('LiveData')
        if not liveData or len(liveData) == 0:
            ftlog.info('Sportlottery._getHttpStatus',
                       'http7Mgetlivedata LiveData empty', liveData)
            return

        matchD = liveData.get(str(self.matchId))
        if matchD:
            self.score = matchD.get('Score', [0, 0])
            self.status = matchD.get('Status', 0)
            if self.status != 0:
                self.save()

            if self.status in RESULTS:
                if self.timer:
                    self.timer.cancel()
        else:
            ftlog.warn('Sportlottery._getHttpStatus',
                       'liveData havenot matchId=', self.matchId, 'obj=', self)

    def __str__(self):
        return '%s:%s:%s:%s:%s' % (self.matchId, self.leagueId,
                                   self.homeTeamId, self.awayTeamId,
                                   self.timestamp)

    def __repr__(self):
        return self.__str__()

    def destructor(self):
        if self.timer:
            self.timer.cancel()

        ftlog.info('Sportlottery=', self)
class Tentacle(object):
    """
    触手
    """
    ST_IDLE = -1
    ST_SWING = 0
    ST_RETRACT = 1
    ST_FINAL = 2

    def __init__(self, table, octopus):
        self.table = table
        self.octopus = octopus
        self._state = None
        self._idleTimer = None
        self._swingTimer = None
        self._retractTimer = None
        self.clear()

    @property
    def state(self):
        return self._state

    def clear(self):
        if self._idleTimer:
            self._idleTimer.cancel()
            self._idleTimer = None
        if self._swingTimer:
            self._swingTimer.cancel()
            self._swingTimer = None
        if self._retractTimer:
            self._retractTimer.cancel()
            self._retractTimer = None
        self._state = None
        self.posId = 0
        self.fishId = 0
        self.fishType = 0
        self.delayTime = 0
        self.frozenTime = 0

    def init(self, posId, fishType, delayTime):
        """初始化章鱼boss的触手"""
        self.tentacleConf = self.table.room.roomConf["tentacleConf"]
        self.posId = posId
        self.fishType = fishType
        self.delayTime = delayTime
        self.idleTime = self.calcIdleTime()
        self.swingTime = self.calcSwingTime()
        self.retractTime = self.calcRetractTime()
        self.finalTime = self.calcFinalTime()
        self.createTentacleFish()
        if self.fishId > 0:
            if self.delayTime > 0:
                self._doIdle()
            else:
                self._doSwing()

    def _doIdle(self):
        """
        空闲等待
        """
        self._state = Tentacle.ST_IDLE
        stageTime = self.getCurrentStateStageTime()
        interval = stageTime[1] - stageTime[0]
        self._idleTimer = FTLoopTimer(interval, 0, self._doSwing)
        self._idleTimer.start()

    def _doSwing(self):
        """
        摆动触手
        """
        self._state = Tentacle.ST_SWING
        if self.delayTime > 0:
            self.octopus.syncTentacleState(self)
        stageTime = self.getCurrentStateStageTime()
        interval = stageTime[1] - stageTime[0]
        self._swingTimer = FTLoopTimer(interval, 0, self._doRetract)
        self._swingTimer.start()

    def _doRetract(self):
        """
        触手到时间正常收回
        """
        if self._state == Tentacle.ST_SWING:
            self._state = Tentacle.ST_RETRACT
            self.octopus.syncTentacleState(self)
            stageTime = self.getCurrentStateStageTime()
            interval = stageTime[1] - stageTime[0]
            self._retractTimer = FTLoopTimer(interval, 0, self._doFinal)
            self._retractTimer.start()

    def _doFinal(self, isCatch=False):
        """
        触手已收回
        """
        self._retractTimer and self._retractTimer.cancel()
        if self.octopus.state == Octopus.ST_APPEARED and isCatch is False:
            # 正常收回,增加剩余可出现触手数量
            self.octopus.surplusTentaclesCount[str(self.fishType)] += 1
        self.disappear()

    def disappear(self):
        """
        触手消失
        """
        self._state = Tentacle.ST_FINAL
        self.table.setFishDied(self.fishId)
        posId, fishType, delayTime = self.octopus.generateNewTentacleConf(
            self.posId)
        self.clear()
        if self.octopus.state == Octopus.ST_APPEARED:
            if fishType and posId:
                if ftlog.is_debug():
                    ftlog.debug("disappear init", self.table.tableId, posId,
                                fishType, delayTime)
                self.init(posId, fishType, delayTime)

    def catch(self):
        """
        摆动状态下触手被捕获
        """
        if self._state == Tentacle.ST_SWING:
            self._state = Tentacle.ST_RETRACT
            self._swingTimer and self._swingTimer.cancel()
            self._retractTimer = FTLoopTimer(self.tentacleConf["retractTime"],
                                             0,
                                             self._doFinal,
                                             isCatch=True)
            self._retractTimer.start()

    def calcIdleTime(self):
        """
        计算空闲状态开始时间戳
        """
        return pktimestamp.getCurrentTimestamp() + 1

    def calcSwingTime(self):
        """
        计算摆动状态开始时间戳
        """
        return self.idleTime + self.delayTime

    def calcRetractTime(self):
        """
        计算收回状态开始时间戳
        """
        return self.swingTime + random.randint(
            *self.tentacleConf["swingTimeRange"]) + self.frozenTime

    def calcFinalTime(self):
        """
        计算结束状态开始时间戳
        """
        return self.retractTime + self.tentacleConf["retractTime"]

    def getCurrentStateStageTime(self):
        """
        获取当前状态开始结束时间
        """
        stateTime = {
            Tentacle.ST_IDLE: self.idleTime,
            Tentacle.ST_SWING: self.swingTime,
            Tentacle.ST_RETRACT: self.retractTime,
            Tentacle.ST_FINAL: self.finalTime
        }
        return stateTime[self._state], stateTime[self._state + 1]

    def createTentacleFish(self):
        """
        创建触手对应的鱼阵
        """
        groupIds = self.table.runConfig.allSuperBossGroupIds[self.fishType]
        if groupIds:
            groupIds = random.choice(groupIds)
            group = self.table.insertFishGroup(groupIds)
            self.fishId = group.startFishId
            self.octopus.surplusTentaclesCount[str(self.fishType)] -= 1

    def frozenTentacleFish(self, frozenTime):
        """
        触手被冻住
        """
        self.frozenTime += frozenTime
        # 刷新触手收回动画开始和结束时间
        self.retractTime = self.calcRetractTime()
        self.finalTime = self.calcFinalTime()
        stageTime = self.getCurrentStateStageTime()
        interval = stageTime[1] - pktimestamp.getCurrentTimestamp()
        if ftlog.is_debug():
            ftlog.debug("frozenTentacleFish", self.frozenTime,
                        self.retractTime, interval)
        self._swingTimer and self._swingTimer.cancel()
        self._swingTimer = FTLoopTimer(interval, 0, self._doRetract)
        self._swingTimer.start()

    def toDict(self):
        if Tentacle.ST_IDLE < self._state < Tentacle.ST_FINAL:
            return {
                "posId": self.posId,
                "fishId": self.fishId,
                "fishType": self.fishType,
                "state": self.state,
                "progress": self.getCurrentStateStageTime()
            }
        return {}
Example #19
0
class Heartbeat(object):

    ST_IDLE = 0
    ST_START = 1
    ST_STOP = 2

    def __init__(self, target, interval):
        self._target = target
        self._state = Heartbeat.ST_IDLE
        self._count = 0
        self._postTaskList = []
        self._timer = None
        self._interval = interval
        self._logger = Logger()
        self._init = False

    def start(self):
        assert (self._state == Heartbeat.ST_IDLE)
        self._state = Heartbeat.ST_START
        self._timer = FTLoopTimer(0, 0, self._onInit)
        self._timer.start()

    def stop(self):
        if self._state != Heartbeat.ST_STOP:
            self._state = Heartbeat.ST_STOP
            if self._timer:
                self._timer.cancel()
            self._timer = None

    @property
    def count(self):
        return self._count

    def postCall(self, func, *args, **kwargs):
        self.postTask(functools.partial(func, *args, **kwargs))

    def postTask(self, task):
        if self._state != Heartbeat.ST_STOP:
            self._postTaskList.append(task)
            if self._init and self._timer:
                self._timer.cancel()
                self._timer = FTLoopTimer(0, 0, self._onTimeout)
                self._timer.start()

    def _onInit(self):
        try:
            self._timer = None
            interval = self._target.onInit()
            if interval:
                self._interval = interval
            self._scheduleTimer()
        except:
            self._logger.error("Heartbeat._onInit")

    def _onTimeout(self):
        try:
            self._timer = None
            self._count += 1
            self._processPostTaskList()
            interval = self._target.onHeartbeat()
            if interval is not None:
                self._interval = interval
        except:
            self._interval = 1
            self._logger.error("Heartbeat._onTimeout")
        self._scheduleTimer()

    def _scheduleTimer(self):
        if self._state == Heartbeat.ST_START:
            interval = 0 if self._postTaskList else self._interval
            self._timer = FTLoopTimer(interval, 0, self._onTimeout)
            self._timer.start()

    def _processPostTaskList(self):
        taskList = self._postTaskList
        self._postTaskList = []
        for task in taskList:
            try:
                task()
            except:
                self._logger.error("task=", task)
class AutofillFishGroup(object):
    """
    千炮模式自动填充鱼群
    """
    def __init__(self, table):
        self.table = table
        # 自动填充鱼配置
        self.autofillFishConf = {}
        # 各鱼种类别下鱼组index
        self._categoryGroupConf = {}
        # 各鱼种类别定时器
        self._checkCategoryTimerList = []
        # 各鱼种类别下的鱼群定时器
        self._checkShoalTimerList = []
        # 刷新分组配置定时器
        self._refreshGroupConfTimer = None
        self.startDefaultAutofillFish()
        # 大奖赛间隔5分钟刷新分组配置
        if self.table.typeName == config.FISH_GRAND_PRIX:
            self._refreshGroupConfTimer = FTLoopTimer(300, -1, self.refreshCategoryGroupConf)
            self._refreshGroupConfTimer.start()

    def clearTimer(self):
        """
        清理所有定时器
        """
        self.clearCategoryTimer()
        self.clearShoalTimer()
        if self._refreshGroupConfTimer:
            self._refreshGroupConfTimer.cancel()
            self._refreshGroupConfTimer = None
        if ftlog.is_debug():
            ftlog.debug("AutofillFishGroup clearTimer", self.table.tableId)

    def clearCategoryTimer(self):
        """
        清理鱼种类别定时器
        """
        for _timer in self._checkCategoryTimerList:
            if _timer:
                _timer.cancel()
                _timer = None
        self._checkCategoryTimerList = []

    def clearShoalTimer(self):
        """
        清理鱼种类别下的鱼群定时器
        """
        for _timer in self._checkShoalTimerList:
            if _timer:
                _timer.cancel()
                _timer = None
        self._checkShoalTimerList = []

    def startDefaultAutofillFish(self):
        """
        默认状态下的自动填充鱼
        """
        self.defaultConf = config.getAutofillFishConf_m("default", self.table.runConfig.fishPool)
        if self.defaultConf:
            self._generalCategoryGroupConf(self.defaultConf)
            self._generalCategoryTimer()
            self._generalShoalTimer()

    def startSuperBossAutofillFish(self):
        """
        超级Boss期间的自动填充鱼
        """
        self.superBossConf = config.getAutofillFishConf_m("superBoss", self.table.runConfig.fishPool)
        if self.superBossConf:
            self._generalCategoryGroupConf(self.superBossConf)
            self._generalCategoryTimer()
            self._generalShoalTimer()

    def refreshCategoryGroupConf(self):
        """
        刷新各类鱼的分组数据(重新随机分组)
        """
        self._generalCategoryGroupConf()

    def _generalCategoryGroupConf(self, autofillFishConf=None):
        """
        生成并记录各类鱼的分组数据
        """
        self.autofillFishConf = autofillFishConf or self.autofillFishConf
        for categoryId, categoryConf in self.autofillFishConf.iteritems():
            self._categoryGroupConf[categoryId] = random.choice(categoryConf["groups"])
        if ftlog.is_debug():
            ftlog.debug("_generalCategoryGroupConf", self.table.tableId, self._categoryGroupConf)

    def _generalCategoryTimer(self):
        """
        生成并存储各类鱼的定时器
        """
        self.clearCategoryTimer()
        for categoryId, categoryConf in self.autofillFishConf.iteritems():
            interval = categoryConf["cSupplyInterval"]
            checkTimer = FTLoopTimer(interval, -1, self._checkCategoryAutoFillFish, categoryId)
            checkTimer.start()
            self._checkCategoryTimerList.append(checkTimer)

    def _checkCategoryAutoFillFish(self, categoryId):
        """
        检测该类鱼是否需要填充鱼
        """
        if self.table.hasTideFishGroup():  # 当前渔场存在鱼潮
            return
        group = self._categoryGroupConf[categoryId]
        groupType = group["groupType"]
        if groupType == 1:
            for fishConf in group["fishes"]:
                fillCount, totalCount = self._getAutoFillCount([fishConf])
                self._addAutoFillFishGroup(categoryId, fishConf, fillCount, totalCount)
        elif groupType == 2:
            fillCount, totalCount = self._getAutoFillCount(group["fishes"])
            for _ in xrange(fillCount):
                fishConf = util.getOneResultByWeight(group["fishes"])
                self._addAutoFillFishGroup(categoryId, fishConf, 1, totalCount)

    def _getAutoFillCount(self, fishConfList):
        """
        统计某类鱼的存活数量,再计算某条鱼所需填充数量
        @return: 需要填充数量, 该类鱼存活数量
        """
        aliveCount = 0
        fishConf = None
        for fishConf in fishConfList:
            aliveCount += self.table.fishCountMap.get(fishConf["fishType"], 0)
        if aliveCount <= fishConf["minCount"]:
            randInt = random.randint(fishConf["minCount"], fishConf["maxCount"])
            return int(math.ceil(float(randInt - aliveCount) / max(fishConf["fishCount"], 1))), aliveCount
        return 0, aliveCount

    def _addAutoFillFishGroup(self, categoryId, fishConf, fillCount, aliveCount):
        """
        添加填充鱼
        """
        if fillCount > 0:
            if fishConf["fishType"] not in self.table.runConfig.allAutofillGroupIds:
                ftlog.error("_addAutoFillFishGroup error", self.table.tableId, fishConf["fishType"])
                return
            if ftlog.is_debug():
                ftlog.debug("_addAutoFillFishGroup tableId =", self.table.tableId,
                            "categoryId =", categoryId,
                            "fishType =", fishConf["fishType"],
                            "aliveCount =", aliveCount,
                            "fillCount =", fillCount,
                            "maxCount =", fishConf["maxCount"])
            autofillGroupIds = self.table.runConfig.allAutofillGroupIds[fishConf["fishType"]]
            if autofillGroupIds:
                groupNames = random.sample(autofillGroupIds, min(len(autofillGroupIds), fillCount))
                groupNames and self.table.insertFishGroup(groupNames)

    def _generalShoalTimer(self):
        """
        生成并存储各类鱼的鱼群定时器
        """
        self.clearShoalTimer()
        for categoryId, categoryConf in self.autofillFishConf.iteritems():
            interval = categoryConf["sSupplyInterval"]
            if interval > 0:
                checkTimer = FTLoopTimer(interval, -1, self._checkAutoFillFishShoal, categoryId)
                checkTimer.start()
                self._checkShoalTimerList.append(checkTimer)

    def _checkAutoFillFishShoal(self, categoryId):
        """
        检测该类鱼的鱼群是否需要填充
        """
        if self.table.hasTideFishGroup():  # 当前渔场存在鱼潮
            return
        group = self._categoryGroupConf[categoryId]
        fishConf = random.choice(group["fishes"])
        self._addAutoFillFishShoal(fishConf)

    def _addAutoFillFishShoal(self, fishConf):
        """
        添加填充鱼群
        """
        autofillGroupIds = self.table.runConfig.allAutofillShoalGroupIds[fishConf["fishType"]]
        if autofillGroupIds:
            groupNames = random.sample(autofillGroupIds, 1)
            groupNames and self.table.insertFishGroup(groupNames)
class FishGrandPrixPlayer(FishMultiplePlayer):
    def _loadUserData(self):
        super(FishGrandPrixPlayer, self)._loadUserData()
        # 捕鱼积分
        self.grandPrixFishPoint = weakdata.getDayFishData(
            self.userId, WeakData.grandPrix_fishPoint, 0)
        # 大奖赛剩余开火次数
        self.grandPrixFireCount = weakdata.getDayFishData(
            self.userId, WeakData.grandPrix_fireCount, 0)
        # 大奖赛剩余技能使用次数
        self.grandPrixUseSkillTimes = weakdata.getDayFishData(
            self.userId, WeakData.grandPrix_useSkillTimes, [])
        # 大奖赛目标鱼捕获数量
        self.grandPrixTargetFish = weakdata.getDayFishData(
            self.userId, WeakData.grandPrix_targetFish, {})
        # 大奖赛火炮等级和倍率
        # self.grandPrixLevelFpMultiple = weakdata.getDayFishData(self.userId, WeakData.grandPrix_levelFpMultiple)
        # 大奖赛开始的时间戳
        self.grandPrixStartTS = weakdata.getDayFishData(
            self.userId, WeakData.grandPrix_startTS, 0)
        # 大奖赛结束定时器
        self.grandPrixEndTimer = None
        # 大奖赛提示定时器
        self.grandPrixTipTimer = None
        # 大奖赛阶段奖励
        self.grandPrixGetPointsInfo = weakdata.getDayFishData(
            self.userId, WeakData.grandPrix_getPointsInfo, [])
        self._freeTimes = weakdata.getDayFishData(self.userId,
                                                  WeakData.grandPrix_freeTimes,
                                                  0)

        self.multipleList = []  # 10、50、200、500
        for v in gdata.roomIdDefineMap().values():
            roomConf = v.configure
            if roomConf.get("typeName") not in [config.FISH_FRIEND]:
                continue
            self.multipleList.append(roomConf.get("multiple", 1))
        self.multipleList.sort()

        self._rankListCache = []  # 超越玩家的集合
        self._surpassUser = {}
        self.grandPrixSurpassCount = weakdata.getDayFishData(
            self.userId, WeakData.grandPrix_surpassCount, 0)  # 大奖赛超越自己次数
        self.bestPoint = weakdata.getDayFishData(self.userId,
                                                 WeakData.grandPrix_point, 0)
        self.inGameTimes = 0  # 游戏时长(分钟)

    def _incrGameTimer(self):
        """增加游戏时长"""
        if self.userId <= 0:
            return
        super(FishGrandPrixPlayer, self)._incrGameTimer()
        self.inGameTimes += 1

    def _checkSkillCondition(self, skillId, select, skillType=0):
        """
        检测技能使用条件
        """
        if self.isGrandPrixMode() and select:  # 大奖赛模式限制技能槽的使用次数
            for idx, val in enumerate(self.grandPrixUseSkillTimes):
                if val.get("skillId") == skillId and val.get("skillType",
                                                             0) == skillType:
                    if val.get("count") <= 0:
                        return 7
                    break
        return super(FishGrandPrixPlayer,
                     self)._checkSkillCondition(skillId, select, skillType)

    def addFire(self,
                bulletId,
                wpId,
                sendTimestamp,
                fpMultiple,
                gunMultiple=1,
                gunX=1,
                skill=None,
                power=None,
                clientFire=True,
                targetPos=None,
                fishType=None,
                costChip=0,
                superBullet=None):
        """添加开火信息"""
        super(FishGrandPrixPlayer,
              self).addFire(bulletId, wpId, sendTimestamp, fpMultiple,
                            gunMultiple, gunX, skill, power, clientFire,
                            targetPos, fishType, costChip, superBullet)
        if clientFire and skill is None and self.isGrandPrixMode():
            self.grandPrixFireCount -= 1  # 开火次数减少
            if self.grandPrixFireCount == 0:  # 结束比赛
                self.endGrandPrix()

    def _refreshSkillSlots(self, skillType=0):
        """
        刷新技能数据 安装技能槽
        """
        if skillType == 0:
            self.skillSlots = {}
        if self.grandPrixUseSkillTimes:
            for val in self.grandPrixUseSkillTimes:
                if isinstance(val, dict) and val.get("skillId"):
                    if val.get("skillType", 0) == skillType:
                        if skillType == 0:
                            self.skillSlots[val["skillId"]] = [
                                val["state"], val["star"], val["grade"]
                            ]
        else:
            super(FishGrandPrixPlayer, self)._refreshSkillSlots(skillType)

    def clearTimer(self):
        """清理提示和结束的定时器"""
        super(FishGrandPrixPlayer, self).clearTimer()
        if self.grandPrixEndTimer:
            self.grandPrixEndTimer.cancel()
            self.grandPrixEndTimer = None
        if self.grandPrixTipTimer:
            self.grandPrixTipTimer.cancel()
            self.grandPrixTipTimer = None
        if self.inGameTimes:
            bireport.reportGameEvent("BI_NFISH_GRAND_PRIX_INGAMETIMES",
                                     self.userId, FISH_GAMEID, 0, 0,
                                     self.grandPrixStartTS, self.inGameTimes,
                                     0, 0, [], util.getClientId(self.userId))

    def checkCanFire(self, fPos, wpId, bulletId, skillId, skillType):
        """
        检测玩家是否可以开火
        """
        canFire, reason, clip, costBullet, extends, skill = \
            super(FishGrandPrixPlayer, self).checkCanFire(fPos, wpId, bulletId, skillId, skillType)
        if canFire and self.isGrandPrixMode() and self.grandPrixFireCount <= 0:
            canFire, reason = False, 8
        return canFire, reason, clip, costBullet, extends, skill

    def sendFireMsg(self, userId, seatId, extends, params):
        """
        发送开火确认消息
        """
        fPos = params.get("fPos")
        wpId = params.get("wpId")
        bulletId = params.get("bulletId")
        skillId = params.get("skillId")
        timestamp = params.get("timestamp")
        canFire = params.get("canFire")
        reason = params.get("reason")
        clip = params.get("clip")
        skillType = params.get("skillType", 0)
        lockFId = params.get("lockFId", 0)
        fPosx, fPosy = fPos
        retMsg = MsgPack()
        retMsg.setCmd("fire")
        retMsg.setResult("gameId", FISH_GAMEID)
        retMsg.setResult("userId", userId)
        retMsg.setResult("wpId", wpId)
        retMsg.setResult("bulletId", bulletId)
        retMsg.setResult("skillId", skillId)
        retMsg.setResult("skillType", skillType)
        retMsg.setResult("extends", extends)
        retMsg.setResult("timestamp", timestamp)
        retMsg.setResult("reason", reason)
        retMsg.setResult("grandPrixFireCount",
                         self.grandPrixFireCount)  # 大奖赛剩余开火次数
        retMsg.setResult("clip", clip)
        retMsg.setResult("lockFId", lockFId)
        superBullet = self.getFire(bulletId).get(
            "superBullet", {})  # self.isSuperBullet(bulletId)
        retMsg.setResult("superBullet", 1 if superBullet else 0)  # 测试代码
        GameMsg.sendMsg(retMsg, userId)
        if canFire:
            retMsg.setResult("fPosx", fPosx)
            retMsg.setResult("fPosy", fPosy)
            retMsg.setResult("seatId", seatId)
            GameMsg.sendMsg(retMsg, self.table.getBroadcastUids(userId))

    def saveUserData(self):
        """保存用户数据"""
        super(FishGrandPrixPlayer, self).saveUserData()
        self.saveGrandPrixPoint()  # 保存大奖赛积分

    def getTargetFishs(self):
        """
        返回目标鱼(魔术炮技能召唤使用)
        """
        fishes = super(FishGrandPrixPlayer, self).getTargetFishs()
        targetFish = [
            int(ft) for ft, val in self.grandPrixTargetFish.iteritems()
            if val[0] < val[1]
        ]  # 0 < 5目标鱼数量
        if targetFish:
            fishes.extend(targetFish)
        return fishes

    def getMatchingFpMultiple(self, fpMultiple):
        """
        获取玩家渔场倍率对应的基础倍率
        """
        multiple = self.table.runConfig.multiple
        for mul in self.multipleList:
            if fpMultiple >= mul:
                multiple = mul
        return multiple

    def isGrandPrixMode(self):
        """
        大奖赛比赛模式
        """
        return self.grandPrixStartTS > 0

    def _calcuCatchFishGrandPrixPoint(self, fishPoint, gunX):
        """
        计算捕鱼积分 炮倍和vip的加成
        """
        if ftlog.is_debug():
            ftlog.debug('calcuCatchFishGrandPrixPoint', fishPoint, gunX)
        vipAddition = config.getVipConf(self.vipLevel).get(
            "grandPrixAddition", 0.)
        gunXAddition = self.getGunXAddition(gunX)
        if ftlog.is_debug():
            ftlog.debug('calcuCatchFishGrandPrixPoint1', vipAddition,
                        gunXAddition)
        point = fishPoint * 100
        points = point * (1 + vipAddition) * (1 + gunXAddition)
        return int(points)

    def getGunXAddition(self, gunX):
        """
        根据开火时候[炮倍 * 炮(单倍率|双倍率)]确定 加成值
        """
        gunMInfo = config.getGrandPrixConf("gunMultiAddition")
        for info in gunMInfo:
            if info["rangeList"][0] <= gunX <= info["rangeList"][1]:
                return info["addition"]
        return 0

    def saveGrandPrixPoint(self):
        """
        保存大奖赛积分
        """
        if not self.isGrandPrixMode():
            return 0, 0
        grandPrixFinalPoint = self.grandPrixFishPoint
        # 计入排行榜
        if grandPrixFinalPoint:
            ranking_system.refreshGrandPrixPoint(self.userId,
                                                 grandPrixFinalPoint)
        # 保存大奖赛数据
        self._saveGrandPrixData()
        return int(grandPrixFinalPoint)  # 最后的积分

    def cancelUsingSkills(self):
        """
        取消处于使用中的技能
        """
        if not self.usingSkill:  # 有之前技能记录
            return
        lastSkillId = self.usingSkill[-1].get("skillId")
        lastSkillType = self.usingSkill[-1].get("skillType", 0)
        lastSkill = self.getSkill(lastSkillId, lastSkillType)
        if not lastSkill:
            return
        if lastSkill.state == 1:
            lastSkill.use(0)
        else:
            self.removeUsingSkill(lastSkillId, lastSkillType)
            orgState = lastSkill.state
            lastSkill.state = 0
            self.table.broadcastSkillUse(lastSkill, 0, self.userId,
                                         orgState)  # 广播选中/取消技能消息
        self.table.broadcastGunChange(self)  # 广播玩家现在的火炮等级

    def unloadSkills(self):
        """
        下载所有技能
        """
        for _skillId in self.skills.keys():
            _skill = self.getSkill(_skillId, 0)
            if not _skill:
                continue
            _skill.clear()
            del _skill

    def startGrandPrix(self):
        """
        大奖赛开始 grandPrixStartTS=0 报名大奖赛/ grandPrixStartTS > 0 直接进入渔场
        """
        curTime = int(time.time())
        dayStartTimestamp = util.getDayStartTimestamp(curTime)
        remainGrandPrixTimeSeconds = util.timeStrToInt(
            config.getGrandPrixConf("openTimeRange")[1]) - (
                curTime - dayStartTimestamp)  # 大奖赛剩余时间
        # 当局进入大奖赛
        if self.grandPrixStartTS == 0:
            event = JoinGrandPrixEvent(self.userId, FISH_GAMEID,
                                       self.table.bigRoomId)  # 参加大奖赛事件
            TGFish.getEventBus().publishEvent(event)
            # 距离大奖赛结束不足10秒不可参赛
            if not grand_prix.isGrandPrixOpenTime(
            ) or remainGrandPrixTimeSeconds < 10:
                code = SignupCode.SC_NOT_OPEN
            elif config.getVipConf(self.vipLevel).get(
                    "grandPrixFreeTimes", 0) > self._freeTimes:  # 用免费次数报名
                self._freeTimes = weakdata.incrDayFishData(
                    self.userId, WeakData.grandPrix_freeTimes, 1)
                code = SignupCode.SC_SUCC
            else:
                # 扣除报名费
                fee = config.getGrandPrixConf("fee")[0]
                _consume = [{
                    "name": fee.get("name"),
                    "count": fee.get("count")
                }]
                _ret = util.consumeItems(self.userId, _consume, "ITEM_USE",
                                         self.table.roomId)
                if _ret:
                    code = SignupCode.SC_SUCC
                else:
                    code = SignupCode.SC_FEE_NOT_ENOUGH
            if code == SignupCode.SC_SUCC:
                # 选择目标鱼
                for _val in config.getGrandPrixConf(
                        "group").values():  # 1、2、3个组
                    idx = random.randint(0, len(_val) - 1)  # 一组鱼
                    _cnt = config.getGrandPrixConf("target").get(
                        str(_val[idx]), {}).get("count", 999)  # 某一种鱼 捕获数量
                    _point = config.getGrandPrixConf("target").get(
                        str(_val[idx]), {}).get("point", 0)  # 某一种鱼 获得的积分
                    self.grandPrixTargetFish[str(
                        _val[idx])] = [0, _cnt, _point]

                # 备份技能数据.
                self.grandPrixUseSkillTimes = []
                for i in range(skill_system.MAX_INSTALL_NUM - 1):
                    self.grandPrixUseSkillTimes.append({
                        "skillId":
                        0,
                        "state":
                        0,
                        "star":
                        0,
                        "grade":
                        0,
                        "count":
                        config.getGrandPrixConf("fireCount")[1],  # 技能使用次数3
                        "skillType":
                        0 if i < skill_system.MAX_INSTALL_NUM else
                        1  # 0主技能 1辅助技能
                    })

                for idx, _skillId in enumerate(self.skillSlots):
                    _skill = self.getSkill(_skillId, 0)
                    self.grandPrixUseSkillTimes[idx]["skillId"] = _skillId
                    self.grandPrixUseSkillTimes[idx][
                        "state"] = _skill.skillState
                    self.grandPrixUseSkillTimes[idx]["star"] = _skill.skillStar
                    self.grandPrixUseSkillTimes[idx][
                        "grade"] = _skill.skillGrade

                if self.inGameTimes:
                    bireport.reportGameEvent("BI_NFISH_GRAND_PRIX_INGAMETIMES",
                                             self.userId, FISH_GAMEID, 0, 0,
                                             self.grandPrixStartTS,
                                             self.inGameTimes, 0, 0, [],
                                             util.getClientId(self.userId))
                    self.inGameTimes = 0

                self.grandPrixFishPoint = 0
                self.grandPrixSurpassCount = 0
                self.grandPrixStartTS = curTime  # 大奖赛开始的时间戳
                self.grandPrixFireCount = config.getGrandPrixConf(
                    "fireCount")[0]
                self._saveGrandPrixData()  # 保存大奖赛数据
        else:
            if not grand_prix.isGrandPrixOpenTime():  # 现在不可参赛
                code = SignupCode.SC_NOT_OPEN
            elif not self.isGrandPrixMode():
                code = SignupCode.SC_SUCC
            else:
                code = SignupCode.SC_UNFINISH

        if code in [SignupCode.SC_SUCC, SignupCode.SC_UNFINISH]:
            interval = max(0.1, remainGrandPrixTimeSeconds)
            if self.grandPrixFireCount == 0:  # 3秒之后结束大奖赛
                interval = 0.1
            if self.grandPrixEndTimer:
                self.grandPrixEndTimer.cancel()
                self.grandPrixEndTimer = None
            self.grandPrixEndTimer = FTLoopTimer(interval, 0,
                                                 self.endGrandPrix)  # 启动结束定时器
            self.grandPrixEndTimer.start()
            # 取消处于使用中的技能,以免干扰技能使用次数计数
            if self.offline == 0:  # 玩家在线
                self.cancelUsingSkills()
                self.unloadSkills()
                self.loadAllSkillData()
                self.syncSkillSlots()
        elif code == SignupCode.SC_NOT_OPEN:  # 没开启
            self._resetGrandPrixData()  # 重置大奖赛相关数据

        self._getSurpassTarget()  # 获取要超越的玩家数据
        mo = MsgPack()
        mo.setCmd("start_grand_prix")
        mo.setResult("gameId", FISH_GAMEID)
        mo.setResult("userId", self.userId)
        mo.setResult("seatId", self.seatId)
        mo.setResult("fireCount", self.grandPrixFireCount)
        mo.setResult("fishPoint", self.grandPrixFishPoint)
        mo.setResult("targetFish", self.grandPrixTargetFish)
        mo.setResult(
            "useSkillTimes", {
                val.get("skillId", 0): val.get("count", 0)
                for val in self.grandPrixUseSkillTimes
            })
        mo.setResult("pointsInfo", grand_prix.getPointInfo(
            self.userId))  # 奖励积分 道具Id、道具数量、是否领取了奖励0|1
        mo.setResult("todayMaxPoints",
                     weakdata.getDayFishData(self.userId,
                                             WeakData.grandPrix_point,
                                             0))  # 今日最高积分
        GameMsg.sendMsg(mo, self.userId)

    def endGrandPrix(self):
        """
        大奖赛结束
        """
        if self.userId == 0:
            return

        if self.grandPrixEndTimer:
            self.grandPrixEndTimer.cancel()
            self.grandPrixEndTimer = None

        if not self.isGrandPrixMode():
            return

        grandPrixFinalPoint = self.saveGrandPrixPoint()
        # 完成大奖赛事件
        event = FinishGrandPrixEvent(self.userId, FISH_GAMEID,
                                     self.table.bigRoomId, grandPrixFinalPoint)
        TGFish.getEventBus().publishEvent(event)
        bireport.reportGameEvent("BI_NFISH_GRAND_PRIX_END", self.userId,
                                 FISH_GAMEID, 0, 0, grandPrixFinalPoint, 0, 0,
                                 0, [], util.getClientId(self.userId))
        if self.inGameTimes:
            bireport.reportGameEvent("BI_NFISH_GRAND_PRIX_INGAMETIMES",
                                     self.userId, FISH_GAMEID, 0, 0,
                                     self.grandPrixStartTS, self.inGameTimes,
                                     0, 0, [], util.getClientId(self.userId))
            self.inGameTimes = 0

        self.cancelUsingSkills()  # 恢复技能数据 取消处于使用中的技能
        self.unloadSkills()  # 下载所有技能
        self._resetGrandPrixData()  # 重置数据存档
        self.loadAllSkillData()  # 重新加载技能 读取并初始化所有技能数据
        self.syncSkillSlots()  # 同步技能槽消息
        rank, rankRewards = ranking_system.getUserRankAndRewards(
            RankType.TodayGrandPrix, self.userId)
        if ftlog.is_debug():
            ftlog.debug("endGrandPrix", self.grandPrixStartTS,
                        self.grandPrixFireCount, self.grandPrixFishPoint)
        mo = MsgPack()
        mo.setCmd("end_grand_prix")
        mo.setResult("gameId", FISH_GAMEID)
        mo.setResult("userId", self.userId)
        mo.setResult("seatId", self.seatId)
        mo.setResult("fishPoint", grandPrixFinalPoint)
        mo.setResult("rank", rank)
        mo.setResult("rankRewards", rankRewards)
        mo.setResult("fee", config.getGrandPrixConf("fee"))
        mo.setResult(
            "tabs",
            ranking_system.getRankingTabs(self.userId,
                                          util.getClientId(self.userId),
                                          RankType.TodayGrandPrix,
                                          rankDetail=True))
        GameMsg.sendMsg(mo, self.userId)
        self.sendGrandPrixInfo()
        self.table.clearPlayer(self)  # 从桌子中踢掉玩家

    def _resetGrandPrixData(self):
        """
        重置大奖赛相关数据
        """
        self.grandPrixStartTS = 0  # 大奖赛开始的时间戳
        self.grandPrixFireCount = 0  # 大奖赛剩余开火次数
        self.grandPrixTargetFish = {}  # 大奖赛目标鱼捕获数量
        self.grandPrixUseSkillTimes = []  # 大奖赛剩余技能使用次数
        self.grandPrixFishPoint = 0  # 捕鱼积分
        self.grandPrixSurpassCount = 0  # 大奖赛超越自己次数
        # self.grandPrixLevelFpMultiple = None        # 大奖赛火炮等级和倍率
        self._rankListCache = []  # 要超越的玩家缓存
        self._surpassUser = {}  # 要超越玩家数据
        self._saveGrandPrixData()  # 保存大奖赛信息
        weakdata.setDayFishData(self.userId, WeakData.grandPrix_surpassCount,
                                0)  # 大奖赛超越自己次数

    def _saveGrandPrixData(self):
        """
        保存大奖赛数据
        """
        weakdata.setDayFishData(self.userId, WeakData.grandPrix_startTS,
                                self.grandPrixStartTS)
        weakdata.setDayFishData(self.userId, WeakData.grandPrix_fireCount,
                                self.grandPrixFireCount)
        weakdata.setDayFishData(self.userId, WeakData.grandPrix_fishPoint,
                                self.grandPrixFishPoint)
        weakdata.setDayFishData(self.userId, WeakData.grandPrix_surpassCount,
                                self.grandPrixSurpassCount)
        weakdata.setDayFishData(self.userId, WeakData.grandPrix_useSkillTimes,
                                json.dumps(self.grandPrixUseSkillTimes))
        weakdata.setDayFishData(self.userId, WeakData.grandPrix_targetFish,
                                json.dumps(self.grandPrixTargetFish))
        # weakdata.setDayFishData(self.userId, WeakData.grandPrix_levelFpMultiple, json.dumps(self.grandPrixLevelFpMultiple))

    def sendGrandPrixInfo(self):
        """
        发送大奖赛信息 大奖赛相关信息(进入渔场后服务器主动推送)
        """
        if not grand_prix.isGrandPrixOpenTime():  # 是否为大奖赛开放时段 00:00 —— 23:00
            self._resetGrandPrixData()
            self._freeTimes = 0  # 免费次数
            weakdata.setDayFishData(self.userId,
                                    WeakData.grandPrix_getPointsInfo,
                                    json.dumps([]))
            weakdata.setDayFishData(self.userId, WeakData.grandPrix_freeTimes,
                                    self._freeTimes)
        if ftlog.is_debug():
            ftlog.debug("sendGrandPrixInfo", self.grandPrixStartTS,
                        self.isGrandPrixMode())
        signUpState = 1 if self.isGrandPrixMode() else 0  # 是否已经报名
        remainFreeTimes = config.getVipConf(self.vipLevel).get(
            "grandPrixFreeTimes", 0) - self._freeTimes  # 剩余免费次数
        openTime = "-".join(config.getGrandPrixConf("openTimeRange"))  # 时间范围
        mo = MsgPack()
        mo.setCmd("grand_prix_info")
        mo.setResult("gameId", FISH_GAMEID)
        mo.setResult("userId", self.userId)
        mo.setResult("seatId", self.seatId)
        mo.setResult("remainFreeTimes", remainFreeTimes)
        mo.setResult("fee", config.getGrandPrixConf("fee"))  # 报名费
        mo.setResult("openTime", openTime)  # 00:00 - 23:00
        mo.setResult(
            "isInOpenTime",
            1 if grand_prix.isGrandPrixOpenTime() else 0)  # 大奖在是否在开放时间段
        mo.setResult("signUpState", signUpState)  # 是否已报名大奖赛
        mo.setResult(
            "todayRankType",
            RankType.TodayGrandPrix)  # 今日榜Type,使用fish_ranking获取排行榜数据,下同
        mo.setResult("todayDate", util.timestampToStr(int(time.time()),
                                                      "%m/%d"))  # 今日榜时间
        mo.setResult("yesterdayRankType", RankType.LastGrandPrix)
        mo.setResult("yesterdayDate",
                     util.timestampToStr(int(time.time() - 86400), "%m/%d"))
        mo.setResult(
            "des",
            config.getMultiLangTextConf(
                config.getGrandPrixConf("info").get("des"),
                lang=self.lang))  # 每日积分超过2400送100珍珠,今日榜单每10分钟刷新1次,最终排名00:00公布
        mo.setResult("pointsInfo", grand_prix.getPointInfo(
            self.userId))  # 奖励积分 道具Id、道具数量、是否领取了奖励0|1
        mo.setResult("todayMaxPoints",
                     weakdata.getDayFishData(self.userId,
                                             WeakData.grandPrix_point,
                                             0))  # 今日最高积分
        GameMsg.sendMsg(mo, self.userId)
        if ftlog.is_debug():
            ftlog.debug("FishGrandPrixPlayer, userId =", self.userId, "mo =",
                        mo)

    def addGrandPrixFishPoint(self, fishPoint, fishType, gunX):
        """
        添加大奖赛捕鱼积分
        """
        if self.isGrandPrixMode():
            fishPoint = self._calcuCatchFishGrandPrixPoint(
                fishPoint, gunX)  # 计算捕获 vip加成 + 火炮倍率加成
            self.grandPrixFishPoint += fishPoint
            fishType = int(fishType) % 100
            for ft, val in self.grandPrixTargetFish.iteritems():  # 添加目标鱼积分
                if fishType == int(ft) % 100 and val[0] < val[1]:
                    self.grandPrixTargetFish[ft][0] += 1
                    if self.grandPrixTargetFish[ft][0] == val[1]:
                        self.grandPrixFishPoint += val[2]
                    break
            if self.grandPrixFishPoint > self.bestPoint:  # 最好的积分
                self.grandPrixSurpassCount += 1  # 大奖赛超越自己次数
                self._surpassTarget()
        else:
            fishPoint = 0
        return fishPoint

    def getPointReward(self, userId):
        """
        是否获取积分阶段奖励
        """
        rewards = []
        for info in config.getGrandPrixConf("stageInfo"):
            point = info["point"]
            reward = info["rewards"]
            if self.grandPrixFishPoint >= point and point not in self.grandPrixGetPointsInfo:
                rewards.extend(reward)
                self.grandPrixGetPointsInfo.append(point)
        if rewards:
            util.addRewards(userId, rewards,
                            "BI_GRAND_PRIX_POINT_REWARD")  # 添加奖励
            weakdata.setDayFishData(userId, WeakData.grandPrix_getPointsInfo,
                                    json.dumps(self.grandPrixGetPointsInfo))
        return rewards

    def sendGrandPrixCatch(self, catchFishPoints):
        """
        大奖赛捕获消息
        """
        mo = MsgPack()
        mo.setCmd("catch_grand_prix")
        mo.setResult("gameId", FISH_GAMEID)
        mo.setResult("userId", self.userId)
        mo.setResult("seatId", self.seatId)
        mo.setResult("catchFishPoints",
                     catchFishPoints)  # [{"fId": 10010, "point": 5},], # 捕鱼积分
        mo.setResult("fishPoint", self.grandPrixFishPoint)  # 捕鱼积分
        mo.setResult("targetFish", self.grandPrixTargetFish
                     )  # {"11011": [0, 10],},   # 大奖赛目标鱼:[捕获数量, 目标数量]
        mo.setResult(
            "rewards", self.getPointReward(self.userId)
        )  # [{"name": 101, "count": 1000}, {"name": 101, "count": 2000}], # 大奖赛积分奖励
        mo.setResult("pointsInfo", grand_prix.getPointInfo(
            self.userId))  # 奖励积分 道具Id、道具数量、是否领取了奖励0|1
        GameMsg.sendMsg(mo, self.userId)

    def setTipTimer(self):
        """
        设置比赛开始和结束的通知消息
        """
        if self.grandPrixTipTimer:
            self.grandPrixTipTimer.cancel()
            self.grandPrixTipTimer = None
            self.sendGrandPrixInfo()  # 发送大奖赛信息
        curTime = int(time.time())
        dayStartTS = util.getDayStartTimestamp(curTime)
        openTimeRange = config.getGrandPrixConf("openTimeRange")
        pastTime = curTime - dayStartTS  # 03:00|23:10
        beginTime = util.timeStrToInt(openTimeRange[0])  # 00:00
        endTime = util.timeStrToInt(openTimeRange[1])  # 23:00
        if pastTime < beginTime:
            interval = beginTime - pastTime + 5  # 开始的通知
        elif beginTime <= pastTime <= endTime:
            interval = endTime - pastTime + 5  # 结束的通知
        else:
            interval = 86400 + beginTime - pastTime + 5  # 开始的通知
        self.grandPrixTipTimer = FTLoopTimer(interval, 0,
                                             self.setTipTimer)  # 死循环
        self.grandPrixTipTimer.start()

    def _getSurpassTarget(self):
        """
        获取要超越的玩家数据
        """
        timestamp = pktimestamp.getCurrentTimestamp()
        rankClass = RankingBase(self.userId,
                                RankType.TodayGrandPrix,
                                RankDefineIndex.GrandPrix,
                                clientId=None,
                                httpRequest=False)
        if rankClass:
            rankingList = rankClass.getTopNRankUsers(timestamp)
            surpassTarget = config.getGrandPrixConf(
                "surpassTarget")  # [100、80、... 3、2、1]
            masterSurpassTargets = []
            for rank in surpassTarget:
                idx = rank - 1
                if 0 <= idx < len(rankingList.rankingUserList):
                    player = rankingList.rankingUserList[idx]
                    name = util.getNickname(player.userId)
                    avatar = userdata.getAttr(player.userId, "purl")
                    masterSurpassTargets.append({
                        "userId": player.userId,
                        "name": name,
                        "score": player.score,
                        "rank": player.rank + 1,
                        "avatar": avatar
                    })
            self._rankListCache = masterSurpassTargets

    def _surpassTarget(self):
        """请求大奖赛中超越玩家数据"""
        if self.userId:
            msg = MsgPack()
            msg.setCmd("g_surpass")
            msg.setResult("gameId", FISH_GAMEID)
            msg.setResult("roomId", self.table.roomId)
            msg.setResult("tableId", self.table.tableId)
            msg.setResult("userId", self.userId)
            if self.grandPrixSurpassCount <= 1 and self.bestPoint != 0:
                msg.setResult("bestScore", self.grandPrixFishPoint)
            if self._surpassUser.get(self.userId, None):
                if self.grandPrixFishPoint > self._surpassUser.get(
                        self.userId).get("score"):
                    msg.setResult("target", self._surpassUser.get(self.userId))
            if self.grandPrixSurpassCount <= 1 or self._surpassUser.get(
                    self.userId, None):
                GameMsg.sendMsg(msg, self.userId)
            self._updateSurpassUser()

    def _updateSurpassUser(self):
        """更新要超越的目标用户"""
        newTarget = None
        maxScore = max(self.grandPrixFishPoint, self.bestPoint)
        for tar in self._rankListCache:
            if maxScore < tar["score"] and tar["userId"] != self.userId:
                newTarget = tar
                break
        self._surpassUser[self.userId] = newTarget
class BufferFishGroup(object):
    """
    buffer鱼群(比赛场专用)
    """
    def __init__(self, table):
        self.table = table
        self._initData()
        self._tableRank = 0
        self._bufferGroupId = None
        self._nextBufferTimer = None

    def clearTimer(self):
        if self._nextBufferTimer:
            self._nextBufferTimer.cancel()
            self._nextBufferTimer = None

    def initGroup(self, tableRank):
        """初始化"""
        self._tableRank = tableRank
        self._setBufferTimer()

    def _initData(self):
        """初始化数据"""
        self.fishes = {}
        for bufferFishMap in config.getBufferFishConf(
                self.table.runConfig.fishPool, self.table.runConfig.gameMode):
            fishType = bufferFishMap["fishType"]
            self.fishes.setdefault(fishType, [])
            for groupId in self.table.runConfig.allBufferGroupIds:
                if fishType and str(fishType) in groupId:
                    self.fishes[fishType].append(groupId)
            self.fishes[fishType].sort(cmp=cmpGroupId)

    def _setBufferTimer(self):
        """设置buffer定时器"""
        if self._nextBufferTimer:
            self._nextBufferTimer.cancel()
            self._nextBufferTimer = None
        interval_ = random.randint(int(25 + (1 - self._tableRank) * 20),
                                   int((1 - self._tableRank) * 40 + 35))
        if ftlog.is_debug():
            ftlog.debug("_setBufferTimer========>", self.table.tableId,
                        interval_, self._tableRank)
        self._nextBufferTimer = FTLoopTimer(interval_, 0,
                                            self._addBufferFishGroup)
        self._nextBufferTimer.start()

    def _addBufferFishGroup(self):
        """添加buffer鱼群"""
        fishType = 0
        randomNum = random.randint(1, 10000)
        for bufferFishMap in config.getBufferFishConf(
                self.table.runConfig.fishPool, self.table.runConfig.gameMode):
            probb = bufferFishMap["probb"]
            if probb[0] <= randomNum <= probb[-1]:
                fishType = bufferFishMap["fishType"]
                break

        allBufferGroupIds = self.fishes[fishType]
        if ftlog.is_debug():
            ftlog.debug("_addBufferFishGroup", fishType, allBufferGroupIds,
                        self.fishes)
        # 选择一个鱼阵
        if allBufferGroupIds:
            self._bufferGroupId = allBufferGroupIds[self.chooseOneGroup()]
            self.table.insertFishGroup(self._bufferGroupId)
            self._setBufferTimer()

    def chooseOneGroup(self):
        """选择一个鱼群"""
        luckyNums = []
        for player_ in self.table.players:
            if player_:
                luckyNums.append(abs(player_.matchLuckyValue))
        w1 = 0.25 * 10000
        w2 = 0.5 * 10000
        w3 = 0.25 * 10000
        divisionNum = sum(luckyNums)
        if len(luckyNums) > 1 and divisionNum != 0:
            k1 = luckyNums[0] * 1.0 / (divisionNum)
            k1 = min(max(k1, 0.1), 0.9)  # k1 和 k3的值最小是0.1
            k3 = 1 - k1
            k2 = k3 / k1 if k1 >= k3 else k1 / k3

            w1 = int(k1 / (k1 + k2 + k3) * 10000)
            w2 = int(k2 / (k1 + k2 + k3) * 10000)
            w3 = int(k3 / (k1 + k2 + k3) * 10000)

        randomNum = random.randint(1, w1 + w2 + w3)
        if ftlog.is_debug():
            ftlog.debug("chooseOneBufferGroup========>",
                        self.table.runConfig.fishPool, luckyNums, w1, w2, w3,
                        randomNum)
        if 1 <= randomNum <= w1:
            return random.choice([0, 1])
        elif w1 + 1 <= randomNum <= w1 + w2:
            return random.choice([2, 3])
        elif w1 + w2 + 1 <= randomNum <= w1 + w2 + w3:
            return random.choice([4, 5])
        else:
            return random.choice([2, 3])

    def cancelNextGroupTimer(self):
        """取消定时器"""
        if self._nextBufferTimer:
            self._nextBufferTimer.cancel()
            self._nextBufferTimer = None