mirror of
https://github.com/EggLinks/DanhengServer-OpenSource.git
synced 2026-01-02 20:26:03 +08:00
448 lines
14 KiB
C#
448 lines
14 KiB
C#
using EggLink.DanhengServer.Data;
|
|
using EggLink.DanhengServer.Data.Excel;
|
|
using EggLink.DanhengServer.Enums.GridFight;
|
|
using EggLink.DanhengServer.GameServer.Game.GridFight.PendingAction;
|
|
using EggLink.DanhengServer.GameServer.Game.GridFight.Sync;
|
|
using EggLink.DanhengServer.GameServer.Server.Packet.Send.GridFight;
|
|
using EggLink.DanhengServer.Proto;
|
|
using EggLink.DanhengServer.Util;
|
|
|
|
namespace EggLink.DanhengServer.GameServer.Game.GridFight.Component;
|
|
|
|
public class GridFightLevelComponent : BaseGridFightComponent
|
|
{
|
|
#region Properties & Fields // TODO : to proto field
|
|
|
|
private uint _curChapterId = 1;
|
|
private uint _curSectionId = 1;
|
|
public Dictionary<uint, List<GridFightGameSectionInfo>> Sections { get; } = [];
|
|
public GridFightGameSectionInfo CurrentSection => Sections[_curChapterId][(int)(_curSectionId - 1)];
|
|
public List<GridFightRoleDamageSttInfo> RoleDamageSttInfos { get; } = [];
|
|
public List<GridFightPortalBuffInfo> PortalBuffs { get; } = [];
|
|
|
|
#endregion
|
|
|
|
#region Constructors
|
|
|
|
public GridFightLevelComponent(GridFightInstance inst) : base(inst)
|
|
{
|
|
// TODO: randomly select a base route id
|
|
List<uint> chapterIds = [1100];
|
|
List<GridFightCampExcel> campPool = GameData.GridFightCampData.Values.Where(x => x.BossBattleArea != 0).ToList();
|
|
foreach (var chapterId in Enumerable.Range(1, 3))
|
|
{
|
|
var chapters = chapterIds.Count >= chapterId
|
|
? [GameData.GridFightStageRouteData[chapterIds[chapterId - 1]]]
|
|
: GameData.GridFightStageRouteData.Values.Where(x => x.Any(j => j.Value.ChapterID == chapterId))
|
|
.ToList();
|
|
if (chapters.Count == 0)
|
|
continue;
|
|
|
|
var select = chapters.RandomElement();
|
|
|
|
var camp = campPool.RandomElement(); // cannot the same
|
|
campPool.Remove(camp);
|
|
|
|
// create section infos
|
|
Sections[(uint)chapterId] = [.. select.Values.Select(x => new GridFightGameSectionInfo(x, camp))];
|
|
}
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Stt
|
|
|
|
public async ValueTask<(Retcode, GridFightRoleDamageSttInfo?)> AddRoleDamageStt(uint roleId, double damage, bool sendPacket = true)
|
|
{
|
|
var roleComp = Inst.GetComponent<GridFightRoleComponent>();
|
|
|
|
var role = roleComp.Data.Roles.OrderBy(x => x.Pos).FirstOrDefault(x => x.RoleId == roleId);
|
|
if (role == null)
|
|
return (Retcode.RetGridFightRoleNotExist, null);
|
|
|
|
var info = RoleDamageSttInfos.FirstOrDefault(x => x.RoleId == roleId && x.Tier == role.Tier);
|
|
GridFightRoleDamageSttInfo res;
|
|
if (info == null)
|
|
{
|
|
res = info = new GridFightRoleDamageSttInfo
|
|
{
|
|
RoleId = roleId,
|
|
Tier = role.Tier,
|
|
TotalDamage = damage,
|
|
IsTrialAvatar = false,
|
|
IsUpgrade = false
|
|
};
|
|
|
|
RoleDamageSttInfos.Add(info);
|
|
}
|
|
else
|
|
{
|
|
res = new GridFightRoleDamageSttInfo
|
|
{
|
|
RoleId = info.RoleId,
|
|
IsTrialAvatar = info.IsTrialAvatar,
|
|
IsUpgrade = info.IsUpgrade,
|
|
Tier = info.Tier,
|
|
TotalDamage = damage
|
|
};
|
|
|
|
info.TotalDamage += damage;
|
|
}
|
|
|
|
if (sendPacket)
|
|
{
|
|
await Inst.Player.SendPacket(new PacketGridFightSyncUpdateResultScNotify(new GridFightRoleDamageSttSyncData(GridFightSrc.KGridFightSrcBattleEnd, this)));
|
|
}
|
|
|
|
return (Retcode.RetSucc, res);
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Actions
|
|
|
|
public async ValueTask<List<BaseGridFightSyncData>> EnterNextSection(bool sendPacket = true, GridFightSrc src = GridFightSrc.KGridFightSrcBattleEnd)
|
|
{
|
|
// if last section of chapter
|
|
if (_curSectionId >= Sections[_curChapterId].Count)
|
|
{
|
|
if (_curChapterId >= Sections.Count)
|
|
{
|
|
// end of game
|
|
return [];
|
|
}
|
|
|
|
_curChapterId++;
|
|
_curSectionId = 1;
|
|
}
|
|
else
|
|
{
|
|
_curSectionId++;
|
|
}
|
|
|
|
List<BaseGridFightSyncData> syncs = [new GridFightLevelSyncData(src, this)];
|
|
|
|
//await Inst.RollPortalBuff();
|
|
if (CurrentSection.Excel.IsAugment == 1)
|
|
{
|
|
// create augment action
|
|
syncs.AddRange(await Inst.CreatePendingAction<GridFightAugmentPendingAction>(sendPacket: false));
|
|
}
|
|
|
|
if (CurrentSection.Excel.NodeType == GridFightNodeTypeEnum.Supply)
|
|
{
|
|
// create supply action
|
|
syncs.AddRange(await Inst.CreatePendingAction<GridFightSupplyPendingAction>(sendPacket: false));
|
|
}
|
|
else
|
|
{
|
|
syncs.AddRange(await Inst.CreatePendingAction<GridFightElitePendingAction>(sendPacket: false));
|
|
|
|
if (CurrentSection.Excel.NodeType is not GridFightNodeTypeEnum.Boss
|
|
and not GridFightNodeTypeEnum.EliteBranch)
|
|
{
|
|
syncs.AddRange(await Inst.CreatePendingAction<GridFightPreparePendingAction>(sendPacket: false));
|
|
}
|
|
}
|
|
|
|
if (sendPacket)
|
|
{
|
|
await Inst.Player.SendPacket(new PacketGridFightSyncUpdateResultScNotify(syncs));
|
|
}
|
|
|
|
return syncs;
|
|
}
|
|
|
|
public async ValueTask<List<BaseGridFightSyncData>> AddPortalBuff(uint portalBuffId, bool sendPacket = true, GridFightSrc src = GridFightSrc.KGridFightSrcSelectPortalBuff)
|
|
{
|
|
var info = new GridFightPortalBuffInfo
|
|
{
|
|
PortalBuffId = portalBuffId
|
|
};
|
|
|
|
PortalBuffs.Add(info);
|
|
|
|
var syncData = new GridFightAddPortalBuffSyncData(src, info);
|
|
if (sendPacket)
|
|
{
|
|
await Inst.Player.SendPacket(new PacketGridFightSyncUpdateResultScNotify(syncData));
|
|
}
|
|
|
|
return [syncData];
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Information
|
|
|
|
public List<GridFightMonsterInfo> GetBossMonsters()
|
|
{
|
|
// get every chapter last section camp
|
|
List<GridFightMonsterInfo> bosses = [];
|
|
foreach (var chapter in Sections.Values)
|
|
{
|
|
var lastSection = chapter.Last();
|
|
var bossMonsters = lastSection.MonsterCamp.Monsters.Where(x => x.MonsterTier == 5).ToList();
|
|
if (bossMonsters.Count == 0)
|
|
continue;
|
|
|
|
var boss = bossMonsters.RandomElement();
|
|
bosses.Add(new GridFightMonsterInfo
|
|
{
|
|
MonsterId = boss.MonsterID,
|
|
Tier = boss.MonsterTier,
|
|
MonsterCampId = lastSection.MonsterCamp.ID
|
|
});
|
|
}
|
|
|
|
return bosses;
|
|
}
|
|
|
|
#endregion
|
|
|
|
#region Serialization
|
|
|
|
public override GridFightGameInfo ToProto()
|
|
{
|
|
return new GridFightGameInfo
|
|
{
|
|
GridLevelInfo = new GridFightLevelInfo
|
|
{
|
|
ChapterId = CurrentSection.ChapterId,
|
|
SectionId = CurrentSection.SectionId,
|
|
RouteId = CurrentSection.Excel.ID,
|
|
GridFightLayerInfo = new GridFightLayerInfo
|
|
{
|
|
RouteInfo = CurrentSection.ToRouteInfo(),
|
|
RouteIsPending = CurrentSection.Excel.IsAugment == 1
|
|
},
|
|
BossInfo = new GridFightBossInfo
|
|
{
|
|
BossMonsters = { GetBossMonsters() }
|
|
},
|
|
GridFightCampList =
|
|
{
|
|
Sections.Values.SelectMany(x => x).Select(h => h.MonsterCamp.ID).ToHashSet().Select(s =>
|
|
new GridFightGameCampInfo
|
|
{
|
|
MonsterCampId = s,
|
|
})
|
|
},
|
|
GridChapterInfo = new GridFightChapterInfo
|
|
{
|
|
SectionInfo =
|
|
{
|
|
Sections.Values.SelectMany(x => x).Select(s => s.ToProto())
|
|
}
|
|
},
|
|
LevelSttInfo = new GridFightLevelSttInfo
|
|
{
|
|
GridFightDamageSttInfo = ToDamageSttInfo()
|
|
},
|
|
GridFightPortalBuffList = { PortalBuffs.Select(x => x.ToProto()) }
|
|
}
|
|
};
|
|
}
|
|
|
|
public GridFightDamageSttInfo ToDamageSttInfo()
|
|
{
|
|
return new GridFightDamageSttInfo
|
|
{
|
|
RoleDamageSttList = { RoleDamageSttInfos.Select(x => x.ToProto()) }
|
|
};
|
|
}
|
|
|
|
#endregion
|
|
}
|
|
|
|
public class GridFightRoleDamageSttInfo
|
|
{
|
|
public uint RoleId { get; set; }
|
|
public uint Tier { get; set; }
|
|
public double TotalDamage { get; set; }
|
|
public bool IsTrialAvatar { get; set; }
|
|
public bool IsUpgrade { get; set; }
|
|
|
|
public GridFightRoleDamageStt ToProto()
|
|
{
|
|
return new GridFightRoleDamageStt
|
|
{
|
|
RoleBasicId = RoleId,
|
|
Tier = Tier,
|
|
IsTrialAvatar = IsTrialAvatar,
|
|
IsUpgrade = IsUpgrade,
|
|
TotalDamage = TotalDamage
|
|
};
|
|
}
|
|
}
|
|
|
|
public class GridFightPortalBuffInfo
|
|
{
|
|
public uint PortalBuffId { get; set; }
|
|
public Dictionary<string, uint> SavedValue { get; set; } = [];
|
|
|
|
public GridFightGamePortalBuffInfo ToProto()
|
|
{
|
|
return new GridFightGamePortalBuffInfo
|
|
{
|
|
PortalBuffId = PortalBuffId,
|
|
GameSavedValueMap = { SavedValue }
|
|
};
|
|
}
|
|
|
|
public BattleGridFightPortalBuffInfo ToBattleInfo()
|
|
{
|
|
return new BattleGridFightPortalBuffInfo
|
|
{
|
|
PortalBuffId = PortalBuffId,
|
|
GameSavedValueMap = { SavedValue }
|
|
};
|
|
}
|
|
|
|
public GridFightPortalBuffSyncInfo ToSyncInfo()
|
|
{
|
|
return new GridFightPortalBuffSyncInfo
|
|
{
|
|
PortalBuffId = PortalBuffId,
|
|
GameSavedValueMap = { SavedValue }
|
|
};
|
|
}
|
|
}
|
|
|
|
public class GridFightGameSectionInfo
|
|
{
|
|
public GridFightStageRouteExcel Excel { get; }
|
|
public uint ChapterId { get; }
|
|
public uint SectionId { get; }
|
|
public GridFightCampExcel MonsterCamp { get; set; }
|
|
public List<GridFightGameEncounterInfo> Encounters { get; } = [];
|
|
|
|
public GridFightGameSectionInfo(GridFightStageRouteExcel excel, GridFightCampExcel camp)
|
|
{
|
|
Excel = excel;
|
|
ChapterId = excel.ChapterID;
|
|
SectionId = excel.SectionID;
|
|
|
|
MonsterCamp = camp;
|
|
|
|
if (Excel.NodeType is not GridFightNodeTypeEnum.Monster and not GridFightNodeTypeEnum.CampMonster
|
|
and not GridFightNodeTypeEnum.Boss and not GridFightNodeTypeEnum.EliteBranch) return;
|
|
|
|
Encounters.Add(new GridFightGameEncounterInfo(1, 1, this));
|
|
}
|
|
|
|
public GridFightRouteInfo ToRouteInfo()
|
|
{
|
|
return new GridFightRouteInfo
|
|
{
|
|
FightCampId = MonsterCamp.ID,
|
|
EliteBranchId = 0,
|
|
RouteEncounterList = { Encounters.Select(x => x.ToProto()) }
|
|
};
|
|
}
|
|
|
|
public GridFightSectionInfo ToProto()
|
|
{
|
|
return new GridFightSectionInfo
|
|
{
|
|
ChapterId = ChapterId,
|
|
SectionId = SectionId
|
|
};
|
|
}
|
|
}
|
|
|
|
public class GridFightGameEncounterInfo
|
|
{
|
|
public GridFightGameEncounterInfo(uint index, uint difficulty, GridFightGameSectionInfo section)
|
|
{
|
|
EncounterIndex = index;
|
|
EncounterDifficulty = difficulty;
|
|
ParentSection = section;
|
|
|
|
var waveNum = ParentSection.Excel.NodeType switch
|
|
{
|
|
//GridFightNodeTypeEnum.Boss => 2,
|
|
//GridFightNodeTypeEnum.EliteBranch => 2,
|
|
_ => 1
|
|
};
|
|
|
|
List<int> monsterNum = ParentSection.Excel.NodeType switch
|
|
{
|
|
GridFightNodeTypeEnum.Boss => [1],
|
|
GridFightNodeTypeEnum.EliteBranch => [3],
|
|
GridFightNodeTypeEnum.CampMonster => [3],
|
|
_ => [Random.Shared.Next(3, 5)]
|
|
};
|
|
|
|
var monsterPool = ParentSection.MonsterCamp.Monsters.Where(x => x.MonsterTier <= 2).OrderBy(_ => Guid.NewGuid()).ToList();
|
|
var monster4Pool = ParentSection.MonsterCamp.Monsters.Where(x => x.MonsterTier is 4 or 3).OrderBy(_ => Guid.NewGuid()).ToList();
|
|
var monster5Pool = ParentSection.MonsterCamp.Monsters.Where(x => x.MonsterTier == 5).OrderBy(_ => Guid.NewGuid()).ToList();
|
|
for (var i = 0; i < waveNum; i++)
|
|
{
|
|
if (i < waveNum - 1 || ParentSection.Excel.NodeType is GridFightNodeTypeEnum.Monster)
|
|
{
|
|
// no elite
|
|
var res = monsterPool.OrderBy(_ => Guid.NewGuid()).Take(monsterNum[i]).ToList();
|
|
MonsterWaves.Add(new GridFightGameMonsterWaveInfo((uint)(i + 1), res, ParentSection.MonsterCamp.ID));
|
|
}
|
|
else
|
|
{
|
|
List<GridFightMonsterExcel> elites = ParentSection.Excel.NodeType switch
|
|
{
|
|
GridFightNodeTypeEnum.Boss => [..monster5Pool],
|
|
_ => [monster4Pool.RandomElement()],
|
|
};
|
|
|
|
List<GridFightMonsterExcel> monsters = [..elites];
|
|
var remain = monsterNum[i] - 1;
|
|
|
|
if (remain > 0)
|
|
{
|
|
monsters.AddRange(monsterPool.OrderBy(_ => Guid.NewGuid()).Take(remain).ToList());
|
|
}
|
|
|
|
MonsterWaves.Add(new GridFightGameMonsterWaveInfo((uint)(i + 1), monsters, ParentSection.MonsterCamp.ID));
|
|
}
|
|
}
|
|
}
|
|
|
|
public uint EncounterIndex { get; set; }
|
|
public uint EncounterDifficulty { get; set; }
|
|
public GridFightGameSectionInfo ParentSection { get; }
|
|
public List<GridFightGameMonsterWaveInfo> MonsterWaves { get; } = [];
|
|
|
|
public GridFightEncounterInfo ToProto()
|
|
{
|
|
return new GridFightEncounterInfo
|
|
{
|
|
EncounterIndex = EncounterIndex,
|
|
EncounterExtraDifficultyLevel = EncounterDifficulty,
|
|
EncounterDropInfo = new GridFightDropInfo(),
|
|
MonsterWaveList = { MonsterWaves.Select(x => x.ToProto()) }
|
|
};
|
|
}
|
|
}
|
|
|
|
public class GridFightGameMonsterWaveInfo(uint wave, List<GridFightMonsterExcel> monsters, uint campId)
|
|
{
|
|
public uint Wave { get; set; } = wave;
|
|
public uint CampId { get; set; } = campId;
|
|
public List<GridFightMonsterExcel> Monsters { get; } = monsters;
|
|
|
|
public GridEncounterMonsterWave ToProto()
|
|
{
|
|
return new GridEncounterMonsterWave
|
|
{
|
|
EncounterWave = Wave,
|
|
FightMonsterList =
|
|
{
|
|
Monsters.Select(x => new GridFightMonsterInfo
|
|
{
|
|
MonsterId = x.MonsterID,
|
|
MonsterCampId = CampId,
|
|
Tier = x.MonsterTier
|
|
})
|
|
}
|
|
};
|
|
}
|
|
} |