import {ObjectId, OptionalId} from 'mongodb'; import {ApiCall, BaseConnection, TsrpcError} from 'tsrpc'; import {checkPlayerGift} from '../api_s2c/event/xianshilibao/fun'; import {md_redPoint_check} from '../api_s2c/gongyu/mingdao/ApiOpen'; import {CollectionPeiJian} from '../module/collection_peijian'; import {Wjjl} from '../module/collection_wjjl'; import {MongodbCollections} from '../module/mongodb'; import {G123} from '../sdk/G123'; import {ResGetList} from '../shared/protocols/item/PtlGetList'; import {ResLogin} from '../shared/protocols/user/PtlLogin'; import {player} from '../shared/protocols/user/type'; import {HeroShared, otherBuff} from '../shared/public/hero'; import {PublicShared} from '../shared/public/public'; import {HeroFun} from './hero'; import {ShiwuFun} from './shiwu'; import {UserFun} from './user'; import {getItemByItemId, getItemNum} from './item'; import {getGud, setGud} from './gud'; import {addGameLog} from "../gameLog"; import {PushGiftFun} from "./pushgift"; export type call = { get otherBuff(): otherBuff; uid: string; eventMsg: k_v>; conn: { readonly id: string; get otherBuff(): otherBuff; get heroPos(): k_v; uid: string; gud: player; item?: ResGetList['list']; refreshPower(): Promise; sendMsg: BaseConnection['sendMsg']; }; service: { name: string }; addEventMsg: ApiCall['addEventMsg']; req: {} }; export class PlayerFun { /** * 获取玩家atn数量 */ static async getAtnNum(call: call, atn: atn): Promise { if (atn.a == 'attr') { let _gud = await getGud(call.conn.uid); return _gud[atn.t] || 0; //return call.conn.gud[atn.t] || 0; } else if (atn.a == 'item') { let numInfo = await getItemNum(call.conn.uid, atn.t); return numInfo[atn.t]?.num || 0; //去掉item Redis相关 //return call.conn.item?.[atn.t]?.num || await G.redis.get('item', call.conn.uid, atn.t, 'num') || 0; } else return 0; } /** * 判断消耗是否满足 * @param err 默认会中断调用栈 向客户端返回道具不足错误信息 */ static async checkNeedIsMeet(call: call, need: atn[], err = true) { for (let atn of need) { let has = await this.getAtnNum(call, atn); if (has < atn.n) { if (err) { // 消耗不足 触发推送礼包 PushGiftFun.chkItemGift(call.uid, atn) throw new TsrpcError('', {code: -104, atn: atn}); } else { return {isOk: false, atn: atn}; } } } return {isOk: true, atn: null}; } /** * 轮询多个消耗条件是否满足 */ static async checkNeedByArgs(call: call, ...args: atn[]): Promise<{ isOk: boolean; atn: atn; }> { let need = args.shift(); let meet = await this.checkNeedIsMeet(call, [need], false); if (meet.isOk) return { isOk: true, atn: need }; if (args.length < 1) { throw new TsrpcError('', {code: -104, atn: meet.atn}); } return await this.checkNeedByArgs(call, ...args); } /** * 扣除消耗 仅限 item attr */ static async cutNeed(call: call, val: atn[]) { let needArr = PublicShared.mergePrize(val); needArr.forEach(v => v.n *= -1); let all = []; let attr = needArr.filter(atn => atn.a == 'attr' && atn.n != 0); if (attr.length > 0) { all.push(this.addAttr(call, attr)); } let item = needArr.filter(atn => atn.a == 'item' && atn.n != 0); if (item.length > 0) { all.push(this.addItem(call, item)); } // 记录消耗 addGameLog(call.uid, call.service.name, call.req, {need: val}) await Promise.all(all); G.emit('USE_ITEM', call.conn.gud, needArr.map(need => { return {...need, n: Math.abs(need.n)}; })); } /** * 发送奖励 */ static async sendPrize(call: call, prizeList: atn[]) { prizeList = PublicShared.mergePrize(prizeList); let attr = prizeList.filter(atn => atn.a == 'attr' && atn.n != 0); let item = prizeList.filter(atn => atn.a == 'item' && atn.n != 0); let hero = prizeList.filter(atn => atn.a == 'hero' && atn.n != 0); let equip = prizeList.filter(atn => atn.a == 'equip' && atn.n != 0); let shiwu = prizeList.filter(atn => atn.a == 'shiwu' && atn.n != 0); let peijian = prizeList.filter(atn => atn.a == 'peijian' && atn.n != 0); // 记录获得 addGameLog(call.uid, call.service.name, call.req, {prize: prizeList}) await Promise.all([ attr.length > 0 && this.addAttr(call, attr), item.length > 0 && this.addItem(call, item), hero.length > 0 && this.addHero(call, hero), equip.length > 0 && this.addEquip(call, equip), shiwu.length > 0 && this.addShiwu(call, shiwu), peijian.length > 0 && this.addPeijian(call, peijian) ]); return prizeList; }; //attr里的指定字段的值,不能小于0 static fixAttrLteZero(t: string, val: number) { if (['jinbi', 'rmbmoney', 'payExp', 'nexp'].includes(t) && val < 0) { return 0 } else { return val; } } /** * 修改玩家属性或货币 */ static async addAttr(call: call, val: Partial<{ [k in keyof ResLogin['gud']]: ResLogin['gud'][k] }> | atn[]) { let change = {}; if (val instanceof Array) { let all = []; for (let atn of val) { change[atn.t] = this.fixAttrLteZero(atn.t, await this.getAtnNum(call, atn) + atn.n); if (atn.t == 'rmbmoney') { this.changeAttrLog(call.conn.uid, change[atn.t], atn, call.conn.gud.rmbmoney) // 扣除钻石时 if (atn.n < 0) { // 监听任务消耗任务 G.emit("Class_task_156", 'Class_task_156', call, -atn.n, 0); } } // 增加vip经验的任务监听 if (atn.t == "payExp" && atn.n > 0) { G.emit("Class_task_157", 'Class_task_157', call, atn.n, 0); } all.push(this.changeAttr(call.conn.uid, change)); all.push(this.upAttr(call, {...atn, n: change[atn.t]})); //await this.changeAttr(call.conn.uid, change); //await this.upAttr(call, {...atn, n: change[atn.t]}); } await Promise.all(all); } else { change = val; await this.changeAttr(call.conn.uid, change); for (let [k, v] of Object.entries(val)) { if (['lv', 'vip', 'renown', 'wxcLv', 'tujianLv'].includes(k)) { UserFun.activeHeadFrame(call.uid, k, v); UserFun.activeChatFrame(call.uid, k, v); } } } // 修改属性应在相关奖励领取之前,否则奖励内获取的用户数据是旧数据 // await this.changeAttr(call.conn.uid, change); call.addEventMsg('msg_s2c/PlayerChange', change); // 等级改变 触发推送礼包 if (change["lv"]) { PushGiftFun.chkLvGift(call.uid, change["lv"]) } // 关卡改变 触发推送礼包 if (change["mapId"]) { PushGiftFun.chkLevelGift(call.uid, change["mapId"]) } } static async changeAttrLog(uid: string, change, atn, before) { let data = { uid, before, rmbmoney: change, change: atn.n, isAdd: atn.n > 0, cTime: G.time, atn } G.mongodb.collection('rmbuse').insertOne(data); // 消费竞赛开启时写入跨服数据库 if (G.huodong.xfjs && !data.isAdd && typeof data.change == 'number') { G.crossmongodb.collection('rmbuse').updateOne({uid: data.uid, type: `xfjs_${G.huodong.xfjsId}`}, { $set: {time: G.time}, $inc: {change: data.change} }, {upsert: true}); } } static async changeAttr(uid: string, change: Partial) { setGud(uid, change); G.mongodb.collection('user').updateOne({uid: uid}, {$set: change}); if (G.server.uid_connections[uid]) { checkPlayerGift(G.server.uid_connections[uid].gud, change); //Object.assign(G.server.uid_connections[uid].gud, change); } addGameLog(uid, "_changeAttr", {}, change) } /** * 检查玩家是否满足等级提升 vip提升等 */ static async upAttr(call: call, atn: atn) { switch (atn.t) { case 'nexp': let addLv = 0; const conf = G.gc.playerLv; const curLv = call.conn.gud.lv; while (conf[curLv + addLv + 1] && atn.n >= conf[curLv + addLv + 1].need) { addLv++; G123.sendUserLevelUp({...call.conn.gud, lv: curLv + addLv, nexp: atn.n}); } addLv && await this.addAttr(call, {lv: curLv + addLv}); break; case 'payExp': let addVip = 0; const vipConf = G.gc.vip; const curVip = call.conn.gud.vip; while (vipConf[curVip + addVip + 1] && atn.n >= vipConf[curVip + addVip + 1].exp) { addVip++; } addVip && await this.addAttr(call, {vip: curVip + addVip}); break; } } /** * 添加道具 */ static async addItem(call: call, val: atn[]) { for (let atn of val) { let upObj = { filter: {uid: call.uid, itemId: atn.t}, update: { $setOnInsert: { firstTime: G.time, }, $set: { lastTime: G.time, }, $inc: { num: atn.n }, }, options: { upsert: true } }; let itemInfo = await getItemByItemId(call.uid, atn.t); let item = itemInfo[atn.t]; //去掉item Redis相关 //let item = call.conn.item?.[atn.t] || await G.redis.get('item', call.uid, atn.t); if (!item) { let data = { _id: '', num: atn.n, uid: call.uid, itemId: atn.t, firstTime: upObj.update.$setOnInsert.firstTime, lastTime: upObj.update.$setOnInsert.firstTime }; G.mongodb.collection('item').updateOne(upObj.filter, upObj.update, upObj.options); call.addEventMsg('msg_s2c/ItemChange', atn.t, data); addGameLog(call.uid, "_itemChange", {"additem": 1}, { "filter": upObj.filter, "update": upObj.update, "options": upObj.options }) } else { if (item.num + atn.n <= 0) { await Promise.all([ G.mongodb.collection('item').deleteOne({uid: call.uid, itemId: atn.t}) ]); call.addEventMsg('msg_s2c/ItemChange', atn.t, {num: 0}); addGameLog(call.uid, "_itemChange", {"delitem": 1}, {"itemId": atn.t}) } else { await Promise.all([ G.mongodb.collection('item').updateOne(upObj.filter, upObj.update, upObj.options) ]); call.addEventMsg('msg_s2c/ItemChange', atn.t, { num: item.num + atn.n, lastTime: upObj.update.$set.lastTime }); addGameLog(call.uid, "_itemChange", {"attritem": 1}, { "filter": upObj.filter, "update": upObj.update, "options": upObj.options, newNum: item.num + atn.n }) } } } } /** * 添加装备 */ static async addEquip(call: call, val: atn[]) { let lshd: k_v = {}; let insertData: OptionalId>[] = val.map(v => { return new Array(v.n).fill(1).map(v1 => { return { lv: 0, uid: call.uid, star: 0, wearaId: '', equipId: v.t, getTime: G.time, adjustment: 0 }; }); }).reduce((a, b) => a.concat(b)); let result = await G.mongodb.collection('equip').insertMany(insertData); addGameLog(call.uid, "_addEquip", {}, insertData) insertData.forEach((v, key) => { let id = result.insertedIds[key].toHexString(); let {_id, ...ops} = v; Wjjl.setVal(call.uid, `has_equip_color_${G.gc.equip[ops.equipId].colour}`, 1, false); call.addEventMsg('msg_s2c/EquipChange', id, { _id: id, ...ops }); if (!lshd[v.equipId]) lshd[v.equipId] = 0; lshd[v.equipId]++; }); G.mongodb.collection('playerInfo', 'lshd_equip').updateOne( { uid: call.uid, type: 'lshd_equip' }, { $inc: lshd }, { upsert: true } ); call.addEventMsg('msg_s2c/LshdChange', 'equip', lshd); } /** * 删除装备 */ static async cutEquip(call: call, _idArr: string[]) { for (let _id of _idArr) { G.redis.del('equip', call.uid, _id); G.mongodb.collection('equip').deleteOne({uid: call.uid, _id: new ObjectId(_id)}); call.addEventMsg('msg_s2c/EquipChange', _id, {num: 0}); addGameLog(call.uid, "_cutEquip", {}, {_id: _id}) } } /** * 添加英雄 */ static async addHero(call: call, val: atn[]) { let lshd: k_v = {}; let insertData: any[] = val.map(v => { return new Array(v.n).fill(1).map(v1 => { return { lv: 1, uid: call.uid, jieji: 0, heroId: v.t, getTime: G.time, }; }); }).reduce((a, b) => a.concat(b)); if (!insertData.length) return insertData.forEach(v => { v.zhanli = HeroShared.getHeroZhanLi(v, call.otherBuff); }); let result = await G.mongodb.collection('hero').insertMany(insertData); addGameLog(call.uid, "_addHero", {}, insertData) for (let key = 0; key < insertData.length; key++) { let v = insertData[key] let id = result.insertedIds[key].toHexString(); let {_id, ...ops} = v; call.addEventMsg('msg_s2c/HeroChange', id, { _id: id, ...ops }); if (!lshd[v.heroId]) lshd[v.heroId] = 0; lshd[v.heroId]++; } G.mongodb.collection('playerInfo', 'lshd_hero').updateOne( { uid: call.uid, type: 'lshd_hero' }, { $inc: lshd }, { upsert: true } ); call.addEventMsg('msg_s2c/LshdChange', 'hero', lshd); } /** * 删除英雄 */ static async cutHero(call: call, _idArr: string[]) { for (let _id of _idArr) { await HeroFun.delHero(call, _id); G.redis.del('hero', call.uid, _id); G.mongodb.collection('hero').deleteOne({uid: call.uid, _id: new ObjectId(_id)}); call.addEventMsg('msg_s2c/HeroChange', _id, {num: 0}); addGameLog(call.uid, "_cutHero", {}, {_id: _id}) } } /** * 添加饰物 */ static async addShiwu(call: call, val: atn[]) { let insertData: OptionalId[] = val.map(v => { return new Array(v.n).fill(1).map(v1 => { let shiwu: MongodbCollections['shiwu'] = { uid: call.uid, colour: v.colour, wearId: '', shiwuId: v.t, jichu: ShiwuFun.randomJichu({colour: v.colour, shiwuId: v.t}), fujia: [] }; if (v.shiwuBuff) { shiwu.jichu = v.shiwuBuff.jichu; shiwu.fujia = v.shiwuBuff.fujia; if (v.shiwuBuff.zhuanshu) shiwu.zhuanshu = v.shiwuBuff.zhuanshu; } else { ShiwuFun.randomFujiaAll(shiwu); ShiwuFun.randomZhuanshu(shiwu); } return shiwu; }); }).reduce((a, b) => a.concat(b)); let result = await G.mongodb.collection('shiwu').insertMany(insertData); addGameLog(call.uid, "_addShiWu", {}, insertData) insertData.forEach((v, key) => { let id = result.insertedIds[key].toHexString(); let {_id, ...ops} = v; call.addEventMsg('msg_s2c/ShiwuChange', id, { _id: id, ...ops }); }); if (insertData.filter(v => v.colour == 5)) { md_redPoint_check(G.server.uid_connections[call.uid], 'peijian_colour_5'); } return Object.values(result.insertedIds).map(v => G.mongodb.conversionId(v)); } /** * 删除饰物 */ static async cutShiwu(call: call, _idArr: string[]) { for (let _id of _idArr) { G.mongodb.collection('shiwu').deleteOne({uid: call.uid, _id: new ObjectId(_id)}); call.addEventMsg('msg_s2c/ShiwuChange', _id, {num: 0}); addGameLog(call.uid, "_cutShiwu", {}, {_id: _id}) } } /** * 添加配件 */ static async addPeijian(call: call, val: atn[]) { let lshd: k_v = {}; let insertData: OptionalId[] = val.map(v => { return new Array(v.n).fill(1).map(v1 => { return { lv: 1, uid: call.uid, wearId: '', jinglian: 0, peijianId: v.t.toString() }; }); }).reduce((a, b) => a.concat(b)); let result = await G.mongodb.collection('peijian').insertMany(insertData); addGameLog(call.uid, "_addPeiJian", {}, insertData) insertData.forEach((v, key) => { let {_id, uid, ...ops} = v; let id = _id.toHexString(); if (G.gc.peijian[v.peijianId].colour != 5) { if (!lshd[v.peijianId]) lshd[v.peijianId] = 0; lshd[v.peijianId]++; } G.redis.set('peijian', call.uid, id, {_id: id, ...ops}); call.addEventMsg('msg_s2c/PeijianChange', id, {_id: id, ...ops}); }); G.mongodb.collection('playerInfo', 'lshd_peijian').updateOne( { uid: call.uid, type: 'lshd_peijian' }, { $inc: lshd }, { upsert: true } ); call.addEventMsg('msg_s2c/LshdChange', 'peijian', lshd); return Object.values(result.insertedIds).map(v => G.mongodb.conversionId(v)); } /** * 删除配件 */ static async cutPeijian(call: call, _idArr: string[]) { for (let _id of _idArr) { G.redis.del('peijian', call.uid, _id); G.mongodb.collection('peijian').deleteOne({uid: call.uid, _id: new ObjectId(_id)}); call.addEventMsg('msg_s2c/PeijianChange', _id, {num: 0}); addGameLog(call.uid, "_cutPeijian", {}, {_id: _id}) } } /** * 获取playattr属性 */ static async getAttr(uid: string, where: { ctype: string; }) { let _w = where; Object.assign(_w, {uid: uid}); const _res = await G.mongodb.collection('playattr').find(_w).toArray(); _res.forEach(v => { if (v._id) { delete v['_id']; } }); return _res; } /** * 获取单条attr数据 */ static async getAttrOne(uid: string, where: { ctype: string; }) { let _w = where; Object.assign(_w, {uid: uid}); const _res = await G.mongodb.collection('playattr').findOne(_w); if (_res) { delete _res['_id']; } return _res; } /** * 取出 当天 符合条件的数据 */ static async getAttrByDate(uid: string, where: { ctype: string; }, time = 0) { if (time == 0) { time = G.time; } let _zeroTime = PublicShared.getToDayZeroTime(time); let _tmp = {lasttime: {$gte: _zeroTime, $lte: _zeroTime + 24 * 60 * 60 - 1}}; let _w = where; Object.assign(_w, {uid: uid, ..._tmp}); const _res = await G.mongodb.collection('playattr').find(_w).toArray(); _res.forEach(v => { if (v._id) { delete v['_id']; } }); return _res; } /** * 设置playAttr属性 */ static async setAttr(uid: string, where: { ctype: string; }, data: {}, islasttime = 1) { let _w = where; Object.assign(_w, {uid: uid}); if (islasttime == 1) { data["lasttime"] = G.time; } let _exists = await this.getAttrOne(uid, _w); if (!_exists) { // 加入创建数据时间 data["ctime"] = G.time; } let _res = await G.mongodb.collection('playattr').updateMany(_w, {$set: data}, {upsert: true}); return _res; } }