425 lines
16 KiB
C#
425 lines
16 KiB
C#
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<uint, Character> _characterDict = new Dictionary<uint, Character>(32);
|
|
private readonly List<Character> _characterList = new List<Character>();
|
|
private readonly Dictionary<int, Troops> _troopDict = new Dictionary<int, Troops>();
|
|
|
|
public List<Character> 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<GameUnit>)_OnAnyCharacterCellIndexChange);
|
|
EventManager.Instance.Register(EventManager.EventName.INFIGHT_UP_VEHICLE, (Action<GameUnit>)_OnAnyCharacterCellIndexChange);
|
|
EventManager.Instance.Register(EventManager.EventName.INFIGHT_DOWN_VEHICLE, (Action<GameUnit>)_OnAnyCharacterCellIndexChange);
|
|
EventManager.Instance.Register(EventManager.EventName.INFIGHT_UNIT_DEAD, (Action<GameUnit>)_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<GameUnit>)_OnAnyCharacterCellIndexChange);
|
|
EventManager.Instance.Unregister(EventManager.EventName.INFIGHT_UP_VEHICLE, (Action<GameUnit>)_OnAnyCharacterCellIndexChange);
|
|
EventManager.Instance.Unregister(EventManager.EventName.INFIGHT_DOWN_VEHICLE, (Action<GameUnit>)_OnAnyCharacterCellIndexChange);
|
|
EventManager.Instance.Unregister(EventManager.EventName.INFIGHT_UNIT_DEAD, (Action<GameUnit>)_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<Character> _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);
|
|
}
|
|
|
|
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<Character> 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.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.eyesColor = monsterTConfig.EmojiEyesColor;
|
|
|
|
|
|
return await _Create(uId, configData, teamId, pos, yaw);
|
|
}
|
|
|
|
public async UniTask<Character> 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.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<int>();
|
|
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;
|
|
|
|
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<GameUnit> 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<GameUnit> 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)
|
|
{
|
|
_RemoveCharacter(character, i);
|
|
i--;
|
|
}
|
|
}
|
|
|
|
if (_needRefreshTargets)
|
|
{
|
|
_UpdateTargets();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
public void ForEach(Action<Character> action)
|
|
{
|
|
foreach (var charactor in _characterDict)
|
|
{
|
|
action(charactor.Value);
|
|
}
|
|
}
|
|
|
|
public List<Character> FindAll(Func<Character, bool> conditions)
|
|
{
|
|
List<Character> result = new List<Character>();
|
|
foreach (var charactor in _characterDict)
|
|
{
|
|
if (conditions(charactor.Value))
|
|
{
|
|
result.Add(charactor.Value);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
public Character FindOne(Func<Character, bool> conditions)
|
|
{
|
|
foreach (var charactor in _characterDict)
|
|
{
|
|
if (conditions(charactor.Value))
|
|
{
|
|
return charactor.Value;
|
|
}
|
|
}
|
|
return null;
|
|
}
|
|
}
|
|
}
|