feat: offering system

This commit is contained in:
StopWuyu
2024-12-15 13:40:39 +08:00
parent b14c26144c
commit 6dfec4ca0b
14 changed files with 380 additions and 2 deletions

View File

@@ -0,0 +1,22 @@
namespace EggLink.DanhengServer.Data.Excel;
[ResourceEntity("OfferingLevelConfig.json")]
public class OfferingLevelConfigExcel : ExcelResource
{
public int ItemCost { get; set; }
public int Level { get; set; }
public int RewardID { get; set; }
public int TypeID { get; set; }
public int UnlockID { get; set; }
public override int GetId()
{
return TypeID * 1000 + Level;
}
public override void Loaded()
{
GameData.OfferingLevelConfigData.TryAdd(TypeID, []);
GameData.OfferingLevelConfigData[TypeID].Add(Level, this);
}
}

View File

@@ -0,0 +1,23 @@
namespace EggLink.DanhengServer.Data.Excel;
[ResourceEntity("OfferingTypeConfig.json")]
public class OfferingTypeConfigExcel : ExcelResource
{
public int MaxLevel { get; set; }
public int ItemID { get; set; }
public int ActivityModuleID { get; set; }
public int LongTailLimit { get; set; }
public int ID { get; set; }
public int UnlockID { get; set; }
public bool IsAutoOffer { get; set; }
public override int GetId()
{
return ID;
}
public override void Loaded()
{
GameData.OfferingTypeConfigData.TryAdd(ID, this);
}
}

View File

@@ -111,6 +111,13 @@ public static class GameData
#endregion
#region Offering
public static Dictionary<int, OfferingTypeConfigExcel> OfferingTypeConfigData { get; private set; } = [];
public static Dictionary<int, Dictionary<int, OfferingLevelConfigExcel>> OfferingLevelConfigData { get; private set; } = [];
#endregion
#region Maze
public static Dictionary<int, NPCDataExcel> NpcDataData { get; private set; } = [];

View File

@@ -0,0 +1,37 @@
using EggLink.DanhengServer.Data;
using EggLink.DanhengServer.Data.Excel;
using EggLink.DanhengServer.Proto;
using SqlSugar;
namespace EggLink.DanhengServer.Database.Inventory;
[SugarTable("offering_data")]
public class OfferingData : BaseDatabaseDataHelper
{
[SugarColumn(IsJson = true, ColumnDataType = "TEXT")]
public Dictionary<int, OfferingTypeData> Offerings { get; set; } = [];
}
public class OfferingTypeData
{
public OfferingState State { get; set; } = OfferingState.Open;
public int CurExp { get; set; }
public int OfferingId { get; set; }
public int Level { get; set; }
public List<int> TakenReward { get; set; } = [];
public OfferingInfo ToProto()
{
var totalExp = CurExp + Enumerable.Range(1, Level).Select(level => GameData.OfferingLevelConfigData.GetValueOrDefault(OfferingId)?.GetValueOrDefault(level)).OfType<OfferingLevelConfigExcel>().Sum(config => config.ItemCost);
return new OfferingInfo
{
OfferingState = State,
HasTakenRewardIdList = { TakenReward.Select(x => (uint)x) },
LevelExp = (uint)CurExp,
OfferingId = (uint)OfferingId,
OfferingLevel = (uint)Level,
TotalExp = (uint)totalExp
};
}
}

View File

@@ -414,7 +414,7 @@ public class InventoryManager(PlayerInstance player) : BasePlayerManager(player)
}
}
public async ValueTask<List<ItemData>> HandleReward(int rewardId, bool notify = false)
public async ValueTask<List<ItemData>> HandleReward(int rewardId, bool notify = false, bool sync = true)
{
GameData.RewardDataData.TryGetValue(rewardId, out var rewardData);
if (rewardData == null) return [];
@@ -426,7 +426,8 @@ public class InventoryManager(PlayerInstance player) : BasePlayerManager(player)
if (i != null) items.Add(i);
}
await Player.SendPacket(new PacketPlayerSyncScNotify(items));
if (sync)
await Player.SendPacket(new PacketPlayerSyncScNotify(items));
var hCoin = await AddItem(1, rewardData.Hcoin, notify, sync: false);
if (hCoin != null)

View File

@@ -0,0 +1,140 @@
using EggLink.DanhengServer.Data;
using EggLink.DanhengServer.Database;
using EggLink.DanhengServer.Database.Inventory;
using EggLink.DanhengServer.GameServer.Game.Player;
using EggLink.DanhengServer.GameServer.Server.Packet.Send.Offering;
using EggLink.DanhengServer.GameServer.Server.Packet.Send.PlayerSync;
using EggLink.DanhengServer.Proto;
namespace EggLink.DanhengServer.GameServer.Game.Inventory;
public class OfferingManager(PlayerInstance player) : BasePlayerManager(player)
{
public OfferingData Data = DatabaseHelper.Instance!.GetInstanceOrCreateNew<OfferingData>(player.Uid);
public OfferingTypeData? GetOfferingData(int offeringId)
{
if (Data.Offerings.TryGetValue(offeringId, out var offeringData))
{
return offeringData;
}
var gameData = GameData.OfferingTypeConfigData.GetValueOrDefault(offeringId); // create a new one
if (gameData == null) return null;
var unlockId = gameData.UnlockID;
var data = new OfferingTypeData
{
OfferingId = offeringId,
State = OfferingState.Lock
};
if (Player.QuestManager!.UnlockHandler.GetUnlockStatus(unlockId))
{
data.State = OfferingState.Open;
}
Data.Offerings[offeringId] = data;
return data;
}
public async ValueTask UpdateOfferingData()
{
List<OfferingTypeData> syncData = [];
foreach (var offering in Data.Offerings.Values)
{
var gameData = GameData.OfferingTypeConfigData.GetValueOrDefault(offering.OfferingId); // create a new one
if (gameData == null) continue;
if (Player.QuestManager!.UnlockHandler.GetUnlockStatus(gameData.UnlockID) &&
offering.State != OfferingState.Open)
{
offering.State = OfferingState.Open;
syncData.Add(offering);
continue;
}
if (Player.QuestManager!.UnlockHandler.GetUnlockStatus(gameData.UnlockID) ||
offering.State == OfferingState.Lock) continue;
offering.State = OfferingState.Lock;
syncData.Add(offering);
}
foreach (var data in syncData)
{
await Player.SendPacket(new PacketOfferingInfoScNotify(data));
}
}
public async ValueTask<(Retcode, OfferingTypeData? data)> SubmitOfferingItem(int offeringId)
{
var offering = GetOfferingData(offeringId);
if (offering is not { State: OfferingState.Open }) return (Retcode.RetOfferingNotUnlock, null);
var gameData = GameData.OfferingTypeConfigData.GetValueOrDefault(offeringId);
if (gameData == null) return (Retcode.RetOfferingNotUnlock, null);
if (offering.Level >= gameData.MaxLevel) return (Retcode.RetOfferingReachMaxLevel, offering);
var item = Player.InventoryManager!.GetItem(gameData.ItemID);
if (item is not { Count: >= 1 }) return (Retcode.RetOfferingItemNotEnough, offering);
var exp = item.Count;
while (true)
{
if (offering.Level >= gameData.MaxLevel) break;
var config = GameData.OfferingLevelConfigData.GetValueOrDefault(offeringId)?.GetValueOrDefault(offering.Level + 1);
if (config == null) break;
if (exp + offering.CurExp < config.ItemCost)
{
offering.CurExp += exp;
exp = 0;
break;
}
exp -= config.ItemCost - offering.CurExp;
offering.Level++;
offering.CurExp = 0;
}
await Player.InventoryManager!.RemoveItem(item.ItemId, item.Count - exp);
return (Retcode.RetSucc, offering);
}
public async ValueTask<(Retcode, OfferingTypeData? data, List<ItemData> reward)> TakeOfferingReward(int offeringId, List<int> takeList)
{
var offering = GetOfferingData(offeringId);
if (offering is not { State: OfferingState.Open }) return (Retcode.RetOfferingNotUnlock, null, []);
var gameData = GameData.OfferingTypeConfigData.GetValueOrDefault(offeringId);
if (gameData == null) return (Retcode.RetOfferingNotUnlock, offering, []);
List<int> rewardIdList = [];
foreach (var excel in takeList.Select(take => GameData.OfferingLevelConfigData.GetValueOrDefault(offeringId)?.GetValueOrDefault(take)))
{
if (excel != null && excel.Level <= offering.Level && !offering.TakenReward.Contains(excel.Level))
{
rewardIdList.Add(excel.RewardID);
}
else
{
return (Retcode.RetOfferingLevelNotUnlock, offering, []);
}
}
offering.TakenReward.AddRange(takeList);
List<ItemData> reward = [];
foreach (var id in rewardIdList)
{
reward.AddRange(await Player.InventoryManager!.HandleReward(id, sync:false));
}
await Player.SendPacket(new PacketPlayerSyncScNotify(reward));
return (Retcode.RetSucc, offering, reward);
}
}

View File

@@ -41,6 +41,7 @@ using EggLink.DanhengServer.Kcp;
using EggLink.DanhengServer.Proto;
using EggLink.DanhengServer.Util;
using static EggLink.DanhengServer.GameServer.Plugin.Event.PluginEvent;
using OfferingManager = EggLink.DanhengServer.GameServer.Game.Inventory.OfferingManager;
namespace EggLink.DanhengServer.GameServer.Game.Player;
@@ -62,6 +63,7 @@ public class PlayerInstance(PlayerData data)
public GachaManager? GachaManager { get; private set; }
public ShopService? ShopService { get; private set; }
public OfferingManager? OfferingManager { get; private set; }
#endregion
@@ -184,6 +186,7 @@ public class PlayerInstance(PlayerData data)
QuestManager = new QuestManager(this);
TrainPartyManager = new TrainPartyManager(this);
MatchThreeManager = new MatchThreeManager(this);
OfferingManager = new OfferingManager(this);
PlayerUnlockData = InitializeDatabase<PlayerUnlockData>();
SceneData = InitializeDatabase<SceneData>();
@@ -416,6 +419,9 @@ public class PlayerInstance(PlayerData data)
if (SceneInstance != null)
await SceneInstance.OnHeartBeat();
if (OfferingManager != null)
await OfferingManager.UpdateOfferingData();
DatabaseHelper.ToSaveUidList.SafeAdd(Uid);
}

View File

@@ -0,0 +1,20 @@
using EggLink.DanhengServer.Database.Inventory;
using EggLink.DanhengServer.GameServer.Server.Packet.Send.Offering;
using EggLink.DanhengServer.Kcp;
using EggLink.DanhengServer.Proto;
namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Offering;
[Opcode(CmdIds.GetOfferingInfoCsReq)]
public class HandlerGetOfferingInfoCsReq : Handler
{
public override async Task OnHandle(Connection connection, byte[] header, byte[] data)
{
var req = GetOfferingInfoCsReq.Parser.ParseFrom(data);
List<OfferingTypeData> dataList = [];
dataList.AddRange(req.OfferingIdList.Select(id => connection.Player!.OfferingManager!.GetOfferingData((int)id)).OfType<OfferingTypeData>());
await connection.SendPacket(new PacketGetOfferingInfoScRsp(dataList));
}
}

View File

@@ -0,0 +1,18 @@
using EggLink.DanhengServer.GameServer.Server.Packet.Send.Offering;
using EggLink.DanhengServer.Kcp;
using EggLink.DanhengServer.Proto;
namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Offering;
[Opcode(CmdIds.SubmitOfferingItemCsReq)]
public class HandlerSubmitOfferingItemCsReq : Handler
{
public override async Task OnHandle(Connection connection, byte[] header, byte[] data)
{
var req = SubmitOfferingItemCsReq.Parser.ParseFrom(data);
var res = await connection.Player!.OfferingManager!.SubmitOfferingItem((int)req.OfferingId);
await connection.SendPacket(new PacketSubmitOfferingItemScRsp(res.Item1, res.data));
}
}

View File

@@ -0,0 +1,18 @@
using EggLink.DanhengServer.GameServer.Server.Packet.Send.Offering;
using EggLink.DanhengServer.Kcp;
using EggLink.DanhengServer.Proto;
namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.Offering;
[Opcode(CmdIds.TakeOfferingRewardCsReq)]
public class HandlerTakeOfferingRewardCsReq : Handler
{
public override async Task OnHandle(Connection connection, byte[] header, byte[] data)
{
var req = TakeOfferingRewardCsReq.Parser.ParseFrom(data);
var res = await connection.Player!.OfferingManager!.TakeOfferingReward((int)req.OfferingId,
req.TakeOfferingRewardIdList.Select(x => (int)x).ToList());
await connection.SendPacket(new PacketTakeOfferingRewardScRsp(res.Item1, res.data, res.reward));
}
}

View File

@@ -0,0 +1,18 @@
using EggLink.DanhengServer.Database.Inventory;
using EggLink.DanhengServer.Kcp;
using EggLink.DanhengServer.Proto;
namespace EggLink.DanhengServer.GameServer.Server.Packet.Send.Offering;
public class PacketGetOfferingInfoScRsp : BasePacket
{
public PacketGetOfferingInfoScRsp(List<OfferingTypeData> dataList) : base(CmdIds.GetOfferingInfoScRsp)
{
var proto = new GetOfferingInfoScRsp
{
OfferingInfoList = { dataList.Select(data => data.ToProto()).ToList() }
};
SetData(proto);
}
}

View File

@@ -0,0 +1,18 @@
using EggLink.DanhengServer.Database.Inventory;
using EggLink.DanhengServer.Kcp;
using EggLink.DanhengServer.Proto;
namespace EggLink.DanhengServer.GameServer.Server.Packet.Send.Offering;
public class PacketOfferingInfoScNotify : BasePacket
{
public PacketOfferingInfoScNotify(OfferingTypeData data) : base(CmdIds.OfferingInfoScNotify)
{
var proto = new OfferingInfoScNotify
{
OfferingInfo = data.ToProto()
};
SetData(proto);
}
}

View File

@@ -0,0 +1,23 @@
using EggLink.DanhengServer.Database.Inventory;
using EggLink.DanhengServer.Kcp;
using EggLink.DanhengServer.Proto;
namespace EggLink.DanhengServer.GameServer.Server.Packet.Send.Offering;
public class PacketSubmitOfferingItemScRsp : BasePacket
{
public PacketSubmitOfferingItemScRsp(Retcode ret, OfferingTypeData? data) : base(CmdIds.SubmitOfferingItemScRsp)
{
var proto = new SubmitOfferingItemScRsp
{
Retcode = (uint)ret
};
if (data != null)
{
proto.OfferingInfo = data.ToProto();
}
SetData(proto);
}
}

View File

@@ -0,0 +1,27 @@
using EggLink.DanhengServer.Database.Inventory;
using EggLink.DanhengServer.Kcp;
using EggLink.DanhengServer.Proto;
namespace EggLink.DanhengServer.GameServer.Server.Packet.Send.Offering;
public class PacketTakeOfferingRewardScRsp : BasePacket
{
public PacketTakeOfferingRewardScRsp(Retcode ret, OfferingTypeData? data, List<ItemData> reward) : base(CmdIds.TakeOfferingRewardScRsp)
{
var proto = new TakeOfferingRewardScRsp
{
Retcode = (uint)ret,
};
if (data != null)
{
proto.OfferingInfo = data.ToProto();
proto.Reward = new ItemList
{
ItemList_ = { reward.Select(x => x.ToProto()) }
};
}
SetData(proto);
}
}