feat(稷下学宫): ff931afe4到7421822f6

This commit is contained in:
luying
2023-08-30 11:02:52 +08:00
parent 59a334d673
commit 05cbe318e9
102 changed files with 147876 additions and 715 deletions

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,184 @@
import { RougelikeRecordDetailType } from '../../db/RougelikeRecordDetail';
import { COLLECTION_TYPE, PUSH_ROUTE, ROUGE_LIKE_CARD_TYPE } from '../../consts';
import { genCode } from '../../pubUtils/util';
import { Card, RougelikeCharaModel, RougelikeCharaPara, RougelikeCharaType } from '../../db/RougelikeChara';
import { RougelikeCardPara, RougelikeCardModel } from '../../db/RougelikeCard';
import { CommonCard, CommonChara } from '../../pubUtils/interface';
import { RougelikeCollectionModel } from '../../db/RougelikeCollection';
import { sendMessageToUserWithSuc } from '../pushService';
import { gameData } from '../../pubUtils/data';
import { clone } from 'underscore';
import { RougeEffect, getSlotUnlockPoint } from './rougeEffectService';
export class HandleAddCard {
roleId: string;
sid: string;
gameCode: string;
getLayer: number = 0;
getWay: number = 0;
addCharas: RougelikeCharaPara[] = []; // 需要更新的角色
addPassiveCards: RougelikeCardPara[] = []; // 需要更新的特性卡
addHolyCards: RougelikeCardPara[] = []; // 需要更新的圣物
addCoin: number = 0; // 需要增加的试炼币
constructor(roleId: string, sid: string, gameCode: string, recordDetail?: RougelikeRecordDetailType) {
this.roleId = roleId;
this.sid = sid;
this.gameCode = gameCode;
if (recordDetail) {
this.getLayer = recordDetail.layer;
this.getWay = recordDetail.nodeType;
}
}
private getCommonParam() {
let { roleId, gameCode, getLayer, getWay } = this;
return { roleId, gameCode, getLayer, getWay };
}
public pushChara(id: number, maxHp: number, passiveCardIds: number[]) {
let cards: Card[] = [];
for (let index = 0; index < passiveCardIds.length; index++) {
let cardId = passiveCardIds[index];
let cardCode = this.pushPassiveCard(cardId, id);
cards.push({ index, cardCode, cardId });
}
let charaCode = genCode(8);
this.addCharas.push({ ...this.getCommonParam(), cards, charaCode, charaId: id, maxHp, hp: maxHp, ap: 0, shield: 0, roundSkill: 0, apSkill: 0 });
return charaCode;
}
public pushCard(id: number, rewardType: ROUGE_LIKE_CARD_TYPE, charaId: number = 0) {
if (rewardType == ROUGE_LIKE_CARD_TYPE.PASSIVE) {
return this.pushPassiveCard(id, charaId);
} else if (rewardType == ROUGE_LIKE_CARD_TYPE.HOLY) {
return this.pushHolyCard(id);
}
}
public pushPassiveCard(id: number, charaId: number = 0) {
let cardCode = genCode(8);
this.addPassiveCards.push({
...this.getCommonParam(), cardCode, cardId: id, type: ROUGE_LIKE_CARD_TYPE.PASSIVE, lv: gameData.rougePassiveCard.get(id)?.lv || 0, charaId
});
return cardCode
}
public pushHolyCards(ids: number[]) {
for (let id of ids) this.pushHolyCard(id);
}
public pushHolyCard(id: number) {
let cardCode = genCode(8);
this.addHolyCards.push({
...this.getCommonParam(), cardCode, cardId: id, type: ROUGE_LIKE_CARD_TYPE.HOLY, useCount: this.getHolyUseCount(id)
});
return cardCode
}
private getHolyUseCount(holyId: number) {
const holyCardData = gameData.rougeHolyCard.get(holyId);
return holyCardData?.useCount || 0;
}
public async save() {
let result: { addCharas?: CommonChara[], addCards?: CommonCard[] } = {};
if (this.addCharas.length > 0) {
let updateParams = this.addCharas || [];
let resultArrs = await RougelikeCharaModel.createCharas(updateParams);
this.addCharas = resultArrs;
result.addCharas = resultArrs.map(param => new CommonChara(param));
}
if (this.addPassiveCards.length > 0 || this.addHolyCards.length > 0) {
let updateParams = [...(this.addPassiveCards || []), ...(this.addHolyCards || [])];
// await RougelikeCardModel.bulkWriteUpdate(updateParams);
updateParams.map(async param => {
const { gameCode, cardCode } = param;
await RougelikeCardModel.updateByCode(gameCode, cardCode, { $set: param })
});
result.addCards = updateParams.map(param => new CommonCard(param));
}
let collections: { type: number, id: number, addNum?: number }[] = [];
for (let { cardId, type } of result.addCards || []) {
if (type == ROUGE_LIKE_CARD_TYPE.PASSIVE) {
collections.push({ type: COLLECTION_TYPE.PASSIVE_CARD, id: cardId });
}
if (type == ROUGE_LIKE_CARD_TYPE.HOLY) collections.push({ type: COLLECTION_TYPE.HOLY_CARD, id: cardId });
}
if (collections.length > 0) await addCollection(this.roleId, this.sid, this.gameCode, collections);
if (this.addCharas.length > 0) {
result.addCharas = await this.getCharasByHolyEffect();
}
return result;
}
// 处理获得角色卡时拥有圣物效果
public async getCharasByHolyEffect() {
let result = await getSlotUnlockPoint(this.roleId, this.gameCode, this.addCharas || []);
this.addCharas = result.dbCharas;
if (result.isUpdate) {
await RougelikeCharaModel.bulkWriteUpdate(this.addCharas || []);
}
return this.addCharas.map(param => new CommonChara(param));
}
// 处理圣物效果
public async getHolyEffect() {
let result: { addCharas?: CommonChara[], addCards?: CommonCard[] } = {};
let charasMap = (this.addCharas || []).reduce((result, cur) => { result.set(cur.charaCode, cur); return result; }, new Map<string, RougelikeCharaPara>());
let cardsMap = [...(this.addPassiveCards || []), ...(this.addHolyCards || [])].reduce((result, cur) => { result.set(cur.cardCode, cur); return result; }, new Map<string, RougelikeCardPara>());
if ((this.addHolyCards || []).length > 0) {
let rougeEffect = new RougeEffect(this.roleId, this.gameCode);
let holyIds = clone(this.addHolyCards.map(cur => { return { cardCode: cur.cardCode, cardId: cur.cardId, useCount: cur.useCount } }));
let { charas, cards } = await rougeEffect.getEffectImmediate(holyIds);
if (charas && charas.size > 0) {
await RougelikeCharaModel.bulkWriteUpdate([...charas.values()]);
charasMap = new Map([...charasMap, ...charas])
}
if (cards && Object.entries(cards).length > 0) {
cardsMap = new Map([...cardsMap, ...cards])
}
}
result.addCharas = [...charasMap.values()].map(param => new CommonChara(param));
result.addCards = [...cardsMap.values()].map(param => new CommonCard(param));
return result;
}
}
export function formateCharasOrCards(params: RougelikeCharaPara[] | RougelikeCardPara[], type: number) {
let result: { charas?: CommonChara[], cards?: CommonCard[] } = {};
if (!params || params.length == 0) return result;
const updateParams = params || [];
if (type == ROUGE_LIKE_CARD_TYPE.CHARA) result.charas = updateParams.map(param => new CommonChara(param));
else result.cards = updateParams.map(param => new CommonCard(param));
return result;
}
export async function addCollection(roleId: string, sid: string, gameCode: string, arr: { type: number, id: number, addNum?: number }[]) {
let collections: { type: number, id: number, num: number }[] = [], passiveCnt = 0;
for (let { type, id, addNum = 1 } of arr) {
if (type == COLLECTION_TYPE.PASSIVE_CARD_SUM) continue;
let collection = await RougelikeCollectionModel.addRec(roleId, type, id, gameCode, addNum);
if (!collection || collection.num > 1) continue;
if (collection.num == 1) passiveCnt++;
collections.push({ type, id, num: collection.num });
}
if (passiveCnt > 0) {
let collection = await RougelikeCollectionModel.addRec(roleId, COLLECTION_TYPE.PASSIVE_CARD_SUM, 0, gameCode, passiveCnt);
collections.push({ type: COLLECTION_TYPE.PASSIVE_CARD, id: 0, num: collection.num });
}
await sendMessageToUserWithSuc(roleId, PUSH_ROUTE.ROUGE_COLLECT_UPDATE, { collections }, sid);
return collections;
}
export async function addSingleCollect(roleId: string, sid: string, gameCode: string, type: number, id: number) {
return await addCollection(roleId, sid, gameCode, [{ type, id }]);
}
export async function addSameTypeCollect(roleId: string, sid: string, gameCode: string, type: number, ids: number[]) {
return await addCollection(roleId, sid, gameCode, ids.map(id => ({ type, id })));
}

View File

@@ -0,0 +1,536 @@
import { ABI_TYPE } from "../../consts/constModules/abilityConst";
import { ROUGELIKE_SKILLTYPE, ROUGE_EFFECT_TYPE, ROUGE_EFFECT_TYPE_KIND, ROUGE_LIKE_CARD_TYPE, ROUGE_SLOT_LIMIT } from "../../consts";
import { RougelikeCardModel, RougelikeCardPara, RougelikeCardType } from "../../db/RougelikeCard";
import { Card, RougelikeCharaModel, RougelikeCharaPara, RougelikeCharaType } from "../../db/RougelikeChara";
import { RougelikeRecordModel } from "../../db/RougelikeRecord";
import { gameData, getRougeEffectTypeKind } from "../../pubUtils/data";
import { DicRougePassiveCardPlan } from "../../pubUtils/dictionary/DicRougePassiveCardPlan";
import { getRandEelm } from "../../pubUtils/util";
import { RougelikeTechModel } from "../../db/RougelikeTech";
import { getAuthorTypeCardNum } from "./rougeService";
import * as util from 'util';
export class holyId {
cardCode: string;
cardId: number;
useCount: number;
}
export class RougeEffect {
roleId: string;
gameCode: string;
newEffect: { effectType: number, effectParam: number[], cardCode?: string }[] = [];
holyMap = new Map<string, { cardCode: string, cardId: number, useCount: number }>();
updateHolyMap = new Map<string, RougelikeCardType>();
updateCharaMap = new Map<string, RougelikeCharaType>();
updateCardMap = new Map<string, RougelikeCardType>();
dbCharas: RougelikeCharaType[] = [];
dbCards: RougelikeCardType[] = [];
constructor(roleId: string, gameCode: string) {
this.roleId = roleId || '';
this.gameCode = gameCode || '';
}
/**
* 根据类型获取effectId
* @param effectTypes
* @param holyIds
* @returns
*/
public async getEffectData(effectTypes: number[], holyIds?: holyId[]) {
let kinds = getRougeEffectTypeKind(effectTypes);
if (kinds.includes(ROUGE_EFFECT_TYPE_KIND.HOLY)) {
if (!holyIds || holyIds.length == 0) {
const dbCards = await RougelikeCardModel.findByGameCodeAndType(this.gameCode, ROUGE_LIKE_CARD_TYPE.HOLY);
holyIds = dbCards.map((cur) => { return { cardCode: cur.cardCode, cardId: cur.cardId, useCount: cur.useCount || 0 } });
}
if (!holyIds || holyIds.length == 0) return { newEffect: this.newEffect, holyMap: this.holyMap };
for (const { cardCode, cardId, useCount } of holyIds) {
let effectIds = gameData.rougeHolyCard.get(cardId)?.effectId || [];
if (effectIds.length == 0) continue;
for (const effectId of effectIds) {
const effectDataOne = gameData.rougeEffect.get(effectId);
if (!effectDataOne) continue;
const effectParam = effectDataOne.effectParam || [];
for (let effectType of effectTypes) {
if (effectType != effectDataOne.effectType || 0) continue;
this.newEffect.push({ effectType, effectParam, cardCode });
this.holyMap.set(cardCode, { cardCode, cardId, useCount })
}
}
}
}
if (kinds.includes(ROUGE_EFFECT_TYPE_KIND.TECH)) {
let tech = await RougelikeTechModel.findByRoleId(this.roleId, 'effectIds');
for (let effectId of (tech?.effectIds || [])) {
const effectDataOne = gameData.rougeEffect.get(effectId);
if (!effectDataOne) continue;
for (let effectType of effectTypes) {
if (effectType != effectDataOne.effectType || 0) continue;
this.newEffect.push({ effectType, effectParam: effectDataOne.effectParam || [] });
}
}
}
return { newEffect: this.newEffect, holyMap: this.holyMap }
}
/* **
* 获取圣物时生效
* @param gameCode
*/
public async getEffectImmediate(holyIds?: holyId[]) {
let result: { charas?: Map<string, RougelikeCharaType>, cards?: Map<string, RougelikeCardType> } = {};
this.dbCharas = await RougelikeCharaModel.findByGameCode(this.gameCode);
this.dbCards = await RougelikeCardModel.findByGameCode(this.gameCode);
await this.getEffectData([
ROUGE_EFFECT_TYPE.HOLY_CHARA_SLOT_UNLOCK_ALL, //2002
ROUGE_EFFECT_TYPE.HOLY_CHARA_SLOT_UNLOCK_RAND, //2003
ROUGE_EFFECT_TYPE.HOLY_PASSIVE_UPDATE_RAND, //2006
ROUGE_EFFECT_TYPE.HOLY_UPDATE_PASSIVE_BY_LV, //2012
ROUGE_EFFECT_TYPE.HOLY_REPAIRE_HOLY, //2015
], holyIds)
await this.getCharaSlot();
await this.getRandCharaSlot();
await this.getRandomCardLv()
await this.getPassiveLv();
await this.getRecoveryHoly();
result = { charas: this.updateCharaMap, cards: this.updateCardMap };
return result;
}
// 获得该圣物时所有学员立刻解锁X个特性槽 2002
private async getCharaSlot() {
if (this.newEffect.length == 0 || this.dbCharas.length == 0) return;
for (const { effectParam, effectType } of this.newEffect) {
if (effectType != ROUGE_EFFECT_TYPE.HOLY_CHARA_SLOT_UNLOCK_ALL) continue;
if (effectParam.length == 0) continue;
for (let val of this.dbCharas) {
let { charaCode, cards = [] } = val;
let unlockNum = effectParam[0] || 0;
for (let i = 0; i < unlockNum; i++) {
unlockNum = effectParam[0] || 0;
if (cards.length >= ROUGE_SLOT_LIMIT) continue;
for (let index = 0; index < ROUGE_SLOT_LIMIT; index++) {
if (cards.find(cur => cur.index == index) != undefined || unlockNum == 0) continue;
cards.push({ index, cardCode: '', cardId: 0 });
this.updateCharaMap.set(charaCode, val);
unlockNum--;
}
}
}
}
}
// 获得该圣物时随机解锁X个学员的Y个特性槽 2003
private async getRandCharaSlot() {
if (this.newEffect.length == 0 || this.dbCharas.length == 0) return;
for (const { effectParam, effectType } of this.newEffect) {
if (effectType != ROUGE_EFFECT_TYPE.HOLY_CHARA_SLOT_UNLOCK_RAND) continue;
if (effectParam.length == 0) continue;
const randomNum = effectParam[0] || 0;
let unlockNum = effectParam[1] || 0;
let charas = getRandEelm(this.dbCharas.filter(cur => cur.cards.length < ROUGE_SLOT_LIMIT), randomNum);
for (let val of charas) {
unlockNum = effectParam[1] || 0;
let { charaCode, cards = [] } = val;
for (let index = 0; index < ROUGE_SLOT_LIMIT; index++) {
if (cards.find(cur => cur.index == index) != undefined || unlockNum == 0) continue;
cards.push({ index, cardCode: '', cardId: 0 });
this.updateCharaMap.set(charaCode, val);
unlockNum--;
}
}
}
}
// 随机升级X个已装备的特性 2006
private async getRandomCardLv() {
let cards: RougelikeCardType[] = [];
if (this.newEffect.length == 0 || this.dbCards.length == 0) return cards;
for (const { effectParam, effectType } of this.newEffect) {
if (effectType != ROUGE_EFFECT_TYPE.HOLY_PASSIVE_UPDATE_RAND) continue;
if (effectParam.length == 0) continue;
const randomNum = effectParam[0] || 0;
let random = this.dbCards.filter(cur => cur.type == ROUGE_LIKE_CARD_TYPE.PASSIVE && cur.charaId != 0 && cur.lv < (gameData.rougePassiveCard.get(cur.cardId)?.lv || 0))
let cards = getRandEelm(random, randomNum);
cards.forEach(cur => {
cur.lv += 1;
this.updateCardMap.set(cur.cardCode, cur);
});
}
}
// 获得该圣物时立即升级所有X星特性卡 2012
private async getPassiveLv() {
if (this.newEffect.length == 0) return;
for (const { effectParam, effectType } of this.newEffect) {
if (effectType != ROUGE_EFFECT_TYPE.HOLY_UPDATE_PASSIVE_BY_LV) continue;
if (effectParam.length == 0) continue;
const level = effectParam[0] || 0;
for (let val of this.dbCards) {
let { lv, cardId, cardCode } = val;
const passiveCardData = gameData.rougePassiveCard.get(cardId);
if (level != passiveCardData?.quality || 0 || lv >= passiveCardData?.lv || 0) continue;
lv += 1;
this.updateCardMap.set(cardCode, val);
}
}
}
// 获得该圣物时随机修复X个已损毁的圣物 2015
private async getRecoveryHoly() {
if (this.newEffect.length == 0) return;
for (const { effectParam, effectType } of this.newEffect) {
if (effectType != ROUGE_EFFECT_TYPE.HOLY_REPAIRE_HOLY) continue;
if (effectParam.length == 0) continue;
const num = effectParam[0] || 0;
let canRandomCards: RougelikeCardType[] = []
this.dbCards.forEach(cur => {
const { cardCode, cardId, useCount = 0, type } = cur;
const holyCardData = gameData.rougeHolyCard.get(cardId);
let tempUseCount = holyCardData?.useCount || 0;
if (tempUseCount > 0 && useCount < tempUseCount && type == ROUGE_LIKE_CARD_TYPE.HOLY) canRandomCards.push(cur);
})
let randomCards = getRandEelm(canRandomCards, num);
for (let val of randomCards) {
let { cardCode, cardId } = val;
val.useCount += 1;
// if (this.holyMap.has(cardCode)) this.holyMap.set(cardCode, { cardCode, cardId, useCount });
this.updateCardMap.set(cardCode, val)
}
}
}
// 和圣物相关maxhp
public async getEffectMaxHp() {
let addRatio = 0;
await this.getEffectData([
ROUGE_EFFECT_TYPE.HOLY_CHARA_MAIN_ATTR_UP_BY_COIN, // 2005
ROUGE_EFFECT_TYPE.HOLY_CHARA_MAIN_ATTR_UP, // 2020
ROUGE_EFFECT_TYPE.TECH_CHARA_MAIN_ATTR_UP, // 3001
])
addRatio = await this.getMaxHpByCoin();
addRatio += await this.getMaxHpByBase();
return addRatio;
}
// 每有X个试炼币全员基础属性id提高Y 2005
private async getMaxHpByCoin() {
let addRatio = 0;
const dbRecord = await RougelikeRecordModel.findByGameCode(this.gameCode);
let coinTotal = dbRecord?.coin || 0;
if (this.newEffect.length == 0) return addRatio;
for (const { effectParam, effectType } of this.newEffect) {
if (effectType != ROUGE_EFFECT_TYPE.HOLY_CHARA_MAIN_ATTR_UP_BY_COIN) continue;
if (effectParam.length == 0) continue;
const count = effectParam[0] || 0;
const id = effectParam[1] || 0;
const value = effectParam[2] || 0;
if (count == 0 || id != ABI_TYPE.ABI_HP || value == 0) continue;
addRatio += Math.floor(coinTotal / count * value)
}
return addRatio;
}
// 基础属性Id&num 2020
private async getMaxHpByBase() {
let addRatio = 0;
if (this.newEffect.length == 0) return addRatio;
for (const { effectParam, effectType } of this.newEffect) {
if (effectType != ROUGE_EFFECT_TYPE.HOLY_CHARA_MAIN_ATTR_UP && effectType != ROUGE_EFFECT_TYPE.TECH_CHARA_MAIN_ATTR_UP) continue;
if (effectParam.length == 0) continue;
const id = effectParam[0] || 0;
const value = effectParam[1] || 0;
if (id != ABI_TYPE.ABI_HP || value == 0) continue;
addRatio += value;
}
return addRatio;
}
}
// 每场战斗结束后学员恢复血量上限X%的生命 2001
export async function getCharaHp(roleId: string, gameCode: string) {
let hpRatio = 0;
let rougeEffect = new RougeEffect(roleId, gameCode);
let { newEffect, holyMap } = await rougeEffect.getEffectData([
ROUGE_EFFECT_TYPE.HOLY_CHARA_HP_RECOVERY_UP,// 2001
]);
if (newEffect.length == 0) return hpRatio;
for (const { effectType, effectParam } of newEffect) {
if (effectParam.length == 0) continue;
hpRatio += (effectParam[0] || 0);
}
return hpRatio;
}
// 战斗胜利后获得的试炼币增加X 2004
export async function getAddCoin(roleId: string, gameCode: string, nodeType: number) {
let coinRatio = 0, coinAdd = 0;
let rougeEffect = new RougeEffect(roleId, gameCode);
let { newEffect, holyMap } = await rougeEffect.getEffectData([
ROUGE_EFFECT_TYPE.HOLY_COIN_UP, //2004
ROUGE_EFFECT_TYPE.TECH_COIN_UP_BY_NODE_TYPE, //3007
]);
if (newEffect.length == 0) return { coinRatio, coinAdd };
for (const { effectType, effectParam } of newEffect) {
if (effectParam.length == 0) continue;
if (effectType == ROUGE_EFFECT_TYPE.HOLY_COIN_UP) {
const type = effectParam[0] || 0;
const value = effectParam[1] || 0;
if (type == 1) coinAdd += value;
else if (type == 2) coinRatio += value;
}
if (effectType == ROUGE_EFFECT_TYPE.TECH_COIN_UP_BY_NODE_TYPE) {
let [targetNodeType = 0, value = 0] = effectParam;
if (targetNodeType == nodeType) coinRatio += value;
}
}
return { coinRatio, coinAdd };
}
// 获得圣物后X流派特性卡的权重增加Y 2007
export async function getAddPassiveWeight(roleId: string, gameCode: string, authorType: number) {
let addPassiveWeight = 0;
let rougeEffect = new RougeEffect(roleId, gameCode);
let { newEffect, holyMap } = await rougeEffect.getEffectData([
ROUGE_EFFECT_TYPE.HOLY_PASSIVE_WEIGHT_UP_BY_AUTHOR,//2007
]);
if (newEffect.length == 0) return addPassiveWeight;
for (const { effectParam } of newEffect) {
if (effectParam.length == 0) continue;
const type = effectParam[0] || 0;
const value = effectParam[1] || 0;
if (type != authorType) continue;
addPassiveWeight += value;
}
return addPassiveWeight;
}
// 非boss战斗失败视为胜利并且满血复活 2009
export async function getNoBossRecoveryHp(roleId: string, gameCode: string) {
let rougeEffect = new RougeEffect(roleId, gameCode);
let { newEffect, holyMap } = await rougeEffect.getEffectData([
ROUGE_EFFECT_TYPE.HOLY_REVIVE_ALL,//2009
]);
if (newEffect.length == 0) return false;
return true; // 拿到true 将所有学员更新和hp=maxHp
}
// 战斗胜利后若有学员死亡则满血复活X名死亡学员 2010
export async function getBattleRecoveryNum(roleId: string, gameCode: string) {
let recoveryNum = 0;
let rougeEffect = new RougeEffect(roleId, gameCode);
let { newEffect, holyMap } = await rougeEffect.getEffectData([
ROUGE_EFFECT_TYPE.HOLY_REVIVE_CHARA_RAND,//2010
]);
if (newEffect.length == 0) return recoveryNum;
for (const { effectParam } of newEffect) {
if (effectParam.length == 0) continue;
recoveryNum += (effectParam[0] || 0)
}
return recoveryNum;
}
// 试炼商店中所有商品X折出售 2011
export async function getShopDiscount(roleId: string, gameCode: string) {
let discount = 100;
let rougeEffect = new RougeEffect(roleId, gameCode);
let { newEffect, holyMap } = await rougeEffect.getEffectData([
ROUGE_EFFECT_TYPE.HOLY_SHOP_DISCOUNT,//2011
]);
if (newEffect.length == 0) return discount;
for (const { effectParam } of newEffect) {
if (effectParam.length == 0) continue;
const tempDiscount = effectParam[0] || 0;
discount *= (tempDiscount / 100);
}
return Math.floor(discount);
}
// 下次选择特性卡时必定出现X星特性卡 2013
export async function getChooseQualityPassives(roleId: string, gameCode: string, passiveCards: DicRougePassiveCardPlan[]) {
let targetPassives: DicRougePassiveCardPlan[] = [];
let rougeEffect = new RougeEffect(roleId, gameCode);
let { newEffect, holyMap } = await rougeEffect.getEffectData([
ROUGE_EFFECT_TYPE.HOLY_PASSIVE_CHOOSE_FIX,//2013
]);
if (newEffect.length == 0) return targetPassives;
for (const { effectParam } of newEffect) {
if (effectParam.length == 0) continue;
const level = effectParam[0] || 0;
for (let val of passiveCards) {
const { cardId } = val;
const passiveCardData = gameData.rougePassiveCard.get(cardId);
if (level != passiveCardData?.lv || 0) continue;
targetPassives.push(val);
}
}
return targetPassives;
}
// 下次选择特性卡时可多选X张特性卡 2014
export async function getAddChoosePassive(roleId: string, gameCode: string) {
let addChooseNum = 0;
let rougeEffect = new RougeEffect(roleId, gameCode);
let { newEffect, holyMap } = await rougeEffect.getEffectData([
ROUGE_EFFECT_TYPE.HOLY_PASSIVE_CHOOSE_NUM_UP,//2014
]);
for (const { effectParam } of newEffect) {
if (effectParam.length == 0) continue;
const num = effectParam[0] || 0;
addChooseNum += num;
}
return addChooseNum
}
// 获得该圣物后所有角色解锁X号位置的特性槽 2023
export async function getSlotUnlockPoint(roleId: string, gameCode: string, dbCharas: RougelikeCharaPara[]) {
let isUpdate = false;
let rougeEffect = new RougeEffect(roleId, gameCode);
let { newEffect, holyMap } = await rougeEffect.getEffectData([
ROUGE_EFFECT_TYPE.HOLY_CHARA_SLOT_UNLOCK_POINT,//2023
]);
for (const { effectParam } of newEffect) {
if (effectParam.length == 0) continue;
const index = effectParam[0] || 0;
for (let { cards = [] } of dbCharas) {
if (cards.length == 0) {
cards.push({ index, cardCode: '', cardId: 0 });
isUpdate = true;
continue;
}
if (cards.find(cur => cur.index == index) == undefined) {
isUpdate = true
cards.push({ index, cardCode: '', cardId: 0 });
}
}
}
return { isUpdate, dbCharas };
}
// 休整点额外恢复X%的生命 2017 3006
export async function getRecoveryExtendHp(roleId: string, gameCode: string) {
let hpRatio = 0;
let rougeEffect = new RougeEffect(roleId, gameCode);
let { newEffect, holyMap } = await rougeEffect.getEffectData([
ROUGE_EFFECT_TYPE.HOLY_RECOVERY_POINT_UP, // 2017
ROUGE_EFFECT_TYPE.TECH_RECOVERY_POINT_UP, // 3006
]);
if (newEffect.length == 0) return hpRatio;
for (const { effectType, effectParam } of newEffect) {
if (effectParam.length == 0) continue;
hpRatio += (effectParam[0] || 0);
}
return hpRatio;
}
// 休整点特训价格X折 2018 3009
export async function getTrainCardDiscount(roleId: string, gameCode: string) {
let discount = 100;
let rougeEffect = new RougeEffect(roleId, gameCode);
let { newEffect, holyMap } = await rougeEffect.getEffectData([
ROUGE_EFFECT_TYPE.HOLY_TRAIN_POINT_DISCOUNT, // 2018
ROUGE_EFFECT_TYPE.TECH_TRAIN_POINT_DISCOUNT, // 3009
]);
if (newEffect.length == 0) return discount;
for (const { effectParam } of newEffect) {
if (effectParam.length == 0) continue;
const tempDiscount = effectParam[0] || 0;
discount *= (tempDiscount / 100);
}
return Math.floor(discount);
}
// 初始获得试炼币&圣物 3003 3005
export async function getEffectWhenGameStart(roleId: string, gameCode: string, authorType: number) {
let rougeEffect = new RougeEffect(roleId, gameCode);
let addCoin = 0, cardIds: number[] = [];
let { newEffect } = await rougeEffect.getEffectData([
ROUGE_EFFECT_TYPE.TECH_INIT_HOLY_BY_AUTHOR,
ROUGE_EFFECT_TYPE.TECH_INIT_COIN,
]);
if (newEffect.length == 0) return { addCoin, cardIds };
for (const { effectType, effectParam } of newEffect) {
if (effectParam.length == 0) continue;
if (effectType == ROUGE_EFFECT_TYPE.TECH_INIT_HOLY_BY_AUTHOR) {
for (let cardId of effectParam) {
let dicCard = gameData.rougeHolyCard.get(cardId);
if (!dicCard || dicCard.authorType != authorType) continue;
cardIds.push(cardId);
}
}
if (effectType == ROUGE_EFFECT_TYPE.TECH_INIT_COIN) addCoin += effectParam[0];
}
return { addCoin, cardIds };
}
// 是否可以选择技能卡 3004
export async function checkCanChooseSkillCard(roleId: string, gameCode: string, skillId: number, cards: Card[]) {
let rougeEffect = new RougeEffect(roleId, gameCode);
let { newEffect } = await rougeEffect.getEffectData([
ROUGE_EFFECT_TYPE.TECH_CAN_CHOOSE_SKILL_CARD, // 3004
]);
let dicSkillCard = gameData.rougeSkillCard.get(skillId);
if (!dicSkillCard) return false;
let { authorType, skillType } = dicSkillCard;
let num = getAuthorTypeCardNum(authorType, cards);
let curParam = newEffect.find(({ effectParam }) => {
return effectParam[1] == skillType;
});
if (!curParam || num < curParam.effectParam[0]) return false;
return true;
}
// 是否可以重置
export async function checkCanReRandomReward(roleId: string, gameCode: string, rewardType: number) {
let rougeEffect = new RougeEffect(roleId, gameCode);
let { newEffect } = await rougeEffect.getEffectData([
ROUGE_EFFECT_TYPE.TECH_PASSIVE_RANDOM_AGAIN, // 3008
]);
let curParam = newEffect.find(({ effectParam }) => {
return effectParam[0] == rewardType;
});
if (!curParam) return { canReRandom: false, costCoin: 0 }
return { canReRandom: true, costCoin: curParam.effectParam[1] || 0 };
}

View File

@@ -0,0 +1,958 @@
import { clone, result } from "underscore";
import { ROUGE_CHARA_INITIAL, ROUGE_CHARA_TYPE, ROUGE_EFFECT_TYPE, ROUGE_LIKE_CARD_TYPE, ROUGE_LIKE_CHOOSE_REWARD, ROUGE_LIKE_NODE_TYPE, ROUGE_LIKE_STATUS, SHOP_REFRESH_TYPE } from "../../consts";
import { Card, RougelikeCharaModel, RougelikeCharaType } from "../../db/RougelikeChara";
import { RougelikeLayerModel, RougelikeLayerType } from "../../db/RougelikeLayer";
import { RougelikeRecordModel, RougelikeRecordType } from "../../db/RougelikeRecord";
import { gameData } from "../../pubUtils/data";
import { ROUGELIKE } from "../../pubUtils/dicParam";
import { genCode, getRandEelm, getRandEelmWithWeight, getRandEelmWithWeightAndNum, getRandValueByMinMax } from "../../pubUtils/util";
import { RougelikeCardModel, RougelikeCardType } from "../../db/RougelikeCard";
import RougelikeRecordDetail, { RougelikeRecordDetailModel, RougelikeRecordDetailPara, RougelikeRecordDetailType } from "../../db/RougelikeRecordDetail";
import { CollectionReturnParam, CommonCard, CommonChara, CommonNode, CommonReward, RewardInter, layerNode } from "../../pubUtils/interface";
import { DicRougeQuestionMarkPlan } from "../../pubUtils/dictionary/DicRougeQuestionMarkPlan";
import { DicRougeRandomEventPlan } from "../../pubUtils/dictionary/DicRougeRandomEventPlan";
import * as util from 'util';
import { RougelikeCollectionModel, } from "../../db/RougelikeCollection";
import { RougelikeScoreModel } from "../../db/RougelikeScore";
import { getTechData } from "./rougeTechService";
import { getZeroPointOfTimeD } from "../../pubUtils/timeUtil";
import { sendMailByContent } from "../mailService";
import { DicRougeCharaCardPlan } from "../../pubUtils/DicRougeCharaCardPlan";
import { MAIL_TYPE, PUSH_ROUTE } from "../../consts";
import { sendMessageToUserWithSuc } from "../pushService";
import { RougeEffect, getAddChoosePassive, getAddPassiveWeight, getChooseQualityPassives, getShopDiscount } from "./rougeEffectService";
import { formateCharasOrCards } from "./rougeCollectService";
import { errlogger } from "../../util/logger";
import { RougelikeExtendModel } from "../../db/RougelikeExtend";
export async function getRougeData(roleId: string) {
let isPlaying = true, gameCode = '';
const dbRecord = await RougelikeRecordModel.findByRoleIdAndStatus(roleId, ROUGE_LIKE_STATUS.SUCCESS);
if (!dbRecord) isPlaying = false;
else gameCode = dbRecord.gameCode;
let dbScore = await RougelikeScoreModel.findByRoleId(roleId);
let techData = await getTechData(roleId);
let dbCollections = await RougelikeCollectionModel.findByRoleId(roleId);
let collections = dbCollections.map((obj) => new CollectionReturnParam(obj));
const dbExtends = await RougelikeExtendModel.findByRoleId(roleId);
const limitIds = dbExtends.map(cur => cur.limitId);
return { isPlaying, gameCode, weeklyScore: dbScore?.score || 0, receivedScore: dbScore?.received || [], ...techData, collections, limitIds }
}
/*
/**
* 获取初始三名角色卡
* @param
* @returns
*/
export function getInitCharaCard() {
let canRandomCharas = gameData.rougeCharaByInitial.get(ROUGE_CHARA_INITIAL.CAN);
if (!canRandomCharas || canRandomCharas.length < ROUGELIKE.INIT_RANDOM_CHARA_COUNT) {
console.error("getInitChara--配置表中能初始随机的角色卡不足, canRandomCharas=%s", canRandomCharas);
return;
}
let randomData = getRandEelm(canRandomCharas, ROUGELIKE.INIT_RANDOM_CHARA_COUNT)
return randomData;
}
/**
* 获取大地图生成数据
* @param layerPlan
* @param layerCount
*/
export function getMap(layerPlan: number, layerCount: number) {
let retLayer = getLayerNodeRandom(layerPlan, layerCount);
if (!retLayer) return [];
return getLayerNodeLineRandom(retLayer) || [];
}
export function getLayerNodeRandom(layerPlan: number, layerCount: number) {
if (!layerPlan || !layerCount) return;
const layerPlanDatas = gameData.rougeLayerPlanByPlanId.get(layerPlan);
if (!layerPlanDatas || layerPlanDatas.length != layerCount) return console.error("getMap--获取配置层数不一致, layerPlan=%s, layerCount=%s", layerPlan, layerCount);
let retLayer = new Map<number, CommonNode>();
//获取可随机到的节点
for (let data of layerPlanDatas) {
let layerNodeNumPlanDatas = gameData.rougeLayerNodeNumPlan.get(data.nodeNumPlan);
if (!layerNodeNumPlanDatas) return console.error("getMap--rougeLayerNodeNumPlan配置错误, planId=%s", data.nodeNumPlan);
let nodeNum = 0;
if (layerNodeNumPlanDatas.length == 1) nodeNum = 1;
else nodeNum = getRandEelmWithWeight(layerNodeNumPlanDatas).dic.nodeNum;
let layerNodePlans = gameData.rougeLayerNodePlan.get(data.nodePlan);
if (!layerNodePlans || layerNodePlans.length == 0) return console.error("getMap--rougeLayerNodePlan配置错误, planId=%s", data.nodePlan);
let randomNodes: CommonNode['layerNodes'] = [], tempIndex = 0;
if (layerNodePlans.length > nodeNum) {
randomNodes = getRandEelmWithWeightAndNum(layerNodePlans, nodeNum).map((cur) => {
let nodeId = cur.dic.nodeId;
let nodeData = gameData.rougeNode.get(nodeId);
if (!nodeData) errlogger.error(`nodePlane ${data.nodePlan} 's nodeId ${nodeId} not found`);
return { detailCode: genCode(8), index: tempIndex++, nodeId, preNodeIndexs: [], type: nodeData.nodeType, isChoose: 0 };
});
} else {
randomNodes = layerNodePlans.map((cur) => {
let nodeId = cur.nodeId;
let nodeData = gameData.rougeNode.get(nodeId);
return { detailCode: genCode(8), index: tempIndex++, nodeId: cur.nodeId, preNodeIndexs: [] as number[], type: nodeData.nodeType, isChoose: 0 };
});
}
retLayer.set(data.layerIndex, { layer: data.layerIndex, layerNodes: randomNodes });
}
// console.log('-x-x--x-x-x-x-x-x-x-x-x- retLayer', util.inspect(retLayer, { depth: null }));
return retLayer;
}
/**
* 随机节点连线
* @param retLayer
* @returns
*/
export function getLayerNodeLineRandom(retLayer: Map<number, CommonNode>) {
for (let [key, value] of retLayer) {
let preLayer = retLayer.get(key - 1);
let curLayer = retLayer.get(key);
if (!preLayer || !curLayer) continue;
let tempPreNodes = preLayer.layerNodes; // 前一层节点数据
let tempCurNodes = curLayer.layerNodes; // 当前层节点数据
let indexMap = new Map<number, { dx: number, index: number }>(); //记录下前一层有那些节点与当前层是否连线
//当前层首节点
tempCurNodes[0].preNodeIndexs.push(tempPreNodes[0].index);
indexMap.set(tempPreNodes[0].index, { dx: 0, index: tempCurNodes[0].index });
//当前层尾节点
if (!(tempCurNodes.length == 1 && tempPreNodes.length == 1)) {
tempCurNodes[tempCurNodes.length - 1].preNodeIndexs.push(tempPreNodes[tempPreNodes.length - 1].index);
indexMap.set(tempPreNodes[tempPreNodes.length - 1].index, { dx: tempCurNodes.length - 1, index: tempCurNodes[tempCurNodes.length - 1].index });
}
for (let i = 0; i < tempCurNodes.length - 1; i++) {
let minIndex = 0, maxIndex = 0, start = 0;
if (i != 0) start = minIndex = maxIndex = Math.max(...tempCurNodes[i - 1].preNodeIndexs);
if (tempPreNodes[start + 1]) maxIndex = tempPreNodes[start + 1].index;
if (tempPreNodes[start + 2]) maxIndex = tempPreNodes[start + 2].index;
let randomIndex = minIndex;
if (minIndex < maxIndex) randomIndex = getRandValueByMinMax(minIndex, maxIndex + 1, 0);
if (tempCurNodes[i].preNodeIndexs.indexOf(randomIndex) != -1) continue;
tempCurNodes[i].preNodeIndexs.push(randomIndex);
if (indexMap.get(randomIndex) && indexMap.get(randomIndex).dx < i) continue;
indexMap.set(randomIndex, { dx: i, index: tempCurNodes[i].index });
}
//处理前一层有节点未连接情况
for (let i = 1; i < tempPreNodes.length - 1; i++) {
if (indexMap.get(tempPreNodes[i].index)) continue;
let minDx = 0, maxDx = 0;
let tempMap = new Map<number, number>();
if (tempPreNodes[i - 1] && indexMap.get(tempPreNodes[i - 1].index)) {
minDx = maxDx = indexMap.get(tempPreNodes[i - 1].index).dx
tempMap.set(minDx, indexMap.get(tempPreNodes[i - 1].index).index);
}
if (tempPreNodes[i + 1] && indexMap.get(tempPreNodes[i + 1].index)) {
maxDx = indexMap.get(tempPreNodes[i + 1].index).dx
tempMap.set(maxDx, indexMap.get(tempPreNodes[i + 1].index).index);
}
let randomDx = minDx;
if (minDx < maxDx) randomDx = getRandValueByMinMax(minDx, maxDx + 1, 0);
tempCurNodes[randomDx].preNodeIndexs.push(tempPreNodes[i].index);
if (indexMap.get(tempPreNodes[i].index) && indexMap.get(tempPreNodes[i].index).dx < randomDx) continue;
indexMap.set(tempPreNodes[i].index, { dx: randomDx, index: tempMap.get(randomDx) });
}
}
// console.log('-x-x--x-x-x-x-x-x-x-x-x- [...retLayer.values()]', util.inspect([...retLayer.values()], { depth: null }));
return [...retLayer.values()];
}
/**
* 选择节点
* @param dbRecord
* @param layerChooseNode
* @returns
*/
export async function chooseNode(dbRecord: RougelikeRecordType, layerChooseNode: layerNode, layer: number) {
const { roleId, gameCode, type, grade, curLayer, authorType } = dbRecord;
const { detailCode, nodeId, } = layerChooseNode
let nodeType = layerChooseNode.type;
const typeGradeData = gameData.rougeTypeGrade.get(type + '_' + grade);
const nodeData = gameData.rougeNode.get(nodeId);
if (!typeGradeData || !nodeData) return;
const layerPlanData = gameData.rougeLayerPlan.get(typeGradeData.layerPlan + '_' + layer);
// console.log("-x--x-x-x-x- nodeData", nodeData)
// console.log("-x--x-x-x-x- typeGradeData", typeGradeData)
// console.log("-x--x-x-x-x- layerPlanData", layerPlanData)
if (!layerPlanData) return;
let isReward = false, isShop = false;
let warId, reward = {} as CommonReward, shops: RougelikeRecordDetailType['shops'] = [],
challenge = {} as RougelikeRecordDetailType['challenge'], question = {} as RougelikeRecordDetail['question'], restPoints: RougelikeRecordDetail['restPoints'] = [];
const dbDetail = await RougelikeRecordDetailModel.findByCode(gameCode, detailCode);
let status = 0;
if (dbDetail) status = dbDetail.status || 0;
let dbPara = { roleId, layer, nodeId, nodeType, status } as RougelikeRecordDetailPara;
//普通关、精英关、boss关
if (nodeType == ROUGE_LIKE_NODE_TYPE.ORDINARY || nodeType == ROUGE_LIKE_NODE_TYPE.ELITE || nodeType == ROUGE_LIKE_NODE_TYPE.BOSS) {
isReward = true;
warId = dbPara.warId = nodeData.param;
}
//挑战关
else if (nodeType == ROUGE_LIKE_NODE_TYPE.CHALLENGE) {
let getChallengeData = getChallenge(typeGradeData.challengePlan);
if (getChallengeData) {
challenge = { challengeId: getChallengeData.challengeId, status: 1, progress: 0 } as RougelikeRecordDetailType['challenge'];
dbPara.challenge = challenge;
}
// isReward = true;
}
//商店
else if (nodeType == ROUGE_LIKE_NODE_TYPE.SHOP) {
isShop = true;
}
//休整点
else if (nodeType == ROUGE_LIKE_NODE_TYPE.REST_POINT) {
isReward = true;
if (dbDetail && dbDetail.restPoints) restPoints = dbDetail.restPoints || restPoints;
else dbPara.restPoints = restPoints;
}
//问号点
else if (nodeType == ROUGE_LIKE_NODE_TYPE.QUEST_POINT) {
if (dbDetail) {
question = dbDetail.question || {} as RougelikeRecordDetail['question'];
warId = dbDetail.warId;
}
else {
const questionMarkPLanData = gameData.rougeQuestionMarkPlan.get(nodeData.param);
if (!questionMarkPLanData) return;
let randomData = {} as DicRougeQuestionMarkPlan;
if (questionMarkPLanData.length == 1) randomData = questionMarkPLanData[0];
else randomData = getRandEelmWithWeight(questionMarkPLanData).dic;
dbPara.questType = randomData.nodeType;
if (randomData.nodeType == ROUGE_LIKE_NODE_TYPE.ORDINARY || randomData.nodeType == ROUGE_LIKE_NODE_TYPE.ELITE) {
isReward = true;
warId = dbPara.warId = 101110101//randomData.param;
}
else if (randomData.nodeType == ROUGE_LIKE_NODE_TYPE.SHOP) {
isShop = true
}
else if (randomData.nodeType == ROUGE_LIKE_NODE_TYPE.EVENT) {
let random = {} as DicRougeRandomEventPlan;
const randomEventPlanData = gameData.rougeRandomEventPlan.get(typeGradeData.randomEventPlan);
if (!randomEventPlanData) return;
if (randomEventPlanData.length == 1) random = randomEventPlanData[0];
else random = getRandEelmWithWeight(randomEventPlanData).dic;
question.randomEventId = random.randomEventId;
question.EventOptions = [];
dbPara.question = question;
}
}
}
if ((!dbDetail || !dbDetail.shops || dbDetail.shops.length == 0) && isShop) {
shops = await getLayerShopReward(roleId, gameCode, authorType, nodeId, layerPlanData.shopPlan);
dbPara.shops = shops;
}
if (dbDetail && dbDetail.shops) shops = dbDetail.shops || shops;
if ((!dbDetail || !dbDetail.rewards || dbDetail.rewards.length == 0) && isReward) {
let result = await getLayerNodeReward(roleId, gameCode, authorType, nodeId, layerPlanData.rewardPlan, layer, dbPara.questType);
if (result) {
reward = result;
dbPara.rewards = result.rewards;
}
}
if (dbDetail && dbDetail.rewards) {
let tempType = (dbDetail?.questType || 0) > 0 ? dbDetail?.questType : nodeType
const layerRewardData = gameData.rougeLayerRewardPlan.get(layerPlanData.rewardPlan + '_' + tempType);
if (!layerRewardData) return;
let { coin, score, tech } = layerRewardData;
reward = { rewards: dbDetail.rewards || [], score: score || 0, techScore: tech || 0, takeoutReward: layerPlanData.takeoutReward || [] };
}
if (!dbDetail) {
// console.log('-x-x--x-x-x-x-x-x-x-x-x- dbPara', util.inspect(dbPara, { depth: null }));
await RougelikeRecordDetailModel.updateByCode(gameCode, detailCode, { $set: dbPara });
await RougelikeRecordModel.updateByGameCode(gameCode, { $set: { curLayer: layer } })
await RougelikeLayerModel.updateByGameCodeAndLayer(gameCode, layer, detailCode, ROUGE_LIKE_CHOOSE_REWARD.CHOOSE)
}
let curNode = { detailCode, nodeId, nodeType, status, warId, reward, shops, challenge, question, restPoints, }
// console.log('-x-x--x-x-x-x-x-x-x-x-x- curNode', util.inspect(curNode, { depth: null }));
return curNode;
}
/**
* 获取当前层当前节点奖励
* @param gameCode
* @param detailCode
* @param nodeId 关卡id
* @param rewardPlan 赠送奖励id
* @returns
*/
export async function getLayerNodeReward(roleId: string, gameCode: string, type: number, nodeId: number, rewardPlan: number, layer: number, nodeType?: number) {
const nodeData = gameData.rougeNode.get(nodeId);
if (!nodeData) return;
if (!nodeType) nodeType = nodeData?.nodeType || 0;
const layerRewardData = gameData.rougeLayerRewardPlan.get(rewardPlan + '_' + nodeType);
if (!layerRewardData) return;
let { charaPlan, charaRandomNum, charaChooseNum,
passiveCardPlan, charaPassivePlan, passiveCardRandomNum, passiveCardChooseNum,
holyCardPlan, holyCardRandomNum, holyCardChooseNum,
coin, score, tech } = layerRewardData;
let dbRougelikeCards = await RougelikeCardModel.findByGameCodeAndType(gameCode, ROUGE_LIKE_CARD_TYPE.PASSIVE);
let rewards: RougelikeRecordDetailType['rewards'] = [];
let charaCards = getCharaCardPlan(charaPlan, charaRandomNum);
if (charaCards && charaCards.length > 0) {
let tempOptions = [], index = 0;
for (let ele of charaCards) {
tempOptions.push({
optionIndex: index++, rewardId: ele.cardId, optionStatus: ROUGE_LIKE_CHOOSE_REWARD.NOCHOOSE,
passiveCardIds: await getSelfPassiveCards(ele.cardId, charaPassivePlan, type, dbRougelikeCards, gameCode, roleId)
})
}
rewards.push({
groupIndex: rewards.length + 1,
rewardType: ROUGE_LIKE_CARD_TYPE.CHARA,
options: tempOptions,
groupStatus: charaChooseNum > 0 ? ROUGE_LIKE_CHOOSE_REWARD.NOCHOOSE : ROUGE_LIKE_CHOOSE_REWARD.CHOOSE,
chooseNum: charaChooseNum,
});
}
let passiveCards = await getPassiveCardPlan(passiveCardPlan, passiveCardRandomNum, type, dbRougelikeCards, gameCode, roleId);
if (passiveCards && passiveCards.length > 0) {
let chooseNum = await getPassiveCardChooseNum(passiveCardChooseNum, passiveCardRandomNum, gameCode, layer, roleId);
rewards.push({
groupIndex: rewards.length + 1,
rewardType: ROUGE_LIKE_CARD_TYPE.PASSIVE,
options: passiveCards.map((ele, index) => { return { optionIndex: index++, rewardId: ele.cardId, optionStatus: ROUGE_LIKE_CHOOSE_REWARD.NOCHOOSE, weightRecord: ele.weightRecord } }),
groupStatus: chooseNum > 0 ? ROUGE_LIKE_CHOOSE_REWARD.NOCHOOSE : ROUGE_LIKE_CHOOSE_REWARD.CHOOSE,
chooseNum
});
}
let holyCards = await getHolyCardPlan(holyCardPlan, holyCardRandomNum, dbRougelikeCards, gameCode, roleId);
if (holyCards && holyCards.length > 0) {
rewards.push({
groupIndex: rewards.length + 1,
rewardType: ROUGE_LIKE_CARD_TYPE.HOLY,
options: holyCards.map((ele, index) => { return { optionIndex: index++, rewardId: ele.cardId, optionStatus: ROUGE_LIKE_CHOOSE_REWARD.NOCHOOSE, weightRecord: ele.weightRecord } }),
groupStatus: holyCardChooseNum > 0 ? ROUGE_LIKE_CHOOSE_REWARD.NOCHOOSE : ROUGE_LIKE_CHOOSE_REWARD.CHOOSE,
chooseNum: holyCardChooseNum,
});
}
// rewards.push({ groupIndex: rewards.length + 1, rewardType: 0, groupStatus: (coin || 0) > 0 ? ROUGE_LIKE_CHOOSE_REWARD.NOCHOOSE : ROUGE_LIKE_CHOOSE_REWARD.CHOOSE, chooseNum: coin || 0 })
return { rewards, score, techScore: tech };
}
// 处理挑战类型中 接下来X次选择特性卡时可选择的卡片数量少1
export async function getPassiveCardChooseNum(passiveCardChooseNum: number, passiveCardRandomNum: number, gameCode: string, layer: number, roleId: string) {
let chooseNum = passiveCardChooseNum;
let dbDetails = await RougelikeRecordDetailModel.findByGameCodeAndLtLayer(gameCode, layer);
if (dbDetails.length == 0) return chooseNum;
for (let { challenge } of dbDetails) {
if (challenge && Object.entries(challenge).length != 0 && challenge.status == 1) {
let { challengeId } = challenge;
const rougeChallengeData = gameData.rougeChallenge.get(challengeId);
if (!rougeChallengeData) return chooseNum;
for (let effectId of (rougeChallengeData.effectId || [])) {
const rougeEffectTypeData = gameData.rougeEffect.get(effectId);
if (rougeEffectTypeData.effectType != ROUGE_EFFECT_TYPE.CHALLENGE_PASSIVE_CARD_REDUCE) continue;
chooseNum -= (rougeEffectTypeData.effectParam[1] || 0);
}
}
}
chooseNum += await getAddChoosePassive(roleId, gameCode);
if (chooseNum < 0) chooseNum = 0;
if (chooseNum > passiveCardRandomNum) chooseNum = passiveCardRandomNum;
return chooseNum;
}
/**
* 获取高级角色卡自带特性
* @param charaId
* @param passiveCardPlan
* @param passiveCardRandomNum
* @param type
* @param dbRougelikeCards
* @returns
*/
export async function getSelfPassiveCards(charaId: number, passiveCardPlan: number, type: number, dbRougelikeCards: RougelikeCardType[], gameCode: string, roleId: string) {
let result: number[] = [];
let charaData = gameData.rougeChara.get(charaId);
if (!charaData) return result;
if (charaData.charaType != ROUGE_CHARA_TYPE.HIGH) return result;
let passiveCards = await getPassiveCardPlan(passiveCardPlan, charaData.initCardCnt, type, dbRougelikeCards, gameCode, roleId);
if (passiveCards && passiveCards.length > 0) result.push(...passiveCards.map((ele) => { return ele.cardId }),);
return result;
}
/**
* 获取当前层当前节点商店数据
* @param gameCode
* @param detailCode
* @param nodeId
* @param shopPlan
* @returns
*/
export async function getLayerShopReward(roleId: string, gameCode: string, type: number, nodeId: number, shopPlan: number) {
let shops: RougelikeRecordDetailType['shops'] = [];
let shopPlanData = gameData.rougeShopPlan.get(shopPlan);
// let nodeData = gameData.rougeNode.get(nodeId);
if (!shopPlanData) return shops;
let dbRougelikeCards = await RougelikeCardModel.findByGameCodeAndType(gameCode, ROUGE_LIKE_CARD_TYPE.PASSIVE);
let passiveCards = await getPassiveCardPlan(shopPlanData.passivecardPlanId, shopPlanData.passiveCardRandomNum, type || 0, dbRougelikeCards, gameCode, roleId);
let index = 0, discount = await getShopDiscount(roleId, gameCode);
if (passiveCards && passiveCards.length > 0) {
for (let ele of passiveCards) {
let price = gameData.rougePassiveCard.get(ele.cardId)?.price || 0
shops.push({
optionIndex: shops.length + index,
rewardType: ROUGE_LIKE_CARD_TYPE.PASSIVE,
rewardId: ele.cardId,
optionStatus: ROUGE_LIKE_CHOOSE_REWARD.NOCHOOSE,
price,
discountPrice: Math.floor(price * discount / 100),
})
index++;
}
}
let holyCards = await getHolyCardPlan(shopPlanData?.holyCardPlanId, shopPlanData?.holyCardRandomNum, dbRougelikeCards, gameCode, roleId);
if (holyCards && holyCards.length > 0) {
for (let ele of holyCards) {
let price = gameData.rougeHolyCard.get(ele.cardId)?.purchasePrice || 0;
shops.push({
optionIndex: shops.length + index,
rewardType: ROUGE_LIKE_CARD_TYPE.HOLY,
rewardId: ele.cardId, optionStatus: ROUGE_LIKE_CHOOSE_REWARD.NOCHOOSE,
price,
discountPrice: Math.floor(price * discount / 100),
})
index++;
}
}
return shops;
}
/**
* 检测配置数据是否满足随机数量
* @param planId
* @param randomNum
* @returns
*/
export function checkRandomLimit(planId: number, randomNum: number, rewardType: number) {
let cards: DicRougeCharaCardPlan[] = [];
if (!planId || planId == 0 || !randomNum || randomNum == 0) return cards;
let cardPlanDatas = gameData.rougeCharaCardPlan.get(planId);
if (rewardType == ROUGE_LIKE_CARD_TYPE.PASSIVE) cardPlanDatas = gameData.rougePassiveCardPlan.get(planId);
else if (rewardType == ROUGE_LIKE_CARD_TYPE.HOLY) cardPlanDatas = gameData.rougeHolyCardPlan.get(planId);
if (!cardPlanDatas) return cards;
else if (cardPlanDatas.length < randomNum) {
console.error("checkRandomLimit可随机的角色卡数量少于需要数量, planId=%s, randomNum=%s", planId, randomNum);
return cardPlanDatas;
}
else if (cardPlanDatas.length >= randomNum) return cardPlanDatas;
return cards;
}
/**
* 获取角色卡随机
* @param planId
* @param charaRandomNum
* @returns
*/
export function getCharaCardPlan(planId: number, charaRandomNum: number) {
let cards = checkRandomLimit(planId, charaRandomNum, ROUGE_LIKE_CARD_TYPE.CHARA);
if (cards.length <= charaRandomNum) return cards;
let randResult = getRandEelmWithWeightAndNum(cards, charaRandomNum);
return randResult.map(cur => cur.dic);
}
/**
* 获取特性卡随机
* @param passiveCardPlan
* @param passiveCardRandomNum
*/
export async function getPassiveCardPlan(passiveCardPlan: number, passiveCardRandomNum: number, type: number, dbRougelikeCards: RougelikeCardType[], gameCode: string, roleId: string) {
let cards = checkRandomLimit(passiveCardPlan, passiveCardRandomNum, ROUGE_LIKE_CARD_TYPE.PASSIVE);
if (cards.length <= passiveCardRandomNum) return cards;
// 计算变化权重
let lableMap = new Map<number, number>(); //统计lable数量
if (dbRougelikeCards && dbRougelikeCards.length > ROUGELIKE.PASSIVE_LABLE_NUM) {
for (let { cardId } of dbRougelikeCards) {
if (!cardId) continue;
let passiveCardData = gameData.rougePassiveCard.get(cardId);
if (!passiveCardData || !passiveCardData.passiveLabel || passiveCardData.passiveLabel.length == 0) continue;
for (let val of passiveCardData.passiveLabel) {
if (!lableMap.get(val)) {
lableMap.set(val, 1);
continue;
}
lableMap.set(val, lableMap.get(val) + 1);
}
}
}
let newCards = [];
let cardsMap = await getCardCount(gameCode, ROUGE_LIKE_CARD_TYPE.PASSIVE);
const { chooseCardsMap, noChooseCardsMap } = await getIsChooseCard(gameCode);
for (let obj of cards) {
let weightRecord: { originalWight?: number, passiveRedWight?: number, passiveLableNum?: number, authorAddWeight?: number, passiveLableNumAddWeight?: number, finalWeight?: number } = {};
if (!obj) continue;
let { cardId, weight } = obj;
if (!cardId || !weight) continue;
let passiveCardData = gameData.rougePassiveCard.get(cardId);
if (!passiveCardData) continue;
const getLimit = cardsMap.get(cardId) || 0;
if (getLimit >= (passiveCardData?.getLimit || 0)) continue; //处理限制获取数量
weightRecord.originalWight = weight;
if (chooseCardsMap.has(cardId)) {
weight = Math.floor(weight * (1 - ROUGELIKE.SELECT_PASSIVECARD_WEIGHT / 100));
weightRecord.passiveRedWight = Math.floor(weight * ROUGELIKE.SELECT_PASSIVECARD_WEIGHT / 100);
}
else if (noChooseCardsMap.has(cardId)) {
weight = Math.floor(weight * (1 - ROUGELIKE.RANDOM_PASSIVECARD_WEIGHT / 100));
weightRecord.passiveRedWight = Math.floor(weight * ROUGELIKE.RANDOM_PASSIVECARD_WEIGHT / 100);
}
weight += await getAddPassiveWeight(roleId, gameCode, type);
if ((passiveCardData?.authorType || 0) == type) weight += ROUGELIKE.AUTHOR_ADD_RANDOM;
weightRecord.authorAddWeight = ROUGELIKE.AUTHOR_ADD_RANDOM;
let labelNum = lableMap.get(cardId) || 0;
if (labelNum >= ROUGELIKE.PASSIVE_LABLE_NUM) {
weight += ROUGELIKE.PASSIVE_LABLE_ADD_RANDOM * (Math.ceil(labelNum / ROUGELIKE.PASSIVE_LABLE_NUM));
weightRecord.passiveLableNum = labelNum;
weightRecord.passiveLableNumAddWeight = ROUGELIKE.PASSIVE_LABLE_ADD_RANDOM * (Math.ceil(labelNum / ROUGELIKE.PASSIVE_LABLE_NUM));
}
// if (!passiveCardData.passiveLabel || passiveCardData.passiveLabel.length == 0) continue;
// for (let val of passiveCardData.passiveLabel) {
// let labelNum = lableMap.get(val) || 0;
// if (labelNum < ROUGELIKE.PASSIVE_LABLE_NUM || ROUGELIKE.PASSIVE_LABLE_NUM == 0) continue;
// weight += ROUGELIKE.PASSIVE_LABLE_ADD_RANDOM * (Math.ceil(labelNum / ROUGELIKE.PASSIVE_LABLE_NUM));
// }
weightRecord.finalWeight = weight;
newCards.push({ ...obj, weight, weightRecord });
}
let targetPassives = await getChooseQualityPassives(roleId, gameCode, newCards);
let result = [];
if (passiveCardRandomNum >= 1 && targetPassives.length > 0) result.push(getRandEelmWithWeight(targetPassives).dic);
let randResult = getRandEelmWithWeightAndNum(newCards, passiveCardRandomNum - result.length);
return [...result, ...randResult.map(cur => cur.dic)]
}
export async function getIsChooseCard(gameCode: string) {
const dbDetails = await RougelikeRecordDetailModel.findByGameCode(gameCode);
let chooseCardsMap = new Map<number, number>();
let noChooseCardsMap = new Map<number, number>();
for (const { rewards } of dbDetails) {
if (!rewards || rewards.length == 0) continue;
for (const { options } of rewards) {
if (!options || options.length == 0) continue;
for (const { rewardId, optionStatus } of options) {
if (optionStatus != 0) {
chooseCardsMap.set(rewardId, rewardId);
continue;
}
noChooseCardsMap.set(rewardId, rewardId);
}
}
}
return { chooseCardsMap, noChooseCardsMap };
}
export async function getCardCount(gameCode: string, type: number) {
const dbCards: RougelikeCardType[] = await RougelikeCardModel.findByGameCodeAndType(gameCode, type);
let cardsMap = new Map<number, number>();
dbCards.forEach((cur) => { cardsMap.set(cur.cardId, (cardsMap.get(cur.cardId) || 0) + 1); })
return cardsMap;
}
/**
* 获取圣物随机
* @param planId
* @param holyCardPlan
*/
export async function getHolyCardPlan(holyCardPlan: number, holyCardRandomNum: number, dbRougelikeCards: RougelikeCardType[], gameCode: string, roleId: string) {
let cards = checkRandomLimit(holyCardPlan, holyCardRandomNum, ROUGE_LIKE_CARD_TYPE.HOLY);
if (cards.length <= holyCardRandomNum) return cards;
let lableMap = new Map<number, number>();//统计lable数量
if (dbRougelikeCards && dbRougelikeCards.length > ROUGELIKE.HOLY_LABLE_NUM) {
for (let { cardId } of dbRougelikeCards) {
if (!cardId) continue;
let passiveCardData = gameData.rougePassiveCard.get(cardId);
if (!passiveCardData || !passiveCardData.holyLabel || passiveCardData.holyLabel.length == 0) continue;
for (let val of passiveCardData.holyLabel) {
if (!lableMap.get(val)) {
lableMap.set(val, 1);
continue;
}
lableMap.set(val, lableMap.get(val) + 1);
}
}
}
// 计算变化权重
let newCards = [];
let cardsMap = await getCardCount(gameCode, ROUGE_LIKE_CARD_TYPE.HOLY);
const { chooseCardsMap, noChooseCardsMap } = await getIsChooseCard(gameCode);
for (let obj of cards) {
let weightRecord: {
originalWight?: number, passiveRedWight?: number, holyRedWight?: number, authorAddWeight?: number,
passiveLableNum?: number, passiveLableNumAddWeight?: number, holyLableNum?: number, holyLableNumAddWeight?: number,
finalWeight?: number
} = {};
if (!obj) continue;
let { cardId, weight } = obj;
if (!cardId || !weight) continue;
let holyCardData = gameData.rougeHolyCard.get(cardId);
if (!holyCardData) continue;
const getLimit = cardsMap.get(cardId) || 0;
if (getLimit >= (holyCardData?.getLimit || 0)) continue; //处理限制获取数量
weightRecord.originalWight = weight;
if (chooseCardsMap.has(cardId)) {
weight = Math.floor(weight * (1 - ROUGELIKE.SELECT_HOLLYCARD_WEIGHT / 100));
weightRecord.holyRedWight = Math.floor(weight * ROUGELIKE.SELECT_HOLLYCARD_WEIGHT / 100);
}
else if (noChooseCardsMap.has(cardId)) {
weight = Math.floor(weight * (1 - ROUGELIKE.RANDOM_HOLLYCARD_WEIGHT / 100));
weightRecord.holyRedWight = Math.floor(weight * ROUGELIKE.RANDOM_HOLLYCARD_WEIGHT / 100);
}
if (!holyCardData.label) {
newCards.push({ ...obj });
continue;
};
let labelNum = lableMap.get(holyCardData.label) || 0;
if (labelNum < ROUGELIKE.HOLY_LABLE_NUM || ROUGELIKE.HOLY_LABLE_NUM == 0) {
newCards.push({ ...obj });
continue;
};
weight += ROUGELIKE.HOLY_LABLE_ADD_RANDOM * (Math.ceil(labelNum / ROUGELIKE.HOLY_LABLE_NUM));
weightRecord.holyLableNum = labelNum;
weightRecord.holyLableNumAddWeight = ROUGELIKE.HOLY_LABLE_ADD_RANDOM * (Math.ceil(labelNum / ROUGELIKE.HOLY_LABLE_NUM));
weightRecord.finalWeight = weight;
newCards.push({ ...obj, weight, weightRecord });
}
let randResult = getRandEelmWithWeightAndNum(newCards, holyCardRandomNum);
return randResult.map(cur => cur.dic);
}
/**
* 获取挑战关卡数据
* @param planId
* @returns
*/
export function getChallenge(planId: number) {
const randomChallenge = getChallengePlan(planId);
if (!randomChallenge) return;
const rougeChallengeData = gameData.rougeChallenge.get(randomChallenge.challengeId);
if (!rougeChallengeData) return;
return rougeChallengeData;
}
/**
* 获取挑战关随机
* @param planId
* @returns
*/
export function getChallengePlan(planId: number) {
const challengePlanData = gameData.rougeChallengePlan.get(planId);
if (!challengePlanData) return;
if (challengePlanData.length == 1) return challengePlanData[0];
return getRandEelmWithWeight(challengePlanData).dic;
}
export async function updateChalleng(dbRecord: RougelikeRecordType, roleId: string, sid: string, gameCode: string, curLayer: number, rougeDamage, isAp?: boolean, isRound?: boolean) {
let len = rougeDamage.length;
const minHp = rougeDamage.reduce((min, cur) => { return Math.min(min, cur.hp); }, Infinity);
let challenges: { challengeId: number, status: number, progress: number, detailCode: string }[] = [];
const { authorType, type, grade } = dbRecord;
let dbDetails = await RougelikeRecordDetailModel.findByGameCodeAndLtLayer(gameCode, curLayer);
if (dbDetails.length == 0) return true;
let updateChallengs: RougelikeRecordDetailPara[] = [];
for (let { detailCode, challenge, rewards = [], nodeId, layer } of dbDetails) {
if (challenge && Object.entries(challenge).length != 0 && challenge.status == 1) {
let { challengeId } = challenge;
const rougeChallengeData = gameData.rougeChallenge.get(challengeId);
if (!rougeChallengeData) return true;
for (let effectId of (rougeChallengeData.effectId || [])) {
const rougeEffectTypeData = gameData.rougeEffect.get(effectId);
if (len == 0) {
//接下来X次选择特性卡时可选择的卡片数量少1
if (rougeEffectTypeData.effectType != ROUGE_EFFECT_TYPE.CHALLENGE_PASSIVE_CARD_REDUCE) continue;
} else {
if (rougeEffectTypeData.effectType == ROUGE_EFFECT_TYPE.CHALLENGE_PASSIVE_CARD_REDUCE) continue;
if (rougeEffectTypeData.effectType == ROUGE_EFFECT_TYPE.CHALLENGE_CHARA_NO_AP_SKILL && isAp) continue;
if (rougeEffectTypeData.effectType == ROUGE_EFFECT_TYPE.CHALLENGE_CHARA_NO_ROUND_SKILL && isRound) continue;
if (rougeEffectTypeData.effectType == ROUGE_EFFECT_TYPE.CHALLENGE_CHARA_HP_LIMIT && (rougeEffectTypeData.effectParam[1] || 0) > minHp) continue;
if (rougeEffectTypeData.effectType == ROUGE_EFFECT_TYPE.CHALLENGE_CHARA_NUM_LIMIT && (rougeEffectTypeData.effectParam[1] || 0) != len) continue;//接下来X场战斗每场战斗只能上阵2名学员
}
challenge.progress += 1;
if (challenge.progress == rougeChallengeData.condition) {
challenge.status = 2;
//处理在挑战进度完成时再随机奖励
const typeGradeData = gameData.rougeTypeGrade.get(type + '_' + grade);
if (!typeGradeData) continue;
const layerPlanData = gameData.rougeLayerPlan.get(typeGradeData.layerPlan + '_' + layer);
if (!layerPlanData) continue;
let result = await getLayerNodeReward(roleId, gameCode, authorType, nodeId, layerPlanData.rewardPlan, layer);
rewards = result.rewards;
};
}
challenges.push({ challengeId, status: challenge.status, progress: challenge.progress, detailCode });
updateChallengs.push({ gameCode, detailCode, challenge, status: 1, rewards });
}
}
await RougelikeRecordDetailModel.bulkWriteUpdate(updateChallengs);
await sendMessageToUserWithSuc(roleId, PUSH_ROUTE.ROUGE_CHALLENGE_UPDATE, { challenges }, sid);
return true;
}
export function getLayerRewardOneData(type: number, grade: number, layer: number, nodeType: number) {
let result: { takeoutReward?: RewardInter[], coin?: number, score?: number, tech?: number, spiritPlan?: number } = {};
const typeGradeData = gameData.rougeTypeGrade.get(type + '_' + grade);
if (!typeGradeData) return result;
const layerPlanData = gameData.rougeLayerPlan.get(typeGradeData.layerPlan + '_' + layer);
if (!layerPlanData) return result;
const layerRewardData = gameData.rougeLayerRewardPlan.get(layerPlanData.rewardPlan + '_' + nodeType);
if (!layerRewardData) return result;
return { ...layerRewardData, takeoutReward: layerPlanData.takeoutReward, spiritPlan: layerPlanData.spiritPlan };
}
export function getRandomSpirit(spiritPlan: number) {
let spiritId: number[] = [];
const spiritPlanData = gameData.spiritPlan.get(spiritPlan);
if (ROUGELIKE.SPIRIT_RANDOM_NUM == 0 || !spiritPlanData || spiritPlanData.length == 0) return spiritId;
let random = getRandEelmWithWeightAndNum(spiritPlanData, ROUGELIKE.SPIRIT_RANDOM_NUM);
spiritId = random.map(cur => { return cur.dic.spiritId });
return spiritId;
}
/**
* 获取
* @param authorType
* @param cards
* @returns
*/
export function getAuthorTypeCardNum(authorType: number, cards: Card[]) {
return cards.filter(card => {
let dicCard = card.cardId == 0 ? null : gameData.rougePassiveCard.get(card.cardId);
if (!dicCard) return false;
return dicCard.authorType == authorType;
}).length;
}
export async function repaireSendScoreReward() {
let maxNum = gameData.rougeScoreNum.num || 0;
let refTime = getZeroPointOfTimeD(Date.now() - 86400000, SHOP_REFRESH_TYPE.WEEKLY);
let allRewards = await RougelikeScoreModel.findByReceiveNum(refTime, maxNum);
let _ids: string[] = [];
for (let { roleId, received, _id, score } of allRewards) {
let goods: RewardInter[] = [];
for (let [index, { reward, score: targetScore }] of gameData.rougeScoreReward) {
if (score >= targetScore && !received.includes(index)) goods.push(...reward);
}
await sendMailByContent(MAIL_TYPE.ROUGE_SCORE_REPAIRE, roleId, { goods });
_ids.push(_id);
}
await RougelikeScoreModel.receiveAll(_ids, maxNum + 1);
}
/**
* 获取最大血量
* @param charaId
* @param type
* @param grade
* @returns
*/
export async function getMaxHp(roleId: string, gameCode: string, charaId: number, type: number, grade: number,) {
let maxHp = 0;
const charaData = gameData.rougeChara.get(charaId);
if (!charaData || !charaData.heroId) return maxHp;
const heroData = gameData.hero.get(charaData.heroId);
// console.log("x-x-x-x--x-xx- heroData", heroData);
if (!heroData || !heroData.hp) return maxHp;
const typeGradeData = gameData.rougeTypeGrade.get(type + '_' + grade);
// console.log("x-x-x-x--x-xx- typeGradeData", typeGradeData);
if (!typeGradeData || !typeGradeData.heroValue) return maxHp;
let rougeEffect = new RougeEffect(roleId, gameCode);
const holyMaxHp = await rougeEffect.getEffectMaxHp();
maxHp = heroData.hp * typeGradeData.heroValue / 10000 * (1 + holyMaxHp / 100);
return Math.floor(maxHp);
}
export async function updateMaxHp(roleId: string, gameCode: string, type: number, grade: number) {
let dbCharas = await RougelikeCharaModel.findByGameCode(gameCode);
if (dbCharas.length == 0) return [];
let result: RougelikeCharaType[] = [];
for (let val of dbCharas) {
let tempMaxHp = await getMaxHp(roleId, gameCode, val.charaId, type, grade);
if (tempMaxHp == val.maxHp) continue;
val.maxHp = tempMaxHp;
result.push(val);
}
await RougelikeCharaModel.bulkWriteUpdate(result);
let { charas } = formateCharasOrCards(result, ROUGE_LIKE_CARD_TYPE.CHARA)
return charas || [];
}
// 获取当前层之前所有未完成挑战关
export async function getPreCurLayerChallengs(gameCode: string, layer: number) {
let challenges: { challengeId: number, status: number, progress: number, detailCode: string, reward: CommonReward }[] = [];
let dbDetails = await RougelikeRecordDetailModel.findByGameCodeAndLtLayer(gameCode, layer);
if (dbDetails.length == 0) return challenges;
for (let { detailCode, challenge, rewards } of dbDetails) {
if (!challenge) continue;
const { challengeId, status, progress } = challenge;
if (status == 3) continue;
challenges.push({ challengeId, status, progress, detailCode, reward: { rewards } });
}
return challenges;
}
export async function getGame(roleId: string) {
let isPlaying = true, nodes: RougelikeLayerType[] = [], hasPass = false, curNode = {};
const dbRecord = await RougelikeRecordModel.findByRoleIdAndStatus(roleId, ROUGE_LIKE_STATUS.SUCCESS);
if (!dbRecord) {
isPlaying = false;
return { isPlaying };
}
const { gameCode, grade, type, authorType = 0, curLayer = 0, maxLayer = 0, coin = 0, score = 0, techScore = 0, coinTotal = 0 } = dbRecord;
const dbNodes = await RougelikeLayerModel.findByGameCode(gameCode);
let dbCurLayerChooseNode = {} as layerNode;
if (dbNodes) nodes = dbNodes.map((obj) => {
const { layer, layerNodes, hasPass: dbHasPass = false } = obj;
if (layer == curLayer) {
hasPass = dbHasPass;
dbCurLayerChooseNode = layerNodes.find(cur => cur.isChoose == ROUGE_LIKE_CHOOSE_REWARD.CHOOSE);
}
return { layer, layerNodes } as RougelikeLayerType
})
const charas: CommonChara[] = formateCharasOrCards(await RougelikeCharaModel.findByGameCode(gameCode), ROUGE_LIKE_CARD_TYPE.CHARA)?.charas || [];
const cards: CommonCard[] = formateCharasOrCards(await RougelikeCardModel.findByGameCode(gameCode), ROUGE_LIKE_CARD_TYPE.PASSIVE | ROUGE_LIKE_CARD_TYPE.HOLY)?.cards || [];
console.log("x-x-x-x-x-x-x-x- dbCurLayerChooseNode", dbCurLayerChooseNode)
if (Object.entries(dbCurLayerChooseNode).length != 0) curNode = await chooseNode(dbRecord, dbCurLayerChooseNode, curLayer)
return {
isPlaying, gameCode, grade, type, authorType,
curLayer, hasPass, maxLayer, coin, coinTotal, score, techScore, nodes: nodes || [], charas, cards, curNode,
preChallengs: await getPreCurLayerChallengs(gameCode, curLayer)
};
}

View File

@@ -0,0 +1,121 @@
import { Circle, RougelikeTechModel, RougelikeTechType } from '../../db/RougelikeTech';
import { gameData } from '../../pubUtils/data';
import { HeroModel } from '../../db/Hero';
import { compareNumberArray } from '../../pubUtils/util';
/**
* 首页获得的科技树数据
* @param techData
* @returns
*/
export async function getTechData(roleId: string) {
let techData = await RougelikeTechModel.findByRoleId(roleId);
if(!techData) return { techTrees: [], sumTechScore: 0 };
let { unlockedTech = [], circles = [] } = techData;
circles = await calCircleCe(roleId, unlockedTech, circles);
let techTrees = unlockedTech.map(techId => getSingleTechData(techId, circles));
return {
techTrees
}
}
/**
* 从首页重新进入,重新计算他的战力
* @param roleId
* @param circles
* @returns
*/
async function calCircleCe(roleId: string, unlockedTech: number[], circles: Circle[]) {
let hids: number[] = circles.map(circle => circle.hid).filter(hid => hid > 0);
if(hids.length <= 0) return circles
let heroes = await HeroModel.findByHidRange(hids, roleId);
let newCircle = circles.map(circle => {
if(circle.hid == 0) return circle;
let hero = heroes.find(cur => cur.hid == circle.hid);
return { ...circle, ce: hero?.ce||0 }
});
let effectIds = getTechEffectIds(unlockedTech, circles)
await RougelikeTechModel.updateCircle(roleId, newCircle, effectIds);
return newCircle;
}
export async function updateEffectId(techData: RougelikeTechType) {
let effectIds = getTechEffectIdsByData(techData);
if(!compareNumberArray(effectIds, techData.effectIds||[])) {
await RougelikeTechModel.updateEffectId(techData.roleId, effectIds);
}
}
/**
* 处理后单个科技点的数据
* @param techData
* @param techId
* @returns
*/
export function getCurTechData(techData: RougelikeTechType, techId: number) {
if(!techData || !techId) return { curTechTree: { techId, circles: [] } }
let { circles = [] } = techData;
return {
curTechTree: getSingleTechData(techId, circles)
}
}
/**
* 获取单片科技树数据
* @param techId
* @param circles
* @returns
*/
function getSingleTechData(techId: number, circles: Circle[]) {
let result: { circleId: number, hid: number, ce: number }[] = [];
for(let { techId: curTechId, hid, circleId, ce } of circles) {
if(curTechId == techId) result.push({ hid, circleId, ce });
}
return { techId, circles: result };
}
/**
* 检查前置科技点是否解锁
* @param techId
* @param unlockedTech
* @returns
*/
export function checkPreRougeTech(techId: number, unlockedTech: number[]) {
let dicTech = gameData.rougeTech.get(techId);
let preTechId = dicTech?.preTechId||[];
for(let techId of preTechId) {
if(!unlockedTech.includes(techId)) return false;
}
return true;
}
/**
* 获取科技树带来的type加成
* @param roleId
* @returns [{ type: number, param: number[] }]
*/
export function getTechEffectIdsByData(techData: RougelikeTechType) {
if(!techData) return [];
return getTechEffectIds(techData.unlockedTech, techData.circles);
}
export function getTechEffectIds(unlockedTech: number[], circles: Circle[]) {
let result: number[] = [];
let techMap = new Map<number, number>();
for(let techId of unlockedTech) {
techMap.set(techId, 0);
}
for(let { techId, ce } of circles) {
let oldCe = techMap.get(techId)||0;
techMap.set(techId, oldCe + ce);
}
for(let [techId, ce] of techMap) {
let levelDatas = gameData.rougeTechLevel.get(techId)||[];
let effectIds: number[] = [];
for(let { techEffectIds, ce: targetCe } of levelDatas) {
if(ce >= targetCe) effectIds = techEffectIds;
}
result.push(...effectIds);
}
return result;
}

View File

@@ -866,6 +866,167 @@ export function checkRouteParam(route: string, msg: any) {
if (!checkNaturalNumbers(msg.id)) return false;
break;
}
case "battle.rougeHandler.getData":
break;
case "battle.rougeHandler.getGame":
break;
case "battle.rougeHandler.getInitCharaCard":
{
let { type, grade } = msg
if (!checkNaturalNumbers(type, grade)) return false;
break;
}
case "battle.rougeHandler.startGame":
{
let { gameCode, authorType } = msg;
if (!checkNaturalStrings(gameCode)) return false;
if (!checkNaturalNumbers(authorType)) return false;
break;
}
case "battle.rougeHandler.gameEnd":
{
let { gameCode } = msg;
if (!checkNaturalStrings(gameCode)) return false;
break;
}
case "battle.rougeHandler.chooseNode":
{
let { gameCode, layer, detailCode } = msg;
if (!checkNaturalStrings(gameCode, detailCode)) return false;
if (!checkNaturalNumbers(layer)) return false;
break;
}
case "battle.rougeHandler.checkBattle":
{
let { gameCode, detailCode, warId, charaCodes } = msg;
if (!checkNaturalStrings(gameCode, detailCode)) return false;
if (!checkNaturalNumbers(warId)) return false;
if (!checkIsDuplicateStrings(charaCodes)) return false;
break;
}
case "battle.rougeHandler.battleEnd":
{
let { gameCode, detailCode, battleCode, warId, status, round, rougeDamage, isAp, isRound } = msg;
if (!checkNaturalStrings(gameCode, detailCode, battleCode)) return false;
if (!checkNaturalNumbers(warId, round, status)) return false;
if (!checkBooleanIfExist(isAp, isRound)) return false;
if (!isArray(rougeDamage) || rougeDamage.length == 0) return false;
let charaCodes: string[] = [];
for (let { charaCode } of rougeDamage) {
if (!charaCode) return false;
charaCodes.push(charaCode);
}
if (!checkIsDuplicateStrings(charaCodes)) return false;
break;
}
case "battle.rougeHandler.chooseReward":
{
let { gameCode, detailCode, groupIndex, optionIndexs } = msg;
if (!checkNaturalStrings(gameCode, detailCode)) return false;
if (!checkNaturalNumbers(groupIndex)) return false;
if (!checkIsDuplicateNumbers(optionIndexs)) return false;
break;
}
case "battle.rougeHandler.reRandomReward":
{
let { gameCode, detailCode, rewardType } = msg;
if (!checkNaturalStrings(gameCode, detailCode)) return false;
if (!checkNaturalNumbers(rewardType)) return false;
break;
}
case "battle.rougeHandler.shopBuy":
{
let { gameCode, detailCode, optionIndex } = msg;
if (!checkNaturalStrings(gameCode, detailCode)) return false;
if (!checkNaturalNumbers(optionIndex)) return false;
break;
}
case "battle.rougeHandler.chooseOption":
{
let { gameCode, detailCode, eventOptions } = msg;
if (!checkNaturalStrings(gameCode, detailCode)) return false;
if (!checkIsDuplicateNumbers(eventOptions)) return false;
break;
}
case "battle.rougeHandler.recovery":
{
let { gameCode, detailCode } = msg;
if (!checkNaturalStrings(gameCode, detailCode)) return false;
break;
}
case "battle.rougeHandler.trainCard":
{
let { gameCode, detailCode, cardCode } = msg;
if (!checkNaturalStrings(gameCode, detailCode, cardCode)) return false;
break;
}
case "battle.rougeHandler.nodeEnd":
{
let { gameCode, detailCode, } = msg;
if (!checkNaturalStrings(gameCode, detailCode)) return false;
break;
}
case "battle.rougeHandler.putOnOrOffCard":
{
let { gameCode, charaCode, cards } = msg;
if (!checkNaturalStrings(gameCode, charaCode)) return false;
let cardCodes: string[] = [], indexs: number[] = [];
for (let { index, cardCode } of cards) {
indexs.push(index);
if (cardCode) cardCodes.push(cardCode);
}
if (!checkIsDuplicateNumbers(indexs)) return false;
if (!checkIsDuplicateStrings(cardCodes)) return false;
break;
}
case "battle.rougeHandler.exchangeChara":
{
let { gameCode, oldCharaCode, newCharaCode } = msg;
if (!checkNaturalStrings(gameCode, oldCharaCode, newCharaCode)) return false;
break;
}
case "battle.rougeHandler.unlockTech":
{
if (!checkNaturalNumbers(msg.techId)) return false;
break;
}
case "battle.rougeHandler.putOnCircle":
{
if (!checkNaturalNumbers(msg.circleId, msg.hid)) return false;
break;
}
case "battle.rougeHandler.chooseSkillCard":
{
if (!checkNaturalStrings(msg.gameCode, msg.charaCode)) return false;
if (!checkNaturalNumbers(msg.skillType, msg.id)) return false;
break;
}
case "battle.rougeHandler.receiveCollectionReward":
{
if (!checkNaturalNumbers(msg.type, msg.id)) return false;
break;
}
case "battle.rougeHandler.receiveScore":
{
if (!checkNaturalNumbers(msg.index)) return false;
break;
}
case "chat.chatHandler.sendGroupMessage":
{
let { channel, type, content, targetRoleId, targetMsgCode } = msg;
@@ -2223,11 +2384,17 @@ export function checkRouteParam(route: string, msg: any) {
case "activity.bindPhoneHandler.debugSetGiftCodeStatus":
case 'activity.rebateHandler.debugSetRebate':
case 'activity.newHeroGKHandler.debugResetGK':
case 'battle.rougeHandler.debugClearTech':
case "battle.rougeHandler.debugAddCard":
case "battle.rougeHandler.debugAddScore":
case "battle.rougeHandler.debugRepaireScoreReward":
case "battle.rougeHandler.debugAddCollection":
{
if (msg.magicWord !== DEBUG_MAGIC_WORD || !isDevelopEnv()) return false;
break;
}
case "battle.rougeHandler.debugAddLimitId":
case "comBattle.comBattleHandler.getTeams":
case "comBattle.comBattleHandler.teammateReady":
case "comBattle.comBattleHandler.battleEnd":
@@ -2363,4 +2530,28 @@ function checkCombo(combo: { groupId: number, levelList: number[] }[]) {
}
}
return true;
}
function checkIsDuplicateNumbers(array: number[]) {
const seen = new Set();
for (const item of array) {
if (!isNumber(item)) return false;
if (seen.has(item)) {
return false; // 发现重复元素
}
seen.add(item);
}
return true; // 没有重复元素
}
function checkIsDuplicateStrings(array: string[]) {
const seen = new Set();
for (const item of array) {
if (!isString(item)) return false;
if (seen.has(item)) {
return false; // 发现重复元素
}
seen.add(item);
}
return true; // 没有重复元素
}

View File

@@ -54,6 +54,7 @@ import { LinkModel } from '../db/Link';
import { getHiddenData } from './memoryCache/hiddenData';
import { AuthorBookModel } from '../db/AuthorBook';
import { gameData } from '../pubUtils/data';
import { getRougeData } from './battle/rougeService';
/**
* init: 初始的时候是否推送 true-推 false-不推
@@ -89,6 +90,7 @@ const modules = [
{ id: 26, type: 'survey', init: false, refresh: true, guild: false },
{ id: 27, type: 'ladder', init: false, refresh: true, guild: false },
{ id: 28, type: 'hiddenData', init: true, refresh: true, guild: false },
{ id: 29, type: 'rouge', init: true, refresh: true, guild: false },
]
export async function pushData(hasInit: boolean, role: RoleType, session: FrontendOrBackendSession, pushType: 'entry' | 'refresh' = 'entry') {
@@ -229,6 +231,8 @@ export async function getModuleData(type: string, data: { role: RoleType, sessio
return await getLadderData(role.roleId, false);
case 'hiddenData':
return getHiddenData();
case 'rouge':
return await getRougeData(role.roleId);
default:
return null;
}

View File

@@ -31,6 +31,7 @@ import { pick } from "underscore";
import { memberJoinGuildToLeague } from "./gvg/gvgTeamService";
import { isToday } from '../pubUtils/timeUtil';
import moment = require("moment");
import { repaireSendScoreReward } from "./battle/rougeService";
export async function getMyGuildInfo(roleId: string, sid: string, userGuild: UserGuildType, guild: GuildType, serverId: number, session: FrontendOrBackendSession) {
@@ -433,6 +434,10 @@ export async function settleGuildWeekly() {
await initSingleRank(REDIS_KEY.GUILD_ACTIVE_RANK);
await initSingleRank(REDIS_KEY.GUILD_LV_RANK);
let curSeasonNum = await CounterModel.getCounter(COUNTER.PVP_SEASON_NUM);
// 发送肉鸽奖励
await repaireSendScoreReward();
console.log('————— settleGuildWeekly结束 —————');
}

View File

@@ -622,6 +622,10 @@ export function checkVoucherId(id: number) {
return id == voucher || id == voucherCoin;
}
export function getRougeTechScoreObject(count: number) {
return { id: CURRENCY_BY_TYPE.get(CURRENCY_TYPE.ROUGE_TECH)||0, count };
}
/**
* 返回 解锁头像/相框
* @param conditions 解锁条件

View File

@@ -0,0 +1,178 @@
import { Client } from './Client';
import 'mocha';
import { PinusWSClient } from 'pinus-robot-plugin';
import { expect } from 'chai';
import { checkBattleGoods, checkDisplayItems, checkSuccessResponse, checkTimeStamp, checkWarJson } from './CheckPatten';
import { DEBUG_MAGIC_WORD } from '../app/consts';
import { getRandSingleEelm } from './pureUtil';
import * as util from 'util';
const NORMAIL_BATTLEID = 101;
const DUNGEON_BATTLEID = 5001;
describe('稷下学宫测试', function () {
let pinusClient: PinusWSClient;
let roleInfo;
before(function (done) {
const c = new Client();
const timer = setInterval(() => {
if (c.client) {
pinusClient = c.client;
roleInfo = c.roleInfo;
clearInterval(timer);
done();
}
}, 500);
});
after(function (done) {
pinusClient.disconnect();
// disconnect 后等待 500ms供服务器清理环境、退出频道等
setTimeout(() => {
done();
}, 500);
});
// it('getData', function (done) {
// pinusClient.request('battle.rougeHandler.getData', {}, (res) => {
// console.log("res:", res)
// done();
// });
// });
// it('getGame', function (done) {
// pinusClient.request('battle.rougeHandler.getGame', {}, (res) => {
// console.log('-x-x--x-x-x-x-x-x-x-x-x- res', util.inspect(res, { depth: null }));
// done();
// });
// });
it('getInitCharaCard', function (done) {
pinusClient.request('battle.rougeHandler.getInitCharaCard', { type: 2, grade: 1 }, (res) => {
console.log('-x-x--x-x-x-x-x-x-x-x-x- res', util.inspect(res, { depth: null }));
done();
});
});
// it('startGame', function (done) {
// pinusClient.request('battle.rougeHandler.startGame', { gameCode: 'MkSLT1HY', authorType: 1 }, (res) => {
// done();
// });
// });
// it('chooseNode', function (done) {
// pinusClient.request('battle.rougeHandler.chooseNode', { gameCode: '4EPoEyML', layer: 2, detailCode: 'gREeb6Ku' }, (res) => {
// console.log('-x-x--x-x-x-x-x-x-x-x-x- res', util.inspect(res, { depth: null }));
// done();
// });
// });
// it('gameEnd', function (done) {
// pinusClient.request('battle.rougeHandler.gameEnd', { gameCode: 'voe64495' }, (res) => {
// console.log('-x-x--x-x-x-x-x-x-x-x-x- res', util.inspect(res, { depth: null }));
// done();
// });
// });
// it('shopBuy', function (done) {
// pinusClient.request('battle.rougeHandler.shopBuy', { gameCode: '5b8ioYN3', detailCode: '6ZhZYDXk', optionIndex: 1 }, (res) => {
// console.log('-x-x--x-x-x-x-x-x-x-x-x- res', util.inspect(res, { depth: null }));
// done();
// });
// });
// it('chooseReward', function (done) {
// pinusClient.request('battle.rougeHandler.chooseReward', { gameCode: '5b8ioYN3', detailCode: 'Jv5JEneT', groupIndex: 1, optionIndexs: [0] }, (res) => {
// console.log('-x-x--x-x-x-x-x-x-x-x-x- res', util.inspect(res, { depth: null }));
// done();
// });
// });
// it('checkBattle', function (done) {
// pinusClient.request('battle.rougeHandler.checkBattle', { gameCode: '4EPoEyML', detailCode: 'gREeb6Ku' ,warId:107, charaCodes:['D41jpqe6']}, (res) => {
// console.log('-x-x--x-x-x-x-x-x-x-x-x- res', util.inspect(res, { depth: null }));
// done();
// });
// });
// it('battleEnd', function (done) {
// pinusClient.request('battle.rougeHandler.battleEnd', { gameCode: '5b8ioYN3', detailCode: 'pEu6Wetd', warId: 102, battleCode: 'R3FobvR2', status: 1, round: 5, rougeDamage: [{ charaCode: 'WaAhKQu6',hp:1, ap:1, shield:1 }] }, (res) => {
// console.log('-x-x--x-x-x-x-x-x-x-x-x- res', util.inspect(res, { depth: null }));
// done();
// });
// });
// it('nodeEnd', function (done) {
// pinusClient.request('battle.rougeHandler.nodeEnd', { gameCode: 'MkSLT1HY', detailCode: 'fve9s7ej' }, (res) => {
// console.log('-x-x--x-x-x-x-x-x-x-x-x- res', util.inspect(res, { depth: null }));
// done();``
// });
// });
// it('chooseOption', function (done) {
// pinusClient.request('battle.rougeHandler.chooseOption', { gameCode: '4EPoEyML', detailCode: 'gREeb6Ku', eventOptions:[1,4,10, 13] }, (res) => {
// console.log('-x-x--x-x-x-x-x-x-x-x-x- res', util.inspect(res, { depth: null }));
// done();
// });
// });
// it('trainCard', function (done) {
// pinusClient.request('battle.rougeHandler.trainCard', { gameCode: '4EPoEyML', detailCode: 'gREeb6Ku', cardCode:'GB4AtwlA' }, (res) => {
// console.log('-x-x--x-x-x-x-x-x-x-x-x- res', util.inspect(res, { depth: null }));
// done();
// });
// });
// it('recovery', function (done) {
// pinusClient.request('battle.rougeHandler.recovery', { gameCode: '4EPoEyML', detailCode: 'gREeb6Ku' }, (res) => {
// console.log('-x-x--x-x-x-x-x-x-x-x-x- res', util.inspect(res, { depth: null }));
// done();
// });
// });
// index: NumberInt("0"),
// cardCode: "ShNTcBGa",
// cardId: NumberInt("10003")
// it('putOnOrOffCard', function (done) {
// pinusClient.request('battle.rougeHandler.putOnOrOffCard', { gameCode: '4EPoEyML', charaCode: 'GLYk8Q1C', cards: [{ index: 0, cardCode: "", }] }, (res) => {
// console.log('-x-x--x-x-x-x-x-x-x-x-x- res', util.inspect(res, { depth: null }));
// done();
// });
// });
// for (let id = 20001; id <= 20034; id++) {
// it('debugAddCard', function (done) {
// pinusClient.request('battle.rougeHandler.debugAddCard', { tye: 3, id }, (res) => {
// console.log('-x-x--x-x-x-x-x-x-x-x-x- res', util.inspect(res, { depth: null }));
// done();
// });
// });
// }
});