diff --git a/game-server/app/servers/role/handler/heroHandler.ts b/game-server/app/servers/role/handler/heroHandler.ts index 1ab1e8d38..7c84cbce0 100644 --- a/game-server/app/servers/role/handler/heroHandler.ts +++ b/game-server/app/servers/role/handler/heroHandler.ts @@ -346,7 +346,7 @@ export class HeroHandler { let shipHidAndLevel = gameData.friendShipHidAandIds.get(shipId); if (!shipHidAndLevel) return resResult(STATUS.HERO_CONECTION_IS_NOT_EXIT); - let hero = await HeroModel.findByHidAndRole(shipHidAndLevel.actorId, roleId, false); + let hero = await HeroModel.findByHidAndRole(shipHidAndLevel.actorId, roleId); if (!hero) return resResult(STATUS.HERO_NOT_FIND); let flag = true; diff --git a/game-server/app/servers/role/handler/roleHandler.ts b/game-server/app/servers/role/handler/roleHandler.ts index 873c8b800..23a7fda87 100644 --- a/game-server/app/servers/role/handler/roleHandler.ts +++ b/game-server/app/servers/role/handler/roleHandler.ts @@ -1,16 +1,15 @@ import { STATUS } from '../../../consts/statusCode'; import { RoleModel } from './../../../db/Role'; -import { CounterModel } from '../../../db/Counter'; import { HeroModel } from '../../../db/Hero'; -import { EquipModel } from '../../../db/Equip'; -import { calculateCE, resResult } from '../../../pubUtils/util'; -import {Application, BackendSession, createTcpMailBox} from 'pinus'; -import { COUNTER } from '../../../consts'; +import { resResult, decodeIdCntArrayStr, parseGoodStr } from '../../../pubUtils/util'; +import {Application, BackendSession} from 'pinus'; import { handleCost } from '../../../services/rewardService'; -import { getTitle, getTeraph } from '../../../pubUtils/data'; -import { SSL_OP_SSLEAY_080_CLIENT_DH_BUG } from 'constants'; +import { getTitle, getTeraph, gameData } from '../../../pubUtils/data'; +import { SCHOOL, SCROLL } from '../../../pubUtils/dicParam'; import { getTeraphAttr } from '../../../consts/constModules/abilityConst' const _ = require('underscore'); +import { SclResultInter, SclPosInter } from '../../../pubUtils/interface'; +import { SchoolModel } from '../../../db/School'; export default function(app: Application) { return new RoleHandler(app); @@ -164,4 +163,177 @@ export class RoleHandler { await RoleModel.updateRoleInfo(roleId, { teraphs: role.teraphs }); return resResult(STATUS.SUCCESS, { roleId, teraphs: role.teraphs }); } + // 获得百家学宫 + async getSchoolList(msg: {}, session: BackendSession) { + let roleId = session.get('roleId'); + + const dicPosition = decodeIdCntArrayStr(SCHOOL.SCHOOL_POSITION, 1); // id=>isOpen + + const userSchoolList = await SchoolModel.findByRoleId(roleId); + + let school = new Array(); + gameData.school.forEach((dicSchool) => { + let position = new Array(); + dicPosition.forEach((isOpen, id) => { + id = parseInt(id); + + 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 resResult(STATUS.SUCCESS, { school }); + + } + + // 摆放/替换/卸下武将 + async changeSchoolHero(msg: { schoolId: number, positionId: number, hid: number }, session: BackendSession) { + let roleId = session.get('roleId'); + let sid = session.get('sid'); + + let { schoolId, positionId, hid } = msg; + if (!gameData.school.has(schoolId)) { + return resResult(STATUS.DIC_DATA_NOT_FOUND); + } + const dicPosition = decodeIdCntArrayStr(SCHOOL.SCHOOL_POSITION, 1); // id(str) => isOpen + if (!dicPosition.has(positionId.toString())) { + return resResult(STATUS.DIC_DATA_NOT_FOUND); + } + let isOpen = !!dicPosition.get(positionId.toString()); // 该位置是否解锁 + + let findHero = await SchoolModel.findByHid(roleId, hid); + if (!!findHero) { + return resResult(STATUS.ROLE_SCHOOL_HERO_USED); + } + let curSchool = await SchoolModel.findBySclAndPos(roleId, schoolId, positionId); + let preHid = 0; // 原先在该位置上的武将 + if (curSchool) { + isOpen = curSchool.isOpen; + preHid = curSchool.hid; + } + + let curHero = await HeroModel.findByHidAndRole(hid, roleId); + if (!curHero) return resResult(STATUS.HERO_NOT_FIND); + + if (!isOpen) { + return resResult(STATUS.ROLE_SCHOOL_POSITION_LOCKED); + } + + await SchoolModel.updateBySclAndPos(roleId, schoolId, positionId, { hid, isOpen }) + + return resResult(STATUS.SUCCESS, { + schoolId, positionId, hid, preHid, isOpen + }); + + } + + // 解锁位置 + async unlockSchoolPosition(msg: { schoolId: number, positionId: number }, session: BackendSession) { + let roleId = session.get('roleId'); + let sid = session.get('sid'); + + let { schoolId, positionId } = msg; + if (!gameData.school.has(schoolId)) { + return resResult(STATUS.DIC_DATA_NOT_FOUND); + } + const dicPosition = decodeIdCntArrayStr(SCHOOL.SCHOOL_POSITION, 1); // id(str) => isOpen + if (!dicPosition.has(positionId.toString())) { + return resResult(STATUS.DIC_DATA_NOT_FOUND); + } + let isOpen = !!dicPosition.get(positionId.toString()); // 该位置是否解锁 + if (isOpen) { + return resResult(STATUS.ROLE_SCHOOL_POSITION_UNLOCK_NOT_NEED); + } + let curSchool = await SchoolModel.findBySclAndPos(roleId, schoolId, positionId); + if (curSchool && curSchool.isOpen) { + return resResult(STATUS.ROLE_SCHOOL_POSITION_UNLOCK_NOT_NEED); + } + + const cost = parseGoodStr(SCHOOL.SCHOOL_UNLOCK_COIN); + const costResult = await handleCost(roleId, sid, cost); + if (!costResult) return resResult(STATUS.ROLE_MATERIAL_NOT_ENOUGH); + + curSchool = await SchoolModel.updateBySclAndPos(roleId, schoolId, positionId, { isOpen: true }) + + return resResult(STATUS.SUCCESS, { + schoolId, positionId, hid: curSchool.hid, isOpen: curSchool.isOpen + }); + } + + + // 激活/升级名将谱 + async activeHeroScroll(msg: { hid: number }, session: BackendSession) { + let roleId = session.get('roleId'); + let sid = session.get('sid'); + + let { hid } = msg; + + let curHero = await HeroModel.findByHidAndRole(hid, roleId, 'star colorStar quality scrollActive scrollStar scrollQuality favour favourLv'); + if (!curHero) return resResult(STATUS.HERO_NOT_FIND); + + let dicHero = gameData.hero.get(hid); + if (!dicHero) return resResult(STATUS.DIC_DATA_NOT_FOUND); + + let { star, colorStar, quality, scrollActive, scrollStar, scrollColorStar, scrollQuality, favour, favourLv } = curHero; + + let update = { + scrollActive, scrollStar, scrollColorStar, scrollQuality, favour, favourLv + }; + if (!scrollActive) { // 初次激活 + update.scrollActive = true; + update.scrollStar = dicHero.initialStars; + update.scrollQuality = dicHero.quality; + update.scrollColorStar = 0; + + // 获取一定好感度 + let friendShipLevels = gameData.friendShipLevel; + if (friendShipLevels[friendShipLevels.length - 1].level > favourLv) { + + update.favour += parseInt(SCROLL.SCROLL_ACTIVE_FAVOUR); + for (let friendShipLevel of friendShipLevels) { + if (friendShipLevel.level < update.favourLv) + continue; + if (friendShipLevel.exp > update.favour) + break; + update.favour -= friendShipLevel.exp; + update.favourLv++; + } + + } + } else { + if (star > scrollStar) { // 可以升星 + update.scrollStar++; + } else if (scrollQuality > quality) { // 可以升品 + update.scrollQuality++; + } else if (colorStar > scrollColorStar) { // 可以升彩星 + update.scrollColorStar++; + } else { + return resResult(STATUS.ROLE_SCROLL_REACH_MAX); + } + } + + curHero = await HeroModel.updateHeroInfo(roleId, hid, update, 'hid scrollActive scrollStar scrollColorStar scrollQuality favour favourLv'); + + return resResult(STATUS.SUCCESS, { + curHero + }); + } + } diff --git a/game-server/package.json b/game-server/package.json index 46bf02b41..5d8aa2bdf 100644 --- a/game-server/package.json +++ b/game-server/package.json @@ -25,6 +25,7 @@ "crc": "^3.5.0", "moment": "^2.29.1", "mongoose": "^5.10.4", + "mongoose-transactions": "^1.1.4", "pinus": "^1.4.9", "pinus-robot": "^1.4.9", "pinus-robot-plugin": "^1.4.9", diff --git a/package.json b/package.json index a7cab30f2..007072b61 100644 --- a/package.json +++ b/package.json @@ -7,8 +7,10 @@ "@typegoose/typegoose": "^7.3.5", "@types/mongoose": "^5.7.36", "bcrypt": "^5.0.0", + "lodash": "^4.17.20", "moment": "^2.27.0", - "mongoose": "^5.10.4" + "mongoose": "^5.10.4", + "underscore": "^1.12.0" }, "devDependencies": {}, "scripts": { diff --git a/shared/consts/constModules/sysConst.ts b/shared/consts/constModules/sysConst.ts index 6f82293b3..f333aa7e0 100644 --- a/shared/consts/constModules/sysConst.ts +++ b/shared/consts/constModules/sysConst.ts @@ -90,6 +90,7 @@ export const FILENAME = { DIC_SUIT: 'dic_zyz_suit', DIC_TITLE: 'dic_zyz_title', DIC_TERAPH: 'dic_zyz_teraph', + DIC_SCHOOL: 'dic_zyz_school' } export const WAR_RELATE_TABLES = [ diff --git a/shared/consts/statusCode.ts b/shared/consts/statusCode.ts index 477e092b7..c93e97a88 100644 --- a/shared/consts/statusCode.ts +++ b/shared/consts/statusCode.ts @@ -173,6 +173,11 @@ export const STATUS = { ROLE_TERAPH_NOT_STRENGTHEN: {code: 30501, simStr: '玩家神像不能强化'}, ROLE_TERAPH_NOT_QUILITY: {code: 30502, simStr: '玩家神像不能进阶'}, + ROLE_SCHOOL_HERO_USED: {code: 30600, simStr: '该武将已配置'}, + ROLE_SCHOOL_POSITION_LOCKED: {code: 30601, simStr: '该位置未解锁'}, + ROLE_SCHOOL_POSITION_UNLOCK_NOT_NEED: {code: 30602, simStr: '该位置已解锁'}, + ROLE_SCROLL_REACH_MAX: {code: 30603, simStr: '已经升到可以升的最高等级'}, + // 社交相关状态 40000 - 49999 // 运营模块相关状态 50000 - 59999 // GM后台相关状态 60000 - 69999 diff --git a/shared/db/Hero.ts b/shared/db/Hero.ts index f38ebf997..9c411122c 100644 --- a/shared/db/Hero.ts +++ b/shared/db/Hero.ts @@ -66,6 +66,10 @@ interface heroUpdate { connections?: Connect[]; ePlace?:EPlace[]; _id?:number; + scrollActive?: boolean; + scrollStar?: number; + scrollColorStar?: number; + scrollQuality?: number; } @index({ roleId: 1, hid: 1 }) @@ -109,6 +113,15 @@ export default class Hero extends BaseModel { @prop({ required: true, default: 0 }) quality: number; // 品质 + @prop({ required: true, default: false }) + scrollActive: boolean; // 是否在名将谱中激活 + @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 }) @@ -139,8 +152,8 @@ export default class Hero extends BaseModel { const hero: HeroType = await HeroModel.findOne({ seqId, roleId }).lean(lean); return hero; } - public static async findByHidAndRole(hid: number, roleId: string, lean = true) { - const hero: HeroType = await HeroModel.findOne({ hid, roleId }).lean(lean); + public static async findByHidAndRole(hid: number, roleId: string, select?: string, lean = true) { + const hero: HeroType = await HeroModel.findOne({ hid, roleId }).select(select).lean(lean); return hero; } @@ -204,9 +217,9 @@ export default class Hero extends BaseModel { return result; } - public static async updateHeroInfo(roleId: string, hid:number, heroUpdate:heroUpdate, lean = true) { + public static async updateHeroInfo(roleId: string, hid:number, heroUpdate:heroUpdate, select?: string, lean = true) { delete heroUpdate._id; - let result: HeroType = await HeroModel.findOneAndUpdate({roleId, hid}, {$set:heroUpdate}).lean(lean); + let result: HeroType = await HeroModel.findOneAndUpdate({roleId, hid}, {$set:heroUpdate}, {new: true}).select(select).lean(lean); return result; } diff --git a/shared/db/School.ts b/shared/db/School.ts new file mode 100644 index 000000000..2203e36ff --- /dev/null +++ b/shared/db/School.ts @@ -0,0 +1,59 @@ +import BaseModel from './BaseModel'; +import { index, getModelForClass, prop, DocumentType } from '@typegoose/typegoose'; + +/** + * 百家学宫表 +*/ + +@index({ roleId: 1 }) +@index({ roleId: 1, schoolId: 1, positionId: 1 }) + +export default class School extends BaseModel { + + @prop({ required: true }) + roleId: string; // 角色 id + + @prop({ required: true }) + schoolId: number; // 学院id + @prop({ required: true }) + positionId: number; // 位置id + @prop({ required: true, default: 0 }) + hid: number; // 放置的武将 + @prop({ required: true, default: false }) + isOpen: boolean; // 位置是否开启 + + public static async findByRoleId(roleId: string) { + let result: SchoolType[] = await SchoolModel.find({ roleId }).lean(); + return result; + } + + public static async findBySclAndPos(roleId: string, schoolId: number, positionId: number) { + let result: SchoolType = await SchoolModel.findOne({ roleId, schoolId, positionId }).lean(); + return result; + } + + public static async findByHid(roleId: string, hid: number) { + let result: SchoolType = await SchoolModel.findOne({ roleId, hid }).lean(); + return result; + } + + public static async updateBySclAndPos(roleId: string, schoolId: number, positionId: number, update: {hid?: number, isOpen?: boolean}) { + + const doc = new SchoolModel(); + const setOnInsert = doc.toJSON(); + for(let key in update) delete setOnInsert[key]; + delete setOnInsert._id; + const result: SchoolType = await SchoolModel.findOneAndUpdate({ roleId, schoolId, positionId }, { $setOnInsert: setOnInsert, $set: update }, { new: true, upsert: true }).lean(); + return result; + + } + + public static async deleteAccount(roleId: string) { + let result = await SchoolModel.deleteMany({ roleId }); + return result; + } +} + +export const SchoolModel = getModelForClass(School); + +export interface SchoolType extends Pick, keyof School> { }; diff --git a/shared/pubUtils/data.ts b/shared/pubUtils/data.ts index 0585795fc..b4227d3a6 100644 --- a/shared/pubUtils/data.ts +++ b/shared/pubUtils/data.ts @@ -32,6 +32,8 @@ import { dicHeroEquip } from './dictionary/DicHeroEquip'; import { dicSuit } from './dictionary/DicSuit'; import { dicTitle } from './dictionary/DicTitle'; import { dicTeraph } from './dictionary/DicTeraph'; +import { dicSchool } from './dictionary/DicSchool'; + export const gameData = { blurprtCompose: dicBlueprtCompose, blueprtPossibility: dicBlueprtPossibility, @@ -74,7 +76,8 @@ export const gameData = { dicHeroEquip: dicHeroEquip, suit: dicSuit, title: dicTitle, - teraphs: dicTeraph + teraphs: dicTeraph, + school: dicSchool }; // 在此提供一些原先在gamedata中提供的方法,以便更方便获取gameData数据 @@ -210,4 +213,4 @@ export function getTitle(titleLv: number) { export function getTeraph(id: number, grade: number) { const teraphInfo = gameData.teraphs.get(id +'_'+ grade); return teraphInfo; -} \ No newline at end of file +} diff --git a/shared/pubUtils/dicParam.ts b/shared/pubUtils/dicParam.ts index c6e5f0484..30624e8e2 100644 --- a/shared/pubUtils/dicParam.ts +++ b/shared/pubUtils/dicParam.ts @@ -2,12 +2,28 @@ export const EQUIP = { EQUIP_ONE_HOLE: '17038&1', // 装备孔1号位消耗 EQUIP_TWO_HOLE: '17038&2', // 装备孔2号位消耗 EQUIP_THREE_HOLE: '17038&4', // 装备孔3号位消耗 - EQUIP_ONE_REFORGED: '17040&1', // 装备位洗练词条第一次消耗 - EQUIP_TWO_REFORGED: '17040&2', // 装备位洗练词条第二次消耗 - EQUIP_THREE_REFORGED: '17040&4', // 装备位洗练词条第三次消耗 - EQUIP_FOUR_REFORGED: '17040&8', // 装备位洗练词条第四次消耗 - EQUIP_ONE_LOCKED: '17041&1', // 装备位洗练词条不锁住钥匙消耗 - EQUIP_TWO_LOCKED: '17041&2', // 装备位洗练词条锁住一条钥匙消耗 - EQUIP_THREE_LOCKED: '17041&4', // 装备位洗练词条锁住二条钥匙消耗 - EQUIP_FOUR_LOCKED: '17041&8', // 装备位洗练词条锁住三条钥匙消耗 + EQUIP_ONE_REFORGED: '17040&1', // 装备位洗练四条词条消耗 + EQUIP_TWO_REFORGED: '17040&2', // 装备位洗练三条词条消耗 + EQUIP_THREE_REFORGED: '17040&4', // 装备位洗练二条词条消耗 + EQUIP_FOUR_REFORGED: '17040&8', // 装备位洗练一条词条消耗 + EQUIP_ONE_LOCKED: '17041&1', // 装备锁定第一条词条锁消耗 + EQUIP_TWO_LOCKED: '17041&2', // 装备锁定第二条词条锁消耗 + EQUIP_THREE_LOCKED: '17041&4', // 装备锁定第三条词条锁消耗 +}; +export const JOB = { + JOB_CLASS_1: '步兵', // 大兵种名称 + JOB_CLASS_2: '枪兵', // 大兵种名称 + JOB_CLASS_3: '骑兵', // 大兵种名称 + JOB_CLASS_4: '弓兵', // 大兵种名称 + JOB_CLASS_5: '游侠', // 大兵种名称 + JOB_CLASS_6: '策士', // 大兵种名称 + JOB_CLASS_7: '道士', // 大兵种名称 + JOB_CLASS_8: '医者', // 大兵种名称 +}; +export const SCHOOL = { + SCHOOL_POSITION: '1&1|2&1|3&1|4&0|5&0', // 百家学宫位置锁定 + SCHOOL_UNLOCK_COIN: '31001&100', // 百家学宫解锁位置消耗 +}; +export const SCROLL = { + SCROLL_ACTIVE_FAVOUR: '50', // 首次激活增加的好感度 }; diff --git a/shared/pubUtils/dictionary/DicSchool.ts b/shared/pubUtils/dictionary/DicSchool.ts new file mode 100644 index 000000000..ac51e6eb6 --- /dev/null +++ b/shared/pubUtils/dictionary/DicSchool.ts @@ -0,0 +1,26 @@ +// 百家学宫列表 +import { readJsonFile, parseNumberList } from '../util' +import { FILENAME } from '../../consts' + +export interface DicSchool { + + // 学宫id + readonly id: number; + // 学宫名 + readonly name: string; + // 提升属性 + readonly upAttribute: Array; +} + + +const str = readJsonFile(FILENAME.DIC_SCHOOL); +let arr = JSON.parse(str); + +export const dicSchool = new Map(); + +arr.forEach(o => { + o.upAttribute = parseNumberList(o.upAttribute) + dicSchool.set(o.id, o); +}); + +arr = undefined; \ No newline at end of file diff --git a/shared/pubUtils/interface.ts b/shared/pubUtils/interface.ts index 96b9d937e..98364c67e 100644 --- a/shared/pubUtils/interface.ts +++ b/shared/pubUtils/interface.ts @@ -41,3 +41,16 @@ export interface EquipInter { export interface BagInter {id: number, itemName: string, count: number, type: number, hid:number, times?: number}; export interface ItemInter {id?: number, count?: number, seqId?: number, type?: number}; + +// 百家学宫,布阵武将位置 +export interface SclPosInter { + id: number; + hid: number; + isOpen: boolean; +} + +// 百家学宫返回 +export interface SclResultInter { + id: number; + position: SclPosInter[] +} \ No newline at end of file diff --git a/shared/pubUtils/itemUtils.ts b/shared/pubUtils/itemUtils.ts index acfec812d..572b5267c 100644 --- a/shared/pubUtils/itemUtils.ts +++ b/shared/pubUtils/itemUtils.ts @@ -14,7 +14,7 @@ export async function addSkins(roleId: string, id: number) { let skinInfo = gameData.fashion.get(id); if (!skinInfo) return false; - let hero = await HeroModel.findByHidAndRole(skinInfo.actorId, roleId, false); + let hero = await HeroModel.findByHidAndRole(skinInfo.actorId, roleId); if (!hero) return false; if (!!_.findWhere(hero.skins, { id }))