// 军团活动蛮夷入侵城门血量等数据存储 import { WoodenHorse, Event, WoodenHorseMember, RaceActivityRankParam, RaceActivityData, MemberInfo, GuildInfo } from "../../domain/battleField/guildActivity"; import { getRaceEventItems, gameData } from "../../pubUtils/data"; import { GuildModel } from "../../db/Guild"; import { sendSingleRaceActEndMsg } from "./guildActivityService"; import { RACE_EVENT_TYPE, RACE_EVENT_EFFECT_TYPE, RACE_EVENT, PUSH_ROUTE, GUILD_ACTIVITY_STATUS, RACE_ACTIVITY_STATUS } from "../../consts"; import { getRandEelm, sortArrRandom, getRandResultByMember } from "../../pubUtils/util"; import { sendMessageToGuildWithSuc, sendMessageToUserWithSuc } from "../pushService"; import { GUILDACTIVITY } from "../../pubUtils/dicParam"; // 粮草先行 export class RaceActivityObject { data: RaceActivityData = new RaceActivityData(); public getStatus() { return this.data.status; } public getItem(roleId: string) { return this.data.items.get(roleId)||[] } // 是否加入过 public getMember(guildCode: string, roleId: string) { let member = this.data.members.get(guildCode)||[]; return member.find(cur => cur.roleId == roleId); } public async updateMemberSid(guildCode: string, serverId: number, roleId: string, sid: string) { let member = this.getMember(guildCode, roleId); if(!member) return member; let wh = await this.getWoodenHorse(guildCode, serverId); let index = wh.members.findIndex(cur => cur.roleId == roleId); if(index != -1) { wh.members[index].updateSid(sid); } return member } public async memberLeave(guildCode: string, serverId: number, roleId: string) { let member = this.getMember(guildCode, roleId); if(!member) return member; let wh = await this.getWoodenHorse(guildCode, serverId); this.leaveMemberSid(wh, roleId); return member } public async memberQuit(guildCode: string, serverId: number, roleId: string) { let member = this.getMember(guildCode, roleId); if(!member) return; member.quit(); let wh = await this.getWoodenHorse(guildCode, serverId); this.leaveMemberSid(wh, roleId, true); } public dismiss(guildCode: string) { let guild = this.data.guildList.find(guild => guild.guildCode == guildCode); if(!guild) return; guild.dismiss(); this.data.woodenHorses.delete(guildCode); } // 加入member private pushMember(guildCode: string, roleId: string, job: number, code: string) { if(!this.data.members.has(guildCode)) { this.data.members.set(guildCode, []); } this.data.members.get(guildCode).push(new MemberInfo(roleId, job, code)); } public getGuilds() { return this.data.guildList.filter(cur => !cur.isDissmiss); } public getMembersOfGuild(guildCode: string) { let members = this.data.members.get(guildCode)||[]; return members.filter(cur => !cur.isQuit); } public getAllWoodenHorses() { return this.data.woodenHorses; } public async getRanks(serverId: number, guildCode?: string) { let myRank: RaceActivityRankParam = null; let ranks: RaceActivityRankParam[] = []; let myIndex = -1; for(let [_, _wh] of this.data.woodenHorses) { if(_wh.serverId == serverId && _wh.status != GUILD_ACTIVITY_STATUS.WAITING) { let wh = await this.getWoodenHorse(_wh.guildCode, _wh.serverId); if(wh) { ranks.push(new RaceActivityRankParam(wh)); } } } ranks.sort((a, b) => { if(a.sortDistance == b.sortDistance) { return a.sortTime - b.sortTime; } else { return b.sortDistance - a.sortDistance; } }).map((wh, index) => { wh.setRank(index + 1); if(guildCode && wh.code == guildCode) { myRank = wh; myIndex = index; } return wh }); if(!myRank && guildCode) { let myWoodenHorse = await this.getWoodenHorse(guildCode, serverId); if(myWoodenHorse) myRank = new RaceActivityRankParam(myWoodenHorse); } return { guildRank: ranks, myGuildRank: myRank, myIndex } } public async joinWoodenHorse(guildCode: string, roleId: string, roleName: string, serverId: number, sid: string, job: number, code: string, isDebug = false) { let woodenHorse = await this.getWoodenHorse(guildCode, serverId, isDebug); if(!woodenHorse) return false; this.joinMember(woodenHorse, roleId, roleName, sid, code) this.pushMember(guildCode, roleId, job, code); if(this.data.status == GUILD_ACTIVITY_STATUS.START) { // this.sendRandItemsToMembers([member], woodenHorse, woodenHorse.remainItems); this.woodenHorseStartRace(woodenHorse); } return await this.getWoodenHorse(guildCode, serverId); } // 获取某个军团的木马状态 public async getWoodenHorse(guildCode: string, serverId: number, isDebug: boolean = false) { if(!this.data.woodenHorses.has(guildCode)) { this.initEvents(guildCode); if(isDebug) { this.data.woodenHorses.set(guildCode, new WoodenHorse(guildCode, guildCode, 0, serverId, this.data.allStartTime, false)); this.data.guildList.push(new GuildInfo(serverId, guildCode)); } else { let guild = await GuildModel.findByCode(guildCode, serverId); if(!guild) return undefined; let { name: guildName, guildCe } = guild; this.data.woodenHorses.set(guildCode, new WoodenHorse(guildCode, guildName, guildCe, serverId, this.data.allStartTime, false)); this.data.guildList.push(new GuildInfo(serverId, guildCode)); } } let woodenHorse = this.data.woodenHorses.get(guildCode); let events = this.data.events.get(guildCode)||[]; let needSendEnd = this.calCurWoodenHorse(woodenHorse, events); if (needSendEnd) { // 抵达后发送奖励,发送消息,结算 await sendSingleRaceActEndMsg(guildCode, woodenHorse); } this.handleItemsByWoodenHorse(woodenHorse); return woodenHorse; } public async useItem(serverId: number, fromRoleId: string, sid: string, fromGuild: string, toGuild: string, id: number, count: number, debug = false) { if(!debug) { let rec = this.handleItems(fromRoleId, sid, [{id, count: -1 * count}]); if(!rec) return false; } let event = new Event(id, fromGuild, toGuild, count); if(!this.data.events.has(toGuild)) { this.data.events.set(toGuild, [event]); } else { let events = this.data.events.get(toGuild)||[]; events.push(event); this.data.events.set(toGuild, events); } let { guildRank, myGuildRank, myIndex } = await this.getRanks(serverId, toGuild); if(myGuildRank) { let ranks = this.getScreenHorseRanks(guildRank, myIndex); for(let rank of ranks||[]) { if(rank) await sendMessageToGuildWithSuc(rank.code, PUSH_ROUTE.GUILD_RACE_EVENT, { timestamp: Date.now(), events: [event] }); } let timeout = setTimeout(async () => { await this.getScreenHorses(serverId, guildRank, myIndex, true); clearTimeout(timeout); }, event.startTime - Date.now()) } else { await sendMessageToGuildWithSuc(toGuild, PUSH_ROUTE.GUILD_RACE_EVENT, { timestamp: Date.now(), events: [event] }); } return event; } // 定时任务到,开始比赛,设置开始赛道,发放初始道具 public startRace() { this.data.status = GUILD_ACTIVITY_STATUS.START; if(this.data.allStartTime == 0) this.data.allStartTime = Date.now(); let guildCodes = new Array(); for(let [code, woodenHorse] of this.data.woodenHorses) { if(woodenHorse.memberCnt > 0) { this.woodenHorseStartRace(woodenHorse); guildCodes.push(code); } } return guildCodes; } public stopRace() { this.data.status = GUILD_ACTIVITY_STATUS.END; for(let [_, woodenHorse] of this.data.woodenHorses) { woodenHorse.status = RACE_ACTIVITY_STATUS.END; } } private woodenHorseStartRace(woodenHorse: WoodenHorse) { woodenHorse.status = RACE_ACTIVITY_STATUS.START; woodenHorse.time = Date.now(); woodenHorse.startTime = Date.now(); woodenHorse.allStartTime = this.data.allStartTime; let members = woodenHorse.members; let normalItems = getRaceEventItems(); this.sendRandItemsToMembers(members, woodenHorse, normalItems); } private sendRandItemsToMembers(members: WoodenHorseMember[], woodenHorse: WoodenHorse, items: Map) { let notReceiveMembers = members.filter(member => !member.isReceived() && member.isOnline); let memberCnt = notReceiveMembers.length; console.log('##### SEND_START', memberCnt, notReceiveMembers) let addItems: Map = new Map(); for(let [id, {total, max}] of items) { let randResult = getRandResultByMember(total, max, memberCnt); for(let i = 0; i < memberCnt; i++) { let member = notReceiveMembers[i]; let count = randResult.arr[i]||0; if(count > 0) { if(!addItems.has(member.roleId)) { addItems.set(member.roleId, { member: member, items: [] }); } addItems.get(member.roleId).items.push({ id, count }); } } this.setRemainItem(woodenHorse, id, randResult.remain, max); } for(let [_roleId, {member, items}] of addItems) { member.setReceived(true); this.handleItems(member.roleId, member.sid, items); } } // 初始进入就随机9个灵球事件 public initEvents(guildCode: string) { if(!this.data.events.get(guildCode)) { this.data.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.data.events.get(guildCode).push(event); } else if (type == RACE_EVENT_TYPE.EVENT) { let event = new Event(events[index], guildCode, guildCode, 1, distance); this.data.events.get(guildCode).push(event); index ++; } } } public getEvents(guildCode: string, distance: number) { let events = this.data.events.get(guildCode)||[]; let result = new Array(); for(let event of events) { let { endTime, endDistance } = event; if(!event.useDistance() && endTime) { if(Date.now() > endTime) continue; } if(event.useDistance() && 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.data.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.data.items.set(roleId, items); sendMessageToUserWithSuc(roleId, PUSH_ROUTE.RACE_ITEM_UPDATE, { items: sendItems }, sid); return items; } public send(guildCode: string) { this.data.hasSentGuild.push(guildCode); } public hasSend(guildCode: string) { return this.data.hasSentGuild.indexOf(guildCode) != -1; } public async calServerHorses(serverId: number) { let { guildRank: ranks } = await this.getRanks(serverId); // 找到往前和往后2艘船 for (let i = 0; i < ranks.length; i++) { await this.getScreenHorses(serverId, ranks, i, true); } } private getScreenHorseRanks(ranks: RaceActivityRankParam[], i: number) { let curRank = ranks[i]; if(!curRank || curRank.durability == 0) return; let beforeHorses: RaceActivityRankParam[] = [], afterHorses: RaceActivityRankParam[] = [], beforeCnt = 0, afterCnt = 0; for(let j = i - 1; j >= 0; j--) { if(ranks[j]) { beforeHorses.push(ranks[j]); if(ranks[j].durability > 0) beforeCnt ++ } if(beforeCnt >= 2) break; } for(let j = i + 1; j < ranks.length; j++) { if(ranks[j]) { afterHorses.push(ranks[j]); if(ranks[j].durability > 0) afterCnt++; } if(afterCnt >= 2) break; } // 发送 // console.log('send', woodenHorseList.length); return [...beforeHorses.reverse(), curRank, ...afterHorses] } private async getScreenHorses(serverId: number, ranks: RaceActivityRankParam[], i: number, needSend: boolean) { let curRank = ranks[i]; if(!curRank) return; // 发送 // console.log('send', woodenHorseList.length); let wh = await this.getWoodenHorse(curRank.code, serverId); let screenRank = this.getScreenHorseRanks(ranks, i); let woodenHorseList: WoodenHorse[] = []; if(curRank.durability != 0) { for(let rank of screenRank) { if(rank.code == curRank.code) { woodenHorseList.push(wh); } else { let wh = await this.getWoodenHorse(rank.code, serverId); woodenHorseList.push(wh); } } } if(needSend) { let events = this.getEvents(curRank.code, wh ? wh.distance : 0); await sendMessageToGuildWithSuc(curRank.code, PUSH_ROUTE.GUILD_RACE_UPDATE, { timestamp: Date.now(), woodenHorseList, guildRank: ranks, myGuildRank: curRank, events }); } return woodenHorseList } /** * 根据时间计算当前木马速度距离耐久等 * @param events * @returns {boolean} needSendEnd 是否跑到终点发送结束新号 */ public calCurWoodenHorse(woodenHorse: WoodenHorse, events: Event[]): boolean { if(woodenHorse.status == RACE_ACTIVITY_STATUS.END) return false; if(woodenHorse.status == RACE_ACTIVITY_STATUS.START) { if(woodenHorse.distance >= GUILDACTIVITY.RACEACTIVITY_LENGTH) { woodenHorse.distance = GUILDACTIVITY.RACEACTIVITY_LENGTH; woodenHorse.status = RACE_ACTIVITY_STATUS.END; woodenHorse.speed = 0; return true; } woodenHorse.distance = Math.floor((woodenHorse.distance + (Date.now() - woodenHorse.time)/1000 * woodenHorse.speed ) * 1000)/1000; // 1位小数点 woodenHorse.time = Date.now(); } let effectiveEvents = new Array(); for(let i = 0; i < events.length; i++) { let event = events[i]; if(event.useDistance() && event.startDistance && woodenHorse.distance > event.startDistance) { let startTime = Date.now() - Math.floor((woodenHorse.distance - event.startDistance) / woodenHorse.speed); event.setStartTime(startTime); // 距离生效的事件的实际生效时间,主要用于速度叠加顺序 } let isEffective = false; if(event.useTime()) { if(event.startTime <= Date.now() && event.endTime > Date.now()) { isEffective = true; } if(event.endTime < Date.now()) { if(event.endTime && event.startTime == event.endTime) { isEffective = true; } let index = events.findIndex(cur => cur.id == event.id); events.splice(index, 1); } } if(event.useDistance()) { if(event.startDistance <= woodenHorse.distance && event.endDistance > woodenHorse.distance) { isEffective = true; } if(event.endDistance < woodenHorse.distance) { if(event.endDistance && event.startDistance == event.endDistance) { isEffective = true; } let index = events.findIndex(cur => cur.id == event.id); events.splice(index, 1); } } if(isEffective) { effectiveEvents.push(event); } } effectiveEvents.sort((a, b) => a.startTime - b.startTime); woodenHorse.speed = GUILDACTIVITY.RACE_INIT_SPEED + woodenHorse.memberCnt * GUILDACTIVITY.RACE_PER_SPEED; for(let { id, count, endTime } of effectiveEvents) { this.calEvent(woodenHorse, id, count, endTime); } if(woodenHorse.durability <= 0) { woodenHorse.status = RACE_ACTIVITY_STATUS.BREAK; woodenHorse.speed = 0; } return false; } private calEvent(woodenHorse: WoodenHorse, id: number, count: number = 1, endTime?: number) { let { effect } = gameData.raceActivityEvents.get(id); switch (id) { case RACE_EVENT.LIANNU: if (woodenHorse.shieldTime < Date.now()) { if (woodenHorse.shield >= count) { woodenHorse.shield -= count; } else { woodenHorse.shield = 0; woodenHorse.durability -= (count - woodenHorse.shield) * effect[0]; if(woodenHorse.durability > 100) woodenHorse.durability = 100; if(woodenHorse.durability < 0) woodenHorse.durability = 0; } } break; case RACE_EVENT.GUISHOUYINFU: if (woodenHorse.shieldTime < Date.now()) { woodenHorse.time = endTime; woodenHorse.speed = effect[1]||0.1; } break; case RACE_EVENT.FENGCHE: case RACE_EVENT.WUGUIBANYUNFU: woodenHorse.speed *= Math.pow(1 + effect[0] / 100, count); break; case RACE_EVENT.LUDUN: woodenHorse.shield += count * effect[0]; break; case RACE_EVENT.TIANSHIDUNFU: woodenHorse.shieldTime = endTime; break; case RACE_EVENT.JIASU_1: woodenHorse.speed *= Math.pow(1 + effect[0] / 100, count); break; case RACE_EVENT.JIASU_2: woodenHorse.speed += effect[0]; break; case RACE_EVENT.HUIFU_1: woodenHorse.durability += effect[0]; if(woodenHorse.durability > 100) woodenHorse.durability = 100; if(woodenHorse.durability < 0) woodenHorse.durability = 0; break; case RACE_EVENT.JIANSU_1: woodenHorse.speed *= Math.pow(1 - effect[0] / 100, count); break; case RACE_EVENT.JIANSU_2: woodenHorse.speed -= effect[0]; if(woodenHorse.speed < 0) woodenHorse.speed = 0; break; case RACE_EVENT.SHANGHAI_1: woodenHorse.durability -= effect[0]; if(woodenHorse.durability > 100) woodenHorse.durability = 100; if(woodenHorse.durability < 0) woodenHorse.durability = 0; break; case RACE_EVENT.ITEM: let onlineMembers = woodenHorse.members.filter(member => member.isOnline); let memberCnt = onlineMembers.length; console.log('##### HANDLE_ITEM', memberCnt, onlineMembers) let ranMember: WoodenHorseMember[] = getRandEelm(onlineMembers, GUILDACTIVITY.RACEACTIVITY_EVENT_MEMBERCNT); if(ranMember.length <= 0) ranMember = onlineMembers; for(let member of ranMember) { let item = gameData.raceEventItems; member.addTempItems(item); } let normalItems = getRaceEventItems(); for(let [id, {total, max}] of normalItems) { let randResult = getRandResultByMember(total, max, memberCnt); for(let i = 0; i < onlineMembers.length; i++) { let count = randResult.arr[i]||0; if(count > 0) { onlineMembers[i].addTempItem({ id, count }); normalItems.get(id).total -= count; } } } break; } } public joinMember(woodenHorse: WoodenHorse, roleId: string, roleName: string, sid: string, code: string) { let index = woodenHorse.members.findIndex(cur => cur.roleId == roleId) if(index == -1) { let member = new WoodenHorseMember(roleId, roleName, sid, code); woodenHorse.members.push(member); woodenHorse.speed++; woodenHorse.memberCnt++; return member; } else { return woodenHorse.members[index]; } } public leaveMemberSid(woodenHorse: WoodenHorse, roleId: string, isQuit = false) { let index = woodenHorse.members.findIndex(cur => cur.roleId == roleId); if(index != -1) { woodenHorse.members[index].leave(); } if(isQuit) woodenHorse.memberCnt--; if(woodenHorse.memberCnt < 0) woodenHorse.memberCnt = 0; } public setRemainItem(woodenHorse: WoodenHorse, id: number, remain: number, max: number) { woodenHorse.remainItems.set(id, { total: remain, max }); } }