|
|
|
|
|
import { color } from "cc";
|
|
|
|
|
|
import { Singleton } from "../../mx/tools/Singleton";
|
|
|
|
|
|
import { BattleType, ChapterDataManager, ChapterDifficulty } from "../manager/ChapterDataManager";
|
|
|
|
|
|
import { SkillMultiplyType, TableNames } from "./ConfigTableData";
|
|
|
|
|
|
import { DEBUG, DEV, EDITOR } from "cc/env";
|
|
|
|
|
|
import MTools from "../../mx/tools/MTools";
|
|
|
|
|
|
import { ConstReplicaEnum } from "../manager/ConstReplicaManager";
|
|
|
|
|
|
import { ChapterNewPlayTypeEnum, ChapterPlayData, IAccelerateGateProperty, IBloodBagAddHpProperty, IBloodMoonProperty, IColdFireProperty, IFogAndWindProperty, IMagicEyeProperty, INightAndLightProperty, IParatroopsProperty, IWitchProperty } from "./ChapterPlayTypes";
|
|
|
|
|
|
import { ItemNumType, OtherDataType, StatisticsType, StatusType } from "./GameData";
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////----------------新机制-------------------///////////////////////
|
|
|
|
|
|
/**是否有章节新机制 */
|
|
|
|
|
|
type FieldExtractor = (rawData: any, isPropMode: boolean) => any;
|
|
|
|
|
|
interface PlayConfigDescriptor {
|
|
|
|
|
|
constKey: number;
|
|
|
|
|
|
needsFilter?: boolean;
|
|
|
|
|
|
// 定义该玩法需要的参数映射关系
|
|
|
|
|
|
// key: 传给 parser 的参数名,value: 如何从 excel 行数据中提取该参数
|
|
|
|
|
|
paramMapping: Record<string, FieldExtractor>;
|
|
|
|
|
|
// 具体的业务解析逻辑
|
|
|
|
|
|
parser: (params: any) => ChapterPlayData;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 项目动态数据
|
|
|
|
|
|
* 大部分通用的动态数据在gg.data中已经实现了
|
|
|
|
|
|
* 根据项目自定义数据获取实现方式在这个脚本里写,方便统一管理和调用
|
|
|
|
|
|
*/
|
|
|
|
|
|
export class ConfigProjectData extends Singleton {
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////----------------道具相关-------------------///////////////////////
|
|
|
|
|
|
//#region 道具
|
|
|
|
|
|
/**将id相同的道具合并 */
|
|
|
|
|
|
mergeSameItem(items: ItemData[]) {
|
|
|
|
|
|
let itemMap = new Map<number, ItemData>();
|
|
|
|
|
|
for (let i = 0; i < items.length; i++) {
|
|
|
|
|
|
let item = items[i];
|
|
|
|
|
|
let itemData = itemMap.get(item.id);
|
|
|
|
|
|
if (itemData) {
|
|
|
|
|
|
itemData.num += item.num;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
itemMap.set(item.id, item);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
items = [];
|
|
|
|
|
|
itemMap.forEach((value, key) => {
|
|
|
|
|
|
items.push(value);
|
|
|
|
|
|
});
|
|
|
|
|
|
return items;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//#endregion
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////----------------体力相关-------------------///////////////////////
|
|
|
|
|
|
//#region 体力
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 扣除体力后同步自动回复计时锚点:从「达到上限」首次降到「未满」时记录 AutoAddVigourTime。
|
|
|
|
|
|
* 实际扣体力走 gg.data.subVigour,须在其 subItemNum 之后调用本方法(project.subVigour 也会调用)。
|
|
|
|
|
|
*/
|
|
|
|
|
|
syncAutoAddVigourTimeAfterConsume(count: number) {
|
|
|
|
|
|
let curentVigour = gg.data.getItemNum(ItemNumType.vigour);
|
|
|
|
|
|
let maxVigour = gg.data.table.getConst(ConstNumType.VigourAutoAddLimit);
|
|
|
|
|
|
if (curentVigour < maxVigour && curentVigour + count >= maxVigour) {
|
|
|
|
|
|
gg.data.setStatistics(StatisticsType.AutoAddVigourTime, gg.data.getCurentTime());
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**消耗体力(仅扣道具;任务/事件请用 gg.data.subVigour) */
|
|
|
|
|
|
subVigour(count: number) {
|
|
|
|
|
|
gg.data.subItemNum(ItemNumType.vigour, count);
|
|
|
|
|
|
this.syncAutoAddVigourTimeAfterConsume(count);
|
|
|
|
|
|
// gg.data.saveToServer();
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**获取下一次自动增加体力的剩余时间(单位秒) */
|
|
|
|
|
|
getAddVigourTime() {
|
|
|
|
|
|
let lastAddTime = gg.data.getStatistics(StatisticsType.AutoAddVigourTime);
|
|
|
|
|
|
let curTime = gg.data.getCurentTime();
|
|
|
|
|
|
let maxTime = gg.data.table.getConst(ConstNumType.VigourAutoAddTime);
|
|
|
|
|
|
let maxVigour = gg.data.table.getConst(ConstNumType.VigourAutoAddLimit);
|
|
|
|
|
|
let curentVigour = gg.data.getItemNum(ItemNumType.vigour);
|
|
|
|
|
|
// 已满:不进入倒计时,返回满间隔,避免 lastAddTime=0 时用 (curTime-0) 算出约 -1775151515 这类无意义值
|
|
|
|
|
|
if (curentVigour >= maxVigour) {
|
|
|
|
|
|
return maxTime;
|
|
|
|
|
|
}
|
|
|
|
|
|
// 未满但锚点未初始化或异常(0、过小、非毫秒级时间戳):写入当前时刻,从满一个周期开始计
|
|
|
|
|
|
const anchorOk = lastAddTime > 1e11 && lastAddTime <= curTime + 60000;
|
|
|
|
|
|
if (!anchorOk) {
|
|
|
|
|
|
gg.data.setStatistics(StatisticsType.AutoAddVigourTime, curTime);
|
|
|
|
|
|
return maxTime;
|
|
|
|
|
|
}
|
|
|
|
|
|
let time = maxTime - Math.floor((curTime - lastAddTime) / 1000);
|
|
|
|
|
|
return time;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**验证是否显示体力恢复时间 */
|
|
|
|
|
|
checkShowAddVigourTime() {
|
|
|
|
|
|
let time = this.getAddVigourTime();
|
|
|
|
|
|
let maxVigour = gg.data.table.getConst(ConstNumType.VigourAutoAddLimit);
|
|
|
|
|
|
let curentVigour = gg.data.getItemNum(ItemNumType.vigour);
|
|
|
|
|
|
return time <= 0 && curentVigour < maxVigour;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**自动恢复体力 */
|
|
|
|
|
|
autoAddVigour() {
|
|
|
|
|
|
let time = this.getAddVigourTime();
|
|
|
|
|
|
//console.log("自动恢复体力时间", time);
|
|
|
|
|
|
let maxVigour = gg.data.table.getConst(ConstNumType.VigourAutoAddLimit);
|
|
|
|
|
|
let maxTime = gg.data.table.getConst(ConstNumType.VigourAutoAddTime);
|
|
|
|
|
|
let curentVigour = gg.data.getItemNum(ItemNumType.vigour);
|
|
|
|
|
|
if (time <= 0 && curentVigour < maxVigour) {
|
|
|
|
|
|
let count = Math.floor(Math.abs(time) / maxTime) + 1;
|
|
|
|
|
|
if (count + curentVigour > maxVigour) count = maxVigour - curentVigour;
|
|
|
|
|
|
console.log("自动恢复体力", count);
|
|
|
|
|
|
gg.data.addItemNum(ItemNumType.vigour, count);
|
|
|
|
|
|
gg.data.setStatistics(StatisticsType.AutoAddVigourTime, gg.data.getCurentTime());
|
|
|
|
|
|
// gg.data.saveToServer();
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
//#endregion
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////----------------武器相关-------------------///////////////////////
|
|
|
|
|
|
//#region 武器
|
|
|
|
|
|
/**武器配置map索引 */
|
|
|
|
|
|
private _weaponDataMap: Map<number, ITableWeapon2> = new Map();
|
|
|
|
|
|
/**获取所有武器ID */
|
|
|
|
|
|
private _weaponDataList: ITableWeapon2[] = [];
|
|
|
|
|
|
/**获取所有武器ID */
|
|
|
|
|
|
private _weaponIds: number[] = [];
|
|
|
|
|
|
/**获取所有武器数据 */
|
|
|
|
|
|
getWeaponDataList() {
|
|
|
|
|
|
if (this._weaponDataList.length <= 0) {
|
|
|
|
|
|
this._weaponDataList = gg.data.table.getTableList<ITableWeapon2>(TableNames.Weapon2);
|
|
|
|
|
|
}
|
|
|
|
|
|
return this._weaponDataList;
|
|
|
|
|
|
}
|
|
|
|
|
|
/**根据类型获取武器数据 */
|
|
|
|
|
|
getWeaponListByType(type: number) {
|
|
|
|
|
|
return this.getWeaponDataList().filter(item => item.type == type);
|
|
|
|
|
|
}
|
|
|
|
|
|
/**根据武器id获取武器数据 */
|
|
|
|
|
|
getWeaponDataByWeaponId(weaponId: number) {
|
|
|
|
|
|
return this.getWeaponDataList().find(item => item.weaponid == weaponId);
|
|
|
|
|
|
}
|
|
|
|
|
|
/**获取所有武器ID */
|
|
|
|
|
|
getWeaponIds() {
|
|
|
|
|
|
if (this._weaponIds.length <= 0) {
|
|
|
|
|
|
this._weaponIds = this.getWeaponDataList().map(item => item.weaponid);
|
|
|
|
|
|
}
|
|
|
|
|
|
return this._weaponIds;
|
|
|
|
|
|
}
|
|
|
|
|
|
/**获取武器数据 */
|
|
|
|
|
|
getWeaponData(weaponId: number) {
|
|
|
|
|
|
if (this._weaponDataMap.size <= 0) {
|
|
|
|
|
|
let arr = this.getWeaponDataList();
|
|
|
|
|
|
for (let i = 0; i < arr.length; i++) {
|
|
|
|
|
|
let item = arr[i];
|
|
|
|
|
|
this._weaponDataMap.set(item.weaponid, item);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
let weaponData = this._weaponDataMap.get(weaponId);
|
|
|
|
|
|
return weaponData;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**获取已经拥有的武器id数组 */
|
|
|
|
|
|
getHaveWeaponIds() {
|
|
|
|
|
|
let weaponIds = gg.data.getArrayData(OtherDataType.HaveWeapon);
|
|
|
|
|
|
return weaponIds.map(item => Number(item));
|
|
|
|
|
|
}
|
|
|
|
|
|
/**设置已拥有的武器id数组 */
|
|
|
|
|
|
setHaveWeaponIds(weaponIds: any[]) {
|
|
|
|
|
|
gg.data.setArrayData(OtherDataType.HaveWeapon, weaponIds);
|
|
|
|
|
|
}
|
|
|
|
|
|
/**获取已经拥有的武器数据列表 */
|
|
|
|
|
|
getHaveWeaponDataList() {
|
|
|
|
|
|
let haveWeaponIds = this.getHaveWeaponIds();
|
|
|
|
|
|
let haveWeaponDataList = haveWeaponIds.map(item => this.getWeaponData(Number(item)));
|
|
|
|
|
|
return haveWeaponDataList;
|
|
|
|
|
|
}
|
|
|
|
|
|
/**获取未拥有的武器数据列表 */
|
|
|
|
|
|
getNoHaveWeaponDataList() {
|
|
|
|
|
|
let haveWeaponIds = this.getHaveWeaponIds();
|
|
|
|
|
|
let weaponIds = this.getWeaponIds();
|
|
|
|
|
|
let noHaveWeaponIds = weaponIds.filter(item => haveWeaponIds.indexOf(item) < 0);
|
|
|
|
|
|
//排除101和102的武器,因为这两个武器没有升级和技能
|
|
|
|
|
|
//noHaveWeaponIds = noHaveWeaponIds.filter(item => item != 101 && item != 102);
|
|
|
|
|
|
//排除isshow==1的武器
|
|
|
|
|
|
noHaveWeaponIds = noHaveWeaponIds.filter(item => this.getWeaponData(Number(item)).isshow != 1);
|
|
|
|
|
|
let noHaveWeaponDataList = noHaveWeaponIds.map(item => this.getWeaponData(Number(item)));
|
|
|
|
|
|
return noHaveWeaponDataList;
|
|
|
|
|
|
}
|
|
|
|
|
|
/**解锁武器 */
|
|
|
|
|
|
unlockWeapon(weaponId: number) {
|
|
|
|
|
|
let haveWeaponIds = this.getHaveWeaponIds();
|
|
|
|
|
|
if (haveWeaponIds.indexOf(weaponId) < 0) {
|
|
|
|
|
|
haveWeaponIds.push(weaponId);
|
|
|
|
|
|
}
|
|
|
|
|
|
this.setHaveWeaponIds(haveWeaponIds);
|
|
|
|
|
|
}
|
|
|
|
|
|
/**解除拥有武器 */
|
|
|
|
|
|
removeHaveWeapon(weaponId: number) {
|
|
|
|
|
|
let haveWeaponIds = this.getHaveWeaponIds();
|
|
|
|
|
|
let index = haveWeaponIds.indexOf(weaponId);
|
|
|
|
|
|
if (index >= 0) {
|
|
|
|
|
|
haveWeaponIds.splice(index, 1);
|
|
|
|
|
|
}
|
|
|
|
|
|
this.setHaveWeaponIds(haveWeaponIds);
|
|
|
|
|
|
}
|
|
|
|
|
|
/**判断武器是否解锁 */
|
|
|
|
|
|
isWeaponUnlocked(weaponId: number) {
|
|
|
|
|
|
let haveWeaponIds = this.getHaveWeaponIds();
|
|
|
|
|
|
return haveWeaponIds.indexOf(weaponId) >= 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**获取武器等级 */
|
|
|
|
|
|
getWeaponLevel(weaponId: number) {
|
|
|
|
|
|
let level = 1;
|
|
|
|
|
|
let weaponLevel = gg.data.getkeyKeyValue(OtherDataType.WeaponLevel, weaponId);
|
|
|
|
|
|
level = weaponLevel && weaponLevel != "" ? parseInt(weaponLevel) : 1;
|
|
|
|
|
|
return level;
|
|
|
|
|
|
}
|
|
|
|
|
|
/**设置武器等级 */
|
|
|
|
|
|
setWeaponLevel(weaponId: number, level: number) {
|
|
|
|
|
|
gg.data.setkeyKeyValue(OtherDataType.WeaponLevel, weaponId, level);
|
|
|
|
|
|
}
|
|
|
|
|
|
/**武器升级 */
|
|
|
|
|
|
upgradeWeapon(weaponId: number) {
|
|
|
|
|
|
let level = this.getWeaponLevel(weaponId);
|
|
|
|
|
|
level++;
|
|
|
|
|
|
this.setWeaponLevel(weaponId, level);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**获取上阵武器状态 */
|
|
|
|
|
|
getEquipWeaponStatus() {
|
|
|
|
|
|
let arr = gg.data.getArrayData(OtherDataType.EquipWeapon);
|
|
|
|
|
|
return arr.map((item) => Number(item));
|
|
|
|
|
|
}
|
|
|
|
|
|
/**设置上阵武器状态,目前8个状态位,例如:[1,2,3,4,5,6,7,8] */
|
|
|
|
|
|
setEquipWeaponStatus(weaponIds: number[]) {
|
|
|
|
|
|
gg.data.setArrayData(OtherDataType.EquipWeapon, weaponIds);
|
|
|
|
|
|
}
|
|
|
|
|
|
/**上阵武器 */
|
|
|
|
|
|
equipWeapon(weaponId: number, index: number) {
|
|
|
|
|
|
let equipWeapons = this.getEquipWeaponStatus();
|
|
|
|
|
|
if (equipWeapons.length <= index) {
|
|
|
|
|
|
equipWeapons.push(weaponId);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
equipWeapons[index] = weaponId;
|
|
|
|
|
|
}
|
|
|
|
|
|
this.setEquipWeaponStatus(equipWeapons);
|
|
|
|
|
|
}
|
|
|
|
|
|
/**下阵武器 */
|
|
|
|
|
|
unequipWeapon(index: number) {
|
|
|
|
|
|
let equipWeapons = this.getEquipWeaponStatus();
|
|
|
|
|
|
if (equipWeapons.length > index) {
|
|
|
|
|
|
equipWeapons[index] = 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
this.setEquipWeaponStatus(equipWeapons);
|
|
|
|
|
|
}
|
|
|
|
|
|
/**获取上阵武器id */
|
|
|
|
|
|
getEquipWeaponId(index: number) {
|
|
|
|
|
|
let equipWeapons = this.getEquipWeaponStatus();
|
|
|
|
|
|
if (equipWeapons.length > index) {
|
|
|
|
|
|
return equipWeapons[index];
|
|
|
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
/**判断武器是否已经上阵
|
|
|
|
|
|
* 返回上阵索引位置
|
|
|
|
|
|
*/
|
|
|
|
|
|
isWeaponEquipped(weaponId: number) {
|
|
|
|
|
|
let equipWeapons = this.getEquipWeaponStatus();
|
|
|
|
|
|
return equipWeapons.indexOf(weaponId);
|
|
|
|
|
|
}
|
|
|
|
|
|
/**获取所有已经上阵的武器 */
|
|
|
|
|
|
getEquipWeaponIds() {
|
|
|
|
|
|
let equipWeapons = this.getEquipWeaponStatus();
|
|
|
|
|
|
return equipWeapons.filter((item) => item > 0);
|
|
|
|
|
|
}
|
|
|
|
|
|
/**获取所有已经上阵的武器数据列表 */
|
|
|
|
|
|
getEquippedWeaponDataList() {
|
|
|
|
|
|
let equipWeapons = this.getEquipWeaponIds();
|
|
|
|
|
|
let equipWeaponDataList = equipWeapons.map(item => this.getWeaponData(item));
|
|
|
|
|
|
return equipWeaponDataList;
|
|
|
|
|
|
}
|
|
|
|
|
|
/**获取空位 */
|
|
|
|
|
|
getEmptyPos() {
|
|
|
|
|
|
let equipWeapons = this.getEquipWeaponStatus();
|
|
|
|
|
|
let index = equipWeapons.indexOf(0);
|
|
|
|
|
|
return index;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 获取level等级的level_para配置的level_para参数
|
|
|
|
|
|
* @param id 武器id
|
|
|
|
|
|
* @param level 武器等级
|
|
|
|
|
|
* @returns
|
|
|
|
|
|
*/
|
|
|
|
|
|
public getLevelParaByWeaponLevel(level: number, weaponId: number) {
|
|
|
|
|
|
let list = gg.data.table.getTableList<ITableWeapon3>(TableNames.Weapon3);
|
|
|
|
|
|
let config = list.find(x => x.id == level)
|
|
|
|
|
|
if (config) {
|
|
|
|
|
|
return config.level_para
|
|
|
|
|
|
}
|
|
|
|
|
|
console.log('获取level_para参数失败==level=', weaponId, level)
|
|
|
|
|
|
return 0
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**根据武器id数组获取所有已经解锁的技能 */
|
|
|
|
|
|
public getAllUnlockedSkillByWeaponIds(weaponIds: ITableWeapon2[]) {
|
|
|
|
|
|
let skillIdArr: number[] = []
|
|
|
|
|
|
for (let weapon of weaponIds) {
|
|
|
|
|
|
let arr = weapon.base_skill.split(',').map(item => parseInt(item));
|
|
|
|
|
|
skillIdArr.push(...arr);
|
|
|
|
|
|
skillIdArr.push(...this.getAllCanUseSkillByWeaponLevel(weapon.weaponid, this.getWeaponLevel(weapon.weaponid)))
|
|
|
|
|
|
}
|
|
|
|
|
|
let resull: ITableSkill[] = [];
|
|
|
|
|
|
for (let skillId of skillIdArr) {
|
|
|
|
|
|
resull.push(gg.data.table.getTableData<ITableSkill>(TableNames.Skill, skillId))
|
|
|
|
|
|
}
|
|
|
|
|
|
return resull
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 根据武器等级获取可用的所有技能
|
|
|
|
|
|
* @param id 武器id
|
|
|
|
|
|
* @param level 武器等级
|
|
|
|
|
|
*/
|
|
|
|
|
|
public getAllCanUseSkillByWeaponLevel(id: number, level: number) {
|
|
|
|
|
|
let config = this.getWeaponData(id);
|
|
|
|
|
|
// //如果当前模式是武器模式获取所有武器技能书
|
|
|
|
|
|
// let weaponArray = gg.data.getCurWeaponModelChapterId(gg.game.CurentSelectChapterId)
|
|
|
|
|
|
// if (weaponArray && weaponArray.length > 0) {
|
|
|
|
|
|
// //设置技能等级7级就会获取所有的技能
|
|
|
|
|
|
// level = 7
|
|
|
|
|
|
// }
|
|
|
|
|
|
// //宝箱模式
|
|
|
|
|
|
// if (gg.game.CurentSelectBattleType == BattleType.BoxMode) {
|
|
|
|
|
|
// level = 11
|
|
|
|
|
|
// }
|
|
|
|
|
|
let skillIdArr = []
|
|
|
|
|
|
//base_skill技能都可以使用, skill技能通过比较等级等级之前的都需要加入, 1,52;2,48;2,49;2,50;3,53;4,54;5,55;6,56
|
|
|
|
|
|
|
|
|
|
|
|
if (config.base_skill.length > 0) {
|
|
|
|
|
|
let t = config.base_skill.split(',')
|
|
|
|
|
|
for (let vaule of t) {
|
|
|
|
|
|
skillIdArr.push(parseInt(vaule))
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (config.skill.length > 0) {
|
|
|
|
|
|
let skillArray = config.skill.split(';')
|
|
|
|
|
|
for (let skill of skillArray) {
|
|
|
|
|
|
let t = skill.split(',')
|
|
|
|
|
|
if (level >= parseInt(t[0])) {
|
|
|
|
|
|
skillIdArr.push(parseInt(t[1]))
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//去重
|
|
|
|
|
|
skillIdArr = skillIdArr.filter((item, index) => skillIdArr.indexOf(item) === index);
|
|
|
|
|
|
//console.log('去重之后的所有可用技能==', skillIdArr)
|
|
|
|
|
|
return skillIdArr
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//通过武器等级获取当前升级需要的武器碎片
|
|
|
|
|
|
public getWeaponFlagNumByLevel(weaponId: number) {
|
|
|
|
|
|
let level = this.getWeaponLevel(weaponId)
|
|
|
|
|
|
let conf = this.getWeaponData(weaponId)
|
|
|
|
|
|
let item_num = conf.item_num
|
|
|
|
|
|
//武器的配置item_num分组获取武器升级需要的碎片
|
|
|
|
|
|
|
|
|
|
|
|
if (item_num) {
|
|
|
|
|
|
let list = gg.data.table.getTableList<ITableWeapon4>(TableNames.Weapon4);
|
|
|
|
|
|
let data = list.find(x => x.id == level)
|
|
|
|
|
|
if (item_num == 1) {
|
|
|
|
|
|
return data.item_num1
|
|
|
|
|
|
} else if (item_num == 2) {
|
|
|
|
|
|
return data.item_num2
|
|
|
|
|
|
} else if (item_num == 3) {
|
|
|
|
|
|
return data.item_num3
|
|
|
|
|
|
} else if (item_num == 4) {
|
|
|
|
|
|
return data.item_num4
|
|
|
|
|
|
} else {
|
|
|
|
|
|
console.log('item_num 未配置,返回默认配置', weaponId, item_num)
|
|
|
|
|
|
return data.item_num1
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
return -1
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**获取紫色及以上武器最大等级 */
|
|
|
|
|
|
public getMaxLevelPurpleWeapon() {
|
|
|
|
|
|
let list = this.getWeaponDataList().filter(x => x.quality >= 3);
|
|
|
|
|
|
let maxlevel = 1;
|
|
|
|
|
|
let haveArr = this.getHaveWeaponDataList();
|
|
|
|
|
|
for (let i = 0; i < list.length; i++) {
|
|
|
|
|
|
if (haveArr.find(x => x.weaponid == list[i].weaponid) == null) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
let level = this.getWeaponLevel(list[i].weaponid);
|
|
|
|
|
|
if (level > maxlevel) {
|
|
|
|
|
|
maxlevel = level;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (DEV || DEBUG || EDITOR)
|
|
|
|
|
|
console.log('紫色及以上武器最大等级==', maxlevel);
|
|
|
|
|
|
return maxlevel
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/*获取已装备的武器id和等级*/
|
|
|
|
|
|
public getEquippedWeaponIdAndLevel() {
|
|
|
|
|
|
let arr = gg.data.project.getHaveWeaponDataList();
|
|
|
|
|
|
let result: number[][] = [];
|
|
|
|
|
|
for (let i = 0; i < arr.length; i++) {
|
|
|
|
|
|
let item = arr[i];
|
|
|
|
|
|
let weaponId = parseInt(item[0]);
|
|
|
|
|
|
let weaponLevel = parseInt(item[1]);
|
|
|
|
|
|
result.push([weaponId, weaponLevel]);
|
|
|
|
|
|
}
|
|
|
|
|
|
return result;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**获取武器最大等级 */
|
|
|
|
|
|
public getMaxLevelWeapon() {
|
|
|
|
|
|
let list = this.getWeaponDataList()
|
|
|
|
|
|
let maxlevel = 1;
|
|
|
|
|
|
let haveArr = this.getHaveWeaponDataList();
|
|
|
|
|
|
for (let i = 0; i < list.length; i++) {
|
|
|
|
|
|
if (haveArr.find(x => x.weaponid == list[i].weaponid) == null) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
let level = this.getWeaponLevel(list[i].weaponid);
|
|
|
|
|
|
if (level > maxlevel) {
|
|
|
|
|
|
maxlevel = level;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (DEV || DEBUG || EDITOR)
|
|
|
|
|
|
console.log('当前武器最大等级==', maxlevel);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
return maxlevel
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//指定品质武器
|
|
|
|
|
|
public getMaxLevelWeaponByQuality(quality: number) {
|
|
|
|
|
|
let list = this.getWeaponDataList().filter(x => x.quality == quality);
|
|
|
|
|
|
let maxlevel = 1;
|
|
|
|
|
|
let haveArr = this.getHaveWeaponDataList();
|
|
|
|
|
|
for (let i = 0; i < list.length; i++) {
|
|
|
|
|
|
if (haveArr.find(x => x.weaponid == list[i].weaponid) == null) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
let level = this.getWeaponLevel(list[i].weaponid);
|
|
|
|
|
|
if (level > maxlevel) {
|
|
|
|
|
|
maxlevel = level;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (DEV || DEBUG || EDITOR)
|
|
|
|
|
|
console.log('指定品质武器最大等级==', maxlevel);
|
|
|
|
|
|
return maxlevel
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//#endregion
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////----------------闯关相关-------------------///////////////////////
|
|
|
|
|
|
//#region 闯关
|
|
|
|
|
|
/**根据章节ID、难度和宝箱索引设置宝箱奖励领取状态:0-未达成,1-已达成未领取,2-已达成已领取 */
|
|
|
|
|
|
setChapterStatus(chapterId: number, difficulty: ChapterDifficulty, index: number, status: number) {
|
|
|
|
|
|
gg.data.setkeyKeyValue(OtherDataType.ChapterBoxStatus, chapterId + '&' + difficulty + '&' + index, status);
|
|
|
|
|
|
gg.targetTaskManager.checkTask()//任务领取状态依赖其他模块不需要统计数据
|
|
|
|
|
|
}
|
|
|
|
|
|
/**根据章节ID、难度和宝箱索引获取宝箱奖励领取状态:0-未达成,1-已达成未领取,2-已达成已领取 */
|
|
|
|
|
|
getChapterStatus(chapterId: number, difficulty: ChapterDifficulty, index: number) {
|
|
|
|
|
|
let status = gg.data.getkeyKeyValue(OtherDataType.ChapterBoxStatus, chapterId + '&' + difficulty + '&' + index);
|
|
|
|
|
|
if (status == '') {
|
|
|
|
|
|
status = "0";
|
|
|
|
|
|
}
|
|
|
|
|
|
return Number(status);
|
|
|
|
|
|
}
|
|
|
|
|
|
/**根据章节ID、难度和章节进度保存状态(仅更新 25%/50% 宝箱;100% 宝箱与通关共用 index=2,仅胜利时写入) */
|
|
|
|
|
|
setChapterStatusByProgress(chapterId: number, difficulty: ChapterDifficulty, progress: number) {
|
|
|
|
|
|
let targetProgress = this.getChapterBoxProgress(chapterId, difficulty);
|
|
|
|
|
|
// index=2(100%)与 isChapterComplete 共用,禁止在失败/暂停进度里提前写入
|
|
|
|
|
|
for (let i = 0; i < 2; i++) {
|
|
|
|
|
|
let curentStatus = this.getChapterStatus(chapterId, difficulty, i);
|
|
|
|
|
|
if (curentStatus == 0) {
|
|
|
|
|
|
if (progress >= targetProgress[i]) {
|
|
|
|
|
|
this.setChapterStatus(chapterId, difficulty, i, 1);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
/**根据章节获取配置的宝箱进度百分比 */
|
|
|
|
|
|
getChapterBoxProgress(chapterId: number, difficulty: ChapterDifficulty) {
|
|
|
|
|
|
return [0.25, 0.5, 1];
|
|
|
|
|
|
}
|
|
|
|
|
|
/**根据章节id,难度判断是否可以挑战 */
|
|
|
|
|
|
isChapterCanChallenge(chapterId: number, difficulty: ChapterDifficulty) {
|
|
|
|
|
|
let curentId = this.getCurentLevelChapterId(difficulty);
|
|
|
|
|
|
if (curentId == 0) return false;
|
|
|
|
|
|
if (difficulty == ChapterDifficulty.Hard) {
|
|
|
|
|
|
let id1 = this.getCurentLevelChapterId(ChapterDifficulty.Normal);
|
|
|
|
|
|
let id2 = this.getCurentLevelChapterId(ChapterDifficulty.Hard);
|
|
|
|
|
|
return chapterId <= id1 - 1 && chapterId <= id2;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (difficulty == ChapterDifficulty.Hell) {
|
|
|
|
|
|
let id1 = this.getCurentLevelChapterId(ChapterDifficulty.Hard);
|
|
|
|
|
|
let id2 = this.getCurentLevelChapterId(ChapterDifficulty.Hell);
|
|
|
|
|
|
return chapterId <= id1 - 1 && chapterId <= id2;
|
|
|
|
|
|
}
|
|
|
|
|
|
return curentId >= chapterId;
|
|
|
|
|
|
}
|
|
|
|
|
|
/**根据章节id和难度判断是否已通关 */
|
|
|
|
|
|
isChapterComplete(chapterId: number, difficulty: ChapterDifficulty) {
|
|
|
|
|
|
if (difficulty == ChapterDifficulty.Normal) {
|
|
|
|
|
|
return chapterId < gg.data.doc.chapter;
|
|
|
|
|
|
}
|
|
|
|
|
|
let status = this.getChapterStatus(chapterId, difficulty, 2);
|
|
|
|
|
|
return status > 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 校正普通难度章节进度(仅修复历史脏数据:胜利写入过 index=2 但 chapter 未+1)。
|
|
|
|
|
|
* 不再用 isChapterComplete 判断,避免把「失败满进度」误判为已通关。
|
|
|
|
|
|
*/
|
|
|
|
|
|
syncNormalChapterProgress(): boolean {
|
|
|
|
|
|
const maxId = this.getMaxChapterId();
|
|
|
|
|
|
let changed = false;
|
|
|
|
|
|
while (gg.data.doc.chapter < maxId
|
|
|
|
|
|
&& this.getChapterStatus(gg.data.doc.chapter, ChapterDifficulty.Normal, 2) > 0) {
|
|
|
|
|
|
gg.data.doc.chapter += 1;
|
|
|
|
|
|
this.setCurSelectSaveChapterId(gg.data.doc.chapter);
|
|
|
|
|
|
changed = true;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (changed) {
|
|
|
|
|
|
gg.game.CurentSelectChapterId = gg.data.doc.chapter;
|
|
|
|
|
|
console.log('[ConfigProjectData] syncNormalChapterProgress -> chapter', gg.data.doc.chapter);
|
|
|
|
|
|
}
|
|
|
|
|
|
return changed;
|
|
|
|
|
|
}
|
|
|
|
|
|
/**根据章节id和难度判断是否已领取 */
|
|
|
|
|
|
isChapterFinishGet(chapterId: number, difficulty: ChapterDifficulty) {
|
|
|
|
|
|
let status = this.getChapterStatus(chapterId, difficulty, 2);
|
|
|
|
|
|
return status > 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
/**根据章节id和难度判断是否可领取宝箱奖励 */
|
|
|
|
|
|
isCanGetChapterBox(chapterId: number, difficulty: ChapterDifficulty, index: number) {
|
|
|
|
|
|
let status = this.getChapterStatus(chapterId, difficulty, index);
|
|
|
|
|
|
return status == 1;
|
|
|
|
|
|
}
|
|
|
|
|
|
/**是否有关卡宝箱可领取 */
|
|
|
|
|
|
isCanGetLevelBox() {
|
|
|
|
|
|
let chapters = gg.data.table.getTableList<ITableChapter>(TableNames.Chapter).filter(x => x.type == BattleType.Level);
|
|
|
|
|
|
for (let i = 0; i < chapters.length; i++) {
|
|
|
|
|
|
let chapter = chapters[i];
|
|
|
|
|
|
for (let j = 0; j < 3; j++) {
|
|
|
|
|
|
let status = this.getChapterStatus(chapter.id, ChapterDifficulty.Normal, j);
|
|
|
|
|
|
if (status == 1) return true;
|
|
|
|
|
|
status = this.getChapterStatus(chapter.id, ChapterDifficulty.Hard, j);
|
|
|
|
|
|
if (status == 1) return true;
|
|
|
|
|
|
status = this.getChapterStatus(chapter.id, ChapterDifficulty.Hell, j);
|
|
|
|
|
|
if (status == 1) return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
if (chapter.id == gg.data.doc.chapter) break;
|
|
|
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
/**根据当前章节判断前面或者后面是否有可领取的宝箱奖励 */
|
|
|
|
|
|
isCanGetBoxBeforeOrAfter(chapterId: number, difficulty: ChapterDifficulty, isBefore: boolean) {
|
|
|
|
|
|
let chapters = gg.data.table.getTableList<ITableChapter>(TableNames.Chapter).filter(x => x.type == BattleType.Level);
|
|
|
|
|
|
if (isBefore) chapters = chapters.filter(x => x.id < chapterId);
|
|
|
|
|
|
else chapters = chapters.filter(x => x.id > chapterId && x.id < gg.data.doc.chapter);
|
|
|
|
|
|
for (let i = 0; i < chapters.length; i++) {
|
|
|
|
|
|
let chapter = chapters[i];
|
|
|
|
|
|
let status = this.getChapterStatus(chapter.id, difficulty, 0);
|
|
|
|
|
|
if (status == 1) return true;
|
|
|
|
|
|
status = this.getChapterStatus(chapter.id, difficulty, 1);
|
|
|
|
|
|
if (status == 1) return true;
|
|
|
|
|
|
status = this.getChapterStatus(chapter.id, difficulty, 2);
|
|
|
|
|
|
if (status == 1) return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
/**获取当前选择的章节ID */
|
|
|
|
|
|
getCurSelectSaveChapterId() {
|
|
|
|
|
|
return gg.data.getStatus(StatusType.CurSelectSaveChapterId);
|
|
|
|
|
|
}
|
|
|
|
|
|
/**设置当前选择的章节ID */
|
|
|
|
|
|
setCurSelectSaveChapterId(chapterId: number) {
|
|
|
|
|
|
gg.data.setStatus(StatusType.CurSelectSaveChapterId, chapterId);
|
|
|
|
|
|
}
|
|
|
|
|
|
/**获取当前选中的章节难度 */
|
|
|
|
|
|
getCurSelectSaveChapterDifficulty() {
|
|
|
|
|
|
return gg.data.getStatus(StatusType.CurSelectSaveChapterDifficulty);
|
|
|
|
|
|
}
|
|
|
|
|
|
/**设置当前选中的章节难度 */
|
|
|
|
|
|
setCurSelectSaveChapterDifficulty(difficulty: ChapterDifficulty) {
|
|
|
|
|
|
gg.data.setStatus(StatusType.CurSelectSaveChapterDifficulty, difficulty);
|
|
|
|
|
|
}
|
|
|
|
|
|
/**根据难度获取当前闯关的章节id */
|
|
|
|
|
|
getCurentLevelChapterId(difficulty: ChapterDifficulty) {
|
|
|
|
|
|
if (difficulty == ChapterDifficulty.Normal) return gg.data.doc.chapter;
|
|
|
|
|
|
if (difficulty !== ChapterDifficulty.Hard && difficulty !== ChapterDifficulty.Hell) return 0;
|
|
|
|
|
|
let chapterConfigList = this.getChapterConfigByType(BattleType.Level);
|
|
|
|
|
|
let chapterId = 0;
|
|
|
|
|
|
for (let i = chapterConfigList.length - 1; i >= 0; i--) {
|
|
|
|
|
|
let chapterConfig = chapterConfigList[i];
|
|
|
|
|
|
if (this.isChapterComplete(chapterConfig.id, difficulty)) {
|
|
|
|
|
|
if (difficulty == ChapterDifficulty.Hard) {
|
|
|
|
|
|
if (this.isChapterComplete(chapterConfig.id, ChapterDifficulty.Normal)) {
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else if (difficulty == ChapterDifficulty.Hell) {
|
|
|
|
|
|
if (this.isChapterComplete(chapterConfig.id, ChapterDifficulty.Hard)) {
|
|
|
|
|
|
break;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
else break;
|
|
|
|
|
|
}
|
|
|
|
|
|
// if (i == 0)
|
|
|
|
|
|
// chapterId = 1;
|
|
|
|
|
|
// else
|
|
|
|
|
|
chapterId = chapterConfig.id;
|
|
|
|
|
|
}
|
|
|
|
|
|
return chapterId;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**根据章节id和难度获取连续失败次数 */
|
|
|
|
|
|
getFailCount(chapterId: number, type: BattleType, difficulty: ChapterDifficulty) {
|
|
|
|
|
|
let failCount = 0;
|
|
|
|
|
|
let str = "";
|
|
|
|
|
|
if (type == BattleType.Level) {
|
|
|
|
|
|
str = gg.data.getkeyKeyValue(OtherDataType.FailContinuousCount, `l${chapterId}&${type}&${difficulty}`);
|
|
|
|
|
|
} else {
|
|
|
|
|
|
str = gg.data.getkeyKeyValue(OtherDataType.FailContinuousCount, `r${type}&${difficulty}`);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (str == null || str == "") failCount = 0;
|
|
|
|
|
|
else failCount = parseInt(str);
|
|
|
|
|
|
return failCount;
|
|
|
|
|
|
}
|
|
|
|
|
|
/**设置连续失败次数 */
|
|
|
|
|
|
setFailCount(chapterId: number, type: BattleType, difficulty: ChapterDifficulty, failCount: number) {
|
|
|
|
|
|
let str = "";
|
|
|
|
|
|
if (type == BattleType.Level) {
|
|
|
|
|
|
str = `l${chapterId}&${type}&${difficulty}`;
|
|
|
|
|
|
} else {
|
|
|
|
|
|
str = `r${type}&${difficulty}`;
|
|
|
|
|
|
}
|
|
|
|
|
|
gg.data.setkeyKeyValue(OtherDataType.FailContinuousCount, str, failCount.toString());
|
|
|
|
|
|
}
|
|
|
|
|
|
/**增加失败次数 */
|
|
|
|
|
|
addFailCount(chapterId: number, type: BattleType, difficulty: ChapterDifficulty) {
|
|
|
|
|
|
let failCount = this.getFailCount(chapterId, type, difficulty);
|
|
|
|
|
|
failCount++;
|
|
|
|
|
|
console.log('增加失败次数', failCount);
|
|
|
|
|
|
|
|
|
|
|
|
this.setFailCount(chapterId, type, difficulty, failCount);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**根据章节id,难度获取通关奖励字符串 */
|
|
|
|
|
|
getChapterRewardStr(chapterId: number, difficulty: ChapterDifficulty) {
|
|
|
|
|
|
let chapterConfig = this.getChapterConfig(chapterId);
|
|
|
|
|
|
if (chapterConfig) {
|
|
|
|
|
|
return chapterConfig.reward
|
|
|
|
|
|
}
|
|
|
|
|
|
return '';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**根据章节id,难度,宝箱索引获取宝箱奖励字符串 */
|
|
|
|
|
|
getChapterBoxRewardStr(chapterId: number, difficulty: ChapterDifficulty, index: number) {
|
|
|
|
|
|
let chapterConfig = this.getChapterConfig(chapterId);
|
|
|
|
|
|
if (chapterConfig) {
|
|
|
|
|
|
let difficultIndex = chapterConfig.difficult;
|
|
|
|
|
|
if (difficultIndex < 0) difficultIndex = 0;
|
|
|
|
|
|
let rewardStr = '';
|
|
|
|
|
|
if (index == 1) rewardStr = chapterConfig.box_reward1;
|
|
|
|
|
|
else if (index == 2) rewardStr = chapterConfig.box_reward2;
|
|
|
|
|
|
else if (index == 3) rewardStr = chapterConfig.box_reward3;
|
|
|
|
|
|
if (rewardStr == null || rewardStr == "") return '';
|
|
|
|
|
|
// let rewardArr = rewardStr.split('#');
|
|
|
|
|
|
// return rewardArr[difficultIndex];
|
|
|
|
|
|
return rewardStr;
|
|
|
|
|
|
}
|
|
|
|
|
|
return '';
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//#endregion
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////----------------战斗相关-------------------///////////////////////
|
|
|
|
|
|
//#region 战斗
|
|
|
|
|
|
/**消耗体力 */
|
|
|
|
|
|
vigourCost: number = 3;
|
|
|
|
|
|
|
|
|
|
|
|
/**获取章节配置 */
|
|
|
|
|
|
getChapterConfig(chapterId: number) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
let chapterConfig = gg.data.table.getTableData<ITableChapter>(TableNames.Chapter, chapterId);
|
|
|
|
|
|
return chapterConfig;
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
/**获取最大章节id */
|
|
|
|
|
|
getMaxChapterId() {
|
|
|
|
|
|
try {
|
|
|
|
|
|
let chapterConfigList = gg.data.table.getTableList<ITableChapter>(TableNames.Chapter).filter(x => x.type == BattleType.Level);
|
|
|
|
|
|
//排除id大于1000的章节
|
|
|
|
|
|
chapterConfigList = chapterConfigList.filter(x => x.id <= 1000);
|
|
|
|
|
|
|
|
|
|
|
|
let maxId = chapterConfigList[chapterConfigList.length - 1].id;
|
|
|
|
|
|
return maxId;
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
/**根据类型获取章节配置列表 */
|
|
|
|
|
|
getChapterConfigByType(type: BattleType) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
let chapterConfigList = gg.data.table.getTableList<ITableChapter>(TableNames.Chapter).filter(x => x.type == type);
|
|
|
|
|
|
return chapterConfigList;
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**获取推荐战力 */
|
|
|
|
|
|
getRecommendPower(chapterId: number, difficulty: ChapterDifficulty) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
let chapterConfig = this.getChapterConfig(chapterId)
|
|
|
|
|
|
if (chapterConfig) {
|
|
|
|
|
|
//let index = 0;
|
|
|
|
|
|
return chapterConfig.power
|
|
|
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('获取章节推荐战力失败', error);
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**验证体力是否足够 */
|
|
|
|
|
|
checkVigourEnough(battleType: BattleType) {
|
|
|
|
|
|
let curentVigour = gg.data.getVigour();
|
|
|
|
|
|
let needVigour = this.vigourCost;
|
|
|
|
|
|
if (curentVigour < needVigour) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**判断战力是否足够 */
|
|
|
|
|
|
checkPowerEnough(chapterId: number, type: BattleType, difficulty: ChapterDifficulty) {
|
|
|
|
|
|
//判断战力是否足够
|
|
|
|
|
|
if (type == BattleType.Level) {
|
|
|
|
|
|
let curentPower = gg.data.getCurPower();
|
|
|
|
|
|
let needPower = this.getRecommendPower(chapterId, difficulty);
|
|
|
|
|
|
if (curentPower * 1.5 <= needPower && gg.data.doc.chapter > 9) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**根据章节id获取新机制数组 */
|
|
|
|
|
|
getNewPlayArr(chapterId: number) {
|
|
|
|
|
|
let chapterConfig = this.getChapterConfig(chapterId);
|
|
|
|
|
|
if (chapterConfig) {
|
|
|
|
|
|
let newPlayArr = chapterConfig.new_play.split(',').map((item) => Number(item));
|
|
|
|
|
|
return newPlayArr;
|
|
|
|
|
|
}
|
|
|
|
|
|
return [];
|
|
|
|
|
|
}
|
|
|
|
|
|
/**根据章节id获取新障碍物数组 */
|
|
|
|
|
|
getNewObstacleArr(chapterId: number) {
|
|
|
|
|
|
let chapterConfig = this.getChapterConfig(chapterId);
|
|
|
|
|
|
if (chapterConfig) {
|
|
|
|
|
|
let newObstacleArr = chapterConfig.obstacleId.split(',').map((item) => Number(item));
|
|
|
|
|
|
return newObstacleArr;
|
|
|
|
|
|
}
|
|
|
|
|
|
return [];
|
|
|
|
|
|
}
|
|
|
|
|
|
/**获取章节所有机制配置 */
|
|
|
|
|
|
getChapterAllMechanismConfig(chapterId: number) {
|
|
|
|
|
|
return []
|
|
|
|
|
|
// let newPlayArr = this.getNewPlayArr(chapterId);
|
|
|
|
|
|
// let mechanismConfigList = gg.data.table.getTableList<ITablePlaymode>(TableNames.Playmode).filter(x => newPlayArr.includes(x.id));
|
|
|
|
|
|
// return mechanismConfigList;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**获取章节所有障碍物配置 */
|
|
|
|
|
|
getChapterAllObstacleConfig(chapterId: number) {
|
|
|
|
|
|
return []
|
|
|
|
|
|
// let obstacleArr = this.getNewObstacleArr(chapterId);
|
|
|
|
|
|
// let obstacleConfigList = gg.data.table.getTableList<ITableObstacle>(TableNames.Obstacle).filter(x => obstacleArr.includes(x.id));
|
|
|
|
|
|
// return obstacleConfigList;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
private _monsterList: ITableMonster[] = [];
|
|
|
|
|
|
private _monsterMap: Map<number, ITableMonster> = new Map();
|
|
|
|
|
|
|
|
|
|
|
|
/**获取所有怪物配置 */
|
|
|
|
|
|
getAllMonsterList1() {
|
|
|
|
|
|
if (this._monsterList.length == 0) {
|
|
|
|
|
|
this._monsterList = gg.data.table.getTableList<ITableMonster>(TableNames.Monster);
|
|
|
|
|
|
}
|
|
|
|
|
|
return this._monsterList;
|
|
|
|
|
|
}
|
|
|
|
|
|
/**根据怪物id获取怪物数据 */
|
|
|
|
|
|
getMonsterDataByID(monsterId: number) {
|
|
|
|
|
|
if (this._monsterMap.size == 0) {
|
|
|
|
|
|
let monsterList = this.getAllMonsterList1();
|
|
|
|
|
|
for (let i = 0; i < monsterList.length; i++) {
|
|
|
|
|
|
const element = monsterList[i];
|
|
|
|
|
|
this._monsterMap.set(element.id, element);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return this._monsterMap.get(monsterId);
|
|
|
|
|
|
}
|
|
|
|
|
|
/**获取章节初始怪物组 */
|
|
|
|
|
|
getChapterInitMonsterGroup(chapterId: number, type: BattleType, difficulty: ChapterDifficulty) {
|
|
|
|
|
|
try {
|
|
|
|
|
|
let chapterConfig = this.getChapterConfig(chapterId)
|
|
|
|
|
|
if (chapterConfig) {
|
|
|
|
|
|
return chapterConfig.monster_group
|
|
|
|
|
|
}
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('获取章节初始怪物组失败', error);
|
|
|
|
|
|
return 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**获取当前章节用户用到的怪物id数组 */
|
|
|
|
|
|
getChapterUserMonsterIdArr(chapterId: number) {
|
|
|
|
|
|
let idArr = []
|
|
|
|
|
|
// 章节配置
|
|
|
|
|
|
let config: ITableChapter = this.getChapterConfig(chapterId)
|
|
|
|
|
|
|
|
|
|
|
|
idArr = idArr.concat(this.spawnMonsterCall1(config.monster1));
|
|
|
|
|
|
idArr = idArr.concat(this.spawnMonsterCall1(config.monster2_1));
|
|
|
|
|
|
idArr = idArr.concat(this.spawnMonsterCall1(config.monster2_2));
|
|
|
|
|
|
idArr = idArr.concat(this.spawnMonsterCall1(config.monster2_3));
|
|
|
|
|
|
idArr = idArr.concat(this.spawnMonsterCall1(config.monster2_4));
|
|
|
|
|
|
idArr = idArr.concat(this.spawnMonsterCall1(config.monster2_5));
|
|
|
|
|
|
|
|
|
|
|
|
idArr = idArr.concat(this.spawnMonsterCall2(config.monster3_1));
|
|
|
|
|
|
idArr = idArr.concat(this.spawnMonsterCall2(config.monster3_2));
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
if (gg.game.CurentSelectBattleType == BattleType.Level && gg.game.CurentSelectBattleDifficulty == ChapterDifficulty.Hell) {
|
|
|
|
|
|
idArr = idArr.concat(this.spawnMonsterCall2(config.monster3_3));
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// let haveMonsterArr = []
|
|
|
|
|
|
// if (gg.game.CurentSelectBattleType == BattleType.Level && gg.game.CurentSelectBattleDifficulty == ChapterDifficulty.Normal) {
|
|
|
|
|
|
// let array = config.monster3_4.split(',')
|
|
|
|
|
|
// haveMonsterArr = gg.zombie.getZombieNormalMonsterIdList()
|
|
|
|
|
|
// let id = Number(array[2])
|
|
|
|
|
|
// if (haveMonsterArr.indexOf(id) == -1) {
|
|
|
|
|
|
// idArr = idArr.concat(this.spawnMonsterCall2(config.monster3_4));
|
|
|
|
|
|
// }
|
|
|
|
|
|
// } else if (gg.game.CurentSelectBattleType == BattleType.Level && gg.game.CurentSelectBattleDifficulty == ChapterDifficulty.Hard) {
|
|
|
|
|
|
// let array = config.monster3_5.split(',')
|
|
|
|
|
|
// haveMonsterArr = gg.zombie.getZombieHardMonsterIdList()
|
|
|
|
|
|
// let id = Number(array[2])
|
|
|
|
|
|
// if (haveMonsterArr.indexOf(id) == -1) {
|
|
|
|
|
|
// idArr = idArr.concat(this.spawnMonsterCall2(config.monster3_5));
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// } else if (gg.game.CurentSelectBattleType == BattleType.Level && gg.game.CurentSelectBattleDifficulty == ChapterDifficulty.Hell) {
|
|
|
|
|
|
// let array = config.monster3_6.split(',')
|
|
|
|
|
|
// haveMonsterArr = gg.zombie.getZombieHellMonsterIdList()
|
|
|
|
|
|
// let id = Number(array[2])
|
|
|
|
|
|
// if (haveMonsterArr.indexOf(id) == -1) {
|
|
|
|
|
|
// idArr = idArr.concat(this.spawnMonsterCall2(config.monster3_6));
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
// }
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
idArr = idArr.distinct();
|
|
|
|
|
|
|
|
|
|
|
|
let monsterModelArr = []
|
|
|
|
|
|
for (let i = 0; i < idArr.length; i++) {
|
|
|
|
|
|
let id = idArr[i];
|
|
|
|
|
|
let monsterData = this.getMonsterDataByID(id);
|
|
|
|
|
|
if (monsterData && monsterModelArr.indexOf(monsterData.model) == -1) {
|
|
|
|
|
|
monsterModelArr.push(monsterData.model)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return monsterModelArr
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**解析出怪扣配置信息 */
|
|
|
|
|
|
spawnMonsterCall1(monsterStr: string) {
|
|
|
|
|
|
let idArr = []
|
|
|
|
|
|
if (monsterStr == '') {
|
|
|
|
|
|
return idArr
|
|
|
|
|
|
}
|
|
|
|
|
|
let t = monsterStr.split('|')
|
|
|
|
|
|
for (let i = 0; i < t.length; i++) {
|
|
|
|
|
|
if (t[i] == '') {
|
|
|
|
|
|
continue
|
|
|
|
|
|
}
|
|
|
|
|
|
let s = t[i].split(';')
|
|
|
|
|
|
for (let j = 0; j < s.length; j++) {
|
|
|
|
|
|
let id = s[j].split(',')[0]
|
|
|
|
|
|
if (idArr.indexOf(id) == -1) {
|
|
|
|
|
|
idArr.push(Number(id))
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return idArr
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
spawnMonsterCall2(monsterStr) {
|
|
|
|
|
|
let idArr = []
|
|
|
|
|
|
if (monsterStr == '') {
|
|
|
|
|
|
return idArr
|
|
|
|
|
|
}
|
|
|
|
|
|
let t = monsterStr.split('|')
|
|
|
|
|
|
for (let i = 0; i < t.length; i++) {
|
|
|
|
|
|
let id = t[i].split(',')[2]
|
|
|
|
|
|
|
|
|
|
|
|
if (gg.game.CurentSelectBattleType == BattleType.BoxMode
|
|
|
|
|
|
|| gg.game.CurentSelectBattleType == BattleType.GachaMode) {
|
|
|
|
|
|
if (id == 30011 || id == 40011) {
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (idArr.indexOf(id) == -1) {
|
|
|
|
|
|
idArr.push(Number(id))
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return idArr;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**根据难度值获取难度称呼 */
|
|
|
|
|
|
getDifficultyName(difficulty: number) {
|
|
|
|
|
|
switch (difficulty) {
|
|
|
|
|
|
case 1: return '简单';
|
|
|
|
|
|
case 2: return '普通';
|
|
|
|
|
|
case 3: return '困难';
|
|
|
|
|
|
case 4: return '地狱';
|
|
|
|
|
|
case 5: return '末世';
|
|
|
|
|
|
default: return '普通';
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**根据品质权重配置组id和危险系数获取章节权重配置 */
|
|
|
|
|
|
getChapterWeightConfig(qualityGroupId: number, dangerCoefficient: number) {
|
|
|
|
|
|
let group = gg.data.table.getTableList<ITableChapter_weight>(TableNames.Chapter_weight).filter(item => item.danger_group == qualityGroupId);
|
|
|
|
|
|
if (group.length > 0) {
|
|
|
|
|
|
for (let i = 0; i < group.length; i++) {
|
|
|
|
|
|
let d = group[i];
|
|
|
|
|
|
if (d.danger_para >= dangerCoefficient) {
|
|
|
|
|
|
return d;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return null;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**获取玩家当前暴击率 */
|
|
|
|
|
|
getPlayerCritRate(weaponId: number) {
|
|
|
|
|
|
return 0
|
|
|
|
|
|
}
|
|
|
|
|
|
/**获取玩家当前暴击伤害 */
|
|
|
|
|
|
getPlayerCritDamage(weaponId: number) {
|
|
|
|
|
|
return 0
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**根据地图id和格子索引获取格子是否被修复 */
|
|
|
|
|
|
getGridIsRepair(mapId: number, gridIndex: number) {
|
|
|
|
|
|
let isRepair = gg.data.getkeyKeyValue(OtherDataType.GridIsRepair, mapId + '&' + gridIndex);
|
|
|
|
|
|
if (isRepair == '') isRepair = "0";
|
|
|
|
|
|
return Number(isRepair);
|
|
|
|
|
|
}
|
|
|
|
|
|
/**根据地图id和格子索引设置格子是否被修复 */
|
|
|
|
|
|
setGridIsRepair(mapId: number, gridIndex: number, isRepair: number) {
|
|
|
|
|
|
gg.data.setkeyKeyValue(OtherDataType.GridIsRepair, mapId + '&' + gridIndex, isRepair);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//#endregion
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
///////////////////////----------------副本相关-------------------///////////////////////
|
|
|
|
|
|
//#region 副本
|
|
|
|
|
|
|
|
|
|
|
|
/**根据副本id获取副本配置 */
|
|
|
|
|
|
getReplicaConfig(id: number): ITableReplica {
|
|
|
|
|
|
let config = gg.data.table.getTableData<ITableReplica>(TableNames.Replica, id);
|
|
|
|
|
|
return config;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**根据副本id和难度获取通关次数 */
|
|
|
|
|
|
getReplicaPassCount(id: number, difficulty: ChapterDifficulty) {
|
|
|
|
|
|
let passCount = gg.data.getkeyKeyValue(OtherDataType.ReplicaPassCount, id + '&' + difficulty);
|
|
|
|
|
|
if (passCount == '') passCount = "0";
|
|
|
|
|
|
return Number(passCount);
|
|
|
|
|
|
}
|
|
|
|
|
|
/**根据副本id和难度设置通关次数 */
|
|
|
|
|
|
setReplicaPassCount(id: number, difficulty: ChapterDifficulty, passCount: number) {
|
|
|
|
|
|
gg.data.setkeyKeyValue(OtherDataType.ReplicaPassCount, id + '&' + difficulty, passCount);
|
|
|
|
|
|
}
|
|
|
|
|
|
/**根据副本类型和难度增加通关次数 */
|
|
|
|
|
|
addReplicaPassCount(id: number, difficulty: ChapterDifficulty, passCount: number = 1) {
|
|
|
|
|
|
let curentCount = this.getReplicaPassCount(id, difficulty);
|
|
|
|
|
|
this.setReplicaPassCount(id, difficulty, curentCount + passCount);
|
|
|
|
|
|
this.addReplicaPassCountToday(id, difficulty, passCount);
|
|
|
|
|
|
}
|
|
|
|
|
|
/**根据副本id和难度判断是否已通关 */
|
|
|
|
|
|
isReplicaComplete(id: number, difficulty: ChapterDifficulty) {
|
|
|
|
|
|
let passCount = this.getReplicaPassCount(id, difficulty);
|
|
|
|
|
|
return passCount > 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
/**根据副本id和难度获取今日通关次数 */
|
|
|
|
|
|
getReplicaPassCountToday(id: number, difficulty: ChapterDifficulty) {
|
|
|
|
|
|
let passCount = gg.data.getkeyKeyValue(OtherDataType.ReplicaPassCountToday, id + '&' + difficulty);
|
|
|
|
|
|
if (passCount == '') passCount = "0";
|
|
|
|
|
|
return Number(passCount);
|
|
|
|
|
|
}
|
|
|
|
|
|
/**根据副本id和难度设置今日通关次数 */
|
|
|
|
|
|
setReplicaPassCountToday(id: number, difficulty: ChapterDifficulty, passCount: number) {
|
|
|
|
|
|
gg.data.setkeyKeyValue(OtherDataType.ReplicaPassCountToday, id + '&' + difficulty, passCount);
|
|
|
|
|
|
}
|
|
|
|
|
|
/**根据副本id和难度增加今日通关次数 */
|
|
|
|
|
|
addReplicaPassCountToday(id: number, difficulty: ChapterDifficulty, passCount: number = 1) {
|
|
|
|
|
|
let curentCount = this.getReplicaPassCountToday(id, difficulty);
|
|
|
|
|
|
this.setReplicaPassCountToday(id, difficulty, curentCount + passCount);
|
|
|
|
|
|
}
|
|
|
|
|
|
/**根据副本类型和难度判断今日是否已通关 */
|
|
|
|
|
|
isReplicaCompleteToday(id: number, difficulty: ChapterDifficulty) {
|
|
|
|
|
|
let passCount = this.getReplicaPassCountToday(id, difficulty);
|
|
|
|
|
|
return passCount > 0;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//#endregion
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
//#region 新玩法
|
|
|
|
|
|
chapterNewPlayInfo = new Map<ChapterNewPlayTypeEnum, ChapterPlayData>()
|
|
|
|
|
|
newPlayIdArr: string[] = []
|
|
|
|
|
|
private generateTimeSequence(
|
|
|
|
|
|
startTime: number,
|
|
|
|
|
|
countMin: number,
|
|
|
|
|
|
countMax: number,
|
|
|
|
|
|
intervalMin: number,
|
|
|
|
|
|
intervalMax: number
|
|
|
|
|
|
): number[] {
|
|
|
|
|
|
const randomLimit = MTools.getRandomValue(countMin, countMax); // 封装随机整数
|
|
|
|
|
|
const timeArr: number[] = [];
|
|
|
|
|
|
let curTime = startTime;
|
|
|
|
|
|
|
|
|
|
|
|
for (let j = 0; j < randomLimit; j++) {
|
|
|
|
|
|
curTime += MTools.getRandomValue(intervalMin, intervalMax);
|
|
|
|
|
|
timeArr.push(curTime);
|
|
|
|
|
|
}
|
|
|
|
|
|
return timeArr;
|
|
|
|
|
|
}
|
|
|
|
|
|
private generateNumSequence(
|
|
|
|
|
|
countMin: number,
|
|
|
|
|
|
countMax: number,
|
|
|
|
|
|
intervalMin: number,
|
|
|
|
|
|
intervalMax: number
|
|
|
|
|
|
): number[] {
|
|
|
|
|
|
const randomLimit = MTools.getRandomValue(countMin, countMax); // 封装随机整数
|
|
|
|
|
|
const timeArr: number[] = [];
|
|
|
|
|
|
for (let j = 0; j < randomLimit; j++) {
|
|
|
|
|
|
timeArr.push(MTools.getRandomValue(intervalMin, intervalMax));
|
|
|
|
|
|
}
|
|
|
|
|
|
return timeArr;
|
|
|
|
|
|
}
|
|
|
|
|
|
// --- 核心优化:策略映射表 ---
|
|
|
|
|
|
// 定义每个玩法的配置解析器
|
|
|
|
|
|
private readonly playConfigHandlers: Partial<Record<ChapterNewPlayTypeEnum, PlayConfigDescriptor>> = {
|
|
|
|
|
|
[ChapterNewPlayTypeEnum.ThunderRemoveObstacle]: {
|
|
|
|
|
|
constKey: ChapterNewPlayTypeEnum.ThunderRemoveObstacle,
|
|
|
|
|
|
needsFilter: true,
|
|
|
|
|
|
paramMapping: {
|
|
|
|
|
|
startTime: (row, isProp) => parseInt(row.starttime.split('|')[isProp ? 1 : 0]),
|
|
|
|
|
|
limits: (row, isProp) => row.timeslimit.split('|')[isProp ? 1 : 0].split(',').map(Number),
|
|
|
|
|
|
intervals: (row, isProp) => row.cd.split('|')[isProp ? 1 : 0].split(',').map(Number),
|
|
|
|
|
|
},
|
|
|
|
|
|
parser: (p) => {
|
|
|
|
|
|
// p.limits = [min, max], p.intervals = [min, max]
|
|
|
|
|
|
return this.generateTimeSequence(p.startTime, p.limits[0], p.limits[1], p.intervals[0], p.intervals[1]);
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// --- 2. 加速门 (AccelerateGate) ---
|
|
|
|
|
|
[ChapterNewPlayTypeEnum.AccelerateGate]: {
|
|
|
|
|
|
constKey: ChapterNewPlayTypeEnum.AccelerateGate,
|
|
|
|
|
|
paramMapping: {
|
|
|
|
|
|
startTime: (row, isProp) => parseInt(row.starttime.split('|')[isProp ? 1 : 0]),
|
|
|
|
|
|
limits: (row, isProp) => row.timeslimit.split('|')[isProp ? 1 : 0].split(',').map(Number),
|
|
|
|
|
|
intervals: (row, isProp) => row.cd.split('|')[isProp ? 1 : 0].split(',').map(Number),
|
|
|
|
|
|
// par1: "门存在时间,加速倍速,buff 时间"
|
|
|
|
|
|
duration: (row, isProp) => parseInt(row.duration.split('|')[isProp ? 1 : 0]),
|
|
|
|
|
|
extraParams: (row, isProp) => row.par1.split('|')[isProp ? 1 : 0].split(',').map(Number),
|
|
|
|
|
|
},
|
|
|
|
|
|
parser: (p) => {
|
|
|
|
|
|
const info = new IAccelerateGateProperty();
|
|
|
|
|
|
info.gateExistTime = p.duration;
|
|
|
|
|
|
info.accelerateSpeed = p.extraParams[0];
|
|
|
|
|
|
info.buffTime = p.extraParams[1];
|
|
|
|
|
|
info.timeArr = this.generateTimeSequence(p.startTime, p.limits[0], p.limits[1], p.intervals[0], p.intervals[1]);
|
|
|
|
|
|
return info;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// --- 3. 藤蔓草地 (VinesGrass) ---
|
|
|
|
|
|
[ChapterNewPlayTypeEnum.VinesGrass]: {
|
|
|
|
|
|
constKey: ChapterNewPlayTypeEnum.VinesGrass,
|
|
|
|
|
|
paramMapping: {
|
|
|
|
|
|
startTime: (row, isProp) => parseInt(row.starttime.split('|')[isProp ? 1 : 0]),
|
|
|
|
|
|
limits: (row, isProp) => row.timeslimit.split('|')[isProp ? 1 : 0].split(',').map(Number),
|
|
|
|
|
|
intervals: (row, isProp) => row.cd.split('|')[isProp ? 1 : 0].split(',').map(Number),
|
|
|
|
|
|
},
|
|
|
|
|
|
parser: (p) => {
|
|
|
|
|
|
return this.generateTimeSequence(p.startTime, p.limits[0], p.limits[1], p.intervals[0], p.intervals[1]);
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// --- 4. 伞兵 (Paratroops) ---
|
|
|
|
|
|
[ChapterNewPlayTypeEnum.Paratroops]: {
|
|
|
|
|
|
constKey: ChapterNewPlayTypeEnum.Paratroops,
|
|
|
|
|
|
needsFilter: true,
|
|
|
|
|
|
paramMapping: {
|
|
|
|
|
|
startTime: (row, isProp) => parseInt(row.starttime.split('|')[isProp ? 1 : 0]),
|
|
|
|
|
|
limits: (row, isProp) => row.timeslimit.split('|')[isProp ? 1 : 0].split(',').map(Number), // 用于时间生成
|
|
|
|
|
|
intervals: (row, isProp) => row.cd.split('|')[isProp ? 1 : 0].split(',').map(Number), // 用于时间生成
|
|
|
|
|
|
monsterId: (row, isProp) => parseInt(row.par1.split('|')[isProp ? 1 : 0]),
|
|
|
|
|
|
// par2: "数量下限,数量上限" (注意:原逻辑里这里可能复用了某些字段,请根据实际Excel调整)
|
|
|
|
|
|
// 假设 par2 是 "numMin,numMax",如果原逻辑是用 cd 的区间做数量,请在此调整
|
|
|
|
|
|
numRange: (row, isProp) => {
|
|
|
|
|
|
// 根据你的原代码:info.numArr = this.generateNumSequence(limitMin, limitMax, numMin, numMax);
|
|
|
|
|
|
// 这里假设 par2 提供了 numMin, numMax
|
|
|
|
|
|
const val = row.par2 ? row.par2.split('|')[isProp ? 1 : 0] : "1,1";
|
|
|
|
|
|
return val.split(',').map(Number);
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
parser: (p) => {
|
|
|
|
|
|
const info = new IParatroopsProperty();
|
|
|
|
|
|
info.monsterId = p.monsterId;
|
|
|
|
|
|
info.timeArr = this.generateTimeSequence(p.startTime, p.limits[0], p.limits[1], p.intervals[0], p.intervals[1]);
|
|
|
|
|
|
// 注意:原代码逻辑 generateNumSequence(limitMin, limitMax, numMin, numMax)
|
|
|
|
|
|
// 这里的参数顺序需严格对应你原有的 generateNumSequence 定义
|
|
|
|
|
|
info.numArr = this.generateNumSequence(p.limits[0], p.limits[1], p.numRange[0], p.numRange[1]);
|
|
|
|
|
|
return info;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// --- 5. 血月 (BloodMoon) ---
|
|
|
|
|
|
[ChapterNewPlayTypeEnum.BloodMoon]: {
|
|
|
|
|
|
constKey: ChapterNewPlayTypeEnum.BloodMoon,
|
|
|
|
|
|
paramMapping: {
|
|
|
|
|
|
startTime: (row, isProp) => parseInt(row.starttime.split('|')[isProp ? 1 : 0]),
|
|
|
|
|
|
intervalTime: (row, isProp) => parseInt(row.cd.split('|')[isProp ? 1 : 0]), // 假设间隔在 cd 列
|
|
|
|
|
|
durationRange: (row, isProp) => row.duration.split('|')[isProp ? 1 : 0].split(',').map(Number), // 持续时间范围
|
|
|
|
|
|
accelerateSpeed: (row, isProp) => parseFloat(row.par1.split('|')[isProp ? 1 : 0]),
|
|
|
|
|
|
},
|
|
|
|
|
|
parser: (p) => {
|
|
|
|
|
|
const info = new IBloodMoonProperty();
|
|
|
|
|
|
info.startTime = p.startTime;
|
|
|
|
|
|
info.accelerateSpeed = p.accelerateSpeed;
|
|
|
|
|
|
info.openTimeArr = [];
|
|
|
|
|
|
info.closeTimeArr = [];
|
|
|
|
|
|
|
|
|
|
|
|
let openTime = p.startTime;
|
|
|
|
|
|
// 循环次数可配置,这里保持原逻辑 100 次
|
|
|
|
|
|
for (let j = 0; j < 100; j++) {
|
|
|
|
|
|
const randTime = MTools.getRandomValue(p.durationRange[0], p.durationRange[1]);
|
|
|
|
|
|
info.openTimeArr.push(openTime);
|
|
|
|
|
|
const closeTime = openTime + randTime;
|
|
|
|
|
|
info.closeTimeArr.push(closeTime);
|
|
|
|
|
|
openTime = closeTime + p.intervalTime;
|
|
|
|
|
|
}
|
|
|
|
|
|
return info;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// --- 6. 魔法眼 (MagicEye) ---
|
|
|
|
|
|
[ChapterNewPlayTypeEnum.MagicEye]: {
|
|
|
|
|
|
constKey: ChapterNewPlayTypeEnum.MagicEye,
|
|
|
|
|
|
paramMapping: {
|
|
|
|
|
|
startTime: (row, isProp) => parseInt(row.starttime.split('|')[isProp ? 1 : 0]),
|
|
|
|
|
|
intervalTime: (row, isProp) => parseInt(row.cd.split('|')[isProp ? 1 : 0]),
|
|
|
|
|
|
existTime: (row, isProp) => parseInt(row.duration.split('|')[isProp ? 1 : 0].split(',')[0]), // 假设取第一个
|
|
|
|
|
|
effectRange: (row, isProp) => row.par1.split('|')[isProp ? 1 : 0].split(',').map(Number),
|
|
|
|
|
|
// shieldCoeff: (row, isProp) => parseFloat(row.par2.split('|')[isProp ? 1 : 0]),
|
|
|
|
|
|
},
|
|
|
|
|
|
parser: (p) => {
|
|
|
|
|
|
const info = new IMagicEyeProperty();
|
|
|
|
|
|
info.startTime = p.startTime;
|
|
|
|
|
|
info.intervalTime = p.intervalTime;
|
|
|
|
|
|
info.existTime = p.existTime;
|
|
|
|
|
|
info.effectTimeLowerLimit = p.effectRange[0];
|
|
|
|
|
|
info.effectTimeUpperLimit = p.effectRange[1];
|
|
|
|
|
|
info.shieldHpCoefficient = p.effectRange[2];
|
|
|
|
|
|
return info;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
// --- 7. 女巫 (Witch) ---
|
|
|
|
|
|
[ChapterNewPlayTypeEnum.Witch]: {
|
|
|
|
|
|
constKey: ChapterNewPlayTypeEnum.Witch,
|
|
|
|
|
|
paramMapping: {
|
|
|
|
|
|
startTime: (row, isProp) => parseInt(row.starttime.split('|')[isProp ? 1 : 0]),
|
|
|
|
|
|
intervalTime: (row, isProp) => parseInt(row.cd.split('|')[isProp ? 1 : 0]),
|
|
|
|
|
|
existTime: (row, isProp) => parseInt(row.duration.split('|')[isProp ? 1 : 0].split(',')[0]),
|
|
|
|
|
|
effectRange: (row, isProp) => row.par1.split('|')[isProp ? 1 : 0].split(',').map(Number),
|
|
|
|
|
|
// maxMonsterNum: (row, isProp) => parseInt(row.par2.split('|')[isProp ? 1 : 0].split(',')[0]),
|
|
|
|
|
|
// addHpCoeff: (row, isProp) => parseFloat(row.par2.split('|')[isProp ? 1 : 0].split(',')[1] || "0"),
|
|
|
|
|
|
},
|
|
|
|
|
|
parser: (p) => {
|
|
|
|
|
|
const info = new IWitchProperty();
|
|
|
|
|
|
info.startTime = p.startTime;
|
|
|
|
|
|
info.intervalTime = p.intervalTime;
|
|
|
|
|
|
info.existTime = p.existTime;
|
|
|
|
|
|
info.effectTimeLowerLimit = p.effectRange[0];
|
|
|
|
|
|
info.effectTimeUpperLimit = p.effectRange[1];
|
|
|
|
|
|
info.maxAddHpMonsterNum = p.effectRange[2];
|
|
|
|
|
|
info.addHpCoefficient = p.effectRange[3];
|
|
|
|
|
|
return info;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// --- 8. 血包加血 (BloodBagAddHp) ---
|
|
|
|
|
|
[ChapterNewPlayTypeEnum.BloodBagAddHp]: {
|
|
|
|
|
|
constKey: ChapterNewPlayTypeEnum.BloodBagAddHp,
|
|
|
|
|
|
paramMapping: {
|
|
|
|
|
|
startTime: (row, isProp) => parseInt(row.starttime.split('|')[isProp ? 1 : 0]),
|
|
|
|
|
|
limits: (row, isProp) => row.timeslimit.split('|')[isProp ? 1 : 0].split(',').map(Number),
|
|
|
|
|
|
intervals: (row, isProp) => row.cd.split('|')[isProp ? 1 : 0].split(',').map(Number),
|
|
|
|
|
|
gateExistTime: (row, isProp) => parseInt(row.duration.split('|')[isProp ? 1 : 0]),
|
|
|
|
|
|
addHpCoeff: (row, isProp) => parseFloat(row.par1.split('|')[isProp ? 1 : 0]),
|
|
|
|
|
|
},
|
|
|
|
|
|
parser: (p) => {
|
|
|
|
|
|
const info = new IBloodBagAddHpProperty();
|
|
|
|
|
|
info.gateExistTime = p.gateExistTime;
|
|
|
|
|
|
info.addHpCoefficient = p.addHpCoeff;
|
|
|
|
|
|
info.timeArr = this.generateTimeSequence(p.startTime, p.limits[0], p.limits[1], p.intervals[0], p.intervals[1]);
|
|
|
|
|
|
return info;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
// --- 9. Boss 空投 (BossAirdrop) ---
|
|
|
|
|
|
[ChapterNewPlayTypeEnum.BossAirdrop]: {
|
|
|
|
|
|
constKey: ChapterNewPlayTypeEnum.BossAirdrop,
|
|
|
|
|
|
needsFilter: true,
|
|
|
|
|
|
paramMapping: {
|
|
|
|
|
|
startTime: (row, isProp) => parseInt(row.starttime.split('|')[isProp ? 1 : 0]),
|
|
|
|
|
|
limits: (row, isProp) => row.timeslimit.split('|')[isProp ? 1 : 0].split(',').map(Number),
|
|
|
|
|
|
intervals: (row, isProp) => row.cd.split('|')[isProp ? 1 : 0].split(',').map(Number),
|
|
|
|
|
|
monsterId: (row, isProp) => parseInt(row.par1.split('|')[isProp ? 1 : 0]),
|
|
|
|
|
|
numRange: (row, isProp) => {
|
|
|
|
|
|
const val = row.par2 ? row.par2.split('|')[isProp ? 1 : 0] : "1,1";
|
|
|
|
|
|
return val.split(',').map(Number);
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
parser: (p) => {
|
|
|
|
|
|
const info = new IParatroopsProperty(); // 复用伞兵属性类?如果是新类请替换
|
|
|
|
|
|
info.monsterId = p.monsterId;
|
|
|
|
|
|
info.timeArr = this.generateTimeSequence(p.startTime, p.limits[0], p.limits[1], p.intervals[0], p.intervals[1]);
|
|
|
|
|
|
info.numArr = this.generateNumSequence(p.limits[0], p.limits[1], p.numRange[0], p.numRange[1]);
|
|
|
|
|
|
return info;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
//新机制寒冬
|
|
|
|
|
|
[ChapterNewPlayTypeEnum.ColdFire]: {
|
|
|
|
|
|
constKey: ChapterNewPlayTypeEnum.ColdFire,
|
|
|
|
|
|
needsFilter: true,
|
|
|
|
|
|
paramMapping: {
|
|
|
|
|
|
|
|
|
|
|
|
timeInterval: (row, isProp) => parseInt(row.cd.split('|')[isProp ? 1 : 0]),
|
|
|
|
|
|
subTemperatureArr: (row, isProp) => row.par1.split('|')[isProp ? 1 : 0].split(',').map(Number),
|
|
|
|
|
|
subHpByTemperatureArr: (row, isProp) => row.par2.split('|')[isProp ? 1 : 0].split('&')[0].split(',').map(Number),
|
|
|
|
|
|
subHpArr: (row, isProp) => row.par2.split('|')[isProp ? 1 : 0].split('&')[1].split(',').map(Number),
|
|
|
|
|
|
},
|
|
|
|
|
|
parser: (p) => {
|
|
|
|
|
|
const info = new IColdFireProperty(); // 复用伞兵属性类?如果是新类请替换
|
|
|
|
|
|
info.startTemperature = 0;
|
|
|
|
|
|
info.timeInterval = p.timeInterval;
|
|
|
|
|
|
info.subTemperatureArr = p.subTemperatureArr;
|
|
|
|
|
|
info.subHpByTemperatureArr = p.subHpByTemperatureArr;
|
|
|
|
|
|
info.subHpArr = p.subHpArr;
|
|
|
|
|
|
return info;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
//新机制大雾
|
|
|
|
|
|
[ChapterNewPlayTypeEnum.FogAndWind]: {
|
|
|
|
|
|
constKey: ChapterNewPlayTypeEnum.FogAndWind,
|
|
|
|
|
|
needsFilter: true,
|
|
|
|
|
|
paramMapping: {
|
|
|
|
|
|
intervals: (row, isProp) => parseInt(row.cd.split('|')[isProp ? 1 : 0]),
|
|
|
|
|
|
limitNum: (row, isProp) => parseInt(row.timeslimit.split('|')[isProp ? 1 : 0]),
|
|
|
|
|
|
fogDownDistance: (row, isProp) => parseInt(row.par1.split('|')[isProp ? 1 : 0]),
|
|
|
|
|
|
},
|
|
|
|
|
|
parser: (p) => {
|
|
|
|
|
|
const info = new IFogAndWindProperty(); // 复用伞兵属性类?如果是新类请替换
|
|
|
|
|
|
info.timeInterval = p.intervals;
|
|
|
|
|
|
info.limitNum = p.limitNum;
|
|
|
|
|
|
info.fogDownDistance = p.fogDownDistance;
|
|
|
|
|
|
return info;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
//新机制黑夜
|
|
|
|
|
|
[ChapterNewPlayTypeEnum.NightAndLight]: {
|
|
|
|
|
|
constKey: ChapterNewPlayTypeEnum.NightAndLight,
|
|
|
|
|
|
needsFilter: true,
|
|
|
|
|
|
paramMapping: {
|
|
|
|
|
|
|
|
|
|
|
|
intervals: (row, isProp) => parseInt(row.cd.split('|')[isProp ? 1 : 0]),
|
|
|
|
|
|
existTime: (row, isProp) => parseInt(row.duration.split('|')[isProp ? 1 : 0]),
|
|
|
|
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
parser: (p) => {
|
|
|
|
|
|
const info = new INightAndLightProperty(); // 复用伞兵属性类?如果是新类请替换
|
|
|
|
|
|
info.timeInterval = p.intervals;
|
|
|
|
|
|
info.durationTime = p.existTime;
|
|
|
|
|
|
return info;
|
|
|
|
|
|
}
|
|
|
|
|
|
},
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
initChapterNewPlayInfo() {
|
|
|
|
|
|
this.chapterNewPlayInfo.clear();
|
|
|
|
|
|
const chapterConfig = gg.game.CurentBattle.CurentConfigChapter2 || gg.game.CurentBattle.CurentConfigChapter;
|
|
|
|
|
|
|
|
|
|
|
|
// 1. 初始化 ID 列表
|
|
|
|
|
|
if (!chapterConfig.new_play) {
|
|
|
|
|
|
this.newPlayIdArr = [];
|
|
|
|
|
|
} else {
|
|
|
|
|
|
this.newPlayIdArr = (chapterConfig.new_play + '').split(',');
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const currentBattleType = gg.game.CurentBattle.BattleType;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 3. 处理全局过滤逻辑 (GachaMode)
|
|
|
|
|
|
if (currentBattleType === BattleType.GachaMode) {
|
|
|
|
|
|
this.newPlayIdArr = this.newPlayIdArr.filter(idStr => {
|
|
|
|
|
|
const id = parseInt(idStr);
|
|
|
|
|
|
const handler = this.playConfigHandlers[id];
|
|
|
|
|
|
if (handler && handler.needsFilter) {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
return true;
|
|
|
|
|
|
});
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
// 4. 【核心优化】遍历并自动解析配置
|
|
|
|
|
|
for (const idStr of this.newPlayIdArr) {
|
|
|
|
|
|
const playType = parseInt(idStr);
|
|
|
|
|
|
const descriptor = this.playConfigHandlers[playType];
|
|
|
|
|
|
|
|
|
|
|
|
if (!descriptor) {
|
|
|
|
|
|
console.warn(`⚠️ 未知的玩法类型 ID: ${playType},请在 playConfigHandlers 中注册`);
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 获取完整的配置行对象 (fullStr 现在是对象)
|
|
|
|
|
|
const rowData = gg.constMechanism.getConfigDataById(descriptor.constKey);
|
|
|
|
|
|
|
|
|
|
|
|
if (!rowData) {
|
|
|
|
|
|
console.error(`❌ 找不到配置 ID: ${descriptor.constKey}`);
|
|
|
|
|
|
continue;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 【自动化】根据 paramMapping 提取并清洗所有参数
|
|
|
|
|
|
const extractedParams: Record<string, any> = {};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
const resultData = descriptor.parser(extractedParams);
|
|
|
|
|
|
console.log('resultData===', playType, resultData)
|
|
|
|
|
|
|
|
|
|
|
|
this.chapterNewPlayInfo.set(playType, resultData);
|
|
|
|
|
|
|
|
|
|
|
|
// 调用业务解析器
|
|
|
|
|
|
// try {
|
|
|
|
|
|
// const resultData = descriptor.parser(extractedParams);
|
|
|
|
|
|
// this.chapterNewPlayInfo.set(playType, resultData);
|
|
|
|
|
|
// } catch (e) {
|
|
|
|
|
|
// console.error(`❌ 玩法 [${playType}] 业务解析失败:`, e);
|
|
|
|
|
|
// }
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
getChapterNewPlayInfo(type: ChapterNewPlayTypeEnum.Paratroops): IParatroopsProperty | undefined;
|
|
|
|
|
|
getChapterNewPlayInfo(type: ChapterNewPlayTypeEnum.AccelerateGate): IAccelerateGateProperty | undefined;
|
|
|
|
|
|
getChapterNewPlayInfo(type: ChapterNewPlayTypeEnum.BloodBagAddHp): IBloodBagAddHpProperty | undefined;
|
|
|
|
|
|
getChapterNewPlayInfo(type: ChapterNewPlayTypeEnum.BloodMoon): IBloodMoonProperty | undefined;
|
|
|
|
|
|
getChapterNewPlayInfo(type: ChapterNewPlayTypeEnum.MagicEye): IMagicEyeProperty | undefined;
|
|
|
|
|
|
getChapterNewPlayInfo(type: ChapterNewPlayTypeEnum.Witch): IWitchProperty | undefined;
|
|
|
|
|
|
getChapterNewPlayInfo(type: ChapterNewPlayTypeEnum.BloodBagAddHp): IBloodBagAddHpProperty | undefined;
|
|
|
|
|
|
getChapterNewPlayInfo(type: ChapterNewPlayTypeEnum.BossAirdrop): IParatroopsProperty | undefined;
|
|
|
|
|
|
// 3. 新机制寒冬
|
|
|
|
|
|
getChapterNewPlayInfo(type: ChapterNewPlayTypeEnum.ColdFire): IColdFireProperty | undefined;
|
|
|
|
|
|
getChapterNewPlayInfo(type: ChapterNewPlayTypeEnum.ThunderRemoveObstacle): number[] | undefined;
|
|
|
|
|
|
getChapterNewPlayInfo(type: ChapterNewPlayTypeEnum.VinesGrass): number[] | undefined;
|
|
|
|
|
|
// 4. 新机制大雾/吹风机
|
|
|
|
|
|
getChapterNewPlayInfo(type: ChapterNewPlayTypeEnum.FogAndWind): IFogAndWindProperty | undefined;
|
|
|
|
|
|
// 5. 新机制黑夜/灯塔
|
|
|
|
|
|
getChapterNewPlayInfo(type: ChapterNewPlayTypeEnum.NightAndLight): INightAndLightProperty | undefined;
|
|
|
|
|
|
|
|
|
|
|
|
// 2. 通用签名 (兜底,返回联合类型)
|
|
|
|
|
|
getChapterNewPlayInfo(type: ChapterNewPlayTypeEnum): ChapterPlayData | undefined;
|
|
|
|
|
|
|
|
|
|
|
|
getChapterNewPlayInfo(type: ChapterNewPlayTypeEnum) {
|
|
|
|
|
|
return this.chapterNewPlayInfo.get(type)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
haveNewPlayInfo() {
|
|
|
|
|
|
let arrayNew: ChapterNewPlayTypeEnum[] = []
|
|
|
|
|
|
//解析new_play(与枚举比较需为 number,否则 includes(枚举) 因 "15" !== 15 恒为 false)
|
|
|
|
|
|
for (let i = 0; i < this.newPlayIdArr.length; i++) {
|
|
|
|
|
|
let id = parseInt(this.newPlayIdArr[i], 10)
|
|
|
|
|
|
if (id >= ChapterNewPlayTypeEnum.ThunderRemoveObstacle) {
|
|
|
|
|
|
arrayNew.push(id)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return arrayNew
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**是否有移除障碍物道具机制 */
|
|
|
|
|
|
isHaveRemoveObsProp() {
|
|
|
|
|
|
return this.newPlayIdArr.indexOf(ChapterNewPlayTypeEnum.RemoveObstacleProp + '') >= 0
|
|
|
|
|
|
}
|
|
|
|
|
|
/**是否有炸弹道具 */
|
|
|
|
|
|
isHaveBombProp() {
|
|
|
|
|
|
return this.newPlayIdArr.indexOf(ChapterNewPlayTypeEnum.PlaceBombProp + '') >= 0
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//#endregion
|
|
|
|
|
|
/***
|
|
|
|
|
|
* 俄罗斯方块需要加载的资源
|
|
|
|
|
|
*/
|
|
|
|
|
|
getTetriResource() {
|
|
|
|
|
|
// 仅收敛到“当前章节 cube_group 在波次权重里可能抽到的 type”,避免全量方块预加载
|
|
|
|
|
|
const tables = gg.data.table.getTableList<ITableBattleCube>(TableNames.BattleCube) || [];
|
|
|
|
|
|
const waveTables = gg.data.table.getTableList<ITableTetriWaveWeight>(TableNames.TetriWaveWeight) || [];
|
|
|
|
|
|
const chapterId = gg.game?.CurentBattle?.ChapterId || gg.data?.doc?.chapter || 1;
|
|
|
|
|
|
const chapter = this.getChapterConfig(chapterId) as ITableChapter;
|
|
|
|
|
|
const cubeGroupField = chapter?.cube_group;
|
|
|
|
|
|
|
|
|
|
|
|
const typeSet = new Set<number>();
|
|
|
|
|
|
if (cubeGroupField && waveTables.length > 0) {
|
|
|
|
|
|
for (let i = 0; i < waveTables.length; i++) {
|
|
|
|
|
|
const wt = (waveTables[i] as any)?.[cubeGroupField] as string;
|
|
|
|
|
|
if (!wt) continue;
|
|
|
|
|
|
const parts = wt.split(',').map(x => (x ?? '').trim()).filter(Boolean);
|
|
|
|
|
|
for (let j = 0; j < parts.length; j++) {
|
|
|
|
|
|
const [vStr, wStr] = parts[j].split('|').map(x => (x ?? '').trim());
|
|
|
|
|
|
const v = Number(vStr);
|
|
|
|
|
|
const w = Number(wStr);
|
|
|
|
|
|
if (Number.isFinite(v) && Number.isFinite(w) && w >= 0) {
|
|
|
|
|
|
typeSet.add(v);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const cubeNameSet = new Set<string>();
|
|
|
|
|
|
for (let i = 0; i < tables.length; i++) {
|
|
|
|
|
|
const table = tables[i];
|
|
|
|
|
|
if (!table?.cubename) continue;
|
|
|
|
|
|
if (typeSet.size > 0) {
|
|
|
|
|
|
if (table.weight > 0 && typeSet.has(table.type)) {
|
|
|
|
|
|
cubeNameSet.add(table.cubename);
|
|
|
|
|
|
}
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 回退:章节/权重异常时保持旧行为,避免资源缺失
|
|
|
|
|
|
cubeNameSet.add(table.cubename);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return Array.from(cubeNameSet);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**方块类型2动态权重值 */
|
|
|
|
|
|
private _type2Weight: number = 0;
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 橙武单通:BattleCube.type==1(基础武器)或 type==5(藤蔓武器形状)按 weapontype(1~6) 过滤,仅保留 ModechooseTetriType;
|
|
|
|
|
|
* 其它 type(无重力/金币/治疗等)为功能块,不过滤。
|
|
|
|
|
|
*/
|
|
|
|
|
|
private _filterOrangeDanWeaponCubeList(tetriCategoryType: number, list: ITableBattleCube[]): ITableBattleCube[] {
|
|
|
|
|
|
if (!list?.length) return list;
|
|
|
|
|
|
if (gg.game?.CurentBattle?.BattleType !== BattleType.OrangeWeaponMode_Dan) return list;
|
|
|
|
|
|
const allow = gg.game.CurentBattle.ModechooseTetriType;
|
|
|
|
|
|
if (!allow?.length) return list;
|
|
|
|
|
|
if (tetriCategoryType !== 1 && tetriCategoryType !== 5) return list;
|
|
|
|
|
|
const filtered = list.filter((item) => {
|
|
|
|
|
|
const wt = item.weapontype;
|
|
|
|
|
|
if (!Number.isFinite(wt) || wt < 1 || wt > 6) return true;
|
|
|
|
|
|
return allow.indexOf(wt) !== -1;
|
|
|
|
|
|
});
|
|
|
|
|
|
return filtered.length ? filtered : list;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//根据战斗的波次获取俄罗斯方块的配置
|
|
|
|
|
|
getTetriConfigByWave(wave: number, chapter: ITableChapter) {
|
|
|
|
|
|
let cubeTables = gg.data.table.getTableList<ITableBattleCube>(TableNames.BattleCube)
|
|
|
|
|
|
|
|
|
|
|
|
if (gg.data.doc.chapter == 1) {
|
|
|
|
|
|
//cubeTables中ischapter1 == 1的方块去掉
|
|
|
|
|
|
cubeTables = cubeTables.filter(item => item.ischapter1 != 1)
|
|
|
|
|
|
//console.log('cubeTables 111 ', cubeTables)
|
|
|
|
|
|
}
|
|
|
|
|
|
let waveTables = gg.data.table.getTableList<ITableTetriWaveWeight>(TableNames.TetriWaveWeight)
|
|
|
|
|
|
let data = waveTables.find(item => item.id == wave)
|
|
|
|
|
|
if (!cubeTables || cubeTables.length == 0) return null
|
|
|
|
|
|
if (!waveTables || waveTables.length == 0) return null
|
|
|
|
|
|
if (!data || !data[chapter.cube_group]) return null
|
|
|
|
|
|
|
|
|
|
|
|
let typeweight = data[chapter.cube_group];
|
|
|
|
|
|
|
|
|
|
|
|
//根据权重获取type值。 ( 1|100, 2|100, 3|100 )type值为1,2,3,权重为100,100,100
|
|
|
|
|
|
const randomByWeightString = (s: string): number | null => {
|
|
|
|
|
|
const parts = (s ?? '').split(',').map(x => x.trim()).filter(Boolean)
|
|
|
|
|
|
if (parts.length == 0) return null
|
|
|
|
|
|
let total = 0
|
|
|
|
|
|
const pairs: { v: number, w: number }[] = []
|
|
|
|
|
|
//
|
|
|
|
|
|
if (gg.game.noGravityWallProbability > 0) {
|
|
|
|
|
|
let have2 = false
|
|
|
|
|
|
for (let i = 0; i < parts.length; i++) {
|
|
|
|
|
|
const [vStr, wStr] = parts[i].split('|').map(x => (x ?? '').trim())
|
|
|
|
|
|
const v = Number(vStr)
|
|
|
|
|
|
if (v == 2) {
|
|
|
|
|
|
have2 = true
|
|
|
|
|
|
break
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (!have2) {
|
|
|
|
|
|
let str2 = '2|' + Math.floor(gg.game.noGravityWallProbability);
|
|
|
|
|
|
parts.push(str2)
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (let i = 0; i < parts.length; i++) {
|
|
|
|
|
|
const [vStr, wStr] = parts[i].split('|').map(x => (x ?? '').trim())
|
|
|
|
|
|
const v = Number(vStr)
|
|
|
|
|
|
|
|
|
|
|
|
let w = Number(wStr)
|
|
|
|
|
|
if (v == 2 && gg.game.noGravityWallProbability > 0) {
|
|
|
|
|
|
w = Math.floor(gg.game.noGravityWallProbability);
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
if (Number.isFinite(v) && Number.isFinite(w) && w > 0) {
|
|
|
|
|
|
pairs.push({ v, w })
|
|
|
|
|
|
total += w
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
if (pairs.length == 0 || total <= 0) return null
|
|
|
|
|
|
let r = Math.random() * total
|
|
|
|
|
|
for (let i = 0; i < pairs.length; i++) {
|
|
|
|
|
|
r -= pairs[i].w
|
|
|
|
|
|
if (r <= 0) return pairs[i].v
|
|
|
|
|
|
}
|
|
|
|
|
|
return pairs[pairs.length - 1].v
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const type = randomByWeightString(typeweight)
|
|
|
|
|
|
if (type == null) return null
|
|
|
|
|
|
|
|
|
|
|
|
let list = cubeTables.filter(item => item.type == type && item.weight > 0)
|
|
|
|
|
|
list = this._filterOrangeDanWeaponCubeList(type, list)
|
|
|
|
|
|
if (!list || list.length == 0) return null
|
|
|
|
|
|
|
|
|
|
|
|
let total = 0
|
|
|
|
|
|
for (let i = 0; i < list.length; i++) {
|
|
|
|
|
|
total += (list[i].weight || 0)
|
|
|
|
|
|
}
|
|
|
|
|
|
if (total <= 0) return list[0]
|
|
|
|
|
|
|
|
|
|
|
|
let r = Math.random() * total
|
|
|
|
|
|
for (let i = 0; i < list.length; i++) {
|
|
|
|
|
|
r -= (list[i].weight || 0)
|
|
|
|
|
|
if (r <= 0) return list[i]
|
|
|
|
|
|
}
|
|
|
|
|
|
return list[list.length - 1]
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 指定波次与方块 type,仅在 BattleCube 表内按 weight 随机一条(不走 TetriWaveWeight 的 typeweight)。
|
|
|
|
|
|
* 用于广告刷新等需要「必出某一类」的场景。
|
|
|
|
|
|
*/
|
|
|
|
|
|
getTetriConfigByWaveForceType(wave: number, forceType: number): ITableBattleCube | null {
|
|
|
|
|
|
let cubeTables = gg.data.table.getTableList<ITableBattleCube>(TableNames.BattleCube)
|
|
|
|
|
|
|
|
|
|
|
|
if (gg.data.doc.chapter == 1) {
|
|
|
|
|
|
//cubeTables中ischapter1 == 1的方块去掉
|
|
|
|
|
|
cubeTables = cubeTables.filter(item => item.ischapter1 != 1)
|
|
|
|
|
|
//console.log('cubeTables', cubeTables)
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const waveTables = gg.data.table.getTableList<ITableTetriWaveWeight>(TableNames.TetriWaveWeight)
|
|
|
|
|
|
if (!cubeTables?.length || !waveTables?.length) return null
|
|
|
|
|
|
const data = waveTables.find(item => item.id == wave)
|
|
|
|
|
|
if (!data) return null
|
|
|
|
|
|
|
|
|
|
|
|
const list = cubeTables.filter(item => item.type == forceType && item.weight > 0)
|
|
|
|
|
|
if (!list?.length) return null
|
|
|
|
|
|
|
|
|
|
|
|
let total = 0
|
|
|
|
|
|
for (let i = 0; i < list.length; i++) {
|
|
|
|
|
|
total += (list[i].weight || 0)
|
|
|
|
|
|
}
|
|
|
|
|
|
if (total <= 0) return list[0]
|
|
|
|
|
|
|
|
|
|
|
|
let r = Math.random() * total
|
|
|
|
|
|
for (let i = 0; i < list.length; i++) {
|
|
|
|
|
|
r -= (list[i].weight || 0)
|
|
|
|
|
|
if (r <= 0) return list[i]
|
|
|
|
|
|
}
|
|
|
|
|
|
return list[list.length - 1]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**根据战斗的波次获取俄罗斯方块的配置(取多个;每次独立随机,允许重复) */
|
|
|
|
|
|
getTetriConfigsByWave(wave: number, count: number = 3, chapter: ITableChapter) {
|
|
|
|
|
|
if (count <= 0) return []
|
|
|
|
|
|
const picked: ITableBattleCube[] = []
|
|
|
|
|
|
for (let i = 0; i < count; i++) {
|
|
|
|
|
|
let one = this.getTetriConfigByWave(wave, chapter)
|
|
|
|
|
|
while (one && one.type != 1 && picked.find(item => item.type != 1) != null) {
|
|
|
|
|
|
one = this.getTetriConfigByWave(wave, chapter)
|
|
|
|
|
|
}
|
|
|
|
|
|
if (one) picked.push(one)
|
|
|
|
|
|
}
|
|
|
|
|
|
return picked
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**广告「神奇方块」刷新:与 getTetriConfigsByWave 同数量;其中必含 1 张 type=2,且该张 price=0(免费),其余仍按波次权重随机(顺序:先免费 type2,再随机补满) */
|
|
|
|
|
|
getTetriConfigsAd(wave: number, count: number = 3, chapter: ITableChapter) {
|
|
|
|
|
|
const picked: ITableBattleCube[] = []
|
|
|
|
|
|
const type2 = this.getTetriConfigByWaveForceType(wave, 2)
|
|
|
|
|
|
if (type2) {
|
|
|
|
|
|
picked.push({ ...(type2 as object), price: 0 } as ITableBattleCube)
|
|
|
|
|
|
}
|
|
|
|
|
|
while (picked.length < count) {
|
|
|
|
|
|
const one = this.getTetriConfigByWave(wave, chapter)
|
|
|
|
|
|
if (one) picked.push(one)
|
|
|
|
|
|
else break
|
|
|
|
|
|
}
|
|
|
|
|
|
return picked
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
getAllDps(skills: ITableSkill[], weaponIDs: number[], stackSumOverride?: Map<number, number> | null) {
|
|
|
|
|
|
let all = 0;
|
|
|
|
|
|
for (let i = 0; i < weaponIDs.length; i++) {
|
|
|
|
|
|
let weaponID = weaponIDs[i];
|
|
|
|
|
|
let weaponData = gg.data.project.getWeaponData(weaponID);
|
|
|
|
|
|
if (!weaponData) continue;
|
|
|
|
|
|
|
|
|
|
|
|
//武器方块数(可选:三选一预览时传入假设叠层,与场上绑定变更一致)
|
|
|
|
|
|
let cubeCount = stackSumOverride != null
|
|
|
|
|
|
? (stackSumOverride.get(weaponID) ?? 0)
|
|
|
|
|
|
: gg.game.CurentBattle.getTetrStackLevelSum(weaponID);
|
|
|
|
|
|
// 仅统计该武器技能 + 全局技能(weapon<=0);排除银币技能 type=3
|
|
|
|
|
|
const skillsForWeapon = (skills ?? []).filter((s) => {
|
|
|
|
|
|
if (!s || s.type === 3) return false;
|
|
|
|
|
|
const wid = s.weapon;
|
|
|
|
|
|
return !wid || wid <= 0 || wid === weaponID;
|
|
|
|
|
|
});
|
|
|
|
|
|
//单一武器技能倍数
|
|
|
|
|
|
let dps = this.calculateDPSWeight(skillsForWeapon, weaponID);
|
|
|
|
|
|
//武器攻击力
|
|
|
|
|
|
let weaponLevel = gg.data.project.getWeaponLevel(weaponID);
|
|
|
|
|
|
let levelPara = gg.data.project.getLevelParaByWeaponLevel(weaponLevel, weaponID);
|
|
|
|
|
|
let attackCoefficient = gg.data.getPlayerAtkCoefficient()
|
|
|
|
|
|
let allAttack = weaponData.weapon_para * attackCoefficient * levelPara;
|
|
|
|
|
|
//武器CD
|
|
|
|
|
|
let cdNum = gg.data.getPlayerCoolCoefficient()
|
|
|
|
|
|
let jisuanCd = weaponData.cd / (1 + cdNum / 10000)
|
|
|
|
|
|
if (!(jisuanCd > 0)) continue;
|
|
|
|
|
|
|
|
|
|
|
|
//单一武器DPS=单一武器方块数*单一武器技能倍数*武器攻击力/武器CD
|
|
|
|
|
|
// weaponpar:表「秒伤」系数;weapon_para:攻击力系数(二者不同字段)
|
|
|
|
|
|
let weaponDps = cubeCount * dps * allAttack / jisuanCd * (weaponData.weaponpar || 1);
|
|
|
|
|
|
// console.log(`weaponDps:`, weaponDps, cubeCount, dps, allAttack, jisuanCd);
|
|
|
|
|
|
|
|
|
|
|
|
all += weaponDps;
|
|
|
|
|
|
}
|
|
|
|
|
|
//console.log(`allDps:`, all);
|
|
|
|
|
|
return all;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**计算一组技能的DPS倍数 */
|
|
|
|
|
|
private calculateDPSWeight(arr: ITableSkill[], weaponId: number) {
|
|
|
|
|
|
//计算已经拥有的所有乘区类型
|
|
|
|
|
|
let typeArr = gg.data.project.getAllMultiplyType(arr);
|
|
|
|
|
|
//this.showLog(`所有乘区类型:`, JSON.stringify(typeArr));
|
|
|
|
|
|
let multDataMap = new Map<number, number>();
|
|
|
|
|
|
//计算已经拥有的所有乘区类型的值
|
|
|
|
|
|
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;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**根据一组技能获取所有乘区类型 */
|
|
|
|
|
|
getAllMultiplyType(skillArr: ITableSkill[]) {
|
|
|
|
|
|
let typeArr: number[] = [];
|
|
|
|
|
|
for (let i = 0; i < skillArr.length; i++) {
|
|
|
|
|
|
let skill = skillArr[i];
|
|
|
|
|
|
let t1 = skill.multiply_type1;
|
|
|
|
|
|
let t2 = skill.multiply_type2;
|
|
|
|
|
|
if (typeArr.indexOf(t1) == -1 && t1 > 0) {
|
|
|
|
|
|
typeArr.push(t1);
|
|
|
|
|
|
}
|
|
|
|
|
|
if (typeArr.indexOf(t2) == -1 && t2 > 0) {
|
|
|
|
|
|
typeArr.push(t2);
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return typeArr;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**根据一组技能计算指定类型乘区值总和 */
|
|
|
|
|
|
getSumValueBySkills(skillArr: ITableSkill[], type: number) {
|
|
|
|
|
|
let sum = 0;
|
|
|
|
|
|
for (let i = 0; i < skillArr.length; i++) {
|
|
|
|
|
|
let skill = skillArr[i];
|
|
|
|
|
|
let t1 = skill.multiply_type1;
|
|
|
|
|
|
let v1 = skill.multiply_num1;
|
|
|
|
|
|
if (t1 == type) {
|
|
|
|
|
|
sum += v1;
|
|
|
|
|
|
}
|
|
|
|
|
|
let t2 = skill.multiply_type2;
|
|
|
|
|
|
let v2 = skill.multiply_num2;
|
|
|
|
|
|
if (t2 == type) {
|
|
|
|
|
|
sum += v2;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
return sum;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**根据乘区暴击率,乘区暴击伤害,其他乘区类型乘区值的总乘积,计算当前技能的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 = gg.data.getPlayerCritCoefficient() / 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;
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 道具绑定数据项
|
|
|
|
|
|
*/
|
|
|
|
|
|
export class ItemData {
|
|
|
|
|
|
constructor(id, num = 1, data: ITableItem = null) {
|
|
|
|
|
|
this.id = id;
|
|
|
|
|
|
this.num = Math.floor(num);
|
|
|
|
|
|
this.staticData = data;
|
|
|
|
|
|
}
|
|
|
|
|
|
/**道具id */
|
|
|
|
|
|
id: number;
|
|
|
|
|
|
/**数量 */
|
|
|
|
|
|
num: number;
|
|
|
|
|
|
//是否是碎片ID转获取武器
|
|
|
|
|
|
isFragment: boolean = false;
|
|
|
|
|
|
/**静态配表数据 */
|
|
|
|
|
|
staticData: ITableItem;
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
* 全局常量类型
|
|
|
|
|
|
*/
|
|
|
|
|
|
export enum ConstNumType {
|
|
|
|
|
|
/**基础生命值 */
|
|
|
|
|
|
BaseHp = 1,
|
|
|
|
|
|
/**默认出怪间隔 */
|
|
|
|
|
|
DefaultMonsterInterval = 4,
|
|
|
|
|
|
/**复活增加银币数 */
|
|
|
|
|
|
ReviveAddCoin = 6,
|
|
|
|
|
|
/**波次预警提前时间 20s(第1关) */
|
|
|
|
|
|
MonsterWarnTime = 7,
|
|
|
|
|
|
/**波次预警提前时间 15s(大于1关) */
|
|
|
|
|
|
MonsterWarnTime2 = 8,
|
|
|
|
|
|
|
|
|
|
|
|
/**看广告增加银币数量 */
|
|
|
|
|
|
VideoAddCoin = 10,
|
|
|
|
|
|
/**广告出现频率20秒*/
|
|
|
|
|
|
AdFrequency = 11,
|
|
|
|
|
|
|
|
|
|
|
|
/**看广告增加银币次数 */
|
|
|
|
|
|
VideoAddCoinTimes = 13,
|
|
|
|
|
|
guideSkill1 = 21,//关卡1引导第1个白银宝箱技能
|
|
|
|
|
|
guideSkill2 = 25,//关卡1引导第2个白银宝箱技能
|
|
|
|
|
|
/**体力自动恢复上限 */
|
|
|
|
|
|
VigourAutoAddLimit = 22,
|
|
|
|
|
|
/**体力自动恢复时间 */
|
|
|
|
|
|
VigourAutoAddTime = 23,
|
|
|
|
|
|
|
|
|
|
|
|
/**宠物钥匙价格 */
|
|
|
|
|
|
PetKeyPrice = 55,
|
|
|
|
|
|
|
|
|
|
|
|
/**绿色宠物显示概率 */
|
|
|
|
|
|
GreenPetShowProb = 56,
|
|
|
|
|
|
/**蓝色宠物显示概率 */
|
|
|
|
|
|
BluePetShowProb = 57,
|
|
|
|
|
|
/**紫色宠物显示概率 */
|
|
|
|
|
|
PurplePetShowProb = 58,
|
|
|
|
|
|
/**橙色宠物显示概率 */
|
|
|
|
|
|
OrangePetShowProb = 59,
|
|
|
|
|
|
/**普通绿色宠物显示概率 */
|
|
|
|
|
|
NormalGreenPetShowProb = 60,
|
|
|
|
|
|
/**普通蓝色宠物显示概率 */
|
|
|
|
|
|
NormalBluePetShowProb = 61,
|
|
|
|
|
|
/**普通紫色宠物显示概率 */
|
|
|
|
|
|
NormalPurplePetShowProb = 62,
|
|
|
|
|
|
/**普通橙色宠物显示概率 */
|
|
|
|
|
|
NormalOrangePetShowProb = 63,
|
|
|
|
|
|
/**广告回血的触发血量 */
|
|
|
|
|
|
AdAddHp = 64,
|
|
|
|
|
|
/**广告回血 回血次数限制*/
|
|
|
|
|
|
AdAddHpLimit = 65,
|
|
|
|
|
|
/**普通——X关后开始产升级卡 */
|
|
|
|
|
|
NCardChapter = 66,
|
|
|
|
|
|
/**困难——X关后开始产升级卡 */
|
|
|
|
|
|
HCardChapter = 67,
|
|
|
|
|
|
/**地狱级——X关后开始产升级卡 */
|
|
|
|
|
|
HellCardChapter = 68,
|
|
|
|
|
|
/**引导使用榴莲的等级 */
|
|
|
|
|
|
GuideUseLianLevel = 69,//引导使用榴莲的等级
|
|
|
|
|
|
/**提示功能看视频次数 */
|
|
|
|
|
|
GuidePosADTimes = 70,
|
|
|
|
|
|
/**弹武器宝箱次数组 */
|
|
|
|
|
|
IsOpenSkillSelect = 71,
|
|
|
|
|
|
/**20波之后,怪物血量系数增长基础值 */
|
|
|
|
|
|
InfiniteModeHpBaseMultiple = 72,
|
|
|
|
|
|
/**20波之后,怪物攻击力增长基础值 */
|
|
|
|
|
|
InfiniteModeAtkBaseMultiple = 73,
|
|
|
|
|
|
/**宝箱波次掉落组,1:掉落,2:不掉 */
|
|
|
|
|
|
BoxWaveDrop = 74,
|
|
|
|
|
|
/**银币刷新消耗*/
|
|
|
|
|
|
CoinRefreshCost = 75,
|
|
|
|
|
|
/**第一关首波刷新方块 */
|
|
|
|
|
|
FirstWaveTetri = 76,
|
|
|
|
|
|
/**第二关首波刷新方块 */
|
|
|
|
|
|
SecondWaveTetri = 77,
|
|
|
|
|
|
/** 怪物刷新无法被选中时间*/
|
|
|
|
|
|
MonsterRefreshUnselectableTime = 78,
|
|
|
|
|
|
/**第2关首波刷新方块*/
|
|
|
|
|
|
FirstWaveTetri2 = 79,
|
|
|
|
|
|
/**橙武副本宝箱波次掉落组,1:掉落,0:不掉*/
|
|
|
|
|
|
OrangeWeaponMode_DanBoxWaveDrop = 80,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
export const ColorDefance = {
|
|
|
|
|
|
/**全局字体统一默认颜色 */
|
|
|
|
|
|
defaultColor: color(138, 94, 68, 255),
|
|
|
|
|
|
defaultBlue: color(45, 123, 161, 255),
|
|
|
|
|
|
defaultYellow: color(171, 109, 31, 255),
|
|
|
|
|
|
warnColor: color(192, 87, 23, 255),
|
|
|
|
|
|
defaultGreen: color(25, 103, 23, 255),
|
|
|
|
|
|
defaultRed: color(146, 32, 29, 255),
|
|
|
|
|
|
defaultOutLineGreen: color(8, 126, 8, 255),
|
|
|
|
|
|
|
|
|
|
|
|
qualityColor: [
|
|
|
|
|
|
color().fromHEX("#8A5E44"),
|
|
|
|
|
|
color().fromHEX("#E5DCC2"),
|
|
|
|
|
|
color().fromHEX("#1a85f0ff"),
|
|
|
|
|
|
color().fromHEX("#fa12eeff"),
|
|
|
|
|
|
color().fromHEX("#ffef0aff"),
|
|
|
|
|
|
color().fromHEX("#FDE77A"),
|
|
|
|
|
|
]
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**商店类型 */
|
|
|
|
|
|
export enum ShopType {
|
|
|
|
|
|
/**人才抽取 */
|
|
|
|
|
|
talent = 1,
|
|
|
|
|
|
/**废品回收 */
|
|
|
|
|
|
waste = 2,
|
|
|
|
|
|
/**商店 */
|
|
|
|
|
|
shop = 3,
|
|
|
|
|
|
/**拍卖 */
|
|
|
|
|
|
auction = 4,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**道具池类型 */
|
|
|
|
|
|
export enum PoolType {
|
|
|
|
|
|
/**普通 */
|
|
|
|
|
|
common = 1,
|
|
|
|
|
|
/**高级 */
|
|
|
|
|
|
high = 2,
|
|
|
|
|
|
/**人才碎片任务基础池子 */
|
|
|
|
|
|
talentTaskBase = 3,
|
|
|
|
|
|
/**人才碎片任务高级池子 */
|
|
|
|
|
|
talentTaskHigh = 4,
|
|
|
|
|
|
/**人才碎片章节基础池子 */
|
|
|
|
|
|
talentChapterBase = 5,
|
|
|
|
|
|
/**人才碎片章节高级池子 */
|
|
|
|
|
|
talentChapterHigh = 6,
|
|
|
|
|
|
/**废品站任务基础池子 */
|
|
|
|
|
|
WasteTaskBase = 7,
|
|
|
|
|
|
/**废品站任务高级池子 */
|
|
|
|
|
|
WasteTaskHigh = 8,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/**俄罗斯方块武器类型
|
|
|
|
|
|
用于确定武器替换*/
|
|
|
|
|
|
export enum TetriWeaponType {
|
|
|
|
|
|
Tetris_Weapon_1 = 1,
|
|
|
|
|
|
Tetris_Weapon_2 = 2,
|
|
|
|
|
|
Tetris_Weapon_3 = 3,
|
|
|
|
|
|
Tetris_Weapon_4 = 4,
|
|
|
|
|
|
Tetris_Weapon_5 = 5,
|
|
|
|
|
|
Tetris_Weapon_6 = 6,
|
|
|
|
|
|
Tetris_Weapon_7 = 7,
|
|
|
|
|
|
Tetris_Weapon_8 = 8,
|
|
|
|
|
|
Tetris_Weapon_9 = 9,
|
|
|
|
|
|
Tetris_Weapon_10 = 10,
|
|
|
|
|
|
Tetris_Weapon_11 = 11,
|
|
|
|
|
|
Tetris_Weapon_12 = 12,
|
|
|
|
|
|
Tetris_Weapon_13 = 13,
|
|
|
|
|
|
Tetris_Weapon_14 = 14,
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/**俄罗斯方块的类型
|
|
|
|
|
|
* "类型id
|
|
|
|
|
|
1:基础方块
|
|
|
|
|
|
2:无重力方块
|
|
|
|
|
|
3:经济武器
|
|
|
|
|
|
4:回复武器
|
|
|
|
|
|
5:藤曼方块
|
|
|
|
|
|
*/
|
|
|
|
|
|
export enum TetriType {
|
|
|
|
|
|
Tetris_Baseattk = 1,
|
|
|
|
|
|
Tetris_Basefloor = 2,
|
|
|
|
|
|
Tetris_BaseGold = 3,
|
|
|
|
|
|
Tetris_Baseheal = 4,
|
|
|
|
|
|
Tetris_Basevine = 5,
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
/**俄罗斯的方块的形状类型cubetype */
|
|
|
|
|
|
export enum CubeType {
|
|
|
|
|
|
Tetris_att_I = 1,
|
|
|
|
|
|
Tetris_att_1_1 = 2,
|
|
|
|
|
|
Tetris_att_J1 = 3,
|
|
|
|
|
|
Tetris_att_J2 = 4,
|
|
|
|
|
|
Tetris_att_J3 = 5,
|
|
|
|
|
|
Tetris_att_J = 6,
|
|
|
|
|
|
Tetris_att_L1 = 7,
|
|
|
|
|
|
Tetris_att_L2 = 8,
|
|
|
|
|
|
Tetris_att_L3 = 9,
|
|
|
|
|
|
Tetris_att_L = 10,
|
|
|
|
|
|
Tetris_att_Z1_1 = 11,
|
|
|
|
|
|
Tetris_att_Z1 = 12,
|
|
|
|
|
|
Tetris_att_Z2_1 = 13,
|
|
|
|
|
|
Tetris_att_Z2 = 14,
|
|
|
|
|
|
Tetris_att_凸_1 = 15,
|
|
|
|
|
|
Tetris_att_凸_2 = 16,
|
|
|
|
|
|
Tetris_att_凸_3 = 17,
|
|
|
|
|
|
Tetris_att_凸 = 18,
|
|
|
|
|
|
Tetris_floor_1a = 101,
|
|
|
|
|
|
Tetris_floor_1b = 102,
|
|
|
|
|
|
Tetris_floor_1c = 103,
|
|
|
|
|
|
Tetris_floor_1d = 104,
|
|
|
|
|
|
Tetris_floor_2a = 105,
|
|
|
|
|
|
Tetris_floor_2b = 106,
|
|
|
|
|
|
Tetris_floor_2c = 107,
|
|
|
|
|
|
Tetris_floor_2d = 108,
|
|
|
|
|
|
Tetris_floor_3a = 109,
|
|
|
|
|
|
Tetris_floor_3b = 110,
|
|
|
|
|
|
Tetris_floor_3c = 111,
|
|
|
|
|
|
Tetris_floor_3d = 112,
|
|
|
|
|
|
Tetris_floor_4a = 113,
|
|
|
|
|
|
Tetris_floor_4b = 114,
|
|
|
|
|
|
Tetris_floor_5a = 115,
|
|
|
|
|
|
Tetris_floor_5b = 116,
|
|
|
|
|
|
Tetris_floor_6a = 117,
|
|
|
|
|
|
Tetris_floor_7a = 118,
|
|
|
|
|
|
Tetris_floor_7b = 119,
|
|
|
|
|
|
Tetris_floor_7c = 120,
|
|
|
|
|
|
Tetris_floor_7d = 121,
|
|
|
|
|
|
Tetris_coin_1 = 19,
|
|
|
|
|
|
Tetris_heal_1 = 20,
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|