def _triggerTYOpenItemEvent(event):
    userId = event.userId
    itemKind = event.item.itemKind
    lang = util.getLanguage(userId)
    for key, value in config.getChestConf().iteritems():
        if value["kindId"] == itemKind.kindId:
            from hall.entity import halluser
            from hall.entity.todotask import TodoTaskShowInfo, TodoTaskHelper
            from newfish.entity import mail_system
            message = config.getMultiLangTextConf("ID_OPEN_ITEM_GET_REWARD_MSG", lang=lang) % value["name"]
            GameMsg.sendPrivate(FISH_GAMEID, userId, 0, message)
            todoTask = TodoTaskShowInfo(message)
            TodoTaskHelper.sendTodoTask(FISH_GAMEID, userId, todoTask)
            halluser.ensureGameDataExists(userId, FISH_GAMEID, util.getClientId(userId))
            message = config.getMultiLangTextConf("ID_USE_ITEM_GET_REWARD_MSG", lang=lang).format(itemKind.displayName, value["name"])
            rewards = [{"name": value["chestId"], "count": 1}]
            mail_system.sendSystemMail(userId, mail_system.MailRewardType.ChestReward, rewards, message)
            break
def newChestItem(userId, chestId, eventId, intEventParam=0):
    """
    生成一个宝箱
    """
    idleOrder = getChestIdleOrder(userId)
    if idleOrder < 0:
        ftlog.debug("newChestItem-> not idle order", userId)
        return False
    kindId = chestKindIdMap.get(chestId // 1000, 0)
    if not kindId:
        ftlog.error("newChestItem-> chestId error", chestId, userId)
        return False
    itemKind = hallitem.itemSystem.findItemKind(kindId)
    if not itemKind:
        ftlog.error("newChestItem-> kindId error", kindId, userId)
        return False
    chestConf = config.getChestConf(chestId)
    if not chestConf:
        ftlog.error("newChestItem-> chestConf error", chestId, userId)
        return False
    itemData = itemKind.newItemData()
    itemData.createTime = pktimestamp.getCurrentTimestamp()
    itemData.beginTime = itemData.createTime
    itemData.itemKindId = kindId
    itemData.chestId = chestId
    itemData.order = idleOrder
    itemData.totalTime = int(chestConf["unlockTime"])
    itemData.state = 0
    item = hallitem.itemSystem.newItemFromItemData(itemData)
    if not item:
        ftlog.error("newChestItem-> newItem error", chestId, userId)
        return False
    userBag = hallitem.itemSystem.loadUserAssets(userId).getUserBag()
    timestamp = pktimestamp.getCurrentTimestamp()
    userBag.addItem(FISH_GAMEID, item, timestamp, eventId, intEventParam)
    return True
def _needCoinAsOpenChest(chestId, timeLeft):
    """
    开启宝箱所需金币数
    """
    coin = math.ceil(timeLeft / 60.0) * max(config.getChestConf(chestId)["openCoin"], 1)
    return int(max(100, coin))
def getChestRewards(userId, chestId):
    """
    获取宝箱物品
    """
    rewards = []
    chestConf = config.getChestConf(chestId)
    if not chestConf:
        ftlog.error("getChestRewards error", userId, chestId)
        return rewards
    # 计算金币、珍珠个数
    coinCount = random.randint(chestConf["coinRange"][0], chestConf["coinRange"][1])
    pearlCount = random.randint(chestConf["pearlRange"][0], chestConf["pearlRange"][1])
    if coinCount > 0:
        coin = {"name": CHIP_KINDID, "count": coinCount}
        rewards.append(coin)
    if pearlCount > 0:
        pearl = {"name": PEARL_KINDID, "count": pearlCount}
        rewards.append(pearl)

    # 根据宝箱物品等级确定物品范围
    chestDropList = getChestDropList(chestConf)

    # 根据稀有度划分技能卡片、技能升星卡、火炮皮肤、(黄/紫)水晶
    skillCardRareMap = {}
    starCardRareMap = {}
    gunSkinPile = []
    crystalItems = []
    for chestDrop in chestDropList:
        chestDropType = dict(chestDrop)["type"]
        if chestDropType == ItemType.Skill:
            rare = dict(chestDrop)["rare"]
            skillCardRareMap.setdefault(rare, []).append(chestDrop)
        elif chestDropType == ItemType.Star:
            rare = dict(chestDrop)["rare"]
            starCardRareMap.setdefault(rare, []).append(chestDrop)
        elif chestDropType == ItemType.GunSkin:
            gunSkinPile.append(chestDrop)
        elif chestDropType == ItemType.Crystal:
            crystalItems.append(chestDrop)
    if ftlog.is_debug():
        ftlog.debug("getChestRewards->skillCardRareMap =", skillCardRareMap, "starCardRareMap =", starCardRareMap)

    # 确定技能卡片概率、随机次数、最小数、最大数
    nCardRate = 1 if random.randint(1, 10000) <= chestConf["nCardRate"] else 0
    rCardRate = 1 if random.randint(1, 10000) <= chestConf["rCardRate"] else 0
    srCardRate = 1 if random.randint(1, 10000) <= chestConf["srCardRate"] else 0
    skillCardRare = {1: [nCardRate, chestConf["nCardRandom"], chestConf["nCardRange"][0], chestConf["nCardRange"][1]],
                     2: [rCardRate, chestConf["rCardRandom"], chestConf["rCardRange"][0], chestConf["rCardRange"][1]],
                     3: [srCardRate, chestConf["srCardRandom"], chestConf["srCardRange"][0], chestConf["srCardRange"][1]]}
    if ftlog.is_debug():
        ftlog.debug("getChestRewards->skillCardRare =", skillCardRare)

    # 非水晶宝箱第一次开宝箱必定出合金飞弹技能卡片(已废弃2020.09.25)
    # openChestCount = gamedata.getGameAttr(userId, FISH_GAMEID, GameData.openChestCount)
    # if not util.isCrystalChest(chestId) and not openChestCount:
    #     itemList = [{"name": 1145, "count": 1}]
    #     rewards = _mergeItemList(rewards, itemList)
    #     ftlog.debug("getChestRewards->assignCardList =", rewards)

    # 根据权重随机出具体的技能卡片
    for rare, skillCardDropList in skillCardRareMap.iteritems():
        if not skillCardRare[rare][0]:
            continue
        for randomCount in xrange(skillCardRare[rare][1]):
            minSkillCard = skillCardRare[rare][2]
            maxSkillCard = skillCardRare[rare][3]
            skillCardList = _randomItemInWeight(skillCardDropList, minSkillCard, maxSkillCard)
            rewards = _mergeItemList(rewards, skillCardList)
    if ftlog.is_debug():
        ftlog.debug("getChestRewards->skillCardList =", rewards)

    # 判断是否达到保底条件,给予指定稀有度范围且保底数量的随机技能卡片
    if chestConf["cardCertain"]:
        skillCardDropList = []
        for rare in chestConf["cardCertainRateRange"]:
            skillCardDropList.extend(skillCardRareMap.get(rare, []))
        skillCardIdList = [skillCardDrop["kindId"] for skillCardDrop in skillCardDropList if skillCardDrop]
        rewardList = [reward["name"] for reward in rewards if reward]
        if skillCardIdList and not (set(skillCardIdList) & set(rewardList)):
            cardCertainList = [{"name": random.choice(skillCardIdList), "count": chestConf["cardCertainNum"]}]
            rewards = _mergeItemList(rewards, cardCertainList)
            if ftlog.is_debug():
                ftlog.debug("getChestRewards->cardCertainList =", cardCertainList)

    # 确定技能升星卡片概率、随机次数、最小数、最大数
    nStarCardRate = 1 if random.randint(1, 10000) <= chestConf["nStarCardRate"] else 0
    rStarCardRate = 1 if random.randint(1, 10000) <= chestConf["rStarCardRate"] else 0
    srStarCardRate = 1 if random.randint(1, 10000) <= chestConf["srStarCardRate"] else 0
    starCardRare = {1: [nStarCardRate, chestConf["nStarCardRandom"], chestConf["nStarCardRange"][0], chestConf["nStarCardRange"][1]],
                    2: [rStarCardRate, chestConf["rStarCardRandom"], chestConf["rStarCardRange"][0], chestConf["rStarCardRange"][1]],
                    3: [srStarCardRate, chestConf["srStarCardRandom"], chestConf["srStarCardRange"][0], chestConf["srStarCardRange"][1]]}
    if ftlog.is_debug():
        ftlog.debug("getChestRewards->starCardRare =", starCardRare)

    # 根据权重随机出具体的技能升星卡片
    for rare, starCardDropList in starCardRareMap.iteritems():
        if not starCardRare[rare][0]:
            continue
        for randomCount in xrange(starCardRare[rare][1]):
            minStarCard = starCardRare[rare][2]
            maxStarCard = starCardRare[rare][3]
            starCardList = _randomItemInWeight(starCardDropList, minStarCard, maxStarCard)
            rewards = _mergeItemList(rewards, starCardList)
    if ftlog.is_debug():
        ftlog.debug("getChestRewards->starCardList =", rewards)

    # 判断是否达到保底条件,给予指定稀有度范围且保底数量的随机技能升星卡片
    if chestConf["starCardCertain"]:
        starCardDropList = []
        for rare in chestConf["starCardCertainRateRange"]:
            starCardDropList.extend(starCardRareMap.get(rare, []))
        starCardIdList = [starCardDrop["kindId"] for starCardDrop in starCardDropList if starCardDrop]
        rewardList = [reward["name"] for reward in rewards if reward]
        if starCardIdList and not (set(starCardIdList) & set(rewardList)):
            starCardCertainList = [{"name": random.choice(starCardIdList), "count": chestConf["starCardCertainNum"]}]
            rewards = _mergeItemList(rewards, starCardCertainList)
            if ftlog.is_debug():
                ftlog.debug("getChestRewards->starCardCertainList =", starCardCertainList)

    # 确定火炮皮肤概率、随机次数、最小数、最大数
    gunSkinRate = 1 if random.randint(1, 10000) <= chestConf["gunSkinRate"] else 0
    gunSkinRare = [gunSkinRate, chestConf["gunSkinRandom"], chestConf["gunSkinRange"][0], chestConf["gunSkinRange"][1]]
    if ftlog.is_debug():
        ftlog.debug("getChestRewards->gunSkinRare =", gunSkinRare)

    # 根据权重随机出具体的火炮皮肤
    if gunSkinRate:
        for randomCount in xrange(gunSkinRare[1]):
            minGunSkin = starCardRare[2]
            maxGunSkin = starCardRare[3]
            gunSkinList = _randomItemInWeight(gunSkinPile, minGunSkin, maxGunSkin)
            rewards = _mergeItemList(rewards, gunSkinList)
    if ftlog.is_debug():
        ftlog.debug("getChestRewards->gunSkinList =", rewards)

    # 确定(黄/紫)水晶概率,随机次数,最小数,最大数
    crystalRate = 1 if random.randint(1, 10000) <= chestConf["crystalRate"] else 0
    crystalData = [crystalRate, chestConf["crystalRandom"], chestConf["crystalRange"][0], chestConf["crystalRange"][1]]
    if ftlog.is_debug():
        ftlog.debug("getChestRewards->crystalData =", crystalData)
    # 根据权重随机出具体的水晶
    if crystalRate:
        for randomCount in xrange(crystalData[1]):
            minCrystal = crystalData[2]
            maxCrystal = crystalData[3]
            crystals = _randomItemInWeight(crystalItems, minCrystal, maxCrystal)
            rewards = _mergeItemList(rewards, crystals)
    if ftlog.is_debug():
        ftlog.debug("getChestRewards->crystals =", rewards)

    # 计算奖券个数
    couponRate = 1 if random.randint(1, 10000) <= chestConf["couponRate"] else 0
    if couponRate:
        couponCount = random.randint(chestConf["couponRange"][0], chestConf["couponRange"][1])
        if couponCount > 0:
            coupon = {"name": COUPON_KINDID, "count": couponCount}
            rewards.append(coupon)

    # 计算海星个数
    starfishRate = 1 if random.randint(1, 10000) <= chestConf["starfishRate"] else 0
    if starfishRate:
        starfishCount = random.randint(chestConf["starfishRange"][0], chestConf["starfishRange"][1])
        if starfishCount > 0:
            starfish = {"name": STARFISH_KINDID, "count": starfishCount}
            rewards.append(starfish)

    # 计算冷却个数
    coolDownRate = 1 if random.randint(1, 10000) <= chestConf["coolDownRate"] else 0
    if coolDownRate:
        coolDownCount = random.randint(chestConf["coolDownRange"][0], chestConf["coolDownRange"][1])
        if coolDownCount > 0:
            coolDown = {"name": SKILLCD_KINDID, "count": coolDownCount}
            rewards.append(coolDown)

    # 计算青铜招财珠个数
    bronzeBulletRate = 1 if random.randint(1, 10000) <= chestConf["bronzeBulletRate"] else 0
    if bronzeBulletRate:
        bronzeBulletCount = random.randint(chestConf["bronzeBulletRange"][0], chestConf["bronzeBulletRange"][1])
        if bronzeBulletCount > 0:
            bronzeBullet = {"name": BRONZE_BULLET_KINDID, "count": bronzeBulletCount}
            rewards.append(bronzeBullet)
    # 计算白银招财珠个数
    silverBulletRate = 1 if random.randint(1, 10000) <= chestConf["silverBulletRate"] else 0
    if silverBulletRate:
        silverBulletCount = random.randint(chestConf["silverBulletRange"][0], chestConf["silverBulletRange"][1])
        if silverBulletCount > 0:
            silverBullet = {"name": SILVER_BULLET_KINDID, "count": silverBulletCount}
            rewards.append(silverBullet)
    # 计算黄金招财珠个数
    goldBulletRate = 1 if random.randint(1, 10000) <= chestConf["goldBulletRate"] else 0
    if goldBulletRate:
        goldBulletCount = random.randint(chestConf["goldBulletRange"][0], chestConf["goldBulletRange"][1])
        if goldBulletCount > 0:
            goldBullet = {"name": GOLD_BULLET_KINDID, "count": goldBulletCount}
            rewards.append(goldBullet)

    # 计算红宝石个数
    rubyRate = 1 if random.randint(1, 10000) <= chestConf["rubyRate"] else 0
    if rubyRate:
        rubyCount = random.randint(chestConf["rubyRange"][0], chestConf["rubyRange"][1])
        if rubyCount > 0:
            ruby = {"name": RUBY_KINDID, "count": rubyCount}
            rewards.append(ruby)

    # 计算物品个数
    for _item in chestConf.get("itemsData", []):
        rate = 1 if random.randint(1, 10000) <= _item["rate"] else 0
        if rate:
            count = random.randint(_item["min"], _item["max"])
            if count > 0:
                val = {"name": _item["kindId"], "count": count}
                rewards.append(val)
    _convertOverflowCardToCoin(userId, rewards)
    return rewards
def getChestInfo(chestId):
    """
    获取宝箱物品详情
    """
    chestInfo = {}
    try:
        chestConf = config.getChestConf(chestId)
        chestInfo["chestId"] = int(chestId)
        chestInfo["chestStar"] = chestConf["star"]
        chestInfo["chestType"] = chestConf["type"]
        chestInfo["show"] = chestConf["show"]
        # 各稀有度技能卡数量范围
        skillCardMinCount, skillCardMaxCount = 0, 0
        if chestConf["nCardRate"] > 0:
            cardMinCount, cardMaxCount = getChestCardQuantitativeRange(chestConf["nCardRate"],
                                                                       chestConf["nCardRandom"],
                                                                       chestConf["nCardRange"])
            skillCardMinCount += cardMinCount
            skillCardMaxCount += cardMaxCount
        if chestConf["rCardRate"] > 0:
            cardMinCount, cardMaxCount = getChestCardQuantitativeRange(chestConf["rCardRate"],
                                                                       chestConf["rCardRandom"],
                                                                       chestConf["rCardRange"])
            skillCardMinCount += cardMinCount
            skillCardMaxCount += cardMaxCount
        if chestConf["srCardRate"] > 0:
            cardMinCount, cardMaxCount = getChestCardQuantitativeRange(chestConf["srCardRate"],
                                                                       chestConf["srCardRandom"],
                                                                       chestConf["srCardRange"])
            skillCardMinCount += cardMinCount
            skillCardMaxCount += cardMaxCount
        if chestConf["cardCertainNum"] > 0:
            skillCardMinCount = max(skillCardMinCount, chestConf["cardCertainNum"])
        # 各稀有度升星卡数量范围
        starCardMinCount, starCardMaxCount = 0, 0
        if chestConf["nStarCardRate"] > 0:
            cardMinCount, cardMaxCount = getChestCardQuantitativeRange(chestConf["nStarCardRate"],
                                                                       chestConf["nStarCardRandom"],
                                                                       chestConf["nStarCardRange"])
            starCardMinCount += cardMinCount
            starCardMaxCount += cardMaxCount
        if chestConf["rCardRate"] > 0:
            cardMinCount, cardMaxCount = getChestCardQuantitativeRange(chestConf["rStarCardRate"],
                                                                       chestConf["rStarCardRandom"],
                                                                       chestConf["rStarCardRange"])
            starCardMinCount += cardMinCount
            starCardMaxCount += cardMaxCount
        if chestConf["srStarCardRate"] > 0:
            cardMinCount, cardMaxCount = getChestCardQuantitativeRange(chestConf["srStarCardRate"],
                                                                       chestConf["srStarCardRandom"],
                                                                       chestConf["srStarCardRange"])
            starCardMinCount += cardMinCount
            starCardMaxCount += cardMaxCount
        if chestConf["starCardCertainNum"] > 0:
            starCardMinCount = max(starCardMinCount, chestConf["starCardCertainNum"])

        if skillCardMinCount or skillCardMaxCount:
            skillCard = {}
            chestInfo["skillCard"] = skillCard
            skillCard["kinds"] = [kind["kindId"] for kind in getChestDropList(chestConf, ItemType.Skill)]
            skillCard["count"] = [skillCardMinCount, skillCardMaxCount]
        if starCardMinCount or starCardMaxCount:
            starCard = {}
            chestInfo["starCard"] = starCard
            starCard["kinds"] = [kind["kindId"] for kind in getChestDropList(chestConf, ItemType.Star)]
            starCard["count"] = [starCardMinCount, starCardMaxCount]

        chestInfo["items"] = []
        # 水晶数量范围
        if chestConf["crystalRate"] > 0:
            crystalMinCount, crystalMaxCount = getChestCardQuantitativeRange(chestConf["crystalRate"],
                                                                             chestConf["crystalRandom"],
                                                                             chestConf["crystalRange"])
            if crystalMinCount or crystalMaxCount:
                crystalItem = {}
                crystalItem["kinds"] = [kind["kindId"] for kind in getChestDropList(chestConf, ItemType.Crystal)]
                crystalItem["count"] = [crystalMinCount, crystalMaxCount]
                chestInfo["items"].append(crystalItem)

        if chestConf["coinRange"][0]:
            chestInfo["coin"] = [chestConf["coinRange"][0], chestConf["coinRange"][1]]
        if chestConf["pearlRange"][0]:
            chestInfo["pearl"] = [chestConf["pearlRange"][0], chestConf["pearlRange"][1]]
        if chestConf["couponRate"]:
            chestInfo["coupon"] = [chestConf["couponRange"][0], chestConf["couponRange"][1]]
        if chestConf["starfishRate"]:
            chestInfo["starfish"] = [chestConf["starfishRange"][0], chestConf["starfishRange"][1]]
        if chestConf["coolDownRate"]:
            chestInfo["coolDown"] = [chestConf["coolDownRange"][0], chestConf["coolDownRange"][1]]
        if chestConf["bronzeBulletRate"]:
            chestInfo["bronzeBullet"] = [chestConf["bronzeBulletRange"][0], chestConf["bronzeBulletRange"][1]]
        if chestConf["silverBulletRate"]:
            chestInfo["silverBullet"] = [chestConf["silverBulletRange"][0], chestConf["silverBulletRange"][1]]
        if chestConf["goldBulletRate"]:
            chestInfo["goldBullet"] = [chestConf["goldBulletRange"][0], chestConf["goldBulletRange"][1]]
        if chestConf["rubyRate"]:
            chestInfo["ruby"] = [chestConf["rubyRange"][0], chestConf["rubyRange"][1]]
        for _item in chestConf.get("itemsData", []):
            if _item["rate"]:
                val = {}
                val["kinds"] = [_item["kindId"]]
                val["count"] = [_item["min"], _item["max"]]
                chestInfo["items"].append(val)
    except Exception as e:
        ftlog.error("getChestInfo error", chestId, e)
        return chestInfo
    return chestInfo
def getChestList(userId):
    """
    获取宝箱列表
    """
    module_tip.resetModuleTip(userId, "chest")
    userBag = hallitem.itemSystem.loadUserAssets(userId).getUserBag()
    chestItemList = userBag.getAllTypeItem(TYChestItem)
    chestItemList = sorted(chestItemList, key=lambda chestItem: chestItem.order)
    chestList = []
    openedChestList = []
    for chestItem in chestItemList:
        if not chestItem.chestId:
            ftlog.error("chest item error", userId)
            continue
        openingOrder = [chestItemTmp.order for chestItemTmp in chestItemList if chestItemTmp.state == 1]
        chestConf = config.getChestConf(chestItem.chestId)
        chest = {}
        if chestItem.totalTime <= 0:
            chestItem.state = ChestState.Opened
        if chestItem.state == ChestState.WaitOpen and not openingOrder:
            chestItem.state = ChestState.Opening
        if chestItem.state == ChestState.WaitOpen:
            chestTimeLeft = chestItem.totalTime
        elif chestItem.state == ChestState.Opening:
            chestTimeLeft = chestItem.beginTime + chestItem.totalTime - pktimestamp.getCurrentTimestamp() + CHEST_OPEN_DELAY_TIME
        else:
            chestTimeLeft = 0
        if chestItem.state == ChestState.Opening and chestTimeLeft <= 0:
            chestItem.state = ChestState.Opened
            noOpenOrderList = [chestItemTmp.order for chestItemTmp in chestItemList if chestItemTmp.state == 0]
            if noOpenOrderList:
                nextOpeningChestItem = None
                for noOpenOrder in noOpenOrderList:
                    if noOpenOrder > chestItem.order:
                        nextOpeningChestItem = [chestItemImp for chestItemImp in chestItemList if chestItemImp.order == noOpenOrder][0]
                        break
                if not nextOpeningChestItem:
                    nextOpeningChestItem = [chestItemImp for chestItemImp in chestItemList if chestItemImp.order == noOpenOrderList[0]][0]
                if nextOpeningChestItem:
                    nextOpeningChestItem.state = ChestState.Opening
                    nextOpeningChestItem.beginTime = chestItem.beginTime + chestItem.totalTime
                    userBag.updateItem(FISH_GAMEID, nextOpeningChestItem, pktimestamp.getCurrentTimestamp())
                    for chestTmp in chestList:
                        if chestTmp["order"] == nextOpeningChestItem.order:
                            chestTmp["state"] = 1
                            chestTmp["timeLeft"] = nextOpeningChestItem.beginTime + nextOpeningChestItem.totalTime - \
                                                   pktimestamp.getCurrentTimestamp() + CHEST_OPEN_DELAY_TIME
                            break
        chestTimeLeft = max(0, chestTimeLeft)
        chest["order"] = chestItem.order
        chest["state"] = chestItem.state
        chest["chestId"] = chestItem.chestId
        chest["itemId"] = chestItem.itemId
        chest["kindId"] = chestItem.kindId
        chest["desc"] = chestItem.itemKind.desc
        chest["createTime"] = chestItem.createTime
        chest["timeLeft"] = chestTimeLeft
        chest["totalTime"] = chestItem.totalTime
        chest["openCoin"] = chestConf["openCoin"]
        chest["diamond"] = _needCoinAsOpenChest(chestItem.chestId, chestTimeLeft)
        chest["info"] = getChestInfo(chestItem.chestId)
        chestList.append(chest)
        userBag.updateItem(FISH_GAMEID, chestItem, pktimestamp.getCurrentTimestamp())
        if chestItem.state == ChestState.Opened:
            openedChestList.append(chestItem.itemId)
    ftlog.debug("getChestList->", chestList)
    module_tip.addModuleTipEvent(userId, "chest", openedChestList)
    return chestList