Files
ZYZ/shared/db/Hero.ts
T
2021-09-01 19:56:19 +08:00

326 lines
12 KiB
TypeScript

import BaseModel from './BaseModel';
import { index, getModelForClass, prop, Ref, mongoose, DocumentType } from '@typegoose/typegoose';
import Equip, { } from './Equip';
import { CounterModel } from './Counter';
import { COUNTER, EQUIP_TYPE, HERO_CE_RATIO } from '../consts';
import { reduceCe } from '../pubUtils/util';
import Skin from './Skin';
type CeAttrUpdate = Partial<CeAttrData>;
export class CeAttrData {
@prop({ required: true })
id: number = 0;
@prop({ required: true })
base: number = 0;
@prop({ required: true })
ratioUp: number = 0;
@prop({ required: true })
fixUp: number = 0;
@prop({ required: true })
equipUp: number = 0;
constructor(id: number) {
this.id = id;
}
public updateAttr(update: { inc?: CeAttrUpdate, set?: CeAttrUpdate }) {
if(update.inc) {
let { base, equipUp, fixUp, ratioUp } = update.inc;
if(base != undefined) this.base += base * HERO_CE_RATIO;
if(equipUp != undefined) this.equipUp += equipUp * HERO_CE_RATIO;
if(fixUp != undefined) this.fixUp += fixUp * HERO_CE_RATIO;
if(ratioUp != undefined) this.ratioUp += ratioUp;
}
if(update.set) {
let { base, equipUp, fixUp, ratioUp } = update.set;
if(base != undefined) this.base = base * HERO_CE_RATIO;
if(equipUp != undefined) this.equipUp = equipUp * HERO_CE_RATIO;
if(fixUp != undefined) this.fixUp = fixUp * HERO_CE_RATIO;
if(ratioUp != undefined) this.ratioUp = ratioUp;
}
}
}
/**
* 英雄表
*/
export class Connect {
@prop({ required: true })
shipId: number;
@prop({ required: true })
level: number;
}
export class HeroSkin {
@prop({ required: true })
id: number;
@prop({ ref: 'Skin', type: mongoose.Schema.Types.ObjectId })
skin: Ref<Skin>;
@prop({ required: true })
enable: boolean;
}
export class EPlace {
@prop({ required: true })
id: number;
@prop({ ref: Equip, type: mongoose.Schema.Types.ObjectId })
equip: Ref<Equip>;
@prop({ required: true })
lv: number;
@prop({ required: true })
refineLv: number;
}
// 初始化
function getInitialEplace() {
let ePlace = new Array<EPlace>();
for (let i = EQUIP_TYPE.START; i <= EQUIP_TYPE.END; i++) {
let p = new EPlace();
p.id = i;
p.equip = null;
p.lv = 0;
p.refineLv = 1;
ePlace.push(p);
}
return ePlace;
}
@index({ roleId: 1, hid: 1 })
@index({ roleId: 1, seqId: 1 })
export default class Hero extends BaseModel {
@prop({ required: true })
roleId: string; // 角色 id
@prop({ required: true })
roleName: string; // 角色名称
@prop({ required: true })
serverId: number; // 区服 id
@prop({ required: true })
hid: number; // 武将 id
@prop({ required: true })
hName: string; // 武将名
@prop({ required: true })
seqId: number; // 武将表自增 id
@prop({ required: true, default: 0 })
exp: number; // 经验值
@prop({ required: true, default: 1 })
lv: number; // 武将等级
@prop({ required: true, default: 0, set: (val: number) => val, get: (val: number) => reduceCe(val) })
ce: number; // 武将战力
@prop({ required: false, set: (val: boolean) => val, get: () => true })
isReducedCe: boolean; // 如果战力没有缩过就会返回false,缩过了就会返回true
@prop({ required: true, default: 0 })
historyCe: number; // 武将历史最高战力
@prop({ required: true, type: CeAttrData, default: [], _id: false })
attr: CeAttrData[]; // 影响战力的属性
@prop({ required: true, default: 1 })
star: number; // 星级
@prop({ required: true, default: 0 })
starStage: number; // 星级六维阶段
@prop({ required: true, default: 0 })
colorStar: number; // 觉醒, 彩星
@prop({ required: true, default: 0 })
colorStarStage: number; // 觉醒六维阶段
@prop({ required: true, default: 0 })
quality: number; // 品质
@prop({ required: true, default: false })
scrollActive: boolean; // 是否在名将谱中激活
@prop({ required: true, default: 0 })
scrollId: number; // 名将谱id
@prop({ required: true, default: 0 })
scrollStar: number; // 名将谱中保存的星级
@prop({ required: true, default: 0 })
scrollColorStar: number; // 名将谱中保存的觉醒等级
@prop({ required: true, default: 0 })
scrollQuality: number; // 名将谱中保存的品质
@prop({ required: true, default: 0 })
job: number; // 职业
@prop({ required: true, default: 0 })
jobStage: number; // 职阶
@prop({ required: true, default: 0 })
favour: number; // 好感度
@prop({ required: true, default: 1 })
favourLv: number; // 好感等级
@prop({ required: true, type: Connect, default: [], _id: false })
connections: Connect[]; // 羁绊
@prop({ required: true, type: HeroSkin, default: [], _id: false })
skins: HeroSkin[]; // 皮肤
@prop({ required: true, type: EPlace, default: getInitialEplace(), _id: false })
ePlace: EPlace[]; // 武将装备引用数组
public static async findByRole(roleId: string, sort: { field: string, sortBy: number }[] = [], select?: string, getters = false) {
let sortParam = {};
for (let { field, sortBy } of sort) {
sortParam[field] = sortBy;
}
const heros: HeroType[] = await HeroModel.find({ roleId }).sort(sortParam).select(select).lean({ getters });
return heros;
}
public static async findBySeqIdRange(seqIds: Array<number>, roleId: string, lean = true) {
const hero: HeroType[] = await HeroModel.find({ seqId: { $in: seqIds }, roleId }).lean(lean);
return hero;
}
public static async findByHidRange(hids: Array<number>, roleId: string, select?: string, getters = false) {
const hero: HeroType[] = await HeroModel.find({ hid: { $in: hids }, roleId }).select(select).lean({ getters });
return hero;
}
public static async findMapByHidRange(hids: Array<number>, roleId: string, select?: string, getters = false) {
const hero = await HeroModel.findByHidRange(hids, roleId, select, getters);
let map = new Map<number, HeroType>();
for (let h of hero) {
map.set(h.hid, h);
}
return map;
}
public static async findBySeqIdAndRole(seqId: number, roleId: string, lean = true) {
const hero: HeroType = await HeroModel.findOne({ seqId, roleId }).lean(lean);
return hero;
}
public static async findByHidAndRole(hid: number, roleId: string, select?: string, getters = false) {
const hero: HeroType = await HeroModel.findOne({ hid, roleId }).select(select).lean({ getters });
return hero;
}
public static async findByHidAndRoleWithEquip(hid: number, roleId: string, lean = true) {
const hero: HeroType = await HeroModel.findOne({ hid, roleId }).populate('ePlace.equip').lean(lean);
return hero;
}
public static async addEquip(roleId: string, hid: number, ePlaceId: number, equipId: string) {
const hero: HeroType = await HeroModel.findOneAndUpdate(
{ roleId, hid, 'ePlace.id': ePlaceId },
{ $set: { 'ePlace.$.equip': equipId } },
{ new: true }).populate('ePlace.equip').lean();
return hero;
}
public static async removeEquip(roleId: string, hid: number, ePlaceId: number, lean = true) {
const hero: HeroType = await HeroModel.findOneAndUpdate(
{ roleId, hid, 'ePlace.id': ePlaceId },
{ $set: { 'ePlace.$.equip': null } },
{ new: true }).populate('ePlace.equip').lean(lean);
return hero;
}
public static async createHero(heroInfo: HeroUpdate, lean = true) {
const doc = new HeroModel();
const seqId = await CounterModel.getNewCounter(COUNTER.HID) || -1;
const update = Object.assign(doc.toJSON(), heroInfo, { seqId });
delete update._id;
const hero: HeroType = await HeroModel.findOneAndUpdate({ roleId: heroInfo.roleId, hid: heroInfo.hid }, update, { upsert: true, new: true }).lean(lean);
return hero;
}
public static async insertHeroes(roleId: string, roleName: string, serverId: number, heroInfos: HeroUpdate[]) {
let insertInfos: HeroUpdate[] = [];
for(let hero of heroInfos) {
const seqId = await CounterModel.getNewCounter(COUNTER.HID) || -1;
insertInfos.push({ ...hero, seqId, roleId, roleName, serverId })
}
const hero: HeroType[] = await HeroModel.insertMany(insertInfos);
return hero;
}
public static async sumTopHeroCe(roleId: string, num: number) {
let ce: Array<{ historyCe: number }> = await HeroModel.aggregate([
{ $match: { roleId } },
{ $sort: { historyCe: -1 } },
{ $limit: num },
{ $group: { _id: null, historyCe: { $sum: '$historyCe' } } }
]);
return ce.length > 0 ? ce[0].historyCe : 0;
}
public static async sumHeroCe(roleId: string) {
let ce: Array<{ ce: number }> = await HeroModel.aggregate([
{ $match: { roleId } },
{ $group: { _id: null, ce: { $sum: '$ce' } } }
]);
return ce.length > 0 ? ce[0].ce : 0;
}
public static async getTopHero(roleId: string, num: number, lean = true) {
const heroes: HeroType[] = await HeroModel.find({ roleId }).limit(num).sort({ ce: -1 }).lean(lean);
return heroes;
}
public static async updateCe(roleId: string, hid: number, ce: number, oldCe: number, historyCe: number, lean = true) {
let distance = ce - oldCe;
let historyDistance = ce > historyCe ? ce - historyCe : 0;
let result: HeroType = await HeroModel.findOneAndUpdate({ roleId, hid }, { $inc: { ce: distance, historyCe: historyDistance } }).lean(lean);
return result;
}
public static async deleteAccount(roleId: string) {
let result = await HeroModel.deleteMany({ roleId });
return result;
}
public static async deleteHero(roleId: string, hid: number) {
let result = await HeroModel.deleteMany({ roleId, hid });
return result;
}
public static async updateHeroInfo(roleId: string, hid: number, heroUpdate: HeroUpdate, select?: string, getters = false) {
delete heroUpdate._id;
let result: HeroType = await HeroModel.findOneAndUpdate({ roleId, hid }, { $set: heroUpdate }, { new: true }).select(select).lean({ getters });
return result;
}
public static async unloadHeroAndEquip(roleId: string, hid: number, id: number, lean = true) {
const hero: HeroType = await HeroModel.findOneAndUpdate(
{ roleId, hid, 'ePlace.id': id },
{ $set: { 'ePlace.$.equip': null } },
{ new: true }).lean(lean);
return hero;
}
public static async findByField(field: string, value?: number | string, lean = true) {
let searchObj = {};
if (field != 'all') {
if (field == 'roleName') {
searchObj['roleName'] = { $regex: new RegExp(value.toString(), 'i') }
} else {
searchObj[field] = value;
}
}
//.select('uid tel username')
const user: HeroType[] = await HeroModel.find(searchObj, {_id: 0}).lean(lean);
return user;
}
public static async getAllRank(serverId: number, select?: string, limit = 200) {
let result: HeroType[] = await HeroModel.find({ serverId }, { _id: false }).select(select).limit(limit).sort({ ce: -1, updatedAt: 1 }).lean({ getters: true });
return result;
}
public static async getMyTopHero(roleId: string, select?: string) {
let result: HeroType = await HeroModel.findOne({ roleId }, { _id: false }).select(select).sort({ ce: -1, updatedAt: 1 }).lean({ getters: true });
return result;
}
public static async getRank(hid: number, serverId: number, select?: string, limit = 200) {
let result: HeroType[] = await HeroModel.find({ serverId, hid }, { _id: false }).select(select).limit(limit).lean({ getters: true });
return result;
}
}
export const HeroModel = getModelForClass(Hero);
export interface HeroType extends Pick<DocumentType<Hero>, keyof Hero> { };
export type HeroUpdate = Partial<HeroType>; // 将所有字段变成可选项