using System; using System.Collections; using System.Collections.Generic; using System.Linq; using Cysharp.Threading.Tasks; using Framework; using Framework.Wrapper; using Gameplay; using Gameplay.Character; using Gameplay.Common; using Gameplay.Effect; using Gameplay.Level; using Gameplay.Net; using Gameplay.Unit; using Gameplay.Utils; using Gameplay.Vehicle.Impl; using PhxhSDK; using TGS; using UnityEngine; using Action = BehaviorDesigner.Runtime.Tasks.Action; using Constants = PhxhSDK.Constants; using Object = UnityEngine.Object; /// /// 用于布阵 /// public class PrepareTeamManager : Singlenton { private const string STAGE_ROOT_NAME = "StageRoot"; private Map _map; private int _prepareRegionId; private int _enemyPrepareRegionId; private LevelData.CharacterToward _toward; private TerrainGridSystem _tgs; private Quaternion _rotation; private bool _hasInit; private Transform _stageRoot; public const int MaxOnFightNum = 6; private List _pvpUnitInfos; //已上阵的阵容 public List PvpUnitInfos => _pvpUnitInfos; private int _onFightVehicleID; //已上阵载具ID private List _onFightCharIDs = new(); //已上阵角色ID private Dictionary _LoadedAssets = new(); //key:cellIndex private Dictionary _onStageUnits = new(); //key:prefabPath private Dictionary> _modelPool = new(); private Dictionary _gameObjectToPathDic = new(); private EffectPlayingData _selectGFX; //特效数据对象 #region API public event Action OnAddModel; public event Action OnRemoveModel; public event Action OnSwapModel; //当前选择的预制体 public string CurrPrefabPath { get; set; } //是否可以放置多个相同预制 public bool CanMultiple { get; set; } public void Init(TerrainGridSystem tgs, MapData mapData, int prepareRegionID, LevelData.CharacterToward toward, Camera camera, int enemyPrepareRegionID, Dictionary _initPrepareInfo = null) { if (_hasInit) Dispose(); _tgs = tgs; _map = MapUtils.LoadMap(tgs, mapData); MapUtils.BindCameraController(camera, _map); _tgs.showCells = true; if (prepareRegionID < 0 || prepareRegionID >= mapData.regions.Count) { Debug.LogError($"找不到region!,regionID:{prepareRegionID}"); return; } if (enemyPrepareRegionID < 0 || enemyPrepareRegionID >= mapData.regions.Count) { Debug.LogError($"找不到region!,regionID:{prepareRegionID}"); return; } _prepareRegionId = prepareRegionID; _enemyPrepareRegionId = enemyPrepareRegionID; _toward = toward; _rotation = Quaternion.Euler(new Vector3(0, LevelUtils.SetCharaterToward(_toward), 0)); _hasInit = true; _stageRoot = new GameObject(STAGE_ROOT_NAME).transform; RevertPreparedModels(_initPrepareInfo); InputManager.Instance.OnClick += OnClick; ShowEnemyPrepareRegion(); ShowPrepareRegion(); RegisterEvents(); } public void Dispose() { _hasInit = false; _map = null; _tgs = null; _onStageUnits.Clear(); _modelPool.Clear(); _gameObjectToPathDic.Clear(); Object.Destroy(_stageRoot.gameObject); _stageRoot = null; InputManager.Instance.OnClick -= OnClick; OnAddModel = null; OnRemoveModel = null; OnSwapModel = null; CurrPrefabPath = null; CanMultiple = false; _selectGFX = null; _pvpUnitInfos.Clear(); _onFightCharIDs.Clear(); _onFightVehicleID = 0; _UnLoadAssets(); UnRegisterEvents(); } private void RegisterEvents() { EventManager.Instance.Register(EventManager.EventName.Level_OnClick, (Action) OnClick); } private void UnRegisterEvents() { EventManager.Instance.Unregister(EventManager.EventName.Level_OnClick, (Action) OnClick); } #region Model //之前已经布置过的模型重新放置上来 public void RevertPreparedModels(Dictionary preparedData) { if (preparedData == null) return; foreach (var kv in preparedData) { AddModel(kv.Value, kv.Key); } } public void AutoAddModel(string path, int cellIndex) { AddModel(path, cellIndex); } public void ShowPrepareRegion() { _tgs.showCells = true; //todo 颜色从配置中获取 _map.SetRegionColor(_prepareRegionId, true, ColorEx.FromStr("#FF960072")); } public void ShowEnemyPrepareRegion() { _map.SetRegionColor(_enemyPrepareRegionId, true, ColorEx.FromStr("#EC808D")); } public void HidePrepareRegion() { _tgs.showCells = false; _map.SetRegionColor(_prepareRegionId, false, ColorEx.FromStr("#FF960072")); } public void ShowSelectFlag() { } private async UniTask _LoadAssetAsync(string path) where T : Object { var asset = await AssetManager.Instance.LoadAssetAsync(path); if (!asset) return null; if (_LoadedAssets.ContainsKey(path)) { ++_LoadedAssets[path]; } else { _LoadedAssets.Add(path, 1); } return asset; } private void _UnLoadAssets() { foreach (var kv in _LoadedAssets) { var path = kv.Key; var count = kv.Value; for (int i = count; i > 0; --i) { AssetManager.Instance.Unload(path); } } } #endregion private void OnClick(Vector2 screenPos) { var cellIndex = GroundHitHelper.CalcHitCellIndex(screenPos, _tgs); //点击非出战区 if (!_map.MapData.IsCellInsideRegion(cellIndex, _prepareRegionId)) { CurrPrefabPath = null; EventManager.Instance.Send(EventManager.EventName.OnUnSelectPvPUnit); _HideSelectView(); return; } //未选中时点击格子 if (string.IsNullOrEmpty(CurrPrefabPath) && _onStageUnits.ContainsKey(cellIndex)) { var unitInfo = _pvpUnitInfos.Find(unit => unit.cellIndex == cellIndex); if (unitInfo != null) { EventManager.Instance.Send(EventManager.EventName.OnSelectPvPUnit, unitInfo.unitID); } _ShowSelectView(cellIndex); return; } _HideSelectView(); if (cellIndex >= 0 && _map.MapData.IsCellInsideRegion(cellIndex, _prepareRegionId) && !string.IsNullOrEmpty(CurrPrefabPath)) { //获得当前选择模型在台上的index var currPrefabOnStageIndex = GetCellIndexOnStage(CurrPrefabPath); if (_onStageUnits.TryGetValue(cellIndex, out var go)) { //如果点击的位置已经有模型了 var pathOnStage = GameObjectToPath(go); if (pathOnStage == CurrPrefabPath) { //台上的模型路径和当前选择的路径相同 下阵 RemoveModel(cellIndex); EventManager.Instance.Send(EventManager.EventName.OnPVPUnitRemove); return; } } if (currPrefabOnStageIndex >= 0 && !CanMultiple) { //如果台上已经有当前选择模型,则和点击位置的模型(可能是空)互换位置 SwapModel(currPrefabOnStageIndex, cellIndex); EventManager.Instance.Send(EventManager.EventName.OnUnSelectPvPUnit); _HideSelectView(); } else { //如果台上没有当前选择的模型,则替换掉当前点击位置的模型 if (!_onStageUnits.ContainsKey(cellIndex)) { if (UI_DefendSettingController.SelectID > 0) { bool isLimited = false; if (_CheckIsCharacter(UI_DefendSettingController.SelectID)) { if (CheckIsCharacterCountLimit()) { isLimited = true; } } if(_CheckIsVehicle(UI_DefendSettingController.SelectID)) { if (CheckIsVehicleCountLimit()) { isLimited = true; } } if (_CheckIsBuilding(UI_DefendSettingController.SelectID)) { if (CheckIsBuildingCountLimit()) { isLimited = true; } } if (isLimited) { UITipsManager.ShowTips(CommonUtils.GetLocalizeText("pvp_count_limit")); EventManager.Instance.Send(EventManager.EventName.OnUnSelectPvPUnit); _HideSelectView(); return; } } } //车换人/人换车 if (_onStageUnits.ContainsKey(cellIndex)) { if (UI_DefendSettingController.SelectID > 0) { bool isLimited = false; var unitInfo = _pvpUnitInfos.Find(unit => unit.cellIndex == cellIndex); if (unitInfo != null) { if (_CheckIsCharacter(UI_DefendSettingController.SelectID)) { if (unitInfo.type == Unit.UnitType.Vehicle) { if (CheckIsCharacterCountLimit()) { isLimited = true; } } if (unitInfo.type == Unit.UnitType.Building) { if (CheckIsCharacterCountLimit()) { isLimited = true; } } } if (_CheckIsVehicle(UI_DefendSettingController.SelectID)) { if (unitInfo.type == Unit.UnitType.Charactor) { if (CheckIsVehicleCountLimit()) { isLimited = true; } } if (unitInfo.type == Unit.UnitType.Building) { if (CheckIsVehicleCountLimit()) { isLimited = true; } } } if (_CheckIsBuilding(UI_DefendSettingController.SelectID)) { if (unitInfo.type == Unit.UnitType.Charactor) { if (CheckIsBuildingCountLimit()) { isLimited = true; } } if (unitInfo.type == Unit.UnitType.Vehicle) { if (CheckIsBuildingCountLimit()) { isLimited = true; } } } if (isLimited) { UITipsManager.ShowTips(CommonUtils.GetLocalizeText("pvp_count_limit")); EventManager.Instance.Send(EventManager.EventName.OnUnSelectPvPUnit); _HideSelectView(); return; } } } } AddModel(CurrPrefabPath, cellIndex); EventManager.Instance.Send(EventManager.EventName.OnUnSelectPvPUnit); _HideSelectView(); } } } private string GameObjectToPath(GameObject go) { return _gameObjectToPathDic.GetValueOrDefault(go); } private int GetCellIndexOnStage(string prefabPath) { foreach (var kv in _onStageUnits) { var cellIndex = kv.Key; var go = kv.Value; if (_gameObjectToPathDic[go] == prefabPath) { return cellIndex; } } return -1; } private async UniTask GetAModel(string prefabPath) { GameObject model = null; if (_modelPool.TryGetValue(prefabPath, out var modelList)) { if (modelList.Count > 0) { model = modelList[^1]; modelList.RemoveAt(modelList.Count - 1); } } if (model == null) { var go = await _LoadAssetAsync(prefabPath); model = Object.Instantiate(go, _stageRoot); _gameObjectToPathDic.Add(model, prefabPath); } return model; } private void ReturnModelToPool(GameObject model) { var path = _gameObjectToPathDic[model]; if (!_modelPool.TryGetValue(path, out var modelList)) { modelList = new(); _modelPool.Add(path, modelList); } modelList.Add(model); } private async void AddModel(string prefabPath, int cellIndex) { if (_onStageUnits.ContainsKey(cellIndex)) { RemoveModel(cellIndex); } OnAddModel?.Invoke(cellIndex, prefabPath); var model = await GetAModel(prefabPath); model.SetActive(true); model.SetLayerEx(LayerMask.NameToLayer("UIView")); // var animTrans = model.transform.GetChild(0); // if (animTrans) // { // var anim = animTrans.GetComponent(); // if (anim) // { // anim.Play("idle"); // } // } SetModelPosition(model, cellIndex); _onStageUnits.Add(cellIndex, model); } private void RemoveModel(int cellIndex) { if (_onStageUnits.TryGetValue(cellIndex, out var model)) { model.SetActive(false); _onStageUnits.Remove(cellIndex); ReturnModelToPool(model); OnRemoveModel?.Invoke(cellIndex); } } private void SwapModel(int cellIndexLeft, int cellIndexRight) { if (cellIndexLeft == cellIndexRight || !_map.MapData.IsCellInsideRegion(cellIndexLeft, _prepareRegionId) || !_map.MapData.IsCellInsideRegion(cellIndexRight, _prepareRegionId)) return; var leftModel = _onStageUnits.GetValueOrDefault(cellIndexLeft); var rightModel = _onStageUnits.GetValueOrDefault(cellIndexRight); _onStageUnits.Remove(cellIndexLeft); _onStageUnits.Remove(cellIndexRight); var unitLeft = _pvpUnitInfos.Find(unit => unit.cellIndex == cellIndexLeft); var unitRight = _pvpUnitInfos.Find(unit => unit.cellIndex == cellIndexRight); if (leftModel != null) { if (unitLeft != null) { unitLeft.cellIndex = cellIndexRight; } _onStageUnits.Add(cellIndexRight, leftModel); SetModelPosition(leftModel, cellIndexRight); } if (rightModel != null) { if (unitRight != null) { unitRight.cellIndex = cellIndexLeft; } _onStageUnits.Add(cellIndexLeft, rightModel); SetModelPosition(rightModel, cellIndexLeft); } OnSwapModel?.Invoke(cellIndexLeft, cellIndexRight); } private void SetModelPosition(GameObject model, int cellIndex) { var positionWS = _map.TGSCellIndex2WorldPosition(cellIndex); model.transform.SetPositionAndRotation(positionWS, _rotation); } #endregion #region Data private void _AddUnit(PVPUnitInfo unit) { _pvpUnitInfos?.Add(unit); } private void _RemoveUnit(PVPUnitInfo unit) { _pvpUnitInfos?.Remove(unit); } private void _InitUnitInfos() { if (_pvpUnitInfos == null) { _pvpUnitInfos = new(); } else { _pvpUnitInfos.Clear(); } _pvpUnitInfos.AddRange(PVPUnitInfo.ParseMyPvpDefenceData()); } private PVPUnitInfo _CreatePVPUnitInfo(int cellIndex, uint id) { PVPUnitInfo unitInfo = new PVPUnitInfo(); var allCharacterInfosDic = CharacterDataInfoManager.Instance.DataDic; var allVehicleDic = VehicleCultivateDataManager.Instance.DataDic; var allBuildingDic = FightBuildingDataManager.Instance.DataDic; unitInfo.unitID = id; if (allCharacterInfosDic.ContainsKey(id)) { unitInfo.type = Unit.UnitType.Charactor; } else if (allVehicleDic.ContainsKey(id)) { unitInfo.type = Unit.UnitType.Vehicle; } else if (allBuildingDic.ContainsKey(id)) { unitInfo.type = Unit.UnitType.Building; } else { return null; } //unitInfo.type = allCharacterInfosDic.ContainsKey(id)? Unit.UnitType.Charactor : Unit.UnitType.Vehicle; unitInfo.cellIndex = cellIndex; return unitInfo; } private bool _CheckIsCharacter(uint uid) { return CharacterDataInfoManager.Instance.DataDic.ContainsKey(uid); } private bool _CheckIsVehicle(uint uid) { return VehicleCultivateDataManager.Instance.DataDic.ContainsKey(uid); } private bool _CheckIsBuilding(uint uid) { return FightBuildingDataManager.Instance.DataDic.ContainsKey(uid); } #endregion #region SelectView private void _ShowSelectView(int cellIndex) { if (_selectGFX == null) { _selectGFX = _GetSelectGFXObj(); } var pos = _GetSelectCellPos(cellIndex); _selectGFX.transform.position = pos; _selectGFX.rootObj.SetLayerEx(LayerDefine.UIView); _selectGFX.SetActive(true); } private void _HideSelectView() { if (_selectGFX == null) { return; } _selectGFX.SetActive(false); } private EffectPlayingData _GetSelectGFXObj() { if (_selectGFX == null) { _selectGFX = EffectManager.instance.PlayEffectById(Framework.Constants.EID_CHARACTER_SELECT); _selectGFX.SetActive(false); } return _selectGFX; } private Vector3 _GetSelectCellPos(int cellIndex) { return _map.TGSCellIndex2WorldPosition(cellIndex); } #endregion #region API public PVPUnitInfo CreateUnitInfo(int cellIndex, uint id) { return _CreatePVPUnitInfo(cellIndex, id); } public void AddUnit(PVPUnitInfo unit) { _AddUnit(unit); } public void RemoveUnit(PVPUnitInfo unit) { _RemoveUnit(unit); } public void InitUnitInfos() { _InitUnitInfos(); } public void OnSelectUnit(uint uid) { EventManager.Instance.Send(EventManager.EventName.OnSelectPvPUnit, uid); } public void OnCancelSelectUnit() { EventManager.Instance.Send(EventManager.EventName.OnUnSelectPvPUnit); } public void RemoveAllModels() { var keyList = _onStageUnits.Keys.ToList(); foreach (var cell in keyList) { RemoveModel(cell); } } public bool CheckIsCharacterCountLimit() { if (_pvpUnitInfos?.Count < GLConfig.Inst.data.PvpDefenderMaxNumber) { return false; } if (_pvpUnitInfos != null) { var count = 0; foreach (var unit in _pvpUnitInfos) { if (unit.type == Unit.UnitType.Charactor) { ++count; } } return count >= GLConfig.Inst.data.PvpFightCharacterLimit; } return false; } public bool CheckIsVehicleCountLimit() { if (_pvpUnitInfos != null) { var count = 0; foreach (var unit in _pvpUnitInfos) { if (unit.type == Unit.UnitType.Vehicle) { ++count; } } return count >= GLConfig.Inst.data.PvpFightVehicleLimit; } return false; } public bool CheckIsBuildingCountLimit() { if (_pvpUnitInfos != null) { var count = 0; foreach (var unit in _pvpUnitInfos) { if (unit.type == Unit.UnitType.Building) { ++count; } } return count >= GLConfig.Inst.data.PvpFightBuildLimit; } return false; } public bool CheckIsCellIndexBlank(int cellIndex) { if (_onStageUnits == null) { return false; } return !_onStageUnits.ContainsKey(cellIndex); } #endregion }