HJ_Server/src/public/player.ts
2023-12-31 11:24:38 +08:00

669 lines
22 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 {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<k_v<any>>;
conn: {
readonly id: string;
get otherBuff(): otherBuff;
get heroPos(): k_v<string>;
uid: string;
gud: player;
item?: ResGetList['list'];
refreshPower(): Promise<any>;
sendMsg: BaseConnection['sendMsg'];
};
service: { name: string };
addEventMsg: ApiCall['addEventMsg'];
req: {}
};
export class PlayerFun {
/**
* 获取玩家atn数量
*/
static async getAtnNum(call: call, atn: atn): Promise<number> {
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<player>) {
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<number> = {};
let insertData: OptionalId<OptionalId<MongodbCollections['equip']>>[] = 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<number> = {};
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<MongodbCollections['shiwu']>[] = 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<number> = {};
let insertData: OptionalId<CollectionPeiJian>[] = 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;
}
}