Files
DanhengServer-OpenSource/GameServer/Game/Battle/BattleManager.cs
2025-04-20 16:34:13 +08:00

406 lines
15 KiB
C#

using EggLink.DanhengServer.Data;
using EggLink.DanhengServer.Data.Excel;
using EggLink.DanhengServer.Database.Inventory;
using EggLink.DanhengServer.GameServer.Game.Player;
using EggLink.DanhengServer.GameServer.Game.RogueMagic;
using EggLink.DanhengServer.GameServer.Game.Scene;
using EggLink.DanhengServer.GameServer.Game.Scene.Entity;
using EggLink.DanhengServer.GameServer.Server.Packet.Send.Battle;
using EggLink.DanhengServer.GameServer.Server.Packet.Send.Lineup;
using EggLink.DanhengServer.GameServer.Server.Packet.Send.Scene;
using EggLink.DanhengServer.Proto;
using EggLink.DanhengServer.Util;
using static EggLink.DanhengServer.GameServer.Plugin.Event.PluginEvent;
namespace EggLink.DanhengServer.GameServer.Game.Battle;
public class BattleManager(PlayerInstance player) : BasePlayerManager(player)
{
public StageConfigExcel? NextBattleStageConfig { get; set; }
public async ValueTask<BattleInstance?> StartBattle(IGameEntity attackEntity, List<IGameEntity> targetEntityList, bool isSkill)
{
if (Player.BattleInstance != null) return Player.BattleInstance;
var targetList = new List<EntityMonster>();
var avatarList = new List<AvatarSceneInfo>();
var propList = new List<EntityProp>();
Player.SceneInstance!.AvatarInfo.TryGetValue(attackEntity.EntityID, out var castAvatar);
if (castAvatar != null)
{
foreach (var entity in targetEntityList)
{
switch (entity)
{
case EntityMonster monster:
targetList.Add(monster);
break;
case EntityProp prop:
propList.Add(prop);
break;
}
}
}
else
{
var isAmbushed =
targetEntityList.Any(entity => Player.SceneInstance!.AvatarInfo.ContainsKey(entity.EntityID));
if (!isAmbushed)
{
return null;
}
var monsterEntity = Player.SceneInstance!.Entities[attackEntity.EntityID];
if (monsterEntity is EntityMonster monster) targetList.Add(monster);
}
if (targetList.Count == 0 && propList.Count == 0)
{
return null;
}
foreach (var prop in propList)
{
await Player.SceneInstance!.RemoveEntity(prop);
if (prop.Excel.IsMpRecover)
{
await Player.LineupManager!.GainMp(2, true, SyncLineupReason.SyncReasonMpAddPropHit);
}
else if (prop.Excel.IsHpRecover)
{
Player.LineupManager!.GetCurLineup()!.Heal(2000, false);
await Player.SendPacket(new PacketSyncLineupNotify(Player.LineupManager!.GetCurLineup()!));
}
else
{
Player.InventoryManager!.HandlePlaneEvent(prop.PropInfo.EventID);
}
Player.RogueManager!.GetRogueInstance()?.OnPropDestruct(prop);
}
if (targetList.Count > 0)
{
var triggerBattle = targetList.Any(target => target.IsAlive);
if (!triggerBattle)
{
return null;
}
var inst = Player.RogueManager!.GetRogueInstance();
if (inst is RogueMagicInstance { CurLevel.CurRoom.AdventureInstance: not null } magic)
{
await magic.HitMonsterInAdventure(targetList);
foreach (var entityMonster in targetList) await entityMonster.Kill();
return null;
}
BattleInstance battleInstance =
new(Player, Player.LineupManager!.GetCurLineup()!, targetList.Where(x => x.IsAlive).ToList())
{
WorldLevel = Player.Data.WorldLevel
};
if (NextBattleStageConfig != null)
{
battleInstance = new BattleInstance(Player, Player.LineupManager!.GetCurLineup()!, [NextBattleStageConfig])
{
WorldLevel = Player.Data.WorldLevel,
};
NextBattleStageConfig = null;
}
avatarList.AddRange(Player.LineupManager!.GetCurLineup()!.BaseAvatars!
.Select(item =>
Player.SceneInstance!.AvatarInfo.Values.FirstOrDefault(x =>
x.AvatarInfo.AvatarId == item.BaseAvatarId))
.OfType<AvatarSceneInfo>());
MazeBuff? mazeBuff = null;
if (castAvatar != null)
{
var index = battleInstance.Lineup.BaseAvatars!.FindIndex(x =>
x.BaseAvatarId == castAvatar.AvatarInfo.AvatarId);
GameData.AvatarConfigData.TryGetValue(castAvatar.AvatarInfo.GetAvatarId(), out var avatarExcel);
if (avatarExcel != null)
{
mazeBuff = new MazeBuff((int)avatarExcel.DamageType, 1, index);
mazeBuff.DynamicValues.Add("SkillIndex", isSkill ? 2 : 1);
}
}
else
{
mazeBuff = new MazeBuff(GameConstants.AMBUSH_BUFF_ID, 1, -1)
{
WaveFlag = 1
};
}
if (mazeBuff != null && mazeBuff.BuffID != 0) // avoid adding a buff with ID 0
{
battleInstance.Buffs.Add(mazeBuff);
}
battleInstance.AvatarInfo = avatarList;
// call battle start
Player.RogueManager!.GetRogueInstance()?.OnBattleStart(battleInstance);
Player.ChallengeManager!.ChallengeInstance?.OnBattleStart(battleInstance);
Player.QuestManager!.OnBattleStart(battleInstance);
Player.BattleInstance = battleInstance;
InvokeOnPlayerEnterBattle(Player, battleInstance);
Player.SceneInstance?.ClearSummonUnit();
return battleInstance;
}
return null;
}
public async ValueTask StartStage(int eventId)
{
if (Player.BattleInstance != null)
{
await Player.SendPacket(new PacketSceneEnterStageScRsp(Player.BattleInstance));
return;
}
GameData.StageConfigData.TryGetValue(eventId, out var stageConfig);
if (stageConfig == null)
{
GameData.StageConfigData.TryGetValue(eventId * 10 + Player.Data.WorldLevel, out stageConfig);
if (stageConfig == null)
{
await Player.SendPacket(new PacketSceneEnterStageScRsp());
return;
}
}
if (NextBattleStageConfig != null)
{
stageConfig = NextBattleStageConfig;
NextBattleStageConfig = null;
}
BattleInstance battleInstance = new(Player, Player.LineupManager!.GetCurLineup()!, [stageConfig])
{
WorldLevel = Player.Data.WorldLevel,
EventId = eventId
};
var avatarList = Player.LineupManager!.GetCurLineup()!.BaseAvatars!.Select(item =>
Player.SceneInstance!.AvatarInfo.Values.FirstOrDefault(x => x.AvatarInfo.AvatarId == item.BaseAvatarId))
.OfType<AvatarSceneInfo>().ToList();
battleInstance.AvatarInfo = avatarList;
// call battle start
Player.RogueManager!.GetRogueInstance()?.OnBattleStart(battleInstance);
Player.ChallengeManager!.ChallengeInstance?.OnBattleStart(battleInstance);
Player.QuestManager!.OnBattleStart(battleInstance);
Player.BattleInstance = battleInstance;
InvokeOnPlayerEnterBattle(Player, battleInstance);
await Player.SendPacket(new PacketSceneEnterStageScRsp(battleInstance));
Player.SceneInstance?.ClearSummonUnit();
}
public async ValueTask<BattleInstance?> StartCocoonStage(int cocoonId, int wave, int worldLevel)
{
if (Player.BattleInstance != null) return null;
GameData.CocoonConfigData.TryGetValue(cocoonId * 100 + worldLevel, out var config);
if (config == null)
{
return null;
}
wave = Math.Min(Math.Max(wave, 1), config.MaxWave);
var cost = config.StaminaCost * wave;
if (Player.Data.Stamina < cost)
{
return null;
}
List<StageConfigExcel> stageConfigExcels = [];
for (var i = 0; i < wave; i++)
{
var stageId = config.StageIDList.RandomElement();
GameData.StageConfigData.TryGetValue(stageId, out var stageConfig);
if (stageConfig == null) continue;
stageConfigExcels.Add(stageConfig);
}
if (stageConfigExcels.Count == 0)
{
return null;
}
BattleInstance battleInstance = new(Player, Player.LineupManager!.GetCurLineup()!, stageConfigExcels)
{
StaminaCost = cost,
WorldLevel = config.WorldLevel,
CocoonWave = wave,
MappingInfoId = config.MappingInfoID
};
if (NextBattleStageConfig != null)
{
battleInstance = new BattleInstance(Player, Player.LineupManager!.GetCurLineup()!, [NextBattleStageConfig])
{
WorldLevel = Player.Data.WorldLevel,
};
NextBattleStageConfig = null;
}
var avatarList = Player.LineupManager!.GetCurLineup()!.BaseAvatars!.Select(item =>
Player.SceneInstance!.AvatarInfo.Values.FirstOrDefault(x => x.AvatarInfo.AvatarId == item.BaseAvatarId))
.OfType<AvatarSceneInfo>().ToList();
battleInstance.AvatarInfo = avatarList;
Player.BattleInstance = battleInstance;
Player.QuestManager!.OnBattleStart(battleInstance);
InvokeOnPlayerEnterBattle(Player, battleInstance);
await ValueTask.CompletedTask;
return battleInstance;
}
public (Retcode, BattleInstance?) StartBattleCollege(int collegeId)
{
if (Player.BattleInstance != null) return (Retcode.RetInBattleNow, null);
GameData.BattleCollegeConfigData.TryGetValue(collegeId, out var config);
if (config == null) return (Retcode.RetFail, null);
var stageId = config.StageID;
GameData.StageConfigData.TryGetValue(stageId, out var stageConfig);
if (stageConfig == null) return (Retcode.RetStageConfigNotExist, null);
BattleInstance battleInstance = new(Player, Player.LineupManager!.GetCurLineup()!, [stageConfig])
{
WorldLevel = Player.Data.WorldLevel,
CollegeConfigExcel = config,
AvatarInfo = []
};
// call battle start
Player.RogueManager!.GetRogueInstance()?.OnBattleStart(battleInstance);
Player.ChallengeManager!.ChallengeInstance?.OnBattleStart(battleInstance);
Player.QuestManager!.OnBattleStart(battleInstance);
Player.BattleInstance = battleInstance;
return (Retcode.RetSucc, battleInstance);
}
public async ValueTask EndBattle(PVEBattleResultCsReq req)
{
InvokeOnPlayerQuitBattle(Player, req);
if (Player.BattleInstance == null)
{
await Player.SendPacket(new PacketPVEBattleResultScRsp());
return;
}
Player.BattleInstance.BattleEndStatus = req.EndStatus;
var battle = Player.BattleInstance;
var updateStatus = true;
var teleportToAnchor = false;
var minimumHp = 0;
var dropItems = new List<ItemData>();
switch (req.EndStatus)
{
case BattleEndStatus.BattleEndWin:
// Drops
foreach (var monster in battle.EntityMonsters) dropItems.AddRange(await monster.Kill(false));
// Spend stamina
if (battle.StaminaCost > 0) await Player.SpendStamina(battle.StaminaCost);
break;
case BattleEndStatus.BattleEndLose:
// Set avatar hp to 20% if the player's party is downed
minimumHp = 2000;
teleportToAnchor = true;
break;
default:
teleportToAnchor = true;
if (battle.CocoonWave > 0) teleportToAnchor = false;
updateStatus = false;
break;
}
if (updateStatus)
{
var lineup = Player.LineupManager!.GetCurLineup()!;
// Update battle status
foreach (var avatar in req.Stt.BattleAvatarList)
{
var avatarInstance = Player.AvatarManager!.GetAvatar((int)avatar.Id);
var prop = avatar.AvatarStatus;
var curHp = (int)Math.Max(Math.Round(prop.LeftHp / prop.MaxHp * 10000), minimumHp);
var curSp = (int)prop.LeftSp * 100;
if (avatarInstance == null)
{
GameData.SpecialAvatarData.TryGetValue((int)(avatar.Id * 10 + Player.Data.WorldLevel),
out var specialAvatar);
if (specialAvatar == null) continue;
specialAvatar.CurHp[Player.Uid] = curHp;
specialAvatar.CurSp[Player.Uid] = curSp;
avatarInstance?.SetCurHp(curHp, lineup.LineupType != 0);
avatarInstance?.SetCurSp(curSp, lineup.LineupType != 0);
}
else
{
avatarInstance.SetCurHp(curHp, lineup.LineupType != 0);
avatarInstance.SetCurSp(curSp, lineup.LineupType != 0);
}
}
await Player.SendPacket(new PacketSyncLineupNotify(lineup));
}
if (teleportToAnchor)
{
var anchorProp = Player.SceneInstance?.GetNearestSpring(long.MaxValue);
if (anchorProp != null)
{
var anchor = Player.SceneInstance?.FloorInfo?.GetAnchorInfo(
anchorProp.PropInfo.AnchorGroupID,
anchorProp.PropInfo.AnchorID
);
if (anchor != null) await Player.MoveTo(anchor.ToPositionProto());
}
}
// call battle end
battle.MonsterDropItems = dropItems;
battle.BattleResult = req;
Player.BattleInstance = null;
await Player.MissionManager!.OnBattleFinish(req, battle);
if (Player.RogueManager?.GetRogueInstance() != null)
await Player.RogueManager!.GetRogueInstance()!.OnBattleEnd(battle, req);
if (Player.ChallengeManager?.ChallengeInstance != null)
await Player.ChallengeManager!.ChallengeInstance.OnBattleEnd(battle, req);
if (Player.ActivityManager!.TrialActivityInstance != null && req.EndStatus == BattleEndStatus.BattleEndWin)
await Player.ActivityManager.TrialActivityInstance.EndActivity(TrialActivityStatus.Finish);
await Player.SendPacket(new PacketPVEBattleResultScRsp(req, Player, battle));
}
}