Files
DanhengServer-OpenSource/GameServer/Game/Rogue/BaseRogueInstance.cs
Somebody 87d228eb79 Feature:Asynchronous Operation & Formatting Code
- Now the async operation is enabled!
- Code formatted by Resharper plugin <3
2024-07-22 17:12:03 +08:00

426 lines
15 KiB
C#

using EggLink.DanhengServer.Data;
using EggLink.DanhengServer.Data.Excel;
using EggLink.DanhengServer.Database.Inventory;
using EggLink.DanhengServer.Game.Battle;
using EggLink.DanhengServer.Game.Player;
using EggLink.DanhengServer.Game.Rogue.Buff;
using EggLink.DanhengServer.Game.Rogue.Event;
using EggLink.DanhengServer.Game.Rogue.Miracle;
using EggLink.DanhengServer.Game.Rogue.Scene.Entity;
using EggLink.DanhengServer.Game.Scene.Entity;
using EggLink.DanhengServer.GameServer.Server.Packet.Send.Rogue;
using EggLink.DanhengServer.Proto;
using EggLink.DanhengServer.Server.Packet.Send.Rogue;
using EggLink.DanhengServer.Server.Packet.Send.Scene;
using EggLink.DanhengServer.Util;
using LineupInfo = EggLink.DanhengServer.Database.Lineup.LineupInfo;
namespace EggLink.DanhengServer.Game.Rogue;
public abstract class BaseRogueInstance(PlayerInstance player, int rogueVersionId, int rogueBuffType)
{
public PlayerInstance Player { get; set; } = player;
public LineupInfo? CurLineup { get; set; }
public int RogueVersionId { get; set; } = rogueVersionId;
public int RogueType { get; set; } = 100;
public int CurReviveCost { get; set; } = 80;
public int CurRerollCost { get; set; } = 30;
public int BaseRerollCount { get; set; } = 1;
public int BaseRerollFreeCount { get; set; } = 0;
public int CurMoney { get; set; } = 100;
public int CurDestroyCount { get; set; }
public int AeonId { get; set; } = 0;
public int RogueBuffType { get; set; } = rogueBuffType;
public bool IsWin { get; set; } = false;
public List<RogueBuffInstance> RogueBuffs { get; set; } = [];
public Dictionary<int, RogueMiracleInstance> RogueMiracles { get; set; } = [];
public SortedDictionary<int, RogueActionInstance> RogueActions { get; set; } = []; // queue_position -> action
public int CurActionQueuePosition { get; set; } = 0;
public int CurEventUniqueID { get; set; } = 100;
public int CurAeonBuffCount { get; set; } = 0;
public int CurAeonEnhanceCount { get; set; } = 0;
public bool AeonBuffPending { get; set; } // prevent multiple aeon buff
public RogueEventManager? EventManager { get; set; }
#region Buffs
public virtual async ValueTask RollBuff(int amount)
{
await RollBuff(amount, 100005);
}
public virtual async ValueTask RollBuff(int amount, int buffGroupId, int buffHintType = 1)
{
var buffGroup = GameData.RogueBuffGroupData[buffGroupId];
var buffList = buffGroup.BuffList;
var actualBuffList = new List<RogueBuffExcel>();
foreach (var buff in buffList)
if (!RogueBuffs.Exists(x => x.BuffExcel.MazeBuffID == buff.MazeBuffID))
actualBuffList.Add(buff);
if (actualBuffList.Count == 0) return; // no buffs to roll
for (var i = 0; i < amount; i++)
{
var menu = new RogueBuffSelectMenu(this)
{
CurCount = i + 1,
TotalCount = amount
};
menu.RollBuff(actualBuffList);
menu.HintId = buffHintType;
var action = menu.GetActionInstance();
RogueActions.Add(action.QueuePosition, action);
}
await UpdateMenu();
}
public virtual async ValueTask<RogueCommonActionResult?> AddBuff(int buffId, int level = 1,
RogueActionSource source = RogueActionSource.RogueCommonActionResultSourceTypeDialogue,
RogueActionDisplayType displayType = RogueActionDisplayType.RogueCommonActionResultDisplayTypeSingle,
bool updateMenu = true, bool notify = true)
{
if (RogueBuffs.Exists(x => x.BuffExcel.MazeBuffID == buffId)) return null;
GameData.RogueBuffData.TryGetValue(buffId * 100 + level, out var excel);
if (excel == null) return null;
if (CurAeonBuffCount > 0) // check if aeon buff exists
if (excel.IsAeonBuff)
return null;
var buff = new RogueBuffInstance(buffId, level);
RogueBuffs.Add(buff);
var result = buff.ToResultProto(source);
if (notify)
await Player.SendPacket(new PacketSyncRogueCommonActionResultScNotify(RogueVersionId, result, displayType));
if (updateMenu) await UpdateMenu();
return result;
}
public virtual async ValueTask AddBuffList(List<RogueBuffExcel> excel)
{
List<RogueCommonActionResult> resultList = [];
foreach (var buff in excel)
{
var res = await AddBuff(buff.MazeBuffID, buff.MazeBuffLevel,
displayType: RogueActionDisplayType.RogueCommonActionResultDisplayTypeMulti, updateMenu: false,
notify: false);
if (res != null) resultList.Add(res);
}
await Player.SendPacket(new PacketSyncRogueCommonActionResultScNotify(RogueVersionId, resultList,
RogueActionDisplayType.RogueCommonActionResultDisplayTypeMulti));
await UpdateMenu();
}
public virtual async ValueTask EnhanceBuff(int buffId,
RogueActionSource source = RogueActionSource.RogueCommonActionResultSourceTypeDialogue)
{
var buff = RogueBuffs.Find(x => x.BuffExcel.MazeBuffID == buffId);
if (buff != null)
{
GameData.RogueBuffData.TryGetValue(buffId * 100 + buff.BuffLevel + 1,
out var excel); // make sure the next level exists
if (excel != null)
{
buff.BuffLevel++;
await Player.SendPacket(new PacketSyncRogueCommonActionResultScNotify(RogueVersionId,
buff.ToResultProto(source), RogueActionDisplayType.RogueCommonActionResultDisplayTypeSingle));
}
}
}
public virtual List<RogueBuffInstance> GetRogueBuffInGroup(int groupId)
{
var group = GameData.RogueBuffGroupData[groupId];
return RogueBuffs.FindAll(x => group.BuffList.Contains(x.BuffExcel));
}
public virtual async ValueTask HandleBuffSelect(int buffId)
{
if (RogueActions.Count == 0) return;
var action = RogueActions.First().Value;
if (action.RogueBuffSelectMenu != null)
{
var buff = action.RogueBuffSelectMenu.Buffs.Find(x => x.MazeBuffID == buffId);
if (buff != null) // check if buff is in the list
{
if (RogueBuffs.Exists(x => x.BuffExcel.MazeBuffID == buffId)) // check if buff already exists
{
// enhance
await EnhanceBuff(buffId, RogueActionSource.RogueCommonActionResultSourceTypeSelect);
}
else
{
var instance = new RogueBuffInstance(buff.MazeBuffID, buff.MazeBuffLevel);
RogueBuffs.Add(instance);
await Player.SendPacket(new PacketSyncRogueCommonActionResultScNotify(RogueVersionId,
instance.ToResultProto(RogueActionSource.RogueCommonActionResultSourceTypeSelect)));
}
}
RogueActions.Remove(action.QueuePosition);
if (action.RogueBuffSelectMenu.IsAeonBuff) AeonBuffPending = false; // aeon buff added
}
await UpdateMenu();
await Player.SendPacket(new PacketHandleRogueCommonPendingActionScRsp(action.QueuePosition, true));
}
public virtual async ValueTask HandleRerollBuff()
{
if (RogueActions.Count == 0) return;
var action = RogueActions.First().Value;
if (action.RogueBuffSelectMenu != null)
{
await action.RogueBuffSelectMenu.RerollBuff(); // reroll
await Player.SendPacket(
new PacketHandleRogueCommonPendingActionScRsp(RogueVersionId, menu: action.RogueBuffSelectMenu));
}
}
#endregion
#region Money
public async ValueTask CostMoney(int amount,
RogueActionDisplayType displayType = RogueActionDisplayType.RogueCommonActionResultDisplayTypeNone)
{
CurMoney -= amount;
await Player.SendPacket(new PacketSyncRogueCommonVirtualItemInfoScNotify(this));
await Player.SendPacket(new PacketSyncRogueCommonActionResultScNotify(RogueVersionId,
new RogueCommonActionResult
{
Source = RogueActionSource.RogueCommonActionResultSourceTypeDialogue,
RogueAction = new RogueCommonActionResultData
{
RemoveItemList = new RogueCommonMoney
{
Num = (uint)amount,
DisplayType = (uint)displayType + 1
}
}
}, displayType));
}
public async ValueTask GainMoney(int amount, int displayType = 2,
RogueActionDisplayType display = RogueActionDisplayType.RogueCommonActionResultDisplayTypeNone)
{
CurMoney += amount;
await Player.SendPacket(new PacketSyncRogueCommonVirtualItemInfoScNotify(this));
await Player.SendPacket(new PacketScenePlaneEventScNotify(new ItemData
{
ItemId = 31,
Count = amount
}));
await Player.SendPacket(new PacketSyncRogueCommonActionResultScNotify(RogueVersionId,
new RogueCommonActionResult
{
Source = RogueActionSource.RogueCommonActionResultSourceTypeDialogue,
RogueAction = new RogueCommonActionResultData
{
GetItemList = new RogueCommonMoney
{
Num = (uint)amount,
DisplayType = (uint)displayType
}
}
}, display));
}
#endregion
#region Miracles
public virtual async ValueTask RollMiracle(int amount, int groupId = 10002)
{
GameData.RogueMiracleGroupData.TryGetValue(groupId, out var group);
if (group == null) return;
for (var i = 0; i < amount; i++)
{
var list = new List<int>();
foreach (var miracle in group)
{
if (RogueMiracles.ContainsKey(miracle)) continue;
list.Add(miracle);
}
if (list.Count == 0) return;
var menu = new RogueMiracleSelectMenu(this);
menu.RollMiracle(list);
var action = menu.GetActionInstance();
RogueActions.Add(action.QueuePosition, action);
}
await UpdateMenu();
}
public virtual async ValueTask HandleMiracleSelect(uint miracleId)
{
if (RogueActions.Count == 0) return;
var action = RogueActions.First().Value;
if (action.RogueMiracleSelectMenu != null)
{
var miracle = action.RogueMiracleSelectMenu.Results.Find(x => x == miracleId);
if (miracle != 0) await AddMiracle((int)miracle);
RogueActions.Remove(action.QueuePosition);
}
await UpdateMenu();
await Player.SendPacket(
new PacketHandleRogueCommonPendingActionScRsp(action.QueuePosition, selectMiracle: true));
}
public virtual async ValueTask AddMiracle(int miracleId)
{
if (RogueMiracles.ContainsKey(miracleId)) return;
GameData.RogueMiracleData.TryGetValue(miracleId, out var excel);
if (excel == null) return;
var miracle = new RogueMiracleInstance(this, miracleId);
RogueMiracles.Add(miracleId, miracle);
await Player.SendPacket(new PacketSyncRogueCommonActionResultScNotify(RogueVersionId, miracle.ToGetResult(),
RogueActionDisplayType.RogueCommonActionResultDisplayTypeSingle));
}
#endregion
#region Actions
public virtual async ValueTask HandleBonusSelect(int bonusId)
{
if (RogueActions.Count == 0) return;
var action = RogueActions.First().Value;
// TODO: handle bonus
GameData.RogueBonusData.TryGetValue(bonusId, out var bonus);
if (bonus != null) TriggerEvent(null, bonus.BonusEvent);
RogueActions.Remove(action.QueuePosition);
await UpdateMenu();
await Player.SendPacket(new PacketHandleRogueCommonPendingActionScRsp(action.QueuePosition, selectBonus: true));
}
public virtual async ValueTask UpdateMenu()
{
if (RogueActions.Count > 0)
await Player.SendPacket(
new PacketSyncRogueCommonPendingActionScNotify(RogueActions.First().Value, RogueVersionId));
}
#endregion
#region Handlers
public virtual void OnBattleStart(BattleInstance battle)
{
foreach (var miracle in RogueMiracles.Values) miracle.OnStartBattle(battle);
foreach (var buff in RogueBuffs) buff.OnStartBattle(battle);
}
public abstract ValueTask OnBattleEnd(BattleInstance battle, PVEBattleResultCsReq req);
public virtual async ValueTask OnPropDestruct(EntityProp prop)
{
if (!prop.Excel.IsHpRecover && !prop.Excel.IsMpRecover)
// gain money
await GainMoney(Random.Shared.Next(10, 30));
CurDestroyCount++;
}
#endregion
#region Events
public void TriggerEvent(RogueEventInstance? rogueEvent, int eventId)
{
EventManager?.TriggerEvent(rogueEvent, eventId);
}
public async ValueTask<RogueEventInstance> GenerateEvent(RogueNpc npc)
{
RogueNPCDialogueExcel? dialogue;
do
{
dialogue = GameData.RogueNPCDialogueData.Values.ToList().RandomElement();
} while (!dialogue.CanUseInVer(RogueType));
var instance = new RogueEventInstance(dialogue, npc, CurEventUniqueID++);
if (EventManager == null) return instance;
await EventManager.AddEvent(instance);
return instance;
}
public async ValueTask HandleSelectOption(int eventUniqueId, int optionId)
{
var entity = Player.SceneInstance!.Entities.Values.FirstOrDefault(x =>
x is RogueNpc npc && npc.RogueEvent?.EventUniqueId == eventUniqueId);
if (entity is not RogueNpc npc) return;
if (EventManager == null) return;
await EventManager.SelectOption(npc.RogueEvent!, optionId);
}
public async ValueTask HandleFinishDialogueGroup(int eventUniqueId)
{
var entity = Player.SceneInstance!.Entities.Values.FirstOrDefault(x =>
x is RogueNpc npc && npc.RogueEvent?.EventUniqueId == eventUniqueId);
if (entity is not RogueNpc npc) return;
await EventManager!.FinishEvent(npc.RogueEvent!);
}
public async ValueTask HandleNpcDisappear(int entityId)
{
Player.SceneInstance!.Entities.TryGetValue(entityId, out var entity);
if (entity is not RogueNpc npc) return;
await EventManager!.NpcDisappear(npc.RogueEvent!);
}
#endregion
#region Serialization
public RogueBuffEnhanceInfo ToEnhanceInfo()
{
var proto = new RogueBuffEnhanceInfo();
foreach (var buff in RogueBuffs) proto.EnhanceInfo.Add(buff.ToEnhanceProto());
return proto;
}
public ChessRogueBuffEnhanceInfo ToChessEnhanceInfo()
{
var proto = new ChessRogueBuffEnhanceInfo();
foreach (var buff in RogueBuffs) proto.EnhanceInfo.Add(buff.ToChessEnhanceProto());
return proto;
}
#endregion
}