using System; using System.Collections.Generic; using Cysharp.Threading.Tasks; using Framework; using Gameplay.Buff; using Gameplay.Character.Utils; using Gameplay.Common; using Gameplay.Net; using Gameplay.Skill; using PhxhSDK; using UnityEngine; using Constants = Framework.Constants; namespace Gameplay.Character { public class CharacterManager : IDisposable, IUpdatable { private readonly Dictionary _characterDict = new Dictionary(32); private readonly List _characterList = new List(); private readonly Dictionary _troopDict = new Dictionary(); public List characterList { get { return _characterList; } } private bool _needRefreshTargets = false; public void Init() { _RegistEvent(); } public async UniTask EnterBattle() { for (var i = 0; i < _characterList.Count; ++i) { var character = _characterList[i]; await character.EnterBattle(); } _needRefreshTargets = true; } public void LeaveBattle() { for (var i = 0; i < _characterList.Count; ++i) { var character = _characterList[i]; character.LeaveBattle(); } } private void _RegistEvent() { EventManager.Instance.Register(EventManager.EventName.INFIGHT_UNIT_CELLINDEX_CHANGE, (Action)_OnAnyCharacterCellIndexChange); EventManager.Instance.Register(EventManager.EventName.INFIGHT_UP_VEHICLE, (Action)_OnAnyCharacterCellIndexChange); EventManager.Instance.Register(EventManager.EventName.INFIGHT_DOWN_VEHICLE, (Action)_OnAnyCharacterCellIndexChange); EventManager.Instance.Register(EventManager.EventName.INFIGHT_UNIT_DEAD, (Action)_OnCharacterDead); } private void _OnCharacterDead(GameUnit deadCharacter) { _needRefreshTargets = true; } private void _OnAnyCharacterCellIndexChange(GameUnit noUse) { _needRefreshTargets = true; } private void _UnRegistEvent() { EventManager.Instance.Unregister(EventManager.EventName.INFIGHT_UNIT_CELLINDEX_CHANGE, (Action)_OnAnyCharacterCellIndexChange); EventManager.Instance.Unregister(EventManager.EventName.INFIGHT_UP_VEHICLE, (Action)_OnAnyCharacterCellIndexChange); EventManager.Instance.Unregister(EventManager.EventName.INFIGHT_DOWN_VEHICLE, (Action)_OnAnyCharacterCellIndexChange); EventManager.Instance.Unregister(EventManager.EventName.INFIGHT_UNIT_DEAD, (Action)_OnCharacterDead); } private void _UpdateTargets() { _needRefreshTargets = true; for (int i = 0; i < _characterList.Count; i++) { var character = _characterList[i]; character.UpdateTargets(); } } public void Dispose() { _UnRegistEvent(); foreach (var charactor in _characterDict) { charactor.Value.Dispose(); } _characterDict.Clear(); } private async UniTask _Create(uint id, CharacterConfigData configData, int teamId, Vector3 pos, float yaw) { DebugUtil.Log("Create Character id: {0} troopId: {1}",id, teamId); var character = new Character(id, configData, teamId); character.transData.disableEvent = true; character.transData.pos = pos; character.transData.yaw = yaw; character.transData.disableEvent = false; character.Node.GameObject.transform.position = pos; character.Node.GameObject.transform.localRotation = Quaternion.Euler(0, yaw, 0); await character.LoadAsync(); await _AddCharacter(character); return character; } private async UniTask _AddCharacter(Character character) { BuffManager.instance.InitCharacter(character); await SkillManager.instance.InitCharacter(character); _characterDict.Add(character.GetID(), character); _characterList.Add(character); _AddToTroop(character); character.buffManager.TriggerEnterBattle(); } private void _RemoveCharacter(Character c, int index = -1) { c?.LeaveBattle(); if (index == -1) { index = _characterList.IndexOf(c); } _characterDict.Remove(c.GetID()); _characterList.RemoveAt(index); _RemoveFromTroop(c); BuffManager.instance.RemoveCharacter(c); SkillManager.instance.RemoveCharacter(c); c.Dispose(); } private void _RemoveFromTroop(Character c) { var teamId = c.commonData.troopId; if (_troopDict.TryGetValue(teamId, out Troops team)) { team.characters.Remove(c); } } public async UniTask CreateNPC(int npcId, int teamId, int level, Vector3 pos, float yaw) { var uId = GUIDManager.GenGuid(); var monsterConfig = TableManager.Instance.Tables.Monster.Get(npcId); var monsterTConfig = TableManager.Instance.Tables.MonsterClass.Get(monsterConfig.MonsterClassID); var monsterLevelConfig = TableManager.Instance.Tables.MonsterLevelUp.Get(level); var configData = new CharacterConfigData(); configData.level = level; configData.attack = Mathf.CeilToInt(monsterTConfig.Attack * monsterConfig.AttackFix * monsterLevelConfig.AttackLevelUpTemplate); var shootCD = 1f / monsterTConfig.ShootSpeed; configData.shootCD = shootCD; configData.firstShootTime = monsterTConfig.FirstShootTime; configData.maxBulletCount = monsterTConfig.ManagazinContain; configData.reloadTime = monsterTConfig.ChangeBulletTime; configData.defense = Mathf.CeilToInt(monsterTConfig.Defence * monsterConfig.DefenceFix * monsterLevelConfig.DefenceLevelUpTemplate); configData.defenseScale = monsterTConfig.DefenceStatusPara; configData.maxHp = Mathf.CeilToInt(monsterTConfig.Hp * monsterConfig.HPFix * monsterLevelConfig.HpLevelUpTemplate); configData.maxShield = Mathf.CeilToInt(monsterTConfig.Shield * monsterConfig.ShieldFix * monsterLevelConfig.ShieldLevelUpTemplate); configData.moveSpeed = monsterTConfig.Speed; configData.retreatSpeed = monsterTConfig.RetreatSpeed; configData.shootDistance = monsterTConfig.ShootRange; configData.moraleAttack = (int)monsterTConfig.YaZhi; configData.initMorale = monsterTConfig.MaxMorale; configData.maxMorale = monsterTConfig.MaxMorale; configData.suppressMorale = monsterTConfig.SuppressMorale; configData.retreatMorale = monsterTConfig.RetreatMorale; configData.moraleRecover = (int)monsterTConfig.CorrageRecover; configData.damagePepoleScale = monsterTConfig.DamageToHuman; configData.damageCarsScale = monsterTConfig.DamageToCarrier; configData.criticalRate = monsterTConfig.CriticalRatio; configData.criticalScale = monsterTConfig.CriticalDamage; configData.moveShootType = monsterTConfig.MoveShootType; configData.breakShootType = (int)monsterTConfig.ShootMode; configData.perShootCount = monsterTConfig.PerShootCount; configData.skillId = monsterTConfig.SkillId; configData.turnSpeed = monsterTConfig.TurnSpeed; configData.job = monsterTConfig.Job; configData.faction = monsterTConfig.Fraction; configData.rarity = 0; configData.modelPath = monsterTConfig.PrefabPath; configData.weaponId = monsterTConfig.WeaponId; configData.crossLevel = monsterTConfig.MoveCrossLevel; configData.standLevel = monsterTConfig.StandCrossLevel; configData.findEnemyRange = monsterConfig.FindEnemyRange; configData.eyeBrowsType = monsterTConfig.EmojiEyeBowsType; configData.eyeBrowColor = monsterTConfig.EmojiEyeBowsColor; configData.eyesColor = monsterTConfig.EmojiEyesColor; configData.inFightEmojiId = monsterTConfig.DefaultEmojiImgId; return await _Create(uId, configData, teamId, pos, yaw); } public async UniTask CreateCharacter(CharacterDataInfo characterInfo, int teamId, Vector3 pos, float yaw) { var cId = characterInfo.Cfg.RoleID; var characterData = TableManager.Instance.Tables.CharacterAttri.Get(cId); var fakeLevel = (int)characterInfo.Level; var levelIndex = fakeLevel - 1; var attackDataList = characterData.Attack; var defenceDataList = characterData.Defence; var hpDataList = characterData.Hp; var shieldDataList = characterData.Shield; var levelAttack = attackDataList[levelIndex]; var levelDefence = defenceDataList[levelIndex]; var levelHp = hpDataList[levelIndex]; var levelShield = shieldDataList[levelIndex]; var skillId = CharacterDataHelper.GetCharacterSkillId(characterInfo); var configData = new CharacterConfigData(); configData.level = fakeLevel; configData.attack = levelAttack; var shootCD = 1f / characterData.ShootSpeed; configData.shootCD = shootCD; configData.firstShootTime = characterData.FirstShootTime; configData.maxBulletCount = characterData.ManagazinContain; configData.reloadTime = characterData.ChangeBulletTime; configData.defense = levelDefence; configData.defenseScale = characterData.DefenceStatusPara; configData.maxHp = levelHp; configData.maxShield = levelShield; configData.moveSpeed = characterData.Speed; configData.retreatSpeed = characterData.RetreatSpeed; configData.shootDistance = characterData.ShootRange; configData.moraleAttack = (int)characterData.YaZhi; configData.initMorale = characterData.MaxMorale; configData.maxMorale = characterData.MaxMorale; configData.suppressMorale = characterData.SuppressMorale; configData.retreatMorale = characterData.RetreatMorale; configData.moraleRecover = (int)characterData.CorrageRecover; configData.damagePepoleScale = characterData.DamageToHuman; configData.damageCarsScale = characterData.DamageToCarrier; configData.criticalRate = characterData.CriticalRatio; configData.criticalScale = characterData.CriticalDamage; configData.moveShootType = characterData.MoveShootType; configData.breakShootType = (int)characterData.ShootMode; configData.perShootCount = characterData.PerShootCount; configData.skillId = skillId; configData.turnSpeed = characterData.TurnSpeed; configData.job = characterData.Job; configData.faction = characterData.Fraction; configData.rarity = characterData.StartStar; configData.modelPath = TableManager.Instance.Tables.SkinCfg.Get(Constants.DEFULT_SKINID,characterData.RoleID).PrefabPath; configData.weaponId = characterData.WeaponId; configData.crossLevel = characterData.MoveCrossLevel; configData.standLevel = characterData.StandCrossLevel; configData.boundTags = new List(); configData.boundTags.Add((int)characterData.Fraction); configData.shiledRecoverDelay = characterData.ShieldRecoverDelay; configData.shieldRecoverFix = characterData.ShiledRecoverValueFixed; configData.shiledRecoverPercent = characterData.ShiledRecoverValuePercent; configData.overflowShield = characterData.OverFlowShiledMax; configData.hpRecoverDelay = characterData.HPRecoverDelay; configData.hpRecoverFix = characterData.HPRecoverValueFixed; configData.hpRecoverPercent = characterData.HPRecoverValuePercent; configData.attackShiledScale = characterData.AttackShiledScale; configData.attackHpScale = characterData.AttackHpScale; configData.eyeBrowsType = characterData.EmojiEyeBowsType; configData.eyesColor = characterData.EmojiEyesColor; configData.eyeBrowColor = characterData.EmojiEyeBowsColor; configData.inFightEmojiId = characterData.DefaultEmojiImgId; return await _Create(characterInfo.ID, configData, teamId, pos, yaw); } private void _AddToTroop(Character c) { var teamId = c.commonData.troopId; if (_troopDict.TryGetValue(teamId, out Troops team)) { team.characters.Add(c); } else { team = new Troops(teamId); team.characters.Add(c); _troopDict.Add(teamId, team); } } public void Remove(uint id) { try { if (_characterDict.TryGetValue(id, out Character c)) { _RemoveCharacter(c); //c.Dispose(); } } catch (Exception e) { DebugUtil.LogError("Remove character error: {0}", e.Message); } } public Character Get(uint id) { Character character; _characterDict.TryGetValue(id, out character); return character; } public void SearchEnemies(GameUnit self, List enemyList) { if (self == null) return; if (self.controlData.followUnit != null && Level.Level.IsEnemy(self.controlData.followUnit.commonData.troopId, self.commonData.troopId)) { enemyList.Add(self.controlData.followUnit); } foreach (var team in _troopDict) { if (Level.Level.IsEnemy(team.Key, self.commonData.troopId)) { enemyList.AddRange(team.Value.characters); } } } public void SearchFriends(GameUnit self, List friendList) { if (self == null) return; if (self.controlData.followUnit != null && !Level.Level.IsEnemy(self.controlData.followUnit.commonData.troopId, self.commonData.troopId)) { friendList.Add(self.controlData.followUnit); } foreach (var team in _troopDict) { if (!Level.Level.IsEnemy(team.Key, self.commonData.troopId)) { friendList.AddRange(team.Value.characters); } } } public void Update(float deltaTime) { for (var i = 0; i < _characterList.Count; ++i) { var character = _characterList[i]; character.Update(deltaTime); if (character.runtimeData.needRemove) { // 触发buff character.buffManager.TriggerExitBattle(); _RemoveCharacter(character, i); i--; } } if (_needRefreshTargets) { _UpdateTargets(); } } public void ForEach(Action action) { foreach (var charactor in _characterDict) { action(charactor.Value); } } public List FindAll(Func conditions) { List result = new List(); foreach (var charactor in _characterDict) { if (conditions(charactor.Value)) { result.Add(charactor.Value); } } return result; } public Character FindOne(Func conditions) { foreach (var charactor in _characterDict) { if (conditions(charactor.Value)) { return charactor.Value; } } return null; } } }