feat: Hand of Zagreus

This commit is contained in:
StopWuyu
2025-06-07 17:26:44 +08:00
parent 9e46cfb7ad
commit 4f2cf356d0
23 changed files with 587 additions and 0 deletions

View File

@@ -0,0 +1,6 @@
namespace EggLink.DanhengServer.GameServer.Game.Player.Components;
public abstract class BasePlayerComponent(PlayerInstance player)
{
protected PlayerInstance Player { get; } = player;
}

View File

@@ -0,0 +1,27 @@
## Introduction of Player Components Part
### Why Components?
Components are a way to organize code in a modular fashion, allowing for better separation of concerns and easier maintenance.
### Why Not Put Everything in PlayerInstance Class?
Putting everything in the `PlayerInstance` class would lead to a monolithic design, making it harder to manage and extend. Components allow for a more flexible architecture where functionality can be added or modified without affecting the entire player instance.
### How to Create Components?
Class `BasePlayerComponent`
- Description: Base class for all player components.
- Usage: Inherit from this class to create a new player component.
- Parameters: PlayerInstance
- Example:
```csharp
public class CustomComponent(PlayerInstance player) : BasePlayerComponent(player)
{
}
```
- Note: Components should be registered in the `PlayerInstance` constructor to ensure they are initialized properly.
### How to Use Components?
- Components are accessed through the `PlayerInstance` class.
- Example:
```csharp
var component = player.GetComponent<CustomComponent>();
```

View File

@@ -0,0 +1,65 @@
using EggLink.DanhengServer.Data;
using EggLink.DanhengServer.Database.Scene;
using EggLink.DanhengServer.Proto;
using EggLink.DanhengServer.Util;
namespace EggLink.DanhengServer.GameServer.Game.Player.Components;
public class SwitchHandComponent(PlayerInstance player) : BasePlayerComponent(player)
{
public int RunningHandConfigId { get; set; } = 0;
public List<SwitchHandInfo> GetHandInfos()
{
List<SwitchHandInfo> infos = [];
foreach (var configId in GameData.MazePuzzleSwitchHandData.Keys)
{
var info = GetHandInfo(configId);
if (info.Item2 == null) continue;
infos.Add(info.Item2);
}
return infos;
}
public (Retcode, SwitchHandInfo?) GetHandInfo(int configId)
{
var excel = GameData.MazePuzzleSwitchHandData.GetValueOrDefault(configId);
if (excel == null) return (Retcode.RetInteractConfigNotExist, null);
if (Player.SceneData!.SwitchHandData.TryGetValue(configId, out var info))
{
return (Retcode.RetSucc, info);
}
// create a new one
info = new SwitchHandInfo
{
ConfigId = configId
};
// set default values
var floorInfo = GameData.GetFloorInfo(excel.FloorID);
if (floorInfo == null) return (Retcode.RetInteractConfigNotExist, null);
if (!floorInfo.Groups.TryGetValue(excel.SwitchHandID[0], out var groupInfo)) return (Retcode.RetReqParaInvalid, null);
var prop = groupInfo.PropList.FirstOrDefault(x => x.ID == excel.SwitchHandID[1]);
if (prop == null) return (Retcode.RetReqParaInvalid, null);
info.Pos = prop.ToPositionProto();
info.Rot = prop.ToRotationProto();
Player.SceneData.SwitchHandData[configId] = info;
return (Retcode.RetSucc, info);
}
public (Retcode, SwitchHandInfo?) UpdateHandInfo(HandInfo info)
{
var dbInfo = GetHandInfo((int)info.ConfigId).Item2;
if (dbInfo == null) return (Retcode.RetInteractConfigNotExist, null);
dbInfo.Pos = info.HandMotion.Pos.ToPosition();
dbInfo.Rot = info.HandMotion.Rot.ToPosition();
dbInfo.State = info.HandState;
dbInfo.ByteValue = info.HandByteValue.ToByteArray();
return (Retcode.RetSucc, dbInfo);
}
}

View File

@@ -20,6 +20,7 @@ using EggLink.DanhengServer.GameServer.Game.Lineup;
using EggLink.DanhengServer.GameServer.Game.Mail;
using EggLink.DanhengServer.GameServer.Game.Message;
using EggLink.DanhengServer.GameServer.Game.Mission;
using EggLink.DanhengServer.GameServer.Game.Player.Components;
using EggLink.DanhengServer.GameServer.Game.Quest;
using EggLink.DanhengServer.GameServer.Game.Raid;
using EggLink.DanhengServer.GameServer.Game.Rogue;
@@ -115,6 +116,7 @@ public class PlayerInstance(PlayerData data)
public BattleCollegeData? BattleCollegeData { get; private set; }
public ServerPrefsData? ServerPrefsData { get; private set; }
public SceneInstance? SceneInstance { get; private set; }
public List<BasePlayerComponent> Components { get; } = [];
public int Uid { get; set; }
public Connection? Connection { get; set; }
public bool Initialized { get; set; }
@@ -200,6 +202,8 @@ public class PlayerInstance(PlayerData data)
ServerPrefsData = InitializeDatabase<ServerPrefsData>();
BattleCollegeData = InitializeDatabase<BattleCollegeData>();
Components.Add(new SwitchHandComponent(this));
if ((int)(ServerPrefsData.Version * 1000) != GameConstants.GameVersionInt)
{
ServerPrefsData.ServerPrefsDict.Clear();
@@ -471,6 +475,11 @@ public class PlayerInstance(PlayerData data)
DatabaseHelper.ToSaveUidList.SafeAdd(Uid);
}
public T GetComponent<T>() where T : BasePlayerComponent
{
return Components.OfType<T>().FirstOrDefault() ?? throw new InvalidOperationException($"Component {typeof(T)} not found.");
}
#endregion
#region Scene Actions

View File

@@ -0,0 +1,31 @@
using EggLink.DanhengServer.GameServer.Game.Scene.Entity;
using EggLink.DanhengServer.GameServer.Server.Packet.Send.MiscModule;
using EggLink.DanhengServer.Kcp;
using EggLink.DanhengServer.Proto;
namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.MiscModule;
[Opcode(CmdIds.MazeKillDirectCsReq)]
public class HandlerMazeKillDirectCsReq : Handler
{
public override async Task OnHandle(Connection connection, byte[] header, byte[] data)
{
var req = MazeKillDirectCsReq.Parser.ParseFrom(data);
foreach (var entityId in req.EntityList.ToList())
{
if (!connection.Player!.SceneInstance!.Entities.TryGetValue((int)entityId, out var entity)) continue;
if (entity is EntityMonster monster)
{
await monster.Kill();
}
else
{
// remove entity if it's not a monster
connection.Player.SceneInstance.Entities.Remove((int)entityId);
}
}
await connection.SendPacket(new PacketMazeKillDirectScRsp(req.EntityList.ToList()));
}
}

View File

@@ -0,0 +1,27 @@
using EggLink.DanhengServer.GameServer.Game.Player.Components;
using EggLink.DanhengServer.GameServer.Server.Packet.Send.SwitchHand;
using EggLink.DanhengServer.Kcp;
using EggLink.DanhengServer.Proto;
namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.SwitchHand;
[Opcode(CmdIds.SwitchHandCoinUpdateCsReq)]
public class HandlerSwitchHandCoinUpdateCsReq : Handler
{
public override async Task OnHandle(Connection connection, byte[] header, byte[] data)
{
var req = SwitchHandCoinUpdateCsReq.Parser.ParseFrom(data);
var component = connection.Player!.GetComponent<SwitchHandComponent>();
var info = component.GetHandInfo(component.RunningHandConfigId);
if (info.Item2 == null)
{
await connection.SendPacket(new PacketSwitchHandCoinUpdateScRsp(info.Item1));
}
else
{
info.Item2.CoinNum = (int)req.HandCoinNum;
await connection.SendPacket(new PacketSwitchHandCoinUpdateScRsp(req.HandCoinNum));
}
}
}

View File

@@ -0,0 +1,34 @@
using EggLink.DanhengServer.GameServer.Game.Player.Components;
using EggLink.DanhengServer.GameServer.Server.Packet.Send.SwitchHand;
using EggLink.DanhengServer.Kcp;
using EggLink.DanhengServer.Proto;
namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.SwitchHand;
[Opcode(CmdIds.SwitchHandDataCsReq)]
public class HandlerSwitchHandDataCsReq : Handler
{
public override async Task OnHandle(Connection connection, byte[] header, byte[] data)
{
var req = SwitchHandDataCsReq.Parser.ParseFrom(data);
var component = connection.Player!.GetComponent<SwitchHandComponent>();
if (req.ConfigId != 0)
{
var info = component.GetHandInfo((int)req.ConfigId);
if (info.Item2 == null)
{
await connection.SendPacket(new PacketSwitchHandDataScRsp(info.Item1));
}
else
{
await connection.SendPacket(new PacketSwitchHandDataScRsp(info.Item2));
}
}
else
{
var infos = component.GetHandInfos();
await connection.SendPacket(new PacketSwitchHandDataScRsp(infos));
}
}
}

View File

@@ -0,0 +1,25 @@
using EggLink.DanhengServer.GameServer.Game.Player.Components;
using EggLink.DanhengServer.GameServer.Server.Packet.Send.SwitchHand;
using EggLink.DanhengServer.Kcp;
namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.SwitchHand;
[Opcode(CmdIds.SwitchHandFinishCsReq)]
public class HandlerSwitchHandFinishCsReq : Handler
{
public override async Task OnHandle(Connection connection, byte[] header, byte[] data)
{
var component = connection.Player!.GetComponent<SwitchHandComponent>();
var info = component.GetHandInfo(component.RunningHandConfigId);
component.RunningHandConfigId = 0;
if (info.Item2 == null)
{
await connection.SendPacket(new PacketSwitchHandFinishScRsp(info.Item1));
}
else
{
await connection.SendPacket(new PacketSwitchHandFinishScRsp(info.Item2));
}
}
}

View File

@@ -0,0 +1,27 @@
using EggLink.DanhengServer.GameServer.Game.Player.Components;
using EggLink.DanhengServer.GameServer.Server.Packet.Send.SwitchHand;
using EggLink.DanhengServer.Kcp;
using EggLink.DanhengServer.Proto;
namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.SwitchHand;
[Opcode(CmdIds.SwitchHandResetGameCsReq)]
public class HandlerSwitchHandResetGameCsReq : Handler
{
public override async Task OnHandle(Connection connection, byte[] header, byte[] data)
{
var req = SwitchHandResetGameCsReq.Parser.ParseFrom(data);
var component = connection.Player!.GetComponent<SwitchHandComponent>();
var info = component.UpdateHandInfo(req.ResetHandInfo);
if (info.Item2 == null)
{
await connection.SendPacket(new PacketSwitchHandResetGameScRsp(info.Item1));
}
else
{
await connection.SendPacket(new PacketSwitchHandResetGameScRsp(info.Item2));
}
}
}

View File

@@ -0,0 +1,31 @@
using EggLink.DanhengServer.GameServer.Game.Player.Components;
using EggLink.DanhengServer.GameServer.Server.Packet.Send.SwitchHand;
using EggLink.DanhengServer.Kcp;
using EggLink.DanhengServer.Proto;
using EggLink.DanhengServer.Util;
namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.SwitchHand;
[Opcode(CmdIds.SwitchHandResetHandPosCsReq)]
public class HandlerSwitchHandResetHandPosCsReq : Handler
{
public override async Task OnHandle(Connection connection, byte[] header, byte[] data)
{
var req = SwitchHandResetHandPosCsReq.Parser.ParseFrom(data);
var component = connection.Player!.GetComponent<SwitchHandComponent>();
var info = component.GetHandInfo((int)req.ConfigId);
if (info.Item2 == null)
{
await connection.SendPacket(new PacketSwitchHandResetHandPosScRsp(info.Item1));
}
else
{
info.Item2.Pos = req.HandMotion.Pos.ToPosition();
info.Item2.Rot = req.HandMotion.Rot.ToPosition();
await connection.SendPacket(new PacketSwitchHandResetHandPosScRsp(info.Item2));
}
}
}

View File

@@ -0,0 +1,20 @@
using EggLink.DanhengServer.GameServer.Game.Player.Components;
using EggLink.DanhengServer.GameServer.Server.Packet.Send.SwitchHand;
using EggLink.DanhengServer.Kcp;
using EggLink.DanhengServer.Proto;
namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.SwitchHand;
[Opcode(CmdIds.SwitchHandStartCsReq)]
public class HandlerSwitchHandStartCsReq : Handler
{
public override async Task OnHandle(Connection connection, byte[] header, byte[] data)
{
var req = SwitchHandStartCsReq.Parser.ParseFrom(data);
var component = connection.Player!.GetComponent<SwitchHandComponent>();
component.RunningHandConfigId = (int)req.ConfigId;
await connection.SendPacket(new PacketSwitchHandStartScRsp(req.ConfigId));
}
}

View File

@@ -0,0 +1,26 @@
using EggLink.DanhengServer.GameServer.Game.Player.Components;
using EggLink.DanhengServer.GameServer.Server.Packet.Send.SwitchHand;
using EggLink.DanhengServer.Kcp;
using EggLink.DanhengServer.Proto;
namespace EggLink.DanhengServer.GameServer.Server.Packet.Recv.SwitchHand;
[Opcode(CmdIds.SwitchHandUpdateCsReq)]
public class HandlerSwitchHandUpdateCsReq : Handler
{
public override async Task OnHandle(Connection connection, byte[] header, byte[] data)
{
var req = SwitchHandUpdateCsReq.Parser.ParseFrom(data);
var component = connection.Player!.GetComponent<SwitchHandComponent>();
var info = component.UpdateHandInfo(req.OperationHandInfo);
if (info.Item2 == null)
{
await connection.SendPacket(new PacketSwitchHandUpdateScRsp(info.Item1, req.HandOperationInfo));
}
else
{
await connection.SendPacket(new PacketSwitchHandUpdateScRsp(info.Item2, req.HandOperationInfo));
}
}
}

View File

@@ -0,0 +1,17 @@
using EggLink.DanhengServer.Kcp;
using EggLink.DanhengServer.Proto;
namespace EggLink.DanhengServer.GameServer.Server.Packet.Send.MiscModule;
public class PacketMazeKillDirectScRsp : BasePacket
{
public PacketMazeKillDirectScRsp(List<uint> entityIds) : base(CmdIds.MazeKillDirectScRsp)
{
var proto = new MazeKillDirectScRsp
{
EntityList = { entityIds }
};
SetData(proto);
}
}

View File

@@ -0,0 +1,24 @@
using EggLink.DanhengServer.Kcp;
using EggLink.DanhengServer.Proto;
namespace EggLink.DanhengServer.GameServer.Server.Packet.Send.SwitchHand;
public class PacketSwitchHandCoinUpdateScRsp : BasePacket
{
public PacketSwitchHandCoinUpdateScRsp(Retcode ret) : base(CmdIds.SwitchHandCoinUpdateScRsp)
{
var proto = new SwitchHandCoinUpdateScRsp
{
Retcode = (uint)ret
};
SetData(proto);
}
public PacketSwitchHandCoinUpdateScRsp(uint coinNum) : base(CmdIds.SwitchHandCoinUpdateScRsp)
{
var proto = new SwitchHandCoinUpdateScRsp
{
HandCoinNum = coinNum
};
SetData(proto);
}
}

View File

@@ -0,0 +1,38 @@
using EggLink.DanhengServer.Database.Scene;
using EggLink.DanhengServer.Kcp;
using EggLink.DanhengServer.Proto;
namespace EggLink.DanhengServer.GameServer.Server.Packet.Send.SwitchHand;
public class PacketSwitchHandDataScRsp : BasePacket
{
public PacketSwitchHandDataScRsp(SwitchHandInfo info) : base(CmdIds.SwitchHandDataScRsp)
{
var proto = new SwitchHandDataScRsp
{
TargetHandInfo = { info.ToProto() }
};
SetData(proto);
}
public PacketSwitchHandDataScRsp(List<SwitchHandInfo> infos) : base(CmdIds.SwitchHandDataScRsp)
{
var proto = new SwitchHandDataScRsp
{
TargetHandInfo = { infos.Select(x => x.ToProto()) }
};
SetData(proto);
}
public PacketSwitchHandDataScRsp(Retcode code) : base(CmdIds.SwitchHandDataScRsp)
{
var proto = new SwitchHandDataScRsp
{
Retcode = (uint)code
};
SetData(proto);
}
}

View File

@@ -0,0 +1,28 @@
using EggLink.DanhengServer.Database.Scene;
using EggLink.DanhengServer.Kcp;
using EggLink.DanhengServer.Proto;
namespace EggLink.DanhengServer.GameServer.Server.Packet.Send.SwitchHand;
public class PacketSwitchHandFinishScRsp : BasePacket
{
public PacketSwitchHandFinishScRsp(SwitchHandInfo info) : base(CmdIds.SwitchHandFinishScRsp)
{
var proto = new SwitchHandFinishScRsp
{
HandInfo = info.ToProto()
};
SetData(proto);
}
public PacketSwitchHandFinishScRsp(Retcode ret) : base(CmdIds.SwitchHandFinishScRsp)
{
var proto = new SwitchHandFinishScRsp
{
Retcode = (uint)ret
};
SetData(proto);
}
}

View File

@@ -0,0 +1,27 @@
using EggLink.DanhengServer.Database.Scene;
using EggLink.DanhengServer.Kcp;
using EggLink.DanhengServer.Proto;
namespace EggLink.DanhengServer.GameServer.Server.Packet.Send.SwitchHand;
public class PacketSwitchHandResetGameScRsp : BasePacket
{
public PacketSwitchHandResetGameScRsp(SwitchHandInfo info) : base(CmdIds.SwitchHandResetGameScRsp)
{
var proto = new SwitchHandResetGameScRsp
{
TargetHandInfo = info.ToProto()
};
SetData(proto);
}
public PacketSwitchHandResetGameScRsp(Retcode ret) : base(CmdIds.SwitchHandResetGameScRsp)
{
var proto = new SwitchHandResetGameScRsp
{
Retcode = (uint)ret
};
SetData(proto);
}
}

View File

@@ -0,0 +1,28 @@
using EggLink.DanhengServer.Database.Scene;
using EggLink.DanhengServer.Kcp;
using EggLink.DanhengServer.Proto;
namespace EggLink.DanhengServer.GameServer.Server.Packet.Send.SwitchHand;
public class PacketSwitchHandResetHandPosScRsp : BasePacket
{
public PacketSwitchHandResetHandPosScRsp(SwitchHandInfo info) : base(CmdIds.SwitchHandResetHandPosScRsp)
{
var proto = new SwitchHandResetHandPosScRsp
{
TargetHandInfo = info.ToProto()
};
SetData(proto);
}
public PacketSwitchHandResetHandPosScRsp(Retcode ret) : base(CmdIds.SwitchHandResetHandPosScRsp)
{
var proto = new SwitchHandResetHandPosScRsp
{
Retcode = (uint)ret
};
SetData(proto);
}
}

View File

@@ -0,0 +1,17 @@
using EggLink.DanhengServer.Kcp;
using EggLink.DanhengServer.Proto;
namespace EggLink.DanhengServer.GameServer.Server.Packet.Send.SwitchHand;
public class PacketSwitchHandStartScRsp : BasePacket
{
public PacketSwitchHandStartScRsp(uint configId) : base(CmdIds.SwitchHandStartScRsp)
{
var proto = new SwitchHandStartScRsp
{
ConfigId = configId
};
SetData(proto);
}
}

View File

@@ -0,0 +1,29 @@
using EggLink.DanhengServer.Database.Scene;
using EggLink.DanhengServer.Kcp;
using EggLink.DanhengServer.Proto;
namespace EggLink.DanhengServer.GameServer.Server.Packet.Send.SwitchHand;
public class PacketSwitchHandUpdateScRsp : BasePacket
{
public PacketSwitchHandUpdateScRsp(SwitchHandInfo info, HandOperationInfo? operationInfo) : base(CmdIds.SwitchHandUpdateScRsp)
{
var proto = new SwitchHandUpdateScRsp
{
HandInfo = info.ToProto(),
HandOperationInfo = operationInfo ?? new HandOperationInfo()
};
SetData(proto);
}
public PacketSwitchHandUpdateScRsp(Retcode ret, HandOperationInfo? operationInfo) : base(CmdIds.SwitchHandUpdateScRsp)
{
var proto = new SwitchHandUpdateScRsp
{
Retcode = (uint)ret,
HandOperationInfo = operationInfo ?? new HandOperationInfo()
};
SetData(proto);
}
}