yuanshen/Yuanshen.Gameserver/Game/GameAvatar.cs
wisedev fc2ac1050f First working version of the server
KCP gameserver with packet dispatch, scene streaming, combat, gacha and the
shop, plus a small HTTP server for the login flow and the wish history page.

Game data under assets is pulled out of the client instead of being typed by
hand. The excel tables come from the packed bin container and the spawn points
from the compiled scene scripts.
2026-09-04 02:10:50 +03:00

201 lines
6.9 KiB
C#

using Yuanshen.Common.Data.Excel;
using Yuanshen.Common.Extensions;
using Yuanshen.Protocol;
namespace Yuanshen.Gameserver.Game;
internal sealed class GameAvatar
{
public uint AvatarId { get; }
public ulong Guid { get; }
public uint SkillDepotId { get; }
public uint EntityId { get; }
public uint WeaponEntityId { get; }
public ulong WeaponGuid { get; set; }
public uint OwnerUid { get; set; }
public uint Level { get; set; } = 90;
public uint PromoteLevel { get; set; } = 6;
public uint TalentLevel { get; set; } = 10;
public uint Constellation { get; set; }
public uint FlycloakId { get; set; } = 140001;
public uint CoreProudSkillLevel => (uint)((PromoteLevel >= 1 ? 1 : 0) + (PromoteLevel >= 4 ? 1 : 0));
public uint BornTime { get; } = 1600000000;
public Dictionary<uint, ulong> ReliquaryGuids { get; } = new();
private readonly AvatarExcel _excel;
private readonly AvatarSkillDepotExcel? _depot;
private readonly AvatarAbilitiesExcel? _abilities;
public GameAvatar(AvatarExcel excel, ulong guid, uint entityId, uint weaponEntityId, AvatarSkillDepotExcel? depot = null, AvatarAbilitiesExcel? abilities = null)
{
_excel = excel;
_depot = depot;
_abilities = abilities;
AvatarId = excel.Id;
SkillDepotId = excel.SkillDepotId;
Guid = guid;
EntityId = entityId;
WeaponEntityId = weaponEntityId;
}
public AvatarInfo AsAvatarInfo()
{
AvatarInfo info = new()
{
AvatarId = AvatarId,
Guid = Guid,
LifeState = 1,
SkillDepotId = SkillDepotId,
BornTime = BornTime,
AvatarType = 1,
WearingFlycloakId = 140001,
CoreProudSkillLevel = CoreProudSkillLevel,
FetterInfo = new AvatarFetterInfo { ExpLevel = 10 }
};
info.PropMap.Add(4001u, new PropValue { Type = 4001, Ival = Level, Val = Level });
info.PropMap.Add(1002u, new PropValue { Type = 1002, Ival = PromoteLevel, Val = PromoteLevel });
info.PropMap.Add(1001u, new PropValue { Type = 1001, Ival = 0, Val = 0 });
info.PropMap.Add(4002u, new PropValue { Type = 4002, Ival = 0, Val = 0 });
foreach ((uint type, float value) in FightProps())
info.FightPropMap.Add(type, value);
foreach (uint skillId in SkillIds())
info.SkillLevelMap.Add(skillId, TalentLevel);
foreach (uint proud in _depot?.InherentProudSkillOpens ?? new List<uint>())
info.InherentProudSkillList.Add(proud);
foreach (uint talentId in TalentIds())
info.TalentIdList.Add(talentId);
info.EquipGuidList.Add(WeaponGuid);
foreach (ulong guid in ReliquaryGuids.Values)
info.EquipGuidList.Add(guid);
return info;
}
public SceneEntityInfo AsSceneEntity(Vector pos, GameWeapon weapon, IEnumerable<GameReliquary>? reliquaries = null)
{
SceneEntityInfo entity = new()
{
EntityType = ProtEntityType.ProtEntityAvatar,
EntityId = EntityId,
LifeState = 1,
MotionInfo = new MotionInfo
{
Pos = pos,
Rot = new Vector(),
Speed = new Vector(),
State = MotionState.MotionStandby
},
EntityClientData = new EntityClientData(),
EntityAuthorityInfo = new EntityAuthorityInfo
{
AbilityInfo = new AbilitySyncStateInfo { IsInited = true },
RendererChangedInfo = new EntityRendererChangedInfo(),
AiInfo = new SceneEntityAiInfo { IsAiOpen = true, BornPos = pos },
BornPos = pos
},
Avatar = new SceneAvatarInfo
{
PlayerId = OwnerUid,
AvatarId = AvatarId,
Guid = Guid,
PeerId = Player.PeerId,
SkillDepotId = SkillDepotId,
BornTime = BornTime,
WearingFlycloakId = 140001,
Weapon = weapon.AsSceneWeapon(WeaponEntityId)
}
};
entity.PropList.Add(new PropPair
{
Type = 4001,
PropValue = new PropValue { Type = 4001, Ival = Level, Val = Level }
});
entity.PropList.Add(new PropPair
{
Type = 1002,
PropValue = new PropValue { Type = 1002, Ival = PromoteLevel, Val = PromoteLevel }
});
entity.Avatar.CoreProudSkillLevel = CoreProudSkillLevel;
foreach (uint skillId in SkillIds())
entity.Avatar.SkillLevelMap.Add(skillId, TalentLevel);
foreach (uint proud in _depot?.InherentProudSkillOpens ?? new List<uint>())
entity.Avatar.InherentProudSkillList.Add(proud);
foreach (uint talentId in TalentIds())
entity.Avatar.TalentIdList.Add(talentId);
foreach ((uint type, float value) in FightProps())
entity.FightPropList.Add(new FightPropPair { Type = type, PropValue = value });
if (reliquaries is not null)
{
foreach (GameReliquary reliquary in reliquaries)
entity.Avatar.ReliquaryList.Add(reliquary.AsSceneReliquary());
}
return entity;
}
public AbilityControlBlock BuildAbilityControlBlock()
{
AbilityControlBlock block = new();
foreach (string ability in _abilities?.Abilities ?? new List<string>())
{
block.AbilityEmbryoList.Add(new AbilityEmbryo
{
AbilityId = (uint)(block.AbilityEmbryoList.Count + 1),
AbilityNameHash = ability.GetStableHash(),
AbilityOverrideNameHash = "Default".GetStableHash()
});
}
return block;
}
private IEnumerable<uint> TalentIds()
{
for (uint i = 0; i < Constellation && i < 6; i++)
yield return AvatarId * 10 + (i + 1);
}
private IEnumerable<uint> SkillIds()
{
if (_depot is null)
yield break;
foreach (uint id in _depot.Skills)
yield return id;
if (_depot.EnergySkill != 0)
yield return _depot.EnergySkill;
}
private IEnumerable<(uint Type, float Value)> FightProps()
{
yield return (1u, (float)_excel.HpBase);
yield return (4u, (float)_excel.AttackBase);
yield return (7u, (float)_excel.DefenseBase);
yield return (1010u, (float)_excel.HpBase);
yield return (2000u, (float)_excel.HpBase);
yield return (2001u, (float)_excel.AttackBase);
yield return (2002u, (float)_excel.DefenseBase);
yield return (20u, (float)_excel.Critical);
yield return (22u, (float)_excel.CriticalHurt);
yield return (23u, (float)_excel.ChargeEfficiency);
for (uint element = 0; element < 7; element++)
{
yield return (70u + element, 100f);
yield return (1000u + element, 100f);
}
}
}