import { STATUS } from './../consts/statusCode'; import { getGamedata } from './gamedata'; import { HeroModel } from '../db/Hero'; import { RoleModel } from '../db/Role'; import { PvpDefenseModel } from '../db/PvpDefense'; import Actor from './actor'; import fs = require('fs'); import path = require('path'); import { DicRandomEffectPool } from './dictionary/DicRandomEffectPool'; const moment = require('moment'); export function genCode(len) { const chars = '123456789ABCDEFGHJKLMNPQRSTWXYZabcdefghijklmnopqrstuvwxyz'; const charArr = chars.split(''); let code = ''; for (let i = 0; i < len; i++) { code += charArr[Math.floor(Math.random() * charArr.length)]; } return code; } export function decodeStr(type, str, splitForm='|') { if(str == '&'|| !str) { return [] } else { return str.split(splitForm).map(cur => { return decodeStrSingle(type,cur); }); }; } export function decodeStrSingle(type, str) { //单层,返回对象而不是数组 let last = str.substr(str.length-1, 1); if(last == '&') str = str.substr(0, str.length - 1); if(str == '&'|| !str) { return {} } else { let arr = str.split('&'); let result: any; switch (type) { case 'fixReward': { let [id, count] = arr; if(isNaN(id) || isNaN(count)) throw new Error('data table format wrong'); result = { id: parseInt(id), count: parseInt(count)}; break; } case 'conditionReward': { let [id, count, condition] = arr; if(isNaN(id) || isNaN(count)) throw new Error('data table format wrong'); result = { id: parseInt(id), count: parseInt(count), condition: parseInt(condition) }; break; } case 'randomReward': { let [id, count, frequency] = arr; if(isNaN(id) || isNaN(count)) throw new Error('data table format wrong'); result = { id: parseInt(id), count: parseInt(count), frequency: parseInt(frequency) }; break; } case 'suitLevel': { let [min, max] = arr; if(isNaN(min) || isNaN(max)) throw new Error('data table format wrong'); result = { min: parseInt(min), max: parseInt(max) }; break; } case 'suitLevelSingle': { let [min, max] = arr; console.log(min, max) if(max) { if(isNaN(min) || isNaN(max)) throw new Error('data table format wrong'); result = { min: parseInt(min), max: parseInt(max) }; break; } else { if(isNaN(min)) throw new Error('data table format wrong'); result = { min: parseInt(min) }; break; } } case 'attribute': { let [id, value] = arr; if(isNaN(id) || isNaN(value)) throw new Error('data table format wrong'); result = { id: parseInt(id), value: parseInt(value) }; break; } case 'point': { let [x] = arr; if(isNaN(x)) throw new Error('data table format wrong'); result = x; break; } case 'decimalReward': { let [id, count] = arr; if(isNaN(id) || isNaN(count)) throw new Error('data table format wrong'); result = { id: parseInt(id), count: parseFloat(count) }; break; } case 'towerTaskCondition': { let [type, param, cnt] = arr; if(isNaN(type) || isNaN(param)|| isNaN(cnt)) throw new Error('data table format wrong'); result = { type: parseInt(type), param: parseInt(param), cnt: parseInt(cnt) }; break; } case 'possibility': { let [id, weight] = arr; if(isNaN(id) || isNaN(weight)) throw new Error('data table format wrong'); result = { id: parseInt(id), weight: parseInt(weight) }; break; } case 'cost': { let [id, count] = arr; if(isNaN(id) || isNaN(count)) throw new Error('data table format wrong'); result = { id: parseInt(id), count: parseInt(count)}; break; } } return result; }; } /** * 将 | 分隔的字符串解析为数组,如:a|b|c 解析为[a, b, c] * @param str 要解析的字符串 */ export function decodeArrayStr(str: string, splitForm='|') { let last = str.substr(str.length-1, 1); if(last == '&') str = str.substr(0, str.length - 1); if(str == '') { return new Array() } else { return str.split(splitForm); } } /** * 将 | 和 & 分隔的字符串解析为 Map,,如:a&b&c|c&d&f 解析为 [[a,b,c], [c,d,f]] * @param str 要解析的字符串 */ export function decodeArrayListStr(str: string) { return decodeArrayStr(str).map(cur => decodeArrayStr(cur, '&')); } /** * 将 | 和 & 分隔的字符串解析为 Map,,如:a&b|c&d|e&f 解析为 Map {a=>b, c=>d, e=>f} * @param str 要解析的字符串 */ export function decodeIdCntArrayStr(str: string, multi: number) { const strArr = decodeArrayStr(str); console.log('decodeIdCntArrayStr: ', strArr); const strMap = new Map(); strArr.forEach(item => { const kv = item.split('&'); strMap.set(kv[0], multi? Math.ceil(parseInt(kv[1]) * multi): kv[1]); }); return strMap; } // 计算当前武将战力 export async function calculateSumCE(roleId: string, type: number, param: { num?: number, heroes?: Array }) { let sum; if(type == 1) { // 最高num人历史最高战力和 sum = await HeroModel.sumTopHeroCe(roleId, param.num||0); } else if(type == 2) { // 所有人战力和 sum = await HeroModel.sumHeroCe(roleId); } return sum; } // 计算当前武将战力 export function calculateCE(heroInfo: {hid: number, lv: number }) { let { hid, lv } = heroInfo; // 假设所有属性和等级的关系是简单的线性关系 let dicHero = getGamedata('dic_zyz_hero'); let curDicHero = dicHero.find(cur => cur.heroId == hid); let { atk, matk, def, mdef, agi, luk, atk_up, matk_up, def_up, mdef_up, agi_up, luk_up } = curDicHero; atk += lv * atk_up; matk += lv * matk_up; def += lv * def_up; mdef += lv * mdef_up; agi += lv * agi_up; luk += lv * luk_up; // 假设战力为所有属性的简单加法 let ce = atk + matk + def + mdef + agi + luk; return ce; } export function getRandomByLen(arr: Array):number export function getRandomByLen(arr: Array):DicRandomEffectPool export function getRandomByLen(arr: Array): any { let len = arr.length; return arr[Math.floor(Math.random() * len)] } export function getRandomWithWeight(randomList: any) { let len = randomList.reduce((pre, cur) => { return pre + cur.weight||1; }, 0); let index = Math.floor(Math.random() * len); let result = { dic: null, index: -1 }; for(let i = 0; i < randomList.length; i++) { let {weight = 0} = randomList[i]; if(index < weight) { result.dic = randomList[i]; result.index = i; break; } index -= weight; } return result } /** * 传入两个时间,返回按照时间差计算,第二个时间比第一个晚几天 * @param preTime 之前的时间 * @param proTime 之后的时间 */ export function deltaDays(preTime: Date, proTime: Date): number { return moment(proTime).diff(moment(preTime), "days"); } /** * 计算按照每 x 天 y 点刷新一次,是否应该刷新 * @param preTime 基准时间 * @param curTime 当前时间 * @param hour 几点刷新 * @param deltaDay 间隔几天刷新,默认每天刷新(deltaDay = 1) */ export function shouldRefresh(preTime: Date, now: Date, hour: number, deltaDay = 1): boolean { if(!preTime) return true; let curTime = new Date(now.getTime()); let refeshTime = setLocalHours(hour, curTime); console.log(refeshTime - preTime.getTime(), (deltaDay>=1?deltaDay-1:0) * 24 * 60 * 60 * 1000, curTime.getTime(), refeshTime) if (refeshTime - preTime.getTime() > (deltaDay>=1?deltaDay-1:0) * 24 * 60 * 60 * 1000 && curTime.getTime() >= refeshTime) { return true; } return false; } /** * 获取 x天后的y点 * @param curTime 现在时间 * @param hour 几点刷新 */ export function getRefTime(now = new Date(), hour: number, day = 0) { let curTime = new Date(now.getTime()); let offset = curTime.getTimezoneOffset(); // 时差,单位分 let checkHour = hour - 8 + offset/60; if(checkHour < 0) { checkHour = 24 + checkHour; if (curTime.getHours() >= checkHour) { curTime.setDate(curTime.getDate() + 1); } } else { if (curTime.getHours() < checkHour) { curTime.setDate(curTime.getDate() - 1); } } let today = setLocalHours(hour, curTime); return new Date(today + day * 24 * 60 * 60 * 1000); } /** * 从一个数组中随机返回不重复的 cnt 个元素数组 * @param source 原数组 * @param cnt 返回随机元素个数 */ export function getRandEelm(source: Array = [], cnt = 1): Array { if (cnt > source.length) return []; if (cnt === source.length) return source; let idxs = new Set(); while(1) { let rand = Math.floor(Math.random() * source.length); idxs.add(rand); if (idxs.size == cnt) { break; } } return source.filter((_item, idx) => idxs.has(idx)); } /** * 在给定数值的浮动范围中随机一个值 * @param base 基础数值 * @param ratio 随机范围:base 值上下浮动 ratio(小于 1 的整数) * @param decimal 返回值保留的小数位数 */ export function getRandValue(base: number, ratio: number, decimal = 2): number { return parseFloat((base * (1 - ratio + Math.random() * ratio * 2)).toFixed(decimal)); } export function resResult(status: {code: number, simStr: string}, data = null, customMsg = '') { const { code, simStr } = status; if (code !== STATUS.SUCCESS.code) { console.log(`normal err, code: ${code}, des: ${customMsg || simStr}`); } return {code, msg: customMsg || simStr, data}; } // 消除 js 浮点计算bug export const cal = { add: function(a:number, b:number) { var c:number, d:number, e:number; try { c = a.toString().split(".")[1].length; } catch (f) { c = 0; } try { d = b.toString().split(".")[1].length; } catch (f) { d = 0; } e = Math.pow(10, Math.max(c, d)); return (this.mul(a, e) + this.mul(b, e)) / e; }, sub: function sub(a:number, b:number) { var c:number, d:number, e:number; try { c = a.toString().split(".")[1].length; } catch (f) { c = 0; } try { d = b.toString().split(".")[1].length; } catch (f) { d = 0; } e = Math.pow(10, Math.max(c, d)); return (this.mul(a, e) - this.mul(b, e)) / e; }, mul: function mul(a:number, b:number) { var c = 0, d = a.toString(), e = b.toString(); try { c += d.split(".")[1].length; } catch (f) {} try { c += e.split(".")[1].length; } catch (f) {} return Number(d.replace(".", "")) * Number(e.replace(".", "")) / Math.pow(10, c); }, div: function div(a:number, b:number) { var c:number, d:number, e = 0, f = 0; try { e = a.toString().split(".")[1].length; } catch (g) {} try { f = b.toString().split(".")[1].length; } catch (g) {} c = Number(a.toString().replace(".", "")); d = Number(b.toString().replace(".", "")); return this.mul(c / d, Math.pow(10, f - e)); } }; //计算公式 export function calculateNum(ratio: {A: number, B: number}, params: {num: number}, defaultVal = 0) { // result = a * num + b try { let {A, B} = ratio; let {num} = params; let result = A * num + B; if(isNaN(result)) { console.error('calculate wrong: ', A, B, num) return defaultVal; } else { return result; } }catch(e) { console.error(e); return defaultVal; } } export function setLocalHours(hour: number, curTime = new Date()) { // curTime: 格林威治时间 let offset = curTime.getTimezoneOffset(); // 格林威治时间和本地时间之间的时差(分) let localTime = new Date(curTime.getTime() + offset * 60 * 1000 + 8 * 60 * 60 * 1000); // 中国时间 localTime.setUTCHours(hour, 0, 0, 0); // 中国的几点 return localTime.getTime() - 8 * 60 * 60 * 1000; // 回到格林威治时间 } export async function updateCe(roleId: string, hero: any ) { let {hid, ce: oldCe, historyCe} = hero; let actor = new Actor(); actor.initHero(hero); let ce = actor.calculateCE(); if(ce != oldCe) { // 更新总战力&最强五人战力 await RoleModel.updateSumCe(roleId, hid, ce, oldCe); // 更新pvp防守阵容战力 await PvpDefenseModel.updateCe(roleId, hid, ce, oldCe); // 更新武将战力,历史最高战力 await HeroModel.updateCe(roleId, hid, ce, oldCe, historyCe); } } export function ratioReward(rewardStr: string, ratio: number): string { let rewards = decodeIdCntArrayStr(rewardStr, ratio); let res = ''; for (let [k, v] of rewards) { res += `${k}&${v}|`; } return res.substring(0, res.length - 1); } export function getItems(str:string) { let arr = new Array<{id: number, count: number}>(); let strArr = str.split('|'); for (let item of strArr) { var itemArr = item.split('&'); arr.push({ id : parseInt(itemArr[0]), count : parseInt(itemArr[1]) }); } return arr; } export function deepCopy (obj) { if (typeof obj !== 'object' || obj === null) { return obj; } var target; if (obj instanceof Array) { target = []; obj.forEach(element => { target.push(deepCopy(element)); }); } else { target = {}; for (const key in obj) { if (obj.hasOwnProperty(key)) { const element = obj[key]; if (!!element || element == 0) { target[key] = deepCopy(element); } } } } return target; }; export function readJsonFile(fileName: string) { const folder = 'jsons'; return fs.readFileSync(path.resolve(__dirname, `../resource/${folder}/${fileName}.json`)).toString('utf8').replace(/^\uFEFF/, ''); } export function readWarJsonFileList() { const folder = 'warJsons'; return fs.readdirSync(__dirname + `/../resource/${folder}`) .filter(cur => {return cur.indexOf('.') != 0}) .map(file => { return fs.readFileSync(path.resolve(__dirname, `../resource/${folder}/${file}`)).toString('utf8'); }); } // 字典表常用解析方法 // 解析物品 {"id": number, "count": number} 格式 export function parseGoodStr(str: string) { let result = new Array<{id: number, count: number}>(); if(!str) return result; let decodeArr = decodeArrayListStr(str); for(let [id, count] of decodeArr) { if(isNaN(parseInt(id)) || isNaN(parseInt(count))) { throw new Error('data table format wrong'); } result.push({id: parseInt(id), count: parseInt(count)}); } return result } // 数字列表 export function parseNumberList(str: string) { let res = new Array(); if(!str) return res; let arr = decodeArrayStr(str, '&'); for(let g of arr) { if(g === "") continue; res.push(parseInt(g)); } return res; }