PVPProc.php 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659
  1. <?php
  2. namespace loyalsoft;
  3. /**
  4. * PVPProc 竞技场 战斗模块, 挑战玩家镜像
  5. * @version
  6. * 1.0.0 Created at 2017-6-26. by --gwang
  7. * @author gwang (mail@wanggangzero.cn)
  8. * @copyright ? 2017-6-26, SJZ LoyalSoft Corporation & gwang. All rights reserved.
  9. */
  10. class PVPProc {
  11. //
  12. // <editor-fold defaultstate="collapsed" desc=" 常量 ">
  13. /**
  14. * 挑战记录最大条数
  15. */
  16. const maxLogCount = 49;
  17. /**
  18. * 查找对手数量
  19. */
  20. const matchCount = 5;
  21. /**
  22. * 竞技场赛季起始时间戳
  23. */
  24. const pvpStartTs = 1588521600; # 2020年5月4日 0时0分0秒
  25. /**
  26. * 每个赛季持续时常
  27. */
  28. const pvpSeasonLengt = 86400 * 14; # 2周
  29. /**
  30. * 竞技场初始积分
  31. */
  32. const pvpBaseScore = 1000;
  33. /**
  34. * 竞技场 最大上榜人数
  35. */
  36. const pvpMaxRank = 500;
  37. // </editor-fold>
  38. /**
  39. * [6803] 挑战 - 查询对手信息 等级、头像、昵称、战队信息(言灵师,等级,星级,武器,技能,言灵)
  40. */
  41. public static function GetChallengeAdversaryInfo() {
  42. $targetUID = req()->paras[0]; # 参数: 对手的UID
  43. $uinfo = UserProc::getUserInfo(req()->zoneid, $targetUID); # 读取玩家信息
  44. if (null == $uinfo) {
  45. Err(ErrCode::user_no_err);
  46. }
  47. $team = JsonUtil::decode($uinfo->game->heroTeamConfig);
  48. $heros = new \stdClass();
  49. $curTeamId = $team->curUseTeamID;
  50. foreach ($team->teamDic->$curTeamId->heros as $i => $hid) {
  51. if ($hid > 0) {
  52. $n_hid = $hid - 10000;
  53. $heros->$n_hid = $uinfo->game->heros->collectHeros->$hid;
  54. }
  55. }
  56. $adversary = array(# # 拼装玩家信息
  57. 'uid' => $targetUID,
  58. 'name' => my_null_default($uinfo->game->baseInfo->name, ""),
  59. 'level' => my_null_default($uinfo->game->baseInfo->level, 1),
  60. 'headImg' => my_null_default($uinfo->game->baseInfo->headImg, ""),
  61. // 'skills' => null, # # skills暂时没有实例数据
  62. 'equipment' => array("equipments" => my_null_default($uinfo->game->store->equipment, new \stdClass())), # 武器
  63. 'yanling' => array("items" => my_null_default($uinfo->game->store->yanling, new \stdClass())), # 言灵
  64. 'heros' => my_null_default($heros, new \stdClass()), # # 英雄集合
  65. );
  66. $result = array(# # 拼装返回值
  67. "adversaryInfo" => $adversary
  68. );
  69. return Resp::ok($result);
  70. }
  71. /**
  72. * [6804] 挑战 - 记录挑战结果
  73. */
  74. static function LogChallengeInfo() {
  75. list($targetUID, $name, $headImg, $win, $msg) = req()->paras; // 参数: 对手uid,对手昵称,对手头像, 对战结果, 胜利者的留言(失败时无法留言"")
  76. $key_mine = MemKey_User::OffensiveLog_zset(req()->zoneid, req()->uid);
  77. $key_target = MemKey_User::DefensiveLog_zset(req()->zoneid, $targetUID);
  78. $ts = now(); # 记录时间戳
  79. gMem()->zadd($key_mine, array(# # 组装被挑战对手信息
  80. JsonUtil::encode(array(
  81. 'uid' => my_null_default($targetUID, "-"),
  82. 'name' => my_null_default($name, ""),
  83. 'headImg' => my_null_default($headImg, ""),
  84. 'win' => my_null_default($win, false),
  85. 'msg' => my_null_default($msg, ""),
  86. 'ts' => $ts
  87. )) => $ts));
  88. gMem()->zremrangebyrank($key_mine, self::maxLogCount, -1); # 保留50条数据
  89. gMem()->zadd($key_target, array(# # 组装挑战者信息
  90. JsonUtil::encode(array(
  91. 'uid' => req()->uid,
  92. 'name' => req()->userInfo->game->baseInfo->name,
  93. 'headImg' => req()->userInfo->game->baseInfo->headImg,
  94. 'win' => !my_null_default($win, false),
  95. 'msg' => my_null_default($msg, ""),
  96. 'ts' => $ts
  97. )) => $ts));
  98. gMem()->zremrangebyrank($key_target, self::maxLogCount, -1); # 保留50条数据
  99. // 暂无发放奖励流程
  100. TaskProc::OnRankChalenge(); // 更新每日任务
  101. UserProc::updateUserInfo();
  102. return Resp::ok(); # 返回成功
  103. }
  104. /**
  105. * [6805] 挑战 - 拉取挑战记录
  106. */
  107. static function GetChagllengeLog() {
  108. // 参数:无
  109. $key_off = MemKey_User::OffensiveLog_zset(req()->zoneid, req()->uid);
  110. $key_def = MemKey_User::DefensiveLog_zset(req()->zoneid, req()->uid);
  111. // 拉取自己的挑战记录
  112. $offLog = gMem()->zrange($key_off, 0, self::maxLogCount);
  113. $defLog = gMem()->zrange($key_def, 0, self::maxLogCount);
  114. // Ps. 挑战记录分为2个榜, 且按照时间戳记录,晚于指定时间戳的判定为未读消息,挑战记录最多记录50条
  115. // if (!CommUtil::isPropertyExists($req->userInfo->game->privateState, "lastCheckDefLog")) {
  116. req()->userInfo->game->privateState->lastCheckDefLog_ts = now(); # 记录时间戳
  117. // }
  118. UserProc::updateUserInfo(); # 回写数据
  119. // 记录拉取时间戳(在主界面有个未读消息条数显示, 需要靠最后拉取时间戳对比, 时间戳之后的消息是未读消息)
  120. // 回传数据记录
  121. array_walk($offLog, function (&$i) { # 解码一下
  122. $i = JsonUtil::decode($i);
  123. });
  124. array_walk($defLog, function (&$i) { # 解码一下
  125. $i = JsonUtil::decode($i);
  126. });
  127. return Resp::ok(array(
  128. 'offLog' => $offLog,
  129. 'defLog' => $defLog
  130. ));
  131. }
  132. // <editor-fold defaultstate="collapsed" desc=" 竞技商店 ">
  133. /**
  134. * 王刚 16:23:39 (2020.5.9)
  135. 商店现在的模式定位:
  136. 商店显示所有物品, 刷新时是重置购买/售罄记录
  137. 刘海 16:24:23 (2020.5.9)
  138. 没错
  139. */
  140. /**
  141. * [6820] 竞技商店 主界面
  142. */
  143. public static function pvpShopMain() {
  144. $pvp = new Info_UserPVP(req()->userInfo->game->pvp); # PVP信息
  145. if ($pvp->shopRefreshTs < now()) { # 检查刷新时间<now => 刷新商品列表
  146. $pvp->shopRefreshTs = now() + glc()->PVP_shop_refresh_interval; # 更新刷新时间
  147. $pvp->curShopItems = GameConfig::pvp_shop(); # 重刷道具
  148. req()->userInfo->game->pvp = $pvp; # 回写
  149. UserProc::updateUserInfo();
  150. }
  151. return Resp::ok($pvp); // 返回
  152. }
  153. /**
  154. * [6821] 竞技 商店 购买道具
  155. */
  156. public static function pvpShopBuy() {
  157. $index = req()->paras[0]; # 参数:道具索引(typeId)
  158. $pvp = new Info_UserPVP(req()->userInfo->game->pvp); # PVP 数据
  159. my_Assert(CommUtil::isPropertyExists($pvp->curShopItems, $index), ErrCode::err_innerfault); # 没有找到改商品
  160. // isEditor() && $citem = new \sm_pvp_shop();
  161. $citem = $pvp->curShopItems->$index; # 查询物品数据
  162. my_Assert($citem->sold == 0, ErrCode::pvp_item_soldout); # 防御道具已售罄
  163. // var_dump($citem);
  164. my_Assert($citem->priceType == 5, ErrCode::pay_price_err); # 防御定价异常
  165. my_Assert($pvp->pvpCoins > $citem->price, ErrCode::pvp_coinnotenough); # pvp币不足
  166. $citem->sold += 1; # 设置已售罄/已购买标志
  167. $pvp->pvpCoins -= $citem->price; # 扣除竞技币
  168. StoreProc::AddMultiItemInStore($citem->goods); # 发放道具
  169. req()->userInfo->game->pvp = $pvp; // 回写数据
  170. UserProc::updateUserInfo();
  171. return Resp::ok(array('pvp' => $pvp, 'store' => req()->userInfo->game->store)); # 返回
  172. }
  173. /**
  174. * [6822] 竞技 商店 刷新道具
  175. */
  176. public static function pvpShopRefresh() {
  177. // 扣除刷新消耗
  178. $pvp = req()->userInfo->game->pvp();
  179. $costCash = glc()->PVP_shop_refresh_cash;
  180. my_Assert(req()->userInfo->game->base()->Consume_Cash($costCash), ErrCode::notenough_cash_msg);
  181. // $pvp->shopRefreshTs = now() + glc()->PVP_shop_refresh_interval; # 更新刷新时间
  182. $pvp->curShopItems = GameConfig::pvp_shop(); # 重刷道具
  183. // req()->userInfo->game->pvp = $pvp; # 回写
  184. UserProc::updateUserInfo(); # 回写玩家数据
  185. return Resp::ok($pvp); # 返回
  186. }
  187. // // </editor-fold>
  188. //
  189. // <editor-fold defaultstate="collapsed" desc=" 竞技场 681x">
  190. //
  191. /**
  192. * 辅助方法:取当前赛季的编号(从赛季起始开始算起)
  193. * @return int
  194. */
  195. public static function GetCurSeasonID() {
  196. $n = ceil((now() - self::pvpStartTs ) / self::pvpSeasonLengt); # 进一取整
  197. return $n;
  198. }
  199. /**
  200. * 辅助方法:取指定赛季的结束时间戳
  201. * @param int $seasonID
  202. * @return int
  203. */
  204. public static function GetSeasonEndTs($seasonID) {
  205. $ts = self::pvpStartTs + $seasonID * self::pvpSeasonLengt; # 从起始点开始 + n个赛季时常
  206. return $ts;
  207. }
  208. /**
  209. * [6810] 竞技场 拉取主界面信息
  210. */
  211. static function pvpMainInfo() {
  212. $uid = req()->uid; # 快速访问UID
  213. $zoneid = req()->zoneid; # 快速访问zoneid
  214. $pvp = new Info_UserPVP(req()->userInfo->game->pvp); # 设计玩家pvp数据结构
  215. $pvp->refreshDailyData(); # 刷新免费挑战次数
  216. $seasonId = self::GetCurSeasonID(); # 当前赛季ID
  217. $key = MemKey_GameRun::Game_PVPScoreByZoneSeason_zset($zoneid, $seasonId); # 积分总榜
  218. $score = self::_getScore_by_uid($uid, $key); # 玩家积分
  219. $rank = self::_getRank_by_uid($uid, $key); # 玩家排名
  220. $fPower = HeroProc::CalcUserFightPower($zoneid, $uid, req()->userInfo->game); # 玩家总战力?还是当前防守队伍的战斗力?
  221. $numNewLog = 0; // todo: 真正查询是否有新战报
  222. $matches = self::getNewMatches($pvp, $uid, $zoneid); # 获得新的匹配对手
  223. $pvp->sendRewardEmail($zoneid, $uid, $seasonId); # 发奖励邮件
  224. req()->userInfo->game->pvp = $pvp;
  225. UserProc::updateUserInfo();
  226. $ret = array(# # 组装 返回值结构
  227. 'score' => $score, # # 自己的积分
  228. 'rank' => $rank, # # 自己的排名
  229. 'pvpCoins' => $pvp->pvpCoins, # # 自己的竞技币
  230. 'fPower' => $fPower, # # 自己的总战力
  231. 'fightTicket' => $pvp->fightTicket, # # 自己的挑战票
  232. 'defTeam' => $pvp->defTeam, # # 自己的防守队伍
  233. 'bHasNewFightLog' => $numNewLog, # # 是否有战报刷新
  234. 'matches' => my_null_default($matches, array()), # # 对手列表
  235. );
  236. return Resp::ok($ret); # 返回
  237. }
  238. /**
  239. * [6811] 竞技场 刷新对手
  240. */
  241. static function pvp_Refresh() {
  242. // 刷新无花费, 间隔时间3秒(客户端控制得了)
  243. $pvp = new Info_UserPVP(req()->userInfo->game->pvp);
  244. $ts = now();
  245. my_Assert($pvp->nextRefreshTs < $ts, ErrCode::pvp_refresh_time); # 验证时间间隔
  246. $pvp->curMatches = self::getNewMatches($pvp, req()->uid, req()->zoneid);
  247. $pvp->nextRefreshTs = now(3);
  248. req()->userInfo->game->pvp = $pvp;
  249. UserProc::updateUserInfo(); # 回写数据
  250. $ret = array(
  251. 'curMatches' => my_null_default($pvp->curMatches, array()), # # 当前对手清单
  252. );
  253. return Resp::ok($ret);
  254. }
  255. /**
  256. * [6812] 竞技场 挑战对手xx
  257. */
  258. static function pvp_PK() {
  259. $uid = req()->uid;
  260. $zoneid = req()->zoneid;
  261. $baseInfo = req()->userInfo->game->baseInfo;
  262. list($target_uid, $result, $target_name, $target_HeadImg) = req()->paras; # 对手id,胜负结果 0负,1胜
  263. $pvp = req()->userInfo->game->pvp;
  264. if ($pvp->freeFightTickets > 0) { # 有免费挑战票,先扣除免费的
  265. $pvp->freeFightTickets -= 1;
  266. } else {
  267. my_Assert($pvp->fightTicket > 0, ErrCode::pvp_no_tickets); # 防御: 挑战票不足
  268. $pvp->fightTicket -= 1; # 扣除挑战票
  269. }
  270. $season = self::GetCurSeasonID(); # 当前赛季
  271. $key = MemKey_GameRun::Game_PVPScoreByZoneSeason_zset($zoneid, $season); # redis key
  272. $RA = self::_getScore_by_uid($uid, $key); # A的积分
  273. $RB = self::_getScore_by_uid($target_uid, $key); # B的积分
  274. $EA = 1 / (1 + pow(10, ($RA - $RB) / 400)); # A的胜率期望值
  275. $EB = 1 / (1 + pow(10, ($RB - $RA) / 400)); # B的胜率期望值
  276. $K = 32; # 浮动系数, 暂定为32
  277. $SA = $result ? 1 : 0; # 我的胜负结果
  278. $SB = $result ? 0 : 1; # 对手的胜负结果
  279. $R_A = intval($RA + $K * ($SA - $EA )); # 我的最终积分
  280. $R_B = intval($RB + $K * ($SB - $EB)); # 对手的最终积分
  281. #
  282. $myOldRank = self::_getRank_by_uid($uid, $key); # 记录下战前排名
  283. self::_addScore_by_uid($zoneid, $uid, $R_A - $RA); # 更新A的积分
  284. $B_Change = $result ? $R_B - $RB : 0; # 计算对方的积分变化:对方输了的情况下扣分,我输了的情况下,不加分
  285. if ($B_Change != 0) { #
  286. self::_addScore_by_uid($zoneid, $target_uid, $B_Change); # 更新B的积分
  287. }
  288. $myNewRank = self::_getRank_by_uid($uid, $key); # 查询战后排名
  289. if ($result) {
  290. $pvp->totalWin += 1;
  291. TaskProc::OnPvPWinN($pvp->totalWin);
  292. TaskProc::OnPvPScoreN($R_A);
  293. }
  294. req()->userInfo->game->pvp = $pvp;
  295. TaskProc::OnPvp(); # 每日PVP挑战即可
  296. UserProc::updateUserInfo(); # 回写数据
  297. // 写挑战记录
  298. $key_mine = MemKey_User::PVP_OffensiveLog_zset($zoneid, $uid); # 我的主动挑战记录
  299. self::_Log_PVP_PK_Info($key_mine, $target_uid, $target_name, $target_HeadImg, $result, $R_A - $RA); # 自己的挑战记录
  300. $key_target = MemKey_User::PVP_DefensiveLog_zset($zoneid, $target_uid); # 对手的被挑战记录
  301. self::_Log_PVP_PK_Info($key_target, $uid, $baseInfo->name, $baseInfo->headImg, !$result, $B_Change); # 对手的被挑战记录
  302. $ret = array(# # 组装返回值
  303. 'freeFightTickets' => $pvp->freeFightTickets, # # 自己剩余免费票
  304. 'fightTicket' => $pvp->fightTicket, # # 自己剩余挑战票
  305. 'RA' => $RA, # # 自己挑战之前积分
  306. 'RB' => $RB, # # 对手挑战之前积分
  307. 'R_A' => $R_A, # # 自己最新积分
  308. 'R_B' => $R_B, # # 对手最新积分
  309. 'rank_diff' => $myNewRank - $myOldRank, # # 自己的排名变化
  310. 'rank_new' => $myNewRank, # # 最新排名
  311. );
  312. return Resp::ok($ret); # 返回
  313. }
  314. /**
  315. * [6813] 竞技场 设置防守队伍
  316. */
  317. public static function pvp_setTeam() {
  318. $heros = req()->paras[0]; # para: 新阵容
  319. $pvp = new Info_UserPVP(req()->userInfo->game->pvp);
  320. my_Assert(is_array($heros), ErrCode::paras_err); # 参数检查
  321. $pvp->defTeam = $heros; # 更新阵容
  322. req()->userInfo->game->pvp = $pvp;
  323. UserProc::updateUserInfo(); # 回存数据
  324. return Resp::ok($pvp); # 返回
  325. }
  326. /**
  327. * [6814] 购买挑战票
  328. * @return Resp
  329. */
  330. static function pvp_buyticket() {
  331. $amt = req()->paras[0]; # 购买数量
  332. my_Assert($amt > 0, ErrCode::paras_err); # 数量>0
  333. $pvp = req()->userInfo->game->pvp();
  334. $g = glc();
  335. $costCash = $g->PVP_pk_ticket_price * $amt; # 计算消耗钻石
  336. my_Assert($costCash > 0, ErrCode::pvp_ticket_cost_ilegal); # 定价数据异常
  337. my_Assert(req()->userInfo->game->base()->Consume_Cash($costCash), ErrCode::notenough_cash_msg); # 扣除钻石失败
  338. $pvp->fightTicket += $amt; # 发放挑战票
  339. // req()->userInfo->game->pvp = $pvp;
  340. UserProc::updateUserInfo(); # 回写玩家数据
  341. $ret = array(
  342. 'fightTicket' => $pvp->fightTicket,
  343. 'costCash' => $costCash,
  344. 'userCash' => req()->userInfo->game->baseInfo->cash
  345. );
  346. return Resp::ok($ret); # 返回
  347. }
  348. /**
  349. * [6815] 竞技场 拉取榜单数据
  350. */
  351. static function pvp_getRank() {
  352. $maxAmt = 10; # 一次最多取10个玩家信息
  353. $zoneid = req()->zoneid;
  354. list($index, $n) = req()->paras; # 起始(0), 数量(n<=max)
  355. if ($n < 1 || $n > $maxAmt) { # 防御非法情况
  356. $n = $maxAmt;
  357. }
  358. $arr = self::getRankPlayers($zoneid, $index - 1, ($index + $n) - 1); // 上榜玩家
  359. $rankId = $index;
  360. $result = ObjectInit();
  361. if (count($arr)) {
  362. foreach ($arr as $key => $value) { // $value 的数据类型是array
  363. $value["uid"] = $key;
  364. $result->$rankId = $value;
  365. $rankId += 1;
  366. }
  367. }
  368. $key_rank = MemKey_GameRun::Game_PVPScoreByZoneSeason_zset($zoneid, self::GetCurSeasonID());
  369. $myRank = self::_getRank_by_uid(req()->uid, $key_rank);
  370. $myScore = self::_getScore_by_uid(req()->uid, $key_rank);
  371. $ret = array(
  372. 'dic' => $result,
  373. 'myRank' => $myRank,
  374. 'myScore' => $myScore
  375. );
  376. return Resp::ok($ret);
  377. }
  378. /**
  379. * [6816] 竞技场 查看战报
  380. */
  381. static function pvp_getFightLogs() {
  382. // 提取主动挑战+被挑战记录
  383. // 更新下刷新时间
  384. // 返回
  385. // 参数:无
  386. $key_off = MemKey_User::PVP_OffensiveLog_zset(req()->zoneid, req()->uid);
  387. $key_def = MemKey_User::PVP_DefensiveLog_zset(req()->zoneid, req()->uid);
  388. // 拉取自己的挑战记录
  389. $offLog = gMem()->zrange($key_off, 0, self::maxLogCount); # 主动挑战数据
  390. $defLog = gMem()->zrange($key_def, 0, self::maxLogCount); # 防守数据
  391. // Ps. 挑战记录分为2个榜, 且按照时间戳记录,晚于指定时间戳的判定为未读消息,挑战记录最多记录50条
  392. $pvp = new Info_UserPVP(req()->userInfo->game->pvp); # 玩家竞技场数据
  393. $pvp->lastCheckDefLog_ts = now(); # 记录时间戳
  394. UserProc::updateUserInfo(); # 回写数据
  395. array_walk($offLog, function (&$i) { # 解码一下
  396. $i = JsonUtil::decode($i);
  397. });
  398. array_walk($defLog, function (&$i) { # 解码一下
  399. $i = JsonUtil::decode($i);
  400. });
  401. return Resp::ok(array(
  402. 'offLog' => $offLog,
  403. 'defLog' => $defLog
  404. ));
  405. }
  406. // ---------------- 辅助函数 -----------------------
  407. // <editor-fold defaultstate="collapsed" desc=" 辅助 函数 ">
  408. /**
  409. * 竞技场 - 记录挑战结果
  410. */
  411. private static function _Log_PVP_PK_Info($key, $oppUID, $name, $headImg, $win, $score) {
  412. $ts = now(); # 记录时间戳
  413. gMem()->zadd($key, array(# # 组装被挑战对手信息
  414. JsonUtil::encode(array(
  415. 'uid' => my_null_default($oppUID, "-"), # # 对手uid
  416. 'name' => my_null_default($name, ""), # # 对手昵称
  417. 'headImg' => my_null_default($headImg, ""), # # 对手头像
  418. 'win' => my_null_default($win, false), # # 胜负结果
  419. 'msg' => "竞技场中没有留言羞辱对手的设定.", # # 胜利者留言
  420. 'scoreInfo' => my_null_default($score, 0), # # 积分变动
  421. 'ts' => $ts, # # 时间戳
  422. )) => $ts));
  423. gMem()->zremrangebyrank($key, self::maxLogCount, -1); # 保留50条数据
  424. }
  425. /**
  426. * 修改积分
  427. * @param int $zoneid
  428. * @param string $uid
  429. * @param int $addValue 可以是负值
  430. */
  431. private static function _addScore_by_uid($zoneid, $uid, $addValue) {
  432. $mem = gMem();
  433. $seasonId = self::GetCurSeasonID();
  434. $key = MemKey_GameRun::Game_PVPScoreByZoneSeason_zset($zoneid, $seasonId); # 积分榜
  435. $score = $mem->zscore($key, $uid);
  436. if (is_null($score) || $score <= 0) { # 分数异常, 理论上不会出现负分
  437. $score = self::pvpBaseScore; # 新手基础分
  438. $mem->zadd($key, array($uid => $score)); # 重置玩家积分
  439. }
  440. $newscore = $mem->zincrby($key, $uid, $addValue); # 返回最新值
  441. // var_dump($newscore);
  442. return $newscore;
  443. }
  444. /**
  445. * 竞技场 查询玩家的积分
  446. * @param type $uid
  447. * @return int
  448. */
  449. static function _getScore_by_uid($uid, $key) {
  450. if ("" == $uid) {
  451. CLog::err('"uid" is Null!');
  452. return 10; # 记录错误并返回一个极低分
  453. }
  454. $mem = gMem();
  455. // var_dump($key);
  456. $score = $mem->zscore($key, $uid);
  457. if (is_null($score) || $score <= 0) { # 分数异常
  458. $score = self::pvpBaseScore; # 新手基础分
  459. $mem->zadd($key, array($uid => $score)); # 更新玩家积分
  460. }
  461. return $score;
  462. }
  463. /**
  464. * 竞技场 查询玩家的排名
  465. * @param string $uid
  466. * @param string $key
  467. * @return int
  468. */
  469. static function _getRank_by_uid($uid, $key) {
  470. $rank = self::pvpMaxRank + 1; # 未上榜
  471. if ("" == $uid) {
  472. CLog::err('"uid" is Null!');
  473. return $rank; # 记录错误并返回未上榜
  474. }
  475. $mem = gMem();
  476. $r = $mem->zrevrank($key, $uid);
  477. if (!is_null($r)) {
  478. $rank = $r + 1; # 有名次,更新(zset从0开始排序)
  479. }
  480. return $rank; # 返回
  481. }
  482. /**
  483. * 获取对手匹配结果
  484. * @param Info_UserPVP $pvp
  485. * @param CRedisutil $mem
  486. * @param type $uid
  487. * @param type $zoneid
  488. */
  489. private static function getNewMatches($pvp, $uid, $zoneid) {
  490. $seasonID = self::GetCurSeasonID();
  491. $key = MemKey_GameRun::Game_PVPScoreByZoneSeason_zset($zoneid, $seasonID); # redis key
  492. $arr = self::findmatcher($key, $uid); # 积分榜查找
  493. if (count($arr) < self::matchCount) { # 再不行, 准备机器人吧
  494. CLog::err('PVP刷对手数量不足, 赶快考虑加入机器人.', 'PVP'); // todo: 这里引入gm对手数据,
  495. }
  496. $ret = self::GetPlayerInfosForPVP($zoneid, $arr);
  497. return $ret;
  498. }
  499. /**
  500. * 查找匹配的对手
  501. * @param type $key
  502. * @param type $uid
  503. */
  504. private static function findmatcher($key, $uid) {
  505. // 根据匹配规格获得5个对手即可(1. 排除自己, 2. 最好不要出现已经刷到过的对手)
  506. // 低于玩家当前积分 -- 1个 -5%~-20%之间
  507. // 与玩家当前积分基本持平 2个, ±5%之间
  508. // 高于玩家当前积分 -- 1个 +5%~+40%之间
  509. $score = self::_getScore_by_uid($uid, $key); # 查积分
  510. // 计算 比自己弱的 上线下线
  511. $bH = ceil($score * (1 - 0.05)); # 低于当前玩家积分5%
  512. $bL = ceil($score * (1 - 0.2)); # 低于当前玩家积分20%
  513. $aL = ceil($score * (1 + 0.05)); # 高于当前玩家积分5%
  514. $aH = ceil($score * (1 + 0.2)); # 高于当前玩家积分20%
  515. $bPlayerUIDs = gMem()->zrevrangebyscore($key, $bH, $bL, true, true, 0, 10); # 取低于玩家积分的uids1个
  516. $mPlayerUIDs = gMem()->zrevrangebyscore($key, $aL, $bH, true, true, 0, 13); # 取玩家相当的uid2个(以防包含玩家自己)
  517. $aPlayerUIDs = gMem()->zrevrangebyscore($key, $aH, $aL, true, true, 0, 10); # 取高于玩家的uids1个
  518. if (array_key_exists($uid, $mPlayerUIDs)) { # 如果积分相近的那一组包含自己
  519. unset($mPlayerUIDs[$uid]); # 剔除掉
  520. } else { # 或者不包含自己
  521. array_pop($mPlayerUIDs); # 那多一个人,踢掉一个
  522. }
  523. $bPlayerUIDs = self::array_random_assoc($bPlayerUIDs, 1);
  524. $mPlayerUIDs = self::array_random_assoc($mPlayerUIDs, 2);
  525. $aPlayerUIDs = self::array_random_assoc($aPlayerUIDs, 1);
  526. $uidWithScore = array_merge($bPlayerUIDs, $mPlayerUIDs, $aPlayerUIDs); # 合并为玩家列表
  527. return $uidWithScore; # 返回uid集合
  528. }
  529. /**
  530. * 随机关联数组
  531. * @param type $arr
  532. * @param type $num
  533. * @return type
  534. */
  535. private static function array_random_assoc($arr, $num = 1) {
  536. $keys = array_keys($arr);
  537. shuffle($keys);
  538. if ($num > count($arr)) {
  539. $num = count($keys);
  540. }
  541. $r = array();
  542. for ($i = 0; $i < $num; $i++) {
  543. $r[$keys[$i]] = $arr[$keys[$i]];
  544. }
  545. return $r;
  546. }
  547. /**
  548. * 获取榜单玩家
  549. * @param Credisutil $mem
  550. * @param int $zoneid
  551. * @param int $start
  552. * @param int $stop
  553. * @return type
  554. */
  555. private static function getRankPlayers($zoneid, $start, $stop) {
  556. $key = MemKey_GameRun::Game_PVPScoreByZoneSeason_zset($zoneid, self::GetCurSeasonID());
  557. $retUidsWithScore = gMem()->zrevrange($key, $start, $stop, true);
  558. return self::GetPlayerInfosForPVP($zoneid, $retUidsWithScore);
  559. }
  560. // private static function get
  561. /**
  562. * 拉取玩家信息-pvp模块专用信息
  563. * @param CredisUtil $mem
  564. * @param type $zoneid
  565. * @param type $retUidsWithScore
  566. * @return type
  567. */
  568. private static function GetPlayerInfosForPVP($zoneid, $retUidsWithScore) {
  569. $arr = ArrayInit();
  570. foreach ($retUidsWithScore as $uid => $score) {
  571. $userGameInfo = UserProc::getUserInfo($zoneid, $uid); # 玩家数据
  572. $teamConfig = $userGameInfo->game->pvp->defTeam; # 防守阵容
  573. $heros = new \stdClass(); # 英雄集合
  574. foreach ($teamConfig as $hid) {
  575. if ($hid > 0) {
  576. $n_hid = $hid - 10000;
  577. $heros->$n_hid = $userGameInfo->game->heros->collectHeros->$hid;
  578. }
  579. }
  580. $fpower = HeroProc::CalcUserFightPower($zoneid, $uid, $userGameInfo->game); # 计算总战力
  581. $adversary = array(# # 拼装玩家信息
  582. 'uid' => $uid,
  583. 'name' => my_null_default($userGameInfo->game->baseInfo->name, ""),
  584. 'level' => my_null_default($userGameInfo->game->baseInfo->level, 1),
  585. 'headImg' => my_null_default($userGameInfo->game->baseInfo->headImg, ""),
  586. // 'skills' => null, # # skills暂时没有实例数据
  587. 'equipment' => array("equipments" => my_null_default($userGameInfo->game->store->equipment, new \stdClass())), # 武器
  588. 'yanling' => array("items" => my_null_default($userGameInfo->game->store->yanling, new \stdClass())), # 言灵
  589. 'heros' => my_null_default($heros, new \stdClass()), # # 英雄集合
  590. 'score' => $score, # # 积分
  591. 'fpower' => $fpower, # # 总战力
  592. );
  593. // $arr[$uid] = $adversary;
  594. $arr[] = $adversary;
  595. }
  596. if (count($arr) <= 0) {
  597. $arr = null;
  598. }
  599. return $arr;
  600. }
  601. // </editor-fold>
  602. //
  603. // </editor-fold>
  604. //
  605. }