Ejemplo n.º 1
0
 def check(self):
     playerrequest = PlayerRequest.getByKey(self.__player.id)
     # プレイ情報.
     playdata = GachaPlayData.getByKey(GachaPlayData.makeID(self.__player.id, self.__gachamaster.boxid))
     playcount = GachaPlayCount.getByKey(GachaPlayCount.makeID(self.__player.id, self.__gachamaster.id))
     
     if playerrequest.req_confirmkey == self.__player.req_confirmkey or playerrequest.req_alreadykey != self.__player.req_confirmkey:
         raise AppTestError(u'ガチャプレイ情報の確認キーが更新されていない')
     elif playcount.getTodayPlayCnt() < 1:
         raise AppTestError(u'ガチャプレイ回数が増えていない.%d, %d, %s' % (playcount.getTodayPlayCnt(), playcount.cnt, playcount.ptime))
     
     # BOX確認.
     box = GachaBox(self.__gachamaster, playdata)
     if box.get_rest_num() != 1:
         raise AppTestError(u'BOXのカードがうまく消費されていない.%d' % box.get_rest_num())
     
     # カードが増えているか.
     cardnum = BackendApi.get_cardnum(self.__player.id)
     if cardnum != self.__gachamaster.continuity:
         raise AppTestError(u'カード枚数がおかしい %d vs %d' % (cardnum, self.__gachamaster.continuity))
     elif cardnum != len(playdata.result['result']):
         raise AppTestError(u'ガチャの結果の長さがおかしい %d vs %d' % (cardnum, len(playdata.result['result'])))
     
     # おまけが来ているか.
     present_num = BackendApi.get_present_num(self.__player.id)
     if present_num < 1:
         raise AppTestError(u'おまけが来ていない')
     
     # チケット消費確認.
     playergachapt = PlayerGachaPt.getByKey(self.__player.id)
     if 0 < playergachapt.tryluckticket:
         raise AppTestError(u'チケットを消費していない')
Ejemplo n.º 2
0
    def check(self):
        model_mgr = ModelRequestMgr()
        # 課金情報.
        entry = BackendApi.get_gachapaymententry(model_mgr,
                                                 self.__payment_entry.id)
        if entry is None:
            raise AppTestError(u'課金レコードが作成されていない')
        elif entry.state != PaymentData.Status.COMPLETED:
            raise AppTestError(u'課金ステータスが異常です.status=%s' % entry.state)

        playerrequest = PlayerRequest.getByKey(self.__player.id)
        # プレイ情報.
        playdata = GachaPlayData.getByKey(
            GachaPlayData.makeID(self.__player.id, self.__gachamaster.boxid))
        playcount = GachaPlayCount.getByKey(
            GachaPlayCount.makeID(self.__player.id, self.__gachamaster.id))

        if playerrequest.req_confirmkey == self.__player.req_confirmkey or playerrequest.req_alreadykey != self.__player.req_confirmkey:
            raise AppTestError(u'ガチャプレイ情報の確認キーが更新されていない')
        elif playcount.getTodayPlayCnt() < 1:
            raise AppTestError(
                u'ガチャプレイ回数が増えていない.%d, %d, %s' %
                (playcount.getTodayPlayCnt(), playcount.cnt, playcount.ptime))

        # BOX確認.
        box = GachaBox(self.__gachamaster, playdata)
        if box.get_rest_num() != 1:
            raise AppTestError(u'BOXのカードがうまく消費されていない.%d' % box.get_rest_num())

        # カードが増えているか.
        cardnum = BackendApi.get_cardnum(self.__player.id)
        tmp_card_num = min(self.__cardnum + entry.continuity,
                           self.__player.cardlimit)
        if tmp_card_num != cardnum:
            raise AppTestError(u'カード枚数がおかしい %d vs %d' %
                               (self.__cardnum, cardnum))

        # おまけが来ているか.
        present_num = BackendApi.get_present_num(self.__player.id)
        if (self.__present_num + self.__gachamaster.continuity) == present_num:
            raise AppTestError(u'おまけが来ていない')
Ejemplo n.º 3
0
    def check(self):
        model_mgr = ModelRequestMgr()
        # 課金情報.
        entry = BackendApi.get_gachapaymententry(model_mgr,
                                                 self.__payment_entry.id)
        if entry is None:
            raise AppTestError(u'課金レコードが作成されていない')
        elif entry.state != PaymentData.Status.COMPLETED:
            raise AppTestError(u'課金ステータスが異常です.status=%s' % entry.state)

        playerrequest = PlayerRequest.getByKey(self.__player.id)
        # プレイ情報.
        playdata = GachaPlayData.getByKey(
            GachaPlayData.makeID(self.__player.id, self.__gachamaster.boxid))
        playcount = GachaPlayCount.getByKey(
            GachaPlayCount.makeID(self.__player.id, self.__gachamaster.id))

        if playerrequest.req_confirmkey == self.__player.req_confirmkey or playerrequest.req_alreadykey != self.__player.req_confirmkey:
            raise AppTestError(u'ガチャプレイ情報の確認キーが更新されていない')
        elif playcount.getTodayPlayCnt() < 1:
            raise AppTestError(
                u'ガチャプレイ回数が増えていない.%d, %d, %s' %
                (playcount.getTodayPlayCnt(), playcount.cnt, playcount.ptime))

        # BOX確認.
        box = GachaBox(self.__gachamaster, playdata)
        if box.get_rest_num() != 1:
            raise AppTestError(u'BOXのカードがうまく消費されていない.%d' % box.get_rest_num())

        # カードが増えているか.
        cardnum = BackendApi.get_cardnum(self.__player.id)
        tmp_card_num = min(self.__cardnum + entry.continuity,
                           self.__player.cardlimit)
        if tmp_card_num != cardnum:
            raise AppTestError(u'カード枚数がおかしい %d vs %d' %
                               (self.__cardnum, cardnum))

        # おまけが来ているか.
        present_num = BackendApi.get_present_num(self.__player.id)
        if (self.__present_num + self.__gachamaster.continuity) == present_num:
            raise AppTestError(u'おまけが来ていない')

        # ランキングスコア情報.
        scoredata = RankingGachaScore.getByKey(
            RankingGachaScore.makeID(self.__player.id,
                                     self.__rankingmaster.id))
        if scoredata is None:
            raise AppTestError(u'ランキングのスコア情報が作成されていない')

        point = self.__gachamaster.continuity * ApiTest.RANKING_POINT
        # 累計Pt.
        if scoredata.total != point:
            raise AppTestError(u'累計Ptが正しくない.%d vs %d' %
                               (scoredata.total, point))

        # 単発Pt.
        if scoredata.single != point:
            raise AppTestError(u'単発Ptが正しくない.%d vs %d' %
                               (scoredata.single, point))

        # 総計Pt.
        if scoredata.firstpoint != point:
            raise AppTestError(u'初回総計Ptが正しくない.%d vs %d' %
                               (scoredata.firstpoint, point))

        wholedata = RankingGachaWholeData.getByKey(self.__rankingmaster.id)
        if wholedata is None:
            raise AppTestError(u'総計Ptが保存されていない')
        elif wholedata.point != point:
            raise AppTestError(u'総計Ptが正しくない.%d vs %d' %
                               (wholedata.point, point))

        # 総計Pt達成報酬用のキュー.
        queue_cnt = RankingGachaWholePrizeQueue.count()
        if (self.__queue_cnt + 1) != queue_cnt:
            raise AppTestError(u'総計Pt達成報酬のキューが正しく積まれていない.%d vs %d' %
                               (self.__queue_cnt + 1, queue_cnt))
Ejemplo n.º 4
0
    def handle(self, *args, **options):

        print '================================'
        print 'copy_img'
        print '================================'

        out = args[0]

        # ガチャのマスターを取得.
        gachalist = GachaMaster.fetchValues(filters={
            'consumetype__in':
            Defines.GachaConsumeType.PAYMENT_TYPES,
            'schedule__gt':
            0
        },
                                            using=backup_db)

        # ガチャのボックスデータを作成.
        read_boxids = []

        card_dict = {}
        for gacha in gachalist:
            if gacha.boxid in read_boxids:
                continue

            schedule = ScheduleMaster.getByKey(gacha.schedule, using=backup_db)
            name = u'%s(%s-%s)' % (gacha.name,
                                   schedule.stime.strftime('%m/%d'),
                                   schedule.stime.strftime('%m/%d'))

            boxmaster = GachaBoxMaster.getByKey(gacha.boxid, using=backup_db)
            gachamasterset = GachaMasterSet(gacha, boxmaster, schedule)

            gachabox = GachaBox(gachamasterset,
                                GachaPlayData.makeInstance(
                                    GachaPlayData.makeID(0, gacha.boxid)),
                                blank=True)
            grouplist = GachaGroupMaster.getByKey(gachabox.get_group_id_list(),
                                                  using=backup_db)

            # カードIDとガチャのIDをひもづける.
            for group in grouplist:
                if 1 < len(group.table):
                    continue
                cardid = group.table[0]['id']
                arr = card_dict[cardid] = card_dict.get(cardid) or []
                arr.append(name)

        # カードマスターを取得.
        cardmasterlist = CardMaster.getByKey(card_dict.keys(),
                                             order_by='id',
                                             using=backup_db)

        # CSVを作成.
        rows = []

        def makeRow(row):
            arr = []
            for v in row:
                s = u'%s' % v
                s = s.replace('"', '""')
                arr.append(u'"%s"' % s)
            return u','.join(arr)

        for cardmaster in cardmasterlist:
            cardsortmaster = CardSortMaster.getByKey(cardmaster.id,
                                                     using=backup_db)

            row = [
                cardmaster.id, cardmaster.name,
                Defines.Rarity.NAMES[cardsortmaster.rare],
                Defines.CharacterType.NAMES[cardsortmaster.ctype]
            ]
            row.extend(card_dict[cardmaster.id])
            str_row = makeRow(row)
            print str_row
            rows.append(str_row)
        csv_data = StrUtil.to_s(u'\n'.join(rows), dest_enc='shift-jis')

        f = None
        try:
            f = open(out, "w")
            f.write(csv_data)
            f.close()
        except:
            if f:
                f.close()
                f = None
            raise

        print '================================'
        print 'output:%s' % out
        print 'all done..'
Ejemplo n.º 5
0
    def process(self):
        args = self.getUrlArgs('/gacharesult/')
        try:
            mid = int(args.get(0))
            key = urllib.unquote(args.get(1))
            code = int(args.get(2) or 0)
        except:
            raise CabaretError(u'引数が想定外です', CabaretError.Code.ILLEGAL_ARGS)
        self.set_masterid(mid)

        self.__now = OSAUtil.get_now()

        v_player = self.getViewerPlayer()

        self.html_param['player'] = Objects.player(self, v_player)

        if code != 0:
            # エラーで引抜できなかった.
            self.procGachaError(code)
            return

        if v_player.req_alreadykey != key:
            # 結果が見当たらない.
            if settings_sub.IS_LOCAL:
                raise CabaretError(u'結果を出せない')
            url = UrlMaker.gacha()
            self.appRedirect(self.makeAppLinkUrlRedirect(url))
            return

        gachamaster = self.getGachaMaster()
        playdata = self.getGachaPlayData()
        model_mgr = self.getModelMgr()

        CONTENT_NUM_PER_PAGE = 10
        PAGING_CONTENT_NUM = CONTENT_NUM_PER_PAGE + 2

        # 獲得したカード.
        resultlist = playdata.result['result'] if isinstance(
            playdata.result, dict) else playdata.result

        if PlayerCrossPromotion.is_session(
        ):  # if we are in a cross promotion event
            self.ssr_card_crosspromotion(v_player, resultlist)

        result_num = len(resultlist)
        page = int(self.request.get(Defines.URLQUERY_PAGE) or 0)
        if PAGING_CONTENT_NUM < result_num:
            # 結果が多いのでページング.
            url = UrlMaker.gacharesult(mid, key, code)
            self.putPagenation(url, page, result_num, CONTENT_NUM_PER_PAGE)

            start = page * CONTENT_NUM_PER_PAGE
            end = start + CONTENT_NUM_PER_PAGE
            self.html_param['is_paging'] = True
        else:
            start = 0
            end = result_num

        cardidlist = [data['id'] for data in resultlist[start:end]]
        cardmasters = BackendApi.get_cardmasters(cardidlist,
                                                 model_mgr,
                                                 using=settings.DB_READONLY)
        obj_cardlist = []
        point_single = 0
        sellprice_total = 0
        sellprice_treasure_total = 0
        sell_num = 0
        for idx, data in enumerate(resultlist):
            point = data['point']
            obj_card = None
            if start <= idx < end:
                master = cardmasters[data['id']]
                card = BackendApi.create_card_by_master(master)
                cardset = CardSet(card, master)
                obj_card = Objects.card(self, cardset, is_new=data['is_new'])
                obj_card['point'] = point
                obj_cardlist.append(obj_card)
            sellprice = data.get('sellprice', 0)
            sellprice_treasure = data.get('sellprice_treasure', 0)
            point_single += point
            if data.get('autosell'):
                if obj_card:
                    obj_card['autosell'] = True
                    obj_card['sellprice'] = sellprice
                    obj_card['sellprice_treasure'] = sellprice_treasure
                sell_num += 1
                sellprice_total += sellprice
                sellprice_treasure_total += sellprice_treasure

        point_total = 0
        if gachamaster.consumetype == Defines.GachaConsumeType.RANKING:
            rankingmaster = BackendApi.get_rankinggacha_master(
                model_mgr, gachamaster.boxid, using=settings.DB_READONLY)
            if rankingmaster and rankingmaster.is_support_totalranking:
                point_total = BackendApi.get_rankinggacha_total_score(
                    playdata.mid, v_player.id)
        elif gachamaster.consumetype == Defines.GachaConsumeType.SR_SSR_PROBABILITY_UP or gachamaster.consumetype == Defines.GachaConsumeType.PTCHANGE:
            #トレードショップが開いていたら
            if gachamaster.trade_shop_master_id is not None and 0 < gachamaster.trade_shop_master_id:
                try:
                    lottery_point = int(args.get(3))
                except:
                    raise CabaretError(u'引数が想定外です',
                                       CabaretError.Code.ILLEGAL_ARGS)
                self.html_param['lottery_point'] = lottery_point
                self.html_param[
                    'user_point'] = BackendApi.get_tradeshop_userpoint(
                        model_mgr, v_player.id)

        if gachamaster.consumetype in {
                Defines.GachaConsumeType.FIXEDSR,
                Defines.GachaConsumeType.STEPUP,
                Defines.GachaConsumeType.STEPUP2
        } and 0 < gachamaster.rarity_fixed_num:
            if page == 0:
                self.html_param['is_rarity_fixed'] = True
                self.html_param[
                    'rarity_fixed_cardlist'] = obj_cardlist[:gachamaster.
                                                            rarity_fixed_num]
                self.html_param['cardlist'] = obj_cardlist[gachamaster.
                                                           rarity_fixed_num:]
            else:
                self.html_param['is_rariry_fixed'] = False
                self.html_param['rarity_fixed_cardlist'] = obj_cardlist
                self.html_param['cardlist'] = obj_cardlist
        else:
            self.html_param['cardlist'] = obj_cardlist

        self.html_param['gacha_result_unique_name'] = gachamaster.unique_name
        self.html_param['point_single'] = point_single
        self.html_param['point_total'] = point_total
        self.html_param['_card_num'] = sell_num
        self.html_param['_gold_add'] = sellprice_total
        self.html_param['_ckt'] = sellprice_treasure_total
        self.html_param['url_tradeshop'] = self.makeAppLinkUrl(
            UrlMaker.tradeshop())
        # 引き抜きガチャチケット消費数.
        self.html_param['gacha_ticket_cost'] = Defines.GACHA_TICKET_COST_NUM

        # 開催中のガチャ情報.
        topic = Defines.GachaConsumeType.TO_TOPIC.get(gachamaster.consumetype)
        self.putOpenGachaList(topic, gachamaster)

        # シート情報.
        seatinfo = BackendApi.make_gachaseatinfo(self,
                                                 v_player.id,
                                                 gachamaster,
                                                 result=True,
                                                 using=settings.DB_READONLY)
        self.html_param['gachaseatinfo'] = seatinfo

        # おまけ等.
        omakelist = []
        if seatinfo and seatinfo['last']:
            omakelist.append(
                '%s%s' %
                (seatinfo['last']['name'], seatinfo['last']['numtext']))
        if isinstance(playdata.result, dict) and playdata.result.get('omake'):
            prizelist = BackendApi.get_prizelist(model_mgr,
                                                 playdata.result['omake'],
                                                 using=settings.DB_READONLY)
            presentlist = BackendApi.create_present_by_prize(
                model_mgr,
                v_player.id,
                prizelist,
                0,
                using=settings.DB_READONLY,
                do_set_save=False)
            presentsetlist = PresentSet.presentToPresentSet(
                model_mgr, presentlist, using=settings.DB_READONLY)
            for presentset in presentsetlist:
                omakelist.append(
                    '%s%s' % (presentset.itemname, presentset.numtext_with_x))
        self.html_param['omakelist'] = omakelist

        seatPlayData = self.__get_seatPlayData_only_firstRound_and_last(
            model_mgr, v_player.id, gachamaster)
        if seatPlayData:
            for i, seat in enumerate(seatinfo["list"]):
                if seat == None:
                    continue
                if i == seatPlayData.last:
                    seat["last"] = True
                else:
                    seat["last"] = False
            itemhash = seatinfo["list"][seatPlayData.last]
            omakelist[0] = '%s%s' % (itemhash['name'], itemhash['numtext'])

        boxgachaprizes = []
        # 条件付きリセット可能BOXガチャの引き切り報酬
        if gachamaster.consumetype in {
                Defines.GachaConsumeType.LIMITED_RESET_BOX,
                Defines.GachaConsumeType.EVENTTICKET
        }:
            boxgacha = GachaBox(gachamaster, playdata)
            if boxgacha.is_empty:
                boxdetail = model_mgr.get_model(GachaBoxGachaDetailMaster,
                                                gachamaster.id,
                                                using=settings.DB_READONLY)
                prizelist = BackendApi.get_prizemaster_list(
                    model_mgr, boxdetail.prizelist)
                presentlist = BackendApi.create_present_by_prize(
                    model_mgr,
                    v_player.id,
                    prizelist,
                    0,
                    using=settings.DB_READONLY,
                    do_set_save=False)
                presentsetlist = PresentSet.presentToPresentSet(
                    model_mgr, presentlist, using=settings.DB_READONLY)
                for presentset in presentsetlist:
                    boxgachaprizes.append(
                        '%s%s' %
                        (presentset.itemname, presentset.numtext_with_x))
                self.html_param['boxgachaprizes'] = boxgachaprizes
        # ガチャ確率 UP の場合最後に引いたガチャをサジェストする
        if gachamaster.consumetype in {
                Defines.GachaConsumeType.SR_SSR_PROBABILITY_UP,
                Defines.GachaConsumeType.FIXEDSR,
                Defines.GachaConsumeType.PTCHANGE,
                Defines.GachaConsumeType.NEWSTORE_SUPPORT_PREMIUM
        }:
            self.put_gacha_probability(gachamaster)

        self.html_param['explain_text'] = self.html_param["gachadata"].values(
        )[0]['explain_text']
        self.writeAppHtml('gacha/result')
Ejemplo n.º 6
0
    def __testGacha(self, gachamaster, cnt):
        """ガチャをテスト実行.
        """
        model_mgr = self.getModelMgr()

        test_playdata = GachaPlayData.makeInstance(
            GachaPlayData.makeID(0, gachamaster.boxid))

        # BOX.
        box = GachaBox(gachamaster, test_playdata)
        groupidlist = box.get_group_id_list()
        groupdict = dict([
            (groupmaster.id, GachaBoxGroup(groupmaster))
            for groupmaster in BackendApi.get_gachagroupmaster_list(
                model_mgr, groupidlist, using=backup_db)
        ])

        # 設定値.
        group_rate_dict = {}
        cardrate_dict = {}
        cardgroup_dict = {}
        for groupid in groupidlist:
            rate = box.get_group_rate(groupid)
            group_rate_dict[groupid] = group_rate_dict.get(groupid, 0) + rate

            group_cardrate_dict = {}
            group = groupdict[groupid]
            for carddata in group.carddata_list:
                cardid = carddata.card
                group_cardrate_dict[cardid] = group_cardrate_dict.get(
                    cardid, 0) + carddata.rate

                arr = cardgroup_dict[cardid] = cardgroup_dict.get(cardid) or []
                arr.append(groupid)

            cardrate_dict[groupid] = group_cardrate_dict

        # ランダム.
        rand = AppRandom()

        #        result_list = []
        distribution_dict = dict.fromkeys(cardgroup_dict.keys(), 0)
        group_distribution_dict = dict.fromkeys(groupdict.keys(), 0)

        def addResult(cardid, point, groupid):
            #            result_list.append((cardid, point, groupid))
            distribution_dict[cardid] = (distribution_dict.get(cardid)
                                         or 0) + 1
            group_distribution_dict[groupid] = (
                group_distribution_dict.get(groupid) or 0) + 1

        # ランキングガチャ用ポイント獲得関数.
        point_getter = lambda x: 0
        if gachamaster.consumetype == Defines.GachaConsumeType.RANKING:
            rankinggachamaster = BackendApi.get_rankinggacha_master(
                model_mgr, gachamaster.boxid, using=backup_db)
            if rankinggachamaster:
                randmax = rankinggachamaster.randmax
                randmin = rankinggachamaster.randmin
                point_getter = lambda x: x.point * (100 + rand.getIntN(
                    randmin + randmax) - randmin) / 100

        # BOXからグループを選択.
        groupidlist = []
        for _ in xrange(cnt):
            if box.is_empty:
                # 空になったのでリセット.
                test_playdata.resetGroupCounts()
                box = GachaBox(gachamaster, test_playdata)
            groupid, _ = box.select(rand)
            group = groupdict[groupid]
            carddata = group.select_obj(rand)
            addResult(carddata.card, point_getter(carddata), groupid)


#        self.html_param['result_list'] = result_list
        self.html_param['distribution_dict'] = distribution_dict
        self.html_param['group_dict'] = groupdict
        self.html_param['group_distribution_dict'] = group_distribution_dict
        self.html_param['group_rate_dict'] = group_rate_dict
        self.html_param['group_rate_total'] = sum(group_rate_dict.values())
        self.html_param['cardrate_dict'] = cardrate_dict
        self.html_param['cardgroup_dict'] = cardgroup_dict

        cardmaster_idlist = distribution_dict.keys()
        cardmaster_dict = BackendApi.get_cardmasters(cardmaster_idlist,
                                                     model_mgr,
                                                     using=backup_db)
        self.html_param['cardmaster_dict'] = cardmaster_dict
Ejemplo n.º 7
0
    def __valid_master(self, master):
        model_mgr = self.getModelMgr()

        if not master.is_public:
            return

        boxmaster = GachaBoxMaster.getByKey(master.boxid)
        if boxmaster is None:
            raise ModelEditValidError(u'存在しないBOXが設定されています.gacha=%d' % master.id)

        if master.stepid != 0:
            stepmaster = GachaStepupMaster.getByKey(master.stepid)
            if stepmaster is None:
                raise ModelEditValidError(u'存在しないSTEPが設定されています.gacha=%d' % master.id)

        if master.seattableid:
            seatmaster = GachaSeatTableMaster.getByKey(master.seattableid)
            if seatmaster is None:
                raise ModelEditValidError(u'存在しないシートテーブルが設定されています.gacha=%d' % master.id)

        if master.gacha_explain_text_id:
            gachaexplainmaster = GachaExplainMaster.getByKey(master.gacha_explain_text_id)
            if gachaexplainmaster is None:
                raise ModelEditValidError(u'存在しないGachaExplainMasterが設定されています.gacha=%d' % master.id)

        if self.valid_error_num < 10:
            for record in self.allmasters:
                if master.id == record.id:
                    self.valid_error_num += 1
                    raise ModelEditValidError(u'IDが重複しています.id={}'.format(master.id))

        master.bonus = master.bonus or []
        if master.bonus:
            def checkBonus(bonus):
                items = {}
                for item in PrizeMaster.getByKey(bonus):
                    items[item.id] = item
                for itemid in bonus:
                    if items.get(itemid) is None:
                        raise ModelEditValidError(u'存在しない引抜のおまけアイテムが設定されています.gacha=%d' % master.id)

            if isinstance(master.bonus[0], dict):
                for data in master.bonus:
                    if data.get('rate', 0) < 1:
                        raise ModelEditValidError(u'確率がないおまけが設定されています.gacha=%d' % master.id)
                    elif not data.get('prize'):
                        raise ModelEditValidError(u'内容がないおまけが設定されています.gacha=%d' % master.id)
                    checkBonus(data['prize'])
            else:
                checkBonus(master.bonus)

        playdata = GachaPlayData.makeInstance(0)
        playdata.mid = master.boxid

        if master.consumetype in Defines.GachaConsumeType.BOX_TYPES:
            # ボックスガチャ.
            if not GachaBox(GachaMasterSet(master, boxmaster), playdata).is_boxgacha:
                raise ModelEditValidError(u'%sなのにBOXでないboxidが指定されています.gacha=%d' % (Defines.GachaConsumeType.NAMES[master.consumetype], master.id))

        if not isinstance(master.variableconsumevalue, dict):
            master.variableconsumevalue = {}

        # 回数別のBOXの検証.
        master.special_boxid = master.special_boxid or []
        if master.special_boxid:
            master_special_boxid = dict(master.special_boxid)
            boxmaster_dict = dict([(bm.id, bm) for bm in GachaBoxMaster.getByKey(master_special_boxid.values())])
            if len(list(set(master_special_boxid.values()))) != len(boxmaster_dict.keys()):
                raise ModelEditValidError(u'存在しないboxidがspecial_boxidに設定されています.gacha=%d' % master.id)

            special_box = dict([(cnt, boxmaster_dict[boxid].box) for cnt, boxid in master.special_boxid])
            box = GachaBox(GachaMasterSet(master, boxmaster), playdata, special_box=special_box)
            try:
                box.validate()
            except CabaretError, err:
                raise ModelEditValidError(u'%s.gacha=%d' % (err.value, master.id))

            # 元のグループのカード.
            cardidlist = []
            for group in GachaGroupMaster.getByKey(box.get_group_id_list()):
                # 各グループの出現率.
                groupdata = GachaBoxGroup(group)
                cardidlist.extend([carddata.card for carddata in groupdata.carddata_list])
            cardidlist = list(set(cardidlist))

            # 回数別のグループのBOX.
            for cnt, boxid in master.special_boxid:
                grouplist = GachaGroupMaster.getByKey(box.get_group_id_list(cnt=cnt))
                for group in grouplist:
                    # 各グループの出現率.
                    groupdata = GachaBoxGroup(group)
                    for carddata in groupdata.carddata_list:
                        if not carddata.card in cardidlist:
                            raise ModelEditValidError(u'%d回目のBOXにしか存在しないカードが有ります.gacha=%d,group=%d,card=%d' % (cnt, master.id, group.id, carddata.card))