using FL.Battle; using FL.Data; using FL.FGUI; using FL.Map; using System.Collections.Generic; using System.Linq; using UnityEngine; using XGame; using XGame.Database; using XGame.Framework; using XGame.Framework.Map; using XGame.Framework.Nodes; using XGame.Framework.Time; namespace FL.Nodes.GameMain { public class MapComponent : NodeComponent { private int _chapterId; private EMapSchedule _schedule; private ITimer _monsterTimer; /// /// boss战斗倒计时 /// private ITimer _bossTimer; /// /// 是否挑战boss失败 /// 失败后战斗进入该关卡的小怪节点循环 /// UI显示挑战boss按钮 /// private bool _isBossFailed; /// /// 是否在挑战boss /// private bool _isBossChallenge; private PlayerEntity _player; ///// ///// 怪物坐标相对出生点的偏移值 ///// 怪物最多13个,分两排排列,索引如下 ///// 二排 9 5 1 2 6 10 ///// 一排 11 7 3 0 4 8 12 ///// 0的左边偏移值为负数 ///// //private Vector3 _monsterOffset = new Vector3(0.8f, 1.5f, 0); ///// ///// 空闲的怪物位置索引 ///// //private List _unusedIndexs; /// /// 怪物的实例对象 /// private Dictionary _monsterMap; /// /// 伙伴的实例对象 /// private Dictionary _partnerMap; private MapKey _currentMap; public override void OnEnable(object intent) { EventSingle.Instance.AddListener(EventDefine.GameMainMapRemoveEntity, OnRemoveEntity); EventSingle.Instance.AddListener(EventDefine.GameMainMapEndMask, OnMapEndMask); EventSingle.Instance.AddListener(EventDefine.PartnerDown, OnPartnerDown); EventSingle.Instance.AddListener(EventDefine.PartnerUp, OnPartnerUp); EventSingle.Instance.AddListener(EventDefine.PartnerPlanChange, OnPartnersChanged); EventSingle.Instance.AddListener(EventDefine.PartnerWearAll, OnPartnersChanged); EventSingle.Instance.AddListener(EventDefine.GameMainMapChallengeBoss, OnChallengeBoss); //InitMonsters(); StartMap(PlayerData.Instance.chapterId); } public override void OnDisable() { EventSingle.Instance.RemoveListener(EventDefine.GameMainMapRemoveEntity, OnRemoveEntity); EventSingle.Instance.RemoveListener(EventDefine.GameMainMapEndMask, OnMapEndMask); EventSingle.Instance.RemoveListener(EventDefine.PartnerDown, OnPartnerDown); EventSingle.Instance.RemoveListener(EventDefine.PartnerUp, OnPartnerUp); EventSingle.Instance.RemoveListener(EventDefine.PartnerPlanChange, OnPartnersChanged); EventSingle.Instance.RemoveListener(EventDefine.PartnerWearAll, OnPartnersChanged); EventSingle.Instance.RemoveListener(EventDefine.GameMainMapChallengeBoss, OnChallengeBoss); _player = null; _partnerMap?.Clear(); _monsterTimer?.Cancel(); _monsterTimer = null; _bossTimer?.Cancel(); _bossTimer = null; _isBossFailed = false; _isBossChallenge = false; _monsterMap?.Clear(); //_unusedIndexs?.Clear(); Context.Map.Close(_currentMap); _currentMap = null; } //private void InitMonsters() //{ // if (_unusedIndexs == null) // _unusedIndexs = new List(); // else // _unusedIndexs.Clear(); // for (var i = 0; i < 13; i++) // { // _unusedIndexs.Add(i); // } //} private void StartMap(int chapterId) { _chapterId = chapterId; _schedule = EMapSchedule.LoadingMap; var chapter = ChapterTableRepo.Get(_chapterId); var map = MapTableRepo.Get(chapter.Map); var mapKey = MapKeys.NameToKey(map.AssetName); if (mapKey == _currentMap) { // 是同一个地图 StartChapter(true, false); } else { // 关闭当前地图 Context.Map.Close(_currentMap); _currentMap = mapKey; // 加载新地图 var mapAsync = Context.Map.RunAsync(mapKey); mapAsync.On(_ => { StartChapter(true, true); }); } } private void StartChapter(bool isChapterChanged, bool isMapChanged) { _schedule = EMapSchedule.Monster1; var chapter = ChapterTableRepo.Get(_chapterId); var map = MapTableRepo.Get(chapter.Map); var dto = new LoadEntitiesDto(); dto.chapterId = _chapterId; dto.isChapterChanged = isChapterChanged; dto.isBossPart = chapter.Part_type == 1; dto.isPlayerMove = true;// isChapterChanged || !_isBossFailed; //战斗位置 dto.battlePosition = GetBattlePosition(map, chapter.Part);// map.BattlePositions[chapter.Part - 1].ToVector3(); var isGenPlayer = _player == null; if (isGenPlayer) { _player = GenPlayer(map.PlayerBirth.ToVector3()); //玩家 dto.player = _player; //伙伴 var partners = GenPartners(_player.BirthPosition); AddPartners(partners); dto.partners = partners; } else if (isMapChanged) { //玩家 dto.player = _player; //伙伴 dto.partners = _partnerMap.Values.ToArray(); } if (isChapterChanged || dto.isBossPart) { // 重置玩家血量 _player.Attr.Dynamic.Clear(); _player.Attr.ResetHp(); } if (isChapterChanged) { // 随从可能有替换过,重置随从的出生点 var birthPosition = _player.BirthPosition; foreach (var partner in _partnerMap.Values) { partner.BirthPosition = birthPosition + PartnersData.Instance.GetPartnerOffset(partner.Attr.UID); } } //怪物数据 var monsters = GenMonsters(); AddMonsters(monsters); dto.monsters = monsters; dto.monsterMoveDistance = map.MonsterBirth.Y - map.BattleFirst.Y;// map.MonsterPositions[chapter.Part - 1].ToVector3().y - dto.battlePosition.y; //定时器 if (!dto.isBossPart) { // 普通节点,增加定时器 //dto.monsterMoveDelay = 0; if (_monsterTimer != null) { // 理论上不会出现这个错误 _monsterTimer.Cancel(); Log.Error($"StartChapter _monsterTimer error. chapterId:{_chapterId}"); } _monsterTimer = Context.Time.AddDelayLooperTimer(chapter.Interval * 20, chapter.Interval * 20, OnLoopGenMonsters); } else { // boss关卡,直接到最后 //dto.monsterMoveDelay = 1000; _schedule = EMapSchedule.Monster5; Context.UI.OpenAsync(UIKeys.MainBossLaiXi); Context.Time.AddDelayTimer(1000, () => { //1秒后自动关闭 Context.UI.Close(UIKeys.MainBossLaiXi); if (chapter.Time > 0) {// boss战斗倒计时 _bossTimer = Context.Time.AddDelayTimer(chapter.Time, () => { CombatFailed(); }); EventSingle.Instance.Notify(EventDefine.GameMainMapBossStart, chapter.Id); } }); } EventSingle.Instance.Notify(EventDefine.GameMainMapLoadEntities, dto); } private void OnLoopGenMonsters(int times) { _schedule += 1; if (_schedule == EMapSchedule.Monster5) { _monsterTimer.Cancel(); _monsterTimer = null; } var chapter = ChapterTableRepo.Get(_chapterId); var map = MapTableRepo.Get(chapter.Map); var dto = new LoadEntitiesDto(); dto.chapterId = _chapterId; //战斗位置 dto.battlePosition = GetBattlePosition(map, chapter.Part);// map.BattlePositions[chapter.Part - 1].ToVector3(); //怪物数据 var monsters = GenMonsters(); AddMonsters(monsters); dto.monsters = monsters; dto.monsterMoveDistance = map.MonsterBirth.Y - map.BattleFirst.Y;// map.MonsterPositions[chapter.Part - 1].ToVector3().y - dto.battlePosition.y; EventSingle.Instance.Notify(EventDefine.GameMainMapLoadEntities, dto); } private PlayerEntity GenPlayer(Vector3 birth) { var playerData = PlayerData.Instance; var player = new PlayerEntity() { BirthPosition = birth//玩家出生点 }; var entityAttr = new EntityAttributes(0, playerData.UID) { Name = playerData.Name, Radius = 1, AttackCD = 2000, }; entityAttr.CopyFrom(playerData.Attr); player.Init(UIDDefine.New(), entityAttr, playerData.skillIds); return player; } private PartnerEntity[] GenPartners(Vector3 targetPosition) { var goneUpMap = PartnersData.Instance.UpPartnerUIDMap; var count = goneUpMap.Count; var partners = new PartnerEntity[count]; var index = 0; foreach (var partnerUID in goneUpMap.Keys) { partners[index++] = GenPartner(targetPosition, partnerUID); } return partners; } private PartnerEntity GenPartner(Vector3 targetPosition, long partnerId) { var partner = PartnersData.Instance.GetPartnerAttrByUID(partnerId); var entity = new PartnerEntity() { BirthPosition = targetPosition + PartnersData.Instance.GetPartnerOffset(partner.UID), EpigraphId = 20300, }; var entityAttr = new EntityAttributes(partner.TableId, partner.UID) { Name = partner.Name, Radius = partner.Radius, AttackCD = 6000, }; entityAttr.CopyFrom(partner.Attr); var table = PartnerTableRepo.Get(partner.TableId); entity.Init(UIDDefine.New(), entityAttr, new int[] { partner.SkillId }); return entity; } private void AddPartners(PartnerEntity[] partners) { if (_partnerMap == null) { _partnerMap = new Dictionary(); } if (partners == null || partners.Length == 0) return; foreach (var partner in partners) { _partnerMap.Add(partner.EntityId, partner); } } private Vector3 GetMonsterBirth(Vector3 position, int index, int cols, out int row) { row = 0; if (cols < 2) return position; var offset = 3; if (cols == 2) { position.x -= offset; offset *= 2; } else if (cols == 3) { position.x -= offset; } else { // 最多支持4列 cols = 4; // -4.5, -1.5, 1.5, 4.5 position.x -= (offset * 1.5f); } row = index / cols; position.x += (index % cols) * offset; return position; } ///// ///// 获取怪物的出生坐标和索引 ///// ///// ///// ///// //private Vector3 GetMonsterBirth(Vector3 position, out int positionIdx) //{ // if (_unusedIndexs.Count > 0) // { // positionIdx = _unusedIndexs[0]; // _unusedIndexs.RemoveAt(0); // } // else // { // positionIdx = 0; // } // if (positionIdx == 0) // { // return position; // } // var offset = Vector3.zero; // var xMultiple = (positionIdx + 1) / 2; // offset.x = _monsterOffset.x * xMultiple; // if (positionIdx % 2 != 0) // { // 索引为奇数的在左边 // offset.x = -offset.x; // } // if (xMultiple % 2 != 0) // { // 倍数为奇数的在上面 // offset.y = _monsterOffset.y; // } // var target = position + offset; // // 随机偏移xy(-0.2, 0.2) // target.x += Random.Range(-0.25f, 0.25f); // target.y += Random.Range(-0.25f, 0.25f); // return target; //} //private void PushMonstPositionIdx(int positionIdx) //{ // if (_unusedIndexs.Contains(positionIdx)) // return; // 避免重复记录 // for (var i = 0; i < _unusedIndexs.Count; i++) // { // if (positionIdx < _unusedIndexs[i] && (i == 0 || positionIdx > _unusedIndexs[i - 1])) // { // 小于当前索引的值 且 大于(i - 1 )的值 // _unusedIndexs.Insert(i, positionIdx); // return; // } // } // _unusedIndexs.Add(positionIdx); //} private MonsterEntity[] GenMonsters() { var chapter = ChapterTableRepo.Get(_chapterId); var map = MapTableRepo.Get(chapter.Map); Vector3 birthPosition = GetMonsterBirth(map, chapter.Part);// map.MonsterPositions[chapter.Part - 1].ToVector3(); int[] monsterRefresh = null; switch (_schedule) { case EMapSchedule.Monster1: monsterRefresh = chapter.Monster_refresh1; break; case EMapSchedule.Monster2: monsterRefresh = chapter.Monster_refresh2; break; case EMapSchedule.Monster3: monsterRefresh = chapter.Monster_refresh3; break; case EMapSchedule.Monster4: monsterRefresh = chapter.Monster_refresh4; break; case EMapSchedule.Monster5: monsterRefresh = chapter.Monster_refresh5; break; default: break; } var monsterCount = (monsterRefresh != null && monsterRefresh.Length > 0) ? monsterRefresh[1] : 0; if (monsterCount > 0) { // 数量0-3 var monsters = new MonsterEntity[monsterCount]; var cols = monsterRefresh[2]; // 敌人列数 var curve = monsterRefresh[3]; //移动轨迹 var isBossPart = chapter.Part_type == 1; for (var i = 0; i < monsterCount; i++) { var position = GetMonsterBirth(birthPosition, i, cols, out var row); var monsterTab = MonsterTableRepo.Get(monsterRefresh[0]); var entityAttr = new EntityAttributes(monsterTab.Id, UIDDefine.New()) { Name = monsterTab.Name, Radius = monsterTab.ModelSize * 0.5f, AttackCD = 3000, }; entityAttr.Static.Hp = monsterTab.Hp; entityAttr.Static.HpLimit = monsterTab.Hp; entityAttr.Static.MoveSpeed = Mathf.RoundToInt(monsterTab.Speed); entityAttr.Static.Attack = monsterTab.Atk; entityAttr.CurrentHp = monsterTab.Hp; var entity = new MonsterEntity(); entity.Init(UIDDefine.New(), entityAttr, monsterTab.Skills); entity.BirthPosition = position; entity.BirthDelay = row * chapter.Interval + (isBossPart ? 1000 : 0); // boss额外加1000毫秒延迟 entity.BirthCurve = curve; entity.IsBoss = monsterTab.Type == 2; monsters[i] = entity; } return monsters; } return null; } private void AddMonsters(MonsterEntity[] monsters) { if (monsters == null || monsters.Length == 0) return; if (_monsterMap == null) { _monsterMap = new Dictionary(); } foreach (var monsterInfo in monsters) { _monsterMap.Add(monsterInfo.EntityId, monsterInfo); } } #region 事件监听 private void OnRemoveEntity(int eventId, object args) { var entityId = (long)args; if (_monsterMap.TryGetValue(entityId, out var entity)) { _monsterMap.Remove(entityId); //PushMonstPositionIdx(entity.PositionIdx); CheckMonsterAllDead(); return; } if (_partnerMap.TryGetValue(entityId, out var partner)) { _partnerMap.Remove(entityId); return; } if (entityId == _player.EntityId) { //TODO 失败结算 CombatFailed(); } } private void OnPartnerDown(int eventId, object args) { var partnerUID = (long)args; var entityId = GetPartnerEntityIdByUID(partnerUID); if (entityId == 0) return; EventSingle.Instance.Notify(EventDefine.GameMainMapRemoveEntity, entityId); } private void OnPartnerUp(int eventId, object args) { var partnerUID = (long)args; var entityId = GetPartnerEntityIdByUID(partnerUID); if (entityId != 0) return; // 已经有实例对象了 var chapter = ChapterTableRepo.Get(_chapterId); var map = MapTableRepo.Get(chapter.Map); var dto = new LoadEntitiesDto(); dto.chapterId = _chapterId; //战斗位置 dto.battlePosition = GetBattlePosition(map, chapter.Part);// map.BattlePositions[chapter.Part - 1].ToVector3(); var partner = GenPartner(dto.battlePosition, partnerUID); var partners = new PartnerEntity[] { partner }; AddPartners(partners); dto.partners = partners; EventSingle.Instance.Notify(EventDefine.GameMainMapLoadEntities, dto); } private void OnPartnersChanged(int eventId, object args) { //TODO 临时代码 if (_partnerMap == null) return; var partnerIds = _partnerMap.Keys.ToList(); _partnerMap.Clear(); foreach (var partnerId in partnerIds) { EventSingle.Instance.Notify(EventDefine.GameMainMapRemoveEntity, partnerId); } var chapter = ChapterTableRepo.Get(_chapterId); var map = MapTableRepo.Get(chapter.Map); var dto = new LoadEntitiesDto(); dto.chapterId = _chapterId; //战斗位置 dto.battlePosition = GetBattlePosition(map, chapter.Part);// map.BattlePositions[chapter.Part - 1].ToVector3(); var partners = GenPartners(dto.battlePosition); AddPartners(partners); dto.partners = partners; EventSingle.Instance.Notify(EventDefine.GameMainMapLoadEntities, dto); } private void OnChallengeBoss(int eventId, object args) { CombatFailed(true); } //private void OnExchangePartner(int eventId, object args) //{ // var addPartners = (List)args; // var activePartnerIds = PartnersData.Instance.ActiveUIDs; // var count = addPartners.Count; // // 从后往前移除 // for (var i = 0; i < count; i++) // { // var partnerId = activePartnerIds[activePartnerIds.Count - 1]; // activePartnerIds.RemoveAt(activePartnerIds.Count - 1); // var EntityId = GetPartnerUIDByTableId(partnerId); // if (EntityId == 0) // continue; // EventSingle.Instance.Notify(EventDefine.GameMainMapRemoveEntity, EntityId); // } // activePartnerIds.AddRange(addPartners); // var chapter = ChapterTableRepo.Get(_chapterId); // var map = MapTableRepo.Get(chapter.Map); // var dto = new LoadEntitiesDto(); // dto.chapterId = _chapterId; // //战斗位置 // dto.battlePosition = map.BattlePositions[chapter.Part - 1].ToVector3(); // var partners = GenPartners(dto.battlePosition, addPartners); // AddPartners(partners); // dto.partners = partners; // EventSingle.Instance.Notify(EventDefine.GameMainMapLoadEntities, dto); //} private void OnMapEndMask(int eventId, object args) { var sign = (EMapCombatSign)args; var chapter = ChapterTableRepo.Get(_chapterId); int chapterId; if (sign is EMapCombatSign.Success) { var nextChapter = ChapterTableRepo.Next(_chapterId); //没找到新关卡,重新开始 if (nextChapter == null || nextChapter.Chapter != chapter.Chapter) { // 没有新关卡 || 章节id变化 //TODO 章节切换 } chapterId = nextChapter?.Id ?? PlayerData.Instance.chapterId; _isBossChallenge = false; } else if (sign is EMapCombatSign.Break) { var bossChapter = ChapterTableRepo.FindNearestBoss(_chapterId); Assert.IsNotNull(bossChapter, $"找不到该关卡的boss节点. Id:{_chapterId}"); chapterId = bossChapter.Id; _isBossFailed = false; _isBossChallenge = true; } else { // 失败,重置到本关卡第一个节点 if (chapter.Part_type == 1) { // boss节点失败,进入本关卡小怪节点循环 _isBossFailed = true; } chapterId = _chapterId / 10 * 10 + 1; _isBossChallenge = false; } StartMap(chapterId); } #endregion private void CheckMonsterAllDead() { if (_monsterMap.Count > 0 || (_player != null && _player.IsDead)) return; // 怪物全部死亡 var chapter = ChapterTableRepo.Get(_chapterId); if (chapter.Part_type == 1) { //boss关卡 _isBossFailed = false; //TODO 结算 _bossTimer?.Cancel(); _bossTimer = null; Context.UI.OpenAsync(UIKeys.MainBattleOverVictory); Context.Time.AddDelayTimer(2000, () => { //2秒后自动关闭 Context.UI.Close(UIKeys.MainBattleOverVictory); EventSingle.Instance.Notify(EventDefine.GameMainMapStartMask, EMapCombatSign.Success); }); } else if (_schedule == EMapSchedule.Monster5) { var nextId = _chapterId + 1; if (_isBossFailed) { // boss节点失败,进入本关卡小怪节点循环 var nextChapter = ChapterTableRepo.Get(nextId); if (nextChapter == null || nextChapter.Part_type == 1) { nextId = _chapterId / 10 * 10 + 1; } } _chapterId = nextId; StartChapter(false, false); } } /// /// 战斗失败 /// private void CombatFailed(bool isBreak = false) { _monsterTimer?.Cancel(); _monsterTimer = null; if (!isBreak) Context.UI.OpenAsync(UIKeys.MainBattleOverFail); var chapter = ChapterTableRepo.Get(_chapterId); // 清空怪物数据 //foreach (var item in _monsterMap) //{ // PushMonstPositionIdx(item.Value.PositionIdx); //} _monsterMap.Clear(); EventSingle.Instance.Notify(EventDefine.GameMainMapStartMask, isBreak ? EMapCombatSign.Break : (EMapCombatSign)(chapter.Part_type + 1)); } private long GetPartnerEntityIdByUID(long uid) { foreach (var item in _partnerMap) { if (item.Value.Attr.UID == uid) return item.Key; } return 0; } private Vector3 GetBattlePosition(MapTable map, int mapPart) { if (_isBossFailed || _isBossChallenge) mapPart = 1; // boss失败后战斗位置不再前移 return map.BattleFirst.ToVector3() + map.PartMove.ToVector3() * (mapPart - 1); } private Vector3 GetMonsterBirth(MapTable map, int mapPart) { if (_isBossFailed || _isBossChallenge) mapPart = 1; // boss失败后战斗位置不再前移 return map.MonsterBirth.ToVector3() + map.PartMove.ToVector3() * (mapPart - 1); } } }