using System; using System.Collections.Generic; using cfg.CharacterCfg; using cfg.item; using cfg.MonsterCfg; using Framework; using Gameplay.Common; using Gameplay.Level; using Gameplay.Level.Data; using Gameplay.Net; using Gameplay.Summon; using Gameplay.Unit.Data; using Gameplay.Utils; using NLDPB; using UnityEngine; namespace Gameplay.Character.Utils { public static class CharacterDataHelper { public static int GetCharacterSkillId(CharacterDataInfo characterInfo) { var skillLevelInfo = TableManager.Instance.Tables.CharacterSkillLevel.Get(characterInfo.Cfg.RoleID, (int)characterInfo.UltraSkillLevel); if (skillLevelInfo == null) { DebugUtil.LogError($"角色ID:{characterInfo.Cfg.RoleID} 等级:{characterInfo.UltraSkillLevel} 无法找到对应的技能等级配置"); return 0; } var skillGroupId = skillLevelInfo.SkillGroupId; var breakLevel = characterInfo.AwakeLevel; var skillReferInfo = TableManager.Instance.Tables.SkillReferConfig.Get(skillGroupId); var breakLevelIndex = (int)breakLevel; var skillId = skillReferInfo.SkillIds[^1]; if (breakLevelIndex < 0 || breakLevelIndex >= skillReferInfo.SkillIds.Count) { DebugUtil.LogError("技能组ID:{0} 突破等级:{1} 无法找到对应的技能ID" , skillGroupId, breakLevel); } else { skillId = skillReferInfo.SkillIds[breakLevelIndex]; } return skillId; } private static void _InitGlobalConfigBefore(CharacterConfigData configData) { configData.shiledRecoverDelay = GLConfig.Inst.data.FightShieldRecoverDelay; configData.shieldRecoverFix = GLConfig.Inst.data.FightShiledRecoverValueFixed; configData.shiledRecoverPercent = GLConfig.Inst.data.FightShiledRecoverValuePercent; configData.hpRecoverDelay = GLConfig.Inst.data.FightHPRecoverDelay; configData.hpRecoverFix = GLConfig.Inst.data.FightHPRecoverValueFixed; configData.hpRecoverPercent = GLConfig.Inst.data.FightHPRecoverValuePercent; configData.turnSpeed = GLConfig.Inst.data.FightCharacterTurnSpeed; } private static void _InitGlobalConfigAfter(CharacterConfigData configData) { configData.overflowShield = Mathf.FloorToInt(GLConfig.Inst.data.FightOverFlowShiledMaxPercent * configData.maxShield); configData.suppressMorale = Mathf.FloorToInt(configData.maxMorale * GLConfig.Inst.data.FightSuppressMoralePercent); configData.retreatMorale = Mathf.FloorToInt(configData.maxMorale * GLConfig.Inst.data.FightRetreateMoralePercent); } private static void _SetWeaponInfo(CharacterConfigData configData, int weaponId, float weaponDegree, float weaponGrasp) { var weaponConfig = TableManager.Instance.Tables.WeaponConfig.GetOrDefault(weaponId); if (weaponConfig == null) { configData.attack = 0; return; } // 熟练度影响攻击力 var weaponAttack = weaponConfig.AttackPower * Mathf.Max(0.01f, weaponDegree); configData.attack = Mathf.CeilToInt(weaponAttack * weaponGrasp); // 掌握度影响攻击速度, 攻击间隔 var cdScale = Mathf.Max(0.01f, weaponGrasp); configData.attackEndTime = weaponConfig.AttackEndTime * cdScale; configData.shootCd = 1f / weaponConfig.ShootSpeed * cdScale; // 其他属性 configData.perReloadCount = weaponConfig.PerReloadCount; configData.canAttackNoReloadFull = weaponConfig.AllowShootAtNoFullReload; configData.raiseWeaponTime = weaponConfig.RaiseWeaponTime; configData.isAttackIgnoreBlock = weaponConfig.AttackIgoreBlock; } public static CharacterConfigData CombineSelfNpcConfigData(LevelData.NPCInfo npcInfo) { var monsterConfig = TableManager.Instance.Tables.Monster.Get(npcInfo.id); var npcConfig = TableManager.Instance.Tables.FightNPCConfig.GetOrDefault(monsterConfig.MonsterClassID); var configData = new CharacterConfigData(); _InitGlobalConfigBefore(configData); configData.cfgId = monsterConfig.MonsterClassID; configData.localId = npcInfo.index; configData.debugShowInfo = new DebugShowInfo(); configData.debugShowInfo.name = npcConfig.NameRead; configData.debugShowInfo.iconPath = ""; configData.debugShowInfo.configId = "FightNPCConfig@" + npcConfig.ConfigId; var weaponConfig = TableManager.Instance.Tables.WeaponConfig.GetOrDefault(npcConfig.WeaponId); if (weaponConfig == null) { throw new Exception("己方NPC配置的武器ID无效"); } var weaponDegree = npcConfig.WeaponDegree * monsterConfig.WeaponDegreeScale; var weaponGrasp = npcConfig.WeaponGrasp * monsterConfig.WeaponGraspScale; _SetWeaponInfo(configData, npcConfig.WeaponId, weaponDegree, weaponGrasp); configData.distanceHitRate = npcConfig.ShootDistanceScale; configData.defense = Mathf.CeilToInt(npcConfig.Defence); configData.defenseScale = npcConfig.DefenceNoMoveScale; configData.maxHp = Mathf.CeilToInt(npcConfig.Hp); configData.maxShield = Mathf.CeilToInt(npcConfig.Shield); configData.moveSpeed = npcConfig.Speed; configData.noFightMoveSpeed = npcConfig.NoFightMoveSpeed; configData.retreatSpeed = npcConfig.RetreatSpeed; configData.maxMorale = npcConfig.MaxMorale; configData.moraleRecover = (int)npcConfig.CorrageRecover; configData.criticalRate = npcConfig.CriticalRatio; configData.criticalScale = npcConfig.CriticalDamage; configData.moveShootType = npcConfig.MoveShootType; configData.skillId = npcConfig.SkillId; configData.job = npcConfig.Job; configData.faction = npcConfig.Fraction; configData.boundTags = new List(); configData.boundTags.Add((int)npcConfig.Fraction); configData.rarity = 0; configData.weaponId = npcConfig.WeaponId; configData.crossLevel = npcConfig.MoveCrossLevel; configData.standLevel = npcConfig.StandCrossLevel; // 使用攻击距离作为索敌距离 configData.findEnemyRange = weaponConfig.AttackDistance; configData.eyeBrowColor = npcConfig.EmojiEyeBowsColor; configData.eyesColor = npcConfig.EmojiEyesColor; configData.nameId = npcConfig.NameId; if (npcConfig.SubType == ENpcCharacterSubType.PlayerMan) { // 根据当前养成数据获得性别 var getIsMan = Actor.Instance.Base.GetIsMan(); if (!getIsMan) { // 女性 configData.nameId += "_1"; } } else if (npcConfig.SubType == ENpcCharacterSubType.PlayerWoman) { var getIsMan = Actor.Instance.Base.GetIsMan(); if (getIsMan) { // 男性 // 移除后缀 configData.nameId = configData.nameId.Substring(0, configData.nameId.Length - 2); } } configData.damageCharacterScale = weaponConfig.DamageScaleForHuman; configData.damageVehicleScale = weaponConfig.DamageScaleForVehicle; configData.damageBuildingsScale = weaponConfig.DamageScaleForBuilding; configData.attackShiledScale = weaponConfig.DamageScaleForShiled; configData.attackHpScale = weaponConfig.DamageScaleForHp; configData.moraleAttack = weaponConfig.MoraleDamage; configData.attackLoopCount = npcConfig.AttackLoopCount; configData.reloadTime = weaponConfig.ReloadTime; configData.maxBulletCount = weaponConfig.MaxBulletCount; configData.shootDistance = weaponConfig.AttackDistance; // TODO: 目前没有怪物的被动技能 configData.breakPassiveSkillId = 0; configData.awakePassiveSkillId = 0; for (int i = 0; i < npcConfig.PassiveBuffIds.Count; i++) { var buffId = npcConfig.PassiveBuffIds[i]; configData.passiveBuffIds.Add(buffId); } _ApplyMonsterScale(configData, monsterConfig); _InitGlobalConfigAfter(configData); // 设置初始值 configData.initMorale = npcConfig.MaxMorale; configData.canEvacuate = npcConfig.CanEvacuate; return configData; } private static void _ApplyMonsterScale(CharacterConfigData configData, DataMonster monsterConfig) { configData.attack = Mathf.CeilToInt(configData.attack * monsterConfig.AttackFix); configData.maxHp = Mathf.CeilToInt(configData.maxHp * monsterConfig.HPFix); configData.maxShield = Mathf.CeilToInt(configData.maxShield * monsterConfig.ShieldFix); configData.defense = Mathf.CeilToInt(configData.defense * monsterConfig.DefenceFix); configData.findEnemyRange = monsterConfig.FindEnemyRange; configData.level = monsterConfig.MonsterLevel; } public static CharacterConfigData CombineEnemyConfigData(int npcId, int localId, int npcLevel) { var monsterConfig = TableManager.Instance.Tables.Monster.Get(npcId); var monsterTConfig = TableManager.Instance.Tables.MonsterClass.Get(monsterConfig.MonsterClassID); var configData = new CharacterConfigData(); _InitGlobalConfigBefore(configData); configData.cfgId = monsterTConfig.MonsterClassID; configData.localId = localId; configData.debugShowInfo = new DebugShowInfo(); configData.debugShowInfo.name = monsterTConfig.Name; configData.debugShowInfo.iconPath = ""; configData.debugShowInfo.configId = "MonsterClass@" + monsterTConfig.MonsterClassID; configData.level = npcLevel; var weaponConfig = TableManager.Instance.Tables.WeaponConfig.GetOrDefault(monsterTConfig.WeaponId); if (weaponConfig == null) { throw new Exception("敌人配置的武器ID无效"); } var weaponDegree = monsterTConfig.WeaponDegree * monsterConfig.WeaponDegreeScale; var weaponGrasp = monsterTConfig.WeaponGrasp * monsterConfig.WeaponGraspScale; _SetWeaponInfo(configData, monsterTConfig.WeaponId, weaponDegree, weaponGrasp); configData.distanceHitRate = monsterTConfig.ShootDistanceScale; configData.defense = Mathf.CeilToInt(monsterTConfig.Defence); configData.defenseScale = monsterTConfig.DefenceNoMoveScale; configData.maxHp = Mathf.CeilToInt(monsterTConfig.Hp); configData.maxShield = Mathf.CeilToInt(monsterTConfig.Shield); configData.moveSpeed = monsterTConfig.Speed; configData.noFightMoveSpeed = monsterTConfig.NoFightMoveSpeed; configData.retreatSpeed = monsterTConfig.RetreatSpeed; configData.maxMorale = monsterTConfig.MaxMorale; configData.moraleRecover = (int)monsterTConfig.CorrageRecover; configData.criticalRate = monsterTConfig.CriticalRatio; configData.criticalScale = monsterTConfig.CriticalDamage; configData.moveShootType = monsterTConfig.MoveShootType; configData.skillId = monsterTConfig.SkillId; configData.job = monsterTConfig.Job; configData.faction = monsterTConfig.Fraction; configData.boundTags = new List(); configData.boundTags.Add((int)monsterTConfig.Fraction); configData.rarity = 0; // configData.modelPath = monsterTConfig.PrefabPath; configData.weaponId = monsterTConfig.WeaponId; configData.crossLevel = monsterTConfig.MoveCrossLevel; configData.standLevel = monsterTConfig.StandCrossLevel; configData.eyeBrowsType = monsterTConfig.EmojiEyeBowsType; configData.eyeBrowColor = monsterTConfig.EmojiEyeBowsColor; configData.eyesColor = monsterTConfig.EmojiEyesColor; configData.nameId = monsterTConfig.NameId; configData.damageCharacterScale = weaponConfig.DamageScaleForHuman; configData.damageVehicleScale = weaponConfig.DamageScaleForVehicle; configData.damageBuildingsScale = weaponConfig.DamageScaleForBuilding; configData.attackShiledScale = weaponConfig.DamageScaleForShiled; configData.attackHpScale = weaponConfig.DamageScaleForHp; configData.moraleAttack = weaponConfig.MoraleDamage; configData.attackLoopCount = monsterTConfig.AttackLoopCount; configData.reloadTime = weaponConfig.ReloadTime; configData.maxBulletCount = weaponConfig.MaxBulletCount; configData.shootDistance = weaponConfig.AttackDistance; configData.findEnemyRange = configData.shootDistance; // TODO: 目前没有怪物的被动技能 configData.breakPassiveSkillId = 0; configData.awakePassiveSkillId = 0; for (int i = 0; i < monsterTConfig.PassiveBuffIds.Count; i++) { var buffId = monsterTConfig.PassiveBuffIds[i]; configData.passiveBuffIds.Add(buffId); } _ApplyMonsterScale(configData, monsterConfig); _InitGlobalConfigAfter(configData); configData.initMorale = configData.maxMorale; return configData; } public static CharacterConfigData CombineConfigData(CharacterDataInfo characterInfo) { var cId = characterInfo.Cfg.RoleID; var characterData = TableManager.Instance.Tables.CharacterAttri.Get(cId); if (characterData == null) { DebugUtil.LogError($"角色ID:{cId} 无法找到对应的角色配置"); return null; } var weaponId = characterInfo.WeaponId(); var weaponConfig = TableManager.Instance.Tables.WeaponConfig.GetOrDefault(weaponId); if (weaponConfig == null) { DebugUtil.LogError($"角色ID:{cId} 无法找到对应的武器配置: {weaponId}"); return null; } var fakeLevel = (int)characterInfo.Level; var levelIndex = fakeLevel - 1; var defenceDataList = characterData.Defence; var hpDataList = characterData.Hp; var shieldDataList = characterData.Shield; var levelDefence = defenceDataList[levelIndex];//GetLevelUpData(characterData.LevelUpTemplate, fakeLevel); var levelHp = hpDataList[levelIndex]; var levelShield = shieldDataList[levelIndex]; var skillId = GetCharacterSkillId(characterInfo); var configData = new CharacterConfigData(); _InitGlobalConfigBefore(configData); configData.cfgId = cId; configData.level = fakeLevel; var weaponDegree = characterInfo.WeaponDegree(); var weaponGrasp = characterInfo.WeaponGrasp(); _SetWeaponInfo(configData, weaponId, weaponDegree, weaponGrasp); configData.distanceHitRate = characterData.ShootDistanceScale; configData.defense = levelDefence; configData.defenseScale = characterData.DefenceNoMoveScale; configData.maxHp = levelHp; configData.maxShield = levelShield; configData.moveSpeed = characterData.Speed; configData.noFightMoveSpeed = characterData.NoFightMoveSpeed; configData.retreatSpeed = characterData.RetreatSpeed; configData.initMorale = characterData.MaxMorale; configData.maxMorale = characterData.MaxMorale; configData.moraleRecover = (int)characterData.CorrageRecover; configData.criticalRate = characterData.CriticalRatio; configData.criticalScale = characterData.CriticalDamage; configData.moveShootType = characterData.MoveShootType; configData.skillId = skillId; configData.job = characterData.Job; configData.faction = CommonUtils.GetEfactionByCfg(characterData); configData.rarity = characterData.StartStar; var skinId = (int)characterInfo.SkinID; var skinConfig = TableManager.Instance.Tables.SkinCfg.Get(skinId, characterData.RoleID); if (skinConfig == null) { DebugUtil.LogError($"{characterData.NameRead} 无有效皮肤配置: {skinId}"); configData.nameId = characterData.NameID; } else { configData.nameId = skinConfig.NameID; } configData.weaponId = characterData.WeaponId; configData.crossLevel = characterData.MoveCrossLevel; configData.standLevel = characterData.StandCrossLevel; configData.boundTags = new List { (int)CommonUtils.GetEfactionByCfg(characterData) }; configData.eyeBrowsType = characterData.EmojiEyeBowsType; configData.eyesColor = characterData.EmojiEyesColor; configData.eyeBrowColor = characterData.EmojiEyeBowsColor; configData.attackHpScale = characterInfo.DamageScaleToHp(); configData.attackShiledScale = characterInfo.DamageScaleToShiled(); configData.damageCharacterScale = characterInfo.DamageScaleToHuman(); configData.damageVehicleScale = characterInfo.DamageScaleToCarrier(); configData.damageBuildingsScale = characterInfo.DamageScaleToBuilding(); configData.moraleAttack = Mathf.FloorToInt(characterInfo.YaZhi()); configData.attackLoopCount = characterInfo.AttackLoopCount(); configData.reloadTime = characterInfo.ChangeBulletTime(); configData.maxBulletCount = characterInfo.BulletCapacity(); configData.shootDistance = characterInfo.AttackDistance(); // TODO: 角色单位使用攻击距离作为索敌距离 configData.findEnemyRange = configData.shootDistance; if (characterInfo.BreakCfg == null) { configData.breakPassiveSkillId = 0; DebugUtil.LogError("角色ID:{0} 突破配置为空, 当前突破等级: {1}", cId, characterInfo.BreakLevel); } else { configData.breakPassiveSkillId = characterInfo.BreakCfg.PassiveSkillID; } if (characterInfo.AwakeCfg == null) { configData.awakePassiveSkillId = 0; DebugUtil.LogError("角色ID:{0} 觉醒配置为空, 当前觉醒等级: {1}", cId, characterInfo.AwakeLevel); } else { configData.awakePassiveSkillId = characterInfo.AwakeCfg.PassiveSkillID; } configData.debugShowInfo = new DebugShowInfo(); configData.debugShowInfo.name = characterData.NameRead; // Assets/Art/UI/Texture/Icon/HeadPic/Icon_HeadPic_A00001.png configData.debugShowInfo.iconPath = skinConfig == null ? "Null" : skinConfig.PIcHeadPath; configData.debugShowInfo.iconPath = $"Assets/Art/UI/Texture/Icon/HeadPic/{configData.debugShowInfo.iconPath}.png"; configData.debugShowInfo.configId = "CharacterDefault@" + cId; configData.debugShowInfo.cacheConfigId = characterInfo.Cfg.RoleID; _InitGlobalConfigAfter(configData); configData.canEvacuate = true; return configData; } public static cfg.CharacterCfg.DataCharacterLevelup GetLevelUpData(int TemplateID, int level) { var _data = TableManager.Instance.Tables.CharacterLevelUp.Get(TemplateID, level); return _data; } public static Ejob ProvideJobToNormalJob(ProvideJob job) { return job switch { ProvideJob.ProvideJob_Scout => Ejob.Job_Scout, ProvideJob.ProvideJob_Shoot => Ejob.Job_Shoot, ProvideJob.ProvideJob_Support => Ejob.Job_Support, ProvideJob.ProvideJob_Suppress => Ejob.Job_Suppress, _ => Ejob.Job_Commander }; } public static ProvideJob NormalJobToProvideJob(Ejob job) { return job switch { Ejob.Job_Scout => ProvideJob.ProvideJob_Scout, Ejob.Job_Shoot => ProvideJob.ProvideJob_Shoot, Ejob.Job_Support => ProvideJob.ProvideJob_Support, Ejob.Job_Suppress => ProvideJob.ProvideJob_Suppress, _ => ProvideJob.ProvideJob_Unlimited }; } /// /// 是否显示觉醒红点 /// /// /// public static bool CheckCharacterCanAwake(uint characterID) { var mCharacterDataInfo = Actor.Instance.Character.GetCharacter(characterID); var mCharacterAwakeCfg = TableManager.Instance.Tables.CharacterAwaking; if (mCharacterDataInfo == null || mCharacterAwakeCfg == null) return false; var Storage = StorageMgr.Instance.GetStorage("Character_Awake_Cost"); Dictionary itemCounts = new Dictionary(); int maxLevel = GetCharacterAwakeMaxLevel(characterID); if (Storage != null && Storage.MaxLevel.ContainsKey((int)characterID)) { return maxLevel > Storage.MaxLevel[(int)characterID]; } return maxLevel > (int)mCharacterDataInfo.AwakeLevel; } /// /// 获取角色觉醒可达最大等级 /// /// /// public static int GetCharacterAwakeMaxLevel(uint characterID) { var mCharacterDataInfo = Actor.Instance.Character.GetCharacter(characterID); var mCharacterAwakeCfg = TableManager.Instance.Tables.CharacterAwaking; Dictionary itemCounts = new Dictionary(); if (mCharacterAwakeCfg == null) return 0; int maxLevel = 0; for (maxLevel = (int)mCharacterDataInfo.AwakeLevel; maxLevel < 4;) { DataCharacterAwaking awakeCfg = mCharacterAwakeCfg.Get((int)characterID, maxLevel + 1); if (awakeCfg == null) { //未找到下一级觉醒配置,当前已经是最高级 break; } foreach (var item in awakeCfg.CostItems) { if (itemCounts.ContainsKey(item.Id)) { itemCounts[item.Id] += item.Count; } else { itemCounts.Add(item.Id, item.Count); } if (CategoryManager.Instance.GetItemCount(item.Id) < itemCounts[item.Id]) { return maxLevel; } } maxLevel++; } return maxLevel; } public static bool IsNewCharacter(int characterID) { var data = Actor.Instance.Character.GetCharacter((uint)characterID);//CharacterDataInfoManager.Instance.GetCharacterInfo((uint)characterID); if (data == null) { DebugUtil.LogWarning($"没有找到角色ID为{characterID}的角色信息"); return false; } return data.isCheck == 1; } } } public class CharacterAwakeCost { public Dictionary MaxLevel = new Dictionary(); public CharacterAwakeCost() { } }