This commit is contained in:
yajunlu
2022-07-20 11:44:51 +08:00
53 changed files with 133574 additions and 6965 deletions

View File

@@ -0,0 +1,416 @@
import { Application, BackendSession, pinus, HandlerService, } from 'pinus';
import { isArray, pick } from 'underscore';
import { gameData } from '../../../pubUtils/data';
import { STATUS } from '../../../consts/statusCode';
import { resResult } from '../../../pubUtils/util';
import { LadderMatchModel, LadderUpdateInter } from '../../../db/LadderMatch';
import { battleEndWhenChange, checkRank, generateInitRecInfo, generateOppPlayers, getBuyCntCost, getLadderData, getLadderEnemies, getLadderOppDetailData, getLadderOppStatus, getNumberArr, ladderBattleEndReward, refreshLadderDaily, refreshLadderEnemies, sendLadderDailyReward, uniqueArr } from '../../../services/ladderService';
import { LadderDataReturn, LadderDefense, LadderDefenseHero, LadderOppDetailReturn, LadderOppLineupReturn, LadderOppPlayerHeroInfo, LadderOppPlayerReturn } from '../../../domain/battleField/ladder';
import { LadderMatchRecModel } from '../../../db/LadderMatchRec';
import { HeroModel } from '../../../db/Hero';
import { LADDER } from '../../../pubUtils/dicParam';
import { handleCost } from '../../../services/role/rewardService';
import { DEBUG_MAGIC_WORD, ITEM_CHANGE_REASON, LADDER_OPP_STATUS, LADDER_STATUS, REDIS_KEY } from '../../../consts';
import { checkBattleHeroesByHid } from '../../../services/normalBattleService';
import { ServerlistModel } from '../../../db/Serverlist';
import { saveLadderDefCeByData } from '../../../services/redisService';
import { pushLadderTopChangeMsg } from '../../../services/sysChatService';
import { checkTaskInLadderEnd, checkTaskInLadderStart, checkTaskInLadderSweep } from '../../../services/task/taskService';
import { BattleRecordModel } from '../../../db/BattleRecord';
export default function (app: Application) {
new HandlerService(app, {});
return new LadderHandler(app);
}
export class LadderHandler {
constructor(private app: Application) {
}
// 1. 获取主界面
async getData(msg: {}, session: BackendSession) {
let roleId = session.get('roleId');
let result = await getLadderData(roleId, true)
return resResult(STATUS.SUCCESS, result);
}
// 2. 获取对手阵容信息
async getOppLineup(msg: { roleId: string, rank: number }, session: BackendSession) {
let roleId: string = session.get('roleId');
let { roleId: targetRoleId, rank } = msg;
let ladderData = await LadderMatchModel.findByRoleId(roleId);
if(!ladderData || !ladderData.defense) return resResult(STATUS.LADDER_NOT_OPEN);
let isRobot = targetRoleId.startsWith('robot');
let result = new LadderOppLineupReturn();
result.setMyRank(ladderData.rank);
if(isRobot) {
let dicLadderDifficultRatio = gameData.ladderDifficultRatio.get(rank);
let dicWar = gameData.war.get(dicLadderDifficultRatio.gkId);
let dicWarJson = gameData.warJson.get(dicWar.dispatchJsonId);
result.setRobot(dicLadderDifficultRatio, dicWarJson);
} else {
let hisLadderData = await LadderMatchModel.findByRoleIdAndInclude(targetRoleId);
result.setPlayer(hisLadderData);
}
return resResult(STATUS.SUCCESS, result);
}
// 3. 点击挑战
async chooseOpp(msg: { roleId: string, rank: number, myRank: number }, session: BackendSession) {
let roleId = session.get('roleId');
let serverId = session.get('serverId');
let sid = session.get('sid');
let { roleId: targetRoleId, rank, myRank } = msg;
// 检查双方排名等,不行的刷新对手回去
let ladderData = await LadderMatchModel.findByRoleId(roleId);
if(!ladderData || !ladderData.defense) return resResult(STATUS.LADDER_NOT_OPEN);
if(!checkRank(myRank, rank)) return resResult(STATUS.LADDER_RANK_ERROR);
let { status, isRobot, hisLadderData } = await getLadderOppStatus(ladderData, targetRoleId, myRank, rank);
if(status != LADDER_OPP_STATUS.BATTLE) {
await LadderMatchModel.unlock(serverId, targetRoleId);
let oppPlayers = await refreshLadderEnemies(ladderData);
return resResult(STATUS.SUCCESS, {
status, oppPlayers
});
}
// 刷新次数
let refOppObj = refreshLadderDaily(ladderData);
let update: LadderUpdateInter = {};
if(refOppObj.shouldRefOpp) {
update = { ...refOppObj };
}
if(refOppObj.challengeCnt + 1 > LADDER.LADDER_CHALLENGE_FREE_TIMES + refOppObj.buyCnt) {
await LadderMatchModel.unlock(serverId, targetRoleId);
return resResult(STATUS.LADDER_CHALLENGE_CNT_MAX);
}
update.challengeCnt = refOppObj.challengeCnt + 1;
ladderData = await LadderMatchModel.updateByRoleIdAndInclude(roleId, update);
// 创建ladderMatchRec发行battleCode
let attackInfo = generateInitRecInfo(false, false, ladderData.rank, ladderData);
let defenseInfo = generateInitRecInfo(isRobot, true, rank, hisLadderData);
let rec = await LadderMatchRecModel.createRec(serverId, roleId, targetRoleId, hisLadderData?.defense, attackInfo, defenseInfo);
// 倒计时倒计时结束没有check设为失败并发失败通知
pinus.app.rpc.systimer.systimerRemote.setLadderCountDown.broadcast(rec.battleCode, rec.checkTime, LADDER_STATUS.CHECK);
let result = await getLadderOppDetailData(rec);
await checkTaskInLadderStart(serverId, roleId, sid);
return resResult(STATUS.SUCCESS, {
status,
challengeCnt: ladderData.challengeCnt,
...result
});
}
// 4. 出战撤退
async giveupCheck(msg: { battleCode: string }, session: BackendSession) {
let roleId = session.get('roleId');
let { battleCode } = msg;
// 更新ladderMatchRec
let rec = await LadderMatchRecModel.giveup(battleCode);
if(rec) {
pinus.app.rpc.systimer.systimerRemote.cancelLadderCountDown.broadcast(rec.battleCode);
}
return resResult(STATUS.SUCCESS, { status: 0, time: 0 });
}
// 5. 获取对手具体战场数据
async getOppData(msg: { battleCode: string }, session: BackendSession) {
let roleId = session.get('roleId');
let { battleCode } = msg;
let rec = await LadderMatchRecModel.findByBattleCode(battleCode);
if(!rec || rec.roleId1 != roleId) return resResult(STATUS.LADDER_REC_NOT_FOUND);
if(rec.status == LADDER_STATUS.COMPLETE || rec.status == LADDER_STATUS.NO) return resResult(STATUS.LADDER_REC_STATUS_IS_COMPLETE);
let result = await getLadderOppDetailData(rec);
return resResult(STATUS.SUCCESS, result);
}
// 6. 布完阵开始挑战
async checkBattle(msg: { battleCode: string, battleId: number, heroes: { actorId: number, order: number }[] }, session: BackendSession) {
let roleId = session.get('roleId');
let roleName = session.get('roleId');
let { battleId, battleCode, heroes } = msg;
let rec = await LadderMatchRecModel.findByBattleCode(battleCode);
if(!rec || rec.roleId1 != roleId) return resResult(STATUS.LADDER_REC_NOT_FOUND);
if(rec.status != LADDER_STATUS.CHECK) return resResult(STATUS.LADDER_REC_STATUS_ERR);
let { isOK, heroes: dbHeroes } = await checkBattleHeroesByHid(roleId, heroes.map(cur => cur.actorId));
if(!isOK) return resResult(STATUS.BATTLE_HERO_NOT_FOUND);
let attackHeroes = dbHeroes.map(hero => {
let heroInfo = new LadderOppPlayerHeroInfo();
heroInfo.setByDefenseHero(hero);
return heroInfo;
});
let warInfo = gameData.war.get(battleId);
await BattleRecordModel.updateBattleRecordByCode(battleCode, {
$set: {
roleId, roleName, battleId,
status: 0,
warName: warInfo?.gk_name,
warType: warInfo?.warType,
}
});
rec = await LadderMatchRecModel.startBattle(battleCode, attackHeroes);
pinus.app.rpc.systimer.systimerRemote.setLadderCountDown.broadcast(rec.battleCode, rec.battleTime, LADDER_STATUS.BATTLE);
return resResult(STATUS.SUCCESS, {
time: rec.battleTime + LADDER.LADDER_BATTLE_COUNTDOWN,
battleCode: rec.battleCode
})
}
// 7. 挑战结算
async battleEnd(msg: { battleCode: string, isSuccess: boolean }, session: BackendSession) {
let roleId = session.get('roleId');
let roleName = session.get('roleName');
let serverId = session.get('serverId');
let sid = session.get('sid');
let { battleCode, isSuccess } = msg;
let rec = await LadderMatchRecModel.findByBattleCode(battleCode);
if(!rec || rec.roleId1 != roleId) return resResult(STATUS.LADDER_REC_NOT_FOUND);
if(rec.status != LADDER_STATUS.BATTLE) return resResult(STATUS.LADDER_REC_STATUS_ERR);
let rank = rec.defenseInfo.oldRank;
let ladderData = await LadderMatchModel.findByRoleId(roleId);
if(!ladderData || !ladderData.defense) return resResult(STATUS.LADDER_NOT_OPEN);
// 取消 checkBattle 的倒计时
pinus.app.rpc.systimer.systimerRemote.cancelLadderCountDown.broadcast(battleCode);
let result = new LadderDataReturn();
// 交换双方排名, transaction
let { isChange, atkLadderMatch, defLadderMatch } = await LadderMatchModel.changeRank(isSuccess, rec.attackInfo, rec.defenseInfo);
if(isChange) {
let oppPlayers = await battleEndWhenChange(atkLadderMatch, true);
await battleEndWhenChange(defLadderMatch, true);
result.setOppPlayers(oppPlayers);
rec = await LadderMatchRecModel.battleEnd(battleCode, isSuccess, atkLadderMatch.rank, defLadderMatch? defLadderMatch.rank: rec.attackInfo.oldRank);
if(atkLadderMatch.rank == 1) {
pushLadderTopChangeMsg(roleId, roleName, serverId);
}
} else {
rec = await LadderMatchRecModel.battleEnd(battleCode, isSuccess);
let oppPlayers = await getLadderEnemies(atkLadderMatch);
result.setOppPlayers(oppPlayers);
}
await BattleRecordModel.updateBattleRecordByCode(battleCode, { $set: { status: isSuccess? 1: 2 } });
let historyRank = ladderData.historyRank;
ladderData = await LadderMatchModel.updateByRoleId(roleId, { historyRank: (historyRank > atkLadderMatch.rank || historyRank == 0)? atkLadderMatch.rank: historyRank });
result.setLadderData(ladderData, rec);
// 获取奖励
let { battleGoods, breakGoods } = await ladderBattleEndReward(roleId, roleName, sid, rank, isSuccess, historyRank, 1);
await checkTaskInLadderEnd(serverId, roleId, sid, isSuccess, historyRank, atkLadderMatch.rank);
return resResult(STATUS.SUCCESS, {...pick(result, ['rank', 'historyRank', 'challengeCnt','status', 'time', 'oppPlayers']), battleGoods, breakGoods});
}
// 8. 战5次扫荡
async battleSweep(msg: { roleId: string, rank: number, count: number }, session: BackendSession) {
let serverId = session.get('serverId');
let roleId = session.get('roleId');
let roleName = session.get('roleName');
let sid = session.get('sid');
let { roleId: targetRoleId, rank, count } = msg;
if(count > LADDER.LADDER_INITIAL_CHALLENGE_TIMES) {
return resResult(STATUS.LADDER_SWEEP_TIMES_OVER);
}
let ladderData = await LadderMatchModel.findByRoleId(roleId);
if(!ladderData || !ladderData.defense) return resResult(STATUS.LADDER_NOT_OPEN);
if(ladderData.rank >= rank) return resResult(STATUS.LADDER_RANK_ERR);
// 刷新次数
let refOppObj = refreshLadderDaily(ladderData);
let update: LadderUpdateInter = {};
if(refOppObj.shouldRefOpp) {
update = { ...refOppObj };
}
if(refOppObj.challengeCnt + count > LADDER.LADDER_CHALLENGE_FREE_TIMES + refOppObj.buyCnt) {
return resResult(STATUS.LADDER_CHALLENGE_CNT_MAX);
}
update.challengeCnt = refOppObj.challengeCnt + count;
ladderData = await LadderMatchModel.updateByRoleIdAndInclude(roleId, update);
// 获取奖励
let { battleGoods } = await ladderBattleEndReward(roleId, roleName, sid, rank, true, rank, count);
await checkTaskInLadderSweep(serverId, roleId, sid, count)
return resResult(STATUS.SUCCESS, {
challengeCnt: ladderData.challengeCnt,
battleGoods
});
}
// 9. 设置防守阵容
async saveDefense(msg: { warId: number, heroes: { actorId: number, ai: number, dataId: number, order: number }[] }, session: BackendSession) {
let { warId, heroes } = msg;
let roleId = session.get('roleId');
let ladderData = await LadderMatchModel.findByRoleId(roleId);
if(!ladderData) return resResult(STATUS.LADDER_NOT_OPEN);
let hids: number[] = heroes.map(cur => cur.actorId);
let dbHeroes = await HeroModel.findByHidRange(hids, roleId, '_id hid ce', true);
let defenseHeroes = heroes.map(cur => {
let dbHero = dbHeroes.find(ccur => ccur.hid == cur.actorId);
return dbHero && new LadderDefenseHero(cur, dbHero._id, dbHero.ce);
}).filter(cur => cur);
let defense = new LadderDefense(defenseHeroes, warId);
ladderData = await LadderMatchModel.updateByRoleId(roleId, { defense });
// 返回
let result = new LadderDataReturn();
result.setLadderData(ladderData);
await saveLadderDefCeByData(roleId, ladderData);
return resResult(STATUS.SUCCESS, pick(result, 'defense'));
}
// 10. 刷新对手
async refreshOppPlayers(msg: {}, session: BackendSession) {
let roleId = session.get('roleId');
let sid = session.get('sid');
let ladderData = await LadderMatchModel.findByRoleId(roleId);
if(!ladderData || !ladderData.defense) return resResult(STATUS.LADDER_NOT_OPEN);
let update: LadderUpdateInter = {};
// 刷新次数
let refOppObj = refreshLadderDaily(ladderData);
if(refOppObj.shouldRefOpp) {
update = { ...refOppObj };
}
if(refOppObj.refOppCnt + 1 > LADDER.LADDER_REFRESH_TIMES) {
return resResult(STATUS.LADDER_REFRESH_CNT_MAX);
}
update.refOppCnt = refOppObj.refOppCnt + 1;
let oppPlayers = await refreshLadderEnemies(ladderData, update);
// 返回
return resResult(STATUS.SUCCESS, { refOppCnt: update.refOppCnt, oppPlayers });
}
// 11. 购买次数
async buyCnt(msg: { count: number }, session: BackendSession) {
let { count } = msg;
let roleId = session.get('roleId');
let sid = session.get('sid');
let ladderData = await LadderMatchModel.findByRoleId(roleId);
if(!ladderData) return resResult(STATUS.LADDER_NOT_OPEN);
let update: LadderUpdateInter = {};
// 刷新次数
let refOppObj = refreshLadderDaily(ladderData);
if(refOppObj.shouldRefOpp) {
update = { ...refOppObj };
}
if(refOppObj.buyCnt + count > LADDER.LADDER_CHALLENGE_COST_TIMES) {
return resResult(STATUS.LADDER_BUY_MAX);
}
let consumes = getBuyCntCost(refOppObj.buyCnt, count);
if(!consumes) return resResult(STATUS.LADDER_BUY_MAX);
let consumeResult = await handleCost(roleId, sid, consumes||[], ITEM_CHANGE_REASON.LADDER_BUY_CNT);
if(!consumeResult) return resResult(STATUS.ROLE_MATERIAL_NOT_ENOUGH);
update.buyCnt = refOppObj.buyCnt + count;
ladderData = await LadderMatchModel.updateByRoleId(roleId, update);
// 返回
let result = new LadderDataReturn();
result.setLadderData(ladderData);
return resResult(STATUS.SUCCESS, pick(result, ['buyCnt', 'challengeCnt']));
}
// 12. 获取战报信息
async getRec(msg: {}, session: BackendSession) {
let roleId = session.get('roleId');
let list = await LadderMatchRecModel.findRec(roleId);
return resResult(STATUS.SUCCESS, { list });
}
// debug接口
// 重置挑战次数 & 购买次数
async resetCnt(msg: { magicWord: string }, session: BackendSession) {
const { magicWord } = msg;
if (magicWord !== DEBUG_MAGIC_WORD) {
return resResult(STATUS.TOKEN_ERR);
}
let roleId = session.get('roleId');
let ladderData = await LadderMatchModel.findByRoleId(roleId);
if(!ladderData) return resResult(STATUS.LADDER_NOT_OPEN);
await LadderMatchModel.updateByRoleId(roleId, { refDaily: new Date(Date.now() - 86400000) });
return resResult(STATUS.SUCCESS);
}
async setMyRank(msg: { magicWord: string, rank: number }, session: BackendSession) {
const { magicWord, rank } = msg;
if (magicWord !== DEBUG_MAGIC_WORD) {
return resResult(STATUS.TOKEN_ERR);
}
let roleId = session.get('roleId');
let ladderData = await LadderMatchModel.findByRoleId(roleId);
if(!ladderData) return resResult(STATUS.LADDER_NOT_OPEN);
ladderData = await LadderMatchModel.updateByRoleId(roleId, { rank, historyRank: (ladderData.historyRank > rank || ladderData.historyRank == 0)? rank: ladderData.historyRank });
await battleEndWhenChange(ladderData, false);
await LadderMatchModel.updateByRoleId(roleId, { refDaily: new Date(Date.now() - 86400000) });
return resResult(STATUS.SUCCESS);
}
async setMyOpp(msg: { magicWord: string, oppRanks: number[] }, session: BackendSession) {
const { magicWord, oppRanks } = msg;
if (magicWord !== DEBUG_MAGIC_WORD) {
return resResult(STATUS.WRONG_PARMS);
}
if(!isArray(oppRanks) || oppRanks.length <= 0) return resResult(STATUS.WRONG_PARMS);
let roleId = session.get('roleId');
let serverId = session.get('serverId');
let ladderData = await LadderMatchModel.findByRoleId(roleId);
if(!ladderData) return resResult(STATUS.LADDER_NOT_OPEN);
let topTen = getNumberArr(1, 10);
let ranks = [...topTen, ...oppRanks];
let newRanks = uniqueArr(ranks.sort((a, b) => a - b));
await generateOppPlayers(newRanks, serverId, roleId);
return resResult(STATUS.SUCCESS);
}
async sendDailyReward(msg: { magicWord: string }, session: BackendSession) {
const { magicWord } = msg;
if (magicWord !== DEBUG_MAGIC_WORD) {
return resResult(STATUS.TOKEN_ERR);
}
let servers = await ServerlistModel.findByEnv(pinus.app.get('env'));
for(let { serverId } of servers) {
await sendLadderDailyReward(serverId);
}
return resResult(STATUS.SUCCESS)
}
}

View File

@@ -11,7 +11,7 @@ import { ItemInter, RewardInter } from '../../../pubUtils/interface';
import { getDropItems, FIGURE_UNLOCK_CONDITION, ITID } from '../../../consts/constModules/itemConst'
import { pushComposeOrangeHero, pushHeroQualityUpMsg, pushHeroStarMax, pushHeroWakeUp } from '../../../services/chatService';
import { PvpDefenseModel } from '../../../db/PvpDefense';
import { checkTask, checkTaskInHeroQUalityUp, checkTaskInHeroStarUp, checkTaskInHeroTrain, checkTaskInHeroWakeUp } from '../../../services/task/taskService';
import { checkTask, checkTaskInHeroGiveFavor, checkTaskInHeroQUalityUp, checkTaskInHeroStarUp, checkTaskInHeroTrain, checkTaskInHeroWakeUp } from '../../../services/task/taskService';
import { isNumber, pick } from 'underscore';
import { updateEplaces } from '../../../services/equipService';
import { addConnect, addConsumeToHero, checkUnlockTalentCondition, initSkinTalent, updateSkinTalent } from '../../../services/roleService';
@@ -423,7 +423,7 @@ export class HeroHandler {
if (heroes.length < dicFriendShip.hids.length) return resResult(STATUS.ROLE_HERO_NOT_EXISTS);
let hero = heroes.find(cur => cur.hid == hid);
if(!hero) return resResult(STATUS.ROLE_HERO_NOT_EXISTS);
let { connections } = hero;
let { connections = []} = hero;
let curConnect = connections.find(cur => cur.shipId == shipId);
let { level: oldLv = 0, exp: oldExp = 0 } = curConnect||{};
@@ -475,8 +475,9 @@ export class HeroHandler {
}
let updateConsume = addConsumeToHero(hero.consumes, material);
let newConnections = addConnect(connections, { shipId, level: newLevel, exp: newExp });
let update = {
connections: addConnect(connections, { shipId, level: newLevel, exp: newExp }),
connections: newConnections,
consumes: updateConsume
}
@@ -484,7 +485,7 @@ export class HeroHandler {
let { curHero } = await calculateCeWithHero(HERO_SYSTEM_TYPE.CONNECT, roleId, serverId, sid, hero.hid, update);
if(isLv) {
// await unlockFigure(sid, roleId, [{ type: FIGURE_UNLOCK_CONDITION.HERO_FAVOR, paramHid: curHero.hid, paramFavourLv: curHero.favourLv }]);
await checkTask(serverId, roleId, sid, TASK_TYPE.HERO_CONNECT, { connectLv: newLevel })
await checkTaskInHeroGiveFavor(serverId, roleId, sid, shipId, newConnections, connections);
}
return resResult(STATUS.SUCCESS, { curHero: { ...pick(curHero, ['hid', 'connections']), shipId } });
}

View File

@@ -1,5 +1,5 @@
import { Application, ChannelService } from 'pinus';
import { resetPvpSeasonTime, guildActivityStart, gateActivityEnd, cityActivityEnd, raceActivityEnd, guildActivitySchedule, auctionSchedule, initMaintenance, stopMaintenance, initAutoCreateServer, addMailsToSchedule, updateTimeLimitRank } from '../../../services/timeTaskService';
import { resetPvpSeasonTime, guildActivityStart, gateActivityEnd, cityActivityEnd, raceActivityEnd, guildActivitySchedule, auctionSchedule, initMaintenance, stopMaintenance, initAutoCreateServer, addMailsToSchedule, updateTimeLimitRank, setLadderCountDown, cancelLadderCountDown } from '../../../services/timeTaskService';
import PvpDefenseType from '../../../db/PvpDefense';
import { DicGuildActivity } from '../../../pubUtils/dictionary/DicGuildActivity';
import { reloadResources } from '../../../pubUtils/data';
@@ -15,6 +15,7 @@ import { MailType } from '../../../db/Mail';
import { GroupMailType } from '../../../db/GroupMail';
import { ServerMailType } from '../../../db/ServerMail';
import { ActivityModelType } from '../../../db/Activity';
import { LADDER_STATUS } from '../../../consts';
export default function (app: Application) {
return new SystimerRemote(app);
@@ -198,4 +199,20 @@ export class SystimerRemote {
errlogger.error(`remote ${__filename} \n ${e.stack}`);
}
}
public async setLadderCountDown(battleCode: string, time: number, status: LADDER_STATUS) {
try {
await setLadderCountDown(battleCode, time, status);
} catch(e) {
errlogger.error(`remote ${__filename} \n ${e.stack}`);
}
}
public async cancelLadderCountDown(battleCode: string) {
try {
await cancelLadderCountDown(battleCode);
} catch(e) {
errlogger.error(`remote ${__filename} \n ${e.stack}`);
}
}
}

View File

@@ -46,6 +46,7 @@ import { sendMessageToAllWithSuc, sendMessageToServerWithSuc, sendMessageToUser,
import { getSurvey } from './gmService';
import { ComBattleTeamModel } from '../db/ComBattleTeam';
import { INFO_WINDOW } from '../pubUtils/dicParam';
import { getLadderData } from './ladderService';
/**
* init: 初始的时候是否推送 true-推 false-不推
@@ -79,6 +80,7 @@ const modules = [
{ id: 24, type: 'donate', init: false, refresh: true, guild: true },
{ id: 25, type: 'shop', init: false, refresh: true, guild: false },
{ id: 26, type: 'survey', init: false, refresh: true, guild: false },
{ id: 27, type: 'ladder', init: false, refresh: true, guild: false },
]
export async function pushData(hasInit: boolean, role: RoleType, session: FrontendOrBackendSession, pushType: 'entry' | 'refresh' = 'entry') {
@@ -206,6 +208,8 @@ export async function getModuleData(type: string, data: { role: RoleType, sessio
return getMainChapter(role);
case 'survey':
return await getSurvey(role.roleId, role.lv);
case 'ladder':
return await getLadderData(role.roleId, false);
default:
return null;
}

View File

@@ -0,0 +1,386 @@
import { ITEM_CHANGE_REASON, LADDER_OPP_STATUS, LADDER_SERVER_GAP_TIME, LADDER_STATUS, MAIL_TYPE, PUSH_ROUTE, REDIS_KEY } from "../consts";
import { HeroType } from "../db/Hero";
import { LadderMatchModel, LadderMatchType, LadderUpdateInter } from "../db/LadderMatch";
import { LadderMatchRecModel, LadderMatchRecType } from "../db/LadderMatchRec";
import { RoleModel, RoleType } from "../db/Role";
import { LadderDataReturn, LadderDefense, LadderOppDetailReturn, LadderOppPlayerHeroInfo, LadderOppPlayerInDB, LadderOppPlayerInfo, LadderOppPlayerReturn } from "../domain/battleField/ladder";
import { RoleAndGuildRankInfo, RoleRankInfo } from "../domain/rank";
import { gameData, getDicLadderMatchByMyRank, getLadderRankReward } from "../pubUtils/data";
import { EXTERIOR, LADDER } from "../pubUtils/dicParam";
import { ItemInter, RewardInter } from "../pubUtils/interface";
import { getRandValueByMinMax, shouldRefresh } from "../pubUtils/util";
import { sendMailByContent } from "./mailService";
import { getHeroesAttributes } from "./playerCeService";
import { sendMessageToUserWithSuc } from "./pushService";
import { Rank } from "./rankService";
import { getLadderDefCe, saveLadderDefCeByData } from "./redisService";
import { addItems } from "./role/rewardService";
import { combineItems } from "./role/util";
import { WarReward } from "./warRewardService";
export function refreshLadderDaily(ladderData: LadderMatchType) {
let { refOppCnt = 0, buyCnt = 0, challengeCnt = 0, refDaily } = ladderData;
let curTime = new Date();
let shouldRefOpp = shouldRefresh(refDaily, curTime);
if (shouldRefOpp) {
refOppCnt = 0; buyCnt = 0; challengeCnt = 0; refDaily = curTime;
}
return {
shouldRefOpp,
refOppCnt, refDaily, buyCnt, challengeCnt,
}
}
export function getBuyCntCost(buyCnt: number, incCnt: number) {
let items: ItemInter[] = [];
for(let i = 1; i <= incCnt; i++) {
let cost = getSingleBuyCost(buyCnt + i);
if(!cost) return false;
items.push(cost);
}
return combineItems(items);
}
function getSingleBuyCost(buyCnt: number) {
for(let { id, count, times } of gameData.ladderConsume) {
if(buyCnt <= times) {
return { id, count }
}
}
return false
}
export async function getLadderData(roleId: string, needOppPlayer: boolean) {
let ladderData = await LadderMatchModel.findByRoleId(roleId);
if(!ladderData) {
let role = await RoleModel.findByRoleId(roleId);
ladderData = await LadderMatchModel.createLadder({ serverId: role.serverId, roleId: role.roleId, role: role._id });
}
// 刷新次数
let refOppObj = refreshLadderDaily(ladderData);
if(refOppObj.shouldRefOpp) {
ladderData = await LadderMatchModel.updateByRoleId(roleId, refOppObj);
}
let ladderRec = await LadderMatchRecModel.findNotCompleteRecByRoleId(roleId);
let result = new LadderDataReturn();
result.setLadderData(ladderData, ladderRec);
if(needOppPlayer) {
let oppPlayersReturn = await getLadderEnemies(ladderData);
result.setOppPlayers(oppPlayersReturn);
}
return result;
}
export async function getLadderEnemies(ladderData: LadderMatchType) {
let oppPlayers = ladderData.oppPlayers||[];
if(!oppPlayers || oppPlayers.length) {
return await getOppPlayerDetailByDb(ladderData.serverId, ladderData.oppPlayers);
} else {
return await refreshLadderEnemies(ladderData);
}
}
async function getOppPlayerDetailByDb(serverId: number, oppPlayers: LadderOppPlayerInDB[]) {
let oppPlayersReturn: LadderOppPlayerReturn[] = [];
for(let oppPlayer of oppPlayers) {
let player = await generateOppPlayer(serverId, oppPlayer.rank, oppPlayers);
oppPlayersReturn.push(player);
}
return oppPlayersReturn;
}
// 获取redis当中的玩家排名数据
export async function getLadderMatchByRank(serverId: number, max: number|'+inf', min: number|'-inf') {
let r = new Rank(REDIS_KEY.LADDER, { serverId }, false, 4000);
let rawResults = await r.getRankByRangeRaw(max, min, false);
return rawResults.map(cur => ({ rank: cur.scores[0], roleId: cur.field }))
}
export async function refreshLadderEnemies(ladderData: LadderMatchType, update: LadderUpdateInter = {}) {
let rankNumbers = randOppRankNumbers(ladderData.rank); // 随机出想要的排名
return await generateOppPlayers(rankNumbers, ladderData.serverId, ladderData.roleId, update);
}
export async function generateOppPlayers(rankNumbers: number[], serverId: number, roleId: string, update: LadderUpdateInter = {}) {
let min = rankNumbers[0], max = rankNumbers[rankNumbers.length - 1];
let ranks = await getLadderMatchByRank(serverId, max, min);
let oppPlayers: LadderOppPlayerReturn[] = [], oppPlayersSave: LadderOppPlayerInDB[] = [];
for(let rank of rankNumbers) {
let player = await generateOppPlayer(serverId, rank, ranks);
oppPlayersSave.push({ rank, roleId: player.roleId, isRobot: player.isRobot });
oppPlayers.push(player);
}
await LadderMatchModel.updateByRoleId(roleId, { ...update, oppPlayers: oppPlayersSave });
return oppPlayers
}
async function generateOppPlayer(serverId: number, rank: number, oppPlayers: { rank: number, roleId: string, isRobot?: boolean }[]) {
let player = oppPlayers.find(cur => cur.rank == rank && !cur.isRobot);
let result = new LadderOppPlayerReturn(rank, !player || rank == 3001);
if(player && rank != 3001) {
let r = new Rank(REDIS_KEY.LADDER, { serverId }, false);
let param = <RoleRankInfo>await r.getParam(player.rank, player.roleId, [player.rank]);
let defCe = await getLadderDefCe(player.roleId);
result.setByRedis(param, defCe);
} else {
let dicLadderDifficultRatio = gameData.ladderDifficultRatio.get(rank);
result.setByRobot(dicLadderDifficultRatio);
}
return result;
}
function randOppRankNumbers(myRank: number) {
let dicLadderMatch = getDicLadderMatchByMyRank(myRank);
if(!dicLadderMatch) return new Array(5).fill(3001);
let beforeRanks = randomRank(true, myRank, dicLadderMatch.rangeBeforeMax, dicLadderMatch.rangeBeforeNum);
let afterRanks = randomRank(false, myRank, dicLadderMatch.rangeAfterMax, dicLadderMatch.rangeAfterNum);
let topTen = getNumberArr(1, 10);
let ranks = [...topTen, ...beforeRanks, ...afterRanks];
return uniqueArr(ranks.sort((a, b) => a - b));
}
export function uniqueArr<T>(arr: T[]) {
return arr.filter( (item, index, arr) => {
//当前元素,在原始数组中的第一个索引==当前索引值,否则返回当前元素
return arr.indexOf(item) === index;
});
}
export function getNumberArr(from: number, to: number) {
let arr: number[] = [];
for(let i = from; i <= to; i++) arr.push(i);
return arr;
}
function randomRank(isBefore: boolean, myRank: number, range: number, len: number) {
if(range == 0 || len == 0) return [];
let arr: number[] = [];
let n = Math.floor(range / len);
for(let i = 0; i < len; i++) {
let rand = getRandValueByMinMax(n * i + 1, n * (i + 1) + 1, 0); // 因为这个的范围是[min, max),而想要的是 (n * i, n * (i - 1)]
if(rand) {
let ratio = isBefore? -1: 1;
arr.push(myRank + ratio * rand);
}
}
return arr;
}
export function checkRank(myRank: number, targetRank: number) {
let dicLadderMatch = getDicLadderMatchByMyRank(myRank);
if(!dicLadderMatch) {
if(myRank == 0 && targetRank == 3001) return true;
return false
}
if(myRank == targetRank) return false;
if(targetRank <= 10) {
return dicLadderMatch.topChallenge == 1;
} else {
if(myRank < targetRank) { // 向后打
if(myRank + dicLadderMatch.rangeAfterMax < targetRank) return false;
} else { // 向前打
if(myRank - dicLadderMatch.rangeBeforeMax > targetRank) return false;
}
}
return true;
}
export async function getLadderOppStatus(ladderData: LadderMatchType, targetRoleId: string, myRank: number, rank: number) {
if(ladderData.rank != myRank) return { status: LADDER_OPP_STATUS.MY_RANK_CHANGE };
let { oppPlayers } = ladderData;
let curOppPlayer = oppPlayers.find(cur => cur.roleId == targetRoleId);
if(!curOppPlayer|| curOppPlayer.rank != rank) return { status: LADDER_OPP_STATUS.OPP_RANK_CHANGE };
if(curOppPlayer.isRobot) {
if(rank <= 3000) {
let hisLadderData = await LadderMatchRecModel.checkByRank(ladderData.serverId, `robot${rank}`);
if(hisLadderData) return { status: LADDER_OPP_STATUS.OPP_RANK_CHANGE };
}
return { status: LADDER_OPP_STATUS.BATTLE, isRobot: true };
} else {
let hisLadderData = await LadderMatchModel.lock(ladderData.serverId, targetRoleId, rank);
if(!hisLadderData) return { status: LADDER_OPP_STATUS.OPP_IS_LOCKED };
return { status: LADDER_OPP_STATUS.BATTLE, isRobot: false, hisLadderData };
}
}
/**
*
* @param isRobot 是否是机器
* @param isDefense 是攻还是守方
* @param rank 排名
* @param ladderData 需要populate过的ladderMatch表
* @returns
*/
export function generateInitRecInfo(isRobot: boolean, isDefense: boolean, rank: number, ladderData: LadderMatchType) {
if(isRobot) {
let dicLadderDifficultRatio = gameData.ladderDifficultRatio.get(rank);
let dicWar = gameData.war.get(dicLadderDifficultRatio.gkId);
let dicWarJson = gameData.warJson.get(dicWar.dispatchJsonId)||[];
let heroes: LadderOppPlayerHeroInfo[] = [];
for(let json of dicWarJson) {
if(json.relation == 2) {
let hero = new LadderOppPlayerHeroInfo();
hero.setByWarJson(json);
heroes.push(hero);
}
}
let info = new LadderOppPlayerInfo();
info.initByRobot(dicLadderDifficultRatio, heroes, isDefense);
return info;
} else {
let defCe = calculateDefCeByDefense(ladderData.defense);
let role = <RoleType>ladderData.role;
let heroes: LadderOppPlayerHeroInfo[] = [];
if(isDefense) { // 防守方可以将武将也存入,攻方要等出战
let defenseHeroes = ladderData.defense?.heroes||[];
for(let defenseHero of defenseHeroes) {
let hero = new LadderOppPlayerHeroInfo();
hero.setByDefenseHero(<HeroType>defenseHero.hero);
heroes.push(hero);
}
}
let info = new LadderOppPlayerInfo();
info.initByPlayer(rank, role, heroes, defCe, isDefense);
return info;
}
}
function calculateDefCeByDefense(defense: LadderDefense) {
if(!defense) return 0;
let ce = 0;
for(let hero of defense.heroes) {
ce += hero.ce;
}
return ce;
}
export async function ladderTimeWillout(battleCode: string, status: LADDER_STATUS) {
console.log('######### ladderTimeWillout')
if (status == LADDER_STATUS.BATTLE) {
let rec = await LadderMatchRecModel.findByBattleCode(battleCode);
await sendMessageToUserWithSuc(rec.roleId1, PUSH_ROUTE.LADDER_BATTLE_STOP, { battleCode });
}
}
export async function ladderTimeout(battleCode: string, status: LADDER_STATUS) {
console.log('######### ladderTimeout')
let rec = await LadderMatchRecModel.timeout(battleCode);
// console.log('##### rec.defenseInfo', rec.defenseInfo, rec.defenseInfo.isRobot, rec.defenseInfo.roleId)
if(rec.defenseInfo && !rec.defenseInfo.isRobot) await LadderMatchModel.unlock(rec.serverId, rec.defenseInfo.roleId);
if(status == LADDER_STATUS.CHECK) {
await sendMessageToUserWithSuc(rec.roleId1, PUSH_ROUTE.LADDER_CHECK_STOP, { battleCode });
}
}
/**
* 结算时候设置排行、发送推送、刷新对手、发送跑马灯
* @param ladderMatch
* @param isAtk
* @returns
*/
export async function battleEndWhenChange(ladderMatch: LadderMatchType, needRefresh: boolean) {
if(!ladderMatch) return;
// 更新redis
if(ladderMatch.rank > 0 && ladderMatch.rank < 3001) {
let r = new Rank(REDIS_KEY.LADDER, { serverId: ladderMatch.serverId });
await r.setRankWithRoleInfo(ladderMatch.roleId, ladderMatch.rank, 0);
await saveLadderDefCeByData(ladderMatch.roleId, ladderMatch);
}
// 刷新对手
if(needRefresh) {
let oppPlayers = await refreshLadderEnemies(ladderMatch);
await sendMessageToUserWithSuc(ladderMatch.roleId, PUSH_ROUTE.LADDER_RANK_UPDATE, {
oldRank: ladderMatch.oldRank,
newRank: ladderMatch.rank,
oppPlayers
});
return oppPlayers
} else {
return []
}
}
export async function ladderBattleEndReward(roleId: string, roleName: string, sid: string, rank: number, isSuccess: boolean, historyRank: number, num: number) {
let dicLadderDifficultRatio = gameData.ladderDifficultRatio.get(rank);
if(!dicLadderDifficultRatio) return null;
let warReward = new WarReward(roleId, roleName, sid, dicLadderDifficultRatio.gkId, isSuccess);
warReward.setLadderReward(dicLadderDifficultRatio.randomFailReward, true);
warReward.setLadderReward(dicLadderDifficultRatio.randomWinReward, false);
let battleGoods = await warReward.saveReward(num);
let breakRewards: RewardInter[] = [];
for(let i = historyRank - 1; i >= rank; i--) {
let dicLadderDifficultRatio = gameData.ladderDifficultRatio.get(i);
breakRewards.push(...(dicLadderDifficultRatio?.onceReward||[]));
}
let breakGoods = breakRewards.length > 0? await addItems(roleId, roleName, sid, combineItems(breakRewards), ITEM_CHANGE_REASON.LADDER_BATTLE_REWARD): [];
return { battleGoods, breakGoods };
}
export function completeLadderRanks(ranks: RoleAndGuildRankInfo[]) {
let result: RoleAndGuildRankInfo[] = [];
for(let i = 1; i <= 200; i++) {
let dicLadderDifficultRatio = gameData.ladderDifficultRatio.get(i);
let { head, ce, spine, level } = dicLadderDifficultRatio;
let rank = ranks.find(cur => cur.rank == i);
if(rank) {
result.push(rank);
} else {
let rank = new RoleAndGuildRankInfo(i, 0);
rank.setByField(`robot${i}`, LADDER.LADDER_ROBOT_NAME, head, EXTERIOR.EXTERIOR_FACECASE, spine, level, 1, '', ce, ce );
result.push(rank);
}
}
return result;
}
export async function sendLadderDailyReward(serverId: number) {
let ranks = await getLadderMatchByRank(serverId, '+inf', '-inf');
for(let { rank, roleId } of ranks) {
let dicRankReward = getLadderRankReward(rank);
if(dicRankReward && dicRankReward.reward && dicRankReward.reward.length > 0) {
await sendMailByContent(MAIL_TYPE.LADDER, roleId, { params: [`${rank}`], goods: dicRankReward.reward });
}
}
}
export async function getLadderOppDetailData(rec: LadderMatchRecType) {
let isRobot = rec.defenseInfo.isRobot;
let dicLadderDifficultRatio = gameData.ladderDifficultRatio.get(rec.defenseInfo.oldRank);
let result = new LadderOppDetailReturn(rec);
if(isRobot) {
result.setByRobot(dicLadderDifficultRatio);
} else {
let hisLadderData = await LadderMatchModel.findByRoleIdAndInclude(rec.roleId2);
let dicWar = gameData.war.get(dicLadderDifficultRatio.gkId);
let dicWarJson = gameData.warJson.get(dicWar.dispatchJsonId);
result.setByPlayer(hisLadderData, dicWarJson);
let attrByHid = await getHeroesAttributes(rec.roleId2);
for(let [hid, attribute] of attrByHid) {
result.setAttribute(hid, attribute.getAttributesToString());
}
}
return result;
}
export async function clearLadderLock() {
let recs = await LadderMatchRecModel.clearLock();
for(let { serverId, defenseInfo } of recs) {
await LadderMatchModel.unlock(serverId, defenseInfo?.roleId);
}
}

View File

@@ -7,7 +7,7 @@ import { EPlace, HeroModel, HeroType, HeroUpdate } from '../db/Hero';
import { RoleUpdate, RoleType, RoleModel } from '../db/Role';
import { Rank } from './rankService';
import { HERO_SYSTEM_TYPE, PUSH_ROUTE, REDIS_KEY } from '../consts';
import { updateRoleOnlineInfo, updateUserInfo } from './redisService';
import { saveLadderDefCe, saveLadderDefCeByData, updateRoleOnlineInfo, updateUserInfo } from './redisService';
import { GuildModel, GuildType } from '../db/Guild';
import { CalCe } from './role/calCe';
import { RoleCeModel, RoleCeUpdate } from '../db/RoleCe';
@@ -18,6 +18,7 @@ import { SchoolModel, SchoolType } from '../db/School';
import { AttributeCal } from '../domain/roleField/attribute';
import { sendMessageToUserWithSuc } from './pushService';
import { SkinType } from '../db/Skin';
import { LadderMatchModel } from '../db/LadderMatch';
interface Param {
isInitRole?: boolean,
@@ -332,6 +333,7 @@ export async function calculateCes(type: HERO_SYSTEM_TYPE, roleId: string, serve
calCe.setResultHero(hero);
heroes.push(hero);
await PvpDefenseModel.updateCe(roleId, hid, ce); // 更新pvp防守阵战力
await LadderMatchModel.updateCe(roleId, hid, ce);
pushHeros.push({ hid, ce, incHeroCe: inc });
changeHids.push(hid);
} else {
@@ -343,6 +345,7 @@ export async function calculateCes(type: HERO_SYSTEM_TYPE, roleId: string, serve
if(changeHids.indexOf(hid) == -1) {
let hero = await HeroModel.updateHeroInfo(roleId, hid, { ce });
await PvpDefenseModel.updateCe(roleId, hid, ce); // 更新pvp防守阵战力
await LadderMatchModel.updateCe(roleId, hid, ce);
pushHeros.push({ hid, ce: hero.ce, incHeroCe: inc });
changeHids.push(hid);
}
@@ -406,7 +409,9 @@ async function updateRank(roleId: string, serverId: number, topLineupCe: number,
// 武将数量
let r6 = new Rank(REDIS_KEY.HERO_NUM_RANK, { serverId });
await r6.setRankWithRoleInfo(roleId, role.heroNum, role.heroNumUpdatedAt, role);
await r6.setRankWithRoleInfo(roleId, role.heroNum, role.heroNumUpdatedAt, role);
await saveLadderDefCeByData(roleId);
}
export async function getSumCe(roleId: string) {

View File

@@ -1,6 +1,6 @@
import { KeyName, KeyNameParam, RankParam, GuildRankParam, RoleRankInfo, LineupParam, myIdInter, GeneralRankParam, ValueConfig, GuildRankInfo, RoleAndGuildRankInfo } from "../domain/rank";
import { getInfoKeyByRedisKey, ROLE_SELECT, GUILD_SELECT, REDIS_KEY, HERO_SELECT, COMPOSE_FIELD_TYPE, KEY_TO_COMPOSE_FIELD, getRedisKeyByRankType, COUNTER, RANK_TYPE, PUSH_ROUTE } from "../consts";
import { redisClient, delKeys } from "./redisService";
import { redisClient, delKeys, saveLadderDefCeByData } from "./redisService";
import { RoleType, RoleModel } from "../db/Role";
import { GuildType, GuildModel } from "../db/Guild";
import { HeroModel, HeroType, HeroUpdate } from "../db/Hero";
@@ -14,6 +14,8 @@ import { RANK_FIRST_REWARD_STATUS } from '../consts';
import { CounterModel } from "../db/Counter";
import { ActivityTimeLimitRankModel } from "../db/ActivityTimeLimitRank";
import { sendMessageToServerWithSuc } from "./pushService";
import { LadderMatchModel } from "../db/LadderMatch";
import { completeLadderRanks } from "./ladderService";
/**
@@ -49,6 +51,11 @@ export class Rank {
private setValueConfig(key: REDIS_KEY) {
switch (key) {
case REDIS_KEY.LADDER:
this.valueConfig = [
new ValueConfig(true, 'rank', 0, false, false)
];
break;
case REDIS_KEY.TOP_LINEUP_RANK:
case REDIS_KEY.TOP_HERO_RANK:
case REDIS_KEY.SUM_CE_RANK:
@@ -653,8 +660,8 @@ export class Rank {
}
}
public async getRankListWithMyRank(myId: myIdInter) {
let ranks = await this.getRankByRange();
public async getRankListWithMyRank(myId: myIdInter, isReverse = false) {
let ranks = await this.getRankByRange('+inf', '-inf', isReverse);
let newRanks = [], newMyRank: any;
let myRank = ranks.find(cur => {
return cur.isMyInfo(myId);
@@ -700,53 +707,67 @@ export class Rank {
return ranks
}
public async getRankByRange(from: number | string = '+inf', to: number | string = '-inf') {
let ranks = new Array<RoleRankInfo | GuildRankInfo>();
// 获取区间里的排行榜,格式 {rank, field, score}[]
public async getRankByRangeRaw(max: number | string = '+inf', min: number | string = '-inf', isReverse = true) {
let ranks: { rank: number, field: string, scores: number[] }[] = [];
let key = this.keyName.getName();
if (this.isUnion) {
key = await this.generateUnionRank();
}
const rankFromDb = await redisClient().zrevrangebyscoreAsync(key, from, to, "WITHSCORES");
const rankFromDb = isReverse?
(await redisClient().zrevrangebyscoreAsync(key, max, min, "WITHSCORES")):
(await redisClient().zrangebyscoreAsync(key, min, max, "WITHSCORES"));
let num = 0;
for (let ii = 0; ii < rankFromDb.length; ii += 2) {
if (num >= this.limit) break;
const field = rankFromDb[ii];
let myId = this.decodeFields(this.key, field);
const scores = this.decodeScore(rankFromDb[ii + 1]);
const info = await redisClient().hgetAsync(this.infoKey, this.composeFields(this.infoKey, myId));
const userInfo = JSON.parse(info);
let param: RoleRankInfo | GuildRankInfo;
if (this.infoKey == REDIS_KEY.USER_INFO) {
if (nowSeconds() - userInfo.updatedAt > 2 * 30 * 24 * 60 * 60) {
continue;
}
param = new RoleRankInfo(userInfo);
param.setInfo(Math.floor(ii / 2) + 1, myId, scores[0], scores[1]);
} else if (this.infoKey == REDIS_KEY.GUILD_INFO) {
if(this.key == REDIS_KEY.GUILD_LV_RANK|| this.key == REDIS_KEY.RACE_ACTIVITY) {
param = new GuildRankInfo(userInfo);
param.setInfo2(Math.floor(ii / 2) + 1, myId, scores[0], scores[1], scores[2]);
} else {
param = new GuildRankInfo(userInfo);
param.setInfo(Math.floor(ii / 2) + 1, myId, scores[0], scores[1]);
}
}
for (let extraKey of this.extraKeys) {
await this.setExInfoToParam(extraKey, param, myId, {});
}
ranks.push(param);
ranks.push({ rank: Math.floor(ii / 2) + 1, field: rankFromDb[ii], scores: this.decodeScore(rankFromDb[ii + 1]) });
num++;
}
return ranks
}
public async getRankByRange(max: number | string = '+inf', min: number | string = '-inf', isReverse = true) {
let raws = await this.getRankByRangeRaw(max, min, isReverse);
let ranks = new Array<RoleRankInfo | GuildRankInfo>();
for (let { rank, field, scores } of raws) {
let param = await this.getParam(rank, field, scores);
if(param) ranks.push(param);
}
return ranks
}
public async getParam(rank: number, field: string, scores: number[]) {
let myId = this.decodeFields(this.key, field);
const info = await redisClient().hgetAsync(this.infoKey, this.composeFields(this.infoKey, myId));
const userInfo = JSON.parse(info);
let param: RoleRankInfo | GuildRankInfo;
if (this.infoKey == REDIS_KEY.USER_INFO) {
if (nowSeconds() - userInfo.updatedAt > 2 * 30 * 24 * 60 * 60) {
return null;
}
param = new RoleRankInfo(userInfo);
param.setInfo(rank, myId, scores[0], scores[1]);
} else if (this.infoKey == REDIS_KEY.GUILD_INFO) {
if(this.key == REDIS_KEY.GUILD_LV_RANK|| this.key == REDIS_KEY.RACE_ACTIVITY) {
param = new GuildRankInfo(userInfo);
param.setInfo2(rank, myId, scores[0], scores[1], scores[2]);
} else {
param = new GuildRankInfo(userInfo);
param.setInfo(rank, myId, scores[0], scores[1]);
}
}
for (let extraKey of this.extraKeys) {
await this.setExInfoToParam(extraKey, param, myId, {});
}
return param
}
public async getRankByRank(start: number, end: number) {
let ranks = new Array<RoleRankInfo | GuildRankInfo>();
@@ -762,35 +783,8 @@ export class Rank {
let num = 0;
for (let ii = 0; ii < rankFromDb.length; ii += 2) {
if (num >= this.limit) break;
const field = rankFromDb[ii];
let myId = this.decodeFields(this.key, field);
const scores = this.decodeScore(rankFromDb[ii + 1]);
const info = await redisClient().hgetAsync(this.infoKey, this.composeFields(this.infoKey, myId));
if(!info) continue;
const userInfo = JSON.parse(info);
let param: RoleRankInfo | GuildRankInfo;
if (this.infoKey == REDIS_KEY.USER_INFO) {
if (nowSeconds() - userInfo.updatedAt > 2 * 30 * 24 * 60 * 60) {
continue;
}
param = new RoleRankInfo(userInfo);
param.setInfo(Math.floor(ii / 2) + 1, myId, scores[0], scores[1]);
} else if (this.infoKey == REDIS_KEY.GUILD_INFO) {
if(this.key == REDIS_KEY.GUILD_LV_RANK) {
param = new GuildRankInfo(userInfo);
param.setInfo2(Math.floor(ii / 2) + 1, myId, scores[0], scores[1], scores[2]);
} else {
param = new GuildRankInfo(userInfo);
param.setInfo(Math.floor(ii / 2) + 1, myId, scores[0], scores[1]);
}
}
for (let extraKey of this.extraKeys) {
await this.setExInfoToParam(extraKey, param, myId, {});
}
ranks.push(param);
let param = await this.getParam(Math.floor(ii / 2) + 1, rankFromDb[ii], this.decodeScore(rankFromDb[ii + 1]));
if(param) ranks.push(param);
num++;
}
return ranks
@@ -1109,6 +1103,16 @@ export async function setRankRedisFromDb(type: string, args?: { serverId?: numbe
await r.setRankWithHeroInfo(hero.roleId, hid, hero.ce, hero.updatedAt.getTime(), hero);
}
}
} else if (type == REDIS_KEY.LADDER) {
let serverId = args.serverId;
let ranks = await LadderMatchModel.findAll(serverId);
let r = new Rank(type, { serverId });
r.setIsInit(true);
for (let rank of ranks) {
let role = <RoleType>rank.role;
await r.setRankWithRoleInfo(rank.roleId, rank.rank, 0, role);
await saveLadderDefCeByData(rank.roleId, rank);
}
}
}
@@ -1171,6 +1175,9 @@ export async function getRankInHandler(redisKey: REDIS_KEY, type: RANK_TYPE, key
let r = new Rank(redisKey, keyParam);
r.setGenerFieldsFun((obj => {
let result = new RoleAndGuildRankInfo(obj.rank, obj.num);
if(type == RANK_TYPE.LADDER) {
result = new RoleAndGuildRankInfo(obj.num, 0);
}
if(obj instanceof GuildRankInfo) {
result.setGuildInfo(obj);
}
@@ -1180,7 +1187,7 @@ export async function getRankInHandler(redisKey: REDIS_KEY, type: RANK_TYPE, key
return result
}));
let { ranks, myRank } = await r.getRankListWithMyRank({ roleId, guildCode });
let { ranks, myRank } = await r.getRankListWithMyRank({ roleId, guildCode }, type != RANK_TYPE.LADDER);
if (!myRank) {
let role = await RoleModel.findByRoleId(roleId, ROLE_SELECT.RANK, true);
if (type == RANK_TYPE.TOP_LINTUP) {
@@ -1212,7 +1219,12 @@ export async function getRankInHandler(redisKey: REDIS_KEY, type: RANK_TYPE, key
let limitRank = await ActivityTimeLimitRankModel.findByCode(serverId, keyParam.activityId, guildCode);
myRank = await r.generMyRankWithGuild(guildCode, limitRank?.score||0, limitRank?.time);
}
} else if (type == RANK_TYPE.LADDER) {
myRank = await r.generMyRankWithRole(roleId, 0, 0, role);
}
}
if(type == RANK_TYPE.LADDER) {
ranks = completeLadderRanks(ranks);
}
return { ranks, myRank }
}

View File

@@ -15,6 +15,7 @@ import { ActivityTimeLimitRankModel } from '../db/ActivityTimeLimitRank';
import { RoleModel, RoleType } from '../db/Role';
import { GuildType } from '../db/Guild';
import { ActivityGroupModel } from '../db/ActivityGroup';
import { LadderMatchModel, LadderMatchType } from '../db/LadderMatch';
/**
* 在服务重新启动时将信息存入redis
@@ -44,6 +45,7 @@ export async function initAllRank() {
// await delKeys(REDIS_KEY.PVP_RANK);
await delKeys(REDIS_KEY.GUILD_FUND);
await delKeys(REDIS_KEY.SUM_CE_SNAPSHOT);
await delKeys(REDIS_KEY.LADDER);
await setRankRedisFromDb(REDIS_KEY.PVP_RANK, {});
@@ -72,6 +74,7 @@ export async function initRank(serverId: number) {
await setRankRedisFromDb(REDIS_KEY.MAIN_RANK, { serverId });
await setRankRedisFromDb(REDIS_KEY.MAIN_ELITE_RANK, { serverId });
await setRankRedisFromDb(REDIS_KEY.HERO_RANK, { serverId });
await setRankRedisFromDb(REDIS_KEY.LADDER, { serverId });
}
export async function initActivitiesRank(serverId: number) {
@@ -519,6 +522,27 @@ export async function hasKey(key: string) {
/**************** 数据库表end */
/**************** 名将擂台防守战力 */
export async function saveLadderDefCeByData(roleId: string, ladderMatch?: LadderMatchType) {
if(!ladderMatch) {
ladderMatch = await LadderMatchModel.findByRoleId(roleId);
if(!ladderMatch) return;
}
let heroes = ladderMatch.defense?.heroes||[];
let defCe = heroes.reduce((pre, cur) => pre + cur.ce, 0);
await saveLadderDefCe(roleId, defCe);
}
export async function saveLadderDefCe(roleId: string, defCe: number) {
await redisClient().hsetAsync(REDIS_KEY.LADDER_DEFCE, `${roleId}`, defCe);
}
export async function getLadderDefCe(roleId: string) {
let defCe = await redisClient().hgetAsync(REDIS_KEY.LADDER_DEFCE, `${roleId}`);
return defCe||0;
}
/**************** 名将擂台防守战力end */
/*************** 将connector服插入redis *******/

View File

@@ -374,6 +374,9 @@ export class CalCe {
seids.push(seid, rand);
}
}
for(let { seid, rand } of jewel.rareSe) {
seids.push(seid, rand);
}
}
let { ratioUp } = this.addSeidEffect(seids);
let map = new Map<number, number>(); // attrId => val

View File

@@ -397,16 +397,20 @@ export async function addJewels(roleId: string, roleName: string, jewels: { id:
export async function getAddJewelInfo(roleId: string, roleName: string, jewel: { id: number, }) {
let { id, } = jewel;
let { name, randomEffect, effectCount } = gameData.jewel.get(id);
let { name, randomEffect, effectCount, rareEffect } = gameData.jewel.get(id);
// 随机属性
let randomResult: number[] = getRandEelm(randomEffect, effectCount);
let rareResult: number[] = getRandEelm(rareEffect, 1);
let randSe: Array<RandSe> = randomResult.map((id: number, index: number) => {
return getJewelRandSe(index + 1, id);
});
let rareSe: Array<RandSe> = rareResult.map((id: number, index: number) => {
return getJewelRandSe(index + 1, id);
});
return { roleId, roleName, id, name, randSe };
return { roleId, roleName, id, name, randSe, rareSe };
}
/**

View File

@@ -7,6 +7,9 @@ const KEY_NAME_MATCH_REGEX = /^[a-zA-Z#][a-zA-Z0-9_]+$/;
let util: any = {};
util.version = '1.2.2';
util.log = function(message: string) {
console.log(message);
}
util.each = function (obj, iterator, context) {
if (obj === null) {
return;

View File

@@ -144,6 +144,13 @@ export async function pushVestigeFirstMsg(roleId: string, roleName: string, serv
return result;
}
export async function pushLadderTopChangeMsg(roleId: string, roleName: string, serverId: number | string) {
const content = JSON.stringify({ roleId, roleName });
const msgData = await createGroupMsg(roleId, roleName, CHANNEL_PREFIX.SYS, `${serverId}`, MSG_TYPE.RICH_TEXT, MSG_SOURCE.LADDER_FIRST_CHANGE, content, null, null);
await pushGroupMsgToRoom(msgData);
return msgData;
}
export async function createMarqueeMsg(roleId: string, roleName: string, serverId: number | string, content: string) {
const msgData = await createGroupMsg(roleId, roleName, CHANNEL_PREFIX.SYS, `${serverId}`, MSG_TYPE.RICH_TEXT, MSG_SOURCE.SEND_MARQUEE, content, null, null);
return msgData;

View File

@@ -531,8 +531,10 @@ export class CheckSingleTask {
// }
case TASK_TYPE.HERO_CONNECT: // 16. 激活x次 x星 武将羁绊
{
let { connectLv } = param;
if(dicTaskParam[1] == connectLv) {
let { shipId, connections, oldConnections } = param;
let oldConnectLv = oldConnections.find(cur => cur.shipId == shipId)?.level||0;
let connectLv = connections.find(cur => cur.shipId == shipId)?.level||0;
if(dicTaskParam[1] <= connectLv && dicTaskParam[1] > oldConnectLv) {
result = { inc: 1 };
}
break;
@@ -1268,6 +1270,52 @@ export class CheckSingleTask {
result = { inc: 1 };
break;
}
case TASK_TYPE.LADDER_CNT: // 121. 名将擂台挑战x次
{
let { count } = param;
result = { inc: count };
break;
}
case TASK_TYPE.LADDER_SUCCESS_CNT: // 122. 名将擂台挑战胜利x次
{
let { isSuccess, count } = param;
if(isSuccess) {
result = { inc: count };
}
break;
}
case TASK_TYPE.LADDER_RANK: // 123. 名将擂台排名
{
let { oldLadderRank, ladderRank } = param;
if(ladderRank < oldLadderRank && dicTaskParam[0] >= ladderRank) {
result = { inc: 1 };
}
break;
}
case TASK_TYPE.CONNECT_ONE_HERO_MAX_LV: // 124. 单个武将其中最高等级的羁绊达X级
{
let { connections } = param;
let records = await getRecord();
let oldLv = records.length > 0? parseInt(records[0]): 0;
let maxLv = Math.max(...connections.map(cur => cur.level))||0;
console.log('##### TASK_TYPE.CONNECT_ONE_HERO_MAX_LV', oldLv, maxLv )
if(oldLv < maxLv) {
result = { set: maxLv, records: [`${maxLv}`] };
}
break;
}
case TASK_TYPE.CONNECT_ONE_HERO_SUM_LV: // 125. 单个武将3条羁绊总共提升X级
{
let { connections } = param;
let records = await getRecord();
let oldLv = records.length > 0? parseInt(records[0]): 0;
let sumLv = connections.reduce((pre, cur) => pre + cur.level, 0);
if(oldLv < sumLv) {
result = { set: sumLv, records: [`${sumLv}`] };
}
break;
}
}
return result
}

View File

@@ -3,7 +3,7 @@ import { pinus, FrontendOrBackendSession } from 'pinus';
import { resResult, shouldRefresh } from '../../pubUtils/util';
import { STATUS, TASK_TYPE, TASK_FUN_TYPE, SHOP_REFRESH_TYPE, WAR_TYPE, PUSH_ROUTE, POP_UP_SHOP_CONDITION_TYPE } from '../../consts';
import { TaskParamInter, TaskListReturn } from '../../domain/roleField/task';
import { EPlace, HeroType } from '../../db/Hero';
import { Connect, EPlace, HeroType } from '../../db/Hero';
import { HeroScores } from '../../db/PvpHistoryOpp';
import { ItemInter, RewardInter } from '../../pubUtils/interface';
import { UserTaskModel, UserTaskType } from '../../db/UserTask';
@@ -314,6 +314,35 @@ export async function checkTaskInDonate(serverId: number, roleId: string, sid: s
await task.saveAndPush(sid);
}
// 名将擂台
export async function checkTaskInLadderStart(serverId: number, roleId: string, sid: string) {
let task = new CheckTask(serverId, roleId);
task.setParam(TASK_TYPE.LADDER_CNT, { count: 1 });
await task.saveAndPush(sid);
}
export async function checkTaskInLadderEnd(serverId: number, roleId: string, sid: string, isSuccess: boolean, oldHistoryRank: number, newHistoryRank: number) {
let task = new CheckTask(serverId, roleId);
task.setParam(TASK_TYPE.LADDER_SUCCESS_CNT, { isSuccess, count: 1 });
task.setParam(TASK_TYPE.LADDER_RANK, { oldLadderRank: oldHistoryRank, ladderRank: newHistoryRank })
await task.saveAndPush(sid);
}
export async function checkTaskInLadderSweep(serverId: number, roleId: string, sid: string, count: number) {
let task = new CheckTask(serverId, roleId);
task.setParam(TASK_TYPE.LADDER_CNT, { count });
task.setParam(TASK_TYPE.LADDER_SUCCESS_CNT, { isSuccess: true, count });
await task.saveAndPush(sid);
}
export async function checkTaskInHeroGiveFavor(serverId: number, roleId: string, sid: string, shipId: number, connections: Connect[], oldConnections: Connect[]) {
let task = new CheckTask(serverId, roleId);
task.setParam(TASK_TYPE.HERO_CONNECT, { shipId, connections, oldConnections });
task.setParam(TASK_TYPE.CONNECT_ONE_HERO_MAX_LV, { connections, oldConnections });
task.setParam(TASK_TYPE.CONNECT_ONE_HERO_SUM_LV, { connections, oldConnections });
await task.saveAndPush(sid);
}
// 获取task状态
export async function getCurTask(roleId: string, session: FrontendOrBackendSession) {
let userTask = await UserTaskModel.findByRole(roleId);

View File

@@ -5,7 +5,7 @@ import { nowSeconds, getTimeFun, getSeconds } from '../pubUtils/timeUtil';
import { getTodayGuildActivity, gameData } from '../pubUtils/data';
import { pvpSeasonEnd } from './pvpService';
import { getAllOnlineRoles, getAllServers, delGuildActivityRank, getServerCreateTime } from './redisService';
import { GUILD_ACTIVITY_TYPE, REFRESH_TIME, COUNTER, AUCTION_TIME, GM_MAIL_TYPE, SERVER_TIMER, ACTIVITY_TYPE, PUSH_ROUTE, STATUS } from '../consts';
import { GUILD_ACTIVITY_TYPE, REFRESH_TIME, COUNTER, AUCTION_TIME, GM_MAIL_TYPE, SERVER_TIMER, ACTIVITY_TYPE, PUSH_ROUTE, STATUS, LADDER_STATUS, LADDER_SERVER_GAP_TIME } from '../consts';
import { pinus } from 'pinus';
import { settleGuildWeekly } from './guildService';
import { SendMailFun, sendMailsByGmMail, } from './mailService';
@@ -17,7 +17,7 @@ import { createNewServer, initMarquee } from './gmService';
import moment = require('moment');
import { CounterModel } from '../db/Counter';
import { reportOneOnline } from './authenticateService';
import { PVP } from '../pubUtils/dicParam';
import { LADDER, PVP } from '../pubUtils/dicParam';
import { fetch37Words } from './sdkService';
import { GMMailModel, GMMailType } from '../db/GMMail';
import { Maintenance, ServerlistModel, ServerlistType } from '../db/Serverlist';
@@ -39,6 +39,8 @@ import { resResult } from '../pubUtils/util';
import { checkPopUpConditionWhenGuildActivityEnd } from './activity/popUpShopService';
import { pushRefreshTime } from './connectorService';
import { sendUnReceivedActivityDailyCoin } from './activity/dailyCoinService';
import { ladderTimeout, ladderTimeWillout, sendLadderDailyReward } from './ladderService';
import { clearLadderLock } from './ladderService';
const PER_SECOND = 1 * 1000;
const PER_DAY = 24 * 60 * 60;
@@ -96,6 +98,9 @@ export async function init() {
// 限时排行榜
await initTimeLimitRank();
// 名将擂台每日奖励
await ladderDailyReward();
}
// 每日刷新
@@ -734,4 +739,38 @@ async function setTakeRankSnapshotSchedule(activityId: number, serverId: number,
await takeSnapshot(data, serverId);
})
}
// —————————————— 活动 end —————————————— //
// —————————————— 活动 end —————————————— //
// —————————————— 名将擂台 start —————————————— //
export async function setLadderCountDown(battleCode: string, time: number, status: LADDER_STATUS) {
if(scheduledJobs[`ladder${battleCode}`]) {
scheduledJobs[`ladder${battleCode}`].cancel();
}
let endTime = time + (status == LADDER_STATUS.CHECK? LADDER.LADDER_BATTLE_PREPARE_COUNTDOWN: LADDER.LADDER_BATTLE_COUNTDOWN);
scheduleJob(`ladder${battleCode}`, endTime * 1000, async () => {
await ladderTimeWillout(battleCode, status);
scheduledJobs[`ladder${battleCode}`].cancel();
scheduleJob(`ladder${battleCode}`, endTime * 1000 + LADDER_SERVER_GAP_TIME * 1000, async () => {
await ladderTimeout(battleCode, status);
scheduledJobs[`ladder${battleCode}`].cancel();
});
})
}
export async function cancelLadderCountDown(battleCode: string) {
if(scheduledJobs[`ladder${battleCode}`]) {
scheduledJobs[`ladder${battleCode}`].cancel();
}
}
async function ladderDailyReward() {
scheduleJob('ladderDailyReward', '0 0 22 * * ?', async () => {
let servers = await ServerlistModel.findByEnv(pinus.app.get('env'));
for(let { serverId } of servers) {
await sendLadderDailyReward(serverId);
}
});
await clearLadderLock();
}
// —————————————— 名将擂台 end —————————————— //

View File

@@ -22,24 +22,26 @@ export class WarReward {
private condition: Map<number, boolean>;
private warInfo: DicWar;
private isSuccess: boolean;
private rewards: Array<{type?: number, id: number, count: number, times?: number}>;
private fixReward: Array<{id: number, count: number}>;
private conditionReward: Array<{id: number, count: number, condition: number}>;
private randomReward: Array<{id: number, count: number, frequency: number}>;
private rewards: Array<{type?: number, id: number, count: number, times?: number}> = [];
private fixReward: Array<{id: number, count: number}> = [];
private conditionReward: Array<{id: number, count: number, condition: number}> = [];
private randomReward: Array<{id: number, count: number, frequency: number}> = [];
private ladderReward: Array<{id: number, count: number, weight: number, isSuccess: boolean}> = [];
private costAp: number;
constructor(roleId: string, roleName: string, sid: string, battleId: number, isSuccess: boolean) {
this.roleId = roleId;
this.roleName = roleName;
this.sid = sid;
this.battleId = battleId;
if(battleId) {
this.battleId = battleId;
this.warInfo = gameData.war.get(battleId);
let { fixReward, conditionReward, randomReward } = this.warInfo;
this.fixReward = fixReward;
this.conditionReward = conditionReward;
this.randomReward = randomReward;
}
this.condition = new Map();
this.warInfo = gameData.war.get(battleId);
let { fixReward, conditionReward, randomReward } = this.warInfo;
this.fixReward = fixReward;
this.conditionReward = conditionReward;
this.randomReward = randomReward;
this.isSuccess = isSuccess;
this.costAp = this.warInfo.cost;
@@ -71,16 +73,24 @@ export class WarReward {
}
}
private handleFixReward(num: number, fixReward: RewardInter[]) {
public setLadderReward(reward: {id: number, count: number, weight: number}[], isSuccess: boolean) {
for(let r of reward) {
this.ladderReward.push({...r, isSuccess});
}
}
private handleFixReward(num: number) {
if(!this.fixReward) return
for(let i = 0; i < num; i++) {
for(let obj of fixReward) {
for(let obj of this.fixReward) {
this.rewards.push({type: BATTLE_REWARD_TYPE.FIX_REWARD, times: i +1, ...obj, count: obj.count });
}
}
}
private handleConditionReward(num: number, conditionReward: {id: number, count: number, condition: number}[]) {
private handleConditionReward(num: number) {
if(!this.conditionReward) return
for(let i = 0; i < num; i++) {
for(let obj of conditionReward) {
for(let obj of this.conditionReward) {
if(this.condition.get(obj.condition)) {
this.rewards.push({type: BATTLE_REWARD_TYPE.CONDITION_REWARD, times: i +1, ...obj, count: obj.count});
}
@@ -88,9 +98,9 @@ export class WarReward {
}
}
private async handleRandomReward(num: number, randomReward: {id: number, count: number, frequency: number}[]) {
for(let obj of randomReward) {
private async handleRandomReward(num: number) {
if(!this.randomReward) return
for(let obj of this.randomReward) {
let { id, frequency } = obj;
let dropHistory = await BattleDropModel.findByGid(this.roleId, this.battleId, id);
let { getNum = 0, allNum = 0, getSum = 0, allSum = 0 } = dropHistory;
@@ -117,18 +127,30 @@ export class WarReward {
}
}
private async handleLadderReward(num: number, isSuccess: boolean) {
if(!this.ladderReward) return;
for(let i = 0; i < num; i++) {
let arr = this.ladderReward.filter(cur => cur.isSuccess == isSuccess);
let { dic: reward } = getRandEelmWithWeight(arr);
if(reward) {
this.rewards.push({type: BATTLE_REWARD_TYPE.RANDOM_REWARD, times: i +1, id: reward.id, count: reward.count });
}
}
}
public async saveReward(num: number, combine: boolean = false) {
this.rewards = new Array();
// let warType = this.warInfo.warType;
if(this.isSuccess) { // 成功了才给固定奖励
console.log(this.fixReward)
if(this.fixReward) this.handleFixReward(num, this.fixReward);
if(this.conditionReward) this.handleConditionReward(num, this.conditionReward);
if(this.randomReward) await this.handleRandomReward(num, this.randomReward);
if(this.fixReward) this.handleFixReward(num);
if(this.conditionReward) this.handleConditionReward(num);
if(this.randomReward) await this.handleRandomReward(num);
// if(this.costAp > 0) await this.handlerBlueprtReward(num);
}
if(this.ladderReward) this.handleLadderReward(num, this.isSuccess);
let rewards = this.rewards;
if(combine) {

View File

@@ -32,6 +32,7 @@ export const WAR_TYPE = {
ACT_NEW_HERO_GK: 18, // 新将演绎活动
TRY: 19, // 试用关卡
BOSS: 20, // 演武台
LADDER: 21, // 名将擂台
};
// 藏宝图掉落参数
@@ -156,4 +157,20 @@ export enum TOWER_FORBIDDEN_CHARA_TYPE {
CHAR = 1, // 角色
JOB = 2, // 职业
CAMP = 3, // 国家
}
export enum LADDER_STATUS {
NO = 0, // 无战场
CHECK = 1, // 出兵中
BATTLE = 2, // 战斗中
COMPLETE = 3, // 战斗结束
}
export const LADDER_SERVER_GAP_TIME = 5; // 服务器比客户端晚5秒
export enum LADDER_OPP_STATUS {
BATTLE = 1, // 可以挑战
OPP_IS_LOCKED = 2, // 对手被挑战
OPP_RANK_CHANGE = 3, // 对手排名变化
MY_RANK_CHANGE = 4, // 自己排名变化
}

View File

@@ -80,6 +80,7 @@ export const MSG_SOURCE = {
GUILD_RECURIT: 24,
EQUIP_STAR_UP: 25,
EQUIP_QUALITY_UP: 26,
LADDER_FIRST_CHANGE: 27,
}
export const DEFAULT_MSG_PER_PAGE = 10;
@@ -160,4 +161,7 @@ export const PUSH_ROUTE = {
TASK_BOX_REFRESH: 'onTaskBoxRefresh',
UPDATE_SURVEY: 'onUpdateSurvey',
DELETE_SURVEY: 'onDeleteSurvey',
LADDER_CHECK_STOP: 'onLadderCheckStop',
LADDER_BATTLE_STOP: 'onLadderBattleStop',
LADDER_RANK_UPDATE: 'onLadderRankUpdate',
}

View File

@@ -56,6 +56,7 @@ export enum MAIL_TYPE {
PVP_BOX = 27, // pvp巅峰之路宝箱
DAILY_COIN = 28, // 招财进宝宝箱
GUILD_DISSMISS = 29, // 军团解散
LADDER = 30, // 名将擂台
};
export const SEND_NAME = '系统';

View File

@@ -244,6 +244,8 @@ export enum REDIS_KEY {
SURVEY_CHANNEL = 'survey', // 文件频道
USER_CHANNEL = 'userChannel', // 玩家登录频道
USER_CODE = 'userCode', // 玩家userCode对应roleId
LADDER = 'ladder', // 名将擂台
LADDER_DEFCE = 'ladderDefCe', // 名将擂台的防守战力
}
// 各排行榜对应hash的key
@@ -308,6 +310,7 @@ export enum RANK_TYPE {
MAIN = 7, // 主线
MAIN_ELITE = 8, // 精英
GUILD_LV = 9, // 军团按等级排序
LADDER = 10, // 名将擂台
GUILD_ACTIVE = 11, // 军团活跃度排序
HERO = 12, // 武将排行
GUILD_FUND = 13, // 军团积分排行,限时类型的排行榜,仅计算期限内增加的值
@@ -340,6 +343,8 @@ export function getRedisKeyByRankType(rankType: RANK_TYPE, isTimelimit = false)
return REDIS_KEY.HERO_RANK;
case RANK_TYPE.GUILD_FUND: // 军团积分排行,限时类型的排行榜,仅计算期限内增加的值
return REDIS_KEY.GUILD_FUND;
case RANK_TYPE.LADDER: // 军团积分排行,限时类型的排行榜,仅计算期限内增加的值
return REDIS_KEY.LADDER;
}
}
@@ -381,6 +386,7 @@ export const KEY_TO_COMPOSE_FIELD = new Map([
[REDIS_KEY.PVP_RANK, COMPOSE_FIELD_TYPE.ROLE],
[REDIS_KEY.GUILD_FUND, COMPOSE_FIELD_TYPE.GUILD],
[REDIS_KEY.SUM_CE_SNAPSHOT, COMPOSE_FIELD_TYPE.ROLE],
[REDIS_KEY.LADDER, COMPOSE_FIELD_TYPE.ROLE],
]);
@@ -536,6 +542,10 @@ export const FILENAME = {
DIC_TOWER_PVP_SUB_ATTR: 'dic_tower_pvp_subattr',
DIC_SYSTEM_OPEN_TIME: 'dic_zyz_systemOpenTime',
DIC_MAIN_WAR_REWARD: 'dic_zyz_mainCity_reward',
DIC_LADDER_DIFFICULTRATIO: 'dic_zyz_ladder_difficultRatio',
DIC_LADDER_RANKREWARD: 'dic_zyz_ladder_rankReward',
DIC_GK_LADDER: 'dic_zyz_gk_ladder',
DIC_LADDER_MATCH: 'dic_zyz_ladderMatch',
}
export const WAR_RELATE_TABLES = [
@@ -556,7 +566,8 @@ export const WAR_RELATE_TABLES = [
FILENAME.DIC_GK_NEWHERO,
FILENAME.DIC_GK_PROBATION,
FILENAME.DIC_GK_ARMY_TRAIN,
FILENAME.DIC_GK_ARMY_BOSS
FILENAME.DIC_GK_ARMY_BOSS,
FILENAME.DIC_GK_LADDER,
]
// 装备栏强化类型
@@ -749,6 +760,11 @@ export enum TASK_TYPE {
BATTLE_EXPEDITION_START = 118, // 挑战远征x次
BATTLE_DUNGEON_START = 119, // 挑战秘境x次
GUILD_GOLD_DONATE = 120, // 军团元宝捐献x次
LADDER_CNT = 121, // 名将擂台挑战x次
LADDER_SUCCESS_CNT = 122, // 名将擂台挑战胜利x次
LADDER_RANK = 123, // 名将擂台排名
CONNECT_ONE_HERO_MAX_LV = 124, // 羁绊最高等级的那一级达到x级
CONNECT_ONE_HERO_SUM_LV = 125, // 单个武将全部羁绊到达
}
// 任务累积类型
@@ -1012,6 +1028,8 @@ export enum ITEM_CHANGE_REASON {
RECEIVE_TOWER_BOX = 148, // 领取镇念塔节点奖励
ACT_DAILY_COIN_RECEIVE_BOX = 149, // 招财进宝领取宝箱
USE_VOUCHER = 150, // 使用代金券
LADDER_BUY_CNT = 151, // 名将擂台购买次数
LADDER_BATTLE_REWARD = 152, // 名将擂台关卡奖励
}
export enum TA_EVENT {

View File

@@ -258,6 +258,18 @@ export const STATUS = {
CAN_NOT_DECLARE: { code: 21109, simStr: '该城池不可宣战' },
CHALLENGE_TIME_NOT_NEED_RESET: { code: 21110, simStr: '不需要重置' },
// 名将擂台
LADDER_NOT_OPEN: { code: 21200, simStr: '请先布置防守阵容' },
LADDER_BUY_MAX: { code: 21201, simStr: '名将擂台购买次数达到上限' },
LADDER_RANK_ERROR: { code: 21202, simStr: '该排名玩家不可挑战' },
LADDER_REFRESH_CNT_MAX: { code: 21203, simStr: '刷新次数达到上限' },
LADDER_REC_NOT_FOUND: { code: 21204, simStr: '未找到该条记录' },
LADDER_REC_STATUS_IS_COMPLETE: { code: 21205, simStr: '该战斗已结束' },
LADDER_CHALLENGE_CNT_MAX: { code: 21206, simStr: '挑战次数已达上限' },
LADDER_REC_STATUS_ERR: { code: 21207, simStr: '战斗状态错误' },
LADDER_RANK_ERR: { code: 21208, simStr: '不可连战比排名自己高的玩家' },
LADDER_SWEEP_TIMES_OVER: { code: 21209, simStr: '连战次数过多' },
// 通用 30000 - 30099
DIC_DATA_NOT_FOUND: { code: 30000, simStr: '数据表未找到' },
ROLE_MATERIAL_NOT_ENOUGH: { code: 30001, simStr: '材料数量不足' },

View File

@@ -8,13 +8,22 @@ import { CounterModel } from './Counter';
import { COUNTER } from '../consts';
import { HeroModel } from './Hero';
export class RandSe {
export class JewelSe {
@prop({ required: true })
id: number; // 随机属性位置id
@prop({ required: true })
seid: number; // 随机属性池id
@prop({ required: true })
rand: number; // 随机属性内需要随机的值
constructor(id: number, seid: number, rand: number) {
this.id = id;
this.seid = seid;
this.rand = rand;
}
}
export class RandSe extends JewelSe {
@prop({ required: true })
preRand?: number; // 淬炼失败的是随机出的值
@prop({ required: true })
@@ -24,13 +33,6 @@ export class RandSe {
quenched: boolean = false; // 是否淬炼了
@prop({ required: false, default: 0 })
quenchCnt: number = 0; // 淬炼次数
constructor(id: number, seid: number, rand: number) {
this.id = id;
this.seid = seid;
this.rand = rand;
}
}
@index({ roleId: 1, hid: 1, id: 1 })
@@ -54,6 +56,8 @@ export default class Jewel extends BaseModel {
@prop({ required: false, default: 0 })
ePlaceId: number; // 武将装备的部位
@prop({ required: false, type: JewelSe, default: [], _id: false })
rareSe: JewelSe[]; // 强化随机属性
@prop({ required: false, type: RandSe, default: [], _id: false })
randSe: RandSe[]; // 强化随机属性
@prop({ required: false, type: RandSe, default: [], _id: false })

148
shared/db/LadderMatch.ts Normal file
View File

@@ -0,0 +1,148 @@
import BaseModel from './BaseModel';
import { index, getModelForClass, prop, DocumentType, mongoose, Ref } from '@typegoose/typegoose';
import { LadderDefense, LadderOppPlayerInDB, LadderOppPlayerInfo } from '../domain/battleField/ladder';
import Role from './Role';
@index({ roleId: 1 })
export default class LadderMatch extends BaseModel {
@prop({ required: true, default: 0 })
serverId: number; // 角色 id
@prop({ required: true, default: '' })
roleId: string; // 角色 id
@prop({ ref: 'Role', type: mongoose.Schema.Types.ObjectId })
role: Ref<Role>;
@prop({ required: true, default: 0 })
rank: number; // 排名
@prop({ required: true, default: 0 })
oldRank: number; // 交换前排名
@prop({ required: true, default: 0 })
historyRank: number; // 历史最高排名
@prop({ required: true, default: 0 })
locked: number; // 是否被挑战
@prop({ required: true, default: null, _id: false })
defense: LadderDefense;
@prop({ required: true, type: LadderOppPlayerInDB, default: [], _id: false })
oppPlayers: LadderOppPlayerInDB[];
@prop({ required: true, default: () => { return new Date() } })
refDaily: Date; // 每日刷新控制refOppCnt和setAttackCnt
@prop({ required: true, default: 0 })
challengeCnt: number; // 挑战次数
@prop({ required: true, default: 0 })
buyCnt: number; // 购买次数
@prop({ required: true, default: 0 })
refOppCnt: number; // 刷新对手次数
public static async findByRoleId(roleId: string) {
const result: LadderMatchType = await LadderMatchModel.findOne({ roleId }).lean();
return result;
}
public static async findByRoleIdAndInclude(roleId: string) {
const result: LadderMatchType = await LadderMatchModel.findOne({ roleId })
.populate('role', 'roleId roleName head frame spine heads frames spines title lv updatedAt')
.populate('defense.heroes.hero', 'hid skinId quality star colorStar lv')
.lean();
return result;
}
public static async createLadder(params: LadderUpdateInter) {
const doc = new LadderMatchModel();
const update = Object.assign(doc.toJSON(), params);
const defense: LadderMatchType = await LadderMatchModel.findOneAndUpdate({ roleId: params.roleId }, { $set: update}, { upsert: true, new: true }).lean();
return defense;
}
public static async updateByRoleId(roleId: string, params: LadderUpdateInter) {
const defense: LadderMatchType = await LadderMatchModel.findOneAndUpdate({ roleId }, { $set: params}, { new: true }).lean();
return defense;
}
public static async updateByRoleIdAndInclude(roleId: string, params: LadderUpdateInter) {
const defense: LadderMatchType = await LadderMatchModel.findOneAndUpdate({ roleId }, { $set: params}, { new: true })
.populate('role', 'roleId roleName head frame spine heads frames spines title lv updatedAt')
.populate('defense.heroes.hero', 'hid skinId quality star colorStar lv')
.lean();
return defense;
}
public static async findAll(serverId: number) {
let result: LadderMatchType[] = [], latestTime = new Date();
while(latestTime) {
let ladderMatch = await LadderMatchModel.find({ serverId, rank: { $gt: 0 }, createdAt: { $lt: latestTime } })
.sort({ createdAt: -1 })
.populate('role', 'roleId roleName head frame spine heads frames spines title lv updatedAt')
.limit(1000);
result.push(...ladderMatch);
latestTime = ladderMatch[ladderMatch.length - 1]?.createdAt;
}
return result
}
public static async lock(serverId: number, roleId: string, rank: number) {
const defense: LadderMatchType = await LadderMatchModel.findOneAndUpdate({ serverId, roleId, rank, locked: 0 }, { $set: { locked: 1 }}, { new: true })
.populate('role', 'roleId roleName head frame spine heads frames spines title lv updatedAt')
.populate('defense.heroes.hero', 'hid skinId quality star colorStar lv')
.lean();
return defense;
}
public static async unlock(serverId: number, roleId: string) {
const defense: LadderMatchType = await LadderMatchModel.findOneAndUpdate({ serverId, roleId }, { $set: { locked: 0 }}, { new: true }).lean();
return defense;
}
public static async changeRank(isSuccess: boolean, attackInfo: LadderOppPlayerInfo, defenseInfo: LadderOppPlayerInfo, times = 0) {
if(times > 10) return { isChange: false }
const session = await mongoose.connection.startSession();
session.startTransaction()
try {
let newAtkLadderMatch: LadderMatchType, newDefLadderMatch: LadderMatchType, isChange = false;
if(defenseInfo.isRobot) {
let atkLadderMatch = await LadderMatchModel.findOne({ roleId: attackInfo.roleId }).lean();
if(isSuccess && atkLadderMatch && (atkLadderMatch.rank == 0 || atkLadderMatch.rank > defenseInfo.oldRank)) {
newAtkLadderMatch = await LadderMatchModel.findOneAndUpdate({ roleId: attackInfo.roleId }, { $set: { rank: defenseInfo.oldRank, oldRank: atkLadderMatch.rank } }, { new: true }).lean();
isChange = true;
} else {
newAtkLadderMatch = atkLadderMatch;
}
} else {
let atkLadderMatch = await LadderMatchModel.findOne({ roleId: attackInfo.roleId }).lean();
let defLadderMatch = await LadderMatchModel.findOne({ roleId: defenseInfo.roleId }).lean();
if(isSuccess && atkLadderMatch && defLadderMatch && (atkLadderMatch.rank == 0 || atkLadderMatch.rank > defLadderMatch.rank)) {
newAtkLadderMatch = await LadderMatchModel.findOneAndUpdate({ roleId: attackInfo.roleId }, { $set: { rank: defLadderMatch.rank, oldRank: atkLadderMatch.rank } }, { new: true }).lean();
newDefLadderMatch = await LadderMatchModel.findOneAndUpdate({ roleId: defenseInfo.roleId }, { $set: { rank: atkLadderMatch.rank, oldRank: defLadderMatch.rank } }, { new: true }).lean();
isChange = true;
} else {
newAtkLadderMatch = atkLadderMatch;
newDefLadderMatch = defLadderMatch;
}
}
await session.commitTransaction()
return { isChange, atkLadderMatch: newAtkLadderMatch, defLadderMatch: newDefLadderMatch }
} catch(e) {
return await this.changeRank(isSuccess, attackInfo, defenseInfo, times + 1);
} finally {
session.endSession()
}
}
public static async updateCe(roleId: string, hid: number, ce: number) {
const rec: LadderMatchType = await LadderMatchModel.findOneAndUpdate(
{ roleId, defense: { $exists: false } },
{ $set: { 'defense.heroes.$[t].ce': ce } },
{ arrayFilters: [{"t.actorId": hid}] }).lean();
return rec
}
}
export const LadderMatchModel = getModelForClass(LadderMatch);
export interface LadderMatchType extends Pick<DocumentType<LadderMatch>, keyof LadderMatch> { };
export type LadderUpdateInter = Partial<LadderMatchType>;

123
shared/db/LadderMatchRec.ts Normal file
View File

@@ -0,0 +1,123 @@
import BaseModel from './BaseModel';
import { index, getModelForClass, prop, DocumentType } from '@typegoose/typegoose';
import { LADDER_STATUS } from '../consts';
import { LadderDefense, LadderOppPlayerInfo, LadderOppPlayerHeroInfo } from '../domain/battleField/ladder';
import { genCode } from '../pubUtils/util';
import { nowSeconds } from '../pubUtils/timeUtil';
import { LADDER } from '../pubUtils/dicParam';
@index({ battleCode: 1 })
export default class LadderMatchRec extends BaseModel {
@prop({ required: true })
battleCode: string; // 唯一code
@prop({ required: true })
roleId1: string; // 攻方玩家id
@prop({ required: true })
roleId2: string; // 守方玩家id
@prop({ required: true })
serverId: number; // 区 id
@prop({ required: true })
status: LADDER_STATUS; // 1-出兵中 2-挑战中 3-挑战完毕
@prop({ required: true })
timeout: boolean; // 是否超时
@prop({ required: true })
checkTime: number; // 开始出兵的时间10位时间戳
@prop({ required: true })
battleTime: number; // 开始挑战的时间10位时间
@prop({ required: true })
endTime: number; // 结束时间
@prop({ required: true, type: () => LadderOppPlayerInfo, default: {}, _id: false })
attackInfo: LadderOppPlayerInfo; // 攻方信息
@prop({ required: true, type: () => LadderOppPlayerInfo, default: {}, _id: false })
defenseInfo: LadderOppPlayerInfo; // 守方信息
@prop({ required: true, type: () => LadderDefense, default: {}, _id: false })
defense: LadderDefense; // 守方信息
public static async findByRoleId(roleId: string, getters = false) {
const result: LadderMatchRecType = await LadderMatchRecModel.findOne({ roleId1: roleId }).lean({ getters});
return result;
}
public static async findNotCompleteRecByRoleId(roleId: string, getters = false) {
const result: LadderMatchRecType = await LadderMatchRecModel.findOne({ roleId1: roleId, status: { $in: [ LADDER_STATUS.BATTLE, LADDER_STATUS.CHECK ] } }).lean({ getters});
return result;
}
public static async createRec(serverId: number, roleId1: string, roleId2: string, defense: LadderDefense, attackInfo: LadderOppPlayerInfo, defenseInfo: LadderOppPlayerInfo) {
const battleCode = genCode(10);
const result: LadderMatchRecType = await LadderMatchRecModel.findOneAndUpdate({ battleCode }, {
$set: { roleId1, roleId2, serverId, defense, status: LADDER_STATUS.CHECK, checkTime: nowSeconds(), attackInfo, defenseInfo }
}, { new: true, upsert: true }).lean();
return result;
}
public static async timeout(battleCode: string) {
const result: LadderMatchRecType = await LadderMatchRecModel.findOneAndUpdate({ battleCode }, { $set: { status: LADDER_STATUS.COMPLETE, timeout: true, endTime: nowSeconds() } }, { new: true }).lean();
return result;
}
public static async giveup(battleCode: string) {
const result: LadderMatchRecType = await LadderMatchRecModel.findOneAndUpdate({ battleCode }, { $set: { status: LADDER_STATUS.NO } }, { new: true }).lean();
return result;
}
public static async findByBattleCode(battleCode: string, select: string = '') {
const result: LadderMatchRecType = await LadderMatchRecModel.findOne({ battleCode }).select(select).lean();
return result;
}
public static async startBattle(battleCode: string, attackHeroes: LadderOppPlayerHeroInfo[]) {
const result: LadderMatchRecType = await LadderMatchRecModel.findOneAndUpdate({ battleCode }, {
$set: { status: LADDER_STATUS.BATTLE, battleTime: nowSeconds(), 'attackInfo.heroes': attackHeroes }
}, { new: true }).lean();
return result;
}
public static async battleEnd(battleCode: string, isSuccess: boolean, atkNewRank?: number, defNewRank?: number) {
let update: LadderMatchRecUpdate = { status: LADDER_STATUS.COMPLETE, endTime: nowSeconds(), timeout: false };
if(isSuccess) {
update['attackInfo.isSuccess'] = true;
update['defenseInfo.isSuccess'] = false;
}
if(atkNewRank != undefined && defNewRank != undefined) {
update['attackInfo.newRank'] = atkNewRank;
update['attackInfo.oldRank'] = defNewRank;
update['defenseInfo.newRank'] = defNewRank;
update['defenseInfo.oldRank'] = atkNewRank;
}
const result: LadderMatchRecType = await LadderMatchRecModel.findOneAndUpdate({ battleCode }, { $set: update }, { new: true }).lean();
return result;
}
public static async findRec(roleId: string) {
let recs: LadderMatchRecType[] = await LadderMatchRecModel.find({
$or: [{roleId1: roleId}, { roleId2: roleId }],
status: LADDER_STATUS.COMPLETE
}).select({ battleCode: 1, roleId1: 1, roleId2: 1, _id: -1, endTime: 1, attackInfo: 1, defenseInfo: 1 }).limit(1000).lean();
return recs;
}
public static async checkByRank(serverId: number, roleId2: string) {
const result = await LadderMatchRecModel.exists({ serverId, roleId2, status: { $in: [LADDER_STATUS.BATTLE, LADDER_STATUS.CHECK] } });
return result;
}
public static async clearLock() {
let recs = await LadderMatchRecModel.find({
$or: [
{ status: LADDER_STATUS.CHECK, checkTime: { $lt: nowSeconds() - LADDER.LADDER_BATTLE_PREPARE_COUNTDOWN } },
{ status: LADDER_STATUS.BATTLE, checkTime: { $lt: nowSeconds() - LADDER.LADDER_BATTLE_COUNTDOWN } }
]
});
await LadderMatchRecModel.updateMany({
battleCode: { $in: recs.map(cur => cur.battleCode) }
}, { $set: { status: LADDER_STATUS.COMPLETE, timeout: true } });
return recs;
}
}
export const LadderMatchRecModel = getModelForClass(LadderMatchRec);
export interface LadderMatchRecType extends Pick<DocumentType<LadderMatchRec>, keyof LadderMatchRec> { };
export type LadderMatchRecUpdate = Partial<LadderMatchRecType>;

View File

@@ -0,0 +1,416 @@
import { mongoose, prop, Ref } from "@typegoose/typegoose";
import { LADDER_STATUS } from "../../consts";
import Hero, { HeroType } from '../../db/Hero';
import { LadderMatchType } from "../../db/LadderMatch";
import { LadderMatchRecType } from "../../db/LadderMatchRec";
import { RoleType } from "../../db/Role";
import { gameData } from "../../pubUtils/data";
import { EXTERIOR, LADDER } from "../../pubUtils/dicParam";
import { DicLadderDifficultRatio } from "../../pubUtils/dictionary/DicLadderDifficultRatio";
import { DicWarJson } from "../../pubUtils/dictionary/DicWarJson";
import { RoleRankInfo } from "../rank";
// ladderMatch表
export class LadderDefenseHero {
@prop({ required: true })
actorId: number; // 武将id
@prop({ required: true })
ai: number; // ai逻辑1-进攻型 2-防守型
@prop({ required: true })
dataId: number;
@prop({ required: true })
order: number;
@prop({ ref: 'Hero', type: mongoose.Schema.Types.ObjectId })
hero: Ref<Hero>;
@prop({ required: true })
ce: number;
constructor(param: { actorId: number, ai: number, dataId: number, order: number }, heroId: string, ce: number) {
this.actorId = param.actorId;
this.ai = param.ai;
this.dataId = param.dataId;
this.order = param.order;
this.hero = heroId;
this.ce = ce;
}
}
// ladderMatch表
export class LadderDefense {
@prop({ required: true })
warId: number;
@prop({ required: true, type: LadderDefenseHero, _id: false })
heroes: LadderDefenseHero[];
constructor(heroes: LadderDefenseHero[], warId: number) {
this.heroes = heroes;
this.warId = warId;
}
}
// LadderMatch表
export class LadderOppPlayerInDB {
@prop({ required: true })
rank: number;
@prop({ required: true })
roleId: string;
@prop({ required: true })
isRobot: boolean;
}
// ladderMatchRec
export class LadderOppPlayerHeroInfo {
@prop({ required: true })
hid: number; // 武将id
@prop({ required: true })
skinId: number; // 皮肤id
@prop({ required: true })
quality: number; // 品质
@prop({ required: true })
star: number; // 星级
@prop({ required: true })
colorStar: number; // 彩星
@prop({ required: true })
lv: number; // 等级
setByWarJson(warJson: DicWarJson) {
this.hid = warJson.actorId;
this.skinId = warJson.actorId;
this.star = warJson.star;
this.colorStar = 0;
this.lv = warJson.lv;
let dicHero = gameData.hero.get(warJson.actorId);
if(dicHero) this.quality = dicHero.quality;
}
setByDefenseHero(hero: HeroType) {
this.hid = hero.hid;
this.skinId = hero.skinId;
this.quality = hero.quality;
this.star = hero.star;
this.colorStar = hero.colorStar;
this.lv = hero.lv;
}
}
// ladderMatchRec
export class LadderOppPlayerInfo {
@prop({ required: true })
oldRank: number = 0; // 原排名
@prop({ required: true })
newRank: number = 0; // 交换后的排名
@prop({ required: true })
roleId: string = ''; // 角色 id
@prop({ required: true })
roleName: string = LADDER.LADDER_ROBOT_NAME; // 角色 名
@prop({ required: true, default: 0 })
lv: number = 1; // 等级
@prop({ required: true, default: EXTERIOR.EXTERIOR_FACE })
head: number = EXTERIOR.EXTERIOR_FACE; // 头像
@prop({ required: true, default: EXTERIOR.EXTERIOR_FACECASE })
frame: number = EXTERIOR.EXTERIOR_FACECASE; // 相框
@prop({ required: true, default: EXTERIOR.EXTERIOR_APPEARANCE })
spine: number = EXTERIOR.EXTERIOR_APPEARANCE; // 形象
@prop({ required: true, default: 0 })
title: number = 1; // 爵位
@prop({ required: true, default: 0 })
ce: number = 0; // 防守战力
@prop({ required: true, default: false })
isSuccess: boolean = false; // 是否胜利
@prop({ required: true, default: [], type: LadderOppPlayerHeroInfo, _id: false })
heroes: LadderOppPlayerHeroInfo[] = []; // 武将
@prop({ required: true })
isRobot: boolean = false; // 原排名
initByPlayer(rank: number, role: RoleType, heroes: LadderOppPlayerHeroInfo[], defCe: number, isSuccess: boolean ) {
this.oldRank = rank;
this.newRank = rank;
this.roleId = role.roleId;
this.roleName = role.roleName;
this.head = role.head;
this.frame = role.frame;
this.spine = role.spine;
this.title = role.title;
this.ce = defCe;
this.lv = role.lv;
this.isSuccess = isSuccess;
this.heroes = heroes;
this.isRobot = false;
}
initByRobot(dicLadderDifficultRatio: DicLadderDifficultRatio, heroes: LadderOppPlayerHeroInfo[], isSuccess: boolean) {
this.oldRank = dicLadderDifficultRatio.rank;
this.newRank = dicLadderDifficultRatio.rank;
this.roleId = `robot${this.oldRank}`;
this.head = dicLadderDifficultRatio.head;
this.spine = dicLadderDifficultRatio.spine;
this.ce = dicLadderDifficultRatio.ce;
this.lv = dicLadderDifficultRatio.level;
this.isSuccess = isSuccess;
this.heroes = heroes;
this.isRobot = true;
}
}
// defense字段 接口返回
export class LadderDefenseHeroReturn {
actorId: number; // 武将id
ai: number; // ai逻辑1-进攻型 2-防守型
dataId: number;
order: number;
constructor(defenseHero: LadderDefenseHero) {
this.actorId = defenseHero.actorId;
this.ai = defenseHero.ai;
this.dataId = defenseHero.dataId;
this.order = defenseHero.order;
}
}
// defense字段 接口返回
export class LadderDefenseReturn {
warId: number;
defCe: number = 0;
heroes: LadderDefenseHeroReturn[] = [];
constructor(defense: LadderDefense) {
if(!defense) return;
this.warId = defense.warId;
for(let defenseHero of defense.heroes) {
this.defCe += defenseHero.ce;
this.heroes.push(new LadderDefenseHeroReturn(defenseHero));
}
}
}
// oppPlayers字段接口返回
export class LadderOppPlayerReturn {
rank: number = 0; // 对手排名
roleId: string = ''; // 对手玩家id
roleName: string = LADDER.LADDER_ROBOT_NAME; // 对手玩家名
head: number = EXTERIOR.EXTERIOR_FACE; // 头像
frame: number = EXTERIOR.EXTERIOR_FACECASE; // 相框
spine: number = EXTERIOR.EXTERIOR_APPEARANCE; // 形象
lv: number = 1; // 对手玩家等级
title: number = 1; // 对手爵位
defCe: number = 0; // 对手防守阵容战力
isRobot: boolean = true; // 是否是机器人
constructor(rank: number, isRobot: boolean) {
this.rank = rank;
this.isRobot = isRobot;
if(isRobot) this.roleId = `robot${this.rank}`;
}
setByRedis(param: RoleRankInfo, defCe: number) {
this.roleId = param.roleId;
this.roleName = param.roleName;
this.head = param.head;
this.frame = param.frame;
this.spine = param.spine;
this.lv = param.lv;
this.title = param.title;
this.defCe = defCe;
}
setByRobot(ladderDifficultRatio: DicLadderDifficultRatio) {
this.head = ladderDifficultRatio.head;
this.spine = ladderDifficultRatio.spine;
this.lv = ladderDifficultRatio.level;
this.defCe = ladderDifficultRatio.ce;
}
}
// getData总体返回
export class LadderDataReturn {
rank: number = 0;
historyRank: number = 0;
challengeCnt: number = 0;
buyCnt: number = 0;
refOppCnt: number = 0;
defense: LadderDefenseReturn = null;
status: LADDER_STATUS = LADDER_STATUS.NO;
time: number = 0;
battleCode: string = '';
oppPlayers: LadderOppPlayerReturn[] = []
setLadderData(ladderData: LadderMatchType, ladderRec?: LadderMatchRecType) {
this.rank = ladderData.rank;
this.historyRank = ladderData.historyRank;
this.challengeCnt = ladderData.challengeCnt;
this.buyCnt = ladderData.buyCnt;
this.refOppCnt = ladderData.refOppCnt;
if(ladderData.defense) {
this.defense = new LadderDefenseReturn(ladderData.defense);
}
if(ladderRec) {
this.battleCode = ladderRec.battleCode;
this.status = ladderRec.status;
if(this.status == LADDER_STATUS.BATTLE) {
this.time = ladderRec.battleTime + LADDER.LADDER_BATTLE_COUNTDOWN;
} else if (this.status == LADDER_STATUS.CHECK) {
this.time = ladderRec.checkTime + LADDER.LADDER_BATTLE_PREPARE_COUNTDOWN;
}
}
}
setOppPlayers(oppPlayers: LadderOppPlayerReturn[]) {
this.oppPlayers = oppPlayers;
}
}
// getOppLineup返回
export class LadderOppLineupReturn {
myRank: number = 0;
isRobot: boolean = true;
rank: number = 0;
roleId: string = '';
roleName: string = LADDER.LADDER_ROBOT_NAME;
title: number = 1;
guildName: string = '';
defCe: number = 0;
heroes: LadderOppPlayerHeroInfo[] = []
setMyRank(myRank: number) {
this.myRank = myRank;
}
setRobot(ladderDifficultRatio: DicLadderDifficultRatio, warJsons: DicWarJson[]) {
this.isRobot = true;
this.rank = ladderDifficultRatio.rank;
this.roleId = `robot${this.rank}`;
this.defCe = ladderDifficultRatio.ce;
for(let warJson of warJsons) {
if(warJson.relation == 2) {
let hero = new LadderOppPlayerHeroInfo();
hero.setByWarJson(warJson);
this.heroes.push(hero);
}
}
}
/**
*
* @param ladderMatch 需要是populate过role和defense.hero
*/
setPlayer(ladderMatch: LadderMatchType) {
this.isRobot = false;
this.rank = ladderMatch.rank;
this.roleId = ladderMatch.roleId;
let role = <RoleType>ladderMatch.role;
this.roleName = role.roleName;
this.title = role.title;
this.guildName = role.guildName;
for(let defenseHero of (ladderMatch.defense?.heroes||[])) {
this.defCe += defenseHero.ce;
let hero = new LadderOppPlayerHeroInfo();
hero.setByDefenseHero(<HeroType>defenseHero.hero);
this.heroes.push(hero);
}
}
}
// getOppData接口的返回
export class LadderOppDetailHeroReturn {
actorId: number = 0; // 武将id
skinId: number = 0; // 皮肤idfashions表的heroId
actorName: string = ''; // 武将名
dataId: number = 0; // 出兵表唯一id
relation: number = 0; // 地方还是我方
dirction: number = 0; // 方向
outIndex: number = 0; // 玩家设置的出场顺序即order字段
x: number = 0; // 战场x坐标
y: number = 0; // 战场y坐标
var: number = 0; // 变量
lv: number = 0; // 等级
hide: number = 0; // 是否隐藏
initial_ai: number = 0; // ai类型
attribute: string = ''; // 武将属性,格式 id&val|id&val|...这里的次级属性都是没有除1000的值
star: number = 0; // 星级
colorStar: number = 0; // 彩星
quality: number = 0; // 品质
skill: string = ''; // 技能
seid: string = ''; // 技能
spine: string = ''; // 动画
constructor(warJson: DicWarJson, defensHero: LadderDefenseHero) {
this.dataId = warJson.dataId;
this.relation = warJson.relation;
this.dirction = warJson.dirction;
this.x = warJson.x;
this.y = warJson.y;
this.var = warJson.var;
this.hide = warJson.hide;
if(defensHero) {
this.outIndex = defensHero.order;
this.initial_ai = defensHero.ai;
let hero = <HeroType>defensHero.hero;
if(hero) {
this.actorId = hero.hid;
this.skinId = hero.skinId;
this.actorName = hero.hName;
this.lv = hero.lv;
}
}
}
setAttribute(attribute: string) {
this.attribute = attribute;
}
}
// getOppData接口的返回
export class LadderOppDetailReturn {
battleCode: string = '';
roleId: string = '';
status: LADDER_STATUS = LADDER_STATUS.NO;
time: number = 0;
isRobot: boolean = false;
title: number = 1;
warId: number = 0;
heroes: LadderOppDetailHeroReturn[] = [];
constructor(ladderRec: LadderMatchRecType) {
this.battleCode = ladderRec.battleCode;
this.roleId = ladderRec.roleId2;
this.status = ladderRec.status;
if(this.status == LADDER_STATUS.BATTLE) {
this.time = ladderRec.battleTime + LADDER.LADDER_BATTLE_COUNTDOWN;
} else if (this.status == LADDER_STATUS.CHECK) {
this.time = ladderRec.checkTime + LADDER.LADDER_BATTLE_PREPARE_COUNTDOWN;
}
}
setByPlayer(ladderMatch: LadderMatchType, warJsons: DicWarJson[]) {
this.isRobot = false;
let role = <RoleType>ladderMatch.role;
this.title = role.title;
if(ladderMatch.defense) {
this.warId = ladderMatch.defense.warId;
let heroes = ladderMatch.defense.heroes||[];
for(let hero of heroes) {
let warJson = warJsons.find(cur => cur.dataId == hero.dataId);
if(warJson) {
let obj = new LadderOppDetailHeroReturn(warJson, hero);
this.heroes.push(obj);
}
}
}
}
setByRobot(dicLadderDifficultRatio: DicLadderDifficultRatio) {
this.isRobot = true;
this.roleId = `robot${dicLadderDifficultRatio.rank}`;
this.warId = dicLadderDifficultRatio.gkId;
}
setAttribute(hid: number, attribute: string) {
let hero = this.heroes.find(cur => cur.actorId == hid);
if(hero) hero.setAttribute(attribute);
}
}

View File

@@ -33,6 +33,9 @@ export class RankParam {
@prop({ required: true })
ce: number = 0;
@prop({ required: true })
topLineupCe: number = 0;
@prop({ required: true })
updatedAt?: number = 0;
@@ -45,6 +48,7 @@ export class RankParam {
if(role.title) this.title = role.title;
if(role.guildName) this.guildName = role.guildName||"";
if(role.ce) this.ce = role.ce;
if(role.topLineupCe) this.topLineupCe = role.topLineupCe;
if(role.updatedAt) {
if(typeof role.updatedAt == 'number') {
this.updatedAt = role.updatedAt;
@@ -225,7 +229,7 @@ export class RoleAndGuildRankInfo {
}
setUserInfo(role: RoleRankInfo) {
this.userInfo = pick(role, ['roleId', 'roleName', 'head', 'frame', 'spine', 'lv', 'title', 'guildName', 'ce']);
this.userInfo = pick(role, ['roleId', 'roleName', 'head', 'frame', 'spine', 'lv', 'title', 'guildName', 'ce', 'topLineupCe']);
this.lineup = role.lineup;
}
@@ -243,6 +247,10 @@ export class RoleAndGuildRankInfo {
}
return false;
}
setByField(roleId: string, roleName: string, head: number, frame: number, spine: number, lv: number, title: number, guildName: string, ce: number, topLineupCe: number) {
this.userInfo = { roleId, roleName, head, frame, spine, lv, title, guildName, ce, topLineupCe }
}
}
export class SimpleGuildRankParam {
@@ -306,6 +314,7 @@ export class KeyName {
case REDIS_KEY.GATE_ACTIVITY:
case REDIS_KEY.RACE_ACTIVITY:
case REDIS_KEY.TOWER_RANK:
case REDIS_KEY.LADDER:
return `${this.key}:${this.serverId}`;
case REDIS_KEY.HERO_RANK:
return `${this.key}:${this.serverId}:${this.hid}`;
@@ -381,7 +390,7 @@ export class GeneralRankParam {
this.type = type;
if(general == 1) {
if(rankInfo instanceof RoleRankInfo) {
this.userInfo = pick(rankInfo, ['roleId', 'roleName', 'head', 'frame', 'spine', 'guildName', 'ce', 'title', 'lv']);
this.userInfo = pick(rankInfo, ['roleId', 'roleName', 'head', 'frame', 'spine', 'guildName', 'ce', 'title', 'lv', 'topLineupCe']);
}
}
this.num = rankInfo.num;

View File

@@ -1,6 +1,6 @@
import { Connect, EPlace, HeroSkin, HeroType, HeroUpdate, Talent } from '../../db/Hero';
import { JewelType, RandSe } from '../../db/Jewel';
import { JewelSe, JewelType, RandSe } from '../../db/Jewel';
import { gameData } from '../../pubUtils/data';
export interface CreateHeroParam extends HeroUpdate {
hid: number;
@@ -104,6 +104,7 @@ export class JewelParam {
ePlaceId: number; // 装备在哪个位置
randSe: RandSe[];
rareSe: JewelSe[];
count: number;
inc: number;
reason: number;
@@ -115,6 +116,7 @@ export class JewelParam {
this.hid = jewel.hid;
this.ePlaceId = jewel.ePlaceId;
this.randSe = jewel.randSe;
this.rareSe = jewel.rareSe;
if(isPush) {
this.count = 1;
this.inc = 1;

View File

@@ -1,4 +1,4 @@
import { EPlace, HeroType } from "../../db/Hero";
import { Connect, EPlace, HeroType } from "../../db/Hero";
import { JewelType } from "../../db/Jewel";
import { HeroScore } from "../battleField/pvp";
@@ -16,7 +16,9 @@ export class TaskParamInter {
heroNum?: number; // 武将数量
trainCount?: number; // 武将训练次数
stageUpCnt?: number; // 武将升阶次数
connectLv?: number; // 羁绊等级
shipId?: number; // 羁绊id
oldConnections?: Connect[]; // 旧的羁绊
connections?: Connect[]; // 羁绊
isSuccess?: boolean; // pvp是否胜利
heroScores?: HeroScore[]; // pvp各武将积分
eventType?: number; // 奇遇类型
@@ -25,6 +27,8 @@ export class TaskParamInter {
pvpRank?: number; // pvp等级
guildJob?: number; // 军团职位
isComplete?: boolean; // 练兵场是否压制
oldLadderRank?: number; // 名将擂台原排名
ladderRank?: number; // 名将擂台排名
oldLv?: number; // 原武将等级,原玩家等级
oldStar?: number; // 原武将星级

View File

@@ -107,6 +107,9 @@ import { dicTowerPvpSubAttr, loadTowerPvpSubAttr } from './dictionary/DicTowerPv
import { dicSystemOpenTime, loadSystemOpenTime } from "./dictionary/DicSystemOpenTime";
import { dicRandomEffectPoolPlan, loadRandomEffectPoolPlan } from './dictionary/DicRandomEffectPoolPlan';
import { dicMainWarRewardByChapter, loadMainWarReward } from './dictionary/DicMainWarReward';
import { dicLadderMatch, loadLadderMatch } from "./dictionary/DicLadderMatch";
import { dicLadderDifficultRatio, loadLadderDifficultRatio } from "./dictionary/DicLadderDifficultRatio";
import { dicLadderRankReward, loadLadderRankReward } from "./dictionary/DicLadderRankReward";
export const gameData = {
daily: dicDaily,
@@ -269,6 +272,10 @@ export const gameData = {
towerPvpSubAttr: dicTowerPvpSubAttr,
sysOpenTime: dicSystemOpenTime,
randomEffectPoolPlan: dicRandomEffectPoolPlan,
ladderConsume: new Array<{id: number, count: number, times: number}>(),
ladderMatch: dicLadderMatch,
ladderDifficultRatio: dicLadderDifficultRatio,
ladderRankReward: dicLadderRankReward
};
// 在此提供一些原先在gamedata中提供的方法以便更方便获取gameData数据
@@ -969,6 +976,37 @@ export function getEquipStrenthenAttr(equipId: number, lv: number) {
return result
}
export function decodeLadderBuyCost() {
gameData.ladderConsume = [];
let str = param.LADDER.LADDER_CHALLENGE_BUY_TIMES_COST;
let arrs = decodeArrayListStr(str);
let sumTimes = 0;
for (let [ id, count, times] of arrs) {
sumTimes += parseInt(times);
gameData.ladderConsume.push({ id: parseInt(id), count: parseInt(count), times: sumTimes });
}
}
export function getDicLadderMatchByMyRank(myRank: number) {
for(let dic of gameData.ladderMatch) {
let { rankMin, rankMax } = dic;
if(rankMin < myRank && (rankMax >= myRank || rankMax == -1) ) {
return dic;
}
}
return null
}
export function getLadderRankReward(myRank: number) {
for(let dic of gameData.ladderRankReward) {
let { rankMin, rankMax } = dic;
if(rankMin < myRank && (rankMax >= myRank || rankMax == -1) ) {
return dic;
}
}
return null
}
// 初始加载
function initDatas() {
parseDicParam();
@@ -987,6 +1025,7 @@ function parseDicParam() {
parseComBtlLvRange();
getCeRatio();
loadAuctionTime();
decodeLadderBuyCost();
}
/**
@@ -1141,6 +1180,9 @@ function loadDatas() {
loadSystemOpenTime();
loadRandomEffectPoolPlan();
loadMainWarReward();
loadLadderMatch();
loadLadderDifficultRatio();
loadLadderRankReward();
}
// 重载dicParam

View File

@@ -320,4 +320,6 @@ export const LADDER = {
LADDER_CHALLENGE_BUY_TIMES_COST: '31002&20&20|31002&50&10|31002&100&10|31002&200&10', // 名将擂台每天购买挑战次数花费 物品id&数量&购买次数
LADDER_ROBOT_NAME: '擂台守卫者', // 机器人默认名
LADDER_INITIAL_CHALLENGE_TIMES: 5, // 战5次
LADDER_LINEUP_HEROS: 6, // 名将擂台最多上阵人数
LADDER_BGMAP_GKID: 60001, // 名将擂台防守阵容地图所用的关卡id
};

View File

@@ -20,6 +20,8 @@ export interface DicJewel {
readonly effectCount: number;
// 套装效果
readonly randomEffect: number[];
// 稀有属性
readonly rareEffect: number[];
// 对应藏宝图id
readonly mapGoodId: number;
// 淬炼消耗
@@ -40,6 +42,7 @@ export function loadJewel() {
arr.forEach(o => {
o.randomEffect = parseNumberList(o.randomEffect);
o.rareEffect = parseNumberList(o.rareEffect);
o.quenchConsume = parseGoodStr(o.quenchConsume);
o.inheritConsume = parseGoodStr(o.inheritConsume);
dicJewel.set(o.good_id, o);

View File

@@ -0,0 +1,55 @@
// 邮件内容
import {decodeArrayListStr, parseGoodStr, readFileAndParse} from '../util'
import { FILENAME } from '../../consts'
import { RewardInter } from '../interface';
export interface DicLadderDifficultRatio {
// id
readonly id: number;
// 排名
readonly rank: number;
// 等级
readonly level: number;
// 关卡id
readonly gkId: number;
// 头像
readonly head: number;
// 形象
readonly spine: number;
// 胜利奖励
readonly randomWinReward: { id: number, count: number, weight: number }[];
// 失败奖励
readonly randomFailReward: { id: number, count: number, weight: number }[];
// 突破奖励
readonly onceReward: RewardInter[];
// 战力
readonly ce: number;
}
export const dicLadderDifficultRatio = new Map<number, DicLadderDifficultRatio>();
export function loadLadderDifficultRatio() {
dicLadderDifficultRatio.clear();
let arr = readFileAndParse(FILENAME.DIC_LADDER_DIFFICULTRATIO);
arr.forEach(o => {
o.gkId = parseInt(o.gkId);
o.randomWinReward = parseRewardWithWeight(o.randomWinReward);
o.randomFailReward = parseRewardWithWeight(o.randomFailReward);
o.onceReward = parseGoodStr(o.onceReward)
dicLadderDifficultRatio.set(o.id, o);
});
arr = undefined;
}
function parseRewardWithWeight(str: string) {
let result = new Array<{ id: number, count: number, weight: number }>();
if (!str) return result;
let decodeArr = decodeArrayListStr(str);
for (let [id, count, weight] of decodeArr) {
if (isNaN(parseInt(id)) || isNaN(parseInt(count)) || isNaN(parseInt(weight))) {
throw new Error('data table format wrong');
}
result.push({ id: parseInt(id), count: parseInt(count), weight: parseInt(weight) });
}
return result
}

View File

@@ -0,0 +1,41 @@
// 名将擂台匹配范围
import { parseNumberList, readFileAndParse} from '../util'
import { FILENAME } from '../../consts'
export interface DicLadderMatch {
// id
readonly id: number;
// 最低排名
readonly rankMin: number;
// 最高排名
readonly rankMax: number;
// 玩家随机范围往前数最多多少人
readonly rangeBeforeMax: number;
// 玩家随机范围往后数最多多少人
readonly rangeAfterMax: number;
// 玩家随机范围往前随多少人
readonly rangeBeforeNum: number;
// 玩家随机范围往后随多少人
readonly rangeAfterNum: number;
// 玩家随机间隔
readonly gap: { min: number, max: number };
// 是否可以挑战前十
readonly topChallenge: number;
}
export const dicLadderMatch: DicLadderMatch[] = [];
export function loadLadderMatch() {
dicLadderMatch.splice(0, dicLadderMatch.length);
let arr = readFileAndParse(FILENAME.DIC_LADDER_MATCH);
arr.forEach(o => {
o.gap = parseGap(o.interval);
dicLadderMatch.push(o);
});
arr = undefined;
}
function parseGap(str: string) {
let arr = parseNumberList(str);
return { min: arr[0], max: arr[1] }
}

View File

@@ -0,0 +1,27 @@
// 名将擂台每天22点排名奖励
import {parseGoodStr, readFileAndParse} from '../util'
import { FILENAME } from '../../consts'
import { RewardInter } from '../interface';
export interface DicLadderRankReward {
// id
readonly id: number;
// 最低排名
readonly rankMin: number;
// 最高排名
readonly rankMax: number;
// 突破奖励
readonly reward: RewardInter[];
}
export const dicLadderRankReward: DicLadderRankReward[] = [];
export function loadLadderRankReward() {
dicLadderRankReward.splice(0, dicLadderRankReward.length);
let arr = readFileAndParse(FILENAME.DIC_LADDER_RANKREWARD);
arr.forEach(o => {
o.reward = parseGoodStr(o.reward)
dicLadderRankReward.push(o);
});
arr = undefined;
}

View File

@@ -48,6 +48,8 @@ declare module 'redis' {
zaddAsync(key: string, score: number, member: string): Promise<number>;
// 返回有序集中指定分数区间内的成员,分数从高到低排序
zrevrangebyscoreAsync(key: string, max: (string|number), min: (string|number), withscores?: string, limit?:string, offset?: number, count?:number): Promise<string[]>;
// 返回有序集中指定分数区间内的成员,分数从高到低排序
zrangebyscoreAsync(key: string, min: (string|number), max: (string|number), withscores?: string, limit?:string, offset?: number, count?:number): Promise<string[]>;
// 获取有序集合的成员数
zcardAsync(key: string): Promise<number>;
// 移除有序集合中给定的排名区间的所有成员

View File

@@ -681,6 +681,8 @@ export function getReasonByWarType(warType: number) {
return ITEM_CHANGE_REASON.TRY_BATTLE_END;
case WAR_TYPE.BOSS:
return ITEM_CHANGE_REASON.BOSS_BATTLE_END;
case WAR_TYPE.LADDER:
return ITEM_CHANGE_REASON.LADDER_BATTLE_REWARD;
default:
return ITEM_CHANGE_REASON.NORMAL_BATTLE_END;
}

View File

@@ -208,5 +208,12 @@
"sendName": "&",
"content": "很抱歉,您的军团已被解散,振作起来寻找新的军团加入吧",
"time": 720
},
{
"id": 30,
"title": "&",
"sendName": "&",
"content": "您在昨日的名将擂台中获得了第%d名排名奖励已发送至您的邮箱请查收",
"time": 720
}
]

View File

@@ -2094,5 +2094,181 @@
"condition": 1,
"taskReward": "40005&4000",
"point": 30
},
{
"id": 132,
"achieveTaskId": 134,
"tab": 2,
"tabName": "游历",
"subTab": 16,
"subTabName": "羁绊",
"taskType": 124,
"taskParam": "5&",
"achieveType": 57,
"achieveTaskName": "亲密无间·壹",
"taskInfo": "单个最高等级的羁绊达5级",
"condition": 5,
"taskReward": "11014&1",
"point": 5
},
{
"id": 133,
"achieveTaskId": 135,
"tab": 2,
"tabName": "游历",
"subTab": 16,
"subTabName": "羁绊",
"taskType": 124,
"taskParam": "10&",
"achieveType": 57,
"achieveTaskName": "亲密无间·贰",
"taskInfo": "单个最高等级的羁绊达10级",
"condition": 10,
"taskReward": "11014&2",
"point": 10
},
{
"id": 134,
"achieveTaskId": 136,
"tab": 2,
"tabName": "游历",
"subTab": 16,
"subTabName": "羁绊",
"taskType": 124,
"taskParam": "12&",
"achieveType": 57,
"achieveTaskName": "亲密无间·叁",
"taskInfo": "单个最高等级的羁绊达12级",
"condition": 12,
"taskReward": "11014&5",
"point": 15
},
{
"id": 135,
"achieveTaskId": 137,
"tab": 2,
"tabName": "游历",
"subTab": 16,
"subTabName": "羁绊",
"taskType": 125,
"taskParam": "15&",
"achieveType": 58,
"achieveTaskName": "同生共死·壹",
"taskInfo": "单个3条羁绊共提升15级",
"condition": 15,
"taskReward": "11014&10",
"point": 20
},
{
"id": 136,
"achieveTaskId": 138,
"tab": 2,
"tabName": "游历",
"subTab": 16,
"subTabName": "羁绊",
"taskType": 125,
"taskParam": "36&",
"achieveType": 58,
"achieveTaskName": "同生共死·贰",
"taskInfo": "单个3条羁绊共提升36级",
"condition": 36,
"taskReward": "11014&5",
"point": 15
},
{
"id": 137,
"achieveTaskId": 139,
"tab": 8,
"tabName": "试炼",
"subTab": 17,
"subTabName": "攻擂",
"taskType": 121,
"taskParam": "10&",
"achieveType": 59,
"achieveTaskName": "死战·壹",
"taskInfo": "名将擂台挑战10次",
"condition": 10,
"taskReward": "40007&50",
"point": 30
},
{
"id": 138,
"achieveTaskId": 140,
"tab": 8,
"tabName": "试炼",
"subTab": 17,
"subTabName": "攻擂",
"taskType": 121,
"taskParam": "50&",
"achieveType": 59,
"achieveTaskName": "死战·贰",
"taskInfo": "名将擂台挑战50次",
"condition": 50,
"taskReward": "40007&100",
"point": 5
},
{
"id": 139,
"achieveTaskId": 141,
"tab": 8,
"tabName": "试炼",
"subTab": 17,
"subTabName": "攻擂",
"taskType": 122,
"taskParam": "10&",
"achieveType": 60,
"achieveTaskName": "胜利者·壹",
"taskInfo": "名将擂台胜利10次",
"condition": 10,
"taskReward": "40007&150",
"point": 10
},
{
"id": 140,
"achieveTaskId": 142,
"tab": 8,
"tabName": "试炼",
"subTab": 17,
"subTabName": "攻擂",
"taskType": 122,
"taskParam": "50&",
"achieveType": 60,
"achieveTaskName": "胜利者·贰",
"taskInfo": "名将擂台胜利50次",
"condition": 50,
"taskReward": "40007&200",
"point": 10
},
{
"id": 141,
"achieveTaskId": 143,
"tab": 8,
"tabName": "试炼",
"subTab": 17,
"subTabName": "攻擂",
"taskType": 123,
"taskParam": "100&1",
"achieveType": 61,
"achieveTaskName": "不屈不挠·壹",
"taskInfo": "名将擂台排名进入前100名",
"condition": 1,
"taskReward": "17054&100",
"point": 10
},
{
"id": 142,
"achieveTaskId": 144,
"tab": 8,
"tabName": "试炼",
"subTab": 17,
"subTabName": "攻擂",
"taskType": 123,
"taskParam": "50&1",
"achieveType": 61,
"achieveTaskName": "不屈不挠·贰",
"taskInfo": "名将擂台排名进入前50名",
"condition": 1,
"taskReward": "17054&100",
"point": 10
}
]

View File

@@ -4,7 +4,7 @@
"name": "ROLE_SEND_TEXT",
"mean": "玩家发送消息",
"type": "2&3&4&5",
"template": "<color=#301B0C>%d</color>",
"template": "<color=#ffffff><outline color=#1c613 width=1>%d</outline></color>",
"worldchat": "<outline color=#1c1613 width=1>%d</outline>",
"comments": "%d玩家发送的消息内容"
},
@@ -13,7 +13,7 @@
"name": "PRIVATE_SEND_GIFT",
"mean": "发送礼物",
"type": "5&",
"template": "<color=#301B0C>我挥了挥小手,给你送了</color><color=#fc879b>%d</color><color=#a49a48>,你一定要喜欢哦!</color>",
"template": "<color=#ffffff><outline color=#1c613 width=1>我挥了挥小手,给你送了</outline></color><color=#fc879b><outline color=#1c613 width=1>%d</outline></color><color=#ffffff><outline color=#1c613 width=1>,你一定要喜欢哦!</outline></color>",
"worldchat": "<outline color=#1c1613 width=1><color=#301B0C>我挥了挥小手,给你送了</color><color=#fc879b>%d</color><color=#a49a48>,你一定要喜欢哦!</color></outline>",
"comments": "1:%d(赠送的道具)"
},
@@ -22,7 +22,7 @@
"name": "TEAM_ROLE",
"mean": "玩家发送组队消息",
"type": "5&",
"template": "<color=#301B0C>%d</color>",
"template": "<color=#ffffff><outline color=#1c613 width=1>%d</outline></color>",
"worldchat": "<outline color=#1c1613 width=1><color=#301B0C>%d</color></outline>",
"comments": "1:%d(发出组队邀请的玩家名)2:%d(跳转链接)"
},
@@ -31,7 +31,7 @@
"name": "HERO_QUALITY_UP",
"mean": "武将升品",
"type": "1&",
"template": "<color=#301B0C>恭喜</color><color=#c08834>%d</color><color=#301B0C>将</color><outline color=#ad5712 width=1>%d</outline><color=#301B0C>升品至</color><outline color=#ad5712 width=1>%d</outline>",
"template": "<color=#ffffff><outline color=#1c613 width=1>恭喜</outline></color><color=#c08834><outline color=#1c613 width=1>%d</outline></color><color=#ffffff><outline color=#1c613 width=1>将</outline></color><outline color=#1c613 width=1>%d</outline><color=#ffffff><outline color=#1c613 width=1>升品至</outline></color><outline color=#ad5712 width=1>%d</outline>",
"worldchat": "<outline color=#1c1613 width=1><color=#301B0C>恭喜</color><color=#c08834>%d</color><color=#a49a48>将</color>%d<color=#a49a48>升品至</color>%d</outline>",
"comments": "1:%d(玩家名)2:%d(武将名)3.%d(武将品质)"
},
@@ -40,7 +40,7 @@
"name": "COMPOSE_ORANGE_HERO",
"mean": "玩家获得传奇武将(橙)",
"type": "1&",
"template": "<color=#301B0C>恭喜</color><color=#c08834>%d</color><color=#301B0C>时来运转,获得传奇武将</color><outline color=#ad5712 width=1>%d</outline>",
"template": "<color=#ffffff><outline color=#1c613 width=1>恭喜</outline></color><color=#c08834><outline color=#1c613 width=1>%d</outline></color><color=#ffffff><outline color=#1c613 width=1>时来运转,获得传奇武将</outline></color><outline color=#ad5712 width=1>%d</outline>",
"worldchat": "<outline color=#1c1613 width=1>恭喜<color=#E6CB68>%d</color>时来运转,获得传奇武将%d</outline>",
"comments": "1:%d(玩家名)2:%d(武将名)"
},
@@ -49,7 +49,7 @@
"name": "HERO_STAR_MAX",
"mean": "武将满星",
"type": "1&",
"template": "<color=#301B0C>恭喜</color><color=#c08834>%d</color><color=#301B0C>将</color><outline color=#ad5712 width=1><color=#ffd800>%d</color></outline><color=#301B0C>培养至</color><outline color=#bd1616 width=1><color=#ff8080>彩星满星</color></outline>",
"template": "<color=#ffffff><outline color=#1c613 width=1>恭喜</outline></color><color=#c08834><outline color=#1c613 width=1>%d</outline></color><color=#ffffff><outline color=#1c613 width=1>将</color><outline color=#ad5712 width=1><color=#ffd800>%d</color></outline><color=#ffffff><outline color=#1c613 width=1>培养至</outline></color><outline color=#bd1616 width=1><color=#ff8080>彩星满星</color></outline>",
"worldchat": "<outline color=#1c1613 width=1>恭喜<color=#c08834>%d</color><color=#a49a48>将</color><color=#f03420>%d</color>培养至彩星满星</outline>",
"comments": "1:%d(玩家名)2:%d(武将名)"
},
@@ -58,7 +58,7 @@
"name": "GUILD_NOTICE",
"mean": "军团修改公告",
"type": "3&",
"template": "<color=#5885BC>公告:%d</color>",
"template": "<color=#5885BC><outline color=#1c613 width=1>公告:</outline></color><outline color=#1c613 width=1>%d</outline>",
"worldchat": "<color=#301B0C>公告:%d</color>",
"comments": "%d玩家发送的消息内容"
},
@@ -67,7 +67,7 @@
"name": "GUILD_STRUCTURE_LV_UP",
"mean": "军团升级建筑",
"type": "3&",
"template": "<color=#301B0C>恭喜主公,军团</color><color=#d9973a>%d</color><color=#301B0C>已升至</color><color=#907ffb>%d</color><color=#301B0C>级</color>",
"template": "<color=#ffffff><outline color=#1c613 width=1>恭喜主公,军团</outline></color><color=#d9973a><outline color=#1c613 width=1>%d</outline></color><color=#ffffff><outline color=#1c613 width=1>已升至</outline></color><color=#907ffb><outline color=#1c613 width=1>%d</outline></color><color=#ffffff><outline color=#1c613 width=1>级</outline></color>",
"worldchat": "<outline color=#1c1613 width=1>恭喜各位小将军,军团<outline color=#5a5cc width=1><color=#53e1fb>%d</color></outline>已升至<color=#907ffb>%d</color>级</outline>",
"comments": "1:%d(军团名)2:%d(军团升级后的等级数)"
},
@@ -76,7 +76,7 @@
"name": "GUILD_TRAIN_SUC",
"mean": "军团练兵场压制成功",
"type": "3&",
"template": "<color=#301B0C>军团据点</color><outline color=#ad5712 width=1>%d</outline><color=#301B0C>被成功压制,对应宝藏已开启,请各位成员前往领取宝藏</color>",
"template": "<color=#ffffff><outline color=#1c613 width=1>军团据点</outline></color><outline color=#ad5712 width=1>%d</outline><color=#ffffff><outline color=#1c613 width=1>被成功压制,对应宝藏已开启,请各位成员前往领取宝藏</outline></color>",
"worldchat": "<outline color=#1c1613 width=1><color=#301B0C>军团据点</color>%d<color=#a49a48>被成功压制,对应宝藏已开启,请各位成员前往领取宝藏</color></outline>",
"comments": "1:%d(武将名)"
},
@@ -85,7 +85,7 @@
"name": "GUILD_BOSS_SUC",
"mean": "军团BOSS胜利",
"type": "3&",
"template": "<color=#301B0C>恭喜主公军团BOSS</color><color=#f019fd>%d</color><color=#301B0C>被成功压制,拍卖行即将开启</color>",
"template": "<color=#ffffff><outline color=#1c613 width=1>恭喜主公军团BOSS</outline></color><color=#f019fd><outline color=#1c613 width=1>%d</outline></color><color=#ffffff><outline color=#1c613 width=1>被成功压制,拍卖行即将开启</outline></color>",
"worldchat": "<outline color=#1c1613 width=1>恭喜各位小将军军团BOSS<color=#f019fd>%d</color>被成功压制,拍卖行即将开启</outline>",
"comments": "1:%d(BOSS名)"
},
@@ -94,8 +94,8 @@
"name": "EQUIP_REFINE_SUC",
"mean": "玩家首次精炼成功",
"type": "1&",
"template": "<color=#301B0C>恭喜</color><color=#c08834>%d</color><color=#301B0C>,成功将</color><on click=%d>%d</on><color=#301B0C>进行精炼</color>",
"worldchat": "<outline color=#1c1613 width=1>恭喜<color=#c08834>%d</color>,成功将<on click=%d>%d</on>进行精炼</outline>",
"template": "<color=#ffffff><outline color=#1c613 width=1>恭喜</outline></color><color=#c08834><outline color=#1c613 width=1>%d</outline></color><color=#ffffff><outline color=#1c613 width=1>,成功将</outline></color><color=%d><outline color=%d width=1><on click=%d>%d</on></outline></color><color=#ffffff><outline color=#1c613 width=1>进行精炼</outline></color>",
"worldchat": "<outline color=#1c1613 width=1>恭喜<color=#c08834>%d</color>,成功将</outline></color><color=%d><outline color=%d width=1><on click=%d>%d</on></outline></color><color=#ffffff><outline color=#1c613 width=1>进行精炼</outline>",
"comments": "1:%d(玩家名)2:%d(装备名)"
},
{
@@ -103,7 +103,7 @@
"name": "EQUIP_COMPOSE_SUIT",
"mean": "装备合成套装",
"type": "1&",
"template": "<color=#301B0C>恭喜</color><color=#c08834>%d</color><color=#301B0C>,合成套装装备</color>%d",
"template": "<color=#ffffff><outline color=#1c613 width=1>恭喜</outline></color><color=#c08834><outline color=#1c613 width=1>%d</outline></color><color=#ffffff><outline color=#1c613 width=1>,合成套装装备</outline></color><outline color=#1c613 width=1>%d</outline>",
"worldchat": "<outline color=#1c1613 width=1>恭喜<color=#c08834>%d</color>,合成套装装备%d</outline>",
"comments": "1:%d(玩家名)2:%d(装备名)"
},
@@ -112,7 +112,7 @@
"name": "EQUIP_COMPOSE_ORANGE",
"mean": "装备合成出橙色品质及其以上装备",
"type": "1&",
"template": "<color=#301B0C>恭喜</color><color=#c08834>%d</color><color=#301B0C>,获得</color>%d",
"template": "<color=#ffffff><outline color=#1c613 width=1>恭喜</outline></color><color=#c08834><outline color=#1c613 width=1>%d</outline></color><color=#ffffff><outline color=#1c613 width=1>,获得</outline></color><outline color=#1c613 width=1>%d</outline>",
"worldchat": "<outline color=#1c1613 width=1>恭喜<color=#c08834>%d</color>,获得%d</outline>",
"comments": "1:%d(玩家名)2:%d(装备名)"
},
@@ -121,7 +121,7 @@
"name": "JEWEL_COMPOSE",
"mean": "宝石合成7阶及其以上",
"type": "1&",
"template": "<color=#301B0C>恭喜</color><color=#c08834>%d</color><color=#301B0C>,合成出%d",
"template": "<color=#ffffff><outline color=#1c613 width=1>恭喜</outline></color><color=#c08834><outline color=#1c613 width=1>%d</outline></color><color=#ffffff><outline color=#1c613 width=1>,合成出</outline></color><outline color=#1c613 width=1>%d</outline>",
"worldchat": "<outline color=#1c1613 width=1>恭喜<color=#c08834>%d</color>,合成出%d</outline>",
"comments": "1:%d(玩家名)2:%d(宝石阶数)3.%d(宝石名)"
},
@@ -130,7 +130,7 @@
"name": "ORANGE_BLUEPRT_COMPOSE",
"mean": "合成橙色及其以上藏宝图",
"type": "1&",
"template": "<color=#301B0C>恭喜</color><color=#c08834>%d</color><color=#301B0C>,合成</color>%d",
"template": "<color=#ffffff><outline color=#1c613 width=1>恭喜</outline></color><color=#c08834><outline color=#1c613 width=1>%d</outline></color><color=#ffffff><outline color=#1c613 width=1>,合成</outline></color><outline color=#1c613 width=1>%d</outline>",
"worldchat": "<outline color=#1c1613 width=1>恭喜<color=#c08834>%d</color>,合成</color>%d</outline>",
"comments": "1:%d(玩家名)2:%d(藏宝图名)"
},
@@ -139,7 +139,7 @@
"name": "TOWER_SUC",
"mean": "镇念塔100层后每通关50层",
"type": "1&",
"template": "<color=#301B0C>恭喜</color><color=#c08834>%d</color><color=#301B0C>,镇念塔通关</color><color=#e5955a>%d</color><color=#301B0C>层</color>",
"template": "<color=#ffffff><outline color=#1c613 width=1>恭喜</outline></color><color=#c08834><outline color=#1c613 width=1>%d</outline></color><color=#ffffff><outline color=#1c613 width=1>,镇念塔通关</outline></color><color=#e5955a><outline color=#1c613 width=1>%d</outline></color><color=#ffffff><outline color=#1c613 width=1>层</outline></color>",
"worldchat": "<outline color=#1c1613 width=1>恭喜<color=#c08834>%d</color>,镇念塔通关<color=#e5955a>%d</color>层</outline>",
"comments": "1:%d(玩家名)2:%d(镇念塔层数)"
},
@@ -148,7 +148,7 @@
"name": "MYSTERY_FIRST_SUC",
"mean": "秘境首通",
"type": "1&",
"template": "<color=#301B0C>恭喜</color><color=#c08834>%d</color><color=#301B0C>,在秘境中成功压制通关</color><color=#e5955a>%d</color>",
"template": "<color=#ffffff><outline color=#1c613 width=1>恭喜</outline></color><color=#c08834><outline color=#1c613 width=1>%d</outline></color><color=#ffffff><outline color=#1c613 width=1>,在秘境中成功压制通关</outline></color><color=#e5955a><outline color=#1c613 width=1>%d</outline></color>",
"worldchat": "<outline color=#1c1613 width=1>恭喜<color=#c08834>%d</color>,在秘境中成功压制通关<color=#e5955a>%d</color></outline>",
"comments": "1:%d(玩家名)2:%d(关卡名)"
},
@@ -157,7 +157,7 @@
"name": "VESTIGE_FIRST_SUC",
"mean": "遗迹首通",
"type": "1&",
"template": "<color=#301B0C>恭喜</color><color=#c08834>%d</color><color=#301B0C>,在遗迹中成功压制通关</color><color=#e5955a>%d</color>",
"template": "<color=#ffffff><outline color=#1c613 width=1>恭喜</outline></color><color=#c08834><outline color=#1c613 width=1>%d</outline></color><color=#ffffff><outline color=#1c613 width=1>,在遗迹中成功压制通关</outline></color><color=#e5955a><outline color=#1c613 width=1>%d</outline></color>",
"worldchat": "<outline color=#1c1613 width=1>恭喜<color=#c08834>%d</color>,在遗迹中成功压制通关<color=#e5955a>%d</color></outline>",
"comments": "1:%d(玩家名)2:%d(关卡名)"
},
@@ -166,7 +166,7 @@
"name": "TEAM_INVITE",
"mean": "组队一键邀请",
"type": "2&3",
"template": "<color=#c08834>%d</color><color=#301B0C>发起组队邀请,诚邀各路侠士加入</color><color=#301B0C> %d</color><color=#301B0C>寻宝队伍,</color><color=#2f9b00><on click=%d>[加入]</on></color>",
"template": "<color=#c08834><outline color=#1c613 width=1>%d</outline></color><color=#ffffff><outline color=#1c613 width=1>发起组队邀请,诚邀各路侠士加入</outline></color><color=#ffffff><outline color=#1c613 width=1>%d</outline></color><color=#ffffff><outline color=#1c613 width=1>寻宝队伍,</outline></color><color=#2f9b00><on click=%d>[加入]</on></color>",
"worldchat": "<outline color=#1c1613 width=1><color=#c08834>%d</color>发起组队邀请,诚邀各路侠士加入<color=#a49a48> %d寻宝队伍</color><color=#2f9b00><on click=%d>[加入]</on></color></outline>",
"comments": "1:%d(发出组队邀请的玩家名)2:%d(藏宝图对应的关卡名); 3:%d(跳转链接)"
},
@@ -175,7 +175,7 @@
"name": "HERO_WAKEUP",
"mean": "武将觉醒",
"type": "1&",
"template": "<color=#301B0C>恭喜</color><color=#c08834>%d</color><color=#301B0C>,成功将</color><outline color=#ad5712 width=1><color=#ffd800>%d</color></outline><color=#301B0C>觉醒</color>",
"template": "<color=#ffffff><outline color=#1c613 width=1>恭喜</outline></color><color=#c08834><outline color=#1c613 width=1>%d</outline></color><color=#ffffff><outline color=#1c613 width=1>,成功将</outline></color><outline color=#ad5712 width=1><color=#ffd800>%d</color></outline><color=#ffffff><outline color=#1c613 width=1>觉醒</outline></color>",
"worldchat": "<outline color=#1c1613 width=1><color=#301B0C>恭喜</color><color=#c08834>%d</color><color=#301B0C>,成功将</color><color=#f03420>%d</color><color=#301B0C>觉醒</color>",
"comments": "1:%d(玩家名)2:%d(武将名)"
},
@@ -184,7 +184,7 @@
"name": "EQUIP_REFRESH_BEST",
"mean": "装备洗炼出特殊属性",
"type": "1&",
"template": "<color=#c08834>%d</color><color=#c08834>锲而不舍,将</color>%d<color=#c08834>洗炼出极品属性</color>",
"template": "<color=#c08834><outline color=#1c613 width=1>%d</outline></color><color=##ffffff><outline color=#1c613 width=1>锲而不舍,将</outline></color><outline color=#1c613 width=1>%d</outline><color=#c08834><color=##ffffff><outline color=#1c613 width=1>洗炼出极品属性</outline></color>",
"worldchat": "<outline color=#1c1613 width=1><color=#c08834>%d</color>锲而不舍,将%d洗炼出极品属性</outline>",
"comments": "1:%d(玩家名)2:%d(装备名)"
},
@@ -193,7 +193,7 @@
"name": "ACQUIRE_RARE_GOODS",
"mean": "掉落其他稀有道具",
"type": "1&",
"template": "<color=#c08834>%d</color><color=#c08834>福星高照,获得稀有物资</color>%d",
"template": "<color=#c08834><outline color=#1c613 width=1>%d</outline></color><color=#ffffff><outline color=#1c613 width=1>福星高照,获得稀有物资</outline></color><outline color=#1c613 width=1>%d</outline>",
"worldchat": "<outline color=#1c1613 width=1><color=#c08834>%d</color>福星高照,获得稀有物资%d</outline>",
"comments": "1:%d(玩家名)2:%d(物品名)"
},
@@ -202,7 +202,7 @@
"name": "GROUP_SEND_GIFT",
"mean": "发送礼物",
"type": "1&",
"template": "<color=#c08834>%d</color><color=#301B0C>赠送给</color><color=#c08834>%d</color><outline color=#5a5ccc width=1><color=#f09fed>%d</color></outline>",
"template": "<color=#c08834><outline color=#1c613 width=1>%d</outline></color><color=#ffffff><outline color=#1c613 width=1>赠送给</outline></color><color=#c08834><outline color=#1c613 width=1>%d</outline></color><outline color=#5a5ccc width=1><color=#f09fed>%d</color></outline>",
"worldchat": "<outline color=#1c1613 width=1><color=#c08834>%d</color>赠送给<color=#c08834>%d</color><color=#5a5ccc>%d</color></outline>",
"comments": "1:%d(赠送的玩家名)2:%d(赠送的玩家名)3.%d(赠送的道具)"
},
@@ -211,7 +211,7 @@
"name": "SEND_MARQUEE",
"mean": "跑马灯内容",
"type": "1&",
"template": "%d",
"template": "<outline color=#1c613 width=1>%d</outline>",
"worldchat": "%d",
"comments": "&"
},
@@ -220,7 +220,7 @@
"name": "GUILD_RECURIT",
"mean": "军团招募",
"type": "2&",
"template": "<color=#53e1fb>%d</color><color=#301B0C>正在招兵买马:</color><color=#c08834>%d</color><color=#2f9b00><on click=%d>[前往]</on></color>",
"template": "<color=#53e1fb><outline color=#1c613 width=1>%d</outline></color><color=#ffffff><outline color=#1c613 width=1>正在招兵买马:</outline></color><color=#c08834><outline color=#1c613 width=1>%d</outline></color><color=#2f9b00><on click=%d>[前往]</on></color>",
"worldchat": "<outline color=#1c1613 width=1><color=#53e1fb>%d</color>正在招兵买马:%d<color=#2f9b00><on click=%d>[前往]</on></color></outline>",
"comments": "1:%d(军团名)2:%d(军团招募内容)"
},
@@ -229,8 +229,8 @@
"name": "EQUIP_STAR_UP",
"mean": "装备升至12星",
"type": "1&",
"template": "<color=#301B0C>恭喜</color><color=#c08834>%d</color><color=#301B0C>,成功将</color><on click=%d>%d</on><color=#301B0C>升至满星</color>",
"worldchat": "<outline color=#1c1613 width=1>恭喜<color=#c08834>%d</color>,成功将<on click=%d>%d</on>升至满星</outline>",
"template": "<color=#ffffff><outline color=#1c613 width=1>恭喜</outline></color><color=#c08834><outline color=#1c613 width=1>%d</outline></color><color=#ffffff><outline color=#1c613 width=1>,成功将</outline></color><color=%d><outline color=%d width=1><on click=%d>%d</on></outline></color><color=#ffffff><outline color=#1c613 width=1>升至满星</outline></color>",
"worldchat": "<outline color=#1c1613 width=1>恭喜<color=#c08834>%d</color>,成功将</outline></color><color=%d><outline color=%d width=1><on click=%d>%d</on></outline></color><color=#ffffff><outline color=#1c613 width=1>升至满星</outline></color>",
"comments": "1:%d(玩家名)2:%d(装备名)"
},
{
@@ -238,8 +238,17 @@
"name": "EQUIP_QUALITY_UP",
"mean": "装备升至金色",
"type": "1&",
"template": "<color=#301B0C>恭喜</color><color=#c08834>%d</color><color=#301B0C>,成功将</color><on click=%d>%d</on><color=#301B0C>升至最高品质</color>",
"worldchat": "<outline color=#1c1613 width=1>恭喜<color=#c08834>%d</color>,成功将<on click=%d>%d</on>升至最高品质</outline>",
"template": "<color=#ffffff><outline color=#1c613 width=1>恭喜</outline></color><color=#c08834><outline color=#1c613 width=1>%d</outline></color><color=#ffffff><outline color=#1c613 width=1>,成功将</outline></color><color=%d><outline color=%d width=1><on click=%d>%d</on></outline></color><color=#ffffff><outline color=#1c613 width=1>升至最高品质</outline></color>",
"worldchat": "<outline color=#1c1613 width=1>恭喜<color=#c08834>%d</color>,成功将</outline></color><color=%d><outline color=%d width=1><on click=%d>%d</on></outline></color><color=#ffffff><outline color=#1c613 width=1>升至最高品质</outline></color>",
"comments": "1:%d(玩家名)2:%d(装备名)"
},
{
"id": 27,
"name": "LADDER_FIRST_CHANGE",
"mean": "名将擂台第一名换人",
"type": "1&",
"template": "<color=#ffffff><outline color=#1c613 width=1>恭喜</outline></color><color=#c08834><outline color=#1c613 width=1>%d</outline></color><color=#ffffff><outline color=#1c613 width=1>,在名将擂台中攻擂成功,登顶第一</outline></color>",
"worldchat": "<color=#ffffff><outline color=#1c613 width=1>恭喜</outline></color><color=#c08834><outline color=#1c613 width=1>%d</outline></color><color=#ffffff><outline color=#1c613 width=1>,在名将擂台中攻擂成功,登顶第一</outline></color>",
"comments": "1:%d(玩家名)"
}
]

File diff suppressed because it is too large Load Diff

View File

@@ -8,7 +8,8 @@
"lv": 1,
"quality": 1,
"effectCount": 2,
"randomEffect": "90101&90401&90601&90801&90901&91201&91301",
"randomEffect": "90401&90601&90801&90901&91201&91301",
"rareEffect": "90110&",
"mapGoodId": 33001,
"quenchConsume": "17057&1",
"successConsume": "17057&100",
@@ -24,7 +25,8 @@
"lv": 2,
"quality": 1,
"effectCount": 2,
"randomEffect": "90102&90402&90602&90802&90902&91202&91302",
"randomEffect": "90402&90602&90802&90902&91202&91302",
"rareEffect": "90111&",
"mapGoodId": 33002,
"quenchConsume": "17057&2",
"successConsume": "17057&100",
@@ -40,7 +42,8 @@
"lv": 3,
"quality": 2,
"effectCount": 2,
"randomEffect": "90103&90403&90603&90803&90903&91203&91303",
"randomEffect": "90403&90603&90803&90903&91203&91303",
"rareEffect": "90112&",
"mapGoodId": 33003,
"quenchConsume": "17057&4",
"successConsume": "17057&100",
@@ -56,7 +59,8 @@
"lv": 4,
"quality": 2,
"effectCount": 2,
"randomEffect": "90104&90404&90604&90804&90904&91204&91304",
"randomEffect": "90404&90604&90804&90904&91204&91304",
"rareEffect": "90113&",
"mapGoodId": 33004,
"quenchConsume": "17057&6",
"successConsume": "17057&100",
@@ -72,7 +76,8 @@
"lv": 5,
"quality": 3,
"effectCount": 3,
"randomEffect": "90105&90405&90605&90805&90905&91205&91305",
"randomEffect": "90405&90605&90805&90905&91205&91305",
"rareEffect": "90114&",
"mapGoodId": 33005,
"quenchConsume": "17057&8",
"successConsume": "17057&100",
@@ -88,7 +93,8 @@
"lv": 6,
"quality": 3,
"effectCount": 3,
"randomEffect": "90106&90406&90606&90806&90906&91206&91306",
"randomEffect": "90406&90606&90806&90906&91206&91306",
"rareEffect": "90115&",
"mapGoodId": 33006,
"quenchConsume": "17057&12",
"successConsume": "17057&100",
@@ -104,7 +110,8 @@
"lv": 7,
"quality": 4,
"effectCount": 4,
"randomEffect": "90107&90407&90607&90807&90907&91207&91307",
"randomEffect": "90407&90607&90807&90907&91207&91307",
"rareEffect": "90116&",
"mapGoodId": 33007,
"quenchConsume": "17057&16",
"successConsume": "17057&100",
@@ -120,7 +127,8 @@
"lv": 8,
"quality": 4,
"effectCount": 4,
"randomEffect": "90108&90408&90608&90808&90908&91208&91308",
"randomEffect": "90408&90608&90808&90908&91208&91308",
"rareEffect": "90117&",
"mapGoodId": 33008,
"quenchConsume": "17057&24",
"successConsume": "17057&100",
@@ -136,7 +144,8 @@
"lv": 9,
"quality": 5,
"effectCount": 4,
"randomEffect": "90109&90409&90609&90809&90909&91209&91309",
"randomEffect": "90409&90609&90809&90909&91209&91309",
"rareEffect": "90118&",
"mapGoodId": 0,
"quenchConsume": "17057&36",
"successConsume": "17057&100",
@@ -152,7 +161,8 @@
"lv": 1,
"quality": 1,
"effectCount": 2,
"randomEffect": "90201&90401&90601&90801&90901&91001&91101",
"randomEffect": "90401&90601&90801&90901&91001&91101",
"rareEffect": "90210&",
"mapGoodId": 33010,
"quenchConsume": "17057&1",
"successConsume": "17057&100",
@@ -168,7 +178,8 @@
"lv": 2,
"quality": 1,
"effectCount": 2,
"randomEffect": "90202&90402&90602&90802&90902&91002&91102",
"randomEffect": "90402&90602&90802&90902&91002&91102",
"rareEffect": "90211&",
"mapGoodId": 33011,
"quenchConsume": "17057&2",
"successConsume": "17057&100",
@@ -184,7 +195,8 @@
"lv": 3,
"quality": 2,
"effectCount": 2,
"randomEffect": "90203&90403&90603&90803&90903&91003&91103",
"randomEffect": "90403&90603&90803&90903&91003&91103",
"rareEffect": "90212&",
"mapGoodId": 33012,
"quenchConsume": "17057&4",
"successConsume": "17057&100",
@@ -200,7 +212,8 @@
"lv": 4,
"quality": 2,
"effectCount": 2,
"randomEffect": "90204&90404&90604&90804&90904&91004&91104",
"randomEffect": "90404&90604&90804&90904&91004&91104",
"rareEffect": "90213&",
"mapGoodId": 33013,
"quenchConsume": "17057&6",
"successConsume": "17057&100",
@@ -216,7 +229,8 @@
"lv": 5,
"quality": 3,
"effectCount": 3,
"randomEffect": "90205&90405&90605&90805&90905&91005&91105",
"randomEffect": "90405&90605&90805&90905&91005&91105",
"rareEffect": "90214&",
"mapGoodId": 33014,
"quenchConsume": "17057&8",
"successConsume": "17057&100",
@@ -232,7 +246,8 @@
"lv": 6,
"quality": 3,
"effectCount": 3,
"randomEffect": "90206&90406&90606&90806&90906&91006&91106",
"randomEffect": "90406&90606&90806&90906&91006&91106",
"rareEffect": "90215&",
"mapGoodId": 33015,
"quenchConsume": "17057&12",
"successConsume": "17057&100",
@@ -248,7 +263,8 @@
"lv": 7,
"quality": 4,
"effectCount": 4,
"randomEffect": "90207&90407&90607&90807&90907&91007&91107",
"randomEffect": "90407&90607&90807&90907&91007&91107",
"rareEffect": "90216&",
"mapGoodId": 33016,
"quenchConsume": "17057&16",
"successConsume": "17057&100",
@@ -264,7 +280,8 @@
"lv": 8,
"quality": 4,
"effectCount": 4,
"randomEffect": "90208&90408&90608&90808&90908&91008&91108",
"randomEffect": "90408&90608&90808&90908&91008&91108",
"rareEffect": "90217&",
"mapGoodId": 33017,
"quenchConsume": "17057&24",
"successConsume": "17057&100",
@@ -280,7 +297,8 @@
"lv": 9,
"quality": 5,
"effectCount": 4,
"randomEffect": "90209&90409&90609&90809&90909&91009&91109",
"randomEffect": "90409&90609&90809&90909&91009&91109",
"rareEffect": "90218&",
"mapGoodId": 0,
"quenchConsume": "17057&36",
"successConsume": "17057&100",
@@ -296,7 +314,8 @@
"lv": 1,
"quality": 1,
"effectCount": 2,
"randomEffect": "90301&90501&90701&90801&90901&91001&91101",
"randomEffect": "90501&90701&90801&90901&91001&91101",
"rareEffect": "90310&",
"mapGoodId": 33019,
"quenchConsume": "17057&1",
"successConsume": "17057&100",
@@ -312,7 +331,8 @@
"lv": 2,
"quality": 1,
"effectCount": 2,
"randomEffect": "90302&90502&90702&90802&90902&91002&91102",
"randomEffect": "90502&90702&90802&90902&91002&91102",
"rareEffect": "90311&",
"mapGoodId": 33020,
"quenchConsume": "17057&2",
"successConsume": "17057&100",
@@ -328,7 +348,8 @@
"lv": 3,
"quality": 2,
"effectCount": 2,
"randomEffect": "90303&90503&90703&90803&90903&91003&91103",
"randomEffect": "90503&90703&90803&90903&91003&91103",
"rareEffect": "90312&",
"mapGoodId": 33021,
"quenchConsume": "17057&4",
"successConsume": "17057&100",
@@ -344,7 +365,8 @@
"lv": 4,
"quality": 2,
"effectCount": 2,
"randomEffect": "90304&90504&90704&90804&90904&91004&91104",
"randomEffect": "90504&90704&90804&90904&91004&91104",
"rareEffect": "90313&",
"mapGoodId": 33022,
"quenchConsume": "17057&6",
"successConsume": "17057&100",
@@ -360,7 +382,8 @@
"lv": 5,
"quality": 3,
"effectCount": 3,
"randomEffect": "90305&90505&90705&90805&90905&91005&91105",
"randomEffect": "90505&90705&90805&90905&91005&91105",
"rareEffect": "90314&",
"mapGoodId": 33023,
"quenchConsume": "17057&8",
"successConsume": "17057&100",
@@ -376,7 +399,8 @@
"lv": 6,
"quality": 3,
"effectCount": 3,
"randomEffect": "90306&90506&90706&90806&90906&91006&91106",
"randomEffect": "90506&90706&90806&90906&91006&91106",
"rareEffect": "90315&",
"mapGoodId": 33024,
"quenchConsume": "17057&12",
"successConsume": "17057&100",
@@ -392,7 +416,8 @@
"lv": 7,
"quality": 4,
"effectCount": 4,
"randomEffect": "90307&90507&90707&90807&90907&91007&91107",
"randomEffect": "90507&90707&90807&90907&91007&91107",
"rareEffect": "90316&",
"mapGoodId": 33025,
"quenchConsume": "17057&16",
"successConsume": "17057&100",
@@ -408,7 +433,8 @@
"lv": 8,
"quality": 4,
"effectCount": 4,
"randomEffect": "90308&90508&90708&90808&90908&91008&91108",
"randomEffect": "90508&90708&90808&90908&91008&91108",
"rareEffect": "90317&",
"mapGoodId": 33026,
"quenchConsume": "17057&24",
"successConsume": "17057&100",
@@ -424,7 +450,8 @@
"lv": 9,
"quality": 5,
"effectCount": 4,
"randomEffect": "90309&90509&90709&90809&90909&91009&91109",
"randomEffect": "90509&90709&90809&90909&91009&91109",
"rareEffect": "90318&",
"mapGoodId": 0,
"quenchConsume": "17057&36",
"successConsume": "17057&100",
@@ -440,7 +467,8 @@
"lv": 1,
"quality": 1,
"effectCount": 2,
"randomEffect": "90001&90501&90701&90801&90901&91001&91101",
"randomEffect": "90501&90701&90801&90901&91001&91101",
"rareEffect": "90010&",
"mapGoodId": 33028,
"quenchConsume": "17057&1",
"successConsume": "17057&100",
@@ -456,7 +484,8 @@
"lv": 2,
"quality": 1,
"effectCount": 2,
"randomEffect": "90002&90502&90702&90802&90902&91002&91102",
"randomEffect": "90502&90702&90802&90902&91002&91102",
"rareEffect": "90011&",
"mapGoodId": 33029,
"quenchConsume": "17057&2",
"successConsume": "17057&100",
@@ -472,7 +501,8 @@
"lv": 3,
"quality": 2,
"effectCount": 2,
"randomEffect": "90003&90503&90703&90803&90903&91003&91103",
"randomEffect": "90503&90703&90803&90903&91003&91103",
"rareEffect": "90012&",
"mapGoodId": 33030,
"quenchConsume": "17057&4",
"successConsume": "17057&100",
@@ -488,7 +518,8 @@
"lv": 4,
"quality": 2,
"effectCount": 2,
"randomEffect": "90004&90504&90704&90804&90904&91004&91104",
"randomEffect": "90504&90704&90804&90904&91004&91104",
"rareEffect": "90013&",
"mapGoodId": 33031,
"quenchConsume": "17057&6",
"successConsume": "17057&100",
@@ -504,7 +535,8 @@
"lv": 5,
"quality": 3,
"effectCount": 3,
"randomEffect": "90005&90505&90705&90805&90905&91005&91105",
"randomEffect": "90505&90705&90805&90905&91005&91105",
"rareEffect": "90014&",
"mapGoodId": 33032,
"quenchConsume": "17057&8",
"successConsume": "17057&100",
@@ -520,7 +552,8 @@
"lv": 6,
"quality": 3,
"effectCount": 3,
"randomEffect": "90006&90506&90706&90806&90906&91006&91106",
"randomEffect": "90506&90706&90806&90906&91006&91106",
"rareEffect": "90015&",
"mapGoodId": 33033,
"quenchConsume": "17057&12",
"successConsume": "17057&100",
@@ -536,7 +569,8 @@
"lv": 7,
"quality": 4,
"effectCount": 4,
"randomEffect": "90007&90507&90707&90807&90907&91007&91107",
"randomEffect": "90507&90707&90807&90907&91007&91107",
"rareEffect": "90016&",
"mapGoodId": 33034,
"quenchConsume": "17057&16",
"successConsume": "17057&100",
@@ -552,7 +586,8 @@
"lv": 8,
"quality": 4,
"effectCount": 4,
"randomEffect": "90008&90508&90708&90808&90908&91008&91108",
"randomEffect": "90508&90708&90808&90908&91008&91108",
"rareEffect": "90017&",
"mapGoodId": 33035,
"quenchConsume": "17057&24",
"successConsume": "17057&100",
@@ -568,7 +603,8 @@
"lv": 9,
"quality": 5,
"effectCount": 4,
"randomEffect": "90009&90509&90709&90809&90909&91009&91109",
"randomEffect": "90509&90709&90809&90909&91009&91109",
"rareEffect": "90018&",
"mapGoodId": 0,
"quenchConsume": "17057&36",
"successConsume": "17057&100",

View File

@@ -0,0 +1,112 @@
[
{
"id": 1,
"rankMin": 0,
"rankMax": 1,
"rangeBeforeMax": 0,
"rangeAfterMax": 0,
"rangeBeforeNum": 0,
"rangeAfterNum": 0,
"interval": "0&0",
"topChallenge": 1
},
{
"id": 2,
"rankMin": 1,
"rankMax": 10,
"rangeBeforeMax": 0,
"rangeAfterMax": 1,
"rangeBeforeNum": 0,
"rangeAfterNum": 1,
"interval": "0&0",
"topChallenge": 1
},
{
"id": 3,
"rankMin": 10,
"rankMax": 20,
"rangeBeforeMax": 5,
"rangeAfterMax": 1,
"rangeBeforeNum": 3,
"rangeAfterNum": 1,
"interval": "0&5",
"topChallenge": 1
},
{
"id": 4,
"rankMin": 20,
"rankMax": 50,
"rangeBeforeMax": 10,
"rangeAfterMax": 1,
"rangeBeforeNum": 5,
"rangeAfterNum": 1,
"interval": "0&5",
"topChallenge": 0
},
{
"id": 5,
"rankMin": 50,
"rankMax": 100,
"rangeBeforeMax": 30,
"rangeAfterMax": 5,
"rangeBeforeNum": 5,
"rangeAfterNum": 1,
"interval": "3&6",
"topChallenge": 0
},
{
"id": 6,
"rankMin": 100,
"rankMax": 200,
"rangeBeforeMax": 50,
"rangeAfterMax": 10,
"rangeBeforeNum": 5,
"rangeAfterNum": 1,
"interval": "5&10",
"topChallenge": 0
},
{
"id": 7,
"rankMin": 200,
"rankMax": 500,
"rangeBeforeMax": 100,
"rangeAfterMax": 50,
"rangeBeforeNum": 5,
"rangeAfterNum": 1,
"interval": "10&20",
"topChallenge": 0
},
{
"id": 8,
"rankMin": 500,
"rankMax": 1000,
"rangeBeforeMax": 200,
"rangeAfterMax": 100,
"rangeBeforeNum": 5,
"rangeAfterNum": 1,
"interval": "20&40",
"topChallenge": 0
},
{
"id": 9,
"rankMin": 1000,
"rankMax": 2000,
"rangeBeforeMax": 300,
"rangeAfterMax": 100,
"rangeBeforeNum": 5,
"rangeAfterNum": 1,
"interval": "30&60",
"topChallenge": 0
},
{
"id": 10,
"rankMin": 2000,
"rankMax": -1,
"rangeBeforeMax": 500,
"rangeAfterMax": 100,
"rangeBeforeNum": 5,
"rangeAfterNum": 1,
"interval": "50&100",
"topChallenge": 0
}
]

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,56 @@
[
{
"id": 1,
"rankMin": 0,
"rankMax": 1,
"reward": "31002&900|31001&5000"
},
{
"id": 2,
"rankMin": 1,
"rankMax": 10,
"reward": "31002&800|31001&4000"
},
{
"id": 3,
"rankMin": 10,
"rankMax": 50,
"reward": "31002&700|31001&3000"
},
{
"id": 4,
"rankMin": 50,
"rankMax": 100,
"reward": "31002&600|31001&2000"
},
{
"id": 5,
"rankMin": 100,
"rankMax": 200,
"reward": "31002&500|31001&2000"
},
{
"id": 6,
"rankMin": 200,
"rankMax": 500,
"reward": "31002&400|31001&2000"
},
{
"id": 7,
"rankMin": 500,
"rankMax": 1000,
"reward": "31002&300|31001&2000"
},
{
"id": 8,
"rankMin": 1000,
"rankMax": 2000,
"reward": "31002&200|31001&2000"
},
{
"id": 9,
"rankMin": 2000,
"rankMax": 3000,
"reward": "31002&100|31001&1000"
}
]

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@@ -43,7 +43,7 @@
},
{
"id": 7,
"name": "主线推进 ",
"name": "主线推进",
"group": 2,
"groupName": "玩法",
"general": 0
@@ -61,5 +61,12 @@
"group": 2,
"groupName": "玩法",
"general": 0
},
{
"id": 10,
"name": "名将擂台",
"group": 2,
"groupName": "玩法",
"general": 0
}
]

View File

@@ -386,7 +386,7 @@
"param": "count&point&",
"string": "领取次数&宝箱的积分&",
"content": "point领取宝箱消耗的点数",
"condition": "count",
"condition": "warcount",
"sumType": 2
},
{
@@ -1005,8 +1005,58 @@
"info": "军团元宝捐献x次",
"param": "count&",
"string": "捐献次数&",
"content": "捐献",
"content": "只算有元宝的捐献",
"condition": "count",
"sumType": 2
},
{
"id": 121,
"name": "名将擂台",
"info": "名将擂台挑战X次",
"param": "count&",
"string": "挑战次数&",
"content": "含扫荡(扣挑战次数就算)",
"condition": "count",
"sumType": 2
},
{
"id": 122,
"name": "名将擂台",
"info": "名将擂台胜利X次",
"param": "count&",
"string": "胜利次数&",
"content": "含扫荡",
"condition": "count",
"sumType": 2
},
{
"id": 123,
"name": "名将擂台",
"info": "名将擂台排名进入前X名",
"param": "rank&count&",
"string": "名次&条件",
"content": 0,
"condition": "count",
"sumType": 1
},
{
"id": 124,
"name": "武将",
"info": "单个武将其中最高等级的羁绊达X级",
"param": "level&",
"string": "等级&",
"content": 0,
"condition": "level&",
"sumType": 1
},
{
"id": 125,
"name": "武将",
"info": "单个武将3条羁绊总共提升X级",
"param": "level&",
"string": "等级&",
"content": 0,
"condition": "level&",
"sumType": 1
}
]

View File

@@ -0,0 +1,278 @@
[
{
"warId": 4601,
"actorName": "我军",
"actorId": 0,
"dataId": 0,
"relation": 0,
"outIndex": 0,
"dirction": 1,
"x": 6,
"y": 7,
"var": 501,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4601,
"actorName": "我军",
"actorId": 0,
"dataId": 0,
"relation": 0,
"outIndex": 1,
"dirction": 1,
"x": 6,
"y": 6,
"var": 502,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4601,
"actorName": "我军",
"actorId": 0,
"dataId": 0,
"relation": 0,
"outIndex": 2,
"dirction": 1,
"x": 5,
"y": 8,
"var": 503,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4601,
"actorName": "我军",
"actorId": 0,
"dataId": 0,
"relation": 0,
"outIndex": 3,
"dirction": 1,
"x": 5,
"y": 5,
"var": 504,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4601,
"actorName": "我军",
"actorId": 0,
"dataId": 0,
"relation": 0,
"outIndex": 4,
"dirction": 1,
"x": 4,
"y": 8,
"var": 505,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4601,
"actorName": "我军",
"actorId": 0,
"dataId": 0,
"relation": 0,
"outIndex": 5,
"dirction": 1,
"x": 4,
"y": 5,
"var": 506,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4601,
"actorName": "典韦",
"actorId": 307,
"dataId": 2001,
"relation": 2,
"outIndex": 0,
"dirction": 0,
"x": 11,
"y": 7,
"var": 2001,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4601,
"actorName": "文丑",
"actorId": 355,
"dataId": 2002,
"relation": 2,
"outIndex": 1,
"dirction": 0,
"x": 11,
"y": 6,
"var": 2002,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4601,
"actorName": "颜良",
"actorId": 354,
"dataId": 2003,
"relation": 2,
"outIndex": 2,
"dirction": 0,
"x": 12,
"y": 8,
"var": 2003,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4601,
"actorName": "孙乾",
"actorId": 331,
"dataId": 2004,
"relation": 2,
"outIndex": 3,
"dirction": 0,
"x": 12,
"y": 5,
"var": 2004,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4601,
"actorName": "蔡琰",
"actorId": 313,
"dataId": 2005,
"relation": 2,
"outIndex": 4,
"dirction": 0,
"x": 13,
"y": 8,
"var": 2005,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4601,
"actorName": "麹义",
"actorId": 350,
"dataId": 2006,
"relation": 2,
"outIndex": 5,
"dirction": 0,
"x": 13,
"y": 5,
"var": 2006,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
}
]

View File

@@ -0,0 +1,278 @@
[
{
"warId": 4602,
"actorName": "我军",
"actorId": 0,
"dataId": 0,
"relation": 0,
"outIndex": 0,
"dirction": 1,
"x": 6,
"y": 7,
"var": 501,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4602,
"actorName": "我军",
"actorId": 0,
"dataId": 0,
"relation": 0,
"outIndex": 1,
"dirction": 1,
"x": 6,
"y": 6,
"var": 502,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4602,
"actorName": "我军",
"actorId": 0,
"dataId": 0,
"relation": 0,
"outIndex": 2,
"dirction": 1,
"x": 5,
"y": 8,
"var": 503,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4602,
"actorName": "我军",
"actorId": 0,
"dataId": 0,
"relation": 0,
"outIndex": 3,
"dirction": 1,
"x": 5,
"y": 5,
"var": 504,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4602,
"actorName": "我军",
"actorId": 0,
"dataId": 0,
"relation": 0,
"outIndex": 4,
"dirction": 1,
"x": 4,
"y": 8,
"var": 505,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4602,
"actorName": "我军",
"actorId": 0,
"dataId": 0,
"relation": 0,
"outIndex": 5,
"dirction": 1,
"x": 4,
"y": 5,
"var": 506,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4602,
"actorName": "潘璋",
"actorId": 360,
"dataId": 2001,
"relation": 2,
"outIndex": 0,
"dirction": 0,
"x": 11,
"y": 7,
"var": 2001,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4602,
"actorName": "乐进",
"actorId": 316,
"dataId": 2002,
"relation": 2,
"outIndex": 1,
"dirction": 0,
"x": 11,
"y": 6,
"var": 2002,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4602,
"actorName": "凌统",
"actorId": 361,
"dataId": 2003,
"relation": 2,
"outIndex": 2,
"dirction": 0,
"x": 12,
"y": 8,
"var": 2003,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4602,
"actorName": "郭嘉",
"actorId": 305,
"dataId": 2004,
"relation": 2,
"outIndex": 3,
"dirction": 0,
"x": 12,
"y": 5,
"var": 2004,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4602,
"actorName": "孙权",
"actorId": 336,
"dataId": 2005,
"relation": 2,
"outIndex": 4,
"dirction": 0,
"x": 13,
"y": 8,
"var": 2005,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4602,
"actorName": "李典",
"actorId": 312,
"dataId": 2006,
"relation": 2,
"outIndex": 5,
"dirction": 0,
"x": 13,
"y": 5,
"var": 2006,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
}
]

View File

@@ -0,0 +1,278 @@
[
{
"warId": 4603,
"actorName": "我军",
"actorId": 0,
"dataId": 0,
"relation": 0,
"outIndex": 0,
"dirction": 1,
"x": 6,
"y": 7,
"var": 501,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4603,
"actorName": "我军",
"actorId": 0,
"dataId": 0,
"relation": 0,
"outIndex": 1,
"dirction": 1,
"x": 6,
"y": 6,
"var": 502,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4603,
"actorName": "我军",
"actorId": 0,
"dataId": 0,
"relation": 0,
"outIndex": 2,
"dirction": 1,
"x": 5,
"y": 8,
"var": 503,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4603,
"actorName": "我军",
"actorId": 0,
"dataId": 0,
"relation": 0,
"outIndex": 3,
"dirction": 1,
"x": 5,
"y": 5,
"var": 504,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4603,
"actorName": "我军",
"actorId": 0,
"dataId": 0,
"relation": 0,
"outIndex": 4,
"dirction": 1,
"x": 4,
"y": 8,
"var": 505,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4603,
"actorName": "我军",
"actorId": 0,
"dataId": 0,
"relation": 0,
"outIndex": 5,
"dirction": 1,
"x": 4,
"y": 5,
"var": 506,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4603,
"actorName": "典韦",
"actorId": 307,
"dataId": 2001,
"relation": 2,
"outIndex": 0,
"dirction": 0,
"x": 11,
"y": 7,
"var": 2001,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4603,
"actorName": "文丑",
"actorId": 355,
"dataId": 2002,
"relation": 2,
"outIndex": 1,
"dirction": 0,
"x": 11,
"y": 6,
"var": 2002,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4603,
"actorName": "颜良",
"actorId": 354,
"dataId": 2003,
"relation": 2,
"outIndex": 2,
"dirction": 0,
"x": 12,
"y": 8,
"var": 2003,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4603,
"actorName": "郭嘉",
"actorId": 305,
"dataId": 2004,
"relation": 2,
"outIndex": 3,
"dirction": 0,
"x": 12,
"y": 5,
"var": 2004,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4603,
"actorName": "孙权",
"actorId": 333,
"dataId": 2005,
"relation": 2,
"outIndex": 4,
"dirction": 0,
"x": 13,
"y": 8,
"var": 2005,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4603,
"actorName": "太史慈",
"actorId": 335,
"dataId": 2006,
"relation": 2,
"outIndex": 5,
"dirction": 0,
"x": 13,
"y": 5,
"var": 2006,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
}
]

View File

@@ -0,0 +1,278 @@
[
{
"warId": 4604,
"actorName": "我军",
"actorId": 0,
"dataId": 0,
"relation": 0,
"outIndex": 0,
"dirction": 1,
"x": 6,
"y": 7,
"var": 501,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4604,
"actorName": "我军",
"actorId": 0,
"dataId": 0,
"relation": 0,
"outIndex": 1,
"dirction": 1,
"x": 6,
"y": 6,
"var": 502,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4604,
"actorName": "我军",
"actorId": 0,
"dataId": 0,
"relation": 0,
"outIndex": 2,
"dirction": 1,
"x": 5,
"y": 8,
"var": 503,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4604,
"actorName": "我军",
"actorId": 0,
"dataId": 0,
"relation": 0,
"outIndex": 3,
"dirction": 1,
"x": 5,
"y": 5,
"var": 504,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4604,
"actorName": "我军",
"actorId": 0,
"dataId": 0,
"relation": 0,
"outIndex": 4,
"dirction": 1,
"x": 4,
"y": 8,
"var": 505,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4604,
"actorName": "我军",
"actorId": 0,
"dataId": 0,
"relation": 0,
"outIndex": 5,
"dirction": 1,
"x": 4,
"y": 5,
"var": 506,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4604,
"actorName": "庞舞",
"actorId": 352,
"dataId": 2001,
"relation": 2,
"outIndex": 0,
"dirction": 0,
"x": 11,
"y": 7,
"var": 2001,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4604,
"actorName": "文丑",
"actorId": 354,
"dataId": 2002,
"relation": 2,
"outIndex": 1,
"dirction": 0,
"x": 11,
"y": 6,
"var": 2002,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4604,
"actorName": "颜良",
"actorId": 355,
"dataId": 2003,
"relation": 2,
"outIndex": 2,
"dirction": 0,
"x": 12,
"y": 8,
"var": 2003,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4604,
"actorName": "郭嘉",
"actorId": 305,
"dataId": 2004,
"relation": 2,
"outIndex": 3,
"dirction": 0,
"x": 12,
"y": 5,
"var": 2004,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4604,
"actorName": "华佗",
"actorId": 346,
"dataId": 2005,
"relation": 2,
"outIndex": 4,
"dirction": 0,
"x": 13,
"y": 8,
"var": 2005,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4604,
"actorName": "王平",
"actorId": 330,
"dataId": 2006,
"relation": 2,
"outIndex": 5,
"dirction": 0,
"x": 13,
"y": 5,
"var": 2006,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
}
]

View File

@@ -0,0 +1,278 @@
[
{
"warId": 4605,
"actorName": "我军",
"actorId": 0,
"dataId": 0,
"relation": 0,
"outIndex": 0,
"dirction": 1,
"x": 6,
"y": 7,
"var": 501,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4605,
"actorName": "我军",
"actorId": 0,
"dataId": 0,
"relation": 0,
"outIndex": 1,
"dirction": 1,
"x": 6,
"y": 6,
"var": 502,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4605,
"actorName": "我军",
"actorId": 0,
"dataId": 0,
"relation": 0,
"outIndex": 2,
"dirction": 1,
"x": 5,
"y": 8,
"var": 503,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4605,
"actorName": "我军",
"actorId": 0,
"dataId": 0,
"relation": 0,
"outIndex": 3,
"dirction": 1,
"x": 5,
"y": 5,
"var": 504,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4605,
"actorName": "我军",
"actorId": 0,
"dataId": 0,
"relation": 0,
"outIndex": 4,
"dirction": 1,
"x": 4,
"y": 8,
"var": 505,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4605,
"actorName": "我军",
"actorId": 0,
"dataId": 0,
"relation": 0,
"outIndex": 5,
"dirction": 1,
"x": 4,
"y": 5,
"var": 506,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4605,
"actorName": "黄盖",
"actorId": 362,
"dataId": 2001,
"relation": 2,
"outIndex": 0,
"dirction": 0,
"x": 11,
"y": 7,
"var": 2001,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4605,
"actorName": "周泰",
"actorId": 332,
"dataId": 2002,
"relation": 2,
"outIndex": 1,
"dirction": 0,
"x": 11,
"y": 6,
"var": 2002,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4605,
"actorName": "马云禄",
"actorId": 327,
"dataId": 2003,
"relation": 2,
"outIndex": 2,
"dirction": 0,
"x": 12,
"y": 8,
"var": 2003,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4605,
"actorName": "孙乾",
"actorId": 331,
"dataId": 2004,
"relation": 2,
"outIndex": 3,
"dirction": 0,
"x": 12,
"y": 5,
"var": 2004,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4605,
"actorName": "小乔",
"actorId": 340,
"dataId": 2005,
"relation": 2,
"outIndex": 4,
"dirction": 0,
"x": 13,
"y": 8,
"var": 2005,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
},
{
"warId": 4605,
"actorName": "麹义",
"actorId": 350,
"dataId": 2006,
"relation": 2,
"outIndex": 5,
"dirction": 0,
"x": 13,
"y": 5,
"var": 2006,
"lv": 1,
"hide": 0,
"initial_ai": 1,
"bossid": 0,
"attribute": "&",
"skill": "&",
"seid": "&",
"star": 0,
"quality": 0,
"jobLv": 0,
"spine": 0
}
]