413 lines
15 KiB
TypeScript
413 lines
15 KiB
TypeScript
import { Channel, pinus } from 'pinus';
|
||
import { getRandValueByMinMax, getRandEelm, decodeIdCntArrayStr, compareVersion } from '../pubUtils/util';
|
||
import { DEFAULT_HEROES, LINEUP_NUM, ROLE_SELECT, TALENT_RELATION_TYPE, TERAPH_RANDOM, SYSTEM_OPEN_ID, GuideUnloadNum, CHECK_HERO_CONSUME, ABI_STAGE, AUTHOR_BOOK_LIMIT_TYPE } from "../consts";
|
||
import { DicTeraph } from '../pubUtils/dictionary/DicTeraph';
|
||
import { Teraph, RoleModel, RoleType, RoleUpdate } from '../db/Role';
|
||
import { SCHOOL } from '../pubUtils/dicParam';
|
||
import { gameData, getEquipByJobClassAndEPlace, getHeroInitTalent, getHeroStarByQuality, getJobByGradeAndClass } from '../pubUtils/data';
|
||
import { SchoolModel } from '../db/School';
|
||
import { SclResultInter, SclPosInter, RewardInter, ItemInter } from '../pubUtils/interface';
|
||
import { Connect, EPlace, HeroModel, HeroSkin, HeroType, HeroUpdate, Talent } from '../db/Hero';
|
||
import { SkinUpdate } from '../db/Skin';
|
||
import { Figure } from '../domain/dbGeneral';
|
||
import { pick } from 'underscore';
|
||
import { Reward } from '../domain/battleField/pvp';
|
||
import { CheckMeterial } from './role/checkMaterial';
|
||
import IP2Region from "ip2region";
|
||
import { getServerCreateTime } from './redisService';
|
||
import { checkWhiteList } from '../pubUtils/sysUtil';
|
||
import { nowSeconds } from '../pubUtils/timeUtil';
|
||
import { ServerlistModel } from '../db/Serverlist';
|
||
import { AuthorBookModel, AuthorBookType } from '../db/AuthorBook';
|
||
const query = new IP2Region({ disableIpv6: true });
|
||
|
||
|
||
export async function getTeraphStrengthenResult(role: RoleType, count: number, dicTeraph: DicTeraph, teraph: Teraph) {
|
||
let criAttr: number[] = [], times = 0;
|
||
let check = new CheckMeterial(role.roleId, role);
|
||
for(let i = 0; i < count; i++) {
|
||
let attrs: number[] = []; // 可以强化的属性
|
||
dicTeraph.mainAttrMax.forEach((max, id) => {
|
||
if (teraph.attr.get(id) < max) {
|
||
attrs.push(id);
|
||
}
|
||
});
|
||
if(attrs.length <= 0) break; // 如果所有属性都到最高
|
||
let isEnough = await check.decrease(dicTeraph.upMaterial);
|
||
|
||
if(!isEnough) break; // 消耗不足
|
||
|
||
// let num = getRandValueByMinMax(TERAPH_RANDOM.MIN, TERAPH_RANDOM.MAX); // 强化时随机增加 2-4 属性
|
||
let arr = getRandEelm(attrs, 1); // 随机出的属性id
|
||
let critical = getRandValueByMinMax(0, 100);//属性暴击率
|
||
if(teraph.criCount == undefined || teraph.count == undefined) {
|
||
teraph.criCount = 0;
|
||
teraph.count = 0;
|
||
}
|
||
let isCritical = critical <= dicTeraph.criRate; // 每10次需要2次保底暴击
|
||
if(!isCritical && Math.floor((teraph.count + 2)/10) * 2 > teraph.criCount) {
|
||
isCritical = true;
|
||
} else if (isCritical && Math.ceil(teraph.count/10) * 2 <= teraph.criCount) {
|
||
isCritical = false;
|
||
}
|
||
teraph.count ++;
|
||
if(isCritical) teraph.criCount ++;
|
||
|
||
let criEffect = isCritical?dicTeraph.criEffect: 1; // 暴击效果
|
||
for (let attrId of arr) {
|
||
let val = teraph.attr.get(attrId); // 已有的强化值
|
||
val += dicTeraph.mainAttrUp.get(attrId) * criEffect;
|
||
let max = dicTeraph.mainAttrMax.get(attrId);
|
||
if(val > max) {
|
||
val = max;
|
||
let attrIndex = attrs.indexOf(attrId);
|
||
attrs.splice(attrIndex, 1);
|
||
}
|
||
teraph.attr.set(attrId, val);
|
||
if(isCritical) criAttr.push(attrId);
|
||
}
|
||
times++;
|
||
|
||
}
|
||
return { times, consumes: check.getConsume(), criAttr }
|
||
}
|
||
|
||
export async function getSimpleRoleInfo(roleId: string) {
|
||
if (!roleId) return null;
|
||
let role = await RoleModel.findByRoleId(roleId, ROLE_SELECT.SHOW_SIMPLE, true);
|
||
return pick(role, ['roleId', 'roleName', 'head', 'frame', 'spine', 'lv', 'title', 'job', 'quitTime', 'loginTime', 'vLv', 'guildName', 'serverId', 'userInfo']);
|
||
}
|
||
|
||
export async function getSimpleRoleInfos(roleIds: string[]) {
|
||
if (!roleIds || !roleIds.length) return null;
|
||
let roles = await RoleModel.findByRoleIds(roleIds, ROLE_SELECT.SHOW_SIMPLE, true);
|
||
return roles.map(role => pick(role, ['roleId', 'roleName', 'head', 'frame', 'spine', 'lv', 'title', 'job', 'quitTime', 'loginTime', 'vLv', 'guildName', 'serverId', 'userInfo']));
|
||
}
|
||
|
||
/**
|
||
* 百家学宫
|
||
* @param roleId
|
||
*/
|
||
export async function getSchoolList(roleId: string) {
|
||
|
||
const dicPosition = decodeIdCntArrayStr(SCHOOL.SCHOOL_POSITION, 1); // id=>isOpen
|
||
|
||
const userSchoolList = await SchoolModel.findByRoleId(roleId);
|
||
|
||
let school = new Array<SclResultInter>();
|
||
gameData.school.forEach((dicSchool) => {
|
||
let position = new Array<SclPosInter>();
|
||
dicPosition.forEach((isOpen, dicId) => {
|
||
let id = parseInt(dicId);
|
||
|
||
let userSchool = userSchoolList.find(cur => cur.schoolId == dicSchool.id && cur.positionId == id);
|
||
if (userSchool) {
|
||
position.push({
|
||
id,
|
||
hid: userSchool.hid,
|
||
isOpen: userSchool.isOpen
|
||
});
|
||
} else {
|
||
position.push({
|
||
id,
|
||
hid: 0,
|
||
isOpen: !!isOpen
|
||
});
|
||
}
|
||
});
|
||
|
||
school.push({
|
||
id: dicSchool.id,
|
||
position
|
||
});
|
||
});
|
||
return school;
|
||
}
|
||
|
||
function getDefaultHeroInfos(heroes: Map<number, HeroUpdate>, skins: Map<number, SkinUpdate>) {
|
||
const initInfos: { heroInfo: HeroUpdate, skinInfo: SkinUpdate}[] = [];
|
||
for(let hid of DEFAULT_HEROES) {
|
||
initInfos.push({
|
||
heroInfo: heroes.get(hid),
|
||
skinInfo: skins.get(hid)
|
||
});
|
||
}
|
||
return initInfos;
|
||
}
|
||
|
||
export function getDefaultRoleInfo(heroes: Map<number, HeroUpdate>, skins: Map<number, SkinUpdate>, role:RoleUpdate, figureInfo: { heads: Figure[], frames: Figure[], spines: Figure[] }) {
|
||
const initInfos = getDefaultHeroInfos(heroes, skins);
|
||
return { initInfos, role, figureInfo };
|
||
}
|
||
|
||
export function getInitHeroById(hid: number) {
|
||
return {
|
||
heroInfo: pinus.app.get('initHeroes').get(hid),
|
||
skinInfo: pinus.app.get('initSkins').get(hid)
|
||
}
|
||
}
|
||
|
||
export function addConsumeToHero(oldConsume: Reward[] = [], consumes: ItemInter[] = []) {
|
||
if(!oldConsume) oldConsume = [];
|
||
let consumeMap = new Map<number, number>();
|
||
for(let { id, count = 1 } of consumes) {
|
||
if(!consumeMap.has(id)) {
|
||
consumeMap.set(id, count);
|
||
} else {
|
||
consumeMap.set(id, consumeMap.get(id) + count);
|
||
}
|
||
|
||
}
|
||
let newConsume: Reward[] = [];
|
||
for(let {id, count} of oldConsume) {
|
||
if(consumeMap.has(id)) {
|
||
newConsume.push({ id, count: count + consumeMap.get(id)});
|
||
consumeMap.delete(id);
|
||
} else {
|
||
newConsume.push({ id, count });
|
||
}
|
||
}
|
||
for(let [id, count] of consumeMap) {
|
||
newConsume.push({ id, count });
|
||
}
|
||
return newConsume;
|
||
}
|
||
|
||
export function addConnect(connections: Connect[], curConnect: Connect) {
|
||
let newConnections: Connect[] = [];
|
||
let index = -1;
|
||
for(let i = 0; i < connections.length; i++) {
|
||
if(connections[i].shipId == curConnect.shipId) {
|
||
index = i;
|
||
newConnections.push(curConnect);
|
||
} else {
|
||
newConnections.push(connections[i]);
|
||
}
|
||
}
|
||
if(index == -1) {
|
||
newConnections.push(curConnect);
|
||
}
|
||
return newConnections;
|
||
}
|
||
|
||
export function checkUnlockTalentCondition(hid: number, id: number, talents: Talent[], usedTalentPoint: number) {
|
||
let dicHero = gameData.hero.get(hid);
|
||
let dicHeroTalent = gameData.heroTalent.get(id);
|
||
if(!dicHeroTalent) return false;
|
||
|
||
if(dicHero.talentId != dicHeroTalent.talentId) return false;
|
||
|
||
// 累计消耗n点天赋点
|
||
if(dicHeroTalent.preTotalPoint > usedTalentPoint) return false;
|
||
// 互斥的天赋没有被解锁
|
||
for(let { type, ids } of dicHeroTalent.relation) {
|
||
if(type == TALENT_RELATION_TYPE.CONFLICT) {
|
||
let hasTalent = talents.find(talent => ids.indexOf(talent.id) != -1);
|
||
if(hasTalent) return false;
|
||
}
|
||
}
|
||
// 前置节点
|
||
let orFlag = false; // 只要其中一个满足就可以
|
||
let maxPrecost = 0; // 前置节点最多的消耗
|
||
for(let arr of dicHeroTalent.prepositionId) {
|
||
let andFlag = true; // arr内所有节点都需要满足
|
||
for(let id of arr) {
|
||
let curTalent = talents.find(cur => cur.id == id);
|
||
if(curTalent) {
|
||
let dic = gameData.heroTalent.get(id);
|
||
if(dic) {
|
||
let allCost = 0;
|
||
for(let { lv, cost } of dic.level) {
|
||
if(curTalent.level >= lv) allCost += cost;
|
||
}
|
||
if(allCost > maxPrecost) maxPrecost = allCost;
|
||
}
|
||
} else {
|
||
andFlag = false;
|
||
}
|
||
}
|
||
if(andFlag) orFlag = true;
|
||
}
|
||
if(!orFlag) return false;
|
||
// 在前置节点中消耗至少n点天赋点(在有多个前置节点时,取最高的1个)
|
||
if(dicHeroTalent.preSinglePoint > maxPrecost) return false;
|
||
return true;
|
||
}
|
||
|
||
export function updateSkinTalent(skins: HeroSkin[], newTalent: Talent, usedTalentPoint: number) {
|
||
let newSkins: HeroSkin[] = [];
|
||
let newTalents: Talent[] = [];
|
||
for(let skin of skins) {
|
||
if(skin.enable) {
|
||
let hasNewTalent = false;
|
||
for(let talent of skin.talent) {
|
||
if(talent.id == newTalent.id) {
|
||
newTalents.push(newTalent);
|
||
hasNewTalent = true;
|
||
} else {
|
||
newTalents.push(talent);
|
||
}
|
||
}
|
||
if(!hasNewTalent) newTalents.push(newTalent);
|
||
newSkins.push({...skin, talent: newTalents, usedTalentPoint })
|
||
} else {
|
||
newSkins.push(skin);
|
||
}
|
||
}
|
||
return { newSkins, newTalents };
|
||
}
|
||
|
||
export function initSkinTalent(skins: HeroSkin[], isAll = false) {
|
||
let newSkins: HeroSkin[] = [];
|
||
for(let skin of skins) {
|
||
if(isAll || skin.enable) {
|
||
newSkins.push({ ...skin, talent: getHeroInitTalent(skin.skinId), usedTalentPoint: 0 });
|
||
} else {
|
||
newSkins.push(skin);
|
||
}
|
||
}
|
||
return newSkins
|
||
}
|
||
|
||
export function checkSystemIsOpen(role: RoleType, id: SYSTEM_OPEN_ID) {
|
||
if(!role || !role.hasInit || !role.guide) return false;
|
||
|
||
let dicSystemOpenTime = gameData.sysOpenTime.get(id);
|
||
if(!dicSystemOpenTime) return false;
|
||
|
||
if(dicSystemOpenTime.warId) {
|
||
return role.warStar.findIndex(cur => cur.id == dicSystemOpenTime.warId) != -1;
|
||
} else {
|
||
return role.lv >= dicSystemOpenTime.lv
|
||
}
|
||
}
|
||
|
||
function calSchoolPoint(quality: number, star: number, colorStar: number) {
|
||
let _star = colorStar > 0? colorStar: star;
|
||
let point = (quality - 1) * 6 + _star;
|
||
return point > 0? point: 0;
|
||
}
|
||
|
||
export async function getSchoolPoint(roleId: string) {
|
||
let schools = await SchoolModel.findByRoleId(roleId);
|
||
let hids = schools.filter(cur => cur.isOpen && cur.hid > 0).map(school => school.hid);
|
||
let heroes = await HeroModel.findByHidRange(hids, roleId);
|
||
return heroes.reduce((pre, cur) => {
|
||
return pre + calSchoolPoint(cur.quality, cur.star, cur.colorStar);
|
||
}, 0);
|
||
}
|
||
|
||
export async function getIpLocation(ip: string) {
|
||
try {
|
||
const res = query.search(ip);
|
||
console.log('##### setIpLocation', res)
|
||
return res.province||res.country||'未知';
|
||
} catch(e) {
|
||
console.error('setIpLocation', e)
|
||
}
|
||
}
|
||
|
||
// 玩家是否是新玩家
|
||
export async function checkIsNewUser(version: string, minVersion: string, uid: number, latestServerUniqId: number) {
|
||
let versionFlag = compareVersion(version||'0.0.0.0', minVersion||'0.0.0.1');
|
||
if(versionFlag >= 0) {
|
||
let hasRole = await RoleModel.checkHasRole(uid, latestServerUniqId);
|
||
return !hasRole
|
||
} else {
|
||
return true;
|
||
}
|
||
}
|
||
|
||
export function calStarUpConsume(hero: HeroType) {
|
||
let originConsumes = hero.consumes||[];
|
||
if(CHECK_HERO_CONSUME) {
|
||
let dicHero = gameData.hero.get(hero.hid);
|
||
let dicJob = gameData.job.get(dicHero?.jobid);
|
||
if(dicHero && dicJob) {
|
||
let decrease = 0;
|
||
for(let star = dicHero.initialStar; star <= hero.star; star++) {
|
||
let curDicHeroStar = getHeroStarByQuality(dicJob.job_class, dicHero.quality, star);
|
||
if(!curDicHeroStar) continue;
|
||
let max = star == hero.star ? hero.starStage: ABI_STAGE.END;
|
||
for (let i = 0; i < max; i++) {
|
||
decrease += curDicHeroStar.advanceUpFragmentNum;
|
||
}
|
||
}
|
||
for(let quality = dicHero.quality; quality < hero.quality; quality++) {
|
||
let curDicHeroQualityUp = gameData.heroQualityUp.get(quality);
|
||
if (!curDicHeroQualityUp) continue;
|
||
decrease += curDicHeroQualityUp.fragmentNum;
|
||
}
|
||
|
||
return decreaseConsume(originConsumes, dicHero.pieceId, decrease);
|
||
}
|
||
}
|
||
return { consumes: originConsumes, newConsumes: [] };
|
||
}
|
||
|
||
// 从原始消耗里减去消耗
|
||
function decreaseConsume(origin: Reward[], pieceId: number, decrease: number) {
|
||
let consumes: Reward[] = [], newConsumes: Reward[] = [];
|
||
for(let { id, count } of origin) {
|
||
if(id == pieceId) {
|
||
consumes.push({ id, count: count - decrease > 0? count - decrease: 0 });
|
||
newConsumes.push({ id, count: decrease });
|
||
} else {
|
||
consumes.push({ id, count });
|
||
}
|
||
}
|
||
return { consumes, newConsumes };
|
||
}
|
||
|
||
/**
|
||
* 诸子列表是否解锁
|
||
* @param roleId
|
||
* @param bookId
|
||
* @returns true: 可以解锁 false:不可解锁
|
||
*/
|
||
export function checkAuthorBookLimit(authorBooks: AuthorBookType[], bookId: number) {
|
||
let dicAuthorsBook = gameData.authorBook.get(bookId);
|
||
if(!dicAuthorsBook) return false;
|
||
|
||
for(let { type, bookId, value } of dicAuthorsBook.limit) {
|
||
if(type == AUTHOR_BOOK_LIMIT_TYPE.ALL) {
|
||
let allProgress = authorBooks.reduce((pre, cur) => pre + cur.progress, 0);
|
||
if(allProgress < value) return false;
|
||
} else if(type == AUTHOR_BOOK_LIMIT_TYPE.ASSIGN) {
|
||
let curBook = authorBooks.find(cur => cur.bookId == bookId);
|
||
let progress = curBook?.progress??0;
|
||
if(progress < value) return false;
|
||
}
|
||
}
|
||
return true;
|
||
}
|
||
|
||
export function replaceAuthorBooks(authorBooks: AuthorBookType[], authorBook: AuthorBookType) {
|
||
let index = authorBooks.findIndex(cur => cur.bookId == authorBook.bookId);
|
||
if(index == -1) {
|
||
authorBooks.push(authorBook);
|
||
} else {
|
||
authorBooks[index] = authorBook;
|
||
}
|
||
return authorBooks;
|
||
}
|
||
|
||
|
||
export function getHeroNewEplace(hero: HeroType, ePlace: EPlace[]) {
|
||
const dicHero = gameData.hero.get(hero.skinId);
|
||
let newEplace = [];
|
||
for (let eP of ePlace) {
|
||
const dicEquip = getEquipByJobClassAndEPlace(dicHero?.jobClass, eP.id);
|
||
if (!dicEquip) continue;
|
||
// const newEquip = new EPlace(eP.id, dicEquip.id);
|
||
newEplace.push({ ...eP, equipId: dicEquip.id });
|
||
}
|
||
return newEplace;
|
||
}
|
||
|
||
export function getNewJob(hero: HeroType, job: number) {
|
||
const dicHero = gameData.hero.get(hero.skinId);
|
||
let dicMyJob = gameData.job.get(job);
|
||
let dicNewJob = getJobByGradeAndClass(dicHero.jobClass, dicMyJob.grade);
|
||
return dicNewJob?.jobid || 0;
|
||
} |