import { _decorator, Component, Node } from 'cc'; import { SkillCostType, SkillMultiplyType, TableNames } from './ConfigTableData'; import MTools from '../../mx/tools/MTools'; import { StatisticsType } from './GameData'; import { Monster } from '../ui/BattleGame/Monster'; import { MapAgent } from '../ui/BattleGame/MapAgent'; import { BattleType } from '../manager/ChapterDataManager'; import { ConstNumType } from './ConfigProjectData'; const { ccclass, property } = _decorator; /**随机技能结果 */ export class SkillSelectResult { /**类型1:普通技能 2:银币 */ type: number = 1; /**技能 */ skill: ITableSkill = null; /**银币数量 */ silver: number = 0; } /**权重项 */ export class WeightItem { /**配置的权重 */ configWeight: number = 0; /**权重 */ weight: number = 0; /**数据 */ data: T = null; /**DPS倍数 */ dps: number = 1; } @ccclass('SkillSelectCore') export class SkillSelectCore { /**选择次数 */ SelectCount: number = 0; /**随机技能结果列表 */ private _result: SkillSelectResult[] = []; /**************************初始化时的参数*******************************/ /**是否主动刷新 */ private _isAdRefresh: boolean = false; /**银币/技能权重配置 */ ConfigSkillWeight: ITableConst_weight = null; /**刷新品质权重 */ ConfigQualityWeight: ITableConst_weight = null; /**2个紫色技能保底阈值 */ ConfigTowPurpleQualityCountMax: ITableConst_weight = null; /**紫色权重最小值 */ ConfigPurpleQualityWeightMin: ITableConst_weight = null; /**紫色脸黑控制,X次开始加权,每次加权值y,保底值z */ ConfigPurpleQualityWeightLimitCount: ITableConst_weight = null; /**每日首次登录增加权重,紫色增加,橙色增加 */ ConfigFirstLoginWeight: ITableConst_weight = null; /**橙色权重最小值 */ ConfigOrangeQualityWeightMin: ITableConst_weight = null; /**橙色脸黑控制,X次开始加权,每次加权值y,保底值z */ ConfigOrangeQualityWeightLimitCount: ITableConst_weight = null; /**连续失败增加权重 紫色,橙色 */ ConfigFailWeight: ITableConst_weight = null; /**最小紫色出现间隔,橙色出现间隔 */ ConfigMinQualityCountInterval: ITableConst_weight = null; /**前2关品质额外加权 */ ConfigFirstTwoQualityWeight: ITableConst_weight = null; /**技能组对应时间 */ ConfigSkillGroupTime: ITableConst_weight = null; /**武器下限检测 */ ConfigWeaponLimit: ITableConst_weight = null; /**武器上限检测 */ ConfigWeaponLimitMax: ITableConst_weight = null; /**前X次必出紫 */ ConfigXCountPurple: ITableConst_weight = null; /**危险系数修正 0.5-2 */ ConfigDangerCoefficient: ITableConst_weight = null; /**DPS修正系数(开方) */ ConfigDpsCoefficient: ITableConst_weight = null; /**攻击单位少于X个不计算 */ ConfigAttackUnitCountMin: ITableConst_weight = null; /**DPS计算时间(每15秒) */ ConfigDpsGroupTime: ITableConst_weight = null; /**技能分T级范围 */ ConfigSkillTLevelRange: ITableConst_weight = null; /**当前章节权重配置 */ ConfigChapterWeight: ITableChapter_weight = null; /**所有技能列表 */ private _allSkillList: ITableSkill[] = []; /**当前已拥有的技能列表 */ private _curentHasSkill: ITableSkill[] = []; /**本次选择可以使用的技能池子 */ private _skillPool: ITableSkill[] = []; /**技能权重池默认 */ private _skillWeightPool: WeightItem[] = []; /**品质权重基础配置值 */ private _qualityWeightConfig: number[] = []; /**两紫一蓝保底最大未出次数 */ private _minQualityCountMax: number = 20; /**橙色权重最小值 */ private _minWeightOrange: number = 1; /**紫色品质加权值最小值 */ private _minWeightPurple: number = 1; /**紫色权重增加值: 紫色权重增加有两个地方有加成:局外角色系统增加和局内特殊技能增加,加到一起后相乘*/ private _weightPurpleAdd: number = 0; /**多少次未出紫色开始对紫色加权 */ private _notPurpleStartAddWeightOnCount: number = 20; /**紫色权重加权步长 */ private _weightPurpleAddStep: number = 5; /**紫色必出最大未出次数 */ private _minQualityPurpleCountMax: number = 40; /**多少次未出橙色开始对橙色加权 */ private _notOrangeStartAddWeightOnCount: number = 20; /**橙色权重加权步长 */ private _weightOrangeAddStep: number = 5; /**橙色必出最大未出次数 */ private _minQualityOrangeCountMax: number = 40; /**每日首次登录加权值 */ private _firstLoginWeight: number[] = [0, 0]; /**连续失败增加权重 紫色,橙色 */ private _failWeight: number[] = [0, 0]; /**连续失败次数 */ private _failCount: number = 0; /**前2关额外加权值 */ private _firstTwoQualityWeight: number[] = [0, 0]; /**脸白处理,不可以连续出紫色的间隔次数 */ private _minQualityPurpleCountInterval: number = 3; /**脸白处理,不可以连续出橙色的间隔次数 */ private _minQualityOrangeCountInterval: number = 3; /**主动刷新次数 */ private _adRefreshCount: number = 0; /**危险系数修正范围值[最小,最大] */ private _dangerCoefficientRange: number[] = [0.5, 2]; /**技能分T级范围 */ private _skillTLevelRange: number[] = [0.25, 0.5, 0.75, 1]; /**临时武器列表 */ private _tempArrWeapon: number[] = []; /**临时技能列表 */ private _tempArrSkill: number[] = []; /**************************计算时的参数*******************************/ /**当前确定的品质 */ private _currentQuality: number[] = []; /**技能权重池当前 */ private _currentSkillWeightPool: WeightItem[] = []; /**两紫一蓝未出次数 */ private _minQualityCount: number = 0; /**当前未出紫色次数 */ private _notPurpleCount: number = 0; /**当前未出橙色次数 */ private _notOrangeCount: number = 0; /**计算前的准备工作 */ private initData() { if (!gg.game?.isBattleContextActive?.()) { return; } if (!this._isAdRefresh) { this.ConfigSkillWeight = gg.data.table.getSkillCostData(SkillCostType.skillCostType_1001); this.ConfigQualityWeight = gg.data.table.getSkillCostData(SkillCostType.skillCostType_1002); this.ConfigTowPurpleQualityCountMax = gg.data.table.getSkillCostData(SkillCostType.skillCostType_1003); this.ConfigPurpleQualityWeightMin = gg.data.table.getSkillCostData(SkillCostType.skillCostType_1004); this.ConfigPurpleQualityWeightLimitCount = gg.data.table.getSkillCostData(SkillCostType.skillCostType_1005); this.ConfigFirstLoginWeight = gg.data.table.getSkillCostData(SkillCostType.skillCostType_1006); this.ConfigOrangeQualityWeightMin = gg.data.table.getSkillCostData(SkillCostType.skillCostType_1007); this.ConfigOrangeQualityWeightLimitCount = gg.data.table.getSkillCostData(SkillCostType.skillCostType_1008); this.ConfigFailWeight = gg.data.table.getSkillCostData(SkillCostType.skillCostType_1009); this.ConfigMinQualityCountInterval = gg.data.table.getSkillCostData(SkillCostType.skillCostType_1010); this.ConfigFirstTwoQualityWeight = gg.data.table.getSkillCostData(SkillCostType.skillCostType_1011); this.ConfigSkillGroupTime = gg.data.table.getSkillCostData(SkillCostType.skillCostType_1021); this._minQualityCount = gg.data.getStatistics(StatisticsType.NoDoublePurpleSkillCount); this._notPurpleCount = gg.data.getStatistics(StatisticsType.NotPurpleCount); this._notOrangeCount = gg.data.getStatistics(StatisticsType.NotOrangeCount); } else { this.ConfigSkillWeight = gg.data.table.getSkillCostData(SkillCostType.skillCostType_1101); this.ConfigQualityWeight = gg.data.table.getSkillCostData(SkillCostType.skillCostType_1102); this.ConfigTowPurpleQualityCountMax = gg.data.table.getSkillCostData(SkillCostType.skillCostType_1103); this.ConfigPurpleQualityWeightMin = gg.data.table.getSkillCostData(SkillCostType.skillCostType_1104); this.ConfigPurpleQualityWeightLimitCount = gg.data.table.getSkillCostData(SkillCostType.skillCostType_1105); this.ConfigFirstLoginWeight = gg.data.table.getSkillCostData(SkillCostType.skillCostType_1106); this.ConfigOrangeQualityWeightMin = gg.data.table.getSkillCostData(SkillCostType.skillCostType_1107); this.ConfigOrangeQualityWeightLimitCount = gg.data.table.getSkillCostData(SkillCostType.skillCostType_1108); this.ConfigFailWeight = gg.data.table.getSkillCostData(SkillCostType.skillCostType_1109); this.ConfigMinQualityCountInterval = gg.data.table.getSkillCostData(SkillCostType.skillCostType_1110); this.ConfigFirstTwoQualityWeight = gg.data.table.getSkillCostData(SkillCostType.skillCostType_1111); this.ConfigSkillGroupTime = gg.data.table.getSkillCostData(SkillCostType.skillCostType_1121); this._minQualityCount = gg.data.getStatistics(StatisticsType.NoDoublePurpleSkillCountActiveRefresh); this._notPurpleCount = gg.data.getStatistics(StatisticsType.NotPurpleCountActiveRefresh); this._notOrangeCount = gg.data.getStatistics(StatisticsType.NotOrangeCountActiveRefresh); } this.ConfigWeaponLimit = gg.data.table.getSkillCostData(SkillCostType.skillCostType_1031); this.ConfigWeaponLimitMax = gg.data.table.getSkillCostData(SkillCostType.skillCostType_1032); this.ConfigXCountPurple = gg.data.table.getSkillCostData(SkillCostType.skillCostType_1100); this.ConfigDangerCoefficient = gg.data.table.getSkillCostData(SkillCostType.skillCostType_1201); this.ConfigDpsCoefficient = gg.data.table.getSkillCostData(SkillCostType.skillCostType_1202); this.ConfigAttackUnitCountMin = gg.data.table.getSkillCostData(SkillCostType.skillCostType_1203); this.ConfigDpsGroupTime = gg.data.table.getSkillCostData(SkillCostType.skillCostType_1204); this.ConfigSkillTLevelRange = gg.data.table.getSkillCostData(SkillCostType.skillCostType_1205); //初始化紫色品质加权值最小值 this._minWeightPurple = this.ConfigPurpleQualityWeightMin.constValue; //初始化橙色品质加权值最小值 this._minWeightOrange = this.ConfigOrangeQualityWeightMin.constValue; //初始化每日首次登录加权值 this._firstLoginWeight = this.ConfigFirstLoginWeight.constString.split(",").map(Number); //初始化连续失败增加权重 this._failWeight = this.ConfigFailWeight.constString.split(",").map(Number); //初始化连续失败次数 this._failCount = gg.data.project.getFailCount(gg.game.CurentBattle.ChapterId, gg.game.CurentBattle.BattleType, gg.game.CurentBattle.Difficulty); //初始化前2关额外加权值 this._firstTwoQualityWeight = this.ConfigFirstTwoQualityWeight.constString.split(",").map(Number); //初始化危险系数修正范围值 this._dangerCoefficientRange = this.ConfigDangerCoefficient.constString.split(",").map(Number); //初始化技能分T级范围 this._skillTLevelRange = this.ConfigSkillTLevelRange.constString.split(",").map(Number); //初始化品质权重配置 let arr = this.ConfigQualityWeight.constString.split(","); this._qualityWeightConfig = arr.map((item) => Number(item)); this.showLog(`初始化品质权重配置:`, JSON.stringify(this._qualityWeightConfig)); //初始化两紫一蓝保底次数 this._minQualityCountMax = this.ConfigTowPurpleQualityCountMax.constValue; this.showLog(`两紫一蓝未出次数:`, this._minQualityCount); //初始化紫色加权参数 let str = this.ConfigPurpleQualityWeightLimitCount.constString; let strArr = str.split(",").map(Number); this._notPurpleStartAddWeightOnCount = strArr[0]; this._weightPurpleAddStep = strArr[1]; this._minQualityPurpleCountMax = strArr[2]; //初始化橙色加权参数 str = this.ConfigOrangeQualityWeightLimitCount.constString; strArr = str.split(",").map(Number); this._notOrangeStartAddWeightOnCount = strArr[0]; this._weightOrangeAddStep = strArr[1]; this._minQualityOrangeCountMax = strArr[2]; //初始化结果列表 this._result = []; //初始化所有技能列表 this._allSkillList = gg.data.table.getTableList(TableNames.Skill); //初始化当前已有拥用的技能列表 this._curentHasSkill = gg.game.CurentBattle.CurUseSkillArr.slice(); //临时加入计算的武器列表 for (let i = 0; i < this._tempArrSkill.length; i++) { let skillId = this._tempArrSkill[i]; let skill = gg.data.table.getTableData(TableNames.Skill, skillId); if (skill) { this._curentHasSkill.push(skill); } } //当前拥有的武器列表 let weaponArr = gg.game.CurentBattle.CurUseWeaponArr.slice(); //临时加入计算的武器列表 for (let i = 0; i < this._tempArrWeapon.length; i++) { let weaponId = this._tempArrWeapon[i]; let weapon = gg.data.project.getWeaponData(weaponId); if (weapon) { weaponArr.push(weapon); } } //武器下限检测 weaponArr = this.checkWeaponLimitMin(weaponArr); //武器上限检测 weaponArr = this.checkWeaponLimitMax(weaponArr); //根据当前武器筛选出可以使用的的技能 this._skillPool = gg.data.project.getAllUnlockedSkillByWeaponIds(weaponArr); this.showLog(`已经拥有的武器:`, JSON.stringify(weaponArr.map(item => item.weaponid))); //排除已达最大技能上限的技能 this._skillPool = this._skillPool.filter(x => { let len = this._curentHasSkill.filter(y => y.id == x.id).length return len < x.limit_num; }); this.showLog(`已经拥有的技能:`, JSON.stringify(this._curentHasSkill.map(item => item.id))); //初始化技能权重池 let skillWeightList = gg.data.table.getTableList(TableNames.SkillPool); skillWeightList = skillWeightList.filter((item) => this._skillPool.find((skill) => skill.id == item.id) != null); this._skillWeightPool = this.getSkillPool("base_skil", skillWeightList); this.showLog(`默认技能权重池:`, JSON.stringify(this._skillWeightPool.map(item => item.data.id))); let poolId = this.getSkillPoolId(); this.showLog(`技能池配置字段ID:`, poolId); this._currentSkillWeightPool = this.getSkillPool(poolId, skillWeightList); this.showLog(`当前技能权重池:`, JSON.stringify(this._currentSkillWeightPool.map(item => item.data.id))); //移除特殊技能 this.removeSpecialSkillFromPool(); //初始化紫色品质加权值加成 this._weightPurpleAdd = this.getPurpleQualityWeightAddForItems(); this._weightPurpleAdd += this.getPurpleQualityWeightAddForSkills(); //初始化动态难度加权值 let groupId = gg.game.CurentBattle.CurentConfigChapter.quality_group; //获取危险系数 let dangerCoefficient = this.getDangerCoefficient(); this.showLog(`危险系数:`, dangerCoefficient); //根据危险系数获取章节权重配置 this.ConfigChapterWeight = gg.data.project.getChapterWeightConfig(groupId, dangerCoefficient); //紫色和橙色间隔限制次数 str = this.ConfigMinQualityCountInterval.constString; strArr = str.split(",").map(Number); this._minQualityPurpleCountInterval = strArr[0]; this._minQualityOrangeCountInterval = strArr[1]; // //动态计算技能权重 // this.calculateSkillWeight(); this.updateData(); } /**更新算法中用到的全局数据 */ private updateData() { } /**开始计算生成count个技能 */ public randomSkills(count: number = 3, isAdRefresh: boolean = false, tempArrWeapon: number[] = [], tempArrSkill: number[] = []): SkillSelectResult[] { this._result = []; if (!gg.game?.isBattleContextActive?.()) { return []; } this._tempArrWeapon = tempArrWeapon; this._tempArrSkill = tempArrSkill; this.SelectCount++; this._isAdRefresh = isAdRefresh; this._adRefreshCount = gg.data.getStatistics(StatisticsType.AdRefreshCount); if (isAdRefresh) { gg.data.addStatistics(StatisticsType.AdRefreshCount, 1); } this.showLog(`---------------技能3选1逻辑开始[第${this.SelectCount}次]----------------------`); this.initData(); //先确定品质 this._currentQuality = []; //处理保底逻辑:玩家自然随机到2个大于等于紫色面板/触发保底时计数清零,计数+1规则:未达成大于等于2紫面板则计数+1(全局计数) if (this._minQualityCount >= this._minQualityCountMax) { this._minQualityCount = 0; this.setMinQualityCount(this._minQualityCount); this._currentQuality = [3, 3, 2]; this.showLog(`触发两紫一蓝保底`); } else { //随机出品质 for (let i = 0; i < count; i++) { //随机出一个品质 if (i >= this._currentQuality.length) { let quality = this.randomQuality(); this._currentQuality.push(quality); } } } //新手前x次必出紫色品质技能 if (this._isAdRefresh && this._adRefreshCount < this.ConfigXCountPurple.constValue) { this._currentQuality[0] = 3; this.showLog(`触发新手前${this.ConfigXCountPurple.constValue}次必出紫色品质技能`, this._adRefreshCount); } this._currentQuality.shuffle(); this.showLog("确定的品质:", JSON.stringify(this._currentQuality)); //根据品质随机出技能 for (let i = 0; i < count; i++) { let skill = this.getSkill(this._currentQuality[i]); if (!skill) continue; this._result.push(skill); //从技能池中移除已拥有的技能 this.removeSkillFromPool(skill.skill.id); //移除特殊技能 this.removeSpecialSkillFromPool(); } this.showLog("随机出的技能:", JSON.stringify(this._result.map(x => x.skill.id))); this.showLog("---------------------------------------------"); //出了两个紫色以上的品质,重置两紫一蓝保底次数 if (this._currentQuality.filter(x => x >= 3).length >= 2) this._minQualityCount = 0; else { this._minQualityCount++; } this.setMinQualityCount(this._minQualityCount); //重置固定技能筛选品质 gg.game.CurentBattle.FixedQuality = 0; return this._result; } /**从技能池中移除一个技能 */ private removeSkillFromPool(id: number) { //移除已拥有的技能 let index = this._currentSkillWeightPool.findIndex((item) => item.data.id == id); if (index > -1) { this._currentSkillWeightPool.splice(index, 1); } index = this._skillWeightPool.findIndex((item) => item.data.id == id); if (index > -1) { this._skillWeightPool.splice(index, 1); } } /**从技能池中移除特殊技能 */ private removeSpecialSkillFromPool() { let removeSkillArr = [48, 49, 50]; //如果已经拥有的技能中包含特殊技能或者已经随机出的技能中包含特殊技能,则从技能池中移除另2个特殊技能 if (this._curentHasSkill.find(x => removeSkillArr.includes(x.id)) != null || this._result.find(x => removeSkillArr.includes(x.skill.id)) != null) { for (let i = 0; i < removeSkillArr.length; i++) { this.removeSkillFromPool(removeSkillArr[i]); } } } /**计算获取一个技能 */ private getSkill(quality: number): SkillSelectResult { if (gg.game.CurentBattle.FixedQuality > 0) { quality = gg.game.CurentBattle.FixedQuality; this.showLog(`固定技能筛选品质:`, quality); } //先随机出一个银币技能 let skill = null;// this.getSilverSkill(); //如果随机出的银币技能不存在,就随机出一个普通技能 if (!skill) { skill = this.getNormalSkill(quality); } return skill; } /**随机出一个银币技能 */ private getSilverSkill(): SkillSelectResult { //如果已经随机出一个银币技能,就返回null if (this._result.find(x => x.type == 2) != null) return null; //获取银币技能权重配置(配置的格式是:【5,95】 表示银币概率5%,技能概率95%) let w = this.ConfigSkillWeight.constString.split(","); let weightArr = w.map(x => Number(x)); if (weightArr.length != 2) return null; let silverWeight = weightArr[0]; //没有随机到银币技能,就返回null if (Math.random() > silverWeight / 100) return null; //随机出一个银币技能 let list = gg.data.table.getTableList(TableNames.Skill).filter((item) => item.type == 3); let arr: WeightItem[] = []; for (let i = 0; i < list.length; i++) { let data = list[i]; let weightItem = new WeightItem(); weightItem.weight = Number(data.weight_normal); weightItem.configWeight = Number(data.weight_normal); weightItem.data = data; arr.push(weightItem); } let silverSkill = MTools.lottery(arr); let numStr = silverSkill.data.property_para1.split(",")[1]; let result = new SkillSelectResult(); result.type = 2; result.silver = Number(numStr); result.skill = silverSkill.data; this.showLog(`随机出了银币技能:`, result.skill.id, result.silver); return result; } /**随机出一个普通技能 */ private getNormalSkill(quality: number): SkillSelectResult { this.showLog("开始技能随机"); let result = new SkillSelectResult(); result.type = 1; let arr = this._currentSkillWeightPool.filter((item) => { return item.data.quality == quality; }); this.showLog(`当前品质技能权重池:`, JSON.stringify(arr.map(item => item.data.id))); //如果当前品质的技能权重池中没有技能,或者固定了技能品质,就从默认权重池中随机 if (arr.length == 0 || gg.game.CurentBattle.FixedQuality > 0 || gg.game.CurentBattle.BattleType == BattleType.BoxMode) { arr = this._skillWeightPool; if (gg.game.CurentBattle.FixedQuality > 0) { arr = arr.filter((item) => item.data.quality == gg.game.CurentBattle.FixedQuality); } if (arr.length == 0 && gg.game.CurentBattle.FixedQuality > 0) { arr = this._skillWeightPool; } this.showLog(`没有品质${quality}的技能,触发默认权重池`); } //如果默认权重池中也没有技能,就返回null if (arr.length == 0) { this.showLog(`默认权重池中也没有技能,返回null`); return null; } let skill = MTools.lottery(arr); result.skill = skill.data; this.showLog(`随机出的技能:`, result.skill.id); return result; } /**随机出一个品质 */ private randomQuality() { //处理脸黑保底 //橙色必出 if (this._notOrangeCount >= this._minQualityOrangeCountMax) { this._notOrangeCount = 0; this.setNotOrangeCount(0); this.showLog(`触发橙色必出`); return 4; } //紫色必出 if (this._notPurpleCount >= this._minQualityPurpleCountMax) { this._notPurpleCount = 0; this.setNotPurpleCount(0); this.showLog(`触发紫色必出`); return 3; } /**当前的品质权重数组 */ let w1 = this._qualityWeightConfig[0]; let w2 = this._qualityWeightConfig[1]; let w3 = this.getPurpleQualityWeight(); let w4 = this.getOrangeQualityWeight(); let qualityWeight: WeightItem[] = [ { weight: w1, configWeight: w1, data: 1, dps: 1 }, { weight: w2, configWeight: w2, data: 2, dps: 1 }, { weight: w3, configWeight: w3, data: 3, dps: 1 }, { weight: w4, configWeight: w4, data: 4, dps: 1 }, ]; let qualityData = MTools.lottery(qualityWeight) as WeightItem; let quality = qualityData.data; //处理脸白逻辑 //如果橙色品质未出次数小于间隔次数,且当前品质是橙色,就重新随机一次 if (this._notOrangeCount < this._minQualityOrangeCountInterval && quality == 4) { qualityData = MTools.lottery(qualityWeight) as WeightItem; quality = qualityData.data; } //如果紫色品质未出次数小于间隔次数,且当前品质是紫色,就重新随机一次 else if (this._notPurpleCount < this._minQualityPurpleCountInterval && quality == 3) { qualityData = MTools.lottery(qualityWeight) as WeightItem; quality = qualityData.data; } //-----------处理计数 //紫色品质未出次数 if (quality == 3) { this._notPurpleCount = 0; } else { this._notPurpleCount++; } this.setNotPurpleCount(this._notPurpleCount); //橙色品质未出次数 if (quality == 4) { this._notOrangeCount = 0; } else { this._notOrangeCount++; } this.setNotOrangeCount(this._notOrangeCount); return quality; } /**计算橙色品质加权值(加权后的最终权重值) */ private getOrangeQualityWeight() { let weight = 0; //橙色权重最小值 let minWeight = this._minWeightOrange; //橙色权重配置值 let confWeight = this._qualityWeightConfig[3]; //保底加权值 let defaultWeight = 0; //橙色品质保底加权值 X次未出现后开始加权,X次后每次权重加Y,+2y,+3y......... if (this._notOrangeCount >= this._notOrangeStartAddWeightOnCount) { defaultWeight = (this._notOrangeCount - this._notOrangeStartAddWeightOnCount) * this._weightOrangeAddStep; } this.showLog(`当前未出橙色次数:${this._notOrangeCount}`); this.showLog(`橙色品质保底加权值:${defaultWeight}`); //每日首次登录加成 let firstLoginWeight = this.getFirstLoginWeight(1); //连续失败加成 let failWeight = this.getFailContinuousWeight(1); //前两关加成 let firstLevelTwoWeight = gg.game.CurentBattle.ChapterId <= 2 ? this._firstTwoQualityWeight[1] : 0; let totalWeight = confWeight + defaultWeight + firstLoginWeight + failWeight + firstLevelTwoWeight; weight = Math.max(minWeight, totalWeight); //如果当前技能池中的橙色技能数量不足,则橙色返回0 let haveNum = this._currentSkillWeightPool.filter(x => x.data.quality == 4).length; let salectNum = this._currentQuality.filter(x => x == 4).length; if (salectNum >= haveNum) { weight = 0; } return weight; } /**计算紫色品质权重(加权后的最终权重值) */ private getPurpleQualityWeight() { let weight = 0; //紫色权重最小值 let minWeight = this._minWeightPurple; //紫色权重配置值 let confWeight = this._qualityWeightConfig[2]; confWeight = confWeight * (1 + this._weightPurpleAdd / 10000); //保底加权值 let defaultWeight = 0; //紫色品质保底加权值 X次未出现后开始加权,X次后每次权重加Y,+2y,+3y......... if (this._notPurpleCount >= this._notPurpleStartAddWeightOnCount) { defaultWeight = (this._notPurpleCount - this._notPurpleStartAddWeightOnCount) * this._weightPurpleAddStep; } this.showLog(`当前未出紫色次数:${this._notPurpleCount}`); this.showLog(`紫色品质保底加权值:${defaultWeight}`); //每日首次登录加成 let firstLoginWeight = this.getFirstLoginWeight(0); //连续失败加成 let failWeight = this.getFailContinuousWeight(0); //紫色品质前两关加成 let firstLevelTwoWeight = gg.game.CurentBattle.ChapterId <= 2 ? this._firstTwoQualityWeight[0] : 0; let totalWeight = confWeight + defaultWeight + firstLoginWeight + failWeight + firstLevelTwoWeight; weight = Math.max(minWeight, totalWeight); return weight; } /**根据已有技能计算紫色品质权重加成百分比 (配置的数值是万分比没有除以10000)*/ private getPurpleQualityWeightAddForSkills() { let add = 0; let arr = this._curentHasSkill; for (let i = 0; i < arr.length; i++) { let skillData = arr[i]; let strArr = skillData.property_para1.split(","); //技能属性类型61,增加紫色品质权重加成 if (strArr[0] == "61") { add += Number(strArr[1]); } } this.showLog(`紫色品质技能加成:`, add); return add; } /**根据已有道具计算紫色品质权重加成百分比(配置的数值是万分比没有除以10000) */ private getPurpleQualityWeightAddForItems() { //99 "时来运转"道具,增加紫色品质权重加成 let itemData = gg.data.table.getItemData(99); let num = gg.data.getItemNum(itemData.id); let result = num * Number(itemData.parameter); this.showLog(`紫色品质道具加成:`, result); return result; } /**获取每日首次登录加成 * @param index 0:紫色品质 1:橙色品质 */ private getFirstLoginWeight(index: number) { let weight = 0; let count = gg.data.getStatistics(StatisticsType.FirstLoginWeight); if (count < 1 && gg.data.LoginDays > 1 && gg.data.IsFirstLoginToday) { weight = this._firstLoginWeight[index]; } return weight; } /**获取连续失败加成 */ private getFailContinuousWeight(index: number) { let weight = 0; //最新关卡看过广告后失败,或者看过广告X波次后主动退出时计数+1 成功则设置为0 //连续失败加成值=失败次数*配置值 let chapterid = gg.data.project.getCurentLevelChapterId(gg.game.CurentBattle.Difficulty); if (chapterid == gg.game.CurentBattle.ChapterId) weight = this._failCount * this._failWeight[index]; return weight; } /**获取危险系数 */ private getDangerCoefficient() { let dangerCoefficient = 0; //玩家当前DPS let dps = gg.game.CurentBattle.CurentDPS; if (dps <= 0) dps = 1; this.showLog("玩家当前DPS:", dps); //获取所有怪物 let monsterList = gg.game.CurentBattle.MonsterSpawner?.node?.getComponentsInChildren(Monster) || []; for (let i = 0; i < monsterList.length; i++) { let monster = monsterList[i]; //怪物被击杀时间=怪物血量/玩家当前平均每秒DPS (分母可能为0,为0的时候整体设置为0) let atkTime = monster.hp / dps; if (dps == 1) atkTime = 0; //this.showLog(`怪物${monster.Data.id}被击杀时间:`, atkTime); //怪物剩余路径距离 let dis = monster.getComponent(MapAgent).getRemainPathDistance(); //this.showLog(`怪物${monster.Data.id}剩余路径距离(单位像素):`, dis); //实际速度 let speed = monster.getMoveSpeed(); //this.showLog(`怪物${monster.Data.id}实际速度(单位像素/秒):`, speed); //怪物行进时间=怪物到攻击点距离/配表固定移速 if (speed == 0) continue; let moveTime = dis / speed; if (moveTime <= 0) moveTime = 1; //this.showLog(`怪物${monster.Data.id}行进时间(单位秒):`, moveTime); //DPS修正系数=(怪物被击杀时间/怪物行进时间)^0.5(配置:常量表id 1202) let dpsCoefficient = Math.pow(atkTime / moveTime, this.ConfigDpsCoefficient.constValue); //修正系数限制最小值不小于配置值 dpsCoefficient = Math.max(dpsCoefficient, this._dangerCoefficientRange[0]); //修正系数限制最大值不大于配置值 dpsCoefficient = Math.min(dpsCoefficient, this._dangerCoefficientRange[1]); //this.showLog(`怪物${monster.Data.id}DPS修正系数:`, dpsCoefficient); //怪物剩余路径距离/53=怪物剩余路径数量 let gridCount = dis / 53; //this.showLog(`怪物${monster.Data.id}剩余路径数量:`, gridCount); //配置的值 let confv = 1; let arr = gg.data.table.getTableList(TableNames.Chapter_monster); //根据格子距离找出表中对应的怪物数据 for (let j = 0; j < arr.length; j++) { let data = arr[j]; confv = data.num_multiple; if (data.distance > gridCount) { break; } } // this.showLog(`怪物${monster.Data.id}配置的值:`, confv); let va = confv * dpsCoefficient; //this.showLog(`怪物${monster.Data.id}危险系数:`, va); dangerCoefficient += va; } this.showLog("危险系数:", dangerCoefficient); return dangerCoefficient; } /**检查武器下限 */ private checkWeaponLimitMin(weaponArr: ITableWeapon2[]) { let str = this.ConfigWeaponLimit.constString; let arr = str.split("|"); for (let i = 0; i < arr.length; i++) { let str = arr[i]; let [selectCount, skillNum] = str.split(",").map(Number); if (this.SelectCount == selectCount) { this.showLog("触发武器下限检查", selectCount, skillNum); //找出所有武器中技能最多的武器 let maxCount = 0; let weapon: ITableWeapon2 = null; for (let j = 0; j < weaponArr.length; j++) { let data = weaponArr[j]; //只有群攻武器才需要检测下限 if (data.sort != 2) continue; let count = this._curentHasSkill.filter((item) => item.weapon == data.weaponid).length; if (count >= maxCount) { maxCount = count; weapon = data; } } //如果技能最多的武器,技能数量小于下限,移除其他武器 if (maxCount < skillNum && weapon != null) { weaponArr = [weapon]; this.showLog("武器下限检查找到下限武器,ID:" + weapon.id); break; } } } return weaponArr; } /**检查武器上限 */ private checkWeaponLimitMax(weaponArr: ITableWeapon2[]) { let num = this.ConfigWeaponLimitMax.constValue; let arr = weaponArr.slice(); for (let j = 0; j < arr.length; j++) { let weapon = arr[j]; let count = this._curentHasSkill.filter((item) => item.weapon == weapon.weaponid).length; if (count >= num) { weaponArr.remove(weapon); this.showLog("武器上限检查找到上限武器,ID:" + weapon); } } return weaponArr; } /**根据当前章节,当前游戏时间,今日看广告次数获取当前技能池配置ID */ private getSkillPoolId() { let id = ""; let adCount = gg.data.getStatistics(StatisticsType.TodayAdCount); let time = gg.game.CurentBattle.CurentWaveNum; let group = gg.game.CurentBattle.CurentConfigChapter.skill_pool_group; let index = 0; let confArr = this.ConfigSkillGroupTime.constString.split(","); for (let i = 0; i < 4; i++) { let str = confArr[i]; let t = Number(str); index = i; if (t >= time) { break; } } let nameStr = this._isAdRefresh ? "refresh_game_time" : "game_time"; nameStr += (index + 1); let list = gg.data.table.getTableList(TableNames.SkillPoolUse); list = list.filter((item) => item.skill_group == group); for (let i = 0; i < list.length; i++) { let item = list[i]; id = item[nameStr]; if (item.adv_num >= adCount) { break; } } return id; } /**根据技能池id和技能池配置数组,获取对应技能池 */ private getSkillPool(poolId: string, list: ITableSkill_pool[]) { let resultArr: WeightItem[] = []; for (let i = 0; i < list.length; i++) { let item = list[i]; let id = item.id; let str = item[poolId]; if (str && str != "") { let [status, weight] = str.split(","); if (status == "1") { let skillData = this._allSkillList.find((x) => x.id == id); resultArr.push({ configWeight: Number(weight), weight: Number(weight), data: skillData, dps: 1 }); } } } return resultArr; } /**设置两紫一篮未出次数 */ private setMinQualityCount(count: number) { if (this._isAdRefresh) gg.data.setStatistics(StatisticsType.NoDoublePurpleSkillCountActiveRefresh, count); else gg.data.setStatistics(StatisticsType.NoDoublePurpleSkillCount, count); } /**设置紫色品质未出次数 */ private setNotPurpleCount(count: number) { if (this._isAdRefresh) gg.data.setStatistics(StatisticsType.NotPurpleCountActiveRefresh, count); else gg.data.setStatistics(StatisticsType.NotPurpleCount, count); } /**设置橙色品质未出次数 */ private setNotOrangeCount(count: number) { if (this._isAdRefresh) gg.data.setStatistics(StatisticsType.NotOrangeCountActiveRefresh, count); else gg.data.setStatistics(StatisticsType.NotOrangeCount, count); } /**计算一组技能的DPS倍数 */ private calculateDPSWeight(arr: ITableSkill[], weaponId: number) { //计算已经拥有的所有乘区类型 let typeArr = gg.data.project.getAllMultiplyType(arr); //this.showLog(`所有乘区类型:`, JSON.stringify(typeArr)); let multDataMap = new Map(); //计算已经拥有的所有乘区类型的值 for (let j = 0; j < typeArr.length; j++) { let type = typeArr[j]; let value = gg.data.project.getSumValueBySkills(arr, type); multDataMap.set(type, value); } //let tempArr = Array.from(multDataMap.values()).map((item) => item); //this.showLog(`所有乘区类型的值:`, tempArr); //当前暴击率 let critRate = multDataMap.get(SkillMultiplyType.CritRate) || 0; //this.showLog(`乘区当前暴击率:`, critRate); //当前暴击伤害 let critDamage = multDataMap.get(SkillMultiplyType.CritDamage) || 0; //this.showLog(`乘区当前暴击伤害:`, critDamage); //当前其他乘区类型乘区值的总乘积 let otherTypeProduct = 1; multDataMap.forEach((value, key) => { if (key != SkillMultiplyType.CritRate && key != SkillMultiplyType.CritDamage) { otherTypeProduct *= (value + 1); } }); //this.showLog(`当前其他乘区类型乘区值的总乘积:`, otherTypeProduct); multDataMap.clear(); let dps = this.calculateDPS(critRate, critDamage, otherTypeProduct, weaponId); return dps; } /**根据乘区暴击率,乘区暴击伤害,其他乘区类型乘区值的总乘积,计算当前技能的DPS */ private calculateDPS(critRate: number, critDamage: number, otherType: number, weaponId: number) { let weaponData = gg.data.project.getWeaponData(weaponId); //基础暴击率 let baseCritRate = weaponData.critical_chance / 10000; //this.showLog(`武器[${weaponId}]基础暴击率:`, baseCritRate); //玩家当前暴击率 let playerCritRate = 0//gg.data.project.getPlayerCritRate(weaponId) / 10000; //this.showLog(`武器[${weaponId}]玩家当前暴击率:`, playerCritRate); //所有暴击率 let allCritRate = baseCritRate + playerCritRate + critRate; //this.showLog(`武器[${weaponId}]所有暴击率:`, allCritRate); //基础暴击伤害 let baseCritDamage = weaponData.critical_dam / 10000; //this.showLog(`武器[${weaponId}]基础暴击伤害:`, baseCritDamage); //玩家当前暴击伤害 let playerCritDamage = 0//gg.data.project.getPlayerCritDamage(weaponId) / 10000; //this.showLog(`武器[${weaponId}]玩家当前暴击伤害:`, playerCritDamage); //所有暴击伤害 let allCritDamage = baseCritDamage + playerCritDamage + critDamage; //this.showLog(`武器[${weaponId}]所有暴击伤害:`, allCritDamage); //暴击率期望值=max(1,玩家当前暴击率*玩家当前暴击伤害+(1-玩家当前暴击率)) let critRateExpect = Math.max(1, allCritRate * allCritDamage + (1 - allCritRate)); //this.showLog(`武器[${weaponId}]暴击率期望值:`, critRateExpect); //DPS倍数=暴击率期望*其他乘区相乘 let dps = critRateExpect * otherType; return dps; } private showLog(...args: any[]) { if (this._isShowLog == false) return; let s = args[0]; args.shift(); console.log(`%c${s}`, "color:#32ABC2", ...args); } /** 新手引导第一次随机技能数据 */ public getFirstRandomSkills() { let str = gg.data.table.getConstString(ConstNumType.guideSkill1); let idArr = str.split(',').map(Number).filter((id) => !isNaN(id)); let skillDataList: ITableSkill[] = []; for (let i = 0; i < idArr.length; i++) { let skillData = gg.data.table.getTableData(TableNames.Skill, idArr[i]); if (skillData) { skillDataList.push(skillData); } } return skillDataList; } /** 新手引导第2次随机技能数据 */ public getSecondRandomSkills() { let str = gg.data.table.getConstString(ConstNumType.guideSkill2); let idArr = str.split(',').map(Number).filter((id) => !isNaN(id)); let skillDataList: ITableSkill[] = []; for (let i = 0; i < idArr.length; i++) { let skillData = gg.data.table.getTableData(TableNames.Skill, idArr[i]); if (skillData) { skillDataList.push(skillData); } } return skillDataList; } _isShowLog: boolean = true; }