Files
ZYZ/game-server/app/services/roleService.ts
2023-08-29 17:27:07 +08:00

413 lines
15 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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;
}