Пример #1
0
    def get_all_desc(amount=None):
        if amount is None:
            docs = MongoArena._get_collection().find({}, {'score': 1}).sort('score', -1)
        else:
            docs = MongoArena._get_collection().find({}, {'score': 1}).sort('score', -1).limit(amount)

        return [(doc['_id'], doc['score']) for doc in docs]
Пример #2
0
    def initialize(self):
        if not func_opened(self.char_id, Arena.FUNC_ID):
            self.mongo_arena = None
            return

        try:
            self.mongo_arena = MongoArena.objects.get(id=self.char_id)
        except DoesNotExist:
            self.mongo_arena = MongoArena(id=self.char_id)
            self.mongo_arena.score = ArenaScoreManager.get_init_score()
            self.mongo_arena.save()
Пример #3
0
    def __init__(self, char_id):
        self.char_id = char_id

        try:
            self.mongo_arena = MongoArena.objects.get(id=char_id)
        except DoesNotExist:
            self.mongo_arena = MongoArena(id=char_id)
            self.mongo_arena.score = ARENA_DEFAULT_SCORE
            self.mongo_arena.save()

        if not self.score:
            redis_client.zadd(REDIS_ARENA_KEY, self.char_id,
                              self.mongo_arena.score)
Пример #4
0
    def get_all():
        docs = MongoArena._get_collection().find(
                {},
                {'score': 1}
        ).sort('score', 1)

        return [(doc['_id'], doc['score']) for doc in docs]
Пример #5
0
    def get_init_score():
        # 获得初始积分
        lowest_doc = MongoArena._get_collection().find({}, {'score':1}).sort('score', 1).limit(1)
        if lowest_doc.count() == 0:
            return ARENA_INITIAL_SCORE

        doc = lowest_doc[0]
        score = int(doc['score'])
        if score < ARENA_LOWEST_SCORE:
            score = ARENA_LOWEST_SCORE
        return score
Пример #6
0
    def __init__(self, char_id):
        self.char_id = char_id

        try:
            self.mongo_arena = MongoArena.objects.get(id=char_id)
        except DoesNotExist:
            self.mongo_arena = MongoArena(id=char_id)
            self.mongo_arena.score = ARENA_DEFAULT_SCORE
            self.mongo_arena.save()

        if not self.score:
            redis_client.zadd(REDIS_ARENA_KEY, self.char_id, self.mongo_arena.score)
Пример #7
0
    def __init__(self, char_id):
        self.char_id = char_id
        try:
            self.mongo_arena = MongoArena.objects.get(id=char_id)
        except DoesNotExist:
            self.mongo_arena = MongoArena(id=char_id)
            self.mongo_arena.save()

        try:
            self.mongo_day = MongoArenaDay.objects.get(id=char_id)
        except DoesNotExist:
            self.mongo_day = MongoArenaDay(id=char_id)
            self.mongo_day.score = 0
            self.mongo_day.save()

        try:
            self.mongo_week = MongoArenaWeek.objects.get(id=char_id)
        except DoesNotExist:
            self.mongo_week = MongoArenaWeek(id=char_id)
            self.mongo_week.score = 0
            self.mongo_week.rank = 0
            self.mongo_week.save()
Пример #8
0
    def get_chars_by_score(low_score=None, high_score=None):
        conditions = []
        if low_score:
            conditions.append( {'score': {'$gte': low_score}} )
        if high_score:
            conditions.append( {'score': {'$lte': high_score}} )

        if len(conditions) == 1:
            conditions = conditions[0]
        elif len(conditions) == 2:
            conditions = {'$and': conditions}

        docs = MongoArena._get_collection().find(conditions, {'_id': 1})
        return [doc['_id'] for doc in docs]
Пример #9
0
    def __init__(self, char_id):
        self.char_id = char_id
        try:
            self.mongo_arena = MongoArena.objects.get(id=char_id)
        except DoesNotExist:
            self.mongo_arena = MongoArena(id=char_id)
            self.mongo_arena.save()

        try:
            self.mongo_day = MongoArenaDay.objects.get(id=char_id)
        except DoesNotExist:
            self.mongo_day = MongoArenaDay(id=char_id)
            self.mongo_day.score = 0
            self.mongo_day.save()

        try:
            self.mongo_week = MongoArenaWeek.objects.get(id=char_id)
        except DoesNotExist:
            self.mongo_week = MongoArenaWeek(id=char_id)
            self.mongo_week.score = 0
            self.mongo_week.rank = 0
            self.mongo_week.save()
Пример #10
0
class Arena(object):
    FUNC_ID = 8
    def __init__(self, char_id):
        self.char_id = char_id
        self.initialize()

    def initialize(self):
        if not func_opened(self.char_id, Arena.FUNC_ID):
            self.mongo_arena = None
            return

        try:
            self.mongo_arena = MongoArena.objects.get(id=self.char_id)
        except DoesNotExist:
            self.mongo_arena = MongoArena(id=self.char_id)
            self.mongo_arena.score = ArenaScoreManager.get_init_score()
            self.mongo_arena.save()


    @property
    def score(self):
        return self.mongo_arena.score if self.mongo_arena else 0

    @property
    def rank(self):
        if self.score < ARENA_RANK_LINE:
            return 5000

        return ArenaScoreManager.get_char_rank(self.score)


    @property
    def remained_free_times(self):
        c = Counter(self.char_id, 'arena')
        return c.remained_value

    @property
    def remained_buy_times(self):
        c = Counter(self.char_id, 'arena_buy')
        return c.remained_value

    def set_score(self, score):
        self.mongo_arena.score = score
        self.mongo_arena.save()

    @classmethod
    def get_top_ranks(cls, amount=10):
        return ArenaScoreManager.get_top_ranks(amount=amount)

    def send_notify(self):
        if self.mongo_arena is None:
            return

        msg = protomsg.ArenaNotify()
        msg.score = self.score
        msg.rank = self.rank
        msg.remained_free_times = self.remained_free_times
        msg.remained_sycee_times = self.remained_buy_times
        msg.arena_cost = ARENA_COST_SYCEE

        publish_to_char(self.char_id, pack_msg(msg))

    def make_panel_response(self):
        if self.mongo_arena is None:
            return None

        msg = protomsg.ArenaPanelResponse()
        msg.ret = 0

        top_ranks = self.get_top_ranks()
        for index, data in enumerate(top_ranks):
            rank = index + 1
            _cid, _score = data

            if _score < ARENA_RANK_LINE:
                break

            board = msg.boards.add()
            board.char.MergeFrom(create_character_infomation_message(_cid))
            board.score = _score
            board.rank = rank

        return msg


    def choose_rival(self):
        my_score = self.score

        def _find(low_score, high_score):
            choosing = ArenaScoreManager.get_chars_by_score(low_score=low_score, high_score=high_score)
            if not choosing:
                return None

            if self.char_id in choosing:
                choosing.remove(self.char_id)

            while choosing:
                got = random.choice(choosing)
                # check cd
                if redis_client.ttl(REDIS_ARENA_BATTLE_CD_KEY(got)) > 0:
                    choosing.remove(got)
                    continue

                return got

            return None

        got = _find(int(my_score * 0.95), int(my_score * 1.05))
        if got:
            return got

        got = _find(int(my_score * 0.8), int(my_score * 1.2))
        if got:
            return got

        choosing = ArenaScoreManager.get_chars_by_score(low_score=int(my_score * 1.2), high_score=None)
        if choosing:
            if self.char_id in choosing:
                choosing.remove(self.char_id)
            return choosing[0]
        return None


    def battle(self):
        need_sycee = 0

        counter = Counter(self.char_id, 'arena')
        if counter.remained_value <= 0:
            counter = Counter(self.char_id, 'arena_buy')
            if counter.remained_value <= 0:
                char = Char(self.char_id).mc
                if char.vip < VIP_MAX_LEVEL:
                    raise SanguoException(
                        errormsg.ARENA_NO_TIMES,
                        self.char_id,
                        "Arena Battle",
                        "arena no times. vip current: {0}, max {1}".format(char.vip, VIP_MAX_LEVEL)
                    )
                raise SanguoException(
                    errormsg.ARENA_NO_TIMES_FINAL,
                    self.char_id,
                    "Arena Battle",
                    "arena no times. vip reach max level {0}".format(VIP_MAX_LEVEL)
                )
            else:
                need_sycee = ARENA_COST_SYCEE

        rival_id = self.choose_rival()
        if not rival_id:
            raise SanguoException(
                errormsg.ARENA_NO_RIVAL,
                self.char_id,
                "Arena Battle",
                "no rival."
            )

        if need_sycee:
            resource = Resource(self.char_id, "Arena Battle", "battle for no free times")
            resource.check_and_remove(sycee=-need_sycee)

        counter.incr()

        # set battle cd
        redis_client.setex(REDIS_ARENA_BATTLE_CD_KEY(rival_id), 1, ARENA_CD)

        msg = protomsg.Battle()
        b = PVP(self.char_id, rival_id, msg)
        b.start()

        t = Task(self.char_id)
        t.trig(2)

        drop = make_standard_drop_from_template()
        adding_score = 0
        if msg.self_win:
            achievement = Achievement(self.char_id)
            achievement.trig(11, 1)

            # 只有打赢才设置积分
            self_score = self.score
            rival_arena = Arena(rival_id)
            rival_score = rival_arena.score

            new_score = calculate_score(self_score, rival_score, msg.self_win)
            self.set_score(new_score)
            adding_score = new_score - self_score

            rival_arena.be_beaten(rival_score, self_score, not msg.self_win, self.char_id)

            TimesLogArenaWin(self.char_id).inc()

            ae = ActivityEntry(self.char_id, 50004)
            if ae and ae.is_valid():
                drop = ae.get_additional_drop()
                Resource(self.char_id, "Arena Win").add(**drop)

        TimesLogArena(self.char_id).inc()
        ae = ActivityEntry(self.char_id, 40006)
        if ae:
            ae.trig()

        self.send_notify()
        drop['stuffs'].append((1001, adding_score))
        return msg, drop


    def be_beaten(self, self_score, rival_score, win, rival_id):
        score = calculate_score(self_score, rival_score, win)
        self.set_score(score)

        rival_name = get_char_property(rival_id, 'name')

        record = MongoEmbeddedArenaBeatenRecord()
        record.name = rival_name
        record.old_score = self_score
        record.new_score = score

        self.mongo_arena.beaten_record.append(record)
        self.mongo_arena.save()


    def login_process(self):
        from core.mail import Mail

        if not self.mongo_arena or not self.mongo_arena.beaten_record:
            return

        def _make_content(record):
            if record.old_score > record.new_score:
                template = MAIl_ARENA_BEATEN_LOST_TEMPLATE
                # des = '-{0}'.format(abs(record.old_score - record.new_score))
            else:
                template = MAIl_ARENA_BEATEN_WIN_TEMPLATE
                # des = '+{0}'.format(abs(record.old_score - record.new_score))

            # return template.format(record.name, record.old_score, record.new_score, des)
            return template.format(record.name)

        contents = [_make_content(record) for record in self.mongo_arena.beaten_record[-1:-5:-1]]

        content_header = u'共受到{0}次挑战,积分从{1}变成{2}\n'.format(
            len(self.mongo_arena.beaten_record),
            self.mongo_arena.beaten_record[0].old_score,
            self.mongo_arena.beaten_record[-1].new_score,
        )

        content_body = u'\n'.join(contents)

        content = content_header + content_body
        if len(self.mongo_arena.beaten_record) > 4:
            content += u'\n...'

        Mail(self.char_id).add(MAIL_ARENA_BEATEN_TITLE, content, send_notify=False)

        self.mongo_arena.beaten_record = []
        self.mongo_arena.save()
Пример #11
0
 def get_top_ranks(amount=10):
     docs = MongoArena._get_collection().find({}, {'score': 1}).sort('score', -1).limit(amount)
     return [(doc['_id'], doc['score']) for doc in docs]
Пример #12
0
class Arena(object):
    def __init__(self, char_id):
        self.char_id = char_id
        try:
            self.mongo_arena = MongoArena.objects.get(id=char_id)
        except DoesNotExist:
            self.mongo_arena = MongoArena(id=char_id)
            self.mongo_arena.save()

        try:
            self.mongo_day = MongoArenaDay.objects.get(id=char_id)
        except DoesNotExist:
            self.mongo_day = MongoArenaDay(id=char_id)
            self.mongo_day.score = 0
            self.mongo_day.save()

        try:
            self.mongo_week = MongoArenaWeek.objects.get(id=char_id)
        except DoesNotExist:
            self.mongo_week = MongoArenaWeek(id=char_id)
            self.mongo_week.score = 0
            self.mongo_week.rank = 0
            self.mongo_week.save()

    @property
    def day_score(self):
        return self.mongo_day.score

    @property
    def week_rank(self):
        return self.mongo_week.rank

    @property
    def week_score(self):
        return self.mongo_week.score

    @property
    def remained_free_times(self):
        c = Counter(self.char_id, 'arena')
        return c.remained_value

    @property
    def remained_buy_times(self):
        c = Counter(self.char_id, 'arena_buy')
        return c.remained_value

    def _fill_up_panel_msg(self, msg):
        msg.week_rank = self.week_rank
        msg.day_rank = 0
        msg.week_score = self.week_score
        msg.day_score = self.day_score
        msg.remained_free_times = self.remained_free_times
        msg.remained_sycee_times = self.remained_buy_times
        msg.arena_cost = ARENA_COST_SYCEE

        top_ranks = MongoArenaTopRanks.objects.all()
        for t in top_ranks:
            char = msg.chars.add()
            char.rank = t.id
            char.name = t.name

    def send_notify(self):
        msg = protomsg.ArenaNotify()
        self._fill_up_panel_msg(msg)
        publish_to_char(self.char_id, pack_msg(msg))

    def choose_rival(self):
        my_score = self.day_score
        choosing = []

        score_diff = 2
        while True:
            if score_diff >= DAY_MAX_SCORE:
                break

            choosing = MongoArenaDay.objects.filter(
                Q(score__gte=my_score) & Q(score__lte=my_score + score_diff)
                & Q(id__ne=self.char_id))
            if choosing:
                break

            score_diff += 2

        choosing = [c.id for c in choosing if c.id != self.char_id]

        if not choosing:
            char_count = MongoCharacter.objects.count()
            id_list = random.sample(range(1, char_count + 1),
                                    min(char_count, 100))
            choosing = MongoCharacter.objects.filter(id__in=id_list)
            choosing = [c.id for c in choosing]
            if self.char_id in choosing:
                choosing.remove(self.char_id)

            if not choosing:
                choosing = MongoCharacter.objects.all()
                choosing = [c.id for c in choosing]
                choosing.remove(self.char_id)

        return random.choice(choosing)

    def battle(self):
        counter = Counter(self.char_id, 'arena')
        try:
            # 免费次数
            counter.incr()
        except CounterOverFlow:
            counter = Counter(self.char_id, 'arena_buy')

            try:
                # 花费元宝次数
                counter.incr()
            except CounterOverFlow:
                char = Char(self.char_id).mc
                if char.vip < VIP_MAX_LEVEL:
                    raise SanguoException(
                        errormsg.ARENA_NO_TIMES, self.char_id, "Arena Battle",
                        "arena no times. vip current: {0}, max {1}".format(
                            char.vip, VIP_MAX_LEVEL))
                raise SanguoException(
                    errormsg.ARENA_NO_TIMES_FINAL, self.char_id,
                    "Arena Battle",
                    "arena no times. vip reach max level {0}".format(
                        VIP_MAX_LEVEL))

            else:
                resource = Resource(self.char_id, "Arena Battle",
                                    "battle for no free times")
                resource.check_and_remove(sycee=-ARENA_COST_SYCEE)

        rival_id = self.choose_rival()

        msg = protomsg.Battle()
        b = PVP(self.char_id, rival_id, msg)
        b.start()

        achievement = Achievement(self.char_id)

        if msg.self_win:
            score = ARENA_GET_SCORE_WHEN_WIN
            achievement.trig(11, 1)
            self.mongo_arena.continues_win += 1
        else:
            score = ARENA_GET_SCORE_WHEN_LOST
            self.mongo_arena.continues_win = 0

        self.mongo_arena.save()

        if score:
            self.mongo_day.score += score
            self.mongo_day.save()

        t = Task(self.char_id)
        t.trig(2)

        self.send_notify()
        return msg
Пример #13
0
class Arena(object):
    FUNC_ID = 8
    def __init__(self, char_id):
        self.char_id = char_id

        try:
            self.mongo_arena = MongoArena.objects.get(id=char_id)
        except DoesNotExist:
            self.mongo_arena = MongoArena(id=char_id)
            self.mongo_arena.score = ARENA_DEFAULT_SCORE
            self.mongo_arena.save()

        if not self.score:
            redis_client.zadd(REDIS_ARENA_KEY, self.char_id, self.mongo_arena.score)

    @property
    def score(self):
        score = redis_client.zscore(REDIS_ARENA_KEY, self.char_id)
        return int(score) if score else 0

    @property
    def rank(self):
        rank = redis_client.zrevrank(REDIS_ARENA_KEY, self.char_id)
        return rank+1 if rank is not None else 0


    @property
    def remained_free_times(self):
        c = Counter(self.char_id, 'arena')
        return c.remained_value

    @property
    def remained_buy_times(self):
        c = Counter(self.char_id, 'arena_buy')
        return c.remained_value

    def set_score(self, score):
        redis_client.zadd(REDIS_ARENA_KEY, self.char_id, score)
        self.mongo_arena.score = score
        self.mongo_arena.save()


    @cache_it('_redis_arena_top_cache', ARENA_TOP_RANKS_CACHE)
    def get_top_ranks(self):
        # return [(char_id, score, power, name,  leader), ...]
        top_data = redis_client.zrevrange(REDIS_ARENA_KEY, 0, 2, withscores=True)
        tops = []
        for _id, _score in top_data:
            char = Char(int(_id))
            tops.append( (int(_id), _score, char.power, char.mc.name, char.leader_oid) )

        tops.sort(key=lambda item: (-item[1], -item[2]))
        return tops


    def fill_up_panel_msg(self, msg, score=None):
        msg.score = score or self.score
        msg.rank = self.rank
        msg.remained_free_times = self.remained_free_times
        msg.remained_sycee_times = self.remained_buy_times
        msg.arena_cost = ARENA_COST_SYCEE

        top_ranks = self.get_top_ranks()

        for index, top in enumerate(top_ranks):
            char = msg.chars.add()
            char.rank = index + 1
            char.name = top[3]
            char.leader = top[4]
            char.power = top[2]


    def send_notify(self, score=None):
        msg = protomsg.ArenaNotify()
        self.fill_up_panel_msg(msg, score=score)
        publish_to_char(self.char_id, pack_msg(msg))


    def choose_rival(self):
        my_score = self.score

        def _find(low_score, high_score):
            choosing = redis_client.zrangebyscore(REDIS_ARENA_KEY, low_score, high_score)
            if not choosing:
                return None

            if str(self.char_id) in choosing:
                choosing.remove(str(self.char_id))

            while choosing:
                got = random.choice(choosing)
                # check cd
                if redis_client.ttl(REDIS_ARENA_BATTLE_CD_KEY(got)) > 0:
                    choosing.remove(got)
                    continue

                return int(got)

            return None

        got = _find(int(my_score * 0.95), int(my_score * 1.05))
        if got:
            return got

        got = _find(int(my_score * 0.8), int(my_score * 1.2))
        if got:
            return got

        choosing = redis_client.zrangebyscore(REDIS_ARENA_KEY, int(my_score * 1.2), '+inf')
        if choosing:
            if str(self.char_id) in choosing:
                choosing.remove(str(self.char_id))
            return int(choosing[0])
        return None


    def battle(self):
        need_sycee = 0

        counter = Counter(self.char_id, 'arena')
        if counter.remained_value <= 0:
            counter = Counter(self.char_id, 'arena_buy')
            if counter.remained_value <= 0:
                char = Char(self.char_id).mc
                if char.vip < VIP_MAX_LEVEL:
                    raise SanguoException(
                        errormsg.ARENA_NO_TIMES,
                        self.char_id,
                        "Arena Battle",
                        "arena no times. vip current: {0}, max {1}".format(char.vip, VIP_MAX_LEVEL)
                    )
                raise SanguoException(
                    errormsg.ARENA_NO_TIMES_FINAL,
                    self.char_id,
                    "Arena Battle",
                    "arena no times. vip reach max level {0}".format(VIP_MAX_LEVEL)
                )
            else:
                need_sycee = ARENA_COST_SYCEE

        rival_id = self.choose_rival()
        if not rival_id:
            raise SanguoException(
                errormsg.ARENA_NO_RIVAL,
                self.char_id,
                "Arena Battle",
                "no rival."
            )

        if need_sycee:
            resource = Resource(self.char_id, "Arena Battle", "battle for no free times")
            resource.check_and_remove(sycee=-need_sycee)

        counter.incr()

        # set battle cd
        redis_client.setex(REDIS_ARENA_BATTLE_CD_KEY(rival_id), 1, ARENA_CD)

        msg = protomsg.Battle()
        b = PVP(self.char_id, rival_id, msg)
        b.start()


        if msg.self_win:
            achievement = Achievement(self.char_id)
            achievement.trig(11, 1)

        self_score = self.score
        rival_arena = Arena(rival_id)
        rival_score = rival_arena.score

        new_score = calculate_score(self_score, rival_score, msg.self_win)
        self.set_score(new_score)

        t = Task(self.char_id)
        t.trig(2)

        self.send_notify(score=new_score)

        rival_arena.be_beaten(rival_score, self_score, not msg.self_win, self.char_id)

        return msg


    def be_beaten(self, self_score, rival_score, win, rival_id):
        score = calculate_score(self_score, rival_score, win)
        self.set_score(score)

        rival_name = Char(rival_id).mc.name

        record = MongoEmbeddedArenaBeatenRecord()
        record.name = rival_name
        record.old_score = self_score
        record.new_score = score

        self.mongo_arena.beaten_record.append(record)
        self.mongo_arena.save()


    def login_process(self):
        from core.mail import Mail

        if not self.mongo_arena.beaten_record:
            return

        def _make_content(record):
            if record.old_score > record.new_score:
                template = MAIl_ARENA_BEATEN_LOST_TEMPLATE
                des = '-{0}'.format(abs(record.old_score - record.new_score))
            else:
                template = MAIl_ARENA_BEATEN_WIN_TEMPLATE
                des = '+{0}'.format(abs(record.old_score - record.new_score))

            return template.format(record.name, record.old_score, record.new_score, des)

        contents = [_make_content(record) for record in self.mongo_arena.beaten_record[-1:-5:-1]]

        content_header = u'共受到{0}次挑战,积分从{1}变成{2}\n'.format(
            len(self.mongo_arena.beaten_record),
            self.mongo_arena.beaten_record[0].old_score,
            self.mongo_arena.beaten_record[-1].new_score,
        )

        content_body = u'\n'.join(contents)

        content = content_header + content_body
        if len(self.mongo_arena.beaten_record) > 4:
            content += u'\n...'

        Mail(self.char_id).add(MAIL_ARENA_BEATEN_TITLE, content, send_notify=False)

        self.mongo_arena.beaten_record = []
        self.mongo_arena.save()
Пример #14
0
class Arena(object):
    def __init__(self, char_id):
        self.char_id = char_id
        try:
            self.mongo_arena = MongoArena.objects.get(id=char_id)
        except DoesNotExist:
            self.mongo_arena = MongoArena(id=char_id)
            self.mongo_arena.save()

        try:
            self.mongo_day = MongoArenaDay.objects.get(id=char_id)
        except DoesNotExist:
            self.mongo_day = MongoArenaDay(id=char_id)
            self.mongo_day.score = 0
            self.mongo_day.save()

        try:
            self.mongo_week = MongoArenaWeek.objects.get(id=char_id)
        except DoesNotExist:
            self.mongo_week = MongoArenaWeek(id=char_id)
            self.mongo_week.score = 0
            self.mongo_week.rank = 0
            self.mongo_week.save()

    @property
    def day_score(self):
        return self.mongo_day.score

    @property
    def week_rank(self):
        return self.mongo_week.rank

    @property
    def week_score(self):
        return self.mongo_week.score

    @property
    def remained_free_times(self):
        c = Counter(self.char_id, 'arena')
        return c.remained_value

    @property
    def remained_buy_times(self):
        c = Counter(self.char_id, 'arena_buy')
        return c.remained_value


    def _fill_up_panel_msg(self, msg):
        msg.week_rank = self.week_rank
        msg.day_rank = 0
        msg.week_score = self.week_score
        msg.day_score = self.day_score
        msg.remained_free_times = self.remained_free_times
        msg.remained_sycee_times = self.remained_buy_times
        msg.arena_cost = ARENA_COST_SYCEE

        top_ranks = MongoArenaTopRanks.objects.all()
        for t in top_ranks:
            char = msg.chars.add()
            char.rank = t.id
            char.name = t.name


    def send_notify(self):
        msg = protomsg.ArenaNotify()
        self._fill_up_panel_msg(msg)
        publish_to_char(self.char_id, pack_msg(msg))


    def choose_rival(self):
        my_score = self.day_score
        choosing = []

        score_diff = 2
        while True:
            if score_diff >= DAY_MAX_SCORE:
                break

            choosing = MongoArenaDay.objects.filter(Q(score__gte=my_score) & Q(score__lte=my_score+score_diff) & Q(id__ne=self.char_id))
            if choosing:
                break

            score_diff += 2

        choosing = [c.id for c in choosing if c.id != self.char_id]

        if not choosing:
            char_count = MongoCharacter.objects.count()
            id_list = random.sample(range(1, char_count+1), min(char_count, 100))
            choosing = MongoCharacter.objects.filter(id__in=id_list)
            choosing = [c.id for c in choosing]
            if self.char_id in choosing:
                choosing.remove(self.char_id)

            if not choosing:
                choosing = MongoCharacter.objects.all()
                choosing = [c.id for c in choosing]
                choosing.remove(self.char_id)

        return random.choice(choosing)


    def battle(self):
        counter = Counter(self.char_id, 'arena')
        try:
            # 免费次数
            counter.incr()
        except CounterOverFlow:
            counter = Counter(self.char_id, 'arena_buy')

            try:
                # 花费元宝次数
                counter.incr()
            except CounterOverFlow:
                char = Char(self.char_id).mc
                if char.vip < VIP_MAX_LEVEL:
                    raise SanguoException(
                        errormsg.ARENA_NO_TIMES,
                        self.char_id,
                        "Arena Battle",
                        "arena no times. vip current: {0}, max {1}".format(char.vip, VIP_MAX_LEVEL)
                    )
                raise SanguoException(
                    errormsg.ARENA_NO_TIMES_FINAL,
                    self.char_id,
                    "Arena Battle",
                    "arena no times. vip reach max level {0}".format(VIP_MAX_LEVEL)
                )

            else:
                resource = Resource(self.char_id, "Arena Battle", "battle for no free times")
                resource.check_and_remove(sycee=-ARENA_COST_SYCEE)

        rival_id = self.choose_rival()

        msg = protomsg.Battle()
        b = PVP(self.char_id, rival_id, msg)
        b.start()

        achievement = Achievement(self.char_id)

        if msg.self_win:
            score = ARENA_GET_SCORE_WHEN_WIN
            achievement.trig(11, 1)
            self.mongo_arena.continues_win += 1
        else:
            score = ARENA_GET_SCORE_WHEN_LOST
            self.mongo_arena.continues_win = 0

        self.mongo_arena.save()

        if score:
            self.mongo_day.score += score
            self.mongo_day.save()

        t = Task(self.char_id)
        t.trig(2)

        self.send_notify()
        return msg
Пример #15
0
 def get_char_rank(char_score):
     docs = MongoArena._get_collection().find({'score': {'$gt': char_score}}, {'_id': 1})
     rank = docs.count()
     return rank + 1
Пример #16
0
 def get_char_score(char_id):
     doc = MongoArena._get_collection().find_one(
             {'_id': char_id},
             {'score': 1}
     )
     return doc['score']
Пример #17
0
class Arena(object):
    FUNC_ID = 8

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

        try:
            self.mongo_arena = MongoArena.objects.get(id=char_id)
        except DoesNotExist:
            self.mongo_arena = MongoArena(id=char_id)
            self.mongo_arena.score = ARENA_DEFAULT_SCORE
            self.mongo_arena.save()

        if not self.score:
            redis_client.zadd(REDIS_ARENA_KEY, self.char_id,
                              self.mongo_arena.score)

    @property
    def score(self):
        score = redis_client.zscore(REDIS_ARENA_KEY, self.char_id)
        return int(score) if score else 0

    @property
    def rank(self):
        rank = redis_client.zrevrank(REDIS_ARENA_KEY, self.char_id)
        return rank + 1 if rank is not None else 0

    @property
    def remained_free_times(self):
        c = Counter(self.char_id, 'arena')
        return c.remained_value

    @property
    def remained_buy_times(self):
        c = Counter(self.char_id, 'arena_buy')
        return c.remained_value

    def set_score(self, score):
        redis_client.zadd(REDIS_ARENA_KEY, self.char_id, score)
        self.mongo_arena.score = score
        self.mongo_arena.save()

    @cache_it('_redis_arena_top_cache', ARENA_TOP_RANKS_CACHE)
    def get_top_ranks(self):
        # return [(char_id, score, power, name,  leader), ...]
        top_data = redis_client.zrevrange(REDIS_ARENA_KEY,
                                          0,
                                          2,
                                          withscores=True)
        tops = []
        for _id, _score in top_data:
            char = Char(int(_id))
            tops.append(
                (int(_id), _score, char.power, char.mc.name, char.leader_oid))

        tops.sort(key=lambda item: (-item[1], -item[2]))
        return tops

    def fill_up_panel_msg(self, msg, score=None):
        msg.score = score or self.score
        msg.rank = self.rank
        msg.remained_free_times = self.remained_free_times
        msg.remained_sycee_times = self.remained_buy_times
        msg.arena_cost = ARENA_COST_SYCEE

        top_ranks = self.get_top_ranks()

        for index, top in enumerate(top_ranks):
            char = msg.chars.add()
            char.rank = index + 1
            char.name = top[3]
            char.leader = top[4]
            char.power = top[2]

    def send_notify(self, score=None):
        msg = protomsg.ArenaNotify()
        self.fill_up_panel_msg(msg, score=score)
        publish_to_char(self.char_id, pack_msg(msg))

    def choose_rival(self):
        my_score = self.score

        def _find(low_score, high_score):
            choosing = redis_client.zrangebyscore(REDIS_ARENA_KEY, low_score,
                                                  high_score)
            if not choosing:
                return None

            if str(self.char_id) in choosing:
                choosing.remove(str(self.char_id))

            while choosing:
                got = random.choice(choosing)
                # check cd
                if redis_client.ttl(REDIS_ARENA_BATTLE_CD_KEY(got)) > 0:
                    choosing.remove(got)
                    continue

                return int(got)

            return None

        got = _find(int(my_score * 0.95), int(my_score * 1.05))
        if got:
            return got

        got = _find(int(my_score * 0.8), int(my_score * 1.2))
        if got:
            return got

        choosing = redis_client.zrangebyscore(REDIS_ARENA_KEY,
                                              int(my_score * 1.2), '+inf')
        if choosing:
            if str(self.char_id) in choosing:
                choosing.remove(str(self.char_id))
            return int(choosing[0])
        return None

    def battle(self):
        need_sycee = 0

        counter = Counter(self.char_id, 'arena')
        if counter.remained_value <= 0:
            counter = Counter(self.char_id, 'arena_buy')
            if counter.remained_value <= 0:
                char = Char(self.char_id).mc
                if char.vip < VIP_MAX_LEVEL:
                    raise SanguoException(
                        errormsg.ARENA_NO_TIMES, self.char_id, "Arena Battle",
                        "arena no times. vip current: {0}, max {1}".format(
                            char.vip, VIP_MAX_LEVEL))
                raise SanguoException(
                    errormsg.ARENA_NO_TIMES_FINAL, self.char_id,
                    "Arena Battle",
                    "arena no times. vip reach max level {0}".format(
                        VIP_MAX_LEVEL))
            else:
                need_sycee = ARENA_COST_SYCEE

        rival_id = self.choose_rival()
        if not rival_id:
            raise SanguoException(errormsg.ARENA_NO_RIVAL, self.char_id,
                                  "Arena Battle", "no rival.")

        if need_sycee:
            resource = Resource(self.char_id, "Arena Battle",
                                "battle for no free times")
            resource.check_and_remove(sycee=-need_sycee)

        counter.incr()

        # set battle cd
        redis_client.setex(REDIS_ARENA_BATTLE_CD_KEY(rival_id), 1, ARENA_CD)

        msg = protomsg.Battle()
        b = PVP(self.char_id, rival_id, msg)
        b.start()

        if msg.self_win:
            achievement = Achievement(self.char_id)
            achievement.trig(11, 1)

        self_score = self.score
        rival_arena = Arena(rival_id)
        rival_score = rival_arena.score

        new_score = calculate_score(self_score, rival_score, msg.self_win)
        self.set_score(new_score)

        t = Task(self.char_id)
        t.trig(2)

        self.send_notify(score=new_score)

        rival_arena.be_beaten(rival_score, self_score, not msg.self_win,
                              self.char_id)

        return msg

    def be_beaten(self, self_score, rival_score, win, rival_id):
        score = calculate_score(self_score, rival_score, win)
        self.set_score(score)

        rival_name = Char(rival_id).mc.name

        record = MongoEmbeddedArenaBeatenRecord()
        record.name = rival_name
        record.old_score = self_score
        record.new_score = score

        self.mongo_arena.beaten_record.append(record)
        self.mongo_arena.save()

    def login_process(self):
        from core.mail import Mail

        if not self.mongo_arena.beaten_record:
            return

        def _make_content(record):
            if record.old_score > record.new_score:
                template = MAIl_ARENA_BEATEN_LOST_TEMPLATE
                des = '-{0}'.format(abs(record.old_score - record.new_score))
            else:
                template = MAIl_ARENA_BEATEN_WIN_TEMPLATE
                des = '+{0}'.format(abs(record.old_score - record.new_score))

            return template.format(record.name, record.old_score,
                                   record.new_score, des)

        contents = [
            _make_content(record)
            for record in self.mongo_arena.beaten_record[-1:-5:-1]
        ]

        content_header = u'共受到{0}次挑战,积分从{1}变成{2}\n'.format(
            len(self.mongo_arena.beaten_record),
            self.mongo_arena.beaten_record[0].old_score,
            self.mongo_arena.beaten_record[-1].new_score,
        )

        content_body = u'\n'.join(contents)

        content = content_header + content_body
        if len(self.mongo_arena.beaten_record) > 4:
            content += u'\n...'

        Mail(self.char_id).add(MAIL_ARENA_BEATEN_TITLE,
                               content,
                               send_notify=False)

        self.mongo_arena.beaten_record = []
        self.mongo_arena.save()