<?php

namespace loyalsoft;

/**
 *  PVPProc 竞技场 战斗模块, 挑战玩家镜像
 * @version
 *          1.0.0 Created at 2017-6-26. by --gwang
 * @author gwang (mail@wanggangzero.cn)
 * @copyright ? 2017-6-26, SJZ LoyalSoft Corporation & gwang. All rights reserved.
 */
class PVPProc {
    //
    // <editor-fold defaultstate="collapsed" desc="    常量    ">

    /**
     * 挑战记录最大条数
     */
    const maxLogCount = 49;

    /**
     * 查找对手数量
     */
    const matchCount = 5;

    /**
     * 竞技场赛季起始时间戳
     */
    const pvpStartTs = 1588521600;                                              # 2020年5月4日 0时0分0秒

    /**
     * 每个赛季持续时常
     */
    const pvpSeasonLengt = 86400 * 14;                                          # 2周

    /**
     * 竞技场初始积分
     */
    const pvpBaseScore = 1000;

    /**
     * 竞技场 最大上榜人数
     */
    const pvpMaxRank = 500;

// </editor-fold>

    /**
     * [6803] 挑战 - 查询对手信息 等级、头像、昵称、战队信息（言灵师，等级，星级，武器，技能，言灵）
     */
    public static function GetChallengeAdversaryInfo() {
        $targetUID = req()->paras[0];                                           # 参数: 对手的UID
        $uinfo = UserProc::getUserInfo(req()->zoneid, $targetUID);              # 读取玩家信息 
        if (null == $uinfo) {
            Err(ErrCode::user_no_err);
        }
        $team = JsonUtil::decode($uinfo->game->heroTeamConfig);

        $heros = new \stdClass();
        $curTeamId = $team->curUseTeamID;
        foreach ($team->teamDic->$curTeamId->heros as $i => $hid) {
            if ($hid > 0) {
                $n_hid = $hid - 10000;
                $heros->$n_hid = $uinfo->game->heros->collectHeros->$hid;
            }
        }

        $adversary = array(#                                                    # 拼装玩家信息
            'uid' => $targetUID,
            'name' => my_null_default($uinfo->game->base()->name, ""),
            'level' => my_null_default($uinfo->game->base()->level, 1),
            'headImg' => my_null_default($uinfo->game->base()->headImg, ""),
//            'skills' => null, #                                                  # skills暂时没有实例数据 
            'equipment' => array("equipments" => my_null_default($uinfo->game->store->equipment, new \stdClass())), # 武器
            'yanling' => array("items" => my_null_default($uinfo->game->store->yanling, new \stdClass())), # 言灵
            'heros' => my_null_default($heros, new \stdClass()), #              # 英雄集合
        );

        $result = array(#                                                       # 拼装返回值
            "adversaryInfo" => $adversary
        );
        return Resp::ok($result);
    }

    /**
     * [6804] 挑战 - 记录挑战结果
     */
    static function LogChallengeInfo() {
        list($targetUID, $name, $headImg, $win, $msg) = req()->paras;     // 参数: 对手uid,对手昵称,对手头像, 对战结果, 胜利者的留言(失败时无法留言"")
        $key_mine = MemKey_User::OffensiveLog_zset(req()->zoneid, req()->uid);
        $key_target = MemKey_User::DefensiveLog_zset(req()->zoneid, $targetUID);
        $ts = now();                                                            # 记录时间戳
        gMem()->zadd($key_mine, array(#                                         # 组装被挑战对手信息
            JsonUtil::encode(array(
                'uid' => my_null_default($targetUID, "-"),
                'name' => my_null_default($name, ""),
                'headImg' => my_null_default($headImg, ""),
                'win' => my_null_default($win, false),
                'msg' => my_null_default($msg, ""),
                'ts' => $ts
            )) => $ts));
        gMem()->zremrangebyrank($key_mine, self::maxLogCount, -1);              # 保留50条数据
        gMem()->zadd($key_target, array(#                                       # 组装挑战者信息
            JsonUtil::encode(array(
                'uid' => req()->uid,
                'name' => req()->userInfo->game->baseInfo->name,
                'headImg' => req()->userInfo->game->baseInfo->headImg,
                'win' => !my_null_default($win, false),
                'msg' => my_null_default($msg, ""),
                'ts' => $ts
            )) => $ts));
        gMem()->zremrangebyrank($key_target, self::maxLogCount, -1);            # 保留50条数据
        // 暂无发放奖励流程
        TaskProc::OnRankChalenge();                                             // 更新每日任务
        UserProc::updateUserInfo();
        return Resp::ok();                                                      # 返回成功
    }

    /**
     * [6805] 挑战 - 拉取挑战记录
     */
    static function GetChagllengeLog() {
        // 参数:无
        $key_off = MemKey_User::OffensiveLog_zset(req()->zoneid, req()->uid);
        $key_def = MemKey_User::DefensiveLog_zset(req()->zoneid, req()->uid);
        // 拉取自己的挑战记录
        $offLog = gMem()->zrange($key_off, 0, self::maxLogCount);
        $defLog = gMem()->zrange($key_def, 0, self::maxLogCount);
        // Ps. 挑战记录分为2个榜, 且按照时间戳记录,晚于指定时间戳的判定为未读消息,挑战记录最多记录50条
//        if (!CommUtil::isPropertyExists($req->userInfo->game->privateState, "lastCheckDefLog")) {
        req()->userInfo->game->privateState->lastCheckDefLog_ts = now();         # 记录时间戳
//        }
        UserProc::updateUserInfo();                                             # 回写数据
        // 记录拉取时间戳(在主界面有个未读消息条数显示, 需要靠最后拉取时间戳对比, 时间戳之后的消息是未读消息)
        // 回传数据记录
        array_walk($offLog, function (&$i) {                                    # 解码一下
            $i = JsonUtil::decode($i);
        });
        array_walk($defLog, function (&$i) {                                    # 解码一下
            $i = JsonUtil::decode($i);
        });
        return Resp::ok(array(
                    'offLog' => $offLog,
                    'defLog' => $defLog
        ));
    }

    // <editor-fold defaultstate="collapsed" desc="   竞技商店  ">
    /**
     * 王刚 16:23:39 (2020.5.9)
      商店现在的模式定位:
      商店显示所有物品, 刷新时是重置购买/售罄记录
      刘海 16:24:23 (2020.5.9)
      没错
     */

    /**
     * [6820] 竞技商店 主界面 
     */
    public static function pvpShopMain() {
        $pvp = new Info_UserPVP(req()->userInfo->game->pvp);                     # PVP信息
        if ($pvp->shopRefreshTs < now()) {                                      # 检查刷新时间<now => 刷新商品列表
            $pvp->shopRefreshTs = now() + glc()->PVP_shop_refresh_interval;     # 更新刷新时间
            $pvp->curShopItems = GameConfig::pvp_shop();                        # 重刷道具
            req()->userInfo->game->pvp = $pvp;                                   # 回写
            UserProc::updateUserInfo();
        }
        return Resp::ok($pvp);                                                  // 返回
    }

    /**
     * [6821] 竞技 商店 购买道具 
     */
    public static function pvpShopBuy() {
        $index = req()->paras[0];                                                # 参数:道具索引(typeId)
        $pvp = new Info_UserPVP(req()->userInfo->game->pvp);                     # PVP 数据
        my_Assert(CommUtil::isPropertyExists($pvp->curShopItems, $index), ErrCode::err_innerfault); # 没有找到改商品
//        isEditor() && $citem = new \sm_pvp_shop();
        $citem = $pvp->curShopItems->$index;                                    # 查询物品数据
        my_Assert($citem->sold == 0, ErrCode::pvp_item_soldout);                # 防御道具已售罄
//        var_dump($citem);
        my_Assert($citem->priceType == 5, ErrCode::pay_price_err);              # 防御定价异常
        my_Assert($pvp->pvpCoins > $citem->price, ErrCode::pvp_coinnotenough);  # pvp币不足 
        $citem->sold += 1;                                                      # 设置已售罄/已购买标志
        $pvp->pvpCoins -= $citem->price;                                        # 扣除竞技币 
        StoreProc::AddMultiItemInStore($citem->goods);                          # 发放道具
        req()->userInfo->game->pvp = $pvp;                                       // 回写数据
        UserProc::updateUserInfo();
        return Resp::ok(array('pvp' => $pvp, 'store' => req()->userInfo->game->store)); # 返回
    }

    /**
     * [6822] 竞技 商店 刷新道具 
     */
    public static function pvpShopRefresh() {
        // 扣除刷新消耗
        $pvp = req()->userInfo->game->pvp();
        $costCash = glc()->PVP_shop_refresh_cash;
        my_Assert(req()->userInfo->game->base()->Consume_Cash($costCash), ErrCode::notenough_cash_msg);
//        $pvp->shopRefreshTs = now() + glc()->PVP_shop_refresh_interval;         # 更新刷新时间
        $pvp->curShopItems = GameConfig::pvp_shop();                            # 重刷道具
//        req()->userInfo->game->pvp = $pvp;                                       # 回写
        UserProc::updateUserInfo();                                             # 回写玩家数据
        return Resp::ok($pvp);                                                  # 返回
    }

// // </editor-fold>
    //
    // <editor-fold defaultstate="collapsed" desc="   竞技场   681x">
    // 

    /**
     * 辅助方法:取当前赛季的编号(从赛季起始开始算起)
     * @return int 
     */
    public static function GetCurSeasonID() {
        $n = ceil((now() - self::pvpStartTs ) / self::pvpSeasonLengt);          # 进一取整
        return $n;
    }

    /**
     * 辅助方法:取指定赛季的结束时间戳
     * @param int $seasonID
     * @return int 
     */
    public static function GetSeasonEndTs($seasonID) {
        $ts = self::pvpStartTs + $seasonID * self::pvpSeasonLengt;              # 从起始点开始 + n个赛季时常
        return $ts;
    }

    /**
     * [6810] 竞技场 拉取主界面信息
     */
    static function pvpMainInfo() {
        $uid = req()->uid;                                                       # 快速访问UID
        $zoneid = req()->zoneid;                                                 # 快速访问zoneid
        $pvp = new Info_UserPVP(req()->userInfo->game->pvp);                     # 设计玩家pvp数据结构
        $pvp->refreshDailyData();                                               # 刷新免费挑战次数
        $seasonId = self::GetCurSeasonID();                                     # 当前赛季ID
        $key = MemKey_GameRun::Game_PVPScoreByZoneSeason_zset($zoneid, $seasonId); # 积分总榜

        $score = self::_getScore_by_uid($uid, $key);                            # 玩家积分
        $rank = self::_getRank_by_uid($uid, $key);                              # 玩家排名
        $fPower = HeroProc::CalcUserFightPower($zoneid, $uid, req()->userInfo->game); # 玩家总战力?还是当前防守队伍的战斗力?
        $numNewLog = 0; // todo: 真正查询是否有新战报
        $matches = self::getNewMatches($pvp, $uid, $zoneid);                    # 获得新的匹配对手
        $pvp->sendRewardEmail($zoneid, $uid, $seasonId);                        # 发奖励邮件 
        req()->userInfo->game->pvp = $pvp;
        UserProc::updateUserInfo();
        $ret = array(#                                                          # 组装 返回值结构
            'score' => $score, #                                                # 自己的积分
            'rank' => $rank, #                                                  # 自己的排名
            'pvpCoins' => $pvp->pvpCoins, #                                     # 自己的竞技币
            'fPower' => $fPower, #                                              # 自己的总战力
            'fightTicket' => $pvp->fightTicket, #                               # 自己的挑战票
            'defTeam' => $pvp->defTeam, #                                       # 自己的防守队伍
            'bHasNewFightLog' => $numNewLog, #                                  # 是否有战报刷新
            'matches' => my_null_default($matches, array()), #                  # 对手列表
        );
        return Resp::ok($ret);                                                  # 返回
    }

    /**
     * [6811] 竞技场 刷新对手 
     */
    static function pvp_Refresh() {
        // 刷新无花费, 间隔时间3秒(客户端控制得了)
        $pvp = new Info_UserPVP(req()->userInfo->game->pvp);
        $ts = now();
        my_Assert($pvp->nextRefreshTs < $ts, ErrCode::pvp_refresh_time);        # 验证时间间隔
        $pvp->curMatches = self::getNewMatches($pvp, req()->uid, req()->zoneid);
        $pvp->nextRefreshTs = now(3);
        req()->userInfo->game->pvp = $pvp;
        UserProc::updateUserInfo();                                             # 回写数据 
        $ret = array(
            'curMatches' => my_null_default($pvp->curMatches, array()), #       # 当前对手清单
        );
        return Resp::ok($ret);
    }

    /**
     * [6812] 竞技场 挑战对手xx
     */
    static function pvp_PK() {
        $uid = req()->uid;
        $zoneid = req()->zoneid;
        $baseInfo = req()->userInfo->game->baseInfo;
        list($target_uid, $result, $target_name, $target_HeadImg) = req()->paras; # 对手id,胜负结果 0负,1胜

        $pvp = req()->userInfo->game->pvp;

        if ($pvp->freeFightTickets > 0) {                                       # 有免费挑战票,先扣除免费的
            $pvp->freeFightTickets -= 1;
        } else {
            my_Assert($pvp->fightTicket > 0, ErrCode::pvp_no_tickets);          # 防御: 挑战票不足
            $pvp->fightTicket -= 1;                                             # 扣除挑战票
        }

        $season = self::GetCurSeasonID();                                       # 当前赛季
        $key = MemKey_GameRun::Game_PVPScoreByZoneSeason_zset($zoneid, $season); # redis key
        $RA = self::_getScore_by_uid($uid, $key);                               # A的积分
        $RB = self::_getScore_by_uid($target_uid, $key);                        # B的积分
        $EA = 1 / (1 + pow(10, ($RA - $RB) / 400));                             # A的胜率期望值
        $EB = 1 / (1 + pow(10, ($RB - $RA) / 400));                             # B的胜率期望值
        $K = 32;                                                                # 浮动系数, 暂定为32
        $SA = $result ? 1 : 0;                                                  # 我的胜负结果
        $SB = $result ? 0 : 1;                                                  # 对手的胜负结果
        $R_A = intval($RA + $K * ($SA - $EA ));                                 # 我的最终积分
        $R_B = intval($RB + $K * ($SB - $EB));                                  # 对手的最终积分
        #
        $myOldRank = self::_getRank_by_uid($uid, $key);                         # 记录下战前排名
        self::_addScore_by_uid($zoneid, $uid, $R_A - $RA);                      # 更新A的积分
        $B_Change = $result ? $R_B - $RB : 0;                                   # 计算对方的积分变化:对方输了的情况下扣分,我输了的情况下,不加分
        if ($B_Change != 0) {                                                          # 
            self::_addScore_by_uid($zoneid, $target_uid, $B_Change);            # 更新B的积分 
        }
        $myNewRank = self::_getRank_by_uid($uid, $key);                         # 查询战后排名
        if ($result) {
            $pvp->totalWin += 1;
            TaskProc::OnPvPWinN($pvp->totalWin);
            TaskProc::OnPvPScoreN($R_A);
        }
        req()->userInfo->game->pvp = $pvp;
        TaskProc::OnPvp();                                                      # 每日PVP挑战即可
        UserProc::updateUserInfo();                                             # 回写数据
        // 写挑战记录
        $key_mine = MemKey_User::PVP_OffensiveLog_zset($zoneid, $uid);          # 我的主动挑战记录
        self::_Log_PVP_PK_Info($key_mine, $target_uid, $target_name, $target_HeadImg, $result, $R_A - $RA); # 自己的挑战记录
        $key_target = MemKey_User::PVP_DefensiveLog_zset($zoneid, $target_uid); # 对手的被挑战记录
        self::_Log_PVP_PK_Info($key_target, $uid, $baseInfo->name, $baseInfo->headImg, !$result, $B_Change); # 对手的被挑战记录

        $ret = array(#                                                          # 组装返回值
            'freeFightTickets' => $pvp->freeFightTickets, #                     # 自己剩余免费票
            'fightTicket' => $pvp->fightTicket, #                               # 自己剩余挑战票
            'RA' => $RA, #                                                      # 自己挑战之前积分
            'RB' => $RB, #                                                      # 对手挑战之前积分
            'R_A' => $R_A, #                                                    # 自己最新积分
            'R_B' => $R_B, #                                                    # 对手最新积分
            'rank_diff' => $myNewRank - $myOldRank, #                           # 自己的排名变化
            'rank_new' => $myNewRank, #                                         # 最新排名
        );
        return Resp::ok($ret);                                                  # 返回
    }

    /**
     * [6813] 竞技场 设置防守队伍
     */
    public static function pvp_setTeam() {
        $heros = req()->paras[0];                                                # para: 新阵容
        $pvp = new Info_UserPVP(req()->userInfo->game->pvp);
        my_Assert(is_array($heros), ErrCode::paras_err);                        # 参数检查
        $pvp->defTeam = $heros;                                                 # 更新阵容
        req()->userInfo->game->pvp = $pvp;
        UserProc::updateUserInfo();                                             # 回存数据
        return Resp::ok($pvp);                                                  # 返回
    }

    /**
     * [6814] 购买挑战票 
     * @return Resp
     */
    static function pvp_buyticket() {
        $amt = req()->paras[0];                                                 # 购买数量
        my_Assert($amt > 0, ErrCode::paras_err);                                # 数量>0
        $pvp = req()->userInfo->game->pvp();
        $g = glc();
        $costCash = $g->PVP_pk_ticket_price * $amt;                             # 计算消耗钻石
        my_Assert($costCash > 0, ErrCode::pvp_ticket_cost_ilegal);              # 定价数据异常
        my_Assert(req()->userInfo->game->base()->Consume_Cash($costCash), ErrCode::notenough_cash_msg);    # 扣除钻石失败

        $pvp->fightTicket += $amt;                                              # 发放挑战票
//        req()->userInfo->game->pvp = $pvp;
        UserProc::updateUserInfo();                                             # 回写玩家数据
        $ret = array(
            'fightTicket' => $pvp->fightTicket,
            'costCash' => $costCash,
            'userCash' => req()->userInfo->game->baseInfo->cash
        );
        return Resp::ok($ret);                                                  # 返回
    }

    /**
     * [6815] 竞技场 拉取榜单数据 
     */
    static function pvp_getRank() {
        $maxAmt = 10;                                                           # 一次最多取10个玩家信息
        $zoneid = req()->zoneid;
        list($index, $n) = req()->paras;                                        # 起始(0), 数量(n<=max)
        if ($n < 1 || $n > $maxAmt) {                                           # 防御非法情况
            $n = $maxAmt;
        }
        $arr = self::getRankPlayers($zoneid, $index - 1, ($index + $n) - 1);    // 上榜玩家
        $rankId = $index;
        $result = ObjectInit();
        if (count($arr)) {
            foreach ($arr as $key => $value) {                                  //  $value 的数据类型是array
                $value["uid"] = $key;
                $result->$rankId = $value;
                $rankId += 1;
            }
        }
        $key_rank = MemKey_GameRun::Game_PVPScoreByZoneSeason_zset($zoneid, self::GetCurSeasonID());
        $myRank = self::_getRank_by_uid(req()->uid, $key_rank);
        $myScore = self::_getScore_by_uid(req()->uid, $key_rank);
        $ret = array(
            'dic' => $result,
            'myRank' => $myRank,
            'myScore' => $myScore
        );
        return Resp::ok($ret);
    }

    /**
     * [6816] 竞技场 查看战报 
     */
    static function pvp_getFightLogs() {
        // 提取主动挑战+被挑战记录
        // 更新下刷新时间
        // 返回
        // 参数:无
        $key_off = MemKey_User::PVP_OffensiveLog_zset(req()->zoneid, req()->uid);
        $key_def = MemKey_User::PVP_DefensiveLog_zset(req()->zoneid, req()->uid);
        // 拉取自己的挑战记录
        $offLog = gMem()->zrange($key_off, 0, self::maxLogCount);               # 主动挑战数据
        $defLog = gMem()->zrange($key_def, 0, self::maxLogCount);               # 防守数据
        // Ps. 挑战记录分为2个榜, 且按照时间戳记录,晚于指定时间戳的判定为未读消息,挑战记录最多记录50条
        $pvp = new Info_UserPVP(req()->userInfo->game->pvp);                    # 玩家竞技场数据
        $pvp->lastCheckDefLog_ts = now();                                       # 记录时间戳 
        UserProc::updateUserInfo();                                             # 回写数据 
        array_walk($offLog, function (&$i) {                                    # 解码一下
            $i = JsonUtil::decode($i);
        });
        array_walk($defLog, function (&$i) {                                    # 解码一下
            $i = JsonUtil::decode($i);
        });
        return Resp::ok(array(
                    'offLog' => $offLog,
                    'defLog' => $defLog
        ));
    }

// ---------------- 辅助函数 -----------------------
// <editor-fold defaultstate="collapsed" desc="    辅助 函数   ">

    /**
     * 竞技场 - 记录挑战结果 
     */
    private static function _Log_PVP_PK_Info($key, $oppUID, $name, $headImg, $win, $score) {
        $ts = now();                                                            # 记录时间戳
        gMem()->zadd($key, array(#                                              # 组装被挑战对手信息
            JsonUtil::encode(array(
                'uid' => my_null_default($oppUID, "-"), #                       # 对手uid
                'name' => my_null_default($name, ""), #                         # 对手昵称
                'headImg' => my_null_default($headImg, ""), #                   # 对手头像    
                'win' => my_null_default($win, false), #                        # 胜负结果
                'msg' => "竞技场中没有留言羞辱对手的设定.", #                     # 胜利者留言
                'scoreInfo' => my_null_default($score, 0), #                    # 积分变动
                'ts' => $ts, #                                                  # 时间戳 
            )) => $ts));
        gMem()->zremrangebyrank($key, self::maxLogCount, -1);                   # 保留50条数据
    }

    /**
     * 修改积分
     * @param int $zoneid
     * @param string $uid
     * @param int $addValue 可以是负值
     */
    private static function _addScore_by_uid($zoneid, $uid, $addValue) {
        $mem = gMem();
        $seasonId = self::GetCurSeasonID();
        $key = MemKey_GameRun::Game_PVPScoreByZoneSeason_zset($zoneid, $seasonId); # 积分榜
        $score = $mem->zscore($key, $uid);
        if (is_null($score) || $score <= 0) {                                   # 分数异常, 理论上不会出现负分
            $score = self::pvpBaseScore;                                        # 新手基础分 
            $mem->zadd($key, array($uid => $score));                            # 重置玩家积分
        }
        $newscore = $mem->zincrby($key, $uid, $addValue);                       # 返回最新值 
//        var_dump($newscore);
        return $newscore;
    }

    /**
     * 竞技场 查询玩家的积分 
     * @param type $uid
     * @return int 
     */
    static function _getScore_by_uid($uid, $key) {
        if ("" == $uid) {
            CLog::err('"uid" is Null!');
            return 10;                                                          # 记录错误并返回一个极低分
        }
        $mem = gMem();
//        var_dump($key);
        $score = $mem->zscore($key, $uid);
        if (is_null($score) || $score <= 0) {                                   # 分数异常
            $score = self::pvpBaseScore;                                        # 新手基础分
            $mem->zadd($key, array($uid => $score));                            # 更新玩家积分
        }
        return $score;
    }

    /**
     * 竞技场 查询玩家的排名
     * @param string $uid
     * @param string $key
     * @return int 
     */
    static function _getRank_by_uid($uid, $key) {
        $rank = self::pvpMaxRank + 1;                                           # 未上榜
        if ("" == $uid) {
            CLog::err('"uid" is Null!');
            return $rank;                                                       # 记录错误并返回未上榜
        }
        $mem = gMem();

        $r = $mem->zrevrank($key, $uid);
        if (!is_null($r)) {
            $rank = $r + 1;                                                     # 有名次,更新(zset从0开始排序)
        }
        return $rank;                                                           # 返回
    }

    /**
     * 获取对手匹配结果
     * @param Info_UserPVP $pvp
     * @param CRedisutil $mem
     * @param type $uid
     * @param type $zoneid
     */
    private static function getNewMatches($pvp, $uid, $zoneid) {
        $seasonID = self::GetCurSeasonID();
        $key = MemKey_GameRun::Game_PVPScoreByZoneSeason_zset($zoneid, $seasonID); # redis key
        $arr = self::findmatcher($key, $uid);                                   # 积分榜查找 
        if (count($arr) < self::matchCount) {                                   # 再不行, 准备机器人吧
            CLog::err('PVP刷对手数量不足, 赶快考虑加入机器人.', 'PVP');           // todo: 这里引入gm对手数据,
        }
        $ret = self::GetPlayerInfosForPVP($zoneid, $arr);
        return $ret;
    }

    /**
     * 查找匹配的对手
     * @param type $key
     * @param type $uid
     */
    private static function findmatcher($key, $uid) {
        // 根据匹配规格获得5个对手即可(1. 排除自己, 2. 最好不要出现已经刷到过的对手)
        // 低于玩家当前积分 -- 1个 -5%~-20%之间
        // 与玩家当前积分基本持平 2个, ±5%之间
        // 高于玩家当前积分 -- 1个 +5%~+40%之间 
        $score = self::_getScore_by_uid($uid, $key);                            # 查积分 
        // 计算 比自己弱的 上线下线
        $bH = ceil($score * (1 - 0.05));                                        # 低于当前玩家积分5%
        $bL = ceil($score * (1 - 0.2));                                         # 低于当前玩家积分20%
        $aL = ceil($score * (1 + 0.05));                                        # 高于当前玩家积分5%
        $aH = ceil($score * (1 + 0.2));                                         # 高于当前玩家积分20%
        $bPlayerUIDs = gMem()->zrevrangebyscore($key, $bH, $bL, true, true, 0, 10); # 取低于玩家积分的uids1个
        $mPlayerUIDs = gMem()->zrevrangebyscore($key, $aL, $bH, true, true, 0, 13); # 取玩家相当的uid2个(以防包含玩家自己)
        $aPlayerUIDs = gMem()->zrevrangebyscore($key, $aH, $aL, true, true, 0, 10); # 取高于玩家的uids1个
        if (array_key_exists($uid, $mPlayerUIDs)) {                             # 如果积分相近的那一组包含自己
            unset($mPlayerUIDs[$uid]);                                          # 剔除掉
        } else {                                                                # 或者不包含自己
            array_pop($mPlayerUIDs);                                            # 那多一个人,踢掉一个
        }

        $bPlayerUIDs = self::array_random_assoc($bPlayerUIDs, 1);
        $mPlayerUIDs = self::array_random_assoc($mPlayerUIDs, 2);
        $aPlayerUIDs = self::array_random_assoc($aPlayerUIDs, 1);

        $uidWithScore = array_merge($bPlayerUIDs, $mPlayerUIDs, $aPlayerUIDs);    # 合并为玩家列表

        return $uidWithScore;                                                   # 返回uid集合
    }

    /**
     * 随机关联数组
     * @param type $arr
     * @param type $num
     * @return type
     */
    private static function array_random_assoc($arr, $num = 1) {
        $keys = array_keys($arr);
        shuffle($keys);

        if ($num > count($arr)) {
            $num = count($keys);
        }

        $r = array();
        for ($i = 0; $i < $num; $i++) {
            $r[$keys[$i]] = $arr[$keys[$i]];
        }
        return $r;
    }

    /**
     * 获取榜单玩家
     * @param Credisutil $mem
     * @param int $zoneid
     * @param int $start
     * @param int $stop
     * @return type
     */
    private static function getRankPlayers($zoneid, $start, $stop) {
        $key = MemKey_GameRun::Game_PVPScoreByZoneSeason_zset($zoneid, self::GetCurSeasonID());
        $retUidsWithScore = gMem()->zrevrange($key, $start, $stop, true);
        return self::GetPlayerInfosForPVP($zoneid, $retUidsWithScore);
    }

//    private  static function get

    /**
     * 拉取玩家信息-pvp模块专用信息
     * @param CredisUtil $mem
     * @param type $zoneid
     * @param type $retUidsWithScore
     * @return type
     */
    private static function GetPlayerInfosForPVP($zoneid, $retUidsWithScore) {
        $arr = ArrayInit();
        foreach ($retUidsWithScore as $uid => $score) {
            $userGameInfo = UserProc::getUserInfo($zoneid, $uid);               # 玩家数据  
            $teamConfig = $userGameInfo->game->pvp->defTeam;                    # 防守阵容 
            $heros = new \stdClass();                                           # 英雄集合
            foreach ($teamConfig as $hid) {
                if ($hid > 0) {
                    $n_hid = $hid - 10000;
                    $heros->$n_hid = $userGameInfo->game->heros->collectHeros->$hid;
                }
            }
            $fpower = HeroProc::CalcUserFightPower($zoneid, $uid, $userGameInfo->game); # 计算总战力
            $adversary = array(#                                                # 拼装玩家信息
                'uid' => $uid,
                'name' => my_null_default($userGameInfo->game->baseInfo->name, ""),
                'level' => my_null_default($userGameInfo->game->baseInfo->level, 1),
                'headImg' => my_null_default($userGameInfo->game->baseInfo->headImg, ""),
//            'skills' => null, #                                                 # skills暂时没有实例数据 
                'equipment' => array("equipments" => my_null_default($userGameInfo->game->store->equipment, new \stdClass())), # 武器
                'yanling' => array("items" => my_null_default($userGameInfo->game->store->yanling, new \stdClass())), # 言灵
                'heros' => my_null_default($heros, new \stdClass()), #          # 英雄集合
                'score' => $score, #                                            # 积分
                'fpower' => $fpower, #                                          # 总战力
            );
//            $arr[$uid] = $adversary;
            $arr[] = $adversary;
        }

        if (count($arr) <= 0) {
            $arr = null;
        }
        return $arr;
    }

// </editor-fold>
//
// </editor-fold>
// 
}
