import { GUILDACTIVITY } from "../../pubUtils/dicParam"; import { SimpleGuildRankParam, SimpleRoleRankParam, GuildRankParam } from '../rank' import { prop } from "@typegoose/typegoose"; import { CITY_STATUS, RACE_EVENT_TYPE, RACE_EVENT_EFFECT_TYPE, REDIS_KEY, RACE_EVENT, STATUS } from "../../consts"; import { gameData, getRaceEventItems } from "../../pubUtils/data"; import { GuildModel } from "../../db/Guild"; import { getRandEelm, sortArrRandom, resResult } from "../../pubUtils/util"; import { RoleType } from "../../db/Role"; import { setRank, getMyUnionRank, setRankWithoutUserInfo } from "../../services/redisService"; import { RewardInter } from "../../pubUtils/interface"; import { pinus } from "pinus"; import { homedir } from "os"; import { sendSingleRaceActEndMsg } from "../../services/guildActivityService"; import { getGuildChannelSid } from "../../services/chatService"; export class GateMembersRec { roleId: string; round: number = 0; enemies: number[] = []; constructor(roleId: string) { this.roleId = roleId; } } // 军团活动蛮夷入侵城门血量等数据存储 export class GateActivityObject { private gateHp: Map = new Map(); // 城门血条,每个军团有一条血条 guildCode => hp private members: Map> = new Map(); // 每个军团参与的成员 guildCode => [{roleId, job}] private membersRecord: Map = new Map(); // 每个成员的回合数和敌军数,防刷 recCode => GateMembersRec private guilds: Map = new Map(); // 参加的所有军团 server => [guildCode] public getObj(guildCode: string, serverId: number) { console.log(guildCode, serverId) return { gateHp: this.getGateHpAndInc(guildCode), members: this.members.get(guildCode)||[], memberesRecord: [...this.membersRecord], guilds: this.guilds.get(serverId)||[] } } public getGateHpAndInc(guildCode: string, inc: number = 0) { let gateHp = this.gateHp.get(guildCode); if(!this.gateHp.has(guildCode)) { gateHp = GUILDACTIVITY.GATEACTIVITY_GATEHP; } if(inc != 0) { gateHp += inc; this.gateHp.set(guildCode, gateHp); } if(gateHp <= 0) gateHp = 0; return gateHp } public getGuilds() { return this.guilds; } public pushMembers(guildCode: string, serverId: number, roleId: string, job: number) { if(this.members.has(guildCode)) { let members = this.members.get(guildCode); if(members.findIndex(cur => cur.roleId == roleId) == -1) { members.push({ roleId, job }); } } else { let arr = new Array(); arr.push(roleId); this.members.set(guildCode, [{ roleId, job }]); } if(this.guilds.has(serverId)) { let arr = this.guilds.get(serverId); if(!arr.includes(guildCode)) arr.push(guildCode); this.guilds.set(serverId, arr); } else { this.guilds.set(serverId, [guildCode]); } } public getMemberRecord(recCode: string, roleId: string) { return this.membersRecord.get(recCode)||new GateMembersRec(roleId) } public setMemberRecord(recCode: string, newMemberRecord: GateMembersRec) { this.membersRecord.set(recCode, newMemberRecord); } public delMemberRecord(recCode: string) { this.membersRecord.delete(recCode); } public delGuildRecord(guildCode: string, serverId: number) { let guildList = this.guilds.get(serverId); if(guildList) { let index = guildList.indexOf(guildCode); guildList.splice(index, 1); } this.members.delete(guildCode); } } // 军团诸侯混战等数据 export class CityActivityObject { private gateHp: Map = new Map(); // 城门血条,每个cityId有一条血条 serverId&cityId => gateHp private members: Map> = new Map(); // 每个军团参与的成员 guildCode => [{roleId, job}] private cities: Map = new Map(); // 各个城池参与的军团 serverId&cityId => [guildCode] private guilds: Map = new Map(); // 军团所在的城池 guildCode => cityId private historyCity: Map = new Map(); // 获取自己打开过的城池的页面 roleId => cityId private getKey(serverId: number, cityId: number) { return `${serverId}_${cityId}`; } private decodeKey(key: string) { let arr = key.split('_'); return { serverId: parseInt(arr[0]), cityId: parseInt(arr[1]) } } public getObj( serverId: number, cityId: number, guildCode: string) { let key = this.getKey(serverId, cityId); return { gateHp: this.gateHp.get(key), members: this.members.get(guildCode), city: this.cities.get(key), guild: this.guilds.get(guildCode) } } public getAllCities() { let allCities = new Array<{ serverId: number, cityId: number, guildCodes: string[]}>(); let serverlists = new Array(); for(let [key, guildCodes] of this.cities) { let { serverId, cityId } = this.decodeKey(key); allCities.push({ serverId, cityId, guildCodes }); if(!serverlists.includes(serverId)) serverlists.push(serverId); } return {cities: allCities, serverlists}; } public getGuildsInCity(serverId: number, cityId: number) { let key = this.getKey(serverId, cityId); return this.cities.get(key)||[]; } public getMembersOfGuild(guildCode: string) { return this.members.get(guildCode)||[]; } public getGateHpAndInc(serverId: number, cityId: number, maxHp: number, inc: number = 0) { let key = this.getKey(serverId, cityId); let gateHp = this.gateHp.get(key); if(!this.gateHp.has(key)) { gateHp = maxHp; } if(inc != 0) { gateHp += inc; this.gateHp.set(key, gateHp); } if(gateHp <= 0) gateHp = 0; return gateHp } public getHistoryCity(roleId: string) { return this.historyCity.get(roleId) } public setHistoryCity(roleId: string, cityId: number) { return this.historyCity.set(roleId, cityId); } public pushMembers(guildCode: string, roleId: string, job: number) { if(this.members.has(guildCode)) { let members = this.members.get(guildCode); if(members.findIndex(cur => cur.roleId == roleId) == -1) { members.push({ roleId, job }); } } else { let arr = new Array(); arr.push(roleId); this.members.set(guildCode, [{ roleId, job }]); } } public pushGuild(guildCode: string, serverId: number, cityId: number) { let key = this.getKey(serverId, cityId); if(!this.cities.has(key)) { this.cities.set(key, [guildCode]); } else { let arr = this.cities.get(key); if(!arr.includes(guildCode)) { arr.push(guildCode); } } this.guilds.set(guildCode, cityId); } public delCityRecord(cityId: number, serverId: number) { let key = this.getKey(serverId, cityId); let guildCodes = this.cities.get(key)||[]; for(let guildCode of guildCodes) { this.members.delete(guildCode); this.guilds.delete(guildCode); } this.cities.delete(key); } } // 军团诸侯混战等数据 export class RaceActivityObject { private status = 0; // 活动状态 0-未开始 1-已开始 2-已结束 // TODO 写进const表 public guildList: Array<{ serverId: number, guildCode: string }> = []; // 所有军团 private members: Map> = new Map(); // 每个军团参与的成员 guildCode => [{roleId, job}] private woodenHorses: Map = new Map(); // 每个军团的木牛流马 guildCode => WoodenHorse private events: Map = new Map(); // 每个军团遇到的事件 private items: Map = new Map(); // 每个玩家的道具 roleId => [{id, count}] public getItem(roleId: string) { return this.items.get(roleId)||[] } // 是否加入过 public hasJoin(guildCode: string, roleId: string) { let member = this.members.get(guildCode)||[]; return member.findIndex(cur => cur.roleId == roleId) != -1; } // 加入member private pushMember(guildCode: string, roleId: string, job: number) { if(!this.members.has(guildCode)) { this.members.set(guildCode, []); } this.members.get(guildCode).push({roleId, job}); } public getMembersOfGuild(guildCode: string) { return this.members.get(guildCode)||[]; } public getAllWoodenHorses() { return this.woodenHorses; } public async joinWoodenHorse(guildCode: string, roleId: string, roleName: string, serverId: number, sid: string, job: number) { let woodenHorse = await this.getWoodenHorse(guildCode, serverId); if(!woodenHorse) return false; woodenHorse.joinMember(roleId, roleName, sid) this.pushMember(guildCode, roleId, job); if(this.status == 1) { // TODO 写进const表 let item = getRaceEventItems(); this.handleItems(roleId, sid, item); } return woodenHorse; } // 获取某个军团的木马状态 public async getWoodenHorse(guildCode: string, serverId: number) { if(!this.woodenHorses.has(guildCode)) { this.initEvents(guildCode); let guild = await GuildModel.findByCode(guildCode, serverId); if(!guild) return false; let leader = guild.leader; let guildRankParam = new GuildRankParam(guild.icon, guild.name, guild.lv, leader); await setRank(REDIS_KEY.RACE_ACTIVITY, serverId, guild.code, 0, Math.pow(10, 11) - guild.guildCe, guildRankParam, true); let { name: guildName, guildCe } = guild; this.woodenHorses.set(guildCode, new WoodenHorse(guildCode, guildName, guildCe, serverId)); this.guildList.push({guildCode, serverId}); if(this.status == 1) { // TODO 写进const表 this.woodenHorseStartRace(this.woodenHorses.get(guildCode)); } } let woodenHorse = this.woodenHorses.get(guildCode); let events = this.events.get(guildCode)||[]; let needSendEnd = woodenHorse.calCurWoodenHorse(events); if(woodenHorse.status == 1 || woodenHorse.status == 2) { // 更新距离 // TODO 写进const表 await setRankWithoutUserInfo(REDIS_KEY.RACE_ACTIVITY, serverId, guildCode, woodenHorse.distance, Math.floor((woodenHorse.time - woodenHorse.startTime)/1000), true, false); if (needSendEnd) { // 抵达后发送奖励,发送消息,结算 await sendSingleRaceActEndMsg(guildCode, woodenHorse); } } this.handleItemsByWoodenHorse(woodenHorse); return woodenHorse; } public async useItem(fromRoleId: string, sid: string, fromGuild: string, toGuild: string, id: number, count: number) { let rec = this.handleItems(fromRoleId, sid, [{id, count: -1 * count}]); if(!rec) return false; let event = new Event(id, fromGuild, toGuild, count); if(!this.events.has(toGuild)) { this.events.set(toGuild, [event]); } else { let events = this.events.get(toGuild)||[]; events.push(event); this.events.set(toGuild, events); } let chatSid = await getGuildChannelSid(toGuild); pinus.app.rpc.chat.guildRemote.sendRaceEvent.toServer(chatSid, toGuild, [event]); return event; } // 定时任务到,开始比赛,设置开始赛道,发放初始道具 public startRace() { this.status = 1; // TODO 写进const表 let guildCodes = new Array(); for(let [code, woodenHorse] of this.woodenHorses) { this.woodenHorseStartRace(woodenHorse); guildCodes.push(code); } return guildCodes; } private woodenHorseStartRace(woodenHorse: WoodenHorse) { woodenHorse.status = 1; // TODO 写进const表 woodenHorse.time = Date.now(); woodenHorse.startTime = Date.now(); let members = woodenHorse.members; for(let { roleId, sid } of members) { let item = getRaceEventItems(); this.handleItems(roleId, sid, item); } } // 初始进入就随机9个灵球事件 public initEvents(guildCode: string) { if(!this.events.get(guildCode)) { this.events.set(guildCode, []); } let dicEncounter = gameData.raceActivityEncounter; // 距离=>事件类型 let goodEventNum = Math.floor(dicEncounter.eventNum/2); let badEventNum = dicEncounter.eventNum - goodEventNum; let dicRaceTypes = gameData.raceTypes.get(RACE_EVENT_TYPE.EVENT); let goodEvents = getRandEelm(dicRaceTypes.get(RACE_EVENT_EFFECT_TYPE.GOOD), goodEventNum); let badEvents = getRandEelm(dicRaceTypes.get(RACE_EVENT_EFFECT_TYPE.BAD), badEventNum); let events = sortArrRandom(goodEvents.concat(badEvents)); let index = 0; for(let [distance, type] of dicEncounter.events) { if(type == RACE_EVENT_TYPE.ITEM) { let event = new Event(RACE_EVENT.ITEM, guildCode, guildCode, 1, distance); this.events.get(guildCode).push(event); } else if (type == RACE_EVENT_TYPE.EVENT) { let event = new Event(events[index], guildCode, guildCode, 1, distance); this.events.get(guildCode).push(event); index ++; } } } public getEvents(guildCode: string, distance: number) { let events = this.events.get(guildCode)||[]; let result = new Array(); for(let event of events) { let { endTime, endDistance } = event; if(endTime) { if(Date.now() > endTime) continue; } if(endDistance) { if(distance > endDistance) continue; } result.push(event); } return result; } private handleItemsByWoodenHorse(woodenHorse: WoodenHorse) { let { members } = woodenHorse; for(let curMember of members) { let { roleId, sid, items = [] } = curMember; if(items.length > 0) this.handleItems(roleId, sid, items); curMember.items = []; } } private handleItems(roleId: string, sid: string, incItems: {id: number, count: number}[]) { let items = this.items.get(roleId)||[]; let sendItems = new Array<{id: number, count: number}>(); for(let {id, count: inc} of incItems) { let curItem = items.find(cur => cur.id == id); if(!curItem) { curItem = { id, count: 0 }; items.push(curItem); } if(curItem.count + inc < 0 ) return false; if(inc != 0) { curItem.count += inc; sendItems.push({ id, count: curItem.count }); } } this.items.set(roleId, items); let uids = [{uid: roleId, sid}]; pinus.app.get('channelService').pushMessageByUids('onRaceItemUpdate', resResult(STATUS.SUCCESS, { items: sendItems }), uids); return items; } public deleteFromList(guildCode: string) { let index = this.guildList.findIndex(cur => cur.guildCode == guildCode); this.guildList.splice(index, 1); } } class WoodenHorseMember { roleId: string; roleName: string; sid: string; items?: RewardInter[]; } // 木牛流马 export class WoodenHorse { @prop({required: true}) serverId: number = 0; // 服务器id @prop({required: true}) guildCode: string = ""; // 军团code 木马的唯一标识 @prop({required: true}) guildName: string = ""; // 军团名 @prop({required: true}) guildCe: number = 0; // 军团战力 @prop({required: true}) status: number = 0; // 状态 0-停止 1-开启 2-结束 @prop({required: true}) speed: number = GUILDACTIVITY.RACE_INIT_SPEED; // 速度 @prop({required: true}) durability: number = 100; // 耐久度 @prop({required: true}) distance: number = 0; // 距离 @prop({required: true}) startTime: number = 0; // 开始时间 @prop({required: true}) time: number = 0; // 到达时间 @prop({required: true}) memberCnt: number = 0; // 成员人数 @prop({required: true}) members: WoodenHorseMember[] = []; // 成员 @prop({required: true}) shield: number = 0; // 护盾数量 @prop({required: true}) shieldTime: number = 0; // 天师盾符 @prop({required: true}) stopContinueTime: number = 0; // 鬼手阴符 constructor(guildCode: string, guildName: string, guildCe: number, serverId: number) { this.guildCode = guildCode; this.guildName = guildName; this.guildCe = guildCe; this.serverId = serverId; } /** * 根据时间计算当前木马速度距离耐久等 * @param events * @returns {boolean} needSendEnd 是否跑到终点发送结束新号 */ public calCurWoodenHorse(events: Event[]): boolean { console.log('**', JSON.stringify(events)) if(this.status == 2) return false; // TODO 写进const表 if(this.status == 0 && this.stopContinueTime && Date.now() > this.stopContinueTime) { this.status = 1; this.stopContinueTime = 0; } if(this.status == 1) { if(this.distance >= GUILDACTIVITY.RACEACTIVITY_LENGTH) { this.distance = GUILDACTIVITY.RACEACTIVITY_LENGTH; this.status = 2; return true; } console.log('*********', this.distance, Date.now(), this.speed) this.distance = Math.floor((this.distance + (Date.now() - this.time)/1000 * this.speed ) * 1000)/1000; // 1位小数点 this.time = Date.now(); } let effectiveEvents = new Array(); let delEvents = new Array(); for(let i = 0; i < events.length; i++) { let event = events[i]; if(!event.startTime && event.startDistance && this.distance > event.startDistance) { let startTime = Date.now() - Math.floor((this.distance - event.startDistance) / this.speed); event.setStartTime(startTime); // 距离生效的事件的实际生效时间,主要用于速度叠加顺序 } let isEffective = false; if(event.startTime <= Date.now() && event.endTime > Date.now()) { isEffective = true; } else if (event.startDistance <= this.distance && event.endDistance > this.distance) { isEffective = true; } else if (event.endTime < Date.now() || event.endDistance < this.distance) { if(event.endTime && event.startTime == event.endTime) { isEffective = true; } if(event.endDistance && event.startDistance == event.endDistance) { isEffective = true; } delEvents.push(i); } if(isEffective) { effectiveEvents.push(event); } } for(let i of delEvents) events.splice(i, 1); // 删除过期事件 effectiveEvents.sort((a, b) => a.startTime - b.startTime); this.speed = GUILDACTIVITY.RACE_INIT_SPEED + this.memberCnt * GUILDACTIVITY.RACE_PER_SPEED; for(let { id, count, endTime } of effectiveEvents) { this.calEvent(id, count, endTime); } return false; } private calEvent(id: number, count: number = 1, endTime?: number) { let { effect } = gameData.raceActivityEvents.get(id); switch (id) { case RACE_EVENT.LIANNU: if (this.shieldTime < Date.now()) { if (this.shield >= count) { this.shield -= count; } else { this.shield = 0; this.durability -= (count - this.shield) * effect[0]; if(this.durability > 100) this.durability = 100; if(this.durability < 0) this.durability = 0; } } break; case RACE_EVENT.GUISHOUYINFU: if (this.shieldTime < Date.now()) { this.stopContinueTime = endTime; this.status = 0; // TODO 写进const表 } break; case RACE_EVENT.FENGCHE: case RACE_EVENT.WUGUIBANYUNFU: this.speed *= Math.pow(1 + effect[0] / 100, count); break; case RACE_EVENT.LUDUN: this.shield += count * effect[0]; break; case RACE_EVENT.TIANSHIDUNFU: this.shieldTime = endTime; break; case RACE_EVENT.JIASU_1: this.speed *= Math.pow(1 + effect[0] / 100, count); break; case RACE_EVENT.JIASU_2: this.speed += effect[0]; break; case RACE_EVENT.HUIFU_1: this.durability += effect[0]; if(this.durability > 100) this.durability = 100; if(this.durability < 0) this.durability = 0; break; case RACE_EVENT.JIANSU_1: this.speed *= Math.pow(1 - effect[0] / 100, count); break; case RACE_EVENT.JIANSU_2: this.speed -= effect[0]; break; case RACE_EVENT.SHANGHAI_1: this.durability -= effect[0]; if(this.durability > 100) this.durability = 100; if(this.durability < 0) this.durability = 0; break; case RACE_EVENT.ITEM: let ranMember: WoodenHorseMember[] = getRandEelm(this.members, GUILDACTIVITY.RACEACTIVITY_EVENT_MEMBERCNT); if(ranMember.length <= 0) ranMember = this.members; for(let obj of ranMember) { let item = gameData.raceEventItems; obj.items = (obj.items||[]).concat(item); } for(let obj of this.members) { let item = getRaceEventItems(); obj.items = (obj.items||[]).concat(item); } break; } if(this.durability <= 0) { this.status = 0; // TODO 写进const表 this.speed = 0; } } public joinMember(roleId: string, roleName: string, sid: string) { if(this.members.findIndex(cur => cur.roleId == roleId) == -1) { this.members.push({ roleId, roleName, sid }); this.speed++; this.memberCnt++; } return this; } } export class Event { @prop({required: true}) id: number; @prop({required: true}) fromGuild: string; @prop({required: true}) toGuild: string; @prop({required: true}) startTime?: number; @prop({required: true}) startDistance?: number; @prop({required: true}) endTime?: number; @prop({required: true}) endDistance?: number; @prop({required: true}) count: number = 1; constructor(id: number, fromGuild: string, toGuild: string, count?: number, distance?: number) { let dicEvent = gameData.raceActivityEvents.get(id); this.id = id; this.fromGuild = fromGuild; this.toGuild = toGuild; if(distance) { this.startDistance = distance; this.endDistance = this.startDistance + dicEvent.continueDistance; } if(dicEvent.effectTime) { this.startTime = Date.now() + dicEvent.effectTime * 1000; this.endTime = this.startTime + dicEvent.continueTime * 1000; } if(count) { this.count = count; } } setStartTime(time: number) { this.startTime = time; } } export interface GuildRankParams { guildRank: SimpleGuildRankParam[], myGuildRank: SimpleGuildRankParam, memberRank?: SimpleRoleRankParam[], myMemberRank?: SimpleRoleRankParam } export class Member { @prop({required: true}) roleId: string; // 玩家id @prop({required: true}) job: number; // 在军团的职位 } export class CityParam { cityId: number = 0; // 城池id guardGuildCode: string = ''; // 占领军团id guardGuildName: string = ''; // 占领军团名 declareCount: number = 0; // 宣战数 status: number = CITY_STATUS.NOT_OPEN; // 城池对于我军状态 constructor(cityId: number) { this.cityId = cityId; } }