diff --git a/game-server/app/servers/guild/handler/gateActivityHandler.ts b/game-server/app/servers/guild/handler/gateActivityHandler.ts new file mode 100644 index 000000000..95f9e2108 --- /dev/null +++ b/game-server/app/servers/guild/handler/gateActivityHandler.ts @@ -0,0 +1,185 @@ +import { Application, BackendSession, ChannelService } from "pinus"; +import { setMedianCe, getMedianCe, getGuildActivityStatus, getRecordScore } from "../../../services/guildActivityService"; +import { resResult } from "../../../pubUtils/util"; +import { STATUS, GUILD_ACTIVITY_TYPE } from "../../../consts"; +import { GameModel } from "../../../db/Game"; +import { ServerlistModel } from "../../../db/Serverlist"; +import { UserGuildActivityRecModel, Record } from "../../../db/UserGuildActivityRec"; +import { UserGuildModel } from "../../../db/UserGuild"; +import { GUILDACTIVITY } from "../../../pubUtils/dicParam"; +import { GuildActivityRecordModel } from "../../../db/GuildActivityRec"; +import { RoleModel, RoleType } from "../../../db/Role"; +import { GuildModel } from "../../../db/Guild"; +import { RankParam, GuildRankParam } from "../../../domain/rank"; +import { setRank, getGuildKeyName, getRankScore, getRank, getUnionRank } from "../../../services/redisService"; +import { REDIS_KEY } from "../../../consts"; + +export default function (app: Application) { + return new GateActivityHandler(app); +} + +export class GateActivityHandler { + channelService: ChannelService; + constructor(private app: Application) { + this.channelService = app.get('channelService'); + } + + private gateHp: Map = new Map(); // 城门血条,每个军团有一条血条 + private members: Map> = new Map(); // 每个军团参与的成员 + private aid = GUILD_ACTIVITY_TYPE.GATE_ACTIVITY; // 蛮夷入侵id + + // 进入蛮夷入侵界面 + async getGateActivity(msg: {}, session: BackendSession) { + + const roleId = session.get('roleId'); + const roleName = session.get('roleName'); + const serverId = session.get('serverId'); + const guildCode = session.get('guildCode'); + + let statusResult = getGuildActivityStatus(this.aid); + if(!statusResult) return resResult(STATUS.DIC_DATA_NOT_FOUND); + + let guildActivityRec = await GuildActivityRecordModel.getRecord(guildCode, serverId, this.aid); + if(!guildActivityRec) return resResult(STATUS.INTERNAL_ERR); + + let { code: sourceCode } = guildActivityRec; + let myGuildActivityRec = await UserGuildActivityRecModel.getRecord(roleId, roleName, guildCode, serverId, sourceCode, [], this.aid); + let { challengeCnt } = myGuildActivityRec; + + // TODO 处理多余字段 + let {ranks: guildRank, myRank: myGuildRank} = await getUnionRank(REDIS_KEY.GUILD_ACTIVITY, serverId, guildCode, 10); + let {ranks: memberRank, myRank: myMemberRank} = await getRank(getGuildKeyName(REDIS_KEY.USER_GUILD_ACTIVITY, guildCode), serverId, roleId); + if(!myGuildRank) { + let guild = await GuildModel.findByCode(guildCode, serverId, 'name'); + myGuildRank = { + rank: 0, + code: guildCode, + name: guild.name, + num: 0 + } + } + if(!myMemberRank) { + myMemberRank = { + rank: 0, + roleId, + roleName, + num: 0 + } + } + + return resResult(STATUS.SUCCESS, { + ...statusResult, + challengeCnt: GUILDACTIVITY.GATEACTIVITY_CHALLENGE_TIMES - challengeCnt, + guildRank, myGuildRank, + memberRank, myMemberRank + }) + } + + // 开启挑战 + async checkBattle(msg: { heroes: number[] }, session: BackendSession) { + let { heroes } = msg; + if(!heroes || heroes.length <= 0) return resResult(STATUS.WRONG_PARMS); + + const roleId = session.get('roleId'); + const roleName = session.get('roleName'); + const serverId = session.get('serverId'); + const guildCode = session.get('guildCode'); + if(!guildCode) return resResult(STATUS.GUILD_AUTH_NOT_ENOUGH); + + let statusResult = getGuildActivityStatus(this.aid); + if(!statusResult) return resResult(STATUS.DIC_DATA_NOT_FOUND); + + let guildActivityRec = await GuildActivityRecordModel.getRecord(guildCode, serverId, this.aid); + if(!guildActivityRec) return resResult(STATUS.INTERNAL_ERR); + + let { code: sourceCode } = guildActivityRec; + let myGuildActivityRec = await UserGuildActivityRecModel.getRecord(roleId, roleName, guildCode, serverId, sourceCode, heroes, this.aid); + let { code, challengeCnt } = myGuildActivityRec; + + // 返回当前军团总军功 + let guildScore = await getRankScore(REDIS_KEY.GUILD_ACTIVITY, serverId, guildCode); + + // 城门血条 + let gateHp = this.gateHp.get(guildCode)||GUILDACTIVITY.GATEACTIVITY_GATEHP; + // 前一天中位数战力 + let medianCe = await getMedianCe(serverId); + + return resResult(STATUS.SUCCESS, { + code, + ...statusResult, + guildScore: guildScore||0, + myScore: 0, + gateHp, + challengeCnt: GUILDACTIVITY.GATEACTIVITY_CHALLENGE_TIMES - challengeCnt, + medianCe + }); + } + + // 上报玩家获得军功 + async action(msg: { code: string, record: { round: number, dataId: number }[] }, session: BackendSession) { + let roleId = session.get('roleId'); + let roleName = session.get('roleName'); + let guildCode = session.get('guildCode'); + let serverId = session.get('serverId'); + + let { code, record } = msg; + + let statusResult = getGuildActivityStatus(this.aid); + if(!statusResult) return resResult(STATUS.DIC_DATA_NOT_FOUND); + + // 计算record内得分 + // TODO 防刷分,应加入记录玩家操作 + let scoreResult = getRecordScore(this.aid, record); + if(!scoreResult) return resResult(STATUS.DIC_DATA_NOT_FOUND); + let { score, newRecords } = scoreResult; + + // 更新redis数据 + let role = await RoleModel.findByRoleId(roleId); + let { lv, vLv, head, frame, spine, title } = role; + let userParam = new RankParam(roleName, lv, vLv, head, frame, spine, title); + let myScore = await setRank(getGuildKeyName(REDIS_KEY.USER_GUILD_ACTIVITY, guildCode), serverId, guildCode, score, Date.now(), userParam); + + let guild = await GuildModel.findByCode(guildCode, serverId); + let leader = guild.leader; + let params = new GuildRankParam(guild.icon, guild.name, guild.lv, leader); + let guildScore = await setRank(REDIS_KEY.GUILD_ACTIVITY, serverId, guild.code, 0, Date.now(), params, true); + + // 更新数据库 + let rec = await UserGuildActivityRecModel.pushRecord(code, newRecords); + let gateHp = this.gateHp.get(guildCode)||GUILDACTIVITY.GATEACTIVITY_GATEHP; + + return resResult(STATUS.SUCCESS, { + code: rec.code, + ...statusResult, + guildScore: guildScore||0, + myScore: myScore||0, + gateHp + }); + } + + // 上报敌军攻打城门情况 + async hitGate(msg: {}, session: BackendSession) { + + } + + // 结束挑战 + async battleEnd(msg: { count: number }, session: BackendSession) { + + } + + + // TODO debug接口,可以不用 + async newServer() { + let serverlist = await GameModel.getAllServerList(); + for(let server of serverlist) { + + await ServerlistModel.newServer(server.id, server.serverType, {...server}); + } + } + + async test() { + + let result = await setMedianCe(); + return resResult(STATUS.SUCCESS, {result}); + } +} diff --git a/game-server/app/services/guildActivityService.ts b/game-server/app/services/guildActivityService.ts new file mode 100644 index 000000000..5f9f6c73c --- /dev/null +++ b/game-server/app/services/guildActivityService.ts @@ -0,0 +1,106 @@ +import { ServerlistModel } from "../db/Serverlist"; +import { RoleModel } from "../db/Role"; +import { reduceCe } from "../pubUtils/util"; +import { GUILDACTIVITY } from "../pubUtils/dicParam"; +import { gameData } from "../pubUtils/data"; +import { getCurHourPoint, getCutDay, nowSeconds } from "../pubUtils/timeUtil"; +import { GUILD_ACTIVITY_STATUS } from "../consts"; +import { Record } from "../db/UserGuildActivityRec"; + +/** + * 定时任务,获得前一天的活跃玩家中位数武将战力 + */ +export async function setMedianCe() { + let servers = await ServerlistModel.getAllServerList(); + + for(let server of servers) { + if(server.serverType == 'official') { + let medianRole = await RoleModel.getMedianRole(server.id); + if(!medianRole) { + await ServerlistModel.updateByServerId(server.id, { medianCe: GUILDACTIVITY.GATEACTIVITY_ENEMYCE }); + } else { + let { topLineup } = medianRole; + let topHero = topLineup.sort((a, b) => b.ce - a.ce); + let medianCe = reduceCe(topHero[0].ce); + await ServerlistModel.updateByServerId(server.id, { medianCe }); + } + } + } + return servers; +} + + +/** + * 获得本服前一天活跃玩家中位数玩家的最强武将战力 + * @param serverId + */ +export async function getMedianCe(serverId: number) { + let server = await ServerlistModel.findByServerId(serverId); + if(server) { + return server.medianCe; + } + return GUILDACTIVITY.GATEACTIVITY_ENEMYCE; +} + +/** + * 根据当前时间判断军团活动倒计时 + * @param id 军团活动id + */ +export function getGuildActivityStatus(id: number) { + let dicGuildActivity = gameData.guildActivity.get(id); + if(!dicGuildActivity) return false; + + let startTime = getCurHourPoint(dicGuildActivity.startTime); + let countdownTime = getCurHourPoint(dicGuildActivity.countDown); + let { duringTime, openDay } = dicGuildActivity; + let endTime = startTime + duringTime; + + let status = 0, time = 0; + let now = nowSeconds(); + if(now >= countdownTime && now < startTime) { + status = GUILD_ACTIVITY_STATUS.WAITING; + time = startTime - now; + } else if (now >= startTime && now < endTime) { + status = GUILD_ACTIVITY_STATUS.START; + time = endTime - now; + } else { + status = GUILD_ACTIVITY_STATUS.END; + } + + let day = getCutDay(); + let isOpen = openDay.includes(day); + + return { + status, time, isOpen + } +} + +export function getRecordScore(aid: number, record: { round: number, dataId: number }[]) { + let dicGuildActivity = gameData.guildActivity.get(aid); + console.log('xxxx', dicGuildActivity.warid) + if(!dicGuildActivity) return false; + + let dicWarJson = gameData.warJson.get(dicGuildActivity.warid); + if(!dicWarJson) return false; + + let curRound = record[0]?.round; + let sum = 0, newRecords = new Array() + for(let {round, dataId} of record) { + let enemy = dicWarJson.find(cur => cur.dataId == dataId); + console.log('yyyy', enemy.enemyType); + let enemyType = enemy.enemyType; + let score = gameData.gateActivityPoint.get(enemyType); + newRecords.push({ round, dataId, score}); + sum += score; + console.log('***%*****', sum, score) + if(round != curRound) { // 每回合开始得10分 + sum += gameData.gateActivityPoint.get(1); + console.log('***%%*****', sum) + curRound = round; + } + } + console.log('***&**', sum) + + + return { score: sum, newRecords } +} \ No newline at end of file diff --git a/game-server/app/services/redisService.ts b/game-server/app/services/redisService.ts index f53d5befe..8d8bacdd9 100644 --- a/game-server/app/services/redisService.ts +++ b/game-server/app/services/redisService.ts @@ -10,7 +10,7 @@ import { SystemConfigModel } from '../db/SystemConfig'; import { GuildRankParam, GuildLeader, RankParam } from '../domain/rank'; import { GuildModel } from '../db/Guild'; import { comBtlRanges } from '../pubUtils/gamedata'; -import { stringify } from 'querystring'; +import { getNextHourPoint } from '../pubUtils/timeUtil'; /** * 在服务重新启动时,将信息存入redis */ @@ -103,14 +103,28 @@ export async function existsRank(key: string, serverId: number) { return result; } -export function getKeyName(key: string, serverId?: number) { + +/** + * 获得redis key的名字 + * @param key REDIS_KEY中配置的key + * @param serverId 服务器id + * @param plus 后面再加 + */ +export function getKeyName(key: string, serverId?: number, plus: string = '') { + let newKey = ''; if(serverId) { - return `${key}:${serverId}`; + newKey = `${key}:${serverId}`; } else { - return key; + newKey = key; } + + if(plus) { + newKey += `:${plus}`; + } + return newKey; } + // 更新玩家信息 export async function redisUserInfoUpdate(key: string, roleId: string, arr: Array<{field: string, value:(string|number|GuildLeader)}>) { let params = await redisClient().hgetAsync(key, roleId); @@ -129,16 +143,31 @@ export async function redisUserInfoAdd(key: string, roleId: string, params: Rank return await redisClient().hsetAsync(key, roleId, value); } -// 更新排行榜, 如pvp这样跨服的,serverId传0 -export async function setRank(key: string, serverId: number, myId: string, score: number, timestamp: number, params: RankParam|GuildRankParam, limit = 100) { +/** + * 更新排行榜 + * @param key 配置在REDIS_KEY中的key + * @param serverId 区服id 如pvp这样跨服的,serverId传0 + * @param myId 玩家roleId或军团code + * @param score 得分 + * @param timestamp 时间 13位时间戳 + * @param params 玩家数据 + * @param isAtom 是否是原子性的更新 + * @param limit + */ +export async function setRank(key: string, serverId: number, myId: string, score: number, timestamp: number, params: RankParam|GuildRankParam, isAtom = false, limit = 100) { // 更新分数 - const _score = encodeScoreWithTime(score, timestamp); - await redisClient().zaddAsync(getKeyName(key, serverId), _score, myId); + let newScore = score; + if(isAtom) { + newScore = await updateRankAtom(key, serverId, myId, score, timestamp); + } else { + const _score = encodeScoreWithTime(score, timestamp); + await redisClient().zaddAsync(getKeyName(key, serverId), _score, myId); + } // 移除100名以外 await redisClient().zremrangebyrankAsync(getKeyName(key, serverId), limit, 10000); - let infoKey = REDIS_RANK_TO_INFO.get(key); + let infoKey = REDIS_RANK_TO_INFO.get(key)||REDIS_KEY.USER_INFO; // 如果没有信息,更新玩家信息 const hasCurUser = await redisClient().hexistsAsync(infoKey, myId); if(!hasCurUser) { @@ -148,12 +177,12 @@ export async function setRank(key: string, serverId: number, myId: string, score } // 获取排行榜 -export async function getRank(key: string, serverId: number, roleId: string) { +export async function getRank(key: string, serverId: number, roleId: string, limit = 100) { let ranks = [], myRank = null; - const rankFromDb = await redisClient().zrevrangebyscoreAsync(getKeyName(key, serverId), '+inf', '-inf', "WITHSCORES", "LIMIT", 0, 100); + const rankFromDb = await redisClient().zrevrangebyscoreAsync(getKeyName(key, serverId), '+inf', '-inf', "WITHSCORES", "LIMIT", 0, limit); - let infoKey = REDIS_RANK_TO_INFO.get(key); + let infoKey = REDIS_RANK_TO_INFO.get(key)||REDIS_KEY.USER_INFO; for(let ii = 0; ii < rankFromDb.length; ii+=2) { const _roleId = rankFromDb[ii]; @@ -496,6 +525,20 @@ async function setServerList() { } } +export async function getAllServers() { + let servers = await redisClient().hgetallAsync(REDIS_KEY.DB_GAME); + let serverlist = new Array(); + for(let serverStr in servers) { + let arr = serverStr.split('_'); + let serverId = parseInt(arr[1]); + + if(!isNaN(serverId) && !serverlist.includes(serverId)) { + serverlist.push(serverId); + } + } + return serverlist; +} + export async function getServerName(serverType: string, serverId: number) { let name = await redisClient().hgetAsync(REDIS_KEY.DB_GAME, `${serverType}_${serverId}`); return name @@ -506,4 +549,96 @@ function redisClient() { return client; } -/**************** 数据库表end */ \ No newline at end of file +/**************** 数据库表end */ + +/**************** 军团活动排行 */ + +/** + * 更新排行榜(将得分和时间拆分开) + * @param key 配置在REDIS_KEY中的key + * @param serverId 区服id + * @param field 玩家id/军团id + * @param score 得分 + * @param time 事件 + */ +async function updateRankAtom(key: string, serverId: number, field: string, score: number, timestamp: number) { + let originKey = getKeyName(key, serverId); + let timeKey = getKeyName(key, serverId, 'time'); + await redisClient().expireatAsync(originKey, getNextHourPoint(5)); + await redisClient().expireatAsync(timeKey, getNextHourPoint(5)); + + let timelen = 10; + let pow = Math.pow(10, timelen + 1); + + let newScore = await redisClient().zincrbyAsync(originKey, score, field); + await redisClient().zaddAsync(timeKey, pow - 1 - Math.floor(timestamp/1000), field); + return newScore; +} + +async function generateUnionRank(key: string, serverId: number) { + let unionKey = getKeyName(key, serverId, 'union'); // 联合的key + let existsKey = await redisClient().existsAsync(unionKey); + if(!existsKey) { + await redisClient().expireatAsync(unionKey, 10); // 10秒更新一次 + let originKey = getKeyName(key, serverId); + let timeKey = getKeyName(key, serverId, 'time'); + + let timelen = 10; + let pow = Math.pow(10, timelen + 1); + await redisClient().zunionstoreAsync(unionKey, 2, originKey, timeKey, 'WEIGHTS', pow, 1); + } + return unionKey; +} + +/** + * 使用updateRankAtom更新的排行榜,使用这个方法获取列表 + * @param key 配置在REDIS_KEY中的key + * @param serverId 区服id + * @param roleId 自己的id + */ +export async function getUnionRank(key: string, serverId: number, roleId: string, limit = 100) { + + let ranks = [], myRank = null; + let unionKey = await generateUnionRank(key, serverId); + const rankFromDb = await redisClient().zrevrangebyscoreAsync(unionKey, '+inf', '-inf', "WITHSCORES", "LIMIT", 0, limit); + + let infoKey = REDIS_RANK_TO_INFO.get(key)||REDIS_KEY.USER_INFO; + + for(let ii = 0; ii < rankFromDb.length; ii+=2) { + const _roleId = rankFromDb[ii]; + const _score = decodeScoreWithTime(rankFromDb[ii + 1]); + const info = await redisClient().hgetAsync(infoKey, _roleId); + const _userInfo = JSON.parse(info); + const tmp = {..._userInfo, num: _score, rank: Math.floor(ii/2)+1}; + if(infoKey == REDIS_KEY.USER_INFO) { + tmp["roleId"] = _roleId; + } else if(infoKey == REDIS_KEY.GUILD_INFO) { + tmp["code"] = _roleId; + } + ranks.push(tmp); + if(roleId == _roleId) myRank = tmp; + } + return {ranks, myRank} +} + +// 获取我的排名 +export async function getMyUnionRank(key: string, serverId: number, roleId: string) { + let unionKey = await generateUnionRank(key, serverId); + let myRank = await redisClient().zrevrankAsync(unionKey, roleId); + return myRank + 1; +} + +export async function getRankScore(key: string, serverId: number, field: string) { + let score = await redisClient().zscoreAsync(getKeyName(key, serverId), field); + return score; +} + +/** + * 按军团名拼接key + * @param key + * @param guildCode 军团编号 + */ +export function getGuildKeyName(key: string, guildCode: string) { + return `${key}:${guildCode}`; +} +/**************** 军团活动排行end */ \ No newline at end of file diff --git a/game-server/app/util/routeUtil.ts b/game-server/app/util/routeUtil.ts index 54fad3081..baecf2bb9 100644 --- a/game-server/app/util/routeUtil.ts +++ b/game-server/app/util/routeUtil.ts @@ -42,6 +42,35 @@ export function battle(session: Session, msg: any, app: Application, cb: (err: E cb(null, res.id); } + +export function guild(session: Session, msg: any, app: Application, cb: (err: Error , serverId ?: string) => void) { + let guildServers = app.getServersByType('guild'); + + if(!guildServers || guildServers.length === 0) { + cb(new Error('can not find guild servers.')); + return; + } + + let rid = session.get('roleId'); + + if (msg.args && msg.args.length > 0) { + for (let arg of msg.args) { + if (!arg.route) continue; + + if (['guild.gateActivityHandler.checkBattle', // 军团活动蛮夷入侵路由,按军团路由 + 'guild.gateActivityHandler.action', + 'guild.gateActivityHandler.hitGate', + 'guild.gateActivityHandler.battleEnd' + ].indexOf(arg.route) !== -1) { + rid = session.get('guildCode'); + } + } + } + + let res = dispatch(rid, guildServers); + cb(null, res.id); +} + export function gm(session: Session, msg: any, app: Application, cb: (err: Error , serverId ?: string) => void) { let gmServers = app.getServersByType('gm'); diff --git a/game-server/config/adminServer.ts b/game-server/config/adminServer.ts index dee61d8aa..7b154f9aa 100644 --- a/game-server/config/adminServer.ts +++ b/game-server/config/adminServer.ts @@ -19,4 +19,7 @@ module.exports = [{ }, { 'type': 'systimer', 'token': 'agarxhqb98rpajloaxn34ga8xrunpagkjwlaw3ruxnpaagl29w4rxn' +}, { + 'type': 'guild', + 'token': 'agarxhqb98rpajloaxn34ga8xrunpagkjwlaw3ruxnpaagl29w4rxn' }]; \ No newline at end of file diff --git a/game-server/config/redis.ts b/game-server/config/redis.ts index 586a82176..082298b78 100644 --- a/game-server/config/redis.ts +++ b/game-server/config/redis.ts @@ -10,6 +10,8 @@ declare module 'redis' { delAsync(key: string): Promise; // 设置过期时间 expireAsync(key: string, time: number): Promise; + // 按时间戳设置过期时间 + expireatAsync(key: string, time: number): Promise; // string类型存储 setexAsync(key: string, time: number, value: string): Promise; // 获取存储string类型value @@ -52,6 +54,12 @@ declare module 'redis' { zrevrankAsync(key: string, field: string): Promise; // 命令返回有序集中,指定区间内的成员,从大到小 zrevrangeAsync(key: string, start: number, end: number): Promise; + // 对有序集合中指定成员的分数加上增量 increment + zincrbyAsync(key: string, increment: number, member: string): Promise; + // 对有序集合中指定成员的分数加上增量 increment + zunionstoreAsync(destination: string, numkeys: number, ...args: any): Promise; + // 返回有序集中,成员的分数值 + zscoreAsync(key: string, member: string): Promise; // 显示集合成员 smembersAsync(key: string): Promise; // 增加集合成员 diff --git a/game-server/config/servers.ts b/game-server/config/servers.ts index 2578c730b..55f1866de 100644 --- a/game-server/config/servers.ts +++ b/game-server/config/servers.ts @@ -43,6 +43,10 @@ module.exports = { ], 'systimer': [ {'id': 'systimer-server-1', 'host': '127.0.0.1', 'port': 6056, "args": " --inspect=9233"} + ], + 'guild': [ + {'id': 'guild-server-1', 'host': '127.0.0.1', 'port': 6057, "args": " --inspect=9238"}, + {'id': 'guild-server-2', 'host': '127.0.0.1', 'port': 6058, "args": " --inspect=9238"} ] }, 'production': { @@ -75,6 +79,10 @@ module.exports = { ], 'systimer': [ {'id': 'systimer-server-1', 'host': '127.0.0.1', 'port': 6056} + ], + 'guild': [ + {'id': 'guild-server-1', 'host': '127.0.0.1', 'port': 6057, "args": " --inspect=9238"}, + {'id': 'guild-server-2', 'host': '127.0.0.1', 'port': 6058, "args": " --inspect=9238"} ] }, 'alpha': { @@ -109,6 +117,10 @@ module.exports = { ], 'systimer': [ {'id': 'systimer-server-1', 'host': '127.0.0.1', 'port': 6056} + ], + 'guild': [ + {'id': 'guild-server-1', 'host': '127.0.0.1', 'port': 6057, "args": " --inspect=9238"}, + {'id': 'guild-server-2', 'host': '127.0.0.1', 'port': 6058, "args": " --inspect=9238"} ] }, 'dev': { @@ -144,6 +156,10 @@ module.exports = { ], 'systimer': [ {'id': 'systimer-server-1', 'host': '127.0.0.1', 'port': 6056, "args": " --inspect=9233"} + ], + 'guild': [ + {'id': 'guild-server-1', 'host': '127.0.0.1', 'port': 6057, "args": " --inspect=9238"}, + {'id': 'guild-server-2', 'host': '127.0.0.1', 'port': 6058, "args": " --inspect=9238"} ] }, 'isbn': { @@ -176,6 +192,10 @@ module.exports = { ], 'systimer': [ {'id': 'systimer-server-1', 'host': '127.0.0.1', 'port': 6056} + ], + 'guild': [ + {'id': 'guild-server-1', 'host': '127.0.0.1', 'port': 6057, "args": " --inspect=9238"}, + {'id': 'guild-server-2', 'host': '127.0.0.1', 'port': 6058, "args": " --inspect=9238"} ] } }; diff --git a/shared/consts/constModules/guildConst.ts b/shared/consts/constModules/guildConst.ts index 083613402..2c8563cd0 100644 --- a/shared/consts/constModules/guildConst.ts +++ b/shared/consts/constModules/guildConst.ts @@ -134,4 +134,18 @@ export enum GUILD_POINT_WAYS { TRAIN = 2, // 训练场 BOSS_WAR = 3, // 演武台 ACTIVITY = 4, // 军团活动 +} + +// 军团活动id +export enum GUILD_ACTIVITY_TYPE { + GATE_ACTIVITY = 1, // 蛮夷入侵 + CITY_ACTIVITY = 2, // 诸侯混战 + RACE_ACTIVITY = 3 // 粮草先行 +} + +// 军团活动状态 +export enum GUILD_ACTIVITY_STATUS { + WAITING = 0, + START = 1, + END = 2 } \ No newline at end of file diff --git a/shared/consts/constModules/sysConst.ts b/shared/consts/constModules/sysConst.ts index ba74fe9a3..ccc2618c4 100644 --- a/shared/consts/constModules/sysConst.ts +++ b/shared/consts/constModules/sysConst.ts @@ -231,14 +231,18 @@ export const REDIS_KEY = { GUILD_ACTIVE_RANK: "guildActiveRank", // 公会周活跃排行榜 DB_GAME: 'db_game', // 服务器列表 ONLINE_USERS: 'onlineUsers', // 在线用户情况 - CHANNEL_SERVERS: 'chat:channelServers', // 渠道对应的 chat 服务器 Id + CHANNEL_SERVERS: 'chat:channelServers', // 渠道对应的 chat 服务器 Id, + USER_GUILD_ACTIVITY: 'usrGuildAct', + GUILD_ACTIVITY: 'guildAct', } // 各排行榜对应hash的key export const REDIS_RANK_TO_INFO = new Map([ [REDIS_KEY.TOWER_RANK, REDIS_KEY.USER_INFO], [REDIS_KEY.PVP_RANK, REDIS_KEY.USER_INFO], - [REDIS_KEY.GUILD_ACTIVE_RANK, REDIS_KEY.GUILD_INFO] + [REDIS_KEY.GUILD_ACTIVE_RANK, REDIS_KEY.GUILD_INFO], + [REDIS_KEY.GUILD_ACTIVITY, REDIS_KEY.GUILD_INFO], + [REDIS_KEY.USER_GUILD_ACTIVITY, REDIS_KEY.USER_INFO] ]); export const FUNC_OPT_TYPE = { @@ -335,7 +339,9 @@ export const FILENAME = { DIC_ARMY_BOSS_RANK_REWARD: 'dic_army_bossrankReward', DIC_ARMY_DONATE_BOX_REWARD: 'dic_army_donateBoxReward', DIC_ROLE_FRIEND: 'dic_zyz_friends', - DIC_ROLE_FRIEND_LEVEL: 'dic_zyz_closelevel' + DIC_ROLE_FRIEND_LEVEL: 'dic_zyz_closelevel', + DIC_GUILD_ACTIVITY: 'dic_zyz_guildActivity', + DIC_GATE_ACTIVITY_POINT: 'dic_zyz_gateActivityPoint' } export const WAR_RELATE_TABLES = [ diff --git a/shared/consts/statusCode.ts b/shared/consts/statusCode.ts index 014827be6..4c96a884f 100644 --- a/shared/consts/statusCode.ts +++ b/shared/consts/statusCode.ts @@ -192,9 +192,12 @@ export const STATUS = { GUILD_TRAIN_BOX_INDEX_IS_GOT:{ code: 20972, simStr: '该位置试炼宝箱已经领取过,请重新选择' }, GUILD_TRAIN_BOX_IS_GOT: { code: 20973, simStr: '玩家已经领取该试炼宝箱' }, - // 军团拍卖 GUILD_LOT_NOT_FOUND: { code: 21001, simStr: '拍品未找到' }, LOT_OFFER_SERIAL: { code: 21002, simStr: '不能连续出价' }, + + // 军团活动 21100-21199 + GUILD_ACTIVITY_NOT_OPEN: { code: 21100, simStr: '活动未开放' }, + // 通用 30000 - 30099 DIC_DATA_NOT_FOUND: { code: 30000, simStr: '数据表未找到' }, ROLE_MATERIAL_NOT_ENOUGH: { code: 30001, simStr: '材料数量不足' }, diff --git a/shared/db/GuildActivityRec.ts b/shared/db/GuildActivityRec.ts new file mode 100644 index 000000000..e1ccca487 --- /dev/null +++ b/shared/db/GuildActivityRec.ts @@ -0,0 +1,80 @@ +import BaseModel from './BaseModel'; +import { index, getModelForClass, prop, DocumentType, Ref } from '@typegoose/typegoose'; +import { ItemReward } from '../domain/dbGeneral'; +import { getTodayZeroDate } from '../pubUtils/timeUtil'; +import { genCode } from '../pubUtils/util'; + + +@index({ code: 1 }) +export default class GuildActivityRecord extends BaseModel { + + @prop({ required: true }) + code: string; // 本次记录唯一值 + + @prop({ required: true }) + guildCode: string; // 军团code + + @prop({ required: true }) + serverId: number; // 分服 + + @prop({ required: true }) + aid: number; // 军团活动id + + @prop({ required: true, default: false }) + isSuccess: boolean; // 是否胜利 + + @prop({ required: true, default: false }) + isCompleted: boolean; // 是否胜利 + + @prop({ required: true }) + rank: number; // 最终排名 + + @prop({ required: true, type: String, default: [] }) + members: string[]; // 参与活动的团员的roleId列表 + + @prop({ required: true, default: 0 }) + memberCnt: number; // 参加的团员的数量 + + @prop({ required: true, type: ()=> ItemReward, default: [] }) + rewards: ItemReward[]; // 转到拍卖行的道具 + + @prop({ required: true }) + score: number; // 蛮夷入侵总军功 + + @prop({ required: true }) + cityId: number; // 诸侯混战占领了的城池 + + @prop({ required: true }) + damage: number; // 对城门造成的伤害 + + @prop({ required: true }) + raceTime: number; // 粮草先行比赛时间(s) + + @prop({ required: true }) + distance: number; // 粮草先行最后的距离 + + @prop({ required: true }) + speed: number; // 粮草先行最后的速度(m/s) + + @prop({ required: true }) + durability: number; // 粮草先行最后的耐久度 + + // 每天一条记录 + public static async getRecord(guildCode: string, serverId: number, aid: number) { + let today = getTodayZeroDate(); + let docs = new GuildActivityRecordModel(); + let code = genCode(10); + let update = Object.assign(docs, { code, guildCode, serverId, aid, createdAt: new Date() }) + let rec: GuildActivityRecordType = await GuildActivityRecordModel.findOneAndUpdate( + { guildCode, createdAt: { $gte: today }}, + { $setOnInsert: update }, + {new: true, upsert: true}).lean(); + return rec; + } + +} + +export const GuildActivityRecordModel = getModelForClass(GuildActivityRecord); + +export interface GuildActivityRecordType extends Pick, keyof GuildActivityRecord> { }; +export type GuildActivityRecordUpdateParam = Partial; // 将所有字段变成可选项 \ No newline at end of file diff --git a/shared/db/Role.ts b/shared/db/Role.ts index ea977117f..03b787678 100644 --- a/shared/db/Role.ts +++ b/shared/db/Role.ts @@ -4,7 +4,7 @@ import { index, getModelForClass, prop, DocumentType, Ref, mongoose } from '@typ import User from './User'; import { shouldRefresh, reduceCe } from '../pubUtils/util'; import { initRoleAtrr } from '../pubUtils/playerCe'; -import { nowSeconds } from '../pubUtils/timeUtil'; +import { nowSeconds, getBeforeDayDate } from '../pubUtils/timeUtil'; import { Figure } from '../domain/dbGeneral'; import { EXTERIOR } from '../pubUtils/dicParam'; import Hero from './Hero'; @@ -483,7 +483,7 @@ export default class Role extends BaseModel { * @param invitedMembers 当天已邀请过的人 */ public static async getInviteList(time: number, serverId: number) { - const result = await RoleModel.find({ loginTime: { $gt: time }, hasGuild: false, serverId }) + const result: RoleType[] = await RoleModel.find({ loginTime: { $gt: time }, hasGuild: false, serverId }) .select({roleId: 1, roleName: 1,ce: 1, head: 1, frame: 1, lv: 1,title: 1,job: 1,quitTime: 1, _id: 0}) .sort({quitTime: -1, lv: -1, ce: -1}) .limit(100).lean({getters: true, virtuals: true}); @@ -492,7 +492,7 @@ export default class Role extends BaseModel { // 获取好友推荐列表 public static async getRecommedList(minLv: number, maxLv: number, time: number) { - const result = await RoleModel.find({ loginTime: { $gt: time }, lv: { $gte: minLv, $lte: maxLv } }, { _id: 0 }) + const result: RoleType[] = await RoleModel.find({ loginTime: { $gt: time }, lv: { $gte: minLv, $lte: maxLv } }, { _id: 0 }) .select(ROLE_SELECT.SHOW_FRIEND_APPLY_LIST) .sort({quitTime: -1, lv: -1, ce: -1}) .limit(200).lean({ getters: true, virtuals: true }); @@ -501,7 +501,7 @@ export default class Role extends BaseModel { // 查询玩家 public static async searchByNameOrId(value: string) { - const result = await RoleModel.find({ + const result: RoleType[] = await RoleModel.find({ $or: [ { roleName: { $regex: new RegExp(value.toString(), 'i') } }, { roleId: value } @@ -521,7 +521,7 @@ export default class Role extends BaseModel { // 退出公会 public static async quitGuild(roleId: string, now: number) { - const result = await RoleModel.findOneAndUpdate({ roleId, hasGuild: true }, { hasGuild: false, guildCode: "", guildName: "", quitGuildTime: now }, { new: true }).lean(); + const result: RoleType = await RoleModel.findOneAndUpdate({ roleId, hasGuild: true }, { hasGuild: false, guildCode: "", guildName: "", quitGuildTime: now }, { new: true }).lean(); return result; } @@ -531,35 +531,44 @@ export default class Role extends BaseModel { if(max) { condition['friendCnt'] = {$lte: max - inc}; } - const result = await RoleModel.findOneAndUpdate(condition, {$inc: { friendCnt: inc }}, {new: true}).lean(); + const result: RoleType = await RoleModel.findOneAndUpdate(condition, {$inc: { friendCnt: inc }}, {new: true}).lean(); return result; } // 增加好友申请数量 public static async increaseFriendApplyCnt(roleId: string, inc: number, max: number) { - const result = await RoleModel.findOneAndUpdate({ roleId, recFrdApplyCnt: {$lte: max - inc}}, {$inc: { recFrdApplyCnt: inc }}, {new: true}).lean(); + const result: RoleType = await RoleModel.findOneAndUpdate({ roleId, recFrdApplyCnt: {$lte: max - inc}}, {$inc: { recFrdApplyCnt: inc }}, {new: true}).lean(); return result; } // 增加黑名单数量 public static async increaseBlockCnt(roleId: string, blockCnt: number, friendCnt: number, maxBlockCnt: number) { - const result = await RoleModel.findOneAndUpdate({ roleId, blockCnt: {$lte: maxBlockCnt - blockCnt}}, {$inc: { blockCnt, friendCnt }}, {new: true}).lean(); + const result: RoleType = await RoleModel.findOneAndUpdate({ roleId, blockCnt: {$lte: maxBlockCnt - blockCnt}}, {$inc: { blockCnt, friendCnt }}, {new: true}).lean(); return result; } public static async updatedRoleMailAt(roleId: string, updatedMailAt: number, lean = true) { - const result = await RoleModel.findOneAndUpdate({ roleId }, {$set: { updatedMailAt }}, {new: true}).lean(lean); + const result: RoleType = await RoleModel.findOneAndUpdate({ roleId }, {$set: { updatedMailAt }}, {new: true}).lean(lean); return result; } public static async addFigure(roleId: string, id: number, figure: Figure) { - const result = await RoleModel.findOneAndUpdate({roleId}, { $pull: { heads: { id: id }, $push: { heads: figure } }}, {new: true}).lean(); + const result: RoleType = await RoleModel.findOneAndUpdate({roleId}, { $pull: { heads: { id: id }, $push: { heads: figure } }}, {new: true}).lean(); return result } public static async checkName(roleName: string, serverId: number) { - const role = await RoleModel.findOne({ roleName, serverId }).lean(); + const role: RoleType = await RoleModel.findOne({ roleName, serverId }).lean(); return !!role; } + + // 获取战力在中位数的玩家 + public static async getMedianRole(serverId: number) { + let yesterday = getBeforeDayDate(1); + const roles: RoleType[] = await RoleModel.find({ serverId, updatedAt: { $gte: new Date(yesterday) } }) + .select('topLineup') + .sort({ ce: -1 }).lean(); + return roles[Math.floor(roles.length/2)] + } } export const RoleModel = getModelForClass(Role); diff --git a/shared/db/Serverlist.ts b/shared/db/Serverlist.ts new file mode 100644 index 000000000..175936997 --- /dev/null +++ b/shared/db/Serverlist.ts @@ -0,0 +1,87 @@ +import { APP_ID } from './../consts'; +import BaseModel from './BaseModel'; +import { index, getModelForClass, prop, DocumentType, modelOptions } from '@typegoose/typegoose'; + +// 暂时服务器列表是从game表取的,之后会转移到使用这张表,目前用作查询该服战力中位数 + +/** + * 游戏字段接口 +*/ +@index({ id: 1 }) +@modelOptions({schemaOptions: {id: false}}) +export default class Serverlist extends BaseModel { + + @prop({ required: true, default: 1 }) + gameId: number; // 游戏id Game表的id + + @prop({ required: true }) + id: number; // 小区id + + @prop({ required: true }) + name: string; // 小区区名 + + @prop({ required: true }) + groupId: number; // 大区id + + @prop({ required: true }) + groupName: string; // 大区区名 + + @prop({ required: true }) + host: string; // pinus连接地址 + + @prop({ required: false }) + port: number; // pinus端口 + + @prop({ required: true }) + serverStatus: number; // 服务器状态 + + public get status() { + let now = new Date(); + if( now > this.openTime) { + return this.serverStatus; + } else { + return 3; // 未开服 + } + } + + @prop({ required: true }) + openTime: Date; + + @prop({ required: true }) + createTime: Date; + + @prop({ required: true }) + serverType: string; + + @prop({ required: true, default: 0 }) + medianCe: number; // 中位数武将战力、缩小10000倍后的结果 + + public static async getAllServerList() { + let servers: ServerlistType[] = await ServerlistModel.find().lean({ getters: true, virtuals: true }); + return servers; + } + + public static async findByServerId(serverId: number) { + let server: ServerlistType = await ServerlistModel.findOne({ id: serverId }).select('medianCe').lean({ getters: true, virtuals: true }); + return server; + } + + public static async updateByServerId(serverId: number, update: ServerlistUpdate) { + let server: ServerlistType = await ServerlistModel.findOneAndUpdate({id: serverId}, {$set: update}).lean({ getters: true, virtuals: true }); + return server; + } + + public static async newServer(serverId: number, serverType: string, params: ServerlistUpdate) { + const doc = new ServerlistModel(); + const update = Object.assign(doc.toJSON(), { id: serverId, serverType }, params); + let server: ServerlistType = await ServerlistModel.findOneAndUpdate({ id: serverId, serverType }, {$setOnInsert: update }, {new: true, upsert: true}).lean({ getters: true, virtuals: true }); + return server; + } +} + +export const ServerlistModel = getModelForClass(Serverlist); + +export interface ServerlistType extends Pick, keyof Serverlist>{ + id: number; +}; +export type ServerlistUpdate = Partial; // 将所有字段变成可选项 diff --git a/shared/db/UserGuildActivityRec.ts b/shared/db/UserGuildActivityRec.ts new file mode 100644 index 000000000..491802455 --- /dev/null +++ b/shared/db/UserGuildActivityRec.ts @@ -0,0 +1,97 @@ +import BaseModel from './BaseModel'; +import { index, getModelForClass, prop, DocumentType, Ref } from '@typegoose/typegoose'; +import { getTodayZeroDate } from '../pubUtils/timeUtil'; +import { genCode } from '../pubUtils/util'; + +export class Record { + @prop({ required: true }) + round: number; // 第几回合 + + @prop({ required: true }) + dataId: number; // 出兵表上的dataId + + @prop({ required: true }) + score: number; // 得分 +} + +@index({ code: 1 }) +export default class UserGuildActivityRec extends BaseModel { + + @prop({ required: true }) + code: string; // 本次记录唯一值 + + @prop({ required: true }) + sourceCode: string; // 军团记录的code + + @prop({ required: true }) + roleId: string; // 玩家id + + @prop({ required: true }) + roleName: string; // 军团唯一记录 + + @prop({ required: true }) + guildCode: string; // 军团code + + @prop({ required: true }) + serverId: number; // 分服 + + @prop({ required: true }) + aid: number; // 军团活动id + + @prop({ required: true, default: false }) + isSuccess: boolean; // 是否胜利 + + @prop({ required: true, default: false }) + isCompleted: boolean; // 是否完成结算了 + + @prop({ required: true, default: 0 }) + rank: number; // 最终排名 + + @prop({ required: true, default: 0 }) + challengeCnt: number; // 向上累加的挑战次数 + + // 南蛮入侵字段 + + @prop({ required: true, type: Number }) + heroes: number[]; // 使用的武将 + + @prop({ required: true }) + round: number; // 坚持回合数 + + @prop({ required: true }) + score: number; // 个人总军功 + + @prop({ required: true, type: Record }) + record: Record[]; // 个人总军功 + + // 每天一条记录 + public static async getRecord(roleId: string, roleName: string, guildCode: string, serverId: number, sourceCode: string, heroes: number[], aid: number) { + let today = getTodayZeroDate(); + let docs = new UserGuildActivityRecModel(); + let code = genCode(10); + let update = Object.assign(docs, { code, roleId, roleName, guildCode, serverId, aid }); + delete update.heroes; + let rec: UserGuildActivityRecType = await UserGuildActivityRecModel.findOneAndUpdate( + { roleId, sourceCode, createdAt: { $gte: today }}, + { $setOnInsert: update }, + {new: true, upsert: true}).lean(); + if(heroes.length > 0 && rec) { + rec = await UserGuildActivityRecModel.findOneAndUpdate( { code: rec.code}, { $push: { heroes: { $each: heroes}}}, {new: true}).lean(); + } + return rec; + } + + public static async pushRecord(code: string, records: Record[]) { + let rec: UserGuildActivityRec = await UserGuildActivityRecModel.findOneAndUpdate( + { code }, + { $push: { record: { $each: records } } }, + { new: true } + ).lean(); + return rec; + } +} + +export const UserGuildActivityRecModel = getModelForClass(UserGuildActivityRec); + +export interface UserGuildActivityRecType extends Pick, keyof UserGuildActivityRec> { }; +export type UserGuildActivityRecUpdateParam = Partial; // 将所有字段变成可选项 \ No newline at end of file diff --git a/shared/pubUtils/data.ts b/shared/pubUtils/data.ts index ec55090e5..95c40cd26 100644 --- a/shared/pubUtils/data.ts +++ b/shared/pubUtils/data.ts @@ -58,6 +58,8 @@ import { dicArmyDonate } from '../pubUtils/dictionary/DicArmyDonateBoxReward'; import { dicRoleFriend, DicRoleFriend } from "./dictionary/DicRoleFriend"; import { dicRoleFriendLv } from "./dictionary/DicRoleFriendLv"; import { Attribute } from "../domain/roleField/attribute"; +import { dicGuildActivity } from './dictionary/DicGuildActivity'; +import { dicGateActivityPoint } from './dictionary/DicGateActivityPoint'; export const gameData = { blurprtCompose: dicBlueprtCompose, @@ -139,7 +141,9 @@ export const gameData = { armyDonateBox: dicArmyDonate, roleFriend: dicRoleFriend, roleFriendLv: dicRoleFriendLv, - figureCondition: figureCondition + figureCondition: figureCondition, + guildActivity: dicGuildActivity, + gateActivityPoint: dicGateActivityPoint }; // 在此提供一些原先在gamedata中提供的方法,以便更方便获取gameData数据 diff --git a/shared/pubUtils/dicParam.ts b/shared/pubUtils/dicParam.ts index 5b26df372..77b62657e 100644 --- a/shared/pubUtils/dicParam.ts +++ b/shared/pubUtils/dicParam.ts @@ -34,6 +34,7 @@ export const PVP = { PVP_SEASON_DAYS: 30, // PVP一个赛季的时间 PVP_OPPONENT_FREEREFRESH: 3, // pvp挑战对手每日免费刷新次数 PVP_WINREWARD_UPLIMIT: 10, // 连胜奖励军功加成上限 + PVP_LINEUP_HEROS: 6, // pvp最多上阵人数 }; export const ARMY = { ARMY_CREAT_COST: 188, // 创建军团需要的元宝 @@ -75,3 +76,27 @@ export const EXTERIOR = { EXTERIOR_FACECASE: 11301, // 默认头像框id EXTERIOR_APPEARANCE: 11419, // 默认形象id }; +export const HTTPMANAGE = { + ADDRESSTYPE: 1, // 1:开发服,2:审核服,3:版号服 + SERVERTYPE: 1, // ADDRESSTYPE下服务器的筛选:1:开发服,2:玩家服 +}; +export const SYSTEAM = { + RECENT_GROUP_MSGS_CNT: 10, // 群消息获取条数 + RECENT_PRIVATE_CHATS_CNT: 20, // 最近私聊个数 + MSG_TYPE: 0, // 0:纯文本,1:副文本,2:表情 +}; +export const GUILDACTIVITY = { + GATEACTIVITY_CHALLENGE_TIMES: 1, // 蛮夷入侵每天可以挑战次的次数 + GATEACTIVITY_HONOUR_RATIO: 0.5, // 功勋/积分的系数 + GATEACTIVITY_ENEMYCE: 2000, // 蛮夷入侵敌军2000战力模板 + GATEACTIVITY_ROUNDSTANDARD: 20, // 蛮夷入侵和基准战力相当的回合 + CITYACTIVITY_CHALLENGE_CD: 300, // 诸侯混战每次战斗之后的cd间隔 + CITYACTIVITY_CD_COST: 100, // 诸侯混战取消cd需要消耗的元宝数 + CITYACTIVITY_ROUND_COUNT: 40, // 诸侯混战的总回合数量 + RACEACTIVITY_LENGTH: 1000, // 粮草先行赛道的总米数 + RACEACTIVITY_ENCOUNTER: '100&1|200&2|300&2|400&1|500&2|600&2|700&1|800&2|900&2', // 触发道具和事件的米数(1:道具;2:事件) + RACEACTIVITY_EVENT_RATIO: 0.5, // 触发事件好坏的比例 + GATEACTIVITY_GATEHP: 10000, // 城门血量 + GATEACTIVITY_BOSS_DIFFICULT: '1&1.2|2&1.5', // boss类型&难度系数|boss类型&难度系数(1:小boss,2:大boss) + RACEACTIVITY_DURABILITY_REWARD: 2, // 每剩余1%耐久度给予X点功勋 +}; diff --git a/shared/pubUtils/dictionary/DicGateActivityPoint.ts b/shared/pubUtils/dictionary/DicGateActivityPoint.ts new file mode 100644 index 000000000..e957262f1 --- /dev/null +++ b/shared/pubUtils/dictionary/DicGateActivityPoint.ts @@ -0,0 +1,15 @@ +import { readJsonFile } from '../util' +import { FILENAME } from '../../consts' + +export interface DicGkPvp { + readonly id: number; + readonly point: number; +} + +const str = readJsonFile(FILENAME.DIC_GATE_ACTIVITY_POINT); +let arr = JSON.parse(str); + +export const dicGateActivityPoint = new Map(); +arr.forEach(o => { + dicGateActivityPoint.set(o.id, o.point); +}); diff --git a/shared/pubUtils/dictionary/DicGuildActivity.ts b/shared/pubUtils/dictionary/DicGuildActivity.ts new file mode 100644 index 000000000..97143babf --- /dev/null +++ b/shared/pubUtils/dictionary/DicGuildActivity.ts @@ -0,0 +1,33 @@ +// 军团活动 +import { readJsonFile, parseNumberList } from '../util' +import { FILENAME } from '../../consts' + +export interface DicGuildActivity { + + // id + readonly id: number; + // 开启日期 星期几 + readonly openDay: number[]; + // 持续时间 秒 + readonly duringTime: number; + // 开启时间 几点 + readonly startTime: number; + // 开始倒计时 几点 + readonly countDown: number; + // 对应关卡id + readonly warid: number; + // 基础功勋值 + readonly honour: number; + +} + +const str = readJsonFile(FILENAME.DIC_GUILD_ACTIVITY); +let arr = JSON.parse(str); + +export const dicGuildActivity = new Map(); + +arr.forEach(o => { + o.openDay = parseNumberList(o.openDay) + dicGuildActivity.set( o.id, o ); +}); +arr = undefined; \ No newline at end of file diff --git a/shared/pubUtils/dictionary/DicWarJson.ts b/shared/pubUtils/dictionary/DicWarJson.ts index b3df59cb4..59164e77d 100644 --- a/shared/pubUtils/dictionary/DicWarJson.ts +++ b/shared/pubUtils/dictionary/DicWarJson.ts @@ -41,6 +41,8 @@ export interface DicWarJson { readonly spine: string; // boss阶段 readonly bossStage: number; + // 敌人是小boss还是大boss,和gateActivityPoint的id相对应 + readonly enemyType: number; // 召唤技能的召唤物 readonly callSkillData: string; diff --git a/shared/pubUtils/timeUtil.ts b/shared/pubUtils/timeUtil.ts index ccc92fbab..3428e2add 100644 --- a/shared/pubUtils/timeUtil.ts +++ b/shared/pubUtils/timeUtil.ts @@ -88,6 +88,22 @@ export function getCurHourPoint(hour: number) { return time; } +/** + * 获取第2天凌晨几点的时间戳 + * @param hour + */ +export function getNextHourPoint(hour: number) { + var date = new Date(); + date.setHours(hour); + date.setMinutes(0); + date.setSeconds(0); + var time = Math.floor(date.getTime() / PER_SECOND); + if (nowSeconds() > time) { + return time + PER_DAY; + } + return time; +} + export function getTodayZeroDate() { var date = new Date(); date.setHours(0); @@ -145,3 +161,14 @@ export function getNextTime(date: Date, h: number, m = 0, s = 0): Date { const timeToday = new Date(milliseconds).setHours(h, m, s); return timeToday > milliseconds ? new Date(timeToday) : new Date(timeToday + PER_DAY * PER_SECOND); } + +/** + * 获得某个时间是星期几 + * @param {Date} time + */ +export function getCutDay(time?: Date) { + if(!time) time = new Date(); + let d = time.getDay(); + if(d == 0) d = 7; + return d; +} diff --git a/shared/resource/jsons/dic_zyz_cityActivity.json b/shared/resource/jsons/dic_zyz_cityActivity.json new file mode 100644 index 000000000..3281c8791 --- /dev/null +++ b/shared/resource/jsons/dic_zyz_cityActivity.json @@ -0,0 +1,82 @@ +[ + { + "id": 1, + "name": "汉中", + "type": 1, + "nextCity": 7, + "difficult": 90, + "warid": 0 + }, + { + "id": 2, + "name": "南中", + "type": 1, + "nextCity": 7, + "difficult": 90, + "warid": 0 + }, + { + "id": 3, + "name": "柴桑", + "type": 1, + "nextCity": 8, + "difficult": 90, + "warid": 0 + }, + { + "id": 4, + "name": "会稽", + "type": 1, + "nextCity": 8, + "difficult": 90, + "warid": 0 + }, + { + "id": 5, + "name": "太原", + "type": 1, + "nextCity": 9, + "difficult": 90, + "warid": 0 + }, + { + "id": 6, + "name": "巨鹿", + "type": 1, + "nextCity": 9, + "difficult": 90, + "warid": 0 + }, + { + "id": 7, + "name": "成都", + "type": 2, + "nextCity": 10, + "difficult": 100, + "warid": 0 + }, + { + "id": 8, + "name": "建业", + "type": 2, + "nextCity": 10, + "difficult": 100, + "warid": 0 + }, + { + "id": 9, + "name": "邺城", + "type": 2, + "nextCity": 10, + "difficult": 100, + "warid": 0 + }, + { + "id": 10, + "name": "洛阳", + "type": 3, + "nextCity": 0, + "difficult": 110, + "warid": 0 + } +] \ No newline at end of file diff --git a/shared/resource/jsons/dic_zyz_cityActivityReward.json b/shared/resource/jsons/dic_zyz_cityActivityReward.json new file mode 100644 index 000000000..ff73c72a0 --- /dev/null +++ b/shared/resource/jsons/dic_zyz_cityActivityReward.json @@ -0,0 +1,218 @@ +[ + { + "id": 1, + "type": 1, + "guildRank": 1, + "min": 1, + "max": 1, + "honour": 2000 + }, + { + "id": 2, + "type": 1, + "guildRank": 1, + "min": 2, + "max": 10, + "honour": 1200 + }, + { + "id": 3, + "type": 1, + "guildRank": 1, + "min": 11, + "max": 0, + "honour": 800 + }, + { + "id": 4, + "type": 1, + "guildRank": 2, + "min": 1, + "max": 1, + "honour": 1200 + }, + { + "id": 5, + "type": 1, + "guildRank": 2, + "min": 2, + "max": 10, + "honour": 720 + }, + { + "id": 6, + "type": 1, + "guildRank": 2, + "min": 11, + "max": 0, + "honour": 480 + }, + { + "id": 7, + "type": 1, + "guildRank": 3, + "min": 1, + "max": 1, + "honour": 800 + }, + { + "id": 8, + "type": 1, + "guildRank": 3, + "min": 2, + "max": 10, + "honour": 480 + }, + { + "id": 9, + "type": 1, + "guildRank": 3, + "min": 11, + "max": 0, + "honour": 240 + }, + { + "id": 10, + "type": 2, + "guildRank": 1, + "min": 1, + "max": 1, + "honour": 4000 + }, + { + "id": 11, + "type": 2, + "guildRank": 1, + "min": 2, + "max": 10, + "honour": 2400 + }, + { + "id": 12, + "type": 2, + "guildRank": 1, + "min": 11, + "max": 0, + "honour": 1600 + }, + { + "id": 13, + "type": 2, + "guildRank": 2, + "min": 1, + "max": 1, + "honour": 2400 + }, + { + "id": 14, + "type": 2, + "guildRank": 2, + "min": 2, + "max": 10, + "honour": 1440 + }, + { + "id": 15, + "type": 2, + "guildRank": 2, + "min": 11, + "max": 0, + "honour": 960 + }, + { + "id": 16, + "type": 2, + "guildRank": 3, + "min": 1, + "max": 1, + "honour": 1600 + }, + { + "id": 17, + "type": 2, + "guildRank": 3, + "min": 2, + "max": 10, + "honour": 960 + }, + { + "id": 18, + "type": 2, + "guildRank": 3, + "min": 11, + "max": 0, + "honour": 480 + }, + { + "id": 19, + "type": 3, + "guildRank": 1, + "min": 1, + "max": 1, + "honour": 12000 + }, + { + "id": 20, + "type": 3, + "guildRank": 1, + "min": 2, + "max": 10, + "honour": 7200 + }, + { + "id": 21, + "type": 3, + "guildRank": 1, + "min": 11, + "max": 0, + "honour": 4800 + }, + { + "id": 22, + "type": 3, + "guildRank": 2, + "min": 1, + "max": 1, + "honour": 7200 + }, + { + "id": 23, + "type": 3, + "guildRank": 2, + "min": 2, + "max": 10, + "honour": 4320 + }, + { + "id": 24, + "type": 3, + "guildRank": 2, + "min": 11, + "max": 0, + "honour": 2880 + }, + { + "id": 25, + "type": 3, + "guildRank": 3, + "min": 1, + "max": 1, + "honour": 4800 + }, + { + "id": 26, + "type": 3, + "guildRank": 3, + "min": 2, + "max": 10, + "honour": 2880 + }, + { + "id": 27, + "type": 3, + "guildRank": 3, + "min": 11, + "max": 0, + "honour": 1440 + } +] \ No newline at end of file diff --git a/shared/resource/jsons/dic_zyz_gateActivityPoint.json b/shared/resource/jsons/dic_zyz_gateActivityPoint.json new file mode 100644 index 000000000..341ac981f --- /dev/null +++ b/shared/resource/jsons/dic_zyz_gateActivityPoint.json @@ -0,0 +1,27 @@ +[ + { + "id": 1, + "getPointWays": "每回合开始时获得", + "point": 10 + }, + { + "id": 2, + "getPointWays": "击杀小兵", + "point": 3 + }, + { + "id": 3, + "getPointWays": "击杀小boss", + "point": 30 + }, + { + "id": 4, + "getPointWays": "击杀大boss", + "point": 90 + }, + { + "id": 5, + "getPointWays": "防守成功", + "point": 100 + } +] \ No newline at end of file diff --git a/shared/resource/jsons/dic_zyz_guildActivity.json b/shared/resource/jsons/dic_zyz_guildActivity.json new file mode 100644 index 000000000..3f0a01b25 --- /dev/null +++ b/shared/resource/jsons/dic_zyz_guildActivity.json @@ -0,0 +1,32 @@ +[ + { + "id": 1, + "name": "蛮夷入侵", + "openDay": "3&0", + "duringTime": 900, + "startTime": 20, + "countDown": 5, + "warid": 7001, + "honour": 500 + }, + { + "id": 2, + "name": "诸侯混战", + "openDay": "2&4&6", + "duringTime": 900, + "startTime": 20, + "countDown": 5, + "warid": 7002, + "honour": 500 + }, + { + "id": 3, + "name": "粮草先行", + "openDay": "1&5", + "duringTime": 600, + "startTime": 20, + "countDown": 5, + "warid": 0, + "honour": 200 + } +] \ No newline at end of file diff --git a/shared/resource/jsons/dic_zyz_guildAuction.json b/shared/resource/jsons/dic_zyz_guildAuction.json new file mode 100644 index 000000000..95d62e3d5 --- /dev/null +++ b/shared/resource/jsons/dic_zyz_guildAuction.json @@ -0,0 +1,65 @@ +[ + { + "id": 1, + "name": "蛮夷入侵", + "min": 1, + "max": 1, + "rewards": "20001&60&2|21008&40&1|42008&20&2|42115&20&3" + }, + { + "id": 1, + "name": "蛮夷入侵", + "min": 2, + "max": 10, + "rewards": "20001&60&1|21008&40&1|42008&20&2|42115&20&3" + }, + { + "id": 1, + "name": "蛮夷入侵", + "min": 11, + "max": 0, + "rewards": "20002&60&1|21008&40&1|42008&20&2|42115&20&3" + }, + { + "id": 2, + "name": "诸侯混战", + "min": 1, + "max": 1, + "rewards": "20001&60&2|21008&40&1|42008&20&2|42115&20&3" + }, + { + "id": 2, + "name": "诸侯混战", + "min": 2, + "max": 10, + "rewards": "20001&60&1|21008&40&1|42008&20&2|42115&20&3" + }, + { + "id": 2, + "name": "诸侯混战", + "min": 11, + "max": 0, + "rewards": "20002&60&1|21008&40&1|42008&20&2|42115&20&3" + }, + { + "id": 3, + "name": "粮草先行", + "min": 1, + "max": 1, + "rewards": "20001&60&2|21008&40&1|42008&20&2|42115&20&3" + }, + { + "id": 3, + "name": "粮草先行", + "min": 2, + "max": 10, + "rewards": "20001&60&1|21008&40&1|42008&20&2|42115&20&3" + }, + { + "id": 3, + "name": "粮草先行", + "min": 11, + "max": 0, + "rewards": "20002&60&1|21008&40&1|42008&20&2|42115&20&3" + } +] \ No newline at end of file diff --git a/shared/resource/warJsons/7001.json b/shared/resource/warJsons/7001.json new file mode 100644 index 000000000..f5d653bc0 --- /dev/null +++ b/shared/resource/warJsons/7001.json @@ -0,0 +1,347 @@ +[ + { + "warId": 7001, + "actorName": "我军", + "actorId": 0, + "dataId": 0, + "relation": 0, + "outIndex": 0, + "dirction": 2, + "x": 6, + "y": 5, + "var": 0, + "lv": 0, + "hide": 0, + "initial_ai": 1, + "attribute": "&", + "skill": 0, + "seid": "&", + "star": 0, + "spine": 0, + "bossStage": 0, + "enemyType": 0, + "dest_oppo": 0 + }, + { + "warId": 7001, + "actorName": "我军", + "actorId": 0, + "dataId": 1, + "relation": 0, + "outIndex": 1, + "dirction": 2, + "x": 8, + "y": 5, + "var": 1, + "lv": 0, + "hide": 0, + "initial_ai": 1, + "attribute": "&", + "skill": 0, + "seid": "&", + "star": 0, + "spine": 0, + "bossStage": 0, + "enemyType": 0, + "dest_oppo": 0 + }, + { + "warId": 7001, + "actorName": "我军", + "actorId": 0, + "dataId": 2, + "relation": 0, + "outIndex": 2, + "dirction": 2, + "x": 10, + "y": 5, + "var": 2, + "lv": 0, + "hide": 0, + "initial_ai": 1, + "attribute": "&", + "skill": 0, + "seid": "&", + "star": 0, + "spine": 0, + "bossStage": 0, + "enemyType": 0, + "dest_oppo": 0 + }, + { + "warId": 7001, + "actorName": "我军", + "actorId": 0, + "dataId": 3, + "relation": 0, + "outIndex": 3, + "dirction": 2, + "x": 12, + "y": 5, + "var": 3, + "lv": 0, + "hide": 0, + "initial_ai": 1, + "attribute": "&", + "skill": 0, + "seid": "&", + "star": 0, + "spine": 0, + "bossStage": 0, + "enemyType": 0, + "dest_oppo": 0 + }, + { + "warId": 7001, + "actorName": "我军", + "actorId": 0, + "dataId": 4, + "relation": 0, + "outIndex": 4, + "dirction": 2, + "x": 14, + "y": 5, + "var": 4, + "lv": 0, + "hide": 0, + "initial_ai": 1, + "attribute": "&", + "skill": 0, + "seid": "&", + "star": 0, + "spine": 0, + "bossStage": 0, + "enemyType": 0, + "dest_oppo": 0 + }, + { + "warId": 7001, + "actorName": "我军", + "actorId": 0, + "dataId": 5, + "relation": 0, + "outIndex": 5, + "dirction": 2, + "x": 9, + "y": 5, + "var": 5, + "lv": 0, + "hide": 0, + "initial_ai": 1, + "attribute": "&", + "skill": 0, + "seid": "&", + "star": 0, + "spine": 0, + "bossStage": 0, + "enemyType": 0, + "dest_oppo": 0 + }, + { + "warId": 7001, + "actorName": "城门", + "actorId": 1083, + "dataId": 1001, + "relation": 1, + "outIndex": 6, + "dirction": 2, + "x": 10, + "y": 11, + "var": 1001, + "lv": 0, + "hide": 0, + "initial_ai": 2, + "attribute": "1&10000", + "skill": 0, + "seid": "&", + "star": 0, + "spine": 0, + "bossStage": 0, + "enemyType": 0, + "dest_oppo": 0 + }, + { + "warId": 7001, + "actorName": "步兵", + "actorId": 1001, + "dataId": 2001, + "relation": 2, + "outIndex": 0, + "dirction": 1, + "x": 8, + "y": 9, + "var": 2001, + "lv": 0, + "hide": 0, + "initial_ai": 4, + "attribute": "1&1400|2&1000|4&600|5&600|9&0|10&25000|11&20000|12&0|13&10000|14&10000|18&0", + "skill": 0, + "seid": "&", + "star": 0, + "spine": 0, + "bossStage": 0, + "enemyType": 2, + "dest_oppo": 1001 + }, + { + "warId": 7001, + "actorName": "步兵", + "actorId": 1001, + "dataId": 2002, + "relation": 2, + "outIndex": 1, + "dirction": 1, + "x": 9, + "y": 9, + "var": 2002, + "lv": 0, + "hide": 0, + "initial_ai": 4, + "attribute": "1&1400|2&1000|4&600|5&600|9&0|10&25000|11&20000|12&0|13&10000|14&10000|18&0", + "skill": 0, + "seid": "&", + "star": 0, + "spine": 0, + "bossStage": 0, + "enemyType": 0, + "dest_oppo": 1001 + }, + { + "warId": 7001, + "actorName": "步兵", + "actorId": 1001, + "dataId": 2003, + "relation": 2, + "outIndex": 2, + "dirction": 1, + "x": 10, + "y": 9, + "var": 2003, + "lv": 0, + "hide": 0, + "initial_ai": 4, + "attribute": "1&1400|2&1000|4&600|5&600|9&0|10&25000|11&20000|12&0|13&10000|14&10000|18&0", + "skill": 0, + "seid": "&", + "star": 0, + "spine": 0, + "bossStage": 0, + "enemyType": 2, + "dest_oppo": 1001 + }, + { + "warId": 7001, + "actorName": "枪兵", + "actorId": 1002, + "dataId": 2004, + "relation": 2, + "outIndex": 3, + "dirction": 1, + "x": 11, + "y": 9, + "var": 2004, + "lv": 0, + "hide": 0, + "initial_ai": 4, + "attribute": "1&1400|2&1000|4&600|5&600|9&0|10&25000|11&20000|12&0|13&10000|14&10000|18&0", + "skill": 0, + "seid": "&", + "star": 0, + "spine": 0, + "bossStage": 0, + "enemyType": 2, + "dest_oppo": 1001 + }, + { + "warId": 7001, + "actorName": "枪兵", + "actorId": 1002, + "dataId": 2005, + "relation": 2, + "outIndex": 4, + "dirction": 1, + "x": 9, + "y": 10, + "var": 2005, + "lv": 0, + "hide": 0, + "initial_ai": 4, + "attribute": "1&1400|2&1000|4&600|5&600|9&0|10&25000|11&20000|12&0|13&10000|14&10000|18&0", + "skill": 0, + "seid": "&", + "star": 0, + "spine": 0, + "bossStage": 0, + "enemyType": 2, + "dest_oppo": 1001 + }, + { + "warId": 7001, + "actorName": "枪兵", + "actorId": 1002, + "dataId": 2006, + "relation": 2, + "outIndex": 5, + "dirction": 1, + "x": 10, + "y": 10, + "var": 2006, + "lv": 0, + "hide": 0, + "initial_ai": 4, + "attribute": "1&1400|2&1000|4&600|5&600|9&0|10&25000|11&20000|12&0|13&10000|14&10000|18&0", + "skill": 0, + "seid": "&", + "star": 0, + "spine": 0, + "bossStage": 0, + "enemyType": 2, + "dest_oppo": 1001 + }, + { + "warId": 7001, + "actorName": "狼", + "actorId": 1014, + "dataId": 2007, + "relation": 2, + "outIndex": 6, + "dirction": 1, + "x": 10, + "y": 11, + "var": 2007, + "lv": 0, + "hide": 1, + "initial_ai": 4, + "attribute": "1&1400|2&1000|4&600|5&600|9&0|10&25000|11&20000|12&0|13&10000|14&10000|18&0", + "skill": 0, + "seid": "8001&", + "star": 0, + "spine": 0, + "bossStage": 1, + "enemyType": 3, + "dest_oppo": 1001 + }, + { + "warId": 7001, + "actorName": "怒狼王", + "actorId": 1035, + "dataId": 2008, + "relation": 2, + "outIndex": 7, + "dirction": 1, + "x": 11, + "y": 11, + "var": 2008, + "lv": 0, + "hide": 1, + "initial_ai": 4, + "attribute": "1&1400|2&1000|4&600|5&600|9&0|10&25000|11&20000|12&0|13&10000|14&10000|18&0", + "skill": 0, + "seid": "8001&", + "star": 0, + "spine": 0, + "bossStage": 1, + "enemyType": 3, + "dest_oppo": 1001 + } +] \ No newline at end of file diff --git a/shared/resource/warJsons/7002.json b/shared/resource/warJsons/7002.json new file mode 100644 index 000000000..b0bb6d4ff --- /dev/null +++ b/shared/resource/warJsons/7002.json @@ -0,0 +1,275 @@ +[ + { + "warId": 7002, + "actorName": "我军", + "actorId": 0, + "dataId": 0, + "relation": 0, + "outIndex": 0, + "dirction": 2, + "x": 6, + "y": 5, + "var": 0, + "lv": 0, + "hide": 0, + "initial_ai": 1, + "attribute": "&", + "skill": 0, + "seid": "&", + "star": 0, + "spine": 0, + "bossStage": 0 + }, + { + "warId": 7002, + "actorName": "我军", + "actorId": 0, + "dataId": 1, + "relation": 0, + "outIndex": 1, + "dirction": 2, + "x": 8, + "y": 5, + "var": 1, + "lv": 0, + "hide": 0, + "initial_ai": 1, + "attribute": "&", + "skill": 0, + "seid": "&", + "star": 0, + "spine": 0, + "bossStage": 0 + }, + { + "warId": 7002, + "actorName": "我军", + "actorId": 0, + "dataId": 2, + "relation": 0, + "outIndex": 2, + "dirction": 2, + "x": 10, + "y": 5, + "var": 2, + "lv": 0, + "hide": 0, + "initial_ai": 1, + "attribute": "&", + "skill": 0, + "seid": "&", + "star": 0, + "spine": 0, + "bossStage": 0 + }, + { + "warId": 7002, + "actorName": "我军", + "actorId": 0, + "dataId": 3, + "relation": 0, + "outIndex": 3, + "dirction": 2, + "x": 12, + "y": 5, + "var": 3, + "lv": 0, + "hide": 0, + "initial_ai": 1, + "attribute": "&", + "skill": 0, + "seid": "&", + "star": 0, + "spine": 0, + "bossStage": 0 + }, + { + "warId": 7002, + "actorName": "我军", + "actorId": 0, + "dataId": 4, + "relation": 0, + "outIndex": 4, + "dirction": 2, + "x": 14, + "y": 5, + "var": 4, + "lv": 0, + "hide": 0, + "initial_ai": 1, + "attribute": "&", + "skill": 0, + "seid": "&", + "star": 0, + "spine": 0, + "bossStage": 0 + }, + { + "warId": 7002, + "actorName": "我军", + "actorId": 0, + "dataId": 5, + "relation": 0, + "outIndex": 5, + "dirction": 2, + "x": 9, + "y": 5, + "var": 5, + "lv": 0, + "hide": 0, + "initial_ai": 1, + "attribute": "&", + "skill": 0, + "seid": "&", + "star": 0, + "spine": 0, + "bossStage": 0 + }, + { + "warId": 7002, + "actorName": "步兵", + "actorId": 1001, + "dataId": 2001, + "relation": 2, + "outIndex": 0, + "dirction": 1, + "x": 8, + "y": 9, + "var": 2001, + "lv": 0, + "hide": 0, + "initial_ai": 1, + "attribute": "1&1400|2&1000|4&600|5&600|9&0|10&25000|11&20000|12&0|13&10000|14&10000|18&0", + "skill": 0, + "seid": "&", + "star": 0, + "spine": 0, + "bossStage": 0 + }, + { + "warId": 7002, + "actorName": "步兵", + "actorId": 1001, + "dataId": 2002, + "relation": 2, + "outIndex": 1, + "dirction": 1, + "x": 9, + "y": 9, + "var": 2002, + "lv": 0, + "hide": 0, + "initial_ai": 1, + "attribute": "1&1400|2&1000|4&600|5&600|9&0|10&25000|11&20000|12&0|13&10000|14&10000|18&0", + "skill": 0, + "seid": "&", + "star": 0, + "spine": 0, + "bossStage": 0 + }, + { + "warId": 7002, + "actorName": "步兵", + "actorId": 1001, + "dataId": 2003, + "relation": 2, + "outIndex": 2, + "dirction": 1, + "x": 10, + "y": 9, + "var": 2003, + "lv": 0, + "hide": 0, + "initial_ai": 1, + "attribute": "1&1400|2&1000|4&600|5&600|9&0|10&25000|11&20000|12&0|13&10000|14&10000|18&0", + "skill": 0, + "seid": "&", + "star": 0, + "spine": 0, + "bossStage": 0 + }, + { + "warId": 7002, + "actorName": "枪兵", + "actorId": 1002, + "dataId": 2004, + "relation": 2, + "outIndex": 3, + "dirction": 1, + "x": 11, + "y": 9, + "var": 2004, + "lv": 0, + "hide": 0, + "initial_ai": 1, + "attribute": "1&1400|2&1000|4&600|5&600|9&0|10&25000|11&20000|12&0|13&10000|14&10000|18&0", + "skill": 0, + "seid": "&", + "star": 0, + "spine": 0, + "bossStage": 0 + }, + { + "warId": 7002, + "actorName": "枪兵", + "actorId": 1002, + "dataId": 2005, + "relation": 2, + "outIndex": 4, + "dirction": 1, + "x": 9, + "y": 10, + "var": 2005, + "lv": 0, + "hide": 0, + "initial_ai": 1, + "attribute": "1&1400|2&1000|4&600|5&600|9&0|10&25000|11&20000|12&0|13&10000|14&10000|18&0", + "skill": 0, + "seid": "&", + "star": 0, + "spine": 0, + "bossStage": 0 + }, + { + "warId": 7002, + "actorName": "枪兵", + "actorId": 1002, + "dataId": 2006, + "relation": 2, + "outIndex": 5, + "dirction": 1, + "x": 10, + "y": 10, + "var": 2006, + "lv": 0, + "hide": 0, + "initial_ai": 1, + "attribute": "1&1400|2&1000|4&600|5&600|9&0|10&25000|11&20000|12&0|13&10000|14&10000|18&0", + "skill": 0, + "seid": "&", + "star": 0, + "spine": 0, + "bossStage": 0 + }, + { + "warId": 7002, + "actorName": "城门", + "actorId": 1083, + "dataId": 2007, + "relation": 2, + "outIndex": 6, + "dirction": 1, + "x": 10, + "y": 11, + "var": 2007, + "lv": 0, + "hide": 0, + "initial_ai": 2, + "attribute": "1&10000", + "skill": 0, + "seid": "&", + "star": 0, + "spine": 0, + "bossStage": 1 + } +] \ No newline at end of file