mirror of
https://github.com/MikuLeaks/MikuSB.git
synced 2026-06-04 13:23:58 +00:00
Compare commits
7 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
6740b8ecf7 | ||
|
|
233419eba3 | ||
|
|
a7a68418db | ||
|
|
f053359c41 | ||
|
|
0cf633a0e1 | ||
|
|
e4397f10ee | ||
|
|
bdb4ee3d51 |
24
Common/Data/Config/StringToUIntConverter.cs
Normal file
24
Common/Data/Config/StringToUIntConverter.cs
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace MikuSB.Data.Config;
|
||||||
|
|
||||||
|
public class StringToUIntConverter : JsonConverter<uint>
|
||||||
|
{
|
||||||
|
public override uint ReadJson(JsonReader reader, Type objectType, uint existingValue, bool hasExistingValue, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
if (reader.Value == null)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
var value = reader.Value.ToString();
|
||||||
|
|
||||||
|
if (string.IsNullOrWhiteSpace(value))
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
return uint.TryParse(value, out var result) ? result : 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void WriteJson(JsonWriter writer, uint value, JsonSerializer serializer)
|
||||||
|
{
|
||||||
|
writer.WriteValue(value);
|
||||||
|
}
|
||||||
|
}
|
||||||
21
Common/Data/Excel/DormGiftExcel.cs
Normal file
21
Common/Data/Excel/DormGiftExcel.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
namespace MikuSB.Data.Excel;
|
||||||
|
|
||||||
|
[ResourceEntity("item/templates/dorm_gift.json")]
|
||||||
|
public class DormGiftExcel : ExcelResource
|
||||||
|
{
|
||||||
|
public uint Genre { get; set; }
|
||||||
|
public uint Detail { get; set; }
|
||||||
|
public uint Particular { get; set; }
|
||||||
|
public uint Level { get; set; }
|
||||||
|
public string I18n { get; set; } = "";
|
||||||
|
|
||||||
|
public override uint GetId()
|
||||||
|
{
|
||||||
|
return (Genre << 24) | (Detail << 16) | (Particular << 8) | Level;
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Loaded()
|
||||||
|
{
|
||||||
|
GameData.DormGiftData.Add(GetId(), this);
|
||||||
|
}
|
||||||
|
}
|
||||||
21
Common/Data/Excel/GuideExcel.cs
Normal file
21
Common/Data/Excel/GuideExcel.cs
Normal file
@@ -0,0 +1,21 @@
|
|||||||
|
using MikuSB.Data.Config;
|
||||||
|
using Newtonsoft.Json;
|
||||||
|
|
||||||
|
namespace MikuSB.Data.Excel;
|
||||||
|
|
||||||
|
[ResourceEntity("guide/guide.json")]
|
||||||
|
public class GuideExcel : ExcelResource
|
||||||
|
{
|
||||||
|
public uint ID { get; set; }
|
||||||
|
[JsonConverter(typeof(StringToUIntConverter))] public uint Group { get; set; }
|
||||||
|
|
||||||
|
public override uint GetId()
|
||||||
|
{
|
||||||
|
return (ID << 48) | (Group << 32);
|
||||||
|
}
|
||||||
|
|
||||||
|
public override void Loaded()
|
||||||
|
{
|
||||||
|
GameData.GuideData.TryAdd(GetId(), this);
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -29,6 +29,8 @@ public static class GameData
|
|||||||
public static Dictionary<uint, CardSkinPartsExcel> CardSkinPartsData { get; private set; } = [];
|
public static Dictionary<uint, CardSkinPartsExcel> CardSkinPartsData { get; private set; } = [];
|
||||||
public static Dictionary<uint, CallItemExcel> CallItemData { get; private set; } = [];
|
public static Dictionary<uint, CallItemExcel> CallItemData { get; private set; } = [];
|
||||||
public static Dictionary<uint, WeaponPartsExcel> WeaponPartsData { get; private set; } = [];
|
public static Dictionary<uint, WeaponPartsExcel> WeaponPartsData { get; private set; } = [];
|
||||||
|
public static Dictionary<uint, GuideExcel> GuideData { get; private set; } = [];
|
||||||
|
public static Dictionary<uint, DormGiftExcel> DormGiftData { get; private set; } = [];
|
||||||
}
|
}
|
||||||
|
|
||||||
public static class GameResourceTemplateId
|
public static class GameResourceTemplateId
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ public class ServerTextCHS
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class WordTextCHS
|
public class WordTextCHS
|
||||||
{
|
{
|
||||||
|
public string Furniture => "家具";
|
||||||
|
public string Skin => "皮肤";
|
||||||
public string WeaponPart => "武器部件";
|
public string WeaponPart => "武器部件";
|
||||||
public string CallItem => "召唤道具";
|
public string CallItem => "召唤道具";
|
||||||
public string SkinPart => "皮肤部件";
|
public string SkinPart => "皮肤部件";
|
||||||
@@ -227,12 +229,14 @@ public class GirlTextCHS
|
|||||||
public string Usage =>
|
public string Usage =>
|
||||||
"用法: /girl add <detail/-1> -p<particular> -l<level> -s<star>\n" +
|
"用法: /girl add <detail/-1> -p<particular> -l<level> -s<star>\n" +
|
||||||
"用法: /girl level <guid/-1> <level>\n" +
|
"用法: /girl level <guid/-1> <level>\n" +
|
||||||
"用法: /girl neuronic <guid/-1> <level>";
|
"用法: /girl neuronic <guid/-1> <level>\n" +
|
||||||
|
"用法: /girl break <guid/-1> <level>";
|
||||||
|
|
||||||
public string NotFound => "角色不存在!";
|
public string NotFound => "角色不存在!";
|
||||||
public string Added => "已为玩家添加 {0} 个角色!";
|
public string Added => "已为玩家添加 {0} 个角色!";
|
||||||
public string UpdateLevel => "已将 {1} 个角色等级设置为 {0}!";
|
public string UpdateLevel => "已将 {1} 个角色等级设置为 {0}!";
|
||||||
public string UpdateNeuronicLevel => "已将 {1} 个角色的神经元等级设置为 {0}!";
|
public string UpdateNeuronicLevel => "已将 {1} 个角色的神经元等级设置为 {0}!";
|
||||||
|
public string UpdateBreakLevel => "已将 {1} 个角色的天启/武格等级设置为 {0}!";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ public class ServerTextCHT
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class WordTextCHT
|
public class WordTextCHT
|
||||||
{
|
{
|
||||||
|
public string Furniture => "傢俱";
|
||||||
|
public string Skin => "皮膚";
|
||||||
public string WeaponPart => "武器部件";
|
public string WeaponPart => "武器部件";
|
||||||
public string CallItem => "召喚道具";
|
public string CallItem => "召喚道具";
|
||||||
public string SkinPart => "外觀部件";
|
public string SkinPart => "外觀部件";
|
||||||
@@ -227,12 +229,14 @@ public class GirlTextCHT
|
|||||||
public string Usage =>
|
public string Usage =>
|
||||||
"用法: /girl add <detail/-1> -p<particular> -l<level> -s<star>\n" +
|
"用法: /girl add <detail/-1> -p<particular> -l<level> -s<star>\n" +
|
||||||
"用法: /girl level <guid/-1> <level>\n" +
|
"用法: /girl level <guid/-1> <level>\n" +
|
||||||
"用法: /girl neuronic <guid/-1> <level>";
|
"用法: /girl neuronic <guid/-1> <level>\n" +
|
||||||
|
"用法: /girl break <guid/-1> <level>";
|
||||||
|
|
||||||
public string NotFound => "角色不存在!";
|
public string NotFound => "角色不存在!";
|
||||||
public string Added => "已為玩家新增 {0} 個角色!";
|
public string Added => "已為玩家新增 {0} 個角色!";
|
||||||
public string UpdateLevel => "已將 {1} 個角色等級設為 {0}!";
|
public string UpdateLevel => "已將 {1} 個角色等級設為 {0}!";
|
||||||
public string UpdateNeuronicLevel => "已將 {1} 個角色的神經元等級設置為 {0}!";
|
public string UpdateNeuronicLevel => "已將 {1} 個角色的神經元等級設置為 {0}!";
|
||||||
|
public string UpdateBreakLevel => "已将 {1} 个角色的天啟/武格等级设置为 {0}!";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -35,6 +35,8 @@ public class ServerTextEN
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public class WordTextEN
|
public class WordTextEN
|
||||||
{
|
{
|
||||||
|
public string Furniture => "Furniture";
|
||||||
|
public string Skin => "Skin";
|
||||||
public string WeaponPart => "Weapon Part";
|
public string WeaponPart => "Weapon Part";
|
||||||
public string CallItem => "Call Item";
|
public string CallItem => "Call Item";
|
||||||
public string SkinPart => "Skin Part";
|
public string SkinPart => "Skin Part";
|
||||||
@@ -193,12 +195,14 @@ public class GirlTextEN
|
|||||||
public string Usage =>
|
public string Usage =>
|
||||||
"Usage: /girl add <detail/-1> -p<particular> -l<level> -s<star>\n" +
|
"Usage: /girl add <detail/-1> -p<particular> -l<level> -s<star>\n" +
|
||||||
"Usage: /girl level <guid/-1> <level>\n" +
|
"Usage: /girl level <guid/-1> <level>\n" +
|
||||||
"Usage: /girl neuronic <guid/-1> <level>";
|
"Usage: /girl neuronic <guid/-1> <level>\n" +
|
||||||
|
"Usage: /girl break <guid/-1> <level>";
|
||||||
|
|
||||||
public string NotFound => "Character not found!";
|
public string NotFound => "Character not found!";
|
||||||
public string Added => "Granted {0} character(s) to player!";
|
public string Added => "Granted {0} character(s) to player!";
|
||||||
public string UpdateLevel => "Set {1} character(s) to level {0}!";
|
public string UpdateLevel => "Set {1} character(s) to level {0}!";
|
||||||
public string UpdateNeuronicLevel => "Set {1} character(s) Neuronic to level {0}!";
|
public string UpdateNeuronicLevel => "Set {1} character(s) Neuronic to level {0}!";
|
||||||
|
public string UpdateBreakLevel => "Set {1} character(s) Manifestation to level {0}!";
|
||||||
}
|
}
|
||||||
|
|
||||||
/// <summary>
|
/// <summary>
|
||||||
|
|||||||
@@ -129,4 +129,41 @@ public class CommandGirl : ICommands
|
|||||||
level.ToString(),
|
level.ToString(),
|
||||||
girls.Count.ToString()));
|
girls.Count.ToString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[CommandMethod("break")]
|
||||||
|
public async ValueTask UpdateBreakLevel(CommandArg arg)
|
||||||
|
{
|
||||||
|
if (!await arg.CheckOnlineTarget()) return;
|
||||||
|
if (!await arg.CheckArgCnt(2)) return;
|
||||||
|
|
||||||
|
var guid = arg.GetInt(0);
|
||||||
|
var level = Math.Clamp(arg.GetInt(1), 0, 45);
|
||||||
|
var player = arg.Target!.Player!;
|
||||||
|
List<CharacterInfo> girls = [];
|
||||||
|
|
||||||
|
if (guid == -1)
|
||||||
|
{
|
||||||
|
foreach (var girl in player.CharacterManager.CharacterData.Characters)
|
||||||
|
{
|
||||||
|
girl.Break = (uint)level;
|
||||||
|
girls.Add(girl);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var girl = player.CharacterManager.GetCharacterByGUID((uint)guid);
|
||||||
|
if (girl == null)
|
||||||
|
{
|
||||||
|
await arg.SendMsg(I18NManager.Translate("Game.Command.Girl.NotFound"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
girl.Break = (uint)level;
|
||||||
|
girls.Add(girl);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (girls.Count > 0) await player.SendPacket(new PacketNtfCallScript(girls));
|
||||||
|
await arg.SendMsg(I18NManager.Translate("Game.Command.Girl.UpdateBreakLevel",
|
||||||
|
level.ToString(),
|
||||||
|
girls.Count.ToString()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -258,4 +258,76 @@ public class CommandGiveAll : ICommands
|
|||||||
await arg.SendMsg(I18NManager.Translate("Game.Command.GiveAll.GiveAllItems",
|
await arg.SendMsg(I18NManager.Translate("Game.Command.GiveAll.GiveAllItems",
|
||||||
I18NManager.Translate("Word.WeaponPart"), weaponPartItems.Count.ToString()));
|
I18NManager.Translate("Word.WeaponPart"), weaponPartItems.Count.ToString()));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
[CommandMethod("skin")]
|
||||||
|
public async ValueTask GiveAllSkin(CommandArg arg)
|
||||||
|
{
|
||||||
|
if (!await arg.CheckOnlineTarget()) return;
|
||||||
|
if (await arg.GetOption('p') is not int particular) return;
|
||||||
|
if (await arg.GetOption('l') is not int level) return;
|
||||||
|
if (await arg.GetOption('g') is not int genre) return;
|
||||||
|
|
||||||
|
var detail = arg.GetInt(0);
|
||||||
|
var player = arg.Target!.Player!;
|
||||||
|
List<GameSkinInfo> skinItems = [];
|
||||||
|
if (detail == -1)
|
||||||
|
{
|
||||||
|
// add all
|
||||||
|
foreach (var config in GameData.CardSkinData.Values)
|
||||||
|
{
|
||||||
|
var skin = await player.InventoryManager!
|
||||||
|
.AddSkinItem((ItemTypeEnum)config.Genre, config.Detail, config.Particular, config.Level, false);
|
||||||
|
if (skin != null) skinItems.Add(skin);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var skin = await player.InventoryManager!.AddSkinItem((ItemTypeEnum)genre, (uint)detail, (uint)particular, (uint)level, false);
|
||||||
|
if (skin == null)
|
||||||
|
{
|
||||||
|
await arg.SendMsg(I18NManager.Translate("Game.Command.GiveAll.NotFound", I18NManager.Translate("Word.Skin")));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
skinItems.Add(skin);
|
||||||
|
}
|
||||||
|
if (skinItems.Count > 0) await player.SendPacket(new PacketNtfCallScript(skinItems));
|
||||||
|
await arg.SendMsg(I18NManager.Translate("Game.Command.GiveAll.GiveAllItems",
|
||||||
|
I18NManager.Translate("Word.Skin"), skinItems.Count.ToString()));
|
||||||
|
}
|
||||||
|
|
||||||
|
[CommandMethod("furniture")]
|
||||||
|
public async ValueTask GiveAllHouseFurniture(CommandArg arg)
|
||||||
|
{
|
||||||
|
if (!await arg.CheckOnlineTarget()) return;
|
||||||
|
if (await arg.GetOption('p') is not int particular) return;
|
||||||
|
if (await arg.GetOption('l') is not int level) return;
|
||||||
|
if (await arg.GetOption('g') is not int genre) return;
|
||||||
|
|
||||||
|
var detail = arg.GetInt(0);
|
||||||
|
var player = arg.Target!.Player!;
|
||||||
|
List<BaseGameItemInfo> furnitureItems = [];
|
||||||
|
if (detail == -1)
|
||||||
|
{
|
||||||
|
// add all
|
||||||
|
foreach (var config in GameData.DormGiftData.Values)
|
||||||
|
{
|
||||||
|
var furniture = await player.InventoryManager!
|
||||||
|
.AddHouseFurnitureItem((ItemTypeEnum)config.Genre, config.Detail, config.Particular, config.Level, false);
|
||||||
|
if (furniture != null) furnitureItems.Add(furniture);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
var furniture = await player.InventoryManager!.AddHouseFurnitureItem((ItemTypeEnum)genre, (uint)detail, (uint)particular, (uint)level, false);
|
||||||
|
if (furniture == null)
|
||||||
|
{
|
||||||
|
await arg.SendMsg(I18NManager.Translate("Game.Command.GiveAll.NotFound", I18NManager.Translate("Word.Furniture")));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
furnitureItems.Add(furniture);
|
||||||
|
}
|
||||||
|
if (furnitureItems.Count > 0) await player.SendPacket(new PacketNtfCallScript(furnitureItems));
|
||||||
|
await arg.SendMsg(I18NManager.Translate("Game.Command.GiveAll.GiveAllItems",
|
||||||
|
I18NManager.Translate("Word.Furniture"), furnitureItems.Count.ToString()));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -27,11 +27,11 @@ public class CharacterManager(PlayerInstance player) : BasePlayerManager(player)
|
|||||||
Timestamp = Extensions.GetUnixSec()
|
Timestamp = Extensions.GetUnixSec()
|
||||||
};
|
};
|
||||||
|
|
||||||
var weaponInfo = await Player.InventoryManager!.AddWeaponItem((ItemTypeEnum)CharacterExcel.DefaultWeaponGPDL[0], CharacterExcel.DefaultWeaponGPDL[1], CharacterExcel.DefaultWeaponGPDL[2], CharacterExcel.DefaultWeaponGPDL[3],sendPacket:false);
|
var weaponInfo = await Player.InventoryManager!.AddWeaponItem((ItemTypeEnum)CharacterExcel.DefaultWeaponGPDL[0], CharacterExcel.DefaultWeaponGPDL[1], CharacterExcel.DefaultWeaponGPDL[2], CharacterExcel.DefaultWeaponGPDL[3],sendPacket: true);
|
||||||
if (weaponInfo != null) character.WeaponUniqueId = weaponInfo.UniqueId;
|
if (weaponInfo != null) character.WeaponUniqueId = weaponInfo.UniqueId;
|
||||||
|
|
||||||
var skinInfo = Player.InventoryManager!.GetSkinItemGDPL(ItemTypeEnum.TYPE_CARD_SKIN, detail, particular, level)
|
var skinInfo = Player.InventoryManager!.GetSkinItemGDPL(ItemTypeEnum.TYPE_CARD_SKIN, detail, particular, level)
|
||||||
?? await Player.InventoryManager!.AddSkinItem(ItemTypeEnum.TYPE_CARD_SKIN, detail, particular, level, false);
|
?? await Player.InventoryManager!.AddSkinItem(ItemTypeEnum.TYPE_CARD_SKIN, detail, particular, level, true);
|
||||||
if (skinInfo != null) character.SkinId = skinInfo.UniqueId;
|
if (skinInfo != null) character.SkinId = skinInfo.UniqueId;
|
||||||
|
|
||||||
if (sendPacket) await Player.SendPacket(new PacketNtfCallScript([character]));
|
if (sendPacket) await Player.SendPacket(new PacketNtfCallScript([character]));
|
||||||
|
|||||||
@@ -333,4 +333,25 @@ public class InventoryManager(PlayerInstance player) : BasePlayerManager(player)
|
|||||||
|
|
||||||
return weaponPartInfo;
|
return weaponPartInfo;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public async ValueTask<BaseGameItemInfo?> AddHouseFurnitureItem(ItemTypeEnum genre, uint detail, uint particular, uint level = 1, bool sendPacket = true)
|
||||||
|
{
|
||||||
|
if (genre != ItemTypeEnum.TYPE_HOUSE) return null;
|
||||||
|
var houseFurnitureData = GameData.DormGiftData.Values.FirstOrDefault(x => x.Genre == (int)genre && x.Detail == detail && x.Particular == particular && x.Level == level);
|
||||||
|
if (houseFurnitureData == null) return null;
|
||||||
|
var templateId = GameResourceTemplateId.FromGdpl((uint)genre, detail, particular, level);
|
||||||
|
if (InventoryData.Items.Values.Any(x => x.TemplateId == templateId)) return null;
|
||||||
|
var furnitureInfo = new BaseGameItemInfo
|
||||||
|
{
|
||||||
|
TemplateId = templateId,
|
||||||
|
UniqueId = InventoryData.NextUniqueUid++,
|
||||||
|
ItemType = genre,
|
||||||
|
ItemCount = 1
|
||||||
|
};
|
||||||
|
InventoryData.Items[furnitureInfo.UniqueId] = furnitureInfo;
|
||||||
|
|
||||||
|
if (sendPacket) await Player.SendPacket(new PacketNtfCallScript([furnitureInfo]));
|
||||||
|
|
||||||
|
return furnitureInfo;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
using Google.Protobuf;
|
using Google.Protobuf;
|
||||||
using MikuSB.Data;
|
using MikuSB.Data;
|
||||||
using MikuSB.Database;
|
using MikuSB.Database;
|
||||||
using MikuSB.Database.Account;
|
using MikuSB.Database.Account;
|
||||||
@@ -230,6 +230,11 @@ public class PlayerInstance(PlayerGameData data)
|
|||||||
proto.Attrs[ToShiftedAttrKey(gid, sid)] = val;
|
proto.Attrs[ToShiftedAttrKey(gid, sid)] = val;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (var x in Data.StrAttrs)
|
||||||
|
{
|
||||||
|
proto.StrAttrs[ToShiftedAttrKey(x.Gid, x.Sid)] = x.Val;
|
||||||
|
}
|
||||||
|
|
||||||
proto.ShowItems.AddRange(Data.ShowItems);
|
proto.ShowItems.AddRange(Data.ShowItems);
|
||||||
|
|
||||||
return proto;
|
return proto;
|
||||||
@@ -251,6 +256,22 @@ public class PlayerInstance(PlayerGameData data)
|
|||||||
Data.ShowItems[index - 1] = itemId;
|
Data.ShowItems[index - 1] = itemId;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public void SetStrAttr(uint gid, uint sid, string value)
|
||||||
|
{
|
||||||
|
var attr = Data.StrAttrs.FirstOrDefault(x => x.Gid == gid && x.Sid == sid);
|
||||||
|
if (attr == null)
|
||||||
|
{
|
||||||
|
attr = new PlayerStrAttr
|
||||||
|
{
|
||||||
|
Gid = gid,
|
||||||
|
Sid = sid
|
||||||
|
};
|
||||||
|
Data.StrAttrs.Add(attr);
|
||||||
|
}
|
||||||
|
|
||||||
|
attr.Val = value;
|
||||||
|
}
|
||||||
|
|
||||||
public uint ToPackedAttrKey(uint gid, uint sid)
|
public uint ToPackedAttrKey(uint gid, uint sid)
|
||||||
{
|
{
|
||||||
if (gid == 0)
|
if (gid == 0)
|
||||||
@@ -267,9 +288,10 @@ public class PlayerInstance(PlayerGameData data)
|
|||||||
return (gid << 16) | sid;
|
return (gid << 16) | sid;
|
||||||
}
|
}
|
||||||
|
|
||||||
public void BuildPlayerAttr()
|
public void BuildPlayerAttr(bool additional = false)
|
||||||
{
|
{
|
||||||
var bootstrapAttrs = BuildLobbyBootstrapAttrs();
|
var bootstrapAttrs = BuildLobbyBootstrapAttrs().ToList();
|
||||||
|
if (additional) bootstrapAttrs.AddRange(BuildGirlFurnitureAttrs());
|
||||||
var existingAttrs = Data.Attrs
|
var existingAttrs = Data.Attrs
|
||||||
.ToDictionary(x => (x.Gid, x.Sid));
|
.ToDictionary(x => (x.Gid, x.Sid));
|
||||||
var seenAttrs = new HashSet<(uint Gid, uint Sid)>();
|
var seenAttrs = new HashSet<(uint Gid, uint Sid)>();
|
||||||
@@ -299,6 +321,24 @@ public class PlayerInstance(PlayerGameData data)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static IEnumerable<(uint Gid, uint Sid, uint Value)> BuildGirlFurnitureAttrs()
|
||||||
|
{
|
||||||
|
// Unlock some furniture slots for every girl
|
||||||
|
// Each furniture attr int stores 10 slots using 3 bits per slot
|
||||||
|
// Value below means slot 0..9 = 1
|
||||||
|
const uint furnitureUnlockedValue = 153391689;
|
||||||
|
|
||||||
|
for (uint girlId = 0; girlId <= 50; girlId++)
|
||||||
|
{
|
||||||
|
// FurnitureStart..FurnitureEnd = 10..19
|
||||||
|
for (uint offset = 10; offset <= 19; offset++)
|
||||||
|
{
|
||||||
|
uint sid = (girlId * 50) + offset;
|
||||||
|
yield return (101, sid, furnitureUnlockedValue);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private static IEnumerable<(uint Gid, uint Sid, uint Value)> BuildLobbyBootstrapAttrs()
|
private static IEnumerable<(uint Gid, uint Sid, uint Value)> BuildLobbyBootstrapAttrs()
|
||||||
{
|
{
|
||||||
// GuideLogic uses group 4. Value 999 is safely above every configured step count,
|
// GuideLogic uses group 4. Value 999 is safely above every configured step count,
|
||||||
@@ -349,6 +389,14 @@ public class PlayerInstance(PlayerGameData data)
|
|||||||
yield return (22, levelId, 1_700_000_000);
|
yield return (22, levelId, 1_700_000_000);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
foreach (var guide in GameData.GuideData.Values)
|
||||||
|
{
|
||||||
|
yield return (4, guide.ID, 999);
|
||||||
|
}
|
||||||
|
|
||||||
|
for (uint favor = 0; favor <= 50; favor++)
|
||||||
|
yield return (101, favor * 50, 500);
|
||||||
|
|
||||||
// Main Scene 0 mean default scene
|
// Main Scene 0 mean default scene
|
||||||
yield return (132, 1, 0);
|
yield return (132, 1, 0);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -12,9 +12,15 @@ public class GirlRegister : IHouseFuncHandler
|
|||||||
if (root == null) return;
|
if (root == null) return;
|
||||||
|
|
||||||
var girlId = HouseJson.NumField(root, "GirlId");
|
var girlId = HouseJson.NumField(root, "GirlId");
|
||||||
|
var floorId = HouseJson.NumField(root, "FloorId");
|
||||||
var sync = new NtfSyncPlayer();
|
var sync = new NtfSyncPlayer();
|
||||||
if (girlId > 0)
|
if (girlId > 0)
|
||||||
|
{
|
||||||
|
var bedroomSid = HouseAttr.GetNextBedroomSid(connection.Player!, (uint)floorId);
|
||||||
await HouseAttr.SetAsync(connection, HouseAttr.GirlRoomNumSid(girlId), HouseAttr.BedroomRegisteredNoRoom, sync);
|
await HouseAttr.SetAsync(connection, HouseAttr.GirlRoomNumSid(girlId), HouseAttr.BedroomRegisteredNoRoom, sync);
|
||||||
|
if (bedroomSid != 0) await HouseAttr.SetAsync(connection, bedroomSid, (uint)girlId, sync);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
await CallGSRouter.SendScript(connection, "House_Request", HouseRequestScript.Synthesize(root), sync);
|
await CallGSRouter.SendScript(connection, "House_Request", HouseRequestScript.Synthesize(root), sync);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -105,6 +105,7 @@ internal static class HouseAttr
|
|||||||
internal const uint BedroomStartSid = 2550;
|
internal const uint BedroomStartSid = 2550;
|
||||||
internal const uint BedroomRegisteredNoRoom = 100;
|
internal const uint BedroomRegisteredNoRoom = 100;
|
||||||
internal const uint PlayerRingInfoSidBase = 3174;
|
internal const uint PlayerRingInfoSidBase = 3174;
|
||||||
|
internal const uint BedroomPerFloor = 8;
|
||||||
|
|
||||||
internal static uint Read(PlayerInstance player, uint sid)
|
internal static uint Read(PlayerInstance player, uint sid)
|
||||||
{
|
{
|
||||||
@@ -175,6 +176,25 @@ internal static class HouseAttr
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal static uint GetNextBedroomSid(PlayerInstance player, uint floorId)
|
||||||
|
{
|
||||||
|
var floorStartSid = BedroomStartSid + ((floorId - 2) * BedroomPerFloor) + 1;
|
||||||
|
|
||||||
|
for (uint i = 0; i < BedroomPerFloor; i++)
|
||||||
|
{
|
||||||
|
var sid = floorStartSid + i;
|
||||||
|
|
||||||
|
var exists = player.Data.Attrs.Any(x =>
|
||||||
|
x.Gid == Gid &&
|
||||||
|
x.Sid == sid);
|
||||||
|
|
||||||
|
if (!exists)
|
||||||
|
return sid;
|
||||||
|
}
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
internal static uint PackArcadePropUse(int type, int id, ushort count) =>
|
internal static uint PackArcadePropUse(int type, int id, ushort count) =>
|
||||||
(((uint)count & 0xffffu) << 16) | (((uint)id & 0xffu) << 8) | ((uint)type & 0xffu);
|
(((uint)count & 0xffffu) << 16) | (((uint)id & 0xffu) << 8) | ((uint)type & 0xffu);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,3 +1,4 @@
|
|||||||
|
using MikuSB.Database;
|
||||||
using MikuSB.Enums.Player;
|
using MikuSB.Enums.Player;
|
||||||
using MikuSB.Proto;
|
using MikuSB.Proto;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
@@ -22,6 +23,7 @@ public class PlayerSetting_ChangeShowCard : ICallGSHandler
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
player.SetShowItem((int)ProfileShowItemTypeEnum.SHOWITEM_GIRL, card.Guid);
|
player.SetShowItem((int)ProfileShowItemTypeEnum.SHOWITEM_GIRL, card.Guid);
|
||||||
|
DatabaseHelper.SaveDatabaseType(player.Data);
|
||||||
var sync = new NtfSyncPlayer();
|
var sync = new NtfSyncPlayer();
|
||||||
sync.ShowItems.AddRange(player.Data.ShowItems);
|
sync.ShowItems.AddRange(player.Data.ShowItems);
|
||||||
await CallGSRouter.SendScript(connection, "PlayerSetting_ChangeShowCard", "{}", sync);
|
await CallGSRouter.SendScript(connection, "PlayerSetting_ChangeShowCard", "{}", sync);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using MikuSB.Enums.Player;
|
using MikuSB.Database;
|
||||||
|
using MikuSB.Enums.Player;
|
||||||
using MikuSB.Proto;
|
using MikuSB.Proto;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
@@ -35,7 +36,7 @@ public class PlayerSetting_SetProfileFace : ICallGSHandler
|
|||||||
}
|
}
|
||||||
player.SetShowItem((int)ProfileShowItemTypeEnum.SHOWITEM_FRAME, item.UniqueId);
|
player.SetShowItem((int)ProfileShowItemTypeEnum.SHOWITEM_FRAME, item.UniqueId);
|
||||||
}
|
}
|
||||||
|
DatabaseHelper.SaveDatabaseType(player.Data);
|
||||||
var sync = new NtfSyncPlayer();
|
var sync = new NtfSyncPlayer();
|
||||||
sync.ShowItems.AddRange(player.Data.ShowItems);
|
sync.ShowItems.AddRange(player.Data.ShowItems);
|
||||||
await CallGSRouter.SendScript(connection, "PlayerSetting_SetProfileFace", "null", sync);
|
await CallGSRouter.SendScript(connection, "PlayerSetting_SetProfileFace", "null", sync);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using MikuSB.Enums.Player;
|
using MikuSB.Database;
|
||||||
|
using MikuSB.Enums.Player;
|
||||||
using MikuSB.Proto;
|
using MikuSB.Proto;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
@@ -30,6 +31,8 @@ public class PlayerSetting_SetShowBadge : ICallGSHandler
|
|||||||
player.SetShowItem((int)slots[i], uniqueId);
|
player.SetShowItem((int)slots[i], uniqueId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
DatabaseHelper.SaveDatabaseType(player.Data);
|
||||||
|
|
||||||
var sync = new NtfSyncPlayer();
|
var sync = new NtfSyncPlayer();
|
||||||
sync.ShowItems.AddRange(player.Data.ShowItems);
|
sync.ShowItems.AddRange(player.Data.ShowItems);
|
||||||
await CallGSRouter.SendScript(connection, "PlayerSetting_SetShowBadge", "null", sync);
|
await CallGSRouter.SendScript(connection, "PlayerSetting_SetShowBadge", "null", sync);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using MikuSB.Enums.Player;
|
using MikuSB.Database;
|
||||||
|
using MikuSB.Enums.Player;
|
||||||
using MikuSB.Proto;
|
using MikuSB.Proto;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
@@ -23,6 +24,8 @@ public class PlayerSetting_SetShowBubble : ICallGSHandler
|
|||||||
}
|
}
|
||||||
|
|
||||||
player.SetShowItem((int)ProfileShowItemTypeEnum.SHOWITEM_BUBBLE, item.UniqueId);
|
player.SetShowItem((int)ProfileShowItemTypeEnum.SHOWITEM_BUBBLE, item.UniqueId);
|
||||||
|
DatabaseHelper.SaveDatabaseType(player.Data);
|
||||||
|
|
||||||
var sync = new NtfSyncPlayer();
|
var sync = new NtfSyncPlayer();
|
||||||
sync.ShowItems.AddRange(player.Data.ShowItems);
|
sync.ShowItems.AddRange(player.Data.ShowItems);
|
||||||
await CallGSRouter.SendScript(connection, "PlayerSetting_SetShowBubble", "null", sync);
|
await CallGSRouter.SendScript(connection, "PlayerSetting_SetShowBubble", "null", sync);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using MikuSB.Enums.Player;
|
using MikuSB.Database;
|
||||||
|
using MikuSB.Enums.Player;
|
||||||
using MikuSB.Proto;
|
using MikuSB.Proto;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
@@ -23,6 +24,8 @@ public class PlayerSetting_SetShowCover : ICallGSHandler
|
|||||||
}
|
}
|
||||||
|
|
||||||
player.SetShowItem((int)ProfileShowItemTypeEnum.SHOWITEM_COVER, item.UniqueId);
|
player.SetShowItem((int)ProfileShowItemTypeEnum.SHOWITEM_COVER, item.UniqueId);
|
||||||
|
DatabaseHelper.SaveDatabaseType(player.Data);
|
||||||
|
|
||||||
var sync = new NtfSyncPlayer();
|
var sync = new NtfSyncPlayer();
|
||||||
sync.ShowItems.AddRange(player.Data.ShowItems);
|
sync.ShowItems.AddRange(player.Data.ShowItems);
|
||||||
await CallGSRouter.SendScript(connection, "PlayerSetting_SetShowCover", "null", sync);
|
await CallGSRouter.SendScript(connection, "PlayerSetting_SetShowCover", "null", sync);
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using MikuSB.Enums.Player;
|
using MikuSB.Database;
|
||||||
|
using MikuSB.Enums.Player;
|
||||||
using MikuSB.Proto;
|
using MikuSB.Proto;
|
||||||
using System.Text.Json;
|
using System.Text.Json;
|
||||||
using System.Text.Json.Serialization;
|
using System.Text.Json.Serialization;
|
||||||
@@ -23,6 +24,8 @@ public class PlayerSetting_SetShowNameCard : ICallGSHandler
|
|||||||
}
|
}
|
||||||
|
|
||||||
player.SetShowItem((int)ProfileShowItemTypeEnum.SHOWITEM_NAMECARD, item.UniqueId);
|
player.SetShowItem((int)ProfileShowItemTypeEnum.SHOWITEM_NAMECARD, item.UniqueId);
|
||||||
|
DatabaseHelper.SaveDatabaseType(player.Data);
|
||||||
|
|
||||||
var sync = new NtfSyncPlayer();
|
var sync = new NtfSyncPlayer();
|
||||||
sync.ShowItems.AddRange(player.Data.ShowItems);
|
sync.ShowItems.AddRange(player.Data.ShowItems);
|
||||||
await CallGSRouter.SendScript(connection, "PlayerSetting_SetShowNameCard", "null", sync);
|
await CallGSRouter.SendScript(connection, "PlayerSetting_SetShowNameCard", "null", sync);
|
||||||
|
|||||||
39
GameServer/Server/CallGS/Handlers/Misc/SettingChange.cs
Normal file
39
GameServer/Server/CallGS/Handlers/Misc/SettingChange.cs
Normal file
@@ -0,0 +1,39 @@
|
|||||||
|
using MikuSB.Proto;
|
||||||
|
using System.Text.Json;
|
||||||
|
using System.Text.Json.Serialization;
|
||||||
|
|
||||||
|
namespace MikuSB.GameServer.Server.CallGS.Handlers.Misc;
|
||||||
|
|
||||||
|
[CallGSApi("SettingChange")]
|
||||||
|
public class SettingChange : ICallGSHandler
|
||||||
|
{
|
||||||
|
private const uint PlayerSettingGid = 44;
|
||||||
|
|
||||||
|
public async Task Handle(Connection connection, string param, ushort seqNo)
|
||||||
|
{
|
||||||
|
var changes = JsonSerializer.Deserialize<List<SettingChangeParam>>(param) ?? [];
|
||||||
|
var player = connection.Player!;
|
||||||
|
var sync = new NtfSyncPlayer();
|
||||||
|
|
||||||
|
foreach (var change in changes)
|
||||||
|
{
|
||||||
|
var value = player.Data.StrAttrs
|
||||||
|
.FirstOrDefault(x => x.Gid == PlayerSettingGid && x.Sid == change.Id)?
|
||||||
|
.Val;
|
||||||
|
|
||||||
|
if (value == null)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
sync.CustomStr[player.ToShiftedAttrKey(PlayerSettingGid, change.Id)] = value;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sync.CustomStr.Count > 0)
|
||||||
|
await connection.SendPacket(CmdIds.NtfSyncAttr, sync);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal sealed class SettingChangeParam
|
||||||
|
{
|
||||||
|
[JsonPropertyName("id")]
|
||||||
|
public uint Id { get; set; }
|
||||||
|
}
|
||||||
@@ -53,6 +53,7 @@ public class HandlerReqLogin : Handler
|
|||||||
await connection.Player.OnEnterGame();
|
await connection.Player.OnEnterGame();
|
||||||
connection.Player.Connection = connection;
|
connection.Player.Connection = connection;
|
||||||
await connection.SendPacket(new PacketRspLogin(connection.Player!));
|
await connection.SendPacket(new PacketRspLogin(connection.Player!));
|
||||||
|
await connection.SendPacket(new PacketNtfCallScript(connection.Player!));
|
||||||
await SendDebugLoginState(connection);
|
await SendDebugLoginState(connection);
|
||||||
|
|
||||||
await connection.Player.OnHeartBeat();
|
await connection.Player.OnHeartBeat();
|
||||||
|
|||||||
@@ -1,4 +1,5 @@
|
|||||||
using MikuSB.Database.Player;
|
using MikuSB.Database;
|
||||||
|
using MikuSB.Database.Player;
|
||||||
using MikuSB.Proto;
|
using MikuSB.Proto;
|
||||||
|
|
||||||
namespace MikuSB.GameServer.Server.Packet.Recv.Login;
|
namespace MikuSB.GameServer.Server.Packet.Recv.Login;
|
||||||
@@ -12,7 +13,11 @@ public class HandlerNtfSetAttr : Handler
|
|||||||
var player = connection.Player!;
|
var player = connection.Player!;
|
||||||
var attr = player.Data.Attrs
|
var attr = player.Data.Attrs
|
||||||
.FirstOrDefault(x => x.Gid == req.Gid && x.Sid == req.Sid);
|
.FirstOrDefault(x => x.Gid == req.Gid && x.Sid == req.Sid);
|
||||||
if (attr != null) attr.Val = req.Val;
|
|
||||||
|
if (attr != null)
|
||||||
|
{
|
||||||
|
attr.Val = req.Val;
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
player.Data.Attrs.Add(new PlayerAttr
|
player.Data.Attrs.Add(new PlayerAttr
|
||||||
@@ -22,6 +27,7 @@ public class HandlerNtfSetAttr : Handler
|
|||||||
Val = req.Val
|
Val = req.Val
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
DatabaseHelper.SaveDatabaseType(player.Data);
|
||||||
await player.OnHeartBeat();
|
await player.OnHeartBeat();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
19
GameServer/Server/Packet/Recv/Player/HandlerNtfSetStrAttr.cs
Normal file
19
GameServer/Server/Packet/Recv/Player/HandlerNtfSetStrAttr.cs
Normal file
@@ -0,0 +1,19 @@
|
|||||||
|
using MikuSB.Database;
|
||||||
|
using MikuSB.Proto;
|
||||||
|
|
||||||
|
namespace MikuSB.GameServer.Server.Packet.Recv.Login;
|
||||||
|
|
||||||
|
[Opcode(CmdIds.NtfSetStrAttr)]
|
||||||
|
public class HandlerNtfSetStrAttr : Handler
|
||||||
|
{
|
||||||
|
public override async Task OnHandle(Connection connection, byte[] data, ushort seqNo)
|
||||||
|
{
|
||||||
|
var req = NtfSetStrAttr.Parser.ParseFrom(data);
|
||||||
|
var player = connection.Player!;
|
||||||
|
|
||||||
|
player.SetStrAttr(req.Gid, req.Sid, req.Val);
|
||||||
|
DatabaseHelper.SaveDatabaseType(player.Data);
|
||||||
|
|
||||||
|
await player.OnHeartBeat();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -68,6 +68,21 @@ public class PacketNtfCallScript : BasePacket
|
|||||||
SetData(proto);
|
SetData(proto);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public PacketNtfCallScript(List<GameSkinInfo> skins) : base(CmdIds.NtfScript)
|
||||||
|
{
|
||||||
|
var proto = new NtfCallScript
|
||||||
|
{
|
||||||
|
Api = "",
|
||||||
|
Arg = "{}",
|
||||||
|
ExtraSync = new NtfSyncPlayer
|
||||||
|
{
|
||||||
|
Items = { skins.Select(x => x.ToProto()) }
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
SetData(proto);
|
||||||
|
}
|
||||||
|
|
||||||
public PacketNtfCallScript(InventoryData inventory) : base(CmdIds.NtfScript)
|
public PacketNtfCallScript(InventoryData inventory) : base(CmdIds.NtfScript)
|
||||||
{
|
{
|
||||||
var proto = new NtfCallScript
|
var proto = new NtfCallScript
|
||||||
@@ -87,7 +102,7 @@ public class PacketNtfCallScript : BasePacket
|
|||||||
|
|
||||||
public PacketNtfCallScript(PlayerInstance Player) : base(CmdIds.NtfScript)
|
public PacketNtfCallScript(PlayerInstance Player) : base(CmdIds.NtfScript)
|
||||||
{
|
{
|
||||||
Player.BuildPlayerAttr();
|
Player.BuildPlayerAttr(true);
|
||||||
var proto = new NtfCallScript
|
var proto = new NtfCallScript
|
||||||
{
|
{
|
||||||
Api = "",
|
Api = "",
|
||||||
|
|||||||
@@ -1 +1 @@
|
|||||||
v=2.0
|
v=2.3
|
||||||
Reference in New Issue
Block a user